Here’s the super simple bindable model class that I’ve created for Nori. I’ve never actually developed a project with Flex, but I really like the idea behind it’s data binding implementation. I tried to create something similar for Flash/AS3.
package com.nudoru.nori.model { import flash.utils.Dictionary; import org.osflash.signals.Signal; import com.nudoru.nori.model.bind.PropertyChangedVO; /** * Adds data binding functionality to the abstract model * Usage: * * To set up a property to be bound: * public function set bind_prop(value:String):void * { * bind_prop_field = value; * dispatchPropertyChange("bind_prop", bind_prop_field [, old_value]); // this is the important line * } * * To bind the property: * bindable_model.bindProperty("bind_prop", binding_listener_function); * bindable_model.bindtest = "hello!"; * * The binding_listener_function may any function and as long as it takes the new property value as an argument * * This class should be subclassed to create more enhanced functionality. */ public class BindableModel extends AbstractModel implements IBindableModel { /** * Signal for simple binding */ protected var _onPropertyChangeSignal :Signal = new Signal(PropertyChangedVO); /** * Map of the bindings */ protected var _bindingMap :Dictionary = new Dictionary(true); public function get onPropertyChangeSignal():Signal { return _onPropertyChangeSignal; } /** * Binds a function to a property name * @param propName Name of the property * @param setter Function to call when the property changes. Must take the property's value as a param * @param overwrite Will remove existing setters and assign a new one */ public function bindProperty(propName:String, setter:Function, overwrite:Boolean = false):void { if(overwrite) unbind(propName); if(!isPropertyBound(propName)) { _bindingMap[propName] = setter; } // if the signal doesn't have any listeners yet, set it up if(onPropertyChangeSignal.numListeners < 1) { onPropertyChangeSignal.add(handlePropertyChanged); } } /** * Remove the bindings for a property */ public function unbind(propName:String):void { if(isPropertyBound(propName)) { delete _bindingMap[propName]; } } /** * Determins if the property is bound to anything */ protected function isPropertyBound(propName:String):Boolean { return (_bindingMap[propName]) ? true : false; } /** * Called from a setter to notify bindings of a change to the value */ protected function dispatchPropertyChange(name:String, value:*, oldvalue:*=undefined):void { if(isPropertyBound(name)) { var vo:PropertyChangedVO = new PropertyChangedVO(name, value, oldvalue); onPropertyChangeSignal.dispatch(vo); } } /** * Listener for the onPropertyChangeSignal signal and notifys bound setter */ protected function handlePropertyChanged(changeObject:PropertyChangedVO):void { if(isPropertyBound(changeObject.name)) { var func:Function = _bindingMap[changeObject.name]as Function; func.call(this, changeObject.value); } } /** * Construtor */ public function BindableModel() { super(); } } }