Trigger an Event when value has changed - actionscript-3

I'm trying to trigger an event (function) in an air application when a value has changed (i.e. when a user has logged in), but so far I've no succeeded in triggering my function...
I have following code:
A user class:
[Bindable]
public class User
{
private var m_rssLink:String;
public function get rssLink():String{
return m_rssLink;
}
public function set rssLink(value:String):void{
m_rssLink = value;
}
}
A header component with id header where the user logs in and the user is defined:
<fx:Declarations>
<user:User id="user" />
</fx:Declarations>
And finally the mxml code with the actionscript:
private var loginWatcher:ChangeWatcher;
protected function creationCompleteHandler(event:FlexEvent):void
{
this.loginWatcher = ChangeWatcher.watch(header.user, ["rssLink"], sendRequest);
}
I've tried to trigger the sendRequest function in a lot of ways by now (with [bindable (event="")], with a bindsetter,... ) and so far Google brought me no other options.
Anyone sees where my code fails, or knows a better way to accomplish this?

Well, you have already made a setter property for User. Use that to track user states.
public function set rssLink(value:String):void{
if (m_rsslink==value) return;
m_rssLink = value;
dispatchEvent(...); // use correct event(s) for certain values, then catch
// them in your watcher class. Use FlexEvents if you want.
}

Related

Adobe/Apache Flex: Modify View in an ActionScript class

I have a WindowedApplication in Apache/Adobe Flex 4 which currently consists of one view (the view defined in the WindowedApplication MXML).
In that application I have an object which listens to data coming from a network. When data is available a method is called on that object and it shall update my view by changing the text of a label.
I do not have a reference to the view in the network listener object though. How can I get it?
This is part of my MXML where I define my view.
<fx:Script source="./ViewCodeBehind.as"/>
<!-- ommited stuff -->
<s:Label id="errorLabel"
text=""
fontSize="14"/>
<!-- Stuff in between -->
<s:Button label="Get Status"
click="getStatus();"/>
The code which is called when the button is clicked:
public function getStatus(): void
{
var networkGateway: NetworkGateway = new NetworkGatewayImpl();
networkGateway.getConnectionStatus();
}
And the NetworkGatewayImpl
public class NetworkGatewayImpl implements NetworkGateway
{
public function NetworkGatewayImpl()
{
}
public function getConnectionStatus(): void
{
// Start asynchronous network call
// when error occurs onNetworkError() is called
}
private function onNetworkError(): void
{
// Set "errorLabel" here: How?
}
}
Essentially I want to know some ways to update "errorLabel" from the NetworkGatewayImpl.
Based on your code, there could be multiple ways to solve this. Easiest way (as per me) would be to dispatch an event from the NetworkGatewayImpl class and listen to it on the instance you have created in the view class. So sample code would look like this:
public function getStatus(): void
{
var networkGateway: NetworkGateway = new NetworkGatewayImpl();
networkGateway.addEventListener("networkError", onNetworkError);
networkGateway.getConnectionStatus();
}
private function onNetworkError(e:Event):void
{
networkGateway.removeEventListener("networkError", onNetworkError);
this.errorLabel.text = "Your Text Here";
}
Dispatch your event like this from your NetworkGatewayImpl class:
private function onNetworkError(): void
{
this.dispatchEvent("networkError");
}
You will have to ensure that your NetworkGatewayImpl also implements the IEventDispatcher interface to be able to dispatch events.
Also, best practice would be to create a custom Event class (extending the Event class) and use constants instead of the literal 'networkError'
Hope this helps.

AS3: Returning value from another function after event happened

