Document Information

Last modified:
2008/04/30 15:47 by ecker

Defining Properties

This article is meant for qooxdoo 0.7 and above. For the qooxdoo 0.6.x version please read the corresponding documentation.

Please also take a look at Property Features to get an compact overview of the available features.

When to use properties?

Dynamic properties in qooxdoo are extremely powerful and convenient. As they support advanced features like validation, events and so on, they might not be quite as lean and fast as an ordinarily coded property that only supports a setter and getter. If you do not need these advanced features or the variable you want to store is extremely time critical, it might be better not to use qooxdoo’s dynamic properties in those cases. You might instead want to create your own setters and getters (if needed) and store the value just as a hidden private variable (e.g. __varName) inside your object.

Basic property declaration

The following code creates a property myProperty and the corresponding functions like setMyProperty() and getMyProperty().

qx.Class.define(
...
properties : {
  myProperty : { nullable : true }
}
...

You should define at least one of the attributes init, nullable or inheritable. Otherwise, the first call to the getter would stop with an exception because the computed value is not (yet) defined.

As an alternative to the init key you could set the init value of the property by calling an initializing function this.initPropertyName(value) in the constructor. See below for details.

Working with the property value

You have multiple possibilities to react on each property change. With change the modification of a property is meant, where the old and the new values differ from each other.

As a class developer the easiest solution with the best performance is to define an apply method. As a user of a class (the one who creates instances) it is the best to simply attach an event listener to the instance, if such an corresponding event is provided in the property declaration.

Defining an apply method

To attach an apply method you must add a key apply to your configuration which points to a name of a functions which needs to be available in your members section. As the apply method normally should not be called directly, it is always a good idea to make the method at least protected by prefixing the name with an underscore _.

The return value of the apply method isn’t used at all. Also the second parameter may be leaved out.

Example

properties : {
  width : { apply : "_applyWidth" }
},
 
members : 
{
  _applyWidth : function(value, old) {
    // do something...
  }
}

The applying method is only called, when the value has changed.

Providing an event interface

For the users of a class it is in many cases a nice idea to also support an event to react on property changes. The event is defined using the event key where the value is the name of the event which should be fired.

qooxdoo fires a qx.event.type.ChangeEvent which supports the methods getValue() and getOldValue() to allow easy access to the new and old property value, respectively.

Events are only useful for public properties. Events for protected and private properties are usually not a good idea.

Example

properties : {
  label : { event : "changeLabel" }
}
...
// later in your application code:
obj.addEventListener("changeLabel", function(e) {
  alert(e.getValue());
});

Supporting init values

Init values are supported by all properties. These values are stored separately by the property engine. This way it is possible to fallback to the init value when property values are being reset.

Defining an init value

There are two ways to set an init value of a property.

Init value in declaration

The preferred way for regular init values is to simply declare them by an init key in the property configuration map. You can use this key standalone or in combination with nullable and/or inheritable.

properties : {
  myProperty : { init : "hello" }
}

Init value in constructor

Alternatively, you could set the init value of the property in the constructor of the class. This is only recommended for cases where a declaration of an init value as explained above is not sufficient.

Using an initializing function this.initPropertyName(value) in the constructor would allow you to assign complex non-primitive types (so-called “reference types” like Array, Object) that should not be shared among instances, but be unique on instance level. Another scenario would be to use a localizable init value. Because this.tr() cannot be used in the property definition, you may either use the static qx.locale.Manager.tr() there instead, or use this.tr() in the call of the initializing function in the constructor.

Since qooxdoo 0.7.2 it is required to add a deferredInit:true to the property configuration to allow a deferred initialization for the above mentioned reference types.

qx.Class.define("qx.MyClass", {
  construct: function() {
    this.initMyProperty([1, 2, 4, 8]);
  },
  properties : {
    myProperty : { deferredInit : true}
  }
};

Applying an init value

It is possible to apply the init value using an user-defined apply method. To do this call the init method initPropertyName() somewhere in your constructor - this “change” will than trigger calling the apply method. Of course, this only makes sense in cases where you have at least an apply or event entry in the property definition.

If you do not use the init method you must be sure that the instances created from the classes are in a consistent state. The getter will return the init value even if not initialized. This may be acceptable in some cases, e.g. for properties without apply or event. But there are other cases, where the developer needs to be carefully and call the init method because otherwise the getter returns wrong information about the internal state (due to an inconsistency between init and applied value).

constructor : function()
{
  this.base(arguments);
 
  // Initialize color with predefined value
  this.initColor();
 
  // Initialize store with empty array
  this.initStore([]);
},
 
properties : 
{
  color : 
  {
    init : "black",
    apply : "_applyColor"
  },
 
  store : {
    apply : "_applyStore"
  }
},
 
members : 
{
  _applyColor : function(value, old) {
    // do something...
  },
  
  _applyStore : function(value, old) {
    // do something...
  }
}

In the above example you can see the different usage possibilities regarding properties and their init values. If you do not want to share “reference types” (like Array, Object) between instances, the init values of these have to be declared in the constructor and not in the property definition.

If an init value is given in the property declaration, the init method does not accept any parameters. The init methods always use the predefined init values. In cases where there is no init value given in the property declaration, it is possible to call the init method with one parameter, which represents the init value. This may be useful to apply reference types to each instance. Thus they would not be shared between instances.

Please remember that init values are not for incoming user values. Please use init only for class defined things, not for user values. Otherwise you torpedo the multi-value idea behind the dynamic properties.

Refining init values

Derived classes can refine the init value of a property defined by their super class. This is however the only modification which is allowed through inheritance. To refine a property just define two keys inside the property (re-)definition: init and refine. refine is a simple boolean flag which must be configured to true.

Normally properties could not be overridden. This is the reason for the refine flag . The flag informs the implementation that the developer is aware of the feature and the modification which should be applied.

properties : {
  width : { refine : true, init : 100 }
}

This will change the default value at definition time. refine is a better solution than a simple set call inside the constructor because it the initial value is stored in a separate namespace as the user value and so it is possible for the user to fall back to the default value suggested by the developer of a class.

Checking incoming values

You can validate incoming values by adding a check key to the corresponding property definition.

Predefined types

You can check against one of these predefined types:

  • Boolean, String, Number, Integer, Float, Double
  • Object, Array, Map
  • Class, Mixin, Interface, Theme
  • Error, RegExp, Function, Date, Node, Element, Document, Window, Event

Due to the fact that JavaScript only supports the Number data type, Float and Double are handled identically to Number. Both are still useful, though, as they are supported by the Javadoc-like comments and the API viewer.

properties : {
  width : { init : 0, check: "Integer" }
}

Possible values

One can define an explicit list of possible values:

properties : {
  color: { init : "black", check : [ "red", "blue", "orange" ] }
}

Providing a list of possible values only works with primitive types (like strings and numbers), but not with reference types (like objects, functions, etc.).

Instance checks

It is also possible to only allow for instances of a class. This is not an explicit class name check, but rather an instanceof check. This means also instances of any class derived from the given class will be accepted. The class is defined using a string, thereby to not influencing the load time dependencies of a class.

properties : {
  logger : { nullable : true, check : "qx.log.Logger" }
}

Interface checks

The incoming value can be checked against an interface, i.e. the value (typically an instance of a class) must implement the given interface. The interface is defined using a string, thereby not influencing the load time dependencies of a class.

properties : {
  application : { check : "qx.application.IApplication" }
}

Implementing custom checks

Custom checks are possible as well, using a custom function defined inside the property definition. This is useful for all complex checks which could not be solved with the built-in possibilities documented above.

properties : 
{
  progress : 
  { 
    init : 0, 
    check : function(value) {
      return !isNaN(value) && value >= 0 && value <= 100;
    }
  }
}

This example demonstrates how to handle numeric values which only accept a given range of numbers (here 0 .. 100). The possibilities for custom checks are only limited by the developer’s fantasy. ;-)

Alternative solution

As an alternative to the custom check function, you may also define a string which will directly be incorporated into the setters and used in a very efficient way. The above example could be coded like this:

properties : 
{
  progress : 
  { 
    init : 0, 
    check : "!isNaN(value) && value >= 0 && value <= 100"
  }
}

This is more efficient, particularly for checks involving rather small tests, as it omits the function call that would be needed in the variant above.

Enabling theme support

The property system supports multiple values per property as explained in the paragraph about the init values. The theme value is another possible value that can be stored in a property. It has a lower priority than the user value and a higher priority than the init value. The style() and unstyle() methods are part of qooxdoo’s theme layer and should not be invoked by the user directly.

setter                                    value                     resetter


setProperty(value)          ^             user             |        resetProperty()
                            |                              |
styleProperty(value)     Priority         theme        Fallback     unstyleProperty()
                            |                              |
initProperty([value])       |             init             v        n.a.

To enable theme support it is sufficient to add an themeable key to the property definition and set its value to true.

properties : {
  width : { themeable : true, init : 100, check : "Number" }
}

themeable should only be enabled for truely theme-relevant properties like width, color and border, but not for functional properties like enabled, tabIndex, etc.

Up to 0.7-alpha2 and shortly after the property attribute appearance that was renamed to themeable to make the wording more consistent.

Working with inheritance

Another great feature of the new property system is inheritance. This is primarily meant for widgets, but should be usable in independent parent-children architectures, too.

Inheritance quickly becomes nothing short of vital for the property system, if you consider that it can reduce redundancy dramatically. It is advantageous both in terms of coding size and storage space, because a value only needs to be declared once for multiple objects inside an hierarchy. Beyond declaring such an inheritable property once, only intended exceptions to the inherited values need to be given to locally override those values.

The inheritance as supported by qooxdoo’s properties is comparable to the inheritance known from CSS. This means, for example, that all otherwise undefined values of inheritable properties automatically fall back to the corresponding parent’s value.

Each property may also have an explicit user value of string “inherit”. The inherited value, which is normally only used as a fallback value, can thus be emphasized by setting “inherit” explicitly. The user may set a property to “inherit” in order to enforce lookup by inheritance, and thereby ignoring init and appearance values.

To mark a property as inheritable simply add the key inheritable and set it to true:

properties : {
  color : { inheritable : true, nullable : true }
}

Optionally, you can configure an init value of inherit. This is especially a good idea if the property should not be nullable:

properties : {
  color : { inheritable : true, init: "inherit" }
}

Inheritable CSS properties

To give you an idea for what kind of custom properties inheritance is particularly useful, the following list of prominent CSS properties which support inheritance may be a good orientation:

  • color
  • cursor
  • font, font-family, ...
  • line-height
  • list-style
  • text-align

This list of CSS properties is only meant for orientation and does not reflect any of qooxdoo widget properties.

Internal methods

The property documentation in the user manual explains the public, non-internal methods for each property. However, there are some more, which are not meant for public use:

  • refreshPropertyName(): For properties which are inheritable. Used by the inheritance system to transfer values from parent to child widgets.
  • stylePropertyName(): For properties with appearance enabled. Used to store a separate value for the appearance of this property. Used by the appearance layer.
  • unstylePropertyName(): For properties with appearance enabled. Used to reset the separately stored appearance value of this property. Used by the appearance layer.

Defining property groups

Property groups is a convenient feature as it automatically generates setters and resetters (but no getters) for a group of properties. A definition of such a group reads:

properties : {
  location : { group : [ "left", "top" ] }
}

As you can see, property groups are defined in the same map as “regular” properties. From a user perspective the API with setters and resetters is equivalent to the API of regular properties:

obj.setLocation( 50, 100);
 
// instead of
// obj.setLeft(50);
// obj.setTop(100);

Shorthand support

Additionaly, you may also provide a mode which modifies the incoming data before calling the setter of each group members. Currently, the only available modifier is shorthand, which emulates the well-known CSS shorthand support for qooxdoo properties. For more information regarding this feature, please have a look at the user manual. The definition of such a property group reads:

properties : 
{
  padding : 
  { 
    group : [ "paddingTop", "paddingRight", "paddingBottom", "paddingLeft" ], 
    mode : "shorthand" 
  }
}

For example, this would allow to set the property in the following way:

obj.setPadding( 10, 20 );
 
// instead of
// obj.setPaddingTop(10);
// obj.setPaddingRight(20);
// obj.setPaddingBottom(10);
// obj.setPaddingLeft(20);
}

Information

Last modified:
2008/04/30 15:47 by ecker

Account

Not logged in

 
 

Job Offers

To further improve qooxdoo we are seeking javascript developers. Read more...

Rich Ajax Platform (RAP)

RAP uses qooxdoo, Java and the Eclipse development model to build rich web applications. Read more...

qooxdoo Web Toolkit (QWT)

Similar to GWT this framework allows to create impressive qooxdoo applications just using Java. Read more...

Pustefix

Pustefix is a MVC-based web application framework using Java and XML/XSLT. Read more...

 
SourceForge.net Logo

Bad Behavior has blocked 0 potential spam attempts in the last 7 days.