flex: manipulating flex elements from external action script class - apache-flex

I have a flex project with a mx:Text.
i have a class that is loaded at the beginning of my project and i want this class to enter text in that text element. the id of the text element is "messagePanel" but when i try to type messagePanel.text i get 'Access of undefined property'. how do i resolve the issue?
example
general.FMS3Connect class connects to an adobe flash media server, when it completes connecting i want it to display the even info code of the connection inside a mx:Box, it's id is messageBox.
on my main mxml file i have the following:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" backgroundGradientColors="[0xFFFFFF,0xAAAAAA]"
xmlns:local="*">
<mx:Script>
<![CDATA[
import general.FMS3Connect;
private var conn:FMS3Connect= new FMS3Connect();
]]>
</mx:Script>
<mx:Text id="messageBox" color="black" text="trying to connect to server..." creationComplete="conn.connect()" >
</mx:Application>
the function connect() for now just has "messageBox.text='test'";
when i execute the application i get the following error:
TypeError: Error #1009: Cannot access a property or method of a null object reference.
how do i resolve the issue?
thanks!

Problem is, your FMS3Connect class has to reference to the Text element.
Easiest (but nasty) solution is to pass in a reference to the Text Element to your connect method, you can then reference the element from that.
something like...
<mx:Text id="messageBox" color="black" text="trying to connect to server..." creationComplete="conn.connect( messageBox )" >
public function connect( messageDisplay : Text ) : void {
// do usual connect stuff.
messageDisplay.text = "test";
}
This isn't the nicest solution in the world, connect shouldn't know about the message box really. But it solved your problem!

wait for creationcomplete event

Related

Why is my object reference null when trying to reference an mx:Canvas from ActionScript?

this is a follow up question from this one, I don't want to keep going in the comments and preventing people from getting hard-earned reputation... :)
In my Cairngorm command class, to get it to compile I needed to tell it what myCanvas was, so I used this line:
var myCanvas : MyCanvas = new MyCanvas;
I'm guessing that's wrong, though, because although it compiles, if I try to do something like this:
if (myCanvas.subObject.value == 0) { ... }
it'll throw this error:
TypeError: Error #1009: Cannot access a property or method of a null object reference.
at com.foo.bar.command::MyCommand/execute()
as if the subObject doesn't exist. It looks like I might be getting a new instance of MyCanvas, not the instance I want from the main.mxml with an id of myCanvas. Am I right? How do I fix this?
Edit (10:59pm GMT+1): Okay, so it looks like I've been way too vague here. Here's my main.mxml:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:control="com.foo.bar.control.*"
xmlns:business="com.foo.bar.business.*"
xmlns:view="com.foo.bar.view.*"
applicationComplete="com.foo.bar.util.StartupUtil.init()"
horizontalScrollPolicy="off"
verticalScrollPolicy="off"
borderThickness="0"
paddingBottom="0"
paddingLeft="0"
paddingTop="0"
paddingRight="0"
>
<mx:Script>
<![CDATA[
import com.foo.bar.model.PlayerModelLocator;
[Bindable]
private var model : PlayerModelLocator = PlayerModelLocator.getInstance();
]]>
</mx:Script>
<!-- ========================================================================== -->
<!-- the ServiceLocator where we specify the remote services -->
<business:Services id="services" />
<!-- the FrontController, containing Commands specific to this application -->
<control:PlayerController id="controller" />
<!-- ========================================================================== -->
<mx:Style source="assets/main.css" />
<view:MyCanvas id="myCanvas" />
</mx:Application>
And here's my com/foo/bar/command/MyCommand.as:
package com.foo.bar.command {
/* add to controller
addCommand( MyEvent.EVENT_CHANGE_VOLUME, ChangeVolumeCommand );
*/
import flash.net.SharedObject;
import com.adobe.cairngorm.control.CairngormEvent;
import com.adobe.cairngorm.commands.ICommand;
import com.foo.bar.model.PlayerModelLocator;
import com.foo.bar.event.MyEvent;
import com.foo.bar.view.*;
public class ChangeVolumeCommand implements ICommand {
public function execute(event:CairngormEvent):void {
var model : PlayerModelLocator = PlayerModelLocator.getInstance();
var myEvent : MyEvent = MyEvent(event);
var myCanvas : MyCanvas = new MyCanvas();
var so:SharedObject = SharedObject.getLocal("fixie.video");
if (myCanvas.subObject.value == 0) {
trace("subobject value is 0");
}
}
}
}
Basically, I want to get a handle on the object with ID myCanvas in main.mxml using the myCanvas object in MyCommand.as
Could be a couple of things. First, you need parentheses on your class name after the "new" statement: new MyCanvas(); Second, you may be trying to access your sub component before the component lifecycle is ready for you to do so. (It's hard to tell from the code you posted since there isn't enough context.)
What is the scope of your myCanvas variable? Is it inside a method somewhere? You will need to make it public or give it getter/setter to be able to access it.
You may also be trying to reference it before it has been added to its parent, using the addChild() method.
There really isn't enough code in your examples to determine the problem, but these things should give you somewhere to start looking.
1 way is to add eventListener when your myCanvas will be ready after CreationComplete and to do all your stuff
and the second is:
define your subObject as in myCanvas class so you'll be able to access it on Init Stage of your component.
regards
Eugene
p.s. all of the time everybody want to get answer without well formed sample of their problem, its terrible!!