Basically I want to check if a user exists in a database using AMF (and that works great!). But then I want to return the boolean value to another function (in another class) that originally called the "checkUserExistance" function. But, since the database connection isn't immidiate, this function will always return a false value (even if "result" is true). So I would like to have the return-line inside the "onUserChecked"-function but that of course gives me an error. I thought I could create an eventListener, but then, the "return userExists"-line would also have to be inside another function, which doesnät work(?)... What can I do?
public function checkUserExistance(username:String) {
var responderBOOLEAN:Responder = new Responder(onUserChecked, onFault)
var userExists:Boolean = false;
connection.connect(gateway);
connection.call("User.checkUser", responderBOOLEAN, username);
connection.close();
function onUserChecked(result:Boolean):void {
userExists = result;
}
return userExists;
}
I'm sorry but you are trying to force an Asynchronous call to a Synchronous one and this is WRONG.
See here
You should learn how to handle events in the correct way.
What can i suggest you that helped me a lot is this
The only true answer here is to save userExists as a member variable, and dispatch event when the server returns you a response. The client side of the things should be similar to:
// add listener, ServerEvent is a custom event (see below)
server.addEventListener(ServerEvent.CHECK_RESPONSE, onCheckResponse);
server.checkUserExistance('username'); // start the query
function onCheckResponse(e:ServerEvent):void {
if (e.userExists) {
}
}
// inside server class
function onUserChecked(result:Boolean):void {
userExists = true;
dispatchEvent(new ServerEvent(ServerEvent.CHECK_RESPONSE, userExists));
}
/* ServerEvent is a custom class that extens Event
Such classes are used so you can pass special properties in them
via constructor (pass data, store it into member variable)
and through getter for that variable.
If you don't like it, simply add/dispatch Event.COMPLETE
and use public property to get userExists from server
*/

Setter isn't always triggered

I use a bindable variable that represents a Model and pass it down thru several nested components.
Top level component is my variable:
[Bindable]
private var meetingInfo:MeetingInfoModel;
I initialize it in the handler for the "preinitialize" event:
meetingInfo = MeetingInfoModel.getInstance();
I then pass it thru to 1 component:
<meetingViewStack:MeetingViewStack meetingInfo="{meetingInfo}"/>
In that component I have the following:
private var _meetingInfo:MeetingInfoModel;
public function set meetingInfo( model:MeetingInfoModel ):void{
_meetingInfo = model;}
[Bindable]
public function get meetingInfo():MeetingInfoModel{
return _meetingInfo;
}
I then pass that variable to another nested component:
<documentShare:DocumentPanel meetingInfo="{meetingInfo}"/>
Where I have the same setter/getter set up. Then I pass it again to another nested component:
<documentShare:AttachmentFilesPanel meetingInfo="{meetingInfo}" />
In that component I have an ItemRenderer for a DataGroup component where it binds to "meetingInfo":
<s:DataGroup id="attachmentsList"
width="100%"
clipAndEnableScrolling="true"
dataProvider="{meetingInfo.docsAndAttachmentsList}"
itemRenderer="com.fmr.transporter.view.documentShare.DocumentUploadRenderer"
visible="{meetingInfo.docsAndAttachmentsList.length > 0}">`enter code here`
As I update the "meetingInfo.docsAndAttachmentsList" ArrayCollection, the setters in each nested component get called correctly, until....
...and this is an AIR app, mind you...
I log out and back in. Then, the setter/getter in the last nested component is not fired this time.
Why, oh why, would this happen?
I've been troubleshooting for a couple days and cannot figure it out for the life of me.
Thanks for any helpful tips!!
The code MeetingInfoModel.getInstance(); hints that MeetingInfoModel is a singleton, is it? If so, setter will be triggered only once, because of the mxmlc generates the check that objects instance changed, the generated code for SDk 4.5.1 will be like this:
[Bindable(event="propertyChange")]
public function set meetingInfo(value:MeetingInfoModel):void
{
var oldValue:Object = this.meetingInfo;
if (oldValue !== value)
{
this._883716727meetingInfo = value;
if (this.hasEventListener("propertyChange"))
this.dispatchEvent(mx.events.PropertyChangeEvent.createUpdateEvent(this, "meetingInfo", oldValue, value));
}
}
so if you didn't recreate instance of MeetingInfoModel the setter will not be triggered.

Value Will Set properly, but Get receives Null

