itemEditEnd for FLex 4.5 - apache-flex

In version 4 there is a Flex itemEditEnd (in Datagrid) event, but does not exist in Flex 4.5, itemEditEnd this event has been replaced by what event?

The MX DataGrid should not have changed; and according to the documentation, the itemEditEnd is still there.
However, Flex 4.5 introduced a DataGrid based on the Spark Architecture. This is a complete new component, and has many differences from the MX DataGrid.
You might look at the gridItemEditorSessionSave event as an alternate.

As per http://opensource.adobe.com/wiki/display/flexsdk/Data+Grid+Editing
I tried to use:
override public function save():void
{
//data.dataField = value;
}
But I got error: "Incopatible override"
Any success at your side?
FIX,change void to Boolean, than in save() you can do pretty much the same stuff as in itemEditEnd in MX DataGrid:
override public function save():Boolean
{
data.dataField = value;
return true; //to save data to dataprovider
}
Example:
<s:GridItemEditor>
<s:TextInput id="valueDisplay" width="100%"/>
<fx:Script>
<![CDATA[
override public function get value():Object
{
return valueDisplay.text;
}
override public function set value(newValue:Object):void
{
valueDisplay.text = newValue.toString();
}
override public function save():Boolean
{
data.dataField = value;
return true;
}
]]>
</fx:Script>
</s:GridItemEditor>

Related

How can you programmatically make the "mouseover" item into the selected item in a tree/list?

I would like to programmatically change a selected item, in a tree or list, to the item currently "marked/focused" under the mouse pointer .
I'm working with an Flex Air standalone application.
I was thinking in the lines of:
myTree.selectedItem = EVENT.TARGET (where EVENT could be a mouseover/rightclick/rollOver event, and TARGET should be the node/item currently under the mouse pointer).
Is there a way of doing this (or in any other way)?
Ahh, and i want to do it without left clicking ;-)
Thank you in advance,
Sebastian
I found this interesting enough so I am asking if this is the easiest way to achieve this. First off, instead of the list, you need to add the rollOver-listener to the ItemRenderer, not to the list itself (as the event.target and event.currentTarget will just show your list).
So lets create a custom ItemRenderer and add a rollOver listener
<xml version="1.0" encoding="utf-8"?>
<s:ItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
autoDrawBackground="true" height="20" rollOver="itemrenderer1_rollOverHandler(event)">
<fx:Script>
<![CDATA[
protected function itemrenderer1_rollOverHandler(event:MouseEvent):void
{
this.dispatchEvent(new CustomEvent(CustomEvent.SELECT_ITEM, data, true));
}
]]>
<s:Label id="label1" text="{data.label}"/>
</s:ItemRenderer>
You need to somehow get the value of the selected item (which is the data on the itemRenderer) so I created a CustomEvent-class just to do so.
package
{
import flash.events.Event;
public class CustomEvent extends Event
{
public var selectedItem:Object;
public static const SELECT_ITEM:String = "selectItem";
public function CustomEvent(type:String, selectedItem:Object, bubbles:Boolean=false, cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
this.selectedItem = selectedItem;
}
}
}
then I added a eventListener to the main class and set the list.selectedItem property accordingly:
//for the main MXML initializer:
this.addEventListener(CustomEvent.SELECT_ITEM, rollOverChangeSelected);
//and the function:
protected function rollOverChangeSelected(ce:CustomEvent):void{
list.selectedItem = ce.selectedItem;
}
Another way: bindable variable
The list:
s:List id="list" allowMultipleSelection="true" selectionColor="red" rollOverColor="red" itemRenderer="customItemRenderer" selectedItem="{_rollOverSelectedItem}">
The variable and set / get methods:
[Bindable] public var _rollOverSelectedItem:Object;
public function get rollOverSelectedItem():Object
{
return _rollOverSelectedItem;
}
public function set rollOverSelectedItem(value:Object):void
{
_rollOverSelectedItem = value;
}
and the ItemRenderer's rollOver-method:
protected function itemrenderer1_rollOverHandler(event:MouseEvent):void
{
this.parentApplication.rollOverSelectedItem = data;
}
What is the best/proper way?

Databinding from the Skin to the View in Flex (Spark)