popup editor for datagrid items - hangs the browser

i've got a pretty straightforward thing: a datagrid which renders some items. clicking on an item would bring up a popup editor (as the item has lots of properties and may not be edited right in the datagrid).
the popup contains just a form and a [Bindable] reference to the item it edits (which is passed from itemClick handler of the datagrid). form's default values are taken by binding to corresponding item properties with {} notion, while form values are bound back to the item using mx:Binding tags.
and now the problem. when the popup is brought up for the first time, everything is fine. however, when after being closed the popup is brought up again by clicking on the same item, the browser hangs (afaik because of change watchers being endlessly fired resulting in stackoverflow or something similar).
we have same behaviour in Safari, IE and Chrome, so i guess it's not to do with something browser-related. removing either [Bindable] from the item reference in the popup or mx:Binding tags from editors suppresses the problem, but of course the editing no longer works.
i'm banging my head against the wall for several days already, but still can't make it work. does it ring a bell to someone, what can be wrong in here (what can be damn easier that this)?
here's the code of the popup:
<mx:TitleWindow xmlns:mx="http://www.adobe.com/2006/mxml" title="Details"
showCloseButton="true" close="PopUpManager.removePopUp(this);" creationComplete="PopUpManager.centerPopUp(this)">
<mx:Script>
<![CDATA[
import mx.managers.PopUpManager;
import my.Detail;
[Bindable] private var _documentDetail:Detail;
public function set documentDetail(value:Detail):void {
this._documentDetail = value;
}
public function set readOnly(value:Boolean):void {
if (value) {
this.currentState = "read-only";
}
}
]]>
</mx:Script>
<mx:states>
<mx:State name="read-only">
<mx:SetProperty target="{startDate}" name="enabled" value="false"/>
<mx:SetProperty target="{comments}" name="enabled" value="false"/>
</mx:State>
</mx:states>
<!--
<mx:Binding source="this.startDate.selectedDate" destination="_documentDetail.startDate"/>
<mx:Binding source="this.comments.text" destination="_documentDetail.comment"/>
-->
<mx:VBox width="100%" height="100%">
<mx:FormItem label="{resourceManager.getString('eRequestAppli','startdate')}:" labelWidth="160" width="100%">
<mx:DateField id="startDate" width="100%" selectedDate="{_documentDetail.startDate}" formatString="{resourceManager.getString('eRequestAppli', 'dateformat')}" editable="false"/>
</mx:FormItem>
<mx:FormItem label="{resourceManager.getString('eRequestAppli','comments')}:" labelWidth="160" width="100%" height="79">
<mx:TextArea id="comments" width="100%" height="100%" text="{_documentDetail.comment}" editable="false"/>
</mx:FormItem>
</mx:VBox>
</mx:TitleWindow>
here's how i call it:
private function show(detail:Detail, readOnly:Boolean=false):void {
var popup:fxc_ProposalDetail =
fxc_ProposalDetail(PopUpManager.createPopUp(UIComponent(Application.application), fxc_ProposalDetail, true));
popup.documentDetail = detail;
popup.readOnly = readOnly;
}
Thanks for posting the code. Now I might be able to help.
Where are you handling the close event of the popup? Besure to use something like this:
private function handleCloseEvent():void {
PopUpManager.removePopUp(this);
}
Besides that it appears your problem has to do with the following:
Because a module is loaded into a child domain, it owns class definitions that are not in the main application’s domain. For example, the first module to load the PopUpManager class becomes the owner of the PopUpManager class for the entire application because it registers the manager with the SingletonManager. If another module later tries to use the PopUpManager, Adobe ® Flash® Player throws an exception.
The solution is to ensure that managers such as PopUpManager and any other shared services are defined by the main application (or loaded late into the shell’s application domain). When you promote one of those classes to the shell, the class can then be used by all modules. Typically, this is done by adding the following to a script block:
import mx.managers.PopUpManager;
private var popUpManager:PopUpManager;
The module that first uses the component owns that component’s class definition in its domain. As a result, if another module tries to use a component that has already been used by another module, its definition will not match the existing definition. To avoid a mismatch of component definitions, create an instance of the component in the main application. The result is that the definition of the component is owned by the main application and can be used by modules in any child domain.
see: http://help.adobe.com/en_US/flex/using/WS2db454920e96a9e51e63e3d11c0bf69084-799a.html
for a better understanding of modules.
as suggested before, reusing the popup instead of creating a new one each time seems to have solved the issue.

