Flex, filter tree using ITreeDataDescriptor without reload and close all nodes - apache-flex

working with flex 4 and implementing filtering using ITreeDataDescriptor.
Filtering worked as expected like the following code snippet
however, i am trying to create a on-demand type of filter where user would type in the search clause in a TextInput, and as the user types, the tree nodes would be filtered on the fly.
my implementation now is have user click on the search button and then reapply the dataDescriptor then reload the data. A big problem is that the whole tree collapses after setting the dataProvider again.
any suggestion on how the on-demand filter can be achieved?
var dataFilter:ServicePricingFilter = new MyFilter();
dataFilter.searchString = this.txtSearchKeyword.text;
this.treeService.dataDescriptor = new MyDataDescriptor(dataFilter);
this.treeService.dataProvider = getTreeData();

I think you shouldn't set the dataprovider every time but use insted the filterFunction property available for ListCollectionView classes (ArrayCollection, XMLListCollection,...)

looked at the post Florian mentioned.
on-demand filtering is done by using ITreeDataDescriptor and a filterFunction.
on keyup event of the search TextInput, call invalidateList() function on the tree.
see the source code provided for the following post
http://www.davidarno.org/2009/04/01/how-to-filter-all-nodes-of-a-flex-tree-component/

Related

Modify editability of a field from field group by code

