UIZE JavaScript Framework

2011 NEWS 2011-05-11 - Improved Declarative Widget Properties

The declarative syntax for widget properties has been improved to cover more cases.

1. Background

The declarative syntax for widget properties provides a way for data that is to be fed to a widget's client-side JavaScript code to be included as part of the HTML that is generated by server code.

As such, the declarative data can be generated by the same server logic that generates the HTML, and the state that is reflected in the particular HTML that is generated can be serialized to JSON for consumption by the widget's JavaScript code that runs on the client side. In this way, the widget's JavaScript doesn't have to try to divine the logical state that the widget is in by poking around in the HTML and DOM nodes generated initially by the server, but the declarative data explicitly tells the widget what its initial state should be to match the initial state of the HTML. The major benefit here is that it lets the server code encapsulate the generation of the HTML along with a declaration of widget state that is picked up when the widget is either adopted or otherwise created on the client side.

1.1. An Example

In the following example, HTML that is to be wired up by an instance of the Uize.Widget.HoverFader widget class is generated by the server, along with a declaration of widget properties that should be used for the widget instance.

EXAMPLE


  
    

  
    


  

Even though the widget instance is created and wired up by separate client side code, the server side code that generates the HTML is still able to provide widget properties for the instance. This is useful here because the server side code knows the CSS class name that is used for the link tags and that needs to be used to provide the Uize.Widget.HoverFader instance with nodes to wire up with the fancy hover effects. By encapsulating this in the server's code, if the CSS class name is modified in the HTML generation code, then the widget properties can be updated as well so that the client side wiring still works correctly. Most importantly, the client side wiring code is none the wiser about the change in the CSS class name - this is all handled by the server side HTML generation code.

2. Improvements

The declarative syntax for widget properties has been around for quite some time, but it has recently been improved in some important ways.

2.1. Widget Properties Are Now Picked Up Early

Widget properties are now picked up as early as possible, so that the property values can be available even to code written for the alphastructor of a widget class.

Provided that a widget is instantiated with an idPrefix specified (which happens automatically when adding a widget as a child widget of another widget), code in the Uize.Widget base class will look for the presence of the global variable for the declarative widget properties. If the variable is present, the properties will be harvested and stitched in to the properties object that is used to set the initial state of the widget instance and that is also available as a parameter to the alphastructor and omegastructor functions of a widget class.

EXAMPLE

/*** create a page widget ***/
  var page = new Uize.Widget.Page; // the default idPrefix is "page"

/*** declarative syntax widget properties for widget not yet created ***/
  window.$page_myWidget = {foo:'bar'}; // for the "myWidget" child widget

/*** create a test widget class ***/
  var MyWidgetClass = Uize.Widget.subclass (
    /*** the alphastructor ***/
      function (_properties) {
        alert (_properties.foo);   // alerts the text "bar"
        alert (this.get ('foo'));  // alerts the text "undefined" (set hasn't happened)
      },
    /*** the omegastructor ***/
      function (_properties) {
        alert (_properties.foo);   // alerts the text "bar" (it's still here, no surprise)
        alert (this.get ('foo'));  // alerts the text "bar" (the set has happened by now)
      }
  );

page.addChild ('myWidget'Uize.Widget);

The above example illustrates how the widget properties that are defined for a widget (using the declarative syntax) before the widget is actually created are available as early as the alphastructor of the widget. Because the alphastructor of the Uize.Widget base class executes before the alphastructor of the MyWidgetClass subclass, the declarative widget properties can be harvested and stitched into the properties parameter that is passed to both the alphastructor and omegastructor, so that the harvested properties are available during the construction code of subclasses.

What's important to note in this example is that the stitched in properties, just like any properties specified explicitly when instantiating a widget, are not yet set on the instance - that happens between the alphastructor and omegastructor. So, using the get instance method to query the value of the foo property in the alphastructor yields the value undefined, while doing the same query inside the omegastructor yields the value 'bar'.

2.2. Widget Properties Can Now Address Widget Trees

Widget properties specified for a widget using the declarative syntax can also contain widget properties for any or all of the widget's child widgets, or even child widgets of the widget's child widgets - all the way down to the deepest child widgets in the widget's widget tree - using the new children state property.

2.3. The New children State Property

The Uize.Widget base class has always supported a children instance property, but this instance property now has a companion state property of the same name but with special handling.

2.3.1. The children State Property vs the children Instance Property

The Uize.Widget class supports a children instance property as well as a children state property, and each has its own purpose.

2.3.1.1. The children Instance Property

The children instance property is a read-only property that provides access to a widget's child widgets.

For example, a widget named rgbSlider that is a child widget of a page widget could be accessed by a statement like page.children.rgbSlider, where the children property of the page widget instance provides access to all of its child widgets.

2.3.1.2. The children State Property

The children state property is a somewhat unique type of state property that provides a versatile way to distribute widget properties to a widget's child widgets.

When the value of the children state property is set, a conformer processes the value and attempts to "consume" it. The value specified should be an object, where each property of the object is treated as providing widget properties for a child widget. The conformer attempts to distribute those widget properties objects to the corresponding child widgets. For example, the statement page.set ({children:{rgbSlider:{value:'#ff0000'}}}) is attempting to distribute the widget properties {value:'#ff0000'} to the child widget named rgbSlider of the page widget.