Flex: How to access movieclips within an imported swf

I have imported a swf (not created with Flex, i.e. non-framework) into a Flex application. Once loaded, I would like to access movieclips within that imported swf. Looking at Adobe's docs (http://livedocs.adobe.com/flex/3/html/help.html?content=controls_15.html), it seems straightforward; however, their examples are between a Flex app and an imported swf (created with Flex).
Like their example, I'm trying to use the SystemManager to access the imported swf's content; however, I receive the following error:
TypeError: Error #1034: Type Coercion failed: cannot convert flash.display::MovieClip#58ca241 to mx.managers.SystemManager.
Is this error occurring because I'm importing a non-framework swf into a framework swf? Thanks in advance for any assistance.
Code:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:SWFLoader source="assets/test.swf" id="loader" creationComplete="swfLoaded()" />
<mx:Script>
<![CDATA[
import mx.managers.SystemManager;
[Bindable]
public var loadedSM:SystemManager;
private function swfLoaded():void
{
loadedSM = SystemManager(loader.content);
}
]]>
</mx:Script>
</mx:Application>
Was test.swf created with an earlier AS version? According to this swfs published for AS 1.0/2.0 runs in a different AS virtual machine than AS 3.
The parent AVM2 SWF file will not have
access to the properties, methods, or
objects of the loaded AVM1Movie
object.
You can access them directly, using their instance names.
private function swfLoaded():void {
var clip1:MovieClip = MovieClip(loader.content.myClip1);
var clip2:MovieClip = MovieClip(loader.content.myClip2);
// ...
}

Flex Warning: Unable to bind to property 'foo' on class 'Object' (class is not an IEventDispatcher)