I have this piece of code:
controlDetails = this.form().design(1).addControl(FormControlType::Group, #quickCreateDetails);
controlDetails.dataSource(fbds.id());
controlDetails.dataGroup(#quickCreateDetails);
controlDetails.frameType(10);
controlDetails.autoDataGroup(true);
controlDetails.hideIfEmpty(false);
controlDetails.columns(2);
I want to modify the editability of one certain field on that dataGroup, but I don't know how to do it with code or in the AOT (DS). Seems like Im pretty much limited...
You have next options:
change Form Data Source filed editability
via AOT - https://msdn.microsoft.com/EN-US/library/aa860145.aspx
via code FormDataSource.object: InventTrans_ds.object(fieldNum(InventTrans, Qty)).allowEdit(false)
change child control design property. addControl returns FormBuildGroupControl. Then you have to loop through controlNum(), find correct design control and cast it to one of FormBuildControl nested type with data bounding. There you have allowEdit method.

Making sure my extended lists always show "current" data?

When you create a Data Extender for a CME list – for instance to add a column for the Schema as in this example – it all works fine and dandy whenever you do actions that force a List reload.
However, some actions don’t force a list reload (like editing a component in a folder, then saving & closing) and it looks like Anguilla is loading the data for the item that changed using a different mechanism that loads only the data for the item in question (which makes sense).
If I would want my extended list view to behave properly and also load my additional attributes whenever a given item changes (instead of only when the list view is reloaded) what else do I need to do?
I found how Anguilla takes care of this. When you implement a Data Extender, you are extending the information regarding the items displayed in the list, which basically means that you are extending the Data (Model) behind the item in question.
Each Item in Tridion has its own class in the Anguilla Framework, for example a Component has its own Tridion.ContentManager.Component javascript "class".
Having said this, and going back to the example that shows the schema name of the component, we are not actually extending the model, since that information is already available in the component. However, we need to overwrite the methods exposed on each used for displaying information in the lists the item is in, in this case a Component.
So, when we deal with a Data Extender, if we want a full implementation of this functionality, we not only need to define the data extender:
<ext:dataextender
name="IntelligentDataExtender"
type="Com.Tridion.PS.Extensions.IntelligentDataExtender,PS.GUI.Extensions">
<ext:description>Shows extra info</ext:description>
</ext:dataextender>
But also we need to define what's the column we are adding:
<ext:lists>
<ext:add>
<ext:extension name="IntelligentColumnExtender"
assignid="IntelligentDataColumnExtender">
<ext:listDefinition>
<ext:selectornamespaces/>
<ext:columns>
<column
xmlns="http://www.sdltridion.com/2009/GUI/extensions/List"
id="IntelligentData"
type="data"
title="Additional Info"
selector="#ExtendedInfo"
translate="String"/>
</ext:columns>
</ext:listDefinition>
<ext:apply>
<ext:view name="DashboardView" />
</ext:apply>
</ext:extension>
</ext:add>
</ext:lists>
Once we have this, the GUI will display the column we just added: "Additional Info"
Well, now we need to achieve the list refreshing when the item is edited/checked-out and in, etc...
For that, we need to extend the model and implement a few methods in the Object we are extending. In this example I am extending the Page object, so whenever a page is edited, the row in the list we want to update gets refreshed, together with the rest of the cells in the table.
To extend the model we need to define what types are we extending, in this example I am going to use the "Page" class as an example. First of all you need to define the model extension in the config file of your Editor:
<cfg:group name="Com.Tridion.PS.Extensions.UI.Model"
merger="Tridion.Web.UI.Core.Configuration.Resources.DomainModelProcessor"
merge="always">
<cfg:domainmodel name="Com.Tridion.PS.Extensions.UI.Model">
<cfg:fileset>
<cfg:file type="script">/Scripts/PSPage.js</cfg:file>
</cfg:fileset>
<cfg:services />
</cfg:domainmodel>
</cfg:group>
and
<ext:modelextensions>
<cfg:itemtypes>
<cfg:itemtype id="tcm:64" implementation="Com.Tridion.PS.Extensions.UI.PSPage" />
</cfg:itemtypes>
</ext:modelextensions>
As you can see I am extending the Page by using the "Com.Tridion.PS.Extensions.UI.PSPage" class that is defined in the Javascript file "/Scripts/PSPage.js".
The only method that handles the row refreshing is the following:
Com.Tridion.PS.Extensions.UI.PSPage.prototype.getListItemXmlAttributes
= function PSPage$getListItemXmlAttributes(customAttributes) {
var attribs = {};
var p = this.properties;
if (customAttributes) {
for (var attr in customAttributes) {
attribs[attr] = customAttributes[attr];
}
}
//This adds my custom column back when the item is updated
attribs["ExtendedInfo"] = p.extendedInfo;
return this.callBase(
"Tridion.ContentManager.Page",
"getListItemXmlAttributes",
[attribs])
};
As you can see I am implementing the "ExtendedInfo" attribute which is the one displayed in my additional column.
There's more than just adding a Data Extender when dealing with adding a column to our lists. I will write a post in my blog here to provide with a fully working example.
I hope it makes sense.
Well, Jaime correctly described how CME updates changed items in Lists. But I want to add some additional information on how List controls, domain model List and Items are interact with each other. This might help you building your own extension with similar functionality.
Most of the domain model List items inherit from Tridion.ContentManager.ListTcmItems class. On the moment when any List item, based on mentioned class, is loaded it will be registered in Lists Registry (and un-registered when the List is unloaded). This will allow Model to use registered Lists as source of static data for Items and to update changed Items data in these Lists.
Update Item static data
For example, we have loaded ListCategories and there is only one Category in the List:
var pub = $models.getItem("tcm:0-1-1");
var list = pub.getListCategories();
list.load();
// After list is loaded
list.getXml();
That getXml() returns an XML like (simplified):
<tcm:ListCategories>
<tcm:Item ID="tcm:1-4-512" Type="512" Title="Keys" />
</tcm:ListCategories>
After that, if you try to get some static data for Category "Keys" it will be already set:
var category = $models.getItem("tcm:1-4-512");
category.isLoaded(); // return false
category.isStaticLoaded(); // return false
category.getTitle(); // return undefined
category.getStaticTitle(); // return "Keys"!
That is possible because $models.getItem call will do two things: it will return an existing (or create a new) domain model object and call $models.updateItemData method with it. This method call will go through all registered Lists in the Lists Registry and for all Lists whose TimeStamp bigger than Item's Last Update TimeStamp will call list.updateItemData with the model object.
The updateItemData method will check if the passed Item is in the list and if it is, then the Item will be updated with the static data that is available from the List.
Updating data of changed Items in the List
When a domain model Item is modified (updated, removed, created new) one of these methods is called:
$models.itemUpdated
$models.itemRemoved
These methods will go through the Lists in Lists Registry and call list.itemUpdated (or list.itemRemoved). These methods will check is passed Item is contained in their List and if so they will update the List xml from the Item data.
For that purpose there is a getListItemXmlNode method in the Tridion.ContentManager.Item class. This method will build List xml node based on the array of attributes, provided by getListItemXmlAttributes method on the Item. That's what Jaime mentioned in his answer.
If the List xml was updated, one of these events will be fired on List object:
itemadd
itemupdate
itemremove
Listening to these events on a List object in your view will allow you to timely update your List Control.
So if you want this mechanism to work with your extension, stick to these rules:
If you are creating new domain model List object - it should inherit Tridion.ContentManager.ListTcmItems class or it should implement the getId(), itemUpdated(item), itemsUpdated(item), itemRemoved(item) and updateItemData(item) methods
If you want to see changes in List control - attach handlers to corresponding events on the domain model List object and update your List control
If you are creating new domain model Item - it should inherit Tridion.ContentManager.Item class and you should improve getListItemXmlAttributes method to return correct array of attributes for the List
The CME will indeed update the items in the list dynamically after the save occurs, without going to the server.
To do so, it calls a method named "getListItemXml" which returns the update XML element for the list. It will then update or add this element, which will update or add the item in the list view.
getListItemXml is a method of the different Model objects.
So how do you take advantage of this? I'm not sure.
Perhaps you could overwrite the method (or maybe getListItemXmlAttributes is best) with your own to add the additional data?
There is also an "itemupdate" event fired whenever an item is updated in the list.
You can hook into that by doing something like this:
var myEventHandler = function(event)
{
$log.message("Item updated. TridionEvent object passed: " + event);
}
var view = $display.getView();
var list = view.getListObject("uri-of-Folder");
list.addEventListener("itemupdate", myEventHandler);
I suppose you could use that to update the list entry for the item after the fact.
Be sure to call removeEventHandler at some point too.
None of this is optimal, obviously.
But I don't know of any extension point that would solve this particular problem.
I think I would (attempt to) implement this by monitoring the items in a folder periodically and updating that list after this polling mechanism had detected a change in that folder.
For example, I would write some javascript timeout or interval that runs in the background and checks the items in the current folder. If it detects a change, it triggers the update of the list.
Alternatively, you could also try to intercept the action that changed your list (e.g. the creation of a new item), maybe by means of an event system, and as such update your list. I don't think this is much different than the first approach, as I think it still implies some level of polling from the GUI side.

Flex: DataGrid and the Command Pattern

I am using a command pattern, so any changes to object state need to happen within a command execution. A normal itemeditor in a DataGrid would just make its changes on the underlying bound object, but I need to intercept that change and make it use a command.
I'm pretty new to flex, so I'm looking for ideas of how to implement this. A basic example is that I have an object with a "date" field. In the datagrid I am using a flex "DateField" component as the itemeditor. When I select a new date, I don't want it to update the datasource, I want it to call a different method where I can access the newly selected value and pass it to a command to execute. Any tips would be greatly appreciated. Thanks in advance.
Use the itemEditBegin and/or itemEditEnd events on the DataGrid and build your command in the handler. This page has a few examples of capturing the edit operation with those events.
In my opinion, you're over-engineering this to hell, to the point that it becomes unusable. Why would you need a command to just change data on the fly? I've been doing Flex for 3 years and I yet to see it done this way. The only time commands are used is for receiving information from the server.
Either way, if you really want to implement it (against my recommendation), you would probably want to do event bubbling with a controller listening higher up the display list for the event, then from there trigger a command. From within the item renderer:
this.dispatchEvent(new Event('someEvent', true));
And then higher up the display list:
dataGrid.addEventListener('someEvent', someEventHandler);
And within the handler you can run the command.

list box control in asp.net

Hello friends I have a list box control in my asp.net project.
I want to know how to get selected index to set currently updated item in database.
Please help me with this. Do i need to perform some data base operation to find the key for currently updated data and then i'll have to set it or there exist some property to deal with this? thanks in adavance
One thing to watch out for, which I have come accross more than once is that if you call your CompanyListBox() method in your Page_Load method, you will lose the selected index unless it is only called on the first page load. To make sure of this, place your call to CompanyListBox() within the following block:
if(!Page.IsPostBack)
{
CompanyListBox();
}
You can access the selected index in your postback by using the following code:
var id = (Int32)listCompany.SelectedItem.Value
Then it is up to you to use that in your data access to update the record in the database. Looks to me that you are using some kind of framework or manager class for your database access. The companyManager should have methods for saving your updated item to the database. Good luck.

DataGrid crash on CompositeCollection Edit

I have a DataGrid.
It's ItemsSource is bound to the ModelView's CompositeCollection through the ViewModel.
The CompositeCollection consists of 2 ObservableCollections.
The display on the grid is fine. I am able to see the collection.
However, when I try to edit one of the rows, I get a crash (NotSupportedException) of:
"'EditItem' is not allowed for this view"
How do I make the rows editable? I need to be able to edit the ModelViews representing each row in the Grid.
Here is the CompositeCollection Property code that I use to bind to the itemssource:
this isn't the exact code since I am not allowed to post the exact code but it is the same logic on how I make the collection
public CompositeCollection ModelViewsCollection
{
get
{
CollectionContainer modelViewContainer;
CompositeCollection modelViewCollection = new CompositeCollection();
modelViewContainer= new CollectionContainer();
modelViewContainer.Collection= this.ModelViewCollection;
modelViewCollection .Add(modelViewContainer);
modelViewContainer= new CollectionContainer();
modelViewContainer.Collection= this.ModelViewCollection2;
modelViewCollection .Add(modelViewContainer);
return modelViewCollection;
}
}
CompositeCollection does not implement IEditableCollectionView which is used by the datagrid to edit.
I have had the same issues, and ended up doing my own fake composite collection on the view model, similiar to what you have, if all you are putting in your collection is two observable collections, its not to hard to track the changes listening to collection changed on both of them. and make your viewmodels collection consist of both of them
You could even do the dirty hack that i did, of rebuilding the ObservableCollection that the grid binds to every time one of the collections change (not elegant i know, but ill go back and optimise when i get time.. i.e. never) With a linq query this stuff is really easy.
Otherwise maybe you could derive from CompositeCollection and try and add the IEditableCollectionView, if you get that working be sure to let me know.
here is the same question on the datagrid forum

Resources