Bindable property change event in Flex - actionscript-3

Can anyone please help me solve this mystery:
I've got a component called Box.as that has following two properties, and have their getters & setters defined:
private var _busy:Boolean;
private var _errorMessage:String;
In MXML that uses this component I define it like this:
<components:Box skinClass="skins.components.BoxSkin"
busy="{presenter.boxBusy}"
errorMessage="{presenter.boxErrorMessage}"/>
Where presenter variable is defined here in MXML and a Presenter class has boxBusy and boxErrorMessage variables defined as bindable property change events:
[Bindable(event="propertyChange")]
function get boxBusy():Boolean;
function set boxBusy(value:Boolean):void;
[Bindable(event="propertyChange")]
function get boxErrorMessage():String;
function set boxErrorMessage(value:String):void;
PROBLEM is that whenever I change boxErrorMessage for the presenter, I see the affect in MXML but nothing happens at all when I change boxBusy. Is there something extra I need to do with boolean variable?
Thanks a lot in advance.

You should omit the (event="propertyChange") specification from your [Bindable] metadata tags on both boxBusy and boxErrorMessage. Also, make sure your get/set methods are declared public.
So, the property, boxBusy, would look something like this:
[Bindable]
public function get boxBusy():Boolean { return _busy; }
public function set boxBusy(value:Boolean):void { _busy = value; }
When you qualify [Bindable] with (event="..."), you're telling Flex, "I will dispatch the named event whenever the binding should be updated".
If you omit the event specification, then flex assumes that the event is named propertyChange. But that's not all it does. It also automatically "wraps" your setter with generated code that transparently dispatches a 'propertyChange' event any time the setter is used to modify the value. This is described in more detail here, at adobe livedocs.
So... by explicitly specifying (event="propertyChange"), you disable flex's default behavior. Even though you're using the default event name, flex will not generate the wrapper code -- instead, it will expect you to dispatch the event from your code, at the appropriate time.
I imagine that your boxErrorMessage property appears to be working, because some other [Bindable] property of your class is changing in the same pass -- thus dispatching propertyChange, and causing your boxErrorMessage binding to update as a side-effect.

It is completely possible that if you are setting busyBox to true the first time the setter is getting called but it will not get called again if you again try to set to true. The code that is by the flex compiler when you use the [Bindable] tag will adds a check to see if you are setting the new value to what the getter will currently will return. If that is the cause it isn't called.
If you were to oscillate between true and false it would get called every time because the new value differs from the current value. But setting it to true-true-true-true-false would only result in it getting called the first time to set to your and the last time to set to false.

Related

Flex 4.6 setting selectedChild= with a bound Variable from a Component

I'm getting the following error:
1067: Implicit coercion of a value of type String to an unrelated type mx.core:INavigatorContent.
Which is located in my main application at the line where I set the selectedChild=
Here's my code for my viewstack which is in my main application:
<mx:ViewStack id="mainViewStack"
width="100%" height="100%"
selectedChild="{topViewControlComponent.selectedChild}">
My component contains the following:
[Bindable]
public var selectedChild:String;
protected function changeView2(child:String):void
{
this.selectedChild = child;
}
<s:Button styleName="controlBarButton"
label="Events"
click="changeView2('userEvents');"/>
I got this to work when I set the viewstack navigator content base off of selectIndex and using an integer...worked fine. But I would rather call them by the id of the Navigator content so that they don't have to be in specific order, if this is possible. Or maybe there's a better way to go about this...Thanks for any help!
The selectedChild property on the ViewStack takes an actual view as its argument, not the name of a view. Using selectedIndex with an int will work fine, or you could call a function in your main application that maps between id and view instance.
Edit: As you said in the comments, you can use click="mainViewStack.selectedChild=userEvents" to set the view as desired.
However, your code in the question is acting like this:
click="mainViewStack.selectedChild='userEvents'"

Is it possible to change an inherited access modifier in ActionScript 3?

I'm currently working on a project where I have a ton of classes inheriting from other classes which inherit from other classes and so on. It's probably more complex than it should be, but I am a sucker for abstraction.
Anyway, at times I need to change a getter/setter from being public to private. I suppose it's not really a need, but a desire to cut off things that are preset in child classes, but still need to be publicly accessible in the parent classes.
So an example would be:
Class Base {
public function set label( value:String ):void{};
}
Class A extends Base {}
Class B extends A {
public function B() {
super();
this.label = "stuff";
}
override public function set label( value:String ):void {
//this setter should not be publicly available since the label should not be possible to change in this class
}
}
Currently, I am doing one of two things in these cases:
override the setter to do nothing or set it to the default value so that it can still update/render/whatever
throw an error saying it is unavailable in that class
I've done some searching and everything seems to point to this being impossible, but I've never found it explicitly stated that it is impossible. So is it possible to change the access modifier on an inherited property/function?
It is not possible, and it really should not be, because it leads to confusing and unpredictable class hierarchies. For starters, if you did something like that, you would break the Liskov Substitution Principle: A super class should at all times be replaceable by its derived classes. Changing the API would clearly prevent that - and thus possibly lead to runtime errors and/or inexplicable glitches, if another programmer accidentally exchanged types.
If the classes you are modeling have different behavior in such a way that would make you "hide" an otherwise public API method, you should probably not use inheritance for this - or perhaps in a different way. From what you are describing, I would guess that in a larger part of your hierarchy, you should probably be using composition instead of inheritance, anyway.
It is not possible for the very reason in the comments by Marty Wallace. But it's not an uncommon thing to do.
However in the alternative you used, The property owner is the base class & hence it should always know of anything that the derived class does with it's own properties.
Instead of your hack I would thus prefer something like this :
public class Base {
protected var _isLabelUsable:Boolean = true;
public function set label( value:String ):void {
if (!_isLabelUsable)
throw new Error("Access of undefined property label.");
// Set Label here
}
}
public class A extends Base {
}
public class B extends A {
public function B() {
super();
_isLabelUsable = false;
}
}
These are all valid points, but...
There are cases where they are all void.
Given a base class that comes from an external source. Like, say, mx:Panel.
It has the property 'titleIcon:Class'
The derived class inherits all properties and functions. But people using it shall never set the titleIcon directly, because part of the derived class' functionality depends on the availability of an icon name being known. It provides a property iconName:String. Setting it will also set the titleIcon.
Now how to prevent people from still setting the icon directly? The UI is offering the old property for AS3 and MXML, and the compiler will (of course) not complain.
If titleIcon is a setter/getter pair (in this case, it is), and not final, then the derived class can override the setter and throw an error, while the iconName setter will assign the icon class to super.titleIcon.
However, this is clumsy and will not work for final functions or variables.
If there were a way to at least tell the UI to not offer the property anymore or show a warning...

