Demystify the Qooxdoo properties system

One of the great -maybe the best- feature of Qooxdoo system, is the properties system.

Basically a property will looks like a traditionnal java bean : you have an internal name (never exposed), and manipulate only getter and setter from it, including inside the class itself.
But, Qooxdoo do implements other mechanisms, like an event system, apply function on value change, check and validate input…

I create this because sometimes I don’t need Qooxdoo because it’s a great but heavy framework, but, I still like the properties feature ! So here is a basic, implementation of Qooxdoo properties, in a javascript -framework less- way.

Some important note before starting :

  • Here the system support only one property at a time, you can, of course, create a properties object which is able to handle many property as sub object.
  • Because there is only one property at a time, the system does not handle the group functionnality of Qooxdoo.
  • Also some capacities are more Qooxdoo specific, like theming, refine, they are of course not implemented here…
  • The class does not provide the same check quality as Qooxdoo, this one here only takes benefits from typeof, and so, is more limited than Qooxdoo one.
  • To get access to event system, you must have the event listener from here included in your page.
  • Except that, the rest is fully working, and most of interesting features are of course implemented !
/* ************************************************************************

	License: LGPLv3

	Authors: deisss

	Date: 2012-09-06

	Date of last modification: 2012-09-06

	Description : Qooxdoo properties emulator in javascript

************************************************************************ */
/**
 * Emulate a basic version of Qooxdoo properties
 * @param options {Object} The options to set
*/
var property = (function(){
	var defaultVal = function(){return true;};
	var defaultApp = function(){};
	var allowedCheckType = ["number", "string", "boolean", "object", "function"];

	return function (options){
		//Store the property value
		var value = null;

		//Others params are more qooxdoo specific
		var params = {
			nullable : true,         //true, false
			apply    : defaultApp,   //any function
			init     : null,         //any value
			check    : "",           //any from allowedCheckType allowed
			validate : defaultVal    //The validate is a function
		};

		//Populate params with options
		if(typeof(options) !== "undefined"){
			for(p in params){
				//We check given options exist in param, and are same type
				if(p in options && typeof(options[p]) === typeof(params[p])){
					params[p] = options[p];
				}
			}
		}

		//tiny changes to get easier check usage
		params.check = params.check.toLowerCase();

		//If check has been set, we check it is allowed
		if(params.check !== ""){
			var c     = allowedCheckType,
				found = false;

			for(var i=0, l=c.length; i<l; ++i){
				if(params.check === c[i]){
					found = true;
					break;
				}
			}

			//The check has not been found in available check, we refuse
			if(found === false){
				params.check = "";
			}
		}

		//Applying init => this is because the init can have a typeof different from the options value
		if(
			typeof(options) !== "undefined" &&
			typeof(options.init) !== "undefined" &&
			(params.check === "" || (typeof(options.init) === params.check))
		){
			params.init = options.init;
		}

		//Updating the value with current init
		value = params.init;

		//Now we create two functions : get and set, which allow to manipulate internal value

		/**
		 * Get the content value
		 * @param {Mixed} Any value stored previously
		*/
		this.get = function(){
			return value;
		}

		/**
		 * Set the content value
		 * @param data {Mixed} The data to store
		*/
		this.set = function(data){
			var valNull  = (data === null || data === undefined),
				previous = value;
			//First we refuse nullable value if nullable is set to false
			if(params.nullable === false && valNull){
				return;
			}

			//Thoose test are not needed if the data is null and params.nullable set to true
			if( ! (params.nullable === true && valNull === true)){
				//Checking the type
				if(params.check !== "" && typeof(data) !== params.check){
					return;
				}
				//Checking validate
				if(params.validate(data, previous) !== true){
					return;
				}
			}

			//From now, the data is consider as validate one, we can continue
			value = data;

			//Calling apply
			params.apply(data, previous);

			//Finally sending event
			if(typeof(this.dispatch) !== "undefined"){
				//Dispatching the event
				this.dispatch("changeValue", {
					value : value,
					old : previous
				});
			}
		}
	};
}());

//Extending the property class with event listener
if(typeof(eventListener) !== "undefined"){
	property.prototype = new eventListener();

	property.prototype.constructor = property;
}

The system use here a closure to not populate javascript default system with default values used by each properties (and so defined only once…).

To use such property system, check the following examples :

var login = new property({
	init : "hello",
	apply : function(value, old){
		console.log("apply login recieve value : ");
		console.log(value);
		console.log("apply login recieve old   : ");
		console.log(old);
	}
});

//Password init will not be set because 0 is not a string element
var password = new property({
	init : 0,
	check : "string",
	apply : function(value, old){
		console.log("apply passwd recieve value : ");
		console.log(value);
		console.log("apply passwd recieve old   : ");
		console.log(old);
	}
});

//Creating a simple function to catch data changes
function myListener(data){
	console.log("event work");
	console.log("recieve value : ");
	console.log(data.value);
	console.log("recieve old   : ");
	console.log(data.old);
}

password.addListener("changeValue", myListener);

If you use the console to check login & password properties, you will see that if you change the login or the password, you may have some log from apply functions or event catch appearing !

An example more « complex » of usage will be a binding system :

function bind(obj1, obj2){
  obj1.addListener("changeValue", function(data){
    //This if is needed if you bind obj1 <-> obj2 in a two way (obj1 bind obj2 and obj2 bind obj1), if the if were not here, an infinite loop will appear in this case.
    if(obj2.get() !== data.value){
      obj2.set(data.value);
    }
  }
}

Simple isn’t it ? As you can see, the property system is not so complicated, and may add to some pretty usefull features…

Feel free to modify/update it. The code here is from my own brain, I didn’t look, even once, how Qooxdoo implements their own properties system, so it may have some strong differences in behaviour from the Qooxdoo one !
Of course, this can also be used in an evironment such as Node.JS, which is this case will be used to have an fine event system between many objects, updating themselves everytime a listened object change one of his properties !

Publicités

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s

%d blogueurs aiment cette page :