I'm pretty new to Flex development in Spark and wanted to clarify the best way to build components.
I had previously tried to use a binding expression to set the selected index of a ViewStack:
public class MyComponentView extends SkinnableComponent
{
public var selectedIndex:int = 0;
}
<s:Skin ...>
<mx:ViewStack selectedIndex="{hostComponent.selectedIndex}">
....
</mx:ViewStack>
</s:Skin ...>
However, this binding expression doesn't appear to work, although it does show the correct number if I move that binding expression to a s:Label.
In order to get this to work I changed the code thus:
public class MyComponentView extends SkinnableComponent
{
[SkinPart(required = "true")]
public var myStack:ViewStack;
private var _selectedIndex:int = 0;
private var _indexChanged:Boolean;
public function set selectedHistoryIndex(value:int):void
{
_selectedIndex = value;
_indexChanged = true;
invalidateProperties();
}
override protected function partAdded(partName:String, instance:Object):void
{
super.partAdded(partName, instance);
switch (instance)
{
case myStack:
_indexChanged = true;
invalidateProperties();
break;
}
}
override protected function commitProperties():void
{
super.commitProperties();
if (_indexChanged && myStack)
{
_indexChanged = false;
myStack.selectedIndex = _selectedIndex;
}
}
}
<s:Skin ...>
<mx:ViewStack id="myStack">
....
</mx:ViewStack>
</s:Skin ...>
Is this the way I'm meant to do it?
As for me, your second way is more preferable. I'd rather change some code to make it better:
public function set selectedHistoryIndex(value:int):void
{
if (_selectedIndex == value)
return;
_selectedIndex = value;
_indexChanged = true;
invalidateProperties();
}
Yes, you can bind to component's properties from skin but this way View (skins in Spark architecture is for View in MVC and host component is for M and C) has knowledge about M which isn't good. The first implementation requires this knowledge from skin.
The second implementation makes View true View (managed by M). And it is good.

Capture Event Generated By Value Object in Component

Let me try to explain this the best I can.
I have a component containing a data grid with the following data grid column
<mx:DataGridColumn dataField="email" headerText="Email Address">
<mx:itemRenderer>
<mx:Component>
<mx:VBox width="100%" horizontalScrollPolicy="off" verticalScrollPolicy="off" horizontalAlign="center">
<mx:TextInput id="tiEmailAddress"
width="95%"
text="{data.email}"
invalid="{data.isNotValidEmail(event);}"
valid="{data.isValidEmail(event);}"/>
<mx:EmailValidator id="validatorEmailAddress"
source="{tiEmailAddress}"
property="text"
required="true"
requiredFieldError="Email address is required."/>
</mx:VBox>
</mx:Component>
</mx:itemRenderer>
</mx:DataGridColumn>
Here's the guts of my value object:
[Bindable(event="contactChange")]
public class ContactVO extends EventDispatcher
{
private var _email:String = "default email";
public var selected:Boolean = false;
public var edited:Boolean = false;
public var isNew:Boolean = false;
public var isValid:Boolean = false;
public function ContactVO()
{ }
public function isNotValidEmail(e:Event):void
{
isValid = false;
email = e.target.text;
}
public function isValidEmail(e:Event):void
{
isValid = true;
email = e.target.text;
}
public function get email():String
{
return _email;
}
public function set email(value:String) : void
{
if (value != _email) {
_email = value;
edited = true;
}
dispatchEvent(new Event("contactChange", true));
}
}
Then back in the component, I have this which gets called at creationComplete
addEventListener("contactChange", processContactChange);
Through the Flex debugger, I can see the addEventListener statement called when the component is created, I can see the event fired from the value object when the validation is performed and the value changes, but processContactChange is never called, so I assume the event is never making it to my component.
Any idea what I've gotten wrong here? Thanks!
[SOLUTION]
The conversation with #Flextras below helped to deepen my understanding of this process and figure out where the disconnect was in my understanding. Basically, I changed the innards of my component's data column entry to the following:
<mx:TextInput id="tiEmailAddress"
width="95%"
text="{data.email}"
invalid="{data.isNotValidEmail(event);}"
valid="{data.isValidEmail(event);}"
creationComplete="{addListener(data)}"/>
<mx:Script>
<![CDATA[
private function addListener(data:Object):void
{
var eventDispatcher:EventDispatcher = data as EventDispatcher;
eventDispatcher.addEventListener("contactChange", outerDocument.processContactChange);
}
]]>
</mx:Script>
and removed the event listener from my creationComplete method
When you add the event listener you have to add it to the class that fires the event. In your code, you're adding it to the component that contains the DataGrid. Neither the itemRenderer, DataGrid, nor component containing the DataGrid fire the event.
IF the component containing the DataGrid has access to the ContactVO event, you can listen directly on that.
Otherwise, you can add an event listener in your itemRenderer.
If you absolutely need to execute code in the component containing the DataGrid, then the itemRenderer should listen on the Value Object for the event, then fire an event of it's own. Make sure that the 'itemRenderer' event bubbles; and it will move up the display hierarchy.

Flash Builder 4 "includeIn" property causing design view error

