|
|
|
The Model-Driven System Builder
|
|
JeeWiz Architect's Guide
|
|
|
|
Contents >
10. JeeWiz Controls
|
|
|
10.3 Using Controls
This section describes
- how to create controls, via the helper method
- the shared functionality.
10.3.1 Creating Controls
|
To create a control, call the helper method 'getNewControl' on a model object -
we normally use $this, because it is guaranteed to be a model object.
This method takes one string parameter giving the name of the control:
#set( $myCtl = $this.getNewControl( "myControl" ) )
|
The engine checks that
- the name of the control matches a '.control' file available on the list of template directories.
(These are listed at the start of each build; the .control file needs to be in one of these directories, not a subdirectory.)
- the control defines any #abstractMethod methods declared in controls that this control extends.
If this is not the case, the control is effectively abstract and cannot be instantiated.
If all is well, a new JeeWiz control is created.
There are two variants of the above constructor that take an extra argument for initialisation:
10.3.2 Creating Control Families in start.vm
|
Families of controls are initialised in start.vm.
There are many approaches to structuring families of controls so that they can be created, initialilsed and looked up easily.
See various start.vm's in the standard template directories - java, bizobject, jsf and persistence.
10.3.3 Using Properties
|
All JeeWiz controls are instances of the same "JwControl" class
(see the implementation in jeewiz/engine/src/uk/co/nte/jw/engine/Jwcontrol.java).
This is different from the way model objects can work,
where there is the opportunity to specify additional Java functionality on top of the base class using a meta-model.
With controls, there is no Java-based functionality: all specific functionality comes from the .control file.
This class subclasses java.util.HashMap.
Therefore, controls can accept and return properties by virtue of Velocity conventions, e.g.
#set( $myCtl.propA = "propertyA" )
...
#if( $myCtl.propA == "propertyB" )
|
The effect here is exactly the same as for model objects.
There are a few built-in properties on controls:
|
| name
| the name of control, exactly as passed into the constructor.
|
|
| new
| creates and returns a new control, using the same name as the control this is invoked on
|
|
| clone
| creates and returns a clone of this control, including any properties set on the control.
Any ListMaps, HashMaps and collections are themselves cloned - a new instance is created and a copy of the contents made.
This is a shallow copy - the keys and values within the collections are not themselves cloned.
|
Properties on a control can also be set using component.properties approach, as described in the previous section.
These get put into the underlying HashMap of the control.
For a control $myControl created in a template, a property 'prop' can be
- referenced in a template by
$myControl.prop
- referenced in a method on the control by
$prop - this is the normal approach.
It is also possible to write $self.prop or $self.get( "prop" )
- set in a template by
#set( $myControl.prop="value" )
- set in a method on the control by
#set( $self.prop = $anotherObject ).
Properties can be of any type, and in particular other objects, as illustrated by the last example above.
10.3.4 Using Properties - Order Of Evaluation
|
Properties on a control - i.e. $myControl.prop in Velocity - are evaluated as follows:
- If the property is 'name', the original name under which the control was instantiated is returned.
In other words, saying
#set( $myControl.name =... ) is executed, and puts a new value into the HashMap, but
does not change the value retrieved by the expression $myControl.name
- Next, if a property is in the HashMap, that is returned as long as it is non-null.
It is possible to put a null value into a HashMap under a given key; if such an entry is retrieved in this case, the value is ignored.
(The value in the HashMap is available via
get(propertyName).)
Values returned in this step can be any object, not just strings.
- Now the engine looks for a suitable Velocity getter on this control
(or on its extensions),
following the JavaBeans naming convention. If a method exists, it is called.
The expectation is that the Velocity method will execute
#return( $expr ); if so, the expression in the #return is returned
as the value - even if it is null.
If not, then any output of the method is returned instead. For example, if getProp() is defined as follows:
#method( getProp )
RETURNED_VALUE
#end
|
then $myControl.prop will return "RETURNED_VALUE".
- Finally, if the property is "new" or "clone", then the actions described above are undertaken.
These rules are slightly different from the rules for model objects, which do not have special new/clone properties but may have Java getters.
There are two useful implications of the above order:
- A control can use the lazy evaluation pattern for properties.
In other words, it can define a getter method to calculate a property, and then set it into the HashMap for the control.
The first time the property is accessed, the method is called.
For subsequent accesses, the property is in the HashMap and will be used; there is no need to call the method again:
#method( getProp )
## do some calculation
#set( $self.prop = $returnValue )
#return( $prop )
#end
|
See the getOrmType() method in jeewiz/resources/hibernate/control/javaSqlTypeBase.control for a real-life example.
- You can override the action of
.new and .clone by defining getNew() and getClone()
methods in Velocity on the control or one of its super-controls.
10.3.5 Gotchas
|
There are two things to be aware of when coding methods in with controls:
- Using
$this instead of $self.
In most coding you do in JeeWiz, $this refers to the current object.
This is true of templates and patterns, and also methods on model objects.
So it is easy to get into the habit of thinking "$this is the current object".
This is mostly a problem when setting values on the object.
It is very easy to write #set( $this.property = ... ) to set a property on the current control,
but this in fact sets the property on the current model object.
In methods on controls, this is not the case: you must use $self to reference the current object - the control.
When you forget this, it is very annoying - not least because it takes a while to uncover the hidden assumption.
However, there is a positive benefit to this topic:
a control can be written to be aware of the context model object as well as the control itself.
- Stateful controls.
Some controls are not stateful - for example, the simple datatypes.
However, there are some that are; in particular the UI controls.
If you put any state on to a control and you get the instance by lookup in a table - e.g. $C.UISF.get( $type ) -
remember to get a new instance, by putting .new on the end.
 |
|
 |
Copyright (c) 2001-2008 New Technology/enterprise Ltd.
| |