I've got an object that contains a dozen or so fields I want to bind to form elements, so that I can use that object to send the data back to the server to be saved.
Definition of my container object:
private static const emptyLink:Object = {
id: -1, title:'',
trigger1:'',trigger2:'',trigger3:'',trigger4:'',trigger5:'',
linkTitle:'', linkBody:'',
answer1:'',answer2:'',answer3:'',answer4:'',answer5:''
};
[Bindable] public var currentLink:Object = emptyLink;
currentLink is assigned at runtime to a specific index from an ArrayCollection, I'm just using the emptyLink object for initialization purposes, mostly.
<mx:Panel id="triggerPanel" title="Trigger" width="33%">
<mx:VBox id="tpBoxes" width="100%" paddingBottom="5" paddingLeft="5" paddingRight="5" paddingTop="5">
<mx:TextInput id="trigger1" width="100%" textAlign="left" text="{currentLink.trigger1}" />
<mx:TextInput id="trigger2" width="100%" textAlign="left" text="{currentLink.trigger2}" />
<mx:TextInput id="trigger3" width="100%" textAlign="left" text="{currentLink.trigger3}" />
<mx:TextInput id="trigger4" width="100%" textAlign="left" text="{currentLink.trigger4}" />
<mx:TextInput id="trigger5" width="100%" textAlign="left" text="{currentLink.trigger5}" />
</mx:VBox>
</mx:Panel>
Of course, this compiles and displays just fine, but there are runtime warnings for each instance:
warning: unable to bind to property 'trigger1' on class 'Object' (class is not an IEventDispatcher)
warning: unable to bind to property 'trigger2' on class 'Object' (class is not an IEventDispatcher)
warning: unable to bind to property 'trigger3' on class 'Object' (class is not an IEventDispatcher)
warning: unable to bind to property 'trigger4' on class 'Object' (class is not an IEventDispatcher)
warning: unable to bind to property 'trigger5' on class 'Object' (class is not an IEventDispatcher)
And the currentLink object is not updated when the TextInput fields are changed.
The obvious answer is that my object needs to be an instance of a class that implements IEventDispatcher. What that answer doesn't tell me is the particulars of implementing that interface (what's required? what's not?), and if there is a simpler way to do this -- like a built in class that will gladly accept my custom properties and allow for binding, without me having to worry about the particulars of implementing the interface.
Does such a class exist? If not, what's the bare minimum and/or accepted standard for accomplishing this task?
You need to use ObjectProxy (as Chetan mentions) - but you also need to use valueCommit to get the text you enter in the input BACK into your object:
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:Script>
<![CDATA[
import mx.utils.ObjectProxy;
private static const emptyLink:Object = {
id: -1, title:'',
trigger1:'',trigger2:'',trigger3:'',trigger4:'',trigger5:'',
linkTitle:'', linkBody:'',
answer1:'',answer2:'',answer3:'',answer4:'',answer5:''
};
[Bindable] public var currentLink:ObjectProxy = new ObjectProxy(emptyLink);
private function handleClick():void
{
trace(currentLink.trigger1);
}
]]>
</mx:Script>
<mx:Panel id="triggerPanel" title="Trigger" width="33%">
<mx:VBox id="tpBoxes" width="100%" paddingBottom="5" paddingLeft="5" paddingRight="5" paddingTop="5">
<mx:TextInput id="trigger1" width="100%" textAlign="left" text="{currentLink.trigger1}" valueCommit="{currentLink.trigger1 = trigger1.text;}"/>
<mx:Button label="Click" click="handleClick()"/>
</mx:VBox>
</mx:Panel>
</mx:WindowedApplication>
Object doesn't dispatch events. Although you have made the variable Bindable, the properties of the object referenced by the variable currentLink can not be bound.
Use ObjectProxy instead.
[Bindable] public var currentLink:ObjectProxy = new ObjectProxy(emptyLink);
The first thing you'll want to know is that binding in Flex 3 is not bidirectional. The binding expression will ensure that if the source of the binding expression (currentLink.trigger1) changes that the target (TextInput) will receive notification of the change and update accordingly. If you want the binding to go in the other direction, there are at least two ways to do this:
Use the mx:Binding tag to direct TextInput.text back to the object
Use BindingUtils to do this programmatically instead.
In Flex 4 they are introducing a new syntax for bidirectional binding #{some.binding.expression} but it's not available in Flex 3.
On the 2nd part: the error that you're receiving is because you are binding to a "generic" prototype Object. When you apply the [Bindable] metadata tag to a property or class, the MXMLC compiler generates AS code that includes use of binding utilities and property change watchers to do make the binding happen. However you can't make the prototype Object do this since it's a built-in. You can create a custom ActionScript class which is bindable (or has certain properties bindable). The MXMLC compiler will generate a class which implements IEventDispatcher and therefore supports binding. This has the advantage of being faster than prototype objects and also gives you compile-time checking, i.e. you will receive a compiler error if you reference an invalid property.
The other alternative is to wrap your prototype in ObjectProxy as one of the other SO members has suggested.
Just a tip on how to find out the offending code in a larger project - put a breakpoint on the two
trace("warning: unable to bind to property '"
lines in the SDK's PropertyWatcher class (Navigate > Open Type > ...). Stacktrace will then help you find the ui component that holds the broken binding.
In general, the reason why you get "unable to bind to property foo on a class, is because you are either missing a getter or setter for foo. You could also make foo scoped to a public variable, (although this breaks encapsulation)
So you need Both of these to make it go away:
public function set foo (o:FooObject) : void {
...
}
or
public function get foo() : FooObject {
...
}
Here's the livedocs reference for the interface. It's pretty much what would be obvious.
To quote:
In general, the easiest way for a user-defined class to gain event dispatching capabilities is to extend EventDispatcher.
Hence,
private static const emptyLink:EventDispatcher = {
I haven't been using Flex for very long, and this might not fit your requirements, but why not use XML? I believe you can set the TextInput text value to attributes in the XML.
I'm using pseudo-code, but something like this makes sense to me:
[Bindable] private static const currentLink:XML = <root>
<trigger1 value=""/>
<trigger2 value="" />
</root>;
...
<mx:TextInput id="trigger1" width ... text="{currentLink.trigger1.#value}" />
Something like this, perhaps?

How can I target a Flex 3 datagrid in MXML from Actionscript?

I have a datagrid defined in an mxml file (flex 3):
I am using an external class to connect to a sqlite database and generate some results (this is working and I can trace the results).
How can I target the datagrid generated in the mxml from the external class? I have tried:
Application.application.resultsGrid.dataProvider = results.data;
And get 'Error: Access of undefined property Application.' from the amxmlc compiler.
I've also tried:
[Bindable]
public var resultsGrid:DataGrid;
In the class properties.
Looks like I needed to include import mx.core.*; and it now works.
I don't really understand your answer. Am I not binding the dataprovider property by doing:
Application.application.resultsGrid.dataProvider = result.data; ?
I'm from a PHP background and familiar with OOP in that environment so the idioms in Flex are quite strange to me.
as brd664 says, what you are actually doing in
Application.application.resultsGrid.dataProvider = result.data;
is actually an assignment. It's just like assigning a value to variable as in
var a : uint = 1;
Binding gives you a little more structure and allows you to populate multiple components based on a single property update. There's a ton of other benefits from binding and probably too much to cover in this post.
Here is a quick and simple example of how binding works. Note that there is one property that is bindable... when you click the button it sets that property to the value of whatever is in the textInput. This update then causes the bindings to fire and updates anything that has been bound to that property. It's one of flex's biggest features (it's also used extensively in silverlight and wpf and probably a load of other technologies that i'm not aware of). Anyway... have a play with it and see if you can get your component to update from a binding.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="horizontal">
<mx:Script>
<![CDATA[
private var _myData : String
[Bindable]
public function get myData() : String
{
return _myData;
}
public function set myData(value : String) : void
{
_myData = value;
}
private function clickHandler(event : MouseEvent) : void
{
myData = myTextInput.text;
}
]]>
</mx:Script>
<mx:VBox>
<mx:HBox>
<mx:Label text="{myData}" />
<mx:Label text="{myData}" />
<mx:Label text="{myData}" />
</mx:HBox>
<mx:TextInput id="myTextInput" text="TYPE HERE" />
<mx:Button label="CLICK TO BIND" click="clickHandler(event)" />
</mx:VBox>
</mx:Application>
Update: The phrasing of your question confused me :(
If you need to populate the datagrid with from you db, you really should be looking at binding the dataProvider property.

Resources