How to do an if-else on a Flex component, based on the value of an object's member? - apache-flex

When I click a row in the datagrid, the "PeopleDetails" state is loaded. On the details state, I have a checkbox. This checkbox was automatically generated when I created the form. This is because the field in the People table is Boolean.
I actually do not want to have a checkbox, but I want the value Yes/No printed next to the label.
So I write some AS code embedded in the MXML code:
<s:Form includeIn="PeopleDetails">
<s:FormItem label="Is Present?">
<fx:Script>
<![CDATA[
if(person!= null ){
if(person.present==true){
Alert.show("Test - Yes");
}
}
else{
Alert.show("No");
}
]]>
</fx:Script>
<s:CheckBox id="personCheckBox2" enabled="false" selected="{person.present}"/>
</s:FormItem>
</s:Form>
Just for testing purposes, I have Alert popups. Eventually, I would change to printing to screen the values Yes/No.
The problem:
-I do not know how to test whether the attribute present in the object person is true or false.
In the above, I get a FB complaint 1120: Access of Undefined property person.
If I remove the AS code, the checkbox works fine. The checkbox uses person.present to know whether it should be checked or not. Why cannot I use person.present to do the if-else test?
I would appreciate any help on this.

If I'm reading you right, you want a Yes/No value rather than a checkbox, this should do it for you:
Change :
<s:CheckBox id="personCheckBox2" enabled="false" selected="{person.present}"/>
To :
<s:Label text="{(person.present)?'Yes':'No'}"/>
So the new form looks like this :
<s:Form includeIn="PeopleDetails">
<s:FormItem label="Is Present?">
<s:Label text="{(person.present)?'Yes':'No'}"/>
</s:FormItem>
</s:Form>

You can't place ActionScript code on a script manner within Script tag. You can only place properties and methods there following OOP way. So put your code inside a method and call this method as a reaction on some event (creationComplete for example).

I hope the above code is part of an ItemRenderer.
Just move the above Script block code to set data() function as below:
override public function set data( value: Object ): void
{
super.data = value;
if(person != null ){
if(person.present==true){
Alert.show("Test - Yes");
}
}
else{
Alert.show("No");
}
}
Also, I don't think Alert will work in Itemrenderer, so replace it with trace("") statement. Hope it helped.

Related

Using a ComboBox as ItemEditor in Flex 4

I have a simple DataGrid with data. Of one of the columns, I want to use a ComboBox to edit the field, instead of the standard edit box.
How do I do that? I have tried all kind of things I found on the internet, but they all fail in simply updating the value. I'd say it shouldn't be too hard to do this.
I'm actually in the process of doing this myself, and with the spark:DataGrid it actually gets a bit easier than halo - but both follow the same setup / architecture.
Start with:
spark.components.gridClasses.ComboBoxGridItemEditor;
Depending on the nature of your data setup and/or how prolific this kind of editing will be for your application, you can write it inline as most documentation will suggest within a <fx:component>, or simply subclass this (although behind the scenes these are the same thing - the later being much easier to reuse). The data for the combo in my scenario is a sub selection of a bigger parent object, so I chose to make it easier on myself and add an additional property dataField to mimic other renderer / editors - in what actually shows in just the cell itself (when not in editing mode).
A basic setup looks something more or less like this (at least mine does):
public class AccountComboEditor extends ComboBoxGridItemEditor
{
private _dataField:String;
public function AccountComboEditor()
{
super();
//note - typically you wouldn't do "logic" in the view but it's simplified as an example
addEventListener(FlexEvent.CREATION_COMPLETE, onCreationComplete);
}
public function get dataField():String { return _dataField; }
public function set dataField(value:String):void
{
if (_dataField !=value) //dosomeadditionalvalidation();
_dataField = value;
}
override public function prepare():void
{
super.prepare();
if (data && dataField && comboBox) comboBox.labelField = data[dataField];
}
protected function onCreationComplete(event:FlexEvent):void
{
//now setup the dataProvider to your combo box -
//as a simple example mine comse out of a model
dataProvider = model.getCollection();
//this isn't done yet though - now you need a listener on the combo to know
//what item was selected, and then get that data_item (label) back onto this
//editor so it has something to show when the combo itself isn't in
//editor mode
}
}
So the real take away is to setup the labelField of the combobox, either internally in the subclass or externally if you need to expose it as an additional property.
The next part is to use this as part of the mx.core.ClassFactory for the actual data grid. A simple view would look like something similar:
<s:DataGrid>
<fx:Script>
private function getMyEditor(dataField:String):ClassFactory
{
var cf:ClassFactory = new ClassFactory(AccountComboEditor);
cf.properties = {dataField : dataField };
return cf;
}
</fx:Script>
<s:columns>
<mx:ArrayList>
<s:GridColumn itemEditor="{getMyEditor('some_data_property')}" />
</mx:ArrayList>
</s:columns>
</s:DataGrid>
This Creating item renderers... doc will give you more info.
I figured it out. I just wanted a simple drop down box, instead of a text-editing field.
The following code does want I want:
<mx:DataGridColumn dataField="type" headerText="Type" editorDataField="value">
<mx:itemEditor>
<fx:Component>
<mx:ComboBox>
<mx:dataProvider>
<fx:String>Gauge</fx:String>
<fx:String>Graph</fx:String>
<fx:String>Indicator</fx:String>
</mx:dataProvider>
</mx:ComboBox>
</fx:Component>
</mx:itemEditor>
</mx:DataGridColumn>

