Flex List Within a List? - list

I am trying to display a small set of hierarchical data and have found the AdvancedDataGrid so terrible in how it handles layout I am going to try and approach it another way using Spark Lists.
I am getting from a MySQL database an ArrayCollection of Lessons. Each Lesson has a parent Topic (I have included a TopicName field within each Lesson for ease) and I want to display the Lessons after grouping them by their respective Topics.
I could create a hierarchical data structure, possibly by using a GroupingCollection2, and wondered if I could display a Spark List of Topics and within the topicItemRenderer I create display a Spark List of Lessons for the given Topic?
Any thoughts very welcome.
Chris

To create a grouped list of data I replaced my AdvancedDataGrid with a Spark List within a Spark List. It now has a layout which is predictable and sizes properly. In my case I know that I only have to display approximately 20 items in my grouped List so I don't really have any performance issues.
This is how I did it:
General -
I created a new mxml component dedicated to the list of Lessons. I organise my code following a Model, View, Presenter pattern so I created LessonList_View (mxml) and LessonList_Presenter (ActionScript class). I have a Model class (Singleton) which the presenter classes instantiate to put/get data. The Model raises events when properties change informing the presenters, who have eventListeners, of the change so that they can update their local properties of which the views are bound. The Model calls PHP methods in a Amfphp Service to acquire data from the MySQL database.
Prepare Data -
I have a relational MySQL database containing a table of Lessons and a table of Topics. Each Lesson has to have one parent Topic. A Topic will have many Lessons. I am using Amfphp to get a subset of Lessons data. Each Lesson row from the database is mapped to a Flex class giving me an ArrayCollection of strongly typed value objects of Type VoLesson. To make life simpler I included the topicName field in my VoLesson ActionScript class, only the topicId is available within the MySQL table, and included this in my SELECT statement when getting the data. I also sort the data by Topic and then by Lesson here so its ready for the next step.
Next I need to create an Array Collection containing ArrayCollections of Lessons of the same Topic. This way, I figured, I can have a parent Spark List displaying Topics and within the ItemRenderer for each Topic List Item I can have a List of Lessons.
Once my LessonList_Presenter has got an ArrayCollection of VoLessons I iterate through it. A new, temporary, ArrayCollection of Lessons (_topicLessons) is populated with Lessons until the topicName changes whereupon I add the current _topicLessons ArrayCollection of VoLessons into a parent ArrayCollection (courseTopicLessons).
The function is as follows:
private function updateCourseTopicLessons():void {
// Reset courseTopicLessons.
this.courseTopicLessons = new ArrayCollection();
// Create a variable to hold the last topicName.
var _topicName:String = "";
// Create an ArrayCollection to hold all of the Lessons for a single Topic.
var _topicLessons:ArrayCollection = new ArrayCollection();
// Iterate through the courseLessons.
for each(var _lesson:VoLesson in this.courseLessons)
{
// check to see if this lesson has a different topicName.
if (_lesson.topicName != _topicName) {
//trace("Different Topic: " + _lesson.topicName);
// Add the previous _topicLessons into the courseTopicLessons ArrayCollection.
if (_topicLessons.length > 0) {
//trace("Adding _topicLessons " + _topicLessons.length + " to courseTopicLessons");
this.courseTopicLessons.addItemAt(_topicLessons, 0)
}
// This is a new Topic. Reset _topicLessons.
//trace("Reset _topicLessons");
_topicLessons = new ArrayCollection();
// Update _topicName.
_topicName = _lesson.topicName;
}
// Add the Lesson to _topicLessons.
//trace("Add Lesson: " + _lesson.lessonTitle + " to _topicLessons")
_topicLessons.addItemAt(_lesson, 0);
}
// Add the previous _topicLessons into the courseTopicLessons ArrayCollection.
if (_topicLessons.length > 0) {
//trace("Adding final _topicLessons " + _topicLessons.length + " to courseTopicLessons")
this.courseTopicLessons.addItemAt(_topicLessons, 0)
}
//trace(this.courseTopicLessons)
}
I used .addItemAt() to keep the sort order correct.
Views and ItemRenderers -
In my LessonList_View I created the List and set it as follows:
<!-- Lessons List -->
<s:List
id="lessonList"
dataProvider="{presenter.courseTopicLessons}"
itemRenderer="views.LessonListTopicItemRenderer_View"
borderVisible="false"
borderColor="0xff69b4"
preventSelection="true"
contentBackgroundAlpha="0">
<s:layout>
<s:VerticalLayout
useVirtualLayout="false"
requestedMinRowCount="1"
gap="8"
paddingTop="8" paddingBottom="8"/>
</s:layout>
</s:List>
I used the borders when checking everything to see the extents of the Lists.
My data provider is an ArrayCollection of ArrayCollections. I want to display List of Topics and within each Topic List Item I want to display a List of Lessons. To display the Topics I know that each ArrayCollection within the parent ArrayCollection will have at least 1 VoLesson (I hope you're following this!). I can display the topicName value from this item. Here is my code for the Lesson List's ItemRenderer:
<s:ItemRenderer
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:presenters="presenters.*"
width="100%" height="100%"
autoDrawBackground="false">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
<presenters:LessonListTopicItemRenderer_Presenter id="presenter"/>
</fx:Declarations>
<fx:Script>
<![CDATA[
import models.Globals;
import vo.VoLesson;
override public function set data( value:Object ) : void {
super.data = value;
// Check to see if the data property is null.
if (value== null)
return;
// If the data property is not null.
var _lesson:VoLesson = VoLesson(value[0]);
topicLabel.text = _lesson.topicName;
}
]]>
</fx:Script>
<s:VGroup gap="8" width="100%">
<!-- Divider line between Topics -->
<s:Line id="topicDividerLine" width="100%">
<s:stroke>
<s:SolidColorStroke color="{presenter.selectedActivityColour_Mid}" weight="1" />
</s:stroke>
</s:Line>
<!-- Topic Label -->
<s:Label
id="topicLabel"
styleName="topicStyle"
color="{presenter.selectedActivityColour}"
maxWidth="{presenter.lessonsListTopicColumnWidth}" />
<s:HGroup paddingLeft="{Globals.LESSONS_LIST_TOPIC_COLUMN_WIDTH}">
<s:List
id="lessonList"
dataProvider="{data}"
borderColor="0xadff2f"
itemRenderer="views.LessonListLessonItemRenderer_View"
borderVisible="false"
preventSelection="true">
<s:layout>
<s:VerticalLayout
useVirtualLayout="false"
requestedMinRowCount="1"
gap="16"
paddingTop="8" paddingBottom="8"/>
</s:layout>
</s:List>
</s:HGroup>
</s:VGroup>
The key thing to remember is that the ItemRenderer will be passed only the data for an individual item in the List, in this case an ArrayCollection of VoLesson objects. Within the element I get the topicName for the first item in the ArrayCollection of VoLessons passed in as 'data' and set my Label's text property.
Below the Topic Label I have my List of Lessons which has the same data provider, an ArrayCollection of VoLesson objects for the same Topic. The ItemRenderer for this List is as follows:
<s:ItemRenderer
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:views="views.*"
xmlns:presenters="presenters.*"
height="100%"
autoDrawBackground="false">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
<presenters:LessonListLessonItemRenderer_Presenter id="presenter"/>
</fx:Declarations>
<fx:Script>
<![CDATA[
import vo.VoLesson;
override public function set data( value:Object ) : void {
super.data = value;
// Check to see if the data property is null.
if (value== null)
return;
// If the data property is not null.
var _lesson:VoLesson = VoLesson(value);
lessonLabel.text = _lesson.lessonTitle;
}
]]>
</fx:Script>
<s:HGroup gap="8" verticalAlign="middle">
<views:IconLesson_View />
<s:Label
id="lessonLabel"
styleName="lessonStyle"
color="{presenter.textDarkGrey}"/>
</s:HGroup>
Remember that the 'data' object for this ItemRenderer, and there will be one for each item in the List of Lessons, will be a single VoLesson object. In the element I get the lessonTitle property from the VoLesson and set the lessonLabel Label's text property.
Final List
The List appears as follows:
I have spent many days trying to coerce an AdvancedDataGrid to size itself and layout the content properly, it was dreadful. Yesterday I decided to start again and this works so much better. For a simple grouped list I would recommend a similar approach.
Regards
Chris

You could do it that way and it would work, But its not a good idea if you have a lot data. You would have to worry about item renderers. I would probably use a tree and create custom item renderers if need be. May be some code might help.

Related

Identifying targetList in ember-drag-sort

I am currently trying to implement an ember-drag-sort nested list into my Ember.js app.
Is there a way to determine in the dragEnd action which "sub-list" the item has been dropped into? (e.g. a class name, id etc)
In my scenario, I am sorting ember data records that can belong to each other (i.e. a nested, 'tree' structure). When I drag one nested record "into" another (making the dragged record a child of the second record), I need to update the parent attribute in ember-data. My question is, how do you pass some id of the second record (the new parent) to the dragEnd action?
Is this even possible?
EDIT:
To put it another way, I want to be able to identify which list I have dropped the item into.
targetList refers to the array on the dragged side of the component. For pushing the parent to the target list alongside the child, you can take a look at this twiddle.
To simplify identification of lists, the additional arguments feature has been implemented by #rwwagner90 (SO, GitHub).
You can pass some kind of list identifier into the additionalArgs argument to your lists. In this example I'm passing parent records which own the lists:
{{#each parents as |parent|}}
{{#drag-sort-list
items = parent.children
additionalArgs = (hash parent=parent)
dragEndAction = (action 'dragEnd')
as |child|
}}
{{child.name}}
{{/drag-sort-list}}
{{/each}}
In the dragEnd action you can access the parent records that own the source list and the target list:
dragEndAction({ sourceList, sourceIndex, sourceArgs, targetList, targetIndex, targetArgs }) {
if (sourceModel === targetModel && sourceIndex === targetIndex) return;
const item = sourceList.objectAt(sourceIndex);
sourceList.removeAt(sourceIndex);
targetList.insertAt(targetIndex, item);
// Access the parent via `sourceArgs` and `targetArgs`
sourceArgs.parent.save();
targetArgs.parent.save();
}

Magento categories navigation

I have a Magento website with the following category structure (CAPITALS letters are CATEGORIES and small letters are products):
ROOT CATEGORY
APPARELS
SHOP BY SIZE
product1
product2
product3
SHOP BY COLLECTION
product4
product5
SHOP BY DESIGN
product6
product7
product8
product9
I want to show my navigation menu as SHOP BY SIZE, SHOP BY COLLECTION and SHOP BY DESIGN. I don't want the navigation to start with APPARELS level. Is there any way to do this?
Note: As per Magento design, ROOT CATEGORY cannot be shown in navigation menu. The navigation menu starts from categories in 2nd level i.e. APPARELS in this case.
Take a look at navigation.php, you could alter the core functionality but by using a module with rewrite (never directly alter a corefile!). I always start there when i need custom navigation functionality.
http://freegento.com/doc/db/d56/_catalog_2_block_2_navigation_8php-source.html
edit, alltough i often use this method, i would advise to avoid rewriting as much as possible, I don't think its possible in this case tough because we are talking about displaying lvl 2 categories as main nav
IF you truly want to use the design Root -> Apparels -> Shop By * you can do this with a single override and modification
config.xml - this is obviously a heavily simplified file, you'll need to provide a helper rewrite for the file.
<?xml version="1.0"?>
<config>
<helpers>
<catalog>
<rewrite>
<category>Namespace_Module_Helper_Catalog_Category</category>
</rewrite>
</catalog>
</helpers>
</config>
Category.php
This assumes you want to use the first child category under your site's root category. In your case it would be "Apparels". This modification takes into account the use of flat or non-flat category tables. There are other options for selecting the ID, one would be a system configuration with the category list as a source thus allowing you to choose your navigations root category directly.
The crux of this file is getting the Parent ID to be the "root category" you want to base the navigation on. Again, for your case the parent ID would be set to that of the "apparels" category.
class Namespace_Module_Helper_Catalog_Category extends Mage_Catalog_Helper_Category {
public function getStoreCategories($sorted=false, $asCollection=false, $toLoad=true)
{
$parent = Mage::app()->getStore()->getRootCategoryId();
$cacheKey = sprintf('%d-%d-%d-%d', $parent, $sorted, $asCollection, $toLoad);
if (isset($this->_storeCategories[$cacheKey])) {
return $this->_storeCategories[$cacheKey];
}
/**
* Check if parent node of the store still exists
*/
$category = Mage::getModel('catalog/category');
/* #var $category Mage_Catalog_Model_Category */
if (!$category->checkId($parent)) {
if ($asCollection) {
return new Varien_Data_Collection();
}
return array();
}
/* Change ian on 1/4/13 at 11:16 AM - Description: Here we capture the id of first child for use as the 'root' */
$category->load($parent);
/** #var $collection Mage_Catalog_Model_Resource_Category_Collection */
$collection = $category->getChildrenCategories();
if (is_array($collection)) {
$category = array_shift($collection); //get the first category in the array. Unknown key.
$parent = $category->getId();
} else {
$parent = $collection->getFirstItem()->getId();
}
$recursionLevel = max(0, (int) Mage::app()->getStore()->getConfig('catalog/navigation/max_depth'));
$storeCategories = $category->getCategories($parent, $recursionLevel, $sorted, $asCollection, $toLoad);
$this->_storeCategories[$cacheKey] = $storeCategories;
return $storeCategories;
}
}

Flex set selectedItem in Spark list outside of IndexChangeEvent handler

I've got a spark list that is part of a CallOutContent, much like this:
<s:CalloutButton id="frequencyChanger" label="{frequencyChangeList.selectedItem.label}">
<s:calloutContent>
<s:BorderContainer>
<s:layout>
<s:VerticalLayout/>
</s:layout>
<s:List id="frequencyChangeList" dataProvider="{Util.getFrequencyList()}" selectedIndex="8" requireSelection="false" changing="frequencyList_changingEvent(event)"/>
</s:BorderContainer>
</s:calloutContent>
</s:CalloutButton>
The dataProvider is an ArrayList with several items of the following structure:
public class ListItem
{
public var label:String;
public var item:Object;
public function PeriodFrequencyListItem(label:String, item:Object) {
this.label=label;
this.item=item;
}
}
The item Object is an Enum.
Background for this is to match the Enum to the corresponding label to be displayed in the List. I would have used a dictionary, but Lists don't work this those (unfortunately).
Anyhow, in the IndexChangeEvent method I can set the selectedItem to the one currently selected:
frequencyChangeList.selectedItem = event.currentTarget.selectedItem;
What I can't do (but desperately need in another part of the class) is to set the selectedItem of the List outside of the IndexChangeEvent method.
Any attempt to set an item with something like the following failed, throwing a null pointer exception.
frequencyChangeList.selectedItem = someListItemObject;
I can set the labels of the CallOutButtons, naturally, that doesn't change the selection of the List.
So, where is the trick to select an item in the list outside of its own 'changing' method?
Any help would be much appreciated.
Cheers!
Have you tried doing this:
myList.selectedIndex = indexOfArray;
see here:
How do I make sure that there is always something selected in a Spark List?

sencha touch :: how to create list of hbox-items

i want to generate a list of data out of a store. each data item holds a title and a boolean. now i want to have a list of the items, each holding the title and a togglefield (showing the boolean) on the right of the title.
how could I do that?
thnx!
From the Sencha Touch documentation, a List is "a mechanism for displaying data using a list layout template", i.e. html template. If you want a 'list' of components, you'll have to make your own extension of DataView (I think).
A workaround could be to put an html checkbox inside your itemTpl.
Something like (warning - not tested):
itemTpl: '<p>{title}: <input type="checkbox" name="BoolCheckbox" class="boolcheckbox"'
+ "{[(values.bool? 'checked="checked"' : '')]}"
+ '></input></p>'
To run your own code in the XTemplate, you bracket it with {[]}. When in this scope, you have access to a variable 'values', which contains the data for the record.
To detect events, you'd add a listener to the list:
itemtap: function (dataView, index, item, e) {
if (e.getTarget().getClass().toString() == "boolcheckbox") {
// do something
}
}
Some resources on templates:
http://dev.sencha.com/deploy/touch/docs/
http://www.sencha.com/learn/xtemplates-part-i/
http://www.sencha.com/learn/xtemplates-part-ii/

How do I add custom column to existing WSS list template

I need to use feature stapler to add some text columns to Posts list inside OOTB blog site definition. I plan not to use site columns, but only to add those columns to list (I don't use site columns because I have multiple site collections and there will be only one Posts list per site collection, so site columns are not very reusable in this case). My question is: How do I achieve this?
Perhaps you can create a feature that uses the object model from the feature receiver to add (and remove as appropriate) the columns to just the specific list when the feature is activated.
I would use the XML Schema approach for creating the columns in order to ensure the same GUID for each column. See
The best solution is to create a hidden custom action for Posts List. I'm posting a simplified version here
Elements.xml:
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<CustomAction Id="XXXXXXXX"
RegistrationType="List"
RegistrationId="101"
Rights="Open"
Location="ViewToolbar"
Sequence="110"
Title="Hidden Settings Button"
ControlAssembly="MyLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=XXXXXX"
ControlClass="MyLib.MyClass"
/>
<FeatureSiteTemplateAssociation Id="XXXXXXX" TemplateName="YOUR_BLOG_SITE_TEMPLATE_NAME" />
MyClass.cs:
[DefaultProperty("Text")]
[ToolboxData("<{0}:MyClass runat=server></{0}:MyClass>")]
public class MyClass : WebControl
{
[Bindable(true)]
[Category("Appearance")]
[DefaultValue("")]
[Localizable(true)]
public string Text
{
get
{
String s = (String)ViewState["Text"];
return ((s == null) ? String.Empty : s);
}
set
{
ViewState["Text"] = value;
}
}
protected override void OnLoad(EventArgs e)
{
SPList list = SPContext.Current.List;
if (list != null)
{
list.Fields.Add(XXX, XXX, XXX);
list.Update();
}
}
}
I cannot see what benefit I have from creating custom action for posts list. Both posts are helpful, but I'll probably create custom feature for that.