Add a property to a Button or other type of Objects

I always created additional property to MovieCLips using the syntax
myMC.myProperty
without any sort of declaration... But i can use this method only with MovieClips.. What about if i want to add a property to a button or any different type of object? I need to extend the class? Do you can me suggest how? Many thanks
You can add property to movieclips in runtime because MovieClip is dynamic class. If the class is not dynamic, you should extend it to create methods and properties.
Read about dynamic classes.
I tend to create custom classes for nearly everything.
I would extend the relevant class and set up a private var for your new property. You can then pass in the value to the constructor or add a getter/setter method to call externally.
private function _myProperty:int;
public function get myProperty():int
{
return _myProperty;
}
public function set myProperty(newVal:int):void
{
_myProperty = newVal;
}
Getter/setter methods add a few lines of code that may seem unnecessary but on big projects when you find a property is being set and you don't know why, you can put a break point in your set myProperty
Subclass is main solution.
Next works only with mx components (flex sdk 3).
Most components have data : Object property that you can freely use to store data.
Monkey patching sometimes is the only way to go. It allows you to add custom properties to flex sdk classes. I don't think you should use it in your case. But I used it to change core logic that is locked by private keyword in flex sdk.
Hope that helps.

Setting a skinState of a button in AS3

I want to set (manually) the skinState (for example 'disabled') of a button (that I skinned) in ActionScript.
For example:
I have a button skin with hostComponent: components.backend.btnMenuBakComp
The button skin has the default button states (up, over, down, ...), but I want to set one of this skinStates in ActionScript.
For example:
subMenu.btnDashboard.currentState = "disabled";
This doesn't work because the state "disabled" is not known in the component (it is only known in the skinState of btnDashboard).
How can I fix this?
Is there another solution then load a new skinClass?
Thanks
Quick and dirty
You can access the skin of any component and just set its state directly:
subMenu.btnDashboard.skin.currentState = "disabled";
That is however not a very clean way to do it. You are telling a Skin class directly what to do and completely bypassing the host component. Hence the host component has no idea of the changes that were made to its skin.
The proper way
A cleaner way to approach this is to expose a property on the host component and then tell the skin to adjust itself to possible changes by overriding the getCurrentSkinState() method.
You could for instance create a property 'enabled' and then tell the skin to update its state by calling invalidateSkinState() whenever 'enabled' is being set.
public function set enabled(value:Boolean):void {
_enabled = value;
invalidateSkinState();
}
Calling invalidateSkinState() will make the skin call getCurrentSkinState() in the next render cycle. This method will then look something like this:
override protected function getCurrentSkinState():String {
return _enabled ? "normal" : "disabled";
}
Do note that since you are skinning a Button (or a subclass of it) all that I've written here is already baked into that component. So the answer to your question might be as simple as : "just set the 'enabled' property to true.
subMenu.btnDashboard.enabled = true;

Linq2Sql: Force discriminator property to be set

The problem I'm having is while using Linq2Sql with inheritance after declaring a new instance of the inherited class the discriminator property is still set to its initial value, not the correct value for the sub-type. It gets the correct value after attaching it to a context and calling SubmitChanges(). There are times where I want to declare a new object of the inherited type and call methods on the base class with the base class knowing inherited type it is working with and the most logical choice would be to use the discriminator property.
Is there a way to force the setting of the discriminator property? I don't want to go to all my sub-classes and implement the OnCreated() partial method for something the context already knows how to do.
I did come up with a slightly better workaround than putting code in the OnCreated() method of each inheriting class and figured I'd leave it here in case anyone stumbles here.
In the OnCreated() of the base class I added code that looked similar to this:
partial void OnCreated()
{
if (this is BaseClass1)
{
this.[DiscriminatorProperty] = DiscriminatorValueForBaseClass1;
}
else if(this is BaseClass2)
{
this.[DiscriminatorProperty] = DiscriminatorValueForBaseClass2;
}
}
It is still duplicating the functionality that the context already knows how to do but at least I'm not implementing the OnCreated() in every base class. I also don't like the fact that if a new class is added or a discriminator value changes you have to update it in the DBML and in the OnCreated(). For this reason I'd still like a way for the context to assign the value, in fact it should be doing this when the inherited class is created.