For any properties objects for which there are no corresponding child widgets, they are retained in order to potentially be used later when a child widget that does correspond is added, at which time the properties will be supplied as overrides to any properties that are specified by the code adding the child widget.

2.3.2. The children State Property Allows Deep Setting

The new children state property allows properties to be distributed to any or all of a widget's child widgets, or even child widgets of the widget's child widgets - all the way down to the deepest child widgets in a widget's widget tree.

Consider the following example...

EXAMPLE

/*** build up a widget tree under the page widget ***/
  var
    page = new Uize.Widget.Page,
    rgbSliders = page.addChild ('rgbSliders',Uize.Widget)
  ;

  rgbSliders.addChild ('sliderR',Uize.Widget.Bar.Slider);
  rgbSliders.addChild ('sliderG',Uize.Widget.Bar.Slider);
  rgbSliders.addChild ('sliderB',Uize.Widget.Bar.Slider);

/*** now, set values for the deepest widgets in the widget tree ***/
  page.set ({
    children:{
      rgbSlider:{
        children:{
          sliderR:{value:127},
          sliderG:{value:255},
          sliderB:{value:0}
        }
      }
    }
  });

If the sliderR, sliderG, and sliderB slider widgets together represented an RGB color value, then the above code would initialize them to represent the color chartreuse. This is admittedly not a terribly compelling example of how to use the special behavior of the children state property, but it demonstrates the basic principle.

2.3.3. The children State Children Property Allows Deferred Pickup

The special behavior of the children state property allows widget properties to be specified for a child widget even before that child widget has been created.

EXAMPLE

/*** create a page widget, specifying properties for child widgets not yet created ***/
  var page = new Uize.Widget.Page ({
    children:{
      rgbSlider:{
        children:{
          sliderR:{value:127},
          sliderG:{value:255},
          sliderB:{value:0}
        }
      }
    }
  });

/*** now, create the child widgets for which properties were specified earlier ***/
  var rgbSliders = page.addChild ('rgbSliders',Uize.Widget);

  rgbSliders.addChild ('sliderR',Uize.Widget.Bar.Slider);
  rgbSliders.addChild ('sliderG',Uize.Widget.Bar.Slider);
  rgbSliders.addChild ('sliderB',Uize.Widget.Bar.Slider);

In the above example, a page widget is being created and a value is being specified for its children state property that is defining widget properties for child widgets in the page widget's widget tree that have not yet been created. Then, after the fact, a widget tree is fleshed out for the page widget, with child widgets that correspond to the structure of the object specified for the children state property when the page widget instance was created. Because of the special way that the children state property works, the widget properties defined earlier are "picked up" by the child widgets when they are created.

2.3.4. The children State Property is Respected in the Declarative Syntax

Along with using the children state property in the constructor of a widget instance, when calling the instance's set method, or in a widget adoption declaration, the children state property can also be used in the declarative syntax for widget properties.

EXAMPLE

/*** declare widget properties for a page widget ***/
  window.$page = {
    children:{
      rgbSlider:{
        children:{
          sliderR:{value:127},
          sliderG:{value:255},
          sliderB:{value:0}
        }
      }
    }
  };

/*** now, create the page widget and build a widget tree under it ***/
  var
    page = new Uize.Widget.Page,
    rgbSliders = page.addChild ('rgbSliders',Uize.Widget)
  ;
  rgbSliders.addChild ('sliderR',Uize.Widget.Bar.Slider);
  rgbSliders.addChild ('sliderG',Uize.Widget.Bar.Slider);
  rgbSliders.addChild ('sliderB',Uize.Widget.Bar.Slider);

In the above example, a $page property is being defined on the window object. Because the default idPrefix of the page widget is 'page', the $page property is effectively declaring widget properties for the page widget.

In our example, the page widget is created later. What you'll notice is that the value of the $page property is an object that contains a children property, which is itself an object containing a single rgbSlider property. This is declaring widget properties for the rgbSlider child widget of the page widget. Furthermore, the rgbSlider property is an object containing its own children property, which is declaring widget properties for the sliderR, sliderG, and sliderB child widgets of the rgbSlider child widget.

So, the widget properties declared for a widget using the declarative syntax may contain widget properties for any or all of the child widgets of the widget - all the way down to the deepest child widgets in the widget's widget tree.

2.3.5. Setting Doesn't Create or Destroy Children

Setting the value of the children state property is handled in a special way and has no effect on the actual child widgets for a widget, nor does it affect the value or contents of the children instance property.

When you set a value for the children state property, the widget instance tries to distribute any specified child widget properties to the widget's current child widgets. If widget properties are specified for a child that does not yet exist, that child widget is not created. Instead, the unapplied child widget properties are remembered. If a child widget is added later and its name corresponds to the name of a child widget for which unapplied widget properties have been remembered, then those widget properties are applied at the time of creating the child widget (for more details, see the section The children State Children Property Allows Deferred Pickup).