How to access (reference) custom itemRenderer in TileList?

I have a TileList cp with 10 item.
How can i call a function in 4. item (for example) from outside, where i created a TileList cp?
Thanks
UPDATE:
Based on your comments, this should be even easier. You would just loop through each of the rows in your List's dataProvider and make whatever changes are necessary. At the end of the function just call the refresh function on the ArrayCollection. Using my example below:
public function myFunction(evt:Event):void
{
for each (var o:MyObject in myDataProvider)
{
o.someProperty = "Updated";
}
myArrayCollection.refresh();
}
After the ArrayCollection is updated, calling the refresh function should cause the List to refresh its item renderers as well.
ORIGINAL ANSWER:
It sounds like you want to call a function outside your ItemRenderer when a button or something is clicked in the ItemRenderer but still be able to access the data for the item that was clicked.
Assuming I've got this correct, you still wouldn't need to access the ItemRenderer itself. You can just do something like this (just a rough example):
<fx:Script>
<![CDATA[
public function myFunction(evt:Event):void
{
trace(MyObject(myList.selectedItem).someProperty.toString());
}
]]>
</fx:Script>
<mx:List id="myList" dataProvider="{myDataProvider}" >
<mx:itemRenderer>
<fx:Component>
<mx:CheckBox selectedField="IsSelected" change="outerDocument.myFunction(event);" />
</fx:Component>
</mx:itemRenderer>
</mx:List>
If you do need to pass a completely separate parameter that's not stored in your List's dataProvider, just pass it as an argument to the change eventHandler.
change="outerDocument.myFunction(event, someOtherValue);"

Gettinf Property **text** not found error in DataGrid

I am using a custom itemEditor for my DataGrid.
The itemEditor has a simple TextField as a component.
But when ever i am clicking the cell i am getting an error as :
ReferenceError: Error #1069: Property text not found on editors.customItemEditor and there is no default value.
at mx.controls::DataGrid/itemEditorItemEditEndHandler()[C:\autobuild\3.2.0\frameworks\projects\framework\src\mx\controls\DataGrid.as:4827]
Plz help me to resolve this issue.
My Opinion is that the error is coming for the "text" field. But i havn't accessed "text" field or used it anywhere in my code.
Regards,
Ravi
i solved the problem with commiting incluting "return data["selected"].toString()" into get text:
<mx:DataGridColumn dataField="selected" rendererIsEditor="true" >
<mx:itemRenderer>
<fx:Component>
<mx:Box styleName="" width="100%" height="100%" backgroundAlpha="0.0"
horizontalAlign="center" verticalAlign="middle">
<fx:Script>
<![CDATA[
public function get text():String
{
return data["selected"].toString();
}
public function set text(value:String):void
{
}
protected function checkbox1_clickHandler(event:MouseEvent):void
{
data["selected"]=event.target["selected"];
}
]]>
</fx:Script>
<mx:CheckBox selected="{data.selected}" click="checkbox1_clickHandler(event)"/>
</mx:Box>
</fx:Component>
</mx:itemRenderer>
</mx:DataGridColumn>
Taken from http://livedocs.adobe.com/flex/3/html/help.html?content=celleditor_8.html
By default, Flex expects an item editor to return a single value to the list-based control. You use the editorDataField property of the list-based control to specify the property of the item editor that contains the new data. Flex converts the value to the appropriate data type for the cell.
The default item editor is a TextInput control. Therefore, the default value of the editorDataField property is "text", which corresponds to the text property of the TextInput control. If you specify a custom item editor, you also set the editorDataField property to the appropriate property of the item editor
I am still Struck in this error but i think there is a hope of coming out of it.... :)
If we take TextInput as the itemEditor, like:
dataGridColumn.itemEditor = new ClassFactory(TextInput);
Then there was no problem as "text" is being internally defined inside TextInput.as.
On a similar note if i copy set text and get text inside our custom Editor, then its working fine.. only problem that is
coming is regarding commiting the changed values.
I am working on it, hope i'll be able to sort it out.
P.S.: I am a newbie here, so plz pardon me of any idiotic RnD... :p

detect change in Flex Form elements (textinput, textarea, combobox, checkbox..)

