decor/Invalidating
decor/Invalidating
is a mixin class to for UI-related Custom Elements
that want to calculate computed properties once, and/or render the UI once, upon multiple property changes.
For that purpose the class adds two main lifecycle phases to the class, computeProperties()
and
refreshRendering()
, which are both called after a batch of property changes.
Invalidating extends decor/Stateful
, and delite/Widget
extends Invalidating.
Table of Contents
Setting Up Invalidating Changes Lifecycle Startup Lifecycle Implementing a Subclass Using Invalidating Rerendering From Scratch
Setting up Invalidating
Note that in order to be subject to invalidation the corresponding property must also haven been declared in the class.
Note that any property subject to compute properties phase will also be subject to the refresh rendering phase in a second phase.
Changes Lifecycle
After a batch of changes, decor/Invalidating
goes through two phases of processing: computeProperties()
and
refreshRendering()
.
The first phase, computeProperties()
, is used to reconcile instances properties after they have been set.
A typical example is making sure the value of a range component is correctly set between min and max values and
that the max value is bigger than min value.
This phase is optional and not all classes leveraging decor/Invalidating
will need it.
This phase can also be used to compute values for properties derived from other properties, such as an employee's
name which is "calculated" from the first name and last name.
The second phase, refreshRendering()
, is used to update the rendering of the class (usually a delite/Widget
)
based on the new values of the changed properties.
The advantage compared to doing that in a custom setter
is that for several properties changes the refresh rendering phase will be called only once,
leading to better performance by making sure the rendering is not modified several times in a row.
Startup Lifecycle
decor/Invalidating
has an initializeRendering()
method that it calls on startup, in order to do the initial
rendering of the DOM.
It also calls computeProperties(this, true)
and refreshRendering(this, true)
since those methods tend to have code that is useful for the initial rendering in addition to responding to changes.
So, the startup lifecycle is:
- mix in the parameters passed to the constructor
- call
computeProperties(this, true)
- call
initializeRendering()
- call
refreshRendering(this, true)
Note that delite/Widget
replaces the initializeRendering()
method with preRender()
, render()
, and postRender()
methods.
Implementing a Subclass
Once you have set up your class, you will need to implement the lifecycle methods to do the initial rendering,
and then to react to property changes.
This can be done by redefining the computeProperties()
, initializeRendering()
, and/or refreshRendering()
methods.
Both computeProperties()
and refreshRendering()
take as parameter a hash object which contains the name of the
properties that have triggered the refresh action. This is particularly useful when several properties are involved.
define(["dcl/dcl", "decor/Invalidating"/*, ...*/], function (dcl, Invalidating/*, ...*/) {
return dcl(Invalidating, {
a: true,
b: "value",
computeProperties: function (oldValues) {
if ("a" in oldValues) {
// do something logical that does not directly impact the DOM because "a" has changed
// To access new value, access directly to `this.a`
}
},
initializeRendering: function () {
// create the initial DOM
},
refreshRendering: function (oldValues) {
if ("b" in oldValues) {
// modify the DOM because "b" has changed
// To access new value, access directly to `this.b`
}
if ("a" in oldValues) {
}
}
});
});
Using Invalidating
Once setup you don't need anything special to use the Invalidating class. You just need to change one of the properties and the refresh methods will be called automatically for you.
If for some reason you want to invalidate a particular property without setting it explicitly
then you can call notifyCurrentValue(property)
.
In some cases you might want to force the rendering to occur right after a given property has been set.
For that you can use deliver()
.
In some cases you might want to avoid rendering from occurring even if a property was changed.
For that you can use discardChanges()
.
Rerendering from scratch
Usually initializeRendering()
(or for delite/Widget
, render()
) will be called only once, when the widget is created.
However, decor/Invalidating
has some special code to allow the widget to be rerendered from scratch,
i.e. code to call initializeRendering()
again, when certain properties are changed.
This is used by delite/Widget
when the widget's template has been changed.
You can trigger a second call to initializeRendering()
by defining a shouldInitializeRendering(oldValues)
method
that returns true when you want initializeRendering()
to be called.