views:

729

answers:

3

There are 3 properties (example 1):

[Bindable] public var name:String;
[Bindable] public var email:Number;
[Bindable] public var address:Boolean;

I needed to have 3 helper methods that will be bindable too (example 2):

[Bindable] public var name:String;
[Bindable] public var email:Number;
[Bindable] public var address:Boolean;

public function get nameIsOk():Boolean     { return !Strings.isEmpty(name) }
public function get emailIsOk():Boolean    { return email == 3 }
public function get addressIsOk():Boolean  { return address }

Sure, the code above doesn't work. I made it work by doing this (example 3):

private var _name:String
[Bindable("nameChanged")]
public function get name():String       { return _name }
public function set name(v:String):void { _name = v; dispatchEvent(new Event("nameChanged")) }
[Bindable("nameChanged")]
public function get nameIsOk():Boolean  { return !Strings.isEmpty(name) }

private var _email:Number
[Bindable("emailChanged")]
public function get email():Number       { return _email }
public function set email(v:Number):void { _email = v; dispatchEvent(new Event("emailChanged")) }
[Bindable("emailChanged")]
public function get emailIsOk():Boolean  { return email == 3 }

private var _address:Boolean
[Bindable("addressChanged")]
public function get address():Boolean       { return _address }
public function set address(v:Boolean):void { _address = v; dispatchEvent(new Event("addressChanged")) }
[Bindable("addressChanged")]
public function get addressIsOk():Boolean  { return address }

It does work, but now it is bloated.

Is there a way to reduce this code (example 3) to something smaller (like example 2)?

UPDATE: Kudos to just_a_dude for nice answer. Here is the final version:

[Bindable] public var name:String;
[Bindable] public var email:Number;
[Bindable] public var address:Boolean;

public function Remixer() {
    for each (var f:String in Strings.split("name email address")) {
        ChangeWatcher.watch(this, f, onChange)
    }
}

private function onChange(e:PropertyChangeEvent):void {
    dispatchEvent(new Event(e.property + "Changed"))
}

[Bindable("nameChanged")]
public function get nameIsOk():Boolean  { return !Strings.isEmpty(name) }

[Bindable("emailChanged")]
public function get emailIsOk():Boolean  { return email == 3 }

[Bindable("addressChanged")]
public function get addressIsOk():Boolean  { return address }
A: 

I'm not sure if you need getters, but if not, a nice way to do it, is to just use a single function, and put your bindable strings as arguments.

if you put this in your object:

public function isOk(s:String):Boolean
{
    return !Strings.isEmpty(s)
}

You would use it like this:

<mx:CheckBox selected="{yourObject.isOk(yourObject.name)}" />

Generally, if you put a function inside the "{}" with parameters which are bindable, it will be called each time that parameter changes.

Robert Bak
This can be reduced to <mx:CheckBox selected="{!Strings.isEmpty(yourObject.name)}" />--It is important that isSomethingOk():Boolean encapsulates some hidden logic (not just String/empty check) and takes no arguments. Passing arguments into isOk(s:String):Boolean breaks encapsulation and requires more attention (you should know field name) which is error-prone.
oshyshko
The problem with your changed code, is that you don't want a simple functionality. What you really want is a complete validator, which should be a class as Joa Ebert says. Just a few points that might help: - in the current version you're dispatching to many messages; when an email changes, all the code is being informed that the isEmailOk is also changed, while this is not happening in most cases. - the validation code can grow to be quite complex and have more than one variable in it, so the validation should be triggered by the change in any of the variables is changed.
Robert Bak
A: 

I would encapsulate your functionality in a class. Do not repeat yourself :)

Joa Ebert
how? :) ________
oshyshko
+1  A: 

Not sure if this is what you're looking for but you can use mx.binding.utils.ChangeWatcher to "watch" for property changes

<?xml version="1.0" encoding="utf-8"?>

<mx:Script>
 <![CDATA[
  import mx.events.PropertyChangeEvent;
  import mx.binding.utils.ChangeWatcher;


  [Bindable] public var firstName:String;
  [Bindable] public var email:Number;
  [Bindable] public var address:Boolean;

  private var _watcher:ChangeWatcher;

  private function init():void {
   ChangeWatcher.watch(this, "firstName", propertyChangeHandler);
   ChangeWatcher.watch(this, "email", propertyChangeHandler);
   ChangeWatcher.watch(this, "address", propertyChangeHandler);

   firstName = "foo";
   email = 0;
   address = true;

   firstName = "bar";
   email = 1;
   address = false;
  }

  protected function propertyChangeHandler(event:PropertyChangeEvent):void {

   var prop:Object = event.property;
   var name:String = prop.toString() + "Changed";

   // trace(name); // displays firstNameChanged or emailChanged or addressChanged

   dispatchEvent(new Event(name));
  }


 ]]>
</mx:Script>

Let me know if this helps

Cheers

just_a_dude
It is exactly what I was looking for. Thank you!
oshyshko
Great :) I'm always glad to be able to help
just_a_dude