So, I have successfully grabbed a value out of an XML document and set it into a separate class called "AddCommas." The trace functions have shown me that it sets properly.
For more details, my objective is to take the language indicator ("fr" for french or "en" for english), set it inside the appropriate class and into a variable I will use. Now, I am using this variable to be used in an if statement; which will help me format a number properly (commas, decimals, spaces) per the clients request.
However, my problem is when I try to get the value to use it. It always comes back as Null. I have placed traces all over my program trying to pinpoint when this happens, but I cannot find it. Here's the code...
The pull from the XML file and into the set (this works fine, but I am adding it for your benefit in case I missed something)
public var commaHold = new AddCommas();
localLanguage = xmlObj.localLanguage;
trace("localLanguage + " + localLanguage);
commaHold.setLanguage(localLanguage); // Set Language
//More code follows...
This is the set function istelf...
public function setLanguage(localLanguage:String){
langHold = localLanguage;
trace("Set Language = " + langHold); //This always shows a successful set
}
Now am I wrong in thinking that in AS3, once langHold in my AddCommas class has been set I should be able to use it without calling a get within the function I am using the If Statement in, right? Such as this?
var language = langHold;
if (language == "en"){
trace("Language is = " + language); // More code follows afterwards and as of now, this shows NULL
Now, I have attempted plenty of Get functions to add the language variable in the call itself to this function and it's always the same. Am I missing some fundamentals here?
Thank you very much for your time.
If you expect a string comparison you need to use quotes, unless en is a String variable since langHold is a String, like:
if (language == "en"){
Consider modifying the set function to use the as3 keyword like:
private var _language:String;
public function set language(value:String):void {
_language = value;
//do other stuff here if necessary, put a breakpoint on the line above
}
public function get language():String{
return _language;
//put a breakpoint on the line above
}
You should be able to see when any instance of your class has the property changed. The only other issue I can think of is it is not the same instance of the class and therefore doesn't share the property value you set earlier. In the debugger you can check the "hashCode" or "address" it shows for this to see if it changes when it hits the breakpoints.
Here's a sample Singleton structure in AS3 (this all goes in one file):
package com.shaunhusain.singletonExample
{
public class SingletonExample
{
private static var instance:SingletonExample;
public static function getIntance():SingletonExample
{
if( instance == null ) instance = new SingletonExample( new SingletonEnforcer() );
return instance;
}
/**
*
* #param se Blocks creation of new managers instead use static method getInstance
*/
public function SingletonExample(se:SingletonEnforcer)
{
}
}
}
internal class SingletonEnforcer {public function SingletonEnforcer(){}}
using this single shared instance from any other class would look something like this:
private var singletonInstance:SingletonExample = SingletonExample.getInstance();
ShaunHusain's theory of using a Singleton was the perfect solution I needed. However, his code gave me a bizarre 1061 error and my format and code appeared to be error free. Regardless, I looked up another way to use a Singleton as follows that worked perfectly for me. Honestly, Shaun's code should work for anyone and I have no idea why it wasn't. I am perfectly willing to admit that it was probably a typo on my end that I just did not see.
I ended up embedding the Set and Get within the Singletons class and used it as an intermediary to hold the information I needed. It worked perfectly.
package chart {
import chart.*;
//
public class StaticInstance {
private static var instance:StaticInstance;
private static var allowInstantiation:Boolean;
private var language:String;
public static function getInstance():StaticInstance {
if (instance == null) {
allowInstantiation = true;
instance = new StaticInstance();
allowInstantiation = false;
}
return instance;
}
public function StaticInstance():void {
if (!allowInstantiation) {
throw new Error("Error: Instantiation failed: Use StaticInsance.getInstance() instead of new.");
}
}
public function setLanguage(_language:String):void{
language = _language;
trace("language set = " + language);
}
public function getLanguage():String{
return language;
}
}
}
This code allowed me to hold the data and call upon it again from two different classes. It's a very hack job instead of just being able to pass on the variable from function to function, but in my case we didn't create this file, we are modifying it and attempting to do things beyond the original scope of the project.
Thanks again for your help Shaun! I hope this helps other people!

[Event(name=“”)]: How is it used and how do custom events work?

I have to work on a project someone else started, but can't contact him because he's out of the country at the moment. Anyway. There is a main mxml and a custom component called "admin".
In admin he declared an event like this:
<fx:Metadata>
[Event(name="sluitFrame")]
</fx:Metadata>
And in a certain function within the component, this event is called upon like this:
dispatchEvent(new Event("sluitFrame"));
This event sluitFrame closes a frame with some admin tools. I need to change the way this works so i'd like to find the corresponding code. On the main mxml there's this code:
<comp:Admin id="compAdmin" creationPolicy="none"
sluitFrame="verbergAdminComponent(event)"/>
So if i understand correctly sluitFrame calls to a custom even called "verbergAdminComponent(event)". So i guess i need this event to change the way the admin frame is closed etc. But this event is nowhere to be found. So I don't understand how "verbergAdminComponent(event)" works or where I can make changes to this event.
Any help is more than welcome and very much needed :)
The [Event... line simply lets the compiler and IDE know that the component/class (in this case, Admin) may dispatch an event by that name. This is important because when someone declares an instance of Admin in a MXML tag, the compiler knows that this event (in this case, sluitFrame) is a valid property. In other words, it lets the compiler know that you can set an event listener in the MXML tag. In your case, every time the Admin object dispatches a sluitFrame event, the function verbergAdminComponent is called and the sluitFrame Event is passed to it.
verbergAdminComponent would be the name of an event handler. It should be a method either in the mxml, in an included .as, or in a base class of that MXML.
Types in Events
If you use an Event in Flash, you could dispatch any named type, since it is a string. So it does not matter how you call it, as long as the listener listens to the exact same type. That's why it works. There is no magic evolved. However, I would choose to use an custom event in that case.
How custom events work
Take a look at custom events work, so you also understand where meta data comes in.
package com.website.events
{
import flash.events.Event;
/**
* #author ExampleUser
*/
public class MyCustomEvent extends Event
{
/**
*
*/
public static const CLOSE_WINDOW:String = "MyCustomEvent.closeWindow";
public function MyCustomEvent(type:String, bubbles:Boolean = false, cancelable:Boolean = false):void
{
super(type, bubbles, cancelable);
}
override public function clone():Event
{
return new MyCustomEvent(this.type, this.bubbles, this.cancelable);
}
override public function toString():String
{
return formatToString("MyCustomEvent", "type", "bubbles", "cancelable", "eventPhase");
}
}
}
I use a static constant instead of directly a string (like your example), so it is typed more strictly.
In actionscript, you'd dispatch the custom event like this. Let's say you create a class named Window.
package com.website.ui
{
/**
* #eventType com.website.events.MyCustomEvent.closeWindow
*/
[Event(name="MyCustomEvent.closeWindow", type="com.website.events.MyCustomEvent")]
import flash.display.Sprite;
import flash.events.MouseEvent;
import com.website.events.MyCustomEvent;
/**
* #author ExampleUser
*/
public class Window extends Sprite
{
public var mcCloseButton:Sprite;
public function Window():void
{
this.mcCloseButton.addEventListener(MouseEvent.CLICK, handleCloseButtonClick);
}
private function handleCloseButtonClick(event:MouseEvent):void
{
this.dispatchEvent(new MyCustomEvent(MyCustomEvent.CLOSE_WINDOW));
}
}
}
This is the class where the meta data should be located. This class is dispatching the event. Other classes that dispatches the same event, could have the meta-data too.
So, Window is dispatching an event with type CLOSE_WINDOW when user clicked on the close button. In another file you would listen to it and do something with it.
package com.website
{
import flash.display.Sprite;
import com.website.events.MyCustomEvent;
import com.website.ui.Window;
/**
* #author ExampleUser
*/
public class Main extends Sprite
{
private var _window:Window;
public function Main():void
{
this._window:Window = new Window();
// a smart code-editor would give you a hint about the possible events when you typed "addEventListener"
this._window.addEventListener(MyCustomEvent.CLOSE_WINDOW, handleWindowClosed);
this.addChild(this._window);
}
private function handleWindowClosed(event:MyCustomEvent):void
{
// do something
this._window.visible = false;
}
}
}
This should work.
Ofcourse in a real-world situation MyCustomEvent would be named WindowEvent.
Event Meta Data
The meta data could be used to give hints to the compiler and nowadays smart code editors (FDT, FlashDevelop, FlashBuilder, IntelliJ etc.) can give code completion. It's basically a description of what kind of events may be dispatched by a class, so you know what listeners could be used.
The code should work even when the meta data is deleted.
The Event meta has a name and a type. The name should be the exact value of the type. In the case of our example, it should be the value of CLOSE_WINDOW, so that's MyCustomEvent.closeWindow.
The type should be the classname with the full package, in the case of our example it would be 'com.website.events.MyCustomEvent'.
Finally, the meta data looks like this:
[Event(name="MyCustomEvent.closeWindow", type="com.website.events.MyCustomEvent")]
BTW I have some tips about your code:
I would suggest to use English function names and parameters, instead of Dutch.
verbergAdminComponent isn't a good name for a handler, it should be something like handleCloseWindow(event), which should call the verbergAdminComponent function.