I have various (read lots of..) flex forms in my app, and I now want to create a system by which the user is notified that he hasn't saved if he modifies anything in the form...
now.. I don't want to rewrite every form I have.. is there some nice way to implement this?
Extending the TextInput (and others..) classes somehow?
thanks
This is not really thought through but should work.
You could create a custom component, let's call it FormWatcher which you would than put next to your Form. What the form watcher would do is wait for the CreationComplete event from the form.
So now, as we have the Form ready you can use the getChildren() method of the form to get all the FormItems in it. Than look inside each of them, and you will get TextInputs, Comboboxes, etc. to which you can add event listeners (as individual components), eg.
// THIS IS WHERE THE COMPONENT SHOULD START
protected function changeHandler(event:Event):void
{
trace ("something is dirty");
}
protected function startWatching(passTheFormHere:Form):void
{
for each (var O:Object in passTheFormHere.getChildren())
{
if (O is FormItem)
{
// Let's assume you only have a single child in one FormItem
// and always one child for simplicity
addChangeHandlerFor((O as FormItem).getChildAt(0));
}
}
}
protected function addChangeHandlerFor(someComponent:Object):void
{
// Most regular flex components send a Event.CHANGE event
// when their value is changing
// keep in mind you should check stuff, this is a simple example
(someComponent).addEventListener(Event.CHANGE,changeHandler);
}
// THIS IS WHERE THE COMPONENT SHOULD END
Just paste this code next to some form, and call the startWatching(nameOfYourForm), you should see the changeHandler is being called.
A few more notes:
1) You should clean up the event listeners once you're done.
2) I would create a component out of it so that you would use it like this:
<mx:Form id="form1" >
[...]
</mx:Form>
<FormWatcher form="{form1}" />
Where FormWatcher would have a Boolean var called "clean" or something.
3) The example is very simple, so it will only work for forms similiar to this one:
<mx:Form id="myForm" >
<mx:FormItem>
<mx:TextInput id="someComponent1" />
</mx:FormItem>
<mx:FormItem>
<mx:CheckBox id="someComponent2" />
</mx:FormItem>
<mx:FormItem>
<mx:TextArea id="someComponent3" />
</mx:FormItem>
</mx:Form>
You could go into the TextInput class (and others) and add that event listener and function, but then you would be changing the SDK itself, which is kind of a bad idea. I would create custom classes extending the ones your using and do a find/replace to make it faster.

Flex Localization: refresh DataProvider values

I have a ToggleButtonBar with a DataProvider setup like this:
<mx:ToggleButtonBar itemClick="clickHandler(event);" selectedIndex="0">
<mx:dataProvider>
<mx:String>{resourceManager.getString('dashboard','daily')}</mx:String>
<mx:String>{resourceManager.getString('dashboard','monthly')}</mx:String>
<mx:String>{resourceManager.getString('dashboard','quarterly')}</mx:String>
<mx:String>{resourceManager.getString('dashboard','yearly')}</mx:String>
</mx:dataProvider>
</mx:ToggleButtonBar>
To switch locale to Chinese, I have a combobox with this handler:
resourceManager.localeChain = "zh_CN";
My problem is that on locale change, while the labels for all the other controls on the screen dynamically reload for the new locale, the dataProvider values don't refresh.
I can manually reset them in code, but is there a cleaner solution?
I would abstract out the data for your data provider into a bindable variable, then just reset the data provider when you change locals.
<mx:Script>
<![CDATA[
[Bindable]
myArray:Array = new Array(
[resourceManager.getString('dashboard','daily')]
, [resourceManager.getString('dashboard','monthly')]
, [{resourceManager.getString('dashboard','quarterly')]
, [resourceManager.getString('dashboard','yearly')]);
]]>
</mx:Script>
<mx:ToggleButtonBar itemClick="clickHandler(event);"
selectedIndex="0" id="myToggleButtonBar" dataprovider="{myArray}" />
Then you can just say
myToggleButtonBar.dataProvider = myArray;
after you swap the locals and it should work.
Disclaimer, there may be some minor errors in my code, I obviously am not able to test it and I don't have flex builder available right now to even check my syntax so I hope I didn't make any spelling mistakes. But this should get you in the ballpark.
Maybe if you make a getter bindable to a custom event for ex: "langChange"
[Bindable("langChange")]
public function get dataProviderToggleB():ArrayCollection
{
var arr :ArrayCollection = new ArrayCollection();
arr.addItem(resourceManager.getString('dashboard','daily'));
arr.addItem(resourceManager.getString('dashboard','monthly'));
return arr;
}
and in your "resourceManager.localeChain" setter you dispatch:
dispatchEvent(new Event("langChange"));
and you can used like this:
<mx:ToggleButtonBar dataProvider="{dataProviderToggleB} itemClick="clickHandler(event);" selectedIndex="0">
I hope this would help you.
You should keep 'daily', ... in your array and use a labelFunction to translate the label.
When the resourceManager sends a change event you should do a combo.labelFunction = labelFunction
The trick is to add brackets around each element in the dataProvider array, that way it gets parsed correctly. Note that this also binds correctly to locale changes in flex, no custom event dispatching is needed.
<mx:ToggleButtonBar itemClick="clickHandler(event);" selectedIndex="0"
dataProvider="{[ (resourceManager.getString('dashboard','daily')),
(resourceManager.getString('dashboard','monthly')),
(resourceManager.getString('dashboard','quarterly')),
(resourceManager.getString('dashboard','yearly')) ]}">
</mx:ToggleButtonBar>

Resources