In my flex app I have various custom components done with mxml or actionscript.
I want all of them to extend a base-class where I can define properties/event listeners etc.
Can someone give me an example how to create that base class and how I can extend it in mxml and actionscript components?
Maybe you could write a common interface for your components, just with the methods they need to implement
public interface ICustomComponent {
function doSomething():void;
// more methods here
}
And then in your AS components you just implement the ICustomComponent interface (or however you named it)
public class CustomButton extends Button implements ICustomComponent {
public function doSomething():void {
}
}
You can do this in MXML components too :
<mx:Button xmlns:mx="http://www.adobe.com/2006/mxml"
implements="ICustomComponent">
<mx:Script>
<![CDATA[
public function doSomething():void {
// blah blah
}
]]>
</mx:Script>
</mx:Button>
Just an idea. Hope it helps
Cheers
Creating the base class:
ActionScript
In BaseClass.as:
public class BaseClass
{
}
Extending from the base class:
ActionScript
public class SubClass extends BaseClass
{
}
MXML
In a file called SubClass.mxml:
<ns:BaseClass xmlns:ns="path.to.base.*">
</ns:BaseClass>
In the example below the component extends Form to create an address form .
Instead of form you can extend your own component .
When using actionscript i would suggest investigating flex components lifecycle for best performance :
http://livedocs.adobe.com/flex/3/html/help.html?content=ascomponents_advanced_2.html
mx:Form xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:MyComp="*"
<mx:FormItem label="NameField">
<mx:TextInput/>
</mx:FormItem>
<mx:FormItem label="Street">
<mx:TextInput/>
</mx:FormItem>
<mx:FormItem label="City" >
<mx:TextInput/>
</mx:FormItem>
<mx:FormItem label="State" >
<MyComp:StateComboBox/>
</mx:FormItem>
The following application file references the AddressForm component in the
AddressForm tag:
mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:MyComp="*"
<MyComp:AddressForm/>
/mx:Application
from http://livedocs.adobe.com/flex/3/html/help.html?content=mxmlcomponents_1.html
Related
I am currently trying to put together a simple Illustrator plugin, and coming from a design background this is proving to be quite a task, I have experience with JS, but not with Flex.
What I want to do is to have a panel in Illustrator, with an input field and a button. You type something in the input and press the button and a text frame with the desired text is added to the canvas.
But how do I pass the value from a mx:Textinput to the Controller.as file? I couldn't find an answer on the web.
This is my main.mxml file:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" historyManagementEnabled="false">
<mx:Script>
<![CDATA[
private var c:Controller = new Controller();
]]>
</mx:Script>
<mx:VBox height="100%" width="100%" verticalAlign="middle" horizontalAlign="center">
<mx:Label text="myVariable"></mx:Label>
<mx:TextInput name="TextValue"/> // I want the text value to be passed to the Controller class so I can pass it on to my JSX function
<mx:Button label="Run" click="c.run()"/>
</mx:VBox>
</mx:Application>
And this is my Controller.as file:
package
{
import flash.external.HostObject;
public class Controller
{
[ Embed (source="myScript.jsx" , mimeType="application/octet-stream" )]
private static var myScriptClass:Class;
public function run():void {
var jsxInterface:HostObject = HostObject.getRoot(HostObject.extensions[0]);
jsxInterface.eval( new myScriptClass ().toString());
//calling from AS to JSX
jsxInterface.myJSXFunction (myVariable); //This is where I want the value to be passed to
}
}
}
You might also pass the string directly to the c.run() call.
public function run(myString:String):void {
...
jsxInterface.myJSXFunction (myString)
...
and then
<mx:TextInput id="TextValue"/>
<mx:Button label="Run" click="c.run(TextValue.text)"/>
Just another approach.
Loic
First declare public property public var myTextValue : String; in your Controller.
Then declare bidirectional binding in your MXML <mx:TextInput text="#{c.myTextValue}"/>
Now you have myTextValue property always containing the actual value.
But bidirectional binding was introduced not that long time ago.
Alternatively, you can add a change event listener to your TextInput instance <mx:TextInput id="myTextInput" change="c.myTextValue = myTextInput.text"/>
I build a swc file by Flash CS5 contains some interface component like TextInput, Label.
And then I use it in a flex program.
But I meet the problem when I want use flex repeater for this component.
Following is the a component defined by myself in swc file using Flash CS.
package {
import fl.controls.TextInput;
......
public dynamic class MyWindow extends UIMovieClip {
public var txt1 : TextInput;
......
}
}
}
Then I use it in my flex program like this:
<local:MyWindow id="myWindow"/>
<fx:Script>
<![CDATA[
......
private function Init() : void {
myWindow.txt1.text = "myText";
}
......
]]>
</fx:Script>
it works well.
But how can I use txt1 in mxml directly? like this:
<local:MyWindow id="myWindow" txt1.text="myText"/>
I know it doesn't work, but I want use repeater to create some similar MyWindow, it need bind the dataProvider. I wrote flex code like this:
<mx:VBox>
<mx:Repeater x="10" y="10" id="multiWindow">
<local:MyWindow txt1.text="{multiWindow.currentItem}"/>
</mx:Repeater>
</mx:VBox>
But it can't work.
Does anyone know how to make it work? Thanks.
=================================================================================
Update code, multiWindow complete code is :
package {
import fl.controls.TextInput;
import mx.flash.UIMovieClip;
import flash.display.DisplayObject;
import flash.events.EventDispatcher;
import flash.display.Sprite;
import flash.display.InteractiveObject;
import flash.display.MovieClip;
import flash.display.DisplayObjectContainer;
public dynamic class MyWindow extends UIMovieClip {
public var txt1 : TextInput;
public var txt2 : TextInput;
public var txt3 : TextInput;
}
}
Any component that you want to use withing an MX container needs to implement IUIComponent. I believe that Flash has a built in base class that you can extend for use with Flex, but you can also just do something like this:
<mx:VBox>
<mx:Repeater x="10" y="10" id="multiWindow">
<mx:UIComponent>
<local:MyWindow txt1.text="{multiWindow.currentItem}"/>
</mx:UIComponent>
</mx:Repeater>
</mx:VBox>
Note that if you haven't given thought to the Flex Component life cycle and layout system, it might not play well.
I have a View class EmployeeList as follows:
<?xml version="1.0" encoding="utf-8"?>
<s:NavigatorContent xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:parsley="http://www.spicefactory.org/parsley"
xmlns:mx="library://ns.adobe.com/flex/mx" width="400" height="300">
<fx:Script>
<![CDATA[
import cafeparsley.model.EmployeeListPM;
[Inject]
[Bindable]
public var model : EmployeeListPM;
[Init]
public function init () : void {
model.init();
}
]]>
</fx:Script>
<s:Panel title="Employee List" horizontalCenter="0">
<s:HGroup paddingTop="50">
<s:Button label="Add New Employee" click="model.addNewEmployee()" />
<mx:Spacer width="100%" />
<s:Button label="Logout" click="model.logout()" />
<mx:Spacer width="100%" height="20" />
</s:HGroup>
<s:List id="empList" dataProvider="{ model.employees }" labelFunction="model.properName"
change="model.initUpdateEmployee(empList.selectedItem);empList.selectedIndex = -1;" width="100%" />
<s:Label id="error" color="0xFF0000" />
</s:Panel>
</s:NavigatorContent>
The PM looks like this:
package cafeparsley.model
{
import cafeparsley.events.EmployeeEvent;
import cafeparsley.events.NavigationEvent;
import cafeparsley.services.impl.EmployeeServiceImpl;
import cafeparsley.vo.Employee;
import flash.events.EventDispatcher;
import mx.collections.ArrayCollection;
import mx.rpc.IResponder;
[Bindable]
[Event(name="navigationEvent", type="cafeparsley.events.NavigationEvent")]
[ManagedEvents("navigationEvent")]
public class EmployeeListPM extends EventDispatcher implements IResponder
{
public var employeeService : EmployeeServiceImpl = new EmployeeServiceImpl();
public var employees : ArrayCollection;
public function init() : void
{
loadEmployees();
}
public function EmployeeListPM()
{
}
public function loadEmployees():void
{
employeeService.loadEmployees( this );
}
Regardless of whether I use or autowiring to perform injection, when I run this I get a the following error message:
TypeError: Error #1009: Cannot access a property or method of a null object reference.
at cafeparsley.view::EmployeeList/_EmployeeList_List1_i()[C:\dev\code\workspace\Examples\CafeParsley\src\cafeparsley\view\EmployeeList.mxml:29]
at cafeparsley.view::EmployeeList/_EmployeeList_Array2_c()
at mx.core::DeferredInstanceFromFunction/getInstance()[E:\dev\4.x\frameworks\projects\framework\src\mx\core\DeferredInstanceFromFunction.as:105]
at spark.components::SkinnableContainer/createDeferredContent()[E:\dev\4.x\frameworks\projects\spark\src\spark\components\SkinnableContainer.as:985]
at spark.components::SkinnableContainer/createContentIfNeeded()[E:\dev\4.x\frameworks\projects\spark\src\spark\components\SkinnableContainer.as:1014]
at spark.components::SkinnableContainer/createChildren()[E:\dev\4.x\frameworks\projects\spark\src\spark\components\SkinnableContainer.as:827]
at mx.core::UIComponent/initialize()[E:\dev\4.x\frameworks\projects\framework\src\mx\core\UIComponent.as:7349]
at mx.core::UIComponent/http://www.adobe.com/2006/flex/mx/internal::childAdded()[E:\dev\4.x\frameworks\projects\framework\src\mx\core\UIComponent.as:7241]
at mx.core::UIComponent/addChildAt()[E:\dev\4.x\frameworks\projects\framework\src\mx\core\UIComponent.as:6947]
at spark.components::Group/addDisplayObjectToDisplayList()[E:\dev\4.x\frameworks\projects\spark\src\spark\components\Group.as:1825]
at spark.components::Group/http://www.adobe.com/2006/flex/mx/internal::elementAdded()[E:\dev\4.x\frameworks\projects\spark\src\spark\components\Group.as:1416]
at spark.components::Group/setMXMLContent()[E:\dev\4.x\frameworks\projects\spark\src\spark\components\Group.as:512]
at spark.components::Group/set mxmlContent()[E:\dev\4.x\frameworks\projects\spark\src\spark\components\Group.as:452]
at spark.components::SkinnableContainer/set mxmlContent()[E:\dev\4.x\frameworks\projects\spark\src\spark\components\SkinnableContainer.as:604]
at spark.components::SkinnableContainer/createDeferredContent()[E:\dev\4.x\frameworks\projects\spark\src\spark\components\SkinnableContainer.as:986]
at spark.components::SkinnableContainer/createContentIfNeeded()[E:\dev\4.x\frameworks\projects\spark\src\spark\components\SkinnableContainer.as:1014]
at spark.components::SkinnableContainer/createChildren()[E:\dev\4.x\frameworks\projects\spark\src\spark\components\SkinnableContainer.as:827]
at spark.components::NavigatorContent/createChildren()[E:\dev\4.x\frameworks\projects\spark\src\spark\components\NavigatorContent.as:225]
at mx.core::UIComponent/initialize()[E:\dev\4.x\frameworks\projects\framework\src\mx\core\UIComponent.as:7349]
at cafeparsley.view::EmployeeList/initialize()
So the employeeListPM is null when the error is thrown. However if I comment out the
<s:List> component, rerun and set a breakpoint in the init method, init() will get called. So it's not that my context config is wrong, it's just that the PM hasn't been injected in time and the error is thrown. But according to the Parsley manual if I use autowiring or <parsley:configure/> the PM should be injected by the time it is needed.
I can't see what is I am doing wrong in what I thought was a relatively trivial dependency injection scenario. Can you help?
A couple of points here:
Your example doesn't include the <Configure /> or <FastInject /> tags, however since you mention them in your post I'll assume that they're just missing from the sample code. (If not, then you need to add one of these for this to work).
However, more likely, you have race conditions in your code.
Specifically, these lines:
labelFunction="model.properName"
change="model.initUpdateEmployee(empList.selectedItem);empList.selectedIndex = -1;"
Model is an injected property, however it's not guaranteed to have been injected at the time that the code is first run.
Instead, move the code into script within the class which performs a null check, and then defers logic back to the PM.
ie:
labelFunction="nameFunction"
private function nameFunction(item:Object):String
{
return (model) ? model.properName(item) : "";
}
Another thing to consider is that you are calling model.init() from the View's function marked with the Parsley [Init] metatag. I would suggest that you apply the same [Init] metatag to the init() method of the Model.
[Init]
public function loadEmployees():void {
employeeService.loadEmployees( this );
}
Do this rather than calling model.init() from the view. Altho the Lifecycle documentation states:
The methods marked with [Init] get invoked after the object has been instantiated and all injections have been processed.
I've had much more consistent results not calling any init() methods between injected objects directly but rather utilizing the metadata tag approach.
From this tutorial http://www.brighthub.com/internet/web-development/articles/11010.aspx
I found the code below. Is there a way to break this out so the mxml file just has the mxml, and the code between the script tags is put in an actionscript file?
Thanks.
-Nick
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute"
width="600"
height="400"
frameRate="100"
creationComplete="CreationComplete()"
enterFrame="EnterFrame(event)">
<mx:Script><![CDATA[
public function CreationComplete():void
{
}
public function EnterFrame(event:Event):void
{
}
]]></mx:Script>
</mx:Application>
There are several ways of achieving this in Flex:
Put the AS code in a .as file and use the "source=yourfile.as" attribute in the Script tag:
<mx:Script source="yourfile.as" />
You can also use the includes="yourfile.as" declaration w/in the Script tag:
<mx:Script
<![CDATA[
include "yourfile.as";
//Other functions
]]>
</mx:Script>
Use a Code-Behind pattern where you define the code in an AS file which extends the visual component you want your MXML file to extend. Then your MXML file simple extends the AS file and you have (via inheritance) access to all the code. It would look something like this (I'm not sure if this would work for the main MXML file which extends Application):
AS File:
package {
public class MainAppClass {
//Your imports here
public function CreationComplete():void {
}
public function EnterFrame(event:Event):void {
}
}
}
MXML File:
<component:MainAppClass xmlns:component="your namespace here"
xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute"
width="600"
height="400"
frameRate="100"
creationComplete="CreationComplete()"
enterFrame="EnterFrame(event)">
</component:MainAppClass>
Use a framework to inject the functionality you are looking for as a type of "model" which contains the data-functionality you will use. It would look something like this in Parsley:
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute"
width="600"
height="400"
frameRate="100"
creationComplete="model.CreationComplete()"
enterFrame="model.EnterFrame(event)">
<mx:Script>
<![CDATA[
[Inject]
[Bindable]
public var model:YourModelClass;
]]>
</mx:Script>
</mx:Application>
Two frameworks which come to mind which can help w/injection are Mate or Parsley.
I'm not sure if the code-behind pattern works with the main MXML file (which extends Application), so if you're having problems, you might try breaking out the content in your Main MXML file into a separate component which is included in Main. It might look something like this:
Main.mxml:
<mx:Application blah,blah,blah>
<component:YourComponent />
</mx:Application>
YourComponent.mxml:
<component:YourComponentCodeBehind creationComplete="model.creationComplete()"...>
//Whatever MXML content you would have put in the Main file, put in here
</component:YourComponentCodeBehind>
YourComponentCodeBehind.as
package {
class YourComponentCodeBehind {
//Whatever AS content you would have put in the Main .as file, put in here
}
}
From what I've been able to gather from Flex architecture, this is a very common way of setting up your application: your main MXML includes a single "view" which is the entry-point to the rest of your application. This view the contains all other views which comprise the app.
Hope that makes sense :)
I have several components where I want to enable buttons based on passing a username to a function. I want to dynamically bind the "enabled" property on a button so that if the "somethingChanged" event fires, a button may become enabled or disabled.
But, I'm not sure where to fire the "somethingChanged" event. It's possible that I may need to fire the "somethingChanged" event from several places in the application. Is this possible with a bound static function?
Am I going about this the right way or is there a better solution?
EventManager.as
public class EventManager():void
{
[Bindable(event="somethingChanged")]
public static function hasAccess(myVal:String):Boolean
{
}
}
testform1.mxml
<s:Button id="testButton1" enabled="{EventFunction.hasAccess("john")}" />
<s:Button id="testButton2" enabled="{EventFunction.hasAccess("mary")}" />
<s:Button id="testButton3" enabled="{EventFunction.hasAccess("beth")}" />
testform2.mxml
<s:Button id="testButton4" enabled="{EventFunction.hasAccess("tom")}" />
<s:Button id="testButton5" enabled="{EventFunction.hasAccess("bill")}" />
<s:Button id="testButton6" enabled="{EventFunction.hasAccess("jerry")}" />
Something like this would work.
package
{
import flash.events.Event;
import flash.events.EventDispatcher;
public class EventManager extends EventDispatcher
{
[Bindable(event="somethingChanged")]
public var hasAccess:Boolean = true;
public static var instance:EventManager = new EventManager();
public static function setHasAccess(hasAccess:Boolean):void
{
instance.hasAccess = hasAccess;
instance.dispatchEvent(new Event("somethingChanged"));
}
}
}
Which would be used like so:
<?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/halo">
<fx:Script>
<![CDATA[
protected function button1_clickHandler(event:MouseEvent):void
{
EventManager.setHasAccess( false );
}
]]>
</fx:Script>
<mx:Button click="button1_clickHandler(event)" enabled="{EventManager.instance.hasAccess}"/>
</s:Application>
I don't know that this approach is a good one. This type of functionality would probably be better served inside a proper MVC structure. This is essentially creating a singleton. To implement it as you describe, you'd probably want some sort of instance per user I suppose. You aren't going to be able to bind to a static function though.