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();
}
}
}