I am creating a custom TextInput component that will define an "error" state. I have extended the TextInput class to change the state to "error" if the errorString property's length is greater than 0. In the skin class, I have defined an "error" state, and added some logic to detect the size and position of the error icon. However, if I have this code at the same time I use the "includeIn" property in the bitmap image tag, I get a design view error. If I either A) Only include that code with no "includeIn" property set, it works or B) dont include the code to set the icon size and position and only use the "includeIn" property, it works. Any ideas what could be causing the design view problem when I use both the "includeIn" property and the icon size/position code at the same time?
TextInput Class:
package classes {
import spark.components.TextInput;
public class TextInput extends spark.components.TextInput {
[SkinState("error")];
public function TextInput() {
super();
}
override public function set errorString( value:String ):void {
super.errorString = value;
invalidateSkinState();
}
override protected function getCurrentSkinState():String {
if (errorString.length>0) {
return "error";
}
return super.getCurrentSkinState();
}
}
}
TextInput Skin File:
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
//THIS IS THE CODE THAT SEEMS TO BE CAUSING THE PROBLEM
if(getStyle("iconSize") == "large") {
errorIcon.right = -12;
errorIcon.source = new errorIconLg();
} else {
errorIcon.right = -5;
errorIcon.source = new errorIconSm();
}
super.updateDisplayList(unscaledWidth, unscaledHeight);
}
</fx:Script>
<s:states>
<s:State name="normal"/>
<s:State name="disabled"/>
<s:State name="error"/>
</s:states>
//If I remove the problem code above or if I take out the includeIn
//property here, it works
<s:BitmapImage id="errorIcon" verticalCenter="0" includeIn="error" />
</s:SparkSkin>
In Flex 4, a component is only instantiated when the state it's in is activated. Therefore, when the skin first loads, errorIcon is a null reference. Its instantiation is deferred until the error state becomes active. In order to instantiate it immediately, you set the itemCreationPolicy="immediate" property on it.
<s:BitmapImage id="errorIcon"
source="../images/error.png"
itemCreationPolicy="immediate"
/>

Unable to bind to property of custom component

Can't seem to bind to data from within a custom component. I've tried BindUtilis and {} but can't seem to fathom it out. Here's what I've got:
I have a class DataModel which has been made bindable
Within Mainn.mxml I have two components: DataGrid (used for testing) & CustomComponent (which extends Canvas)
When the data within DataModel.somelist is updated the DataGrid reflects the changes but the CustomComponent doesn't appear to.
I was expecting to see the trace (CustomComponent.dataProvider) fired whenever this._dataModel.itemList is changed. What am I doing wrong?
Main.mxml looks something like this:
<mx:Script>
<![CDATA[
import model.DataModel;
[Bindable]
private var _dataModel:DataModel = DataModel.getInstance();
]]>
</mx:Script>
<mx:VBox width="100%" height="100%">
<views:ItemDisplayList width="100%" height="300" id="idl" >
<views:dataProvider>
{this._dataModel.itemList}
</views:dataProvider>
</views:ItemDisplayList>
<mx:DataGrid id="dg" width="100%" height="300" >
<mx:dataProvider>
{this._dataModel.itemList}
</mx:dataProvider>
</mx:DataGrid>
</mx:VBox>
The CustomComponent has this AS class:
package code{
import model.DataModel;
import mx.containers.Canvas;
public class CustomComponent extends Canvas{
[Bindable]
private var _dataModel:DataModel = DataModel.getInstance();
private var _dataProvider:ArrayCollection ;
public function CustomComponent(){
super();
_dataProvider = new ArrayCollection();
trace("CustomComponent start");
}
public function get dataProvider() : ArrayCollection {
trace("get dataProvider");
return _dataProvider;
}
public function set dataProvider(value: ArrayCollection) : void {
trace("set dataProvider");
this._dataProvider = value;
invalidateProperties();
invalidateSize();
invalidateDisplayList();
dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE));
}
...
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number ) : void{
trace("updateDisplayList");
super.updateDisplayList(unscaledWidth, unscaledHeight);
}
}
}
Are you resetting the dataProvider in your custom component, or by updating you mean adding/removing an item from dataModel.itemList?
If you're adding an item to dataModel.itemList, then your custom component wont update but the DataGrid will. This is because in the DataGrid (maybe it's in the core ListBase component in Flex?), when you set dataProvider, it adds an event listener for CollectionEvent.COLLECTION_CHANGE. When it hears that (when something in your ArrayCollection/IList changes, from remove/add/refresh/etc), it runs a few methods to update the DataGrid's itemRenderers. You'll have to code that manually if you create your own dataProvider property.
If you set the dataProvider explicitly on your custom component (custom.dataProvider = myArrayCollection), it will update. But that's not the most efficient thing to do. I recommend extending one of the List classes that has already implemented a dataProvider property, it's a lot to tackle.
Check out the source for the mx ListBase's dataProvider method to see what they did.
Hope that helps,
Lance

Resources