I would like to create a custom Button component with three labels: left-, center-, and right-justified. I can't just use the label justification property, because I want to use all 3 labels at the same time.
I'm familiar with creating custom components, but I've never tried to build one quite like this before...
Here's what I have so far:
<?xml version="1.0" encoding="utf-8"?>
<s:Button
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark">
<fx:Script>
<![CDATA[
private var labelContentL:String;
private var labelContentC:String;
private var labelContentR:String;
public function set labelL(value:String):void
{
labelContentL = value;
}
public function set labelC(value:String):void
{
labelContentC = value;
}
public function set labelR(value:String):void
{
labelContentR = value;
}
public function get labelL():String
{
return labelContentL;
}
public function get labelC():String
{
return labelContentC;
}
public function get labelR():String
{
return labelContentR;
}
]]>
</fx:Script>
<s:Label id="l" width="100%" text="{labelContentL}" textAlign="left" paddingLeft="10" />
<s:Label id="c" width="100%" text="{labelContentC}" textAlign="center" />
<s:Label id="r" width="100%" text="{labelContentR}" textAlign="right" paddingRight="10" />
</s:Button>
The labels won't change after the button is created, so I'm not worried about the missing [Bindable] metadata.
I'm stuck right now, getting the following compiler error:
Multiple initializer values for default property, 'label', of type 'String'.
...for each of the 3 <s:Label> lines.
Based on this answer to a similar question, I tried adding label="" to my <s:Button> declaration, but that just adds another error.
How do I fix this?
Your problem is that a tag named label under the button tag isn't an item of type label, it's the label used on the button, and is of type string.
Why not do it as a skin, rather than a custom component?
<?xml version="1.0" encoding="utf-8"?>
<s:Skin xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx">
<!-- host component -->
<fx:Metadata>
[HostComponent("ThreeLabelButton")]
</fx:Metadata>
<!-- states -->
<s:states>
<s:State name="disabled" />
<s:State name="down" />
<s:State name="over" />
<s:State name="up" />
</s:states>
<s:Rect bottomLeftRadiusX="5"
bottomRightRadiusX="5"
topLeftRadiusX="5"
topRightRadiusX="5"
top="0" left="0" right="0" bottom="0">
<s:fill>
<s:SolidColor color.up="#CCCCCC" color.over="#555555" color.down="#888888" />
</s:fill>
</s:Rect>
<mx:Label id="leftLabel" text="{hostComponent.leftText}" left="0" />
<mx:Label id="rightLabel" text="{hostComponent.rightText}" right="0" />
<mx:Label id="centerLabel" text="{hostComponent.centerText}" left="{(this.width - centerLabel.width) / 2}" />
</s:Skin>
This will work with this class:
import mx.controls.Label;
import spark.components.supportClasses.ButtonBase;
public class ThreeLabelButtonComponent extends ButtonBase
{
public function ThreeLabelButtonComponent()
{
super();
}
[SkinPart]
public var leftLabel:Label;
[SkinPart]
public var rightLabel:Label;
[SkinPart]
public var centerLabel:Label;
[Bindable]
public var leftText:String;
[Bindable]
public var rightText:String;
[Bindable]
public var centerText:String;
protected override function partAdded(partName:String, instance:Object):void
{
super.partAdded(partName, instance);
if(instance === leftLabel)
{
leftLabel.text = leftText;
}
if(instance === rightLabel)
{
rightLabel.text = rightText;
}
if(instance === centerLabel)
{
centerLabel.text = centerText;
}
}
}
Related
I have a datagrid with 2 columns. I need to access the item renderer of the second column whenever column-1 value changes. i.e if value of column 1 is A- need to display text field in column2 and if value is B, i need to show dropdown.
Col1----------Col2
A ---------- DropDown
B ---------- TextBox
A ---------- DropDown
Any Solutions???
public class ItemRendererfroDropTest extends GridItemRenderer
{
private var dropdown:DropDownList;
public function ItemRendererfroDropTest()
{
super();
dropdown=new DropDownList();
dropdown.dataProvider=new ArrayCollection(new Array("ABC","PQR"));
this.addElement(dropdown);
dropdown.addEventListener(FlexEvent.VALUE_COMMIT,dataChanged);
}
private function dataChanged(event:FlexEvent):void
{
owner.dispatchEvent(new CustomEvent(CustomEvent.DATA_CHANGED,true));
}
}
public class ItemRenderlabel extends GridItemRenderer
{
public var wlabel:Label=new Label();
public var checkbox:CheckBox=new CheckBox();
public function ItemRenderlabel()
{
super();
this.addEventListener(CustomEvent.DATA_CHANGED,mappingChanged,true);
this.addElement(wlabel);
}
private function mappingChanged(e:CustomEvent):void
{
Alert.show("asfAS");
}
}
You can get some idea from below code: - Event you can do it only for particular item by using dpHierarchy.itemUpdated() by updating individual row.
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600"
creationComplete="init()">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.events.FlexEvent;
[Bindable]
private var dpHierarchy:ArrayCollection= new ArrayCollection([
{name:"A", region: "Arizona"},
{name:"B", region: "Arizona"},
{name:"C", region: "California"},
{name:"D", region: "California"}
]);
private function init():void
{
this.addEventListener(FlexEvent.DATA_CHANGE, changeDataHandler);
}
private function changeDataHandler(event:FlexEvent):void
{
dpHierarchy.refresh();
}
]]>
</fx:Script>
<mx:AdvancedDataGrid id="myADG"
width="100%" height="100%"
variableRowHeight="true" dataProvider="{dpHierarchy}">
<mx:columns>
<mx:AdvancedDataGridColumn dataField="name" headerText="Name" itemRenderer="AddComboboxADG"/>
<mx:AdvancedDataGridColumn dataField="region" headerText="Region" itemRenderer="SelectedCustomComponent"/>
</mx:columns>
</mx:AdvancedDataGrid>
</s:Application>
//AddComboboxADG custom item renderer
<?xml version="1.0" encoding="utf-8"?>
<s:MXAdvancedDataGridItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
focusEnabled="true">
<fx:Script>
<![CDATA[
import mx.controls.AdvancedDataGrid;
import mx.controls.advancedDataGridClasses.AdvancedDataGridColumn;
import mx.controls.advancedDataGridClasses.AdvancedDataGridHeaderRenderer;
import mx.controls.advancedDataGridClasses.AdvancedDataGridListData;
import mx.events.AdvancedDataGridEvent;
import mx.events.DataGridEvent;
import mx.events.FlexEvent;
import mx.events.ItemClickEvent;
import spark.components.supportClasses.ItemRenderer;
import spark.events.IndexChangeEvent;
protected function comboBoxID_changeHandler(event:IndexChangeEvent):void
{
data.name = comboBoxID.selectedItem;
dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE, true));
}
override public function set data(value:Object):void
{
super.data = value;
if(data.name == "A")
{
comboBoxID.selectedIndex = 0;
}else if(data.name == "B")
{
comboBoxID.selectedIndex = 1;
}else if(data.name == "C")
{
comboBoxID.selectedIndex = 2;
}else
{
comboBoxID.selectedIndex = 3;
}
}
]]>
</fx:Script>
<s:DropDownList id="comboBoxID" change="comboBoxID_changeHandler(event)">
<s:ArrayCollection>
<fx:String>A</fx:String>
<fx:String>B</fx:String>
<fx:String>C</fx:String>
<fx:String>D</fx:String>
</s:ArrayCollection>
</s:DropDownList>
</s:MXAdvancedDataGridItemRenderer>
//SelectedCustomComponent custom item renderer
<?xml version="1.0" encoding="utf-8"?>
<s:MXAdvancedDataGridItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
focusEnabled="true">
<fx:Script>
<![CDATA[
override public function set data(value:Object):void
{
super.data = value;
customFirstDropDown.visible = customTextInput.visible = customSecondDropDown.visible = lblData.visible = false;
if(data.name == "A")
{
customFirstDropDown.visible = true;
}else if(data.name == "B")
{
customTextInput.visible = true;
}else if(data.name == "C")
{
customSecondDropDown.visible = true;
}else
{
lblData.visible = true;
}
}
]]>
</fx:Script>
<s:DropDownList id="customFirstDropDown" visible="false" selectedIndex="0">
<s:ArrayCollection>
<fx:String>First</fx:String>
<fx:String>Second</fx:String>
<fx:String>Third</fx:String>
</s:ArrayCollection>
</s:DropDownList>
<s:TextInput id="customTextInput" visible="false" text="Selected"/>
<s:DropDownList id="customSecondDropDown" visible="false" selectedIndex="0">
<s:ArrayCollection>
<fx:String>1</fx:String>
<fx:String>2</fx:String>
<fx:String>3</fx:String>
</s:ArrayCollection>
</s:DropDownList>
<s:Label id="lblData" visible="false" text="Selected"/>
</s:MXAdvancedDataGridItemRenderer>
Create Item renderer using ViewStack with views as required, and override data function as normally done for item renderer, to set active child of container base of field, If you wants to set Active child on fly see this post
Hopes that helps
I have a custom Flex 4.5 component (to be used in a List) -
Game.mxml (represents a clickable playing table in a card game):
<?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"
xmlns:comps="*"
width="160" height="160"
autoDrawBackground="false"
creationComplete="init(event)">
<fx:Metadata>
[Event(name="pref_event", type="PrefEvent")]
</fx:Metadata>
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
private var _data:XML;
[Bindable("dataChange")]
public override function get data():Object {
return _data;
}
public override function set data(value:Object):void {
_data = value as XML;
if (null == _data)
return;
gameid = _data.#id;
for (var i:uint = 0; i < 3; i++) {
this['_user' + i].data = _data.elements('user')[i];
}
dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE));
}
public function init(event:FlexEvent):void {
_rect.filters = Util.SHADOW;
addEventListener(MouseEvent.CLICK, handleClick);
}
private function handleClick(event:MouseEvent):void {
trace("Clicked: " + gameid);
dispatchEvent(new PrefEvent(PrefEvent.GAME_CLICKED, gameid, true));
}
public function set gameid(str:String):void {
_gameid.text = '#' + str;
}
public function get gameid():String {
return _gameid.text.substring(1);
}
]]>
</fx:Script>
<s:Rect id="_rect" x="20" y="20" width="120" height="120" radiusX="16" radiusY="16">
<s:stroke>
<s:SolidColorStroke color="#4F69B5" weight="4"/>
</s:stroke>
<s:fill>
<s:SolidColor color="#CCDDEE" />
</s:fill>
</s:Rect>
<s:Label id="_gameid" x="0" y="75" width="{width}" fontSize="16" textAlign="center" color="#FFFFFF" />
<comps:User id="_user0" x="56" y="4" scaleX=".3" scaleY=".3" interactive="false" visible="false" />
<comps:User id="_user1" x="108" y="60" scaleX=".3" scaleY=".3" interactive="false" visible="false" />
<comps:User id="_user2" x="4" y="60" scaleX=".3" scaleY=".3" interactive="false" visible="false" />
</s:ItemRenderer>
and I'm trying to dispatch a custom event from it - PrefEvent.as:
package {
import flash.events.Event;
public class PrefEvent extends Event {
public var str:String;
public static const GAME_CLICKED:String = 'game_clicked';
public static const CARD_CLICKED:String = 'card_clicked';
public static const CARD_PLAYED:String = 'card_played';
public function PrefEvent(type:String, n:String, bubbles:Boolean = false, cancelable:Boolean = false){
super(type, bubbles,cancelable);
str = n;
}
public override function clone():Event {
return new PrefEvent(type, str, bubbles, cancelable);
}
public override function toString():String {
return str;
}
}
}
And here is finally my test code - GameTest.mxml:
<?xml version="1.0" encoding="utf-8"?>
<s:Application
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:comps="*"
width="700" height="525"
creationComplete="init()">
<fx:Script>
<![CDATA[
public function init():void {
game.data = <game id="8946">
<user id="OK353118989212"/>
<user id="OK351923295875"/>
</game>;
}
private function gameClicked(event:PrefEvent):void {
trace("game clicked: " + event);
}
]]>
</fx:Script>
<comps:Game id="game" x="0" y="0" pref_event="gameClicked(event)" />
</s:Application>
But I never see the "game clicked: " trace. Does anybody please know why?
I'm probably missing something minor, because I can see the "Clicked: 8946" trace.
Problem:
In Game.mxml File has "pref_event" Event
[Event(name="pref_event", type="PrefEvent")]
but your dispatching PrefEvent.GAME_CLICKED ("game_clicked").
Solution:
You must dispatch the right event for this.
dispatchEvent( new PrefEvent("pref_event",gameid, true) );
[Event(name="pref_event", type="PrefEvent")] != new PrefEvent(PrefEvent.GAME_CLICKED, gameid, true)
The name of the events are not the same.
You should write this in your Game.mxml file :
<fx:Metadata>
[Event(name="game_clicked", type="PrefEvent")]
</fx:Metadata>
And this in your main file :
<comps:Game id="game" x="0" y="0" game_clicked="gameClicked(event)" />
See here for further informations...
Is there a direct way to get the item index of the data inside an itemRenderer? I need to display the item number against each item. I am currently doing a workaround and won't allow reuse of my itemRenderer component.
var index:int = model.dataColl.getItemIndex(data) + 1;
itemNo = index.toString();
This is what i am using now, it works, but the concepts of component reuse and data abstraction are compromised.
I am using Flex 3.
The first answer seems to be working, but slow. It takes O(n^2) time as it runs through dataProvider array to get item index each time.
We can access rowIndex from listData - it represents index of the current visible item. Vertical scroll position from parent List represents the amount of scrolled items:
<?xml version="1.0" encoding="utf-8"?>
<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml" implements="mx.controls.listClasses.IDropInListItemRenderer">
<mx:Script>
<![CDATA[
import mx.controls.listClasses.BaseListData;
import mx.controls.listClasses.ListBase;
[Bindable] private var index:int = 0;
private var _listData:BaseListData;
public function get listData():BaseListData
{
return _listData;
}
public function set listData(value:BaseListData):void
{
_listData = value;
}
override public function set data(value:Object):void
{
super.data = value;
if (data && listData)
index = _listData.rowIndex + ListBase(_listData.owner).verticalScrollPosition;
else
index = 0;
}
]]>
</mx:Script>
<mx:Label text="{index}"/>
<mx:Label text="{data.toString()}"/>
</mx:HBox>
You can use labelFunction to solve your problem. For the simple test application:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application creationComplete="init()" layout="absolute" xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
private static const DATA_LENGTH:Number = 100;
[Bindable]
private var dp:ArrayCollection;
private function countFinction(item:Object):String
{
return (dp.getItemIndex(item) + 1).toString();
}
private function init():void
{
var dataArray:Array = [];
for (var i:int = 0; i < DATA_LENGTH; i++)
{
var item:Object = { firstName: "First" + (i + 1), lastName: "Last" + (i + 1) };
dataArray.push(item);
}
dp = new ArrayCollection(dataArray);
}
]]>
</mx:Script>
<mx:List dataProvider="{dp}" height="500" horizontalCenter="0" itemRenderer="TestRenderer"
labelFunction="countFinction" verticalCenter="0" width="500" />
</mx:Application>
You can use the following test renderer:
<?xml version="1.0" encoding="utf-8"?>
<mx:HBox implements="mx.controls.listClasses.IDropInListItemRenderer" xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
import mx.controls.listClasses.BaseListData;
private var _listData:BaseListData;
/**
* #inheritDoc
*/
public function get listData():BaseListData
{
return _listData;
}
[Bindable(event = "listDataChanged")]
/**
* #inheritDoc
*/
public function set listData(value:BaseListData):void
{
if (value == _listData)
return;
_listData = value;
dispatchEvent(new Event("listDataChanged"));
}
]]>
</mx:Script>
<mx:Label fontWeight="bold" text="{listData.label}" />
<mx:Label text="{data.firstName + ' ' + data.lastName}" />
</mx:HBox>
Item renderer has an item index property on it.
this.itemIndex
Sample:
<s:List>
<s:dataProvider>
<s:ArrayList>
<fx:String>Item 1</fx:String>
<fx:String>Item 2</fx:String>
<fx:String>Item 3</fx:String>
<fx:String>Item 4</fx:String>
<fx:String>Item 5</fx:String>
<fx:String>Item 6</fx:String>
</s:ArrayList>
</s:dataProvider>
<s:itemRenderer>
<fx:Component>
<s:ItemRenderer>
<s:HGroup>
<s:Label text="This index is: {this.itemIndex}" />
<s:Label text="{data}" />
</s:HGroup>
</s:ItemRenderer>
</fx:Component>
</s:itemRenderer>
</s:List>
I am using the following .as file as the component for an itemrenderer in a list. Basically each item is rendered in a TextInput and each TextInput has a remove button as you can see from the code. When clicking the remove button, I want to remove the selectedItem..so I am putting the function removeItem() in MainMxml.xml and calling it from the .as file.
However I am getting an error "Cannot access a method or property of a null object reference". Can you help me out with this error?
The .as file as follows:
package components {
import flash.events.Event;
import flash.events.MouseEvent;
import mx.events.FlexEvent;
import renderers.TextInputRenderer;
import spark.components.Button;
import spark.components.TextInput;
import spark.events.TextOperationEvent;
public class ClearableTextInput extends TextInput {
[SkinPart(required="true")]
public var clearButton:Button;
[Bindable]
public var mainMxml:MainMxml;
public function ClearableTextInput() {
super();
//watch for programmatic changes to text property
this.addEventListener(FlexEvent.VALUE_COMMIT, textChangedHandler, false, 0, true);
//watch for user changes (aka typing) to text property
this.addEventListener(TextOperationEvent.CHANGE, textChangedHandler, false, 0, true);
}
private function textChangedHandler(e:Event):void {
if (clearButton) {
clearButton.visible = (text.length > 0);
}
}
private function clearClick(e:MouseEvent):void {
mainMxml.removeItem();
}
override protected function partAdded(partName:String, instance:Object):void {
super.partAdded(partName, instance);
if (instance == clearButton) {
clearButton.addEventListener(MouseEvent.CLICK, clearClick);
clearButton.visible = (text != null && text.length > 0);
}
}
override protected function partRemoved(partName:String, instance:Object):void {
super.partRemoved(partName, instance);
if (instance == clearButton) {
clearButton.removeEventListener(MouseEvent.CLICK, clearClick);
}
}
}
}
And the ItemRenderer is as such:
<?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" xmlns:components="components.*" width="100%">
<s:layout>
<s:HorizontalLayout/>
</s:layout>
<fx:Script>
<![CDATA[
import mx.core.EdgeMetrics;
import mx.core.UIComponent;
import skins.ClearableTextInputSkin;
]]>
</fx:Script>
<components:ClearableTextInput id="clearTxt" text="{data.label}" skinClass="skins.ClearableTextInputSkin" />
</s:ItemRenderer>
I am also setting the clear button in the ClearableTextInputSkin which is shown below:
<?xml version="1.0" encoding="utf-8"?>
<s:SparkSkin
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
minWidth="100" minHeight="22"
alpha.disabled="0.5"
blendMode="normal">
<fx:Metadata>
[HostComponent("components.ClearableTextInput")]
</fx:Metadata>
<!-- states -->
<s:states>
<s:State name="normal"/>
<s:State name="disabled"/>
</s:states>
<!-- bg -->
<s:Rect id="border" left="0" right="0" top="0" bottom="0" radiusX="3">
<s:fill>
<s:SolidColor id="bgFill" color="#ffffff" />
</s:fill>
<s:stroke>
<s:SolidColorStroke id="borderStroke" color="#333333" weight="1" />
</s:stroke>
</s:Rect>
<!-- text -->
<s:RichEditableText id="textDisplay" left="4" right="24" top="1" bottom="0"
color="#333333"
verticalAlign="middle" />
<s:Button id="clearButton" right="4" verticalCenter="0" />
</s:SparkSkin>
Your help would be much appreciated.
Many thanks.
The answer can be very very long :)
Starting with your code the problem is in line:
mainMxml.removeItem();
Your mainMxml instance is null and that's why you have your NPE (null pointer exception).
But the code in general shows that you currently don't understand Flex, especially data binding. And of course have problems with app's architecture.
First, your line:
[Bindable]
public var mainMxml:MainMxml;
doesn't do anything.
Data binding is just a way to listen changes of variable annotated with [Bindable] metatag. [Bindable] is not a kind of dependency injection but completely opposite of it.
So nobody set the value of your mainMxml field. And to tell the true, it is not the right way to try to inject value there. Instead of that, you should use Observer design pattern and fire event from your component:
package events {
public class ClearableTextInputEvent extends Event {
public static const PERFORM_CLEAR:String = "performClear";
public function ClearableTextInputEvent(type:String) {
super(type);
}
}
}
So now the component:
package components {
import flash.events.Event;
import flash.events.MouseEvent;
import mx.events.FlexEvent;
import renderers.TextInputRenderer;
import spark.components.Button;
import spark.components.TextInput;
import spark.events.TextOperationEvent;
[Event(name="performClear", type="events.ClearableTextInputEvent")]
public class ClearableTextInput extends TextInput {
[SkinPart(required="true")]
public var clearButton:Button;
public function ClearableTextInput() {
super();
//watch for programmatic changes to text property
this.addEventListener(FlexEvent.VALUE_COMMIT, textChangedHandler, false, 0, true);
//watch for user changes (aka typing) to text property
this.addEventListener(TextOperationEvent.CHANGE, textChangedHandler, false, 0, true);
}
private function textChangedHandler(e:Event):void {
if (clearButton) {
clearButton.visible = (text.length > 0);
}
}
private function clearClick(e:MouseEvent):void {
dispatchEvent(new ClearableTextInputEvent(ClearableTextInputEvent.PERFORM_CLEAR));
}
override protected function partAdded(partName:String, instance:Object):void {
super.partAdded(partName, instance);
if (instance == clearButton) {
clearButton.addEventListener(MouseEvent.CLICK, clearClick);
clearButton.visible = (text != null && text.length > 0);
}
}
override protected function partRemoved(partName:String, instance:Object):void {
super.partRemoved(partName, instance);
if (instance == clearButton) {
clearButton.removeEventListener(MouseEvent.CLICK, clearClick);
}
}
}
}
Ok. Now our renderer. There we should use event bubbling to inform our list container (I suppose it is an instance of MainMxml) about need to remove row. We should create an event class for that.
NB. You can use the same event class but the problem is your ClearableTextInput component and item renderer have different responsibilities
and ClearableTextInput can be used again in some other renderers. It is good practice to create different events for the different layers of your application for low coupling:
package events {
public class RemoveRowEvent extends Event {
public static const REMOVE_CURRENT_ROW:String = "removeCurrentRow";
public function RemoveRowEvent(type:String) {
super(type, true);
}
}
}
Now your renderer:
<?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" xmlns:components="components.*" width="100%">
<s:layout>
<s:HorizontalLayout/>
</s:layout>
<fx:Script>
<![CDATA[
import mx.core.EdgeMetrics;
import mx.core.UIComponent;
import skins.ClearableTextInputSkin;
]]>
</fx:Script>
<components:ClearableTextInput id="clearTxt" text="{data.label}" skinClass="skins.ClearableTextInputSkin" performClear="dispatchEvent(new RemoveRowEvent(RemoveRowEvent.REMOVE_CURRENT_ROW))" />
</s:ItemRenderer>
And finally in your list's container (MainMxml instance I suppose):
…
addEventListener(RemoveRowEvent.REMOVE_CURRENT_ROW, onRowRemove);
…
private function onRowRemove(event:RemoveRowEvent):void {
removeItem();
event.stopImmediatePropagation();
}
I wrote this draft in the browser so please fix imports etc. by yourself :)
I get this error when i launch the application(flash type with dismiss all and continue) and i'm out of ideas:
TypeError: Error #1009: Cannot access
a property or method of a null object
reference. at
Magazin/xmlService_resultHandler()[D:\Documents
and Settings\chechu\Adobe Flash
Builder 4\Magazin\src\Magazin.mxml:41]
at
Magazin/__xmlService_result()[D:\Documents
and Settings\chechu\Adobe Flash
Builder 4\Magazin\src\Magazin.mxml:64]
at
flash.events::EventDispatcher/dispatchEventFunction()
at
flash.events::EventDispatcher/dispatchEvent()
at
HTTPOperation/http://www.adobe.com/2006/flex/mx/internal::dispatchRpcEvent()[E:\dev\4.x\frameworks\projects\rpc\src\mx\rpc\http\HTTPService.as:989]
at
mx.rpc::AbstractInvoker/http://www.adobe.com/2006/flex/mx/internal::resultHandler()[E:\dev\4.x\frameworks\projects\rpc\src\mx\rpc\AbstractInvoker.as:318]
at
mx.rpc::Responder/result()[E:\dev\4.x\frameworks\projects\rpc\src\mx\rpc\Responder.as:56]
at
mx.rpc::AsyncRequest/acknowledge()[E:\dev\4.x\frameworks\projects\rpc\src\mx\rpc\AsyncRequest.as:84]
at
DirectHTTPMessageResponder/completeHandler()[E:\dev\4.x\frameworks\projects\rpc\src\mx\messaging\channels\DirectHTTPChannel.as:446]
at
flash.events::EventDispatcher/dispatchEventFunction()
at
flash.events::EventDispatcher/dispatchEvent()
at flash.net::URLLoader/onComplete()
The main application:
import events.ProductEvent;
import mx.collections.ArrayCollection;
import mx.controls.Alert;
import mx.events.FlexEvent;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import valueObject.ImageClass;
[Bindable]
public var imagesCollection:ArrayCollection;
protected function xmlService_faultHandler(event:FaultEvent):void
{
Alert.show("meeah");
}
protected function xmlService_resultHandler(event:ResultEvent):void
{
var imageCollection:ArrayCollection=event.result.Images.image ;
var imData:ImageClass;
for each(var im:Object in imageCollection)
{
imData=new ImageClass();
imData.url=im.url;
imData.big_url=im.big_url;
imData.cat=im.cat;
imData.descript=im.descript;
imData.price=im.price;
imagesCollection.addItem(imData); line:41
}
Alert.show("gg");
}
[Bindable]
public var mama:ArrayCollection=new ArrayCollection();
protected function gallery1_addToCartHandler(event:ProductEvent):void
{
mama.addItem(event);
currentState="cart";
}
]]>
</fx:Script>
<s:states>
<s:State name="normal"/>
<s:State name="cart"/>
</s:states>
<fx:Declarations>
<s:HTTPService id="xmlService" line 64
url="dataXml/pics.xml" fault="xmlService_faultHandler(event)"
result="xmlService_resultHandler(event)"/>
</fx:Declarations>
<components:Gallery imagesArray="{imagesCollection}" addToCart="gallery1_addToCartHandler(event)" />
<components:cart x="500" y="300" itemRenderer="components.CartRenderer" dataProvider="{mama}" />
</s:Application>
The Gallery component:
<?xml version="1.0" encoding="utf-8"?>
<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" >
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Metadata>
[Event(name="addToCart",type="events.ProductEvent")]
</fx:Metadata>
<s:layout>
<s:VerticalLayout/>
</s:layout>
<fx:Script>
<![CDATA[
import events.ProductEvent;
import mx.collections.ArrayCollection;
import valueObject.ImageClass;
[Bindable]
public var imagesArray:ArrayCollection;
public function goa_clickHandler(event:MouseEvent):void
{
for (var i:uint=0; i<imagesArray.length;i++)
{
var objEvent:ProductEvent=new ProductEvent("addToCart",true);
objEvent.imData=imagesArray[i] ;
dispatchEvent(objEvent);
}
}
]]>
</fx:Script>
<s:SkinnableDataContainer id="cont" dataProvider="{imagesArray}">
<s:itemRenderer >
<fx:Component>
<s:ItemRenderer>
<s:HGroup>
<s:Label id="gagal" text="{data.price}"/>
<mx:Image source="{data.url}" width="50" height="50" />
<s:Button id="goa" label="buy" click="outerDocument.goa_clickHandler(event)"/>
</s:HGroup>
</s:ItemRenderer>
</fx:Component>
</s:itemRenderer>
</s:SkinnableDataContainer>
</s:Group>
The ProductEvent class:
package events
{
import flash.events.Event;
import valueObject.ImageClass;
[Bindable]
public class ProductEvent extends Event
{
public var imData:ImageClass;
public function ProductEvent(type:String,bubbles:Boolean=true)
{
super(type,bubbles);
}
override public function clone():Event
{
var eventOb:ProductEvent=new ProductEvent(type,bubbles);
eventOb.imData=this.imData;
return eventOb;
}
}
The ImageClass:
package valueObject
{
[Bindable]
public class ImageClass
{
public var url:String;
public var big_url:String;
public var descript:String;
public var price:String;
public var cat:String;
public function ImageClass()
{
/*this.url=url;
this.big_url=big_url;
this.descript=descript;
this.price=price;
this.cat=cat;*/
}
}
}
and the xml:
<?xml version="1.0"?>
<Images>
<image>
<url>poze/pics/IMG_1163.jpg</url>
<big_url>poze/big_pics/IMG_1163.jpg </big_url>
<descript>P</descript>
<price>99.99</price>
<cat>A</cat>
</image>
<image>
<url>poze/pics/IMG_1175.jpg</url>
<big_url>poze/big_pics/IMG_1175.jpg</big_url>
<descript>U</descript>
<price>99.99</price>
<cat>A</cat>
</image>
<image>
<url>poze/pics/IMG_1186.jpg</url>
<big_url>poze/big_pics/IMG_1186.jpg</big_url>
<descript>L</descript>
<price>99.99</price>
<cat>A</cat>
</image>
You're accessing imagesCollection variable in line 41, but haven't initialized it - it still contains null
Either change
[Bindable]
public var imagesCollection:ArrayCollection;
To
[Bindable]
public var imagesCollection:ArrayCollection = new ArrayCollection();
or add
imagesCollection = new ArrayCollection();
to the beginning of xmlService_resultHandler method.
Remember...
Whenever there is a null reference error then you need to initialize the variable that you are using.
Say if you are using Array or ArrayCollection
you need to do: yourArrayVarriable = new Array(); inside the function or
you need to do: yourArrayCollectionVarriable = new ArrayCollection(); inside the function
and then start using yourArrayVarriable or yourArrayCollectionVarriable to store the values