Wire Components"
Raymondchao (talk | contribs) |
m (remove empty version history (via JWB)) |
||
(14 intermediate revisions by 4 users not shown) | |||
Line 2: | Line 2: | ||
=Wire Components= | =Wire Components= | ||
− | In <javadoc>org.zkoss.zk.ui.select.SelectorComposer</javadoc>, when you specify a <code>@Wire</code> annotation on a field or setter method, the SelectorComposer will automatically find the component and assign it to the field or pass it into the setter method. | + | In a controller that extends <javadoc>org.zkoss.zk.ui.select.SelectorComposer</javadoc>, when you specify a <code>@Wire</code> annotation on a field or setter method, the SelectorComposer will automatically find the target component and assign it to the field or pass it into the setter method. It only finds the target component among the applied component and its child components. |
− | You can either | + | == Wire with ID selector by Default == |
+ | You can either specify <strong>component selector syntax</strong>, as the matching criteria for wiring, or leave it empty to wire by '''component id (default)'''. For example, | ||
ZUL: | ZUL: | ||
Line 13: | Line 14: | ||
</window> | </window> | ||
</source> | </source> | ||
+ | * For this case, <code>MyComposer</code> can only wire <code><window></code> and its child components. | ||
Controller: | Controller: | ||
<source lang="java"> | <source lang="java"> | ||
+ | @Wire | ||
+ | Button btn; // wire to the button with id "btn" | ||
@Wire("window > textbox") | @Wire("window > textbox") | ||
Textbox tb; // wire to the first textbox whose parent is a window | Textbox tb; // wire to the first textbox whose parent is a window | ||
− | |||
− | |||
</source> | </source> | ||
− | + | =CSS3-like Selectors= | |
− | |||
− | |||
The string value in <code>@Wire</code> annotation is a <strong>component selector</strong>, which shares an analogous syntax of CSS3 selector. The selector specifies matching criteria against the component tree under the component which applies to this composer. | The string value in <code>@Wire</code> annotation is a <strong>component selector</strong>, which shares an analogous syntax of CSS3 selector. The selector specifies matching criteria against the component tree under the component which applies to this composer. | ||
− | Given a selector in <code>@Wire</code> annotation, the SelectorComposer will wire a field to the component of '''the first match''' (in a depth-first-search sense) if the data type of the field is a subtype of <code>Component</code>. Alternatively, if the field type is subtype of <code>Collection</code>, it will wire to an instance of <code>Collection</code> containing all the matched components. | + | Given a selector in <code>@Wire</code> annotation, the SelectorComposer will wire a field to the component of '''the first match''' (in a depth-first-search sense) if the data type of the field is a subtype of <code>Component</code>. Alternatively, if the field type is a subtype of <code>Collection</code>, it will wire to an instance of <code>Collection</code> containing all the matched components. |
− | The syntax | + | The syntax elements of selectors are described as the following: |
− | + | ==Type== | |
The component type as in ZUML definition, case insensitive. | The component type as in ZUML definition, case insensitive. | ||
<source lang="java"> | <source lang="java"> | ||
Line 38: | Line 38: | ||
</source> | </source> | ||
− | + | ==Combinator== | |
Combinator constraints the relative position of components. | Combinator constraints the relative position of components. | ||
<source lang="java"> | <source lang="java"> | ||
Line 57: | Line 57: | ||
</source> | </source> | ||
− | + | ==ID== | |
The component id. | The component id. | ||
<source lang="java"> | <source lang="java"> | ||
@Wire("label#lb") | @Wire("label#lb") | ||
− | Label label; // wire to the first label of id "lb" in the same id space of root component | + | Label label; // wire to the first label of id "lb" in the same id space of the root component |
@Wire("#btn") | @Wire("#btn") | ||
Button btn; // wire to the first component of id "btn", if not a Button, an exception will be thrown. | Button btn; // wire to the first component of id "btn", if not a Button, an exception will be thrown. | ||
</source> | </source> | ||
− | Unlike CSS3, the id only | + | Unlike CSS3, the id only refers to the component in the same IdSpace of the previous level or root component. For example, given zul |
<source lang="xml"> | <source lang="xml"> | ||
<window apply="foo.MyComposer"> | <window apply="foo.MyComposer"> | ||
Line 95: | Line 95: | ||
</source> | </source> | ||
− | + | ==Class== | |
The sclass of component. For example, | The sclass of component. For example, | ||
<source lang="xml"> | <source lang="xml"> | ||
Line 118: | Line 118: | ||
</source> | </source> | ||
− | + | ==Attribute== | |
− | The attributes on components, which means the value obtained from calling corresponding getter method on the component. | + | The attributes on components, which means the value obtained from calling the corresponding getter method on the component. |
* Note: <code>[id="myid"]</code> does not restrict id space like <code>#myid</code> does, so they are '''not''' equivalent. | * Note: <code>[id="myid"]</code> does not restrict id space like <code>#myid</code> does, so they are '''not''' equivalent. | ||
Line 128: | Line 128: | ||
</source> | </source> | ||
− | + | ==Pseudo Class== | |
A pseudo class is a custom criterion on a component. There are a few default pseudo classes available: | A pseudo class is a custom criterion on a component. There are a few default pseudo classes available: | ||
Line 143: | Line 143: | ||
</source> | </source> | ||
− | The <code>nth-child</code> and <code>nth-last-child</code> pseudo classes | + | The <code>nth-child</code> and <code>nth-last-child</code> pseudo classes parameters can also take a pattern, which follows [http://www.w3.org/TR/css3-selectors/#nth-child-pseudo CSS3 specification]. |
− | + | ==Asterisk== | |
Asterisk simply matches anything. It is more useful when working with combinators: | Asterisk simply matches anything. It is more useful when working with combinators: | ||
Line 157: | Line 157: | ||
</source> | </source> | ||
− | + | ||
− | Multiple selectors separated by | + | ==Multiple Selectors== |
+ | Multiple selectors separated by commas refer to an OR condition. For example, | ||
<source lang="java"> | <source lang="java"> | ||
Line 167: | Line 168: | ||
</source> | </source> | ||
+ | |||
+ | ==Shadow Selectors== | ||
+ | {{versionSince| 8.0.1}} | ||
+ | Shadow selectors can only be used to select shadow related elements. | ||
+ | |||
+ | One pesudo class ''':host''' and one pseudo element '''::shadow''' have been added. | ||
+ | * ''':host''' select all shadow hosts, which are non-shadow elements, hosting at least one shadow element. | ||
+ | * ''':host(selector)''' select all shadow hosts matching the additional selector given. | ||
+ | * '''::shadow''' select all shadow roots, which are shadow elements, hosted by a non-shadow element. | ||
+ | |||
+ | <source lang="xml"> | ||
+ | <div id="host"> | ||
+ | <apply id="root" dynamicValue="true"><!-- set dynamicValue="true" to avoid being removed after render --> | ||
+ | <if id="if1" test="@load(vm.showLabel)"><!-- using @load will also prevent this element from being removed --> | ||
+ | <label id="lb1" value="some text here" /> | ||
+ | </if> | ||
+ | <if id="if2" test="false"><!-- no dynamicValue or data binding expression, will be removed after render --> | ||
+ | <label id="lb2" value="some more text here" /><!-- will not render because the if test equals false --> | ||
+ | </if> | ||
+ | </apply> | ||
+ | </div> | ||
+ | </source> | ||
+ | |||
+ | Here are some examples of using shadow selectors with the above zul | ||
+ | |||
+ | <source lang="java"> | ||
+ | @Wire(":host") // wire to the div with id "host", as it is the only shadow host | ||
+ | @Wire(":host(#div2)") // wire to nothing, no shadow host with the id "div2" exists | ||
+ | @Wire("::shadow") // wire to the apply with id "root", as it is the only shadow root | ||
+ | @Wire(":host if") // wire to nothing, cannot select from non-shadow(Div) into shadow(If) without using ::shadow | ||
+ | @Wire(":host::shadow if") // wire to if with the id "if1", as "if2" will be removed after render, thus cannot be selected | ||
+ | @Wire(":host::shadow if label") // wire to nothing, cannot select from shadow(If) to non-shadow(Label) | ||
+ | @Wire(":host label") // wire to "lb1", as the host(Div) and the first label are non-shadow elements | ||
+ | @Wire("#host::shadow#root #if1") // wire to if with the id "if1", with performance boost | ||
+ | </source> | ||
| | ||
− | + | ||
− | You can either put the <code>@Wire</code> annotation on field or method. In the latter case, it is equivalent to call the method with the matched component as the parameter. This feature allows a more delicate control on handling auto-wires. | + | =Wiring by Method= |
+ | You can either put the <code>@Wire</code> annotation on a field or method. In the latter case, it is equivalent to call the method with the matched component as the parameter. This feature allows a more delicate control on handling auto-wires. | ||
<source lang="java"> | <source lang="java"> | ||
Line 179: | Line 216: | ||
</source> | </source> | ||
− | In the example above, the SelectorComposer will find the grid of id "users" and call <code>initUserGrid</code> with the grid as parameter. | + | In the example above, the SelectorComposer will find the grid of id "users" and call <code>initUserGrid</code> with the grid as a parameter. |
* If the method is static or has wrong signature (more than one parameter), an exception will be thrown. | * If the method is static or has wrong signature (more than one parameter), an exception will be thrown. | ||
Line 189: | Line 226: | ||
| | ||
− | + | =Wiring a Collection= | |
− | You can also wire '''all''' matched components to a Collection field or by method | + | You can also wire '''all''' matched components to a Collection field or by method if the field is of Collection type or the method takes a Collection as the parameter. |
− | * If the field starts null or uninitialized or wiring by method, SelectorComposer will try to construct an appropriate instance and assign to the field or pass to method call. | + | * If the field starts null or uninitialized or wiring by method, SelectorComposer will try to construct an appropriate instance and assign it to the field or pass it to a method call. |
* If the field starts with an instance of Collection already, the collection will be cleared and filled with matched components. | * If the field starts with an instance of Collection already, the collection will be cleared and filled with matched components. | ||
* If it wires by method and the selector matches no components, an empty collection will be passed into the method call. | * If it wires by method and the selector matches no components, an empty collection will be passed into the method call. | ||
Line 209: | Line 246: | ||
</source> | </source> | ||
− | |||
− | |||
=Wiring Sequence= | =Wiring Sequence= | ||
While extending from <javadoc>org.zkoss.zk.ui.select.SelectorComposer</javadoc>, the fields and methods with the proper annotations will be wired automatically. Here is the sequence of wiring: | While extending from <javadoc>org.zkoss.zk.ui.select.SelectorComposer</javadoc>, the fields and methods with the proper annotations will be wired automatically. Here is the sequence of wiring: | ||
Line 221: | Line 256: | ||
| | ||
=Performance Tips= | =Performance Tips= | ||
− | The selector utility is implemented by a mixed strategy. In a selector sequence, the first few levels with | + | The selector utility is implemented by a mixed strategy. In a selector sequence, the first few levels with ids specified are handled by <javadoc method="getFellow(Component)">org.zkoss.zk.ui.Component</javadoc>, and the rest are covered by depth first search (DFS). In brief, the more ids you specify in the '''first few levels''' of a selector string, the more boost you can obtain in component finding. For example, |
<source lang="java"> | <source lang="java"> | ||
Line 251: | Line 286: | ||
</source> | </source> | ||
− | In brief, it is recommended to specify id in selector when you have a large component tree. If possible, you can specify id on all levels | + | In brief, it is recommended to specify id in selector when you have a large component tree. If possible, you can specify id on all levels to maximize the performance gain from the algorithm. |
| | ||
− | + | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
{{ZKDevelopersReferencePageFooter}} | {{ZKDevelopersReferencePageFooter}} |
Latest revision as of 04:37, 5 February 2024
Wire Components
In a controller that extends SelectorComposer, when you specify a @Wire
annotation on a field or setter method, the SelectorComposer will automatically find the target component and assign it to the field or pass it into the setter method. It only finds the target component among the applied component and its child components.
Wire with ID selector by Default
You can either specify component selector syntax, as the matching criteria for wiring, or leave it empty to wire by component id (default). For example,
ZUL:
<window apply="foo.MyComposer">
<textbox />
<button id="btn" />
</window>
- For this case,
MyComposer
can only wire<window>
and its child components.
Controller:
@Wire
Button btn; // wire to the button with id "btn"
@Wire("window > textbox")
Textbox tb; // wire to the first textbox whose parent is a window
CSS3-like Selectors
The string value in @Wire
annotation is a component selector, which shares an analogous syntax of CSS3 selector. The selector specifies matching criteria against the component tree under the component which applies to this composer.
Given a selector in @Wire
annotation, the SelectorComposer will wire a field to the component of the first match (in a depth-first-search sense) if the data type of the field is a subtype of Component
. Alternatively, if the field type is a subtype of Collection
, it will wire to an instance of Collection
containing all the matched components.
The syntax elements of selectors are described as the following:
Type
The component type as in ZUML definition, case insensitive.
@Wire("button")
Button btn; // wire to the first button.
Combinator
Combinator constraints the relative position of components.
@Wire("window button")
Button btn0; // wire to the first button who has an ancestor window
@Wire("window > button") // ">" refers to child
Button btn1; // wire to the first button whose parent is a window
@Wire("window + button") // "+" refers to adjacent sibling (next sibling)
Button btn2; // wire to the first button whose previous sibling is a window
@Wire("window ~ button") // "~" refers to general sibling
Button btn3; // wire to the first button who has an older sibling window
You can have any number of levels of combinators:
@Wire("window label + button")
Button btn4; // wire to the first button whose previous sibling is a label with an ancestor window.
ID
The component id.
@Wire("label#lb")
Label label; // wire to the first label of id "lb" in the same id space of the root component
@Wire("#btn")
Button btn; // wire to the first component of id "btn", if not a Button, an exception will be thrown.
Unlike CSS3, the id only refers to the component in the same IdSpace of the previous level or root component. For example, given zul
<window apply="foo.MyComposer">
<div>
<window id="win">
<div>
<button id="btn" /><!-- button 1 -->
<textbox id="tb" /><!-- textbox 1 -->
</div>
</window>
<button id="btn" /><!-- button 2 -->
</div>
</window>
@Wire("#btn")
Button btnA; // wire to button 2
@Wire("#win #btn")
Button btnB; // wire to button 1
@Wire("#win + #btn")
Button btnC; // wire to button 2
@Wire("#tb")
Textbox tbA; // fails, as there is no textbox of id "tb"
// in the id space of the root window (who applies to the composer).
@Wire("#win #tb")
Textbox tbB; // wire to textbox 1
Class
The sclass of component. For example,
<window apply="foo.MyComposer">
<div>
<button /><!-- button 1 -->
</div>
<span sclass="myclass">
<button /><!-- button 2 -->
</span>
<div sclass="myclass">
<button /><!-- button 3 -->
</div>
</window>
@Wire(".myclass button")
Button btnA; // wire to button 2
@Wire("div.myclass button")
Button btnB; // wire to button 3
Attribute
The attributes on components, which means the value obtained from calling the corresponding getter method on the component.
- Note:
[id="myid"]
does not restrict id space like#myid
does, so they are not equivalent.
@Wire("button[label='submit']")
Button btn; // wire to the first button whose getLabel() call returns "submit"
Pseudo Class
A pseudo class is a custom criterion on a component. There are a few default pseudo classes available:
@Wire("div:root") // matches only the root component
@Wire("div:first-child") // matches if the component is the first child among its siblings
@Wire("div:last-child") // matches if the component is the last child among its siblings
@Wire("div:only-child") // matches if the component is the only child of its parent
@Wire("div:empty") // matches if the component has no child
@Wire("div:nth-child(3)") // matches if the component is the 3rd child of its parent
@Wire("div:nth-child(even)") // matches if the component is an even child of its parent
@Wire("div:nth-last-child(3)") // matches if the component is the last 3rd child of its parent
@Wire("div:nth-last-child(even)") // matches if the component is an even child of its parent, counting from the end
The nth-child
and nth-last-child
pseudo classes parameters can also take a pattern, which follows CSS3 specification.
Asterisk
Asterisk simply matches anything. It is more useful when working with combinators:
@Wire("*")
Component rt; // wire to any component first met, which is the root.
@Wire("window#win > * > textbox")
Textbox textbox; // wire to the first grandchild textbox of the window with id "win"
@Wire("window#win + * + textbox")
Textbox textbox; // wire to the second next sibling textbox of the window with id "win"
Multiple Selectors
Multiple selectors separated by commas refer to an OR condition. For example,
@Wire("grid, listbox, tree")
MeshElement mesh; // wire to the first grid, listbox or tree component
@Wire("#win timebox, #win datebox")
InputElement input; // wire to the first timebox or datebox under window with id "win"
Shadow Selectors
Since 8.0.1 Shadow selectors can only be used to select shadow related elements.
One pesudo class :host and one pseudo element ::shadow have been added.
- :host select all shadow hosts, which are non-shadow elements, hosting at least one shadow element.
- :host(selector) select all shadow hosts matching the additional selector given.
- ::shadow select all shadow roots, which are shadow elements, hosted by a non-shadow element.
<div id="host">
<apply id="root" dynamicValue="true"><!-- set dynamicValue="true" to avoid being removed after render -->
<if id="if1" test="@load(vm.showLabel)"><!-- using @load will also prevent this element from being removed -->
<label id="lb1" value="some text here" />
</if>
<if id="if2" test="false"><!-- no dynamicValue or data binding expression, will be removed after render -->
<label id="lb2" value="some more text here" /><!-- will not render because the if test equals false -->
</if>
</apply>
</div>
Here are some examples of using shadow selectors with the above zul
@Wire(":host") // wire to the div with id "host", as it is the only shadow host
@Wire(":host(#div2)") // wire to nothing, no shadow host with the id "div2" exists
@Wire("::shadow") // wire to the apply with id "root", as it is the only shadow root
@Wire(":host if") // wire to nothing, cannot select from non-shadow(Div) into shadow(If) without using ::shadow
@Wire(":host::shadow if") // wire to if with the id "if1", as "if2" will be removed after render, thus cannot be selected
@Wire(":host::shadow if label") // wire to nothing, cannot select from shadow(If) to non-shadow(Label)
@Wire(":host label") // wire to "lb1", as the host(Div) and the first label are non-shadow elements
@Wire("#host::shadow#root #if1") // wire to if with the id "if1", with performance boost
Wiring by Method
You can either put the @Wire
annotation on a field or method. In the latter case, it is equivalent to call the method with the matched component as the parameter. This feature allows a more delicate control on handling auto-wires.
@Wire("grid#users")
private void initUserGrid(Grid grid) {
// ... your own handling
}
In the example above, the SelectorComposer will find the grid of id "users" and call initUserGrid
with the grid as a parameter.
- If the method is static or has wrong signature (more than one parameter), an exception will be thrown.
- Wiring by method requires a selector on
@Wire
annotation, otherwise an exception will be thrown. - If the component is not found, the method is still called, but with
null
value passed in. - Do not confuse
@Wire
with@Listen
, while the latter wires to events.
Wiring a Collection
You can also wire all matched components to a Collection field or by method if the field is of Collection type or the method takes a Collection as the parameter.
- If the field starts null or uninitialized or wiring by method, SelectorComposer will try to construct an appropriate instance and assign it to the field or pass it to a method call.
- If the field starts with an instance of Collection already, the collection will be cleared and filled with matched components.
- If it wires by method and the selector matches no components, an empty collection will be passed into the method call.
@Wire("textbox")
List<Textbox> boxes; // wire to an ArrayList containing all matched textboxes
@Wire("button")
Set<Button> buttons; // wire to a HashSet containing all matched buttons
@Wire("grid#users row")
List<Row> rows = new LinkedList<Row>(); // the LinkedList will be filled with matched row components.
@Wire("panel")
public void initPanels(List<Panel> panels) {
// ...
}
Wiring Sequence
While extending from SelectorComposer, the fields and methods with the proper annotations will be wired automatically. Here is the sequence of wiring:
- In Composer.doAfterCompose(T), it wires components to the fields and methods with the Wire annotation.
- Before
onCreate
event of the component which applies to the composer, the SelectorComposer will attempt to wire the null fields and methods again, for some of the components might have been generated after doAfterCompose() call.
Performance Tips
The selector utility is implemented by a mixed strategy. In a selector sequence, the first few levels with ids specified are handled by Component.getFellow(Component), and the rest are covered by depth first search (DFS). In brief, the more ids you specify in the first few levels of a selector string, the more boost you can obtain in component finding. For example,
@Wire("#win #hl > #btn") // fast, as it is entirely handled by getFellow()
@Wire("window hlayout > button") // slower, entirely handled by DFS
@Wire("#win hlayout > button") // first level is handled by getFellow(), other handled by DFS
@Wire("window #hl > #btn") // slower, as the first level has no id, all levels are handled by DFS
- Note: specifying id via attribute (for instance,
[id='myid']
) does not lead to the same performance boost.
In the case of multiple selectors, only the first few identical levels with ids enjoy the performance gain.
@Wire("#win #hl > button, #win #hl > toolbarbutton")
// the first two levels have boost
@Wire("#win #hl > #btn, #win #hl > #toolbtn")
// the first two levels have boost
@Wire("#win + #hl > #btn, #win #hl > #btn")
// only the first level has boost, as they differ in the first combinator
@Wire("#win hlayout > #btn, #win hlayout > #toolbtn")
// only the first level has boost, as the second level has no id specified
In brief, it is recommended to specify id in selector when you have a large component tree. If possible, you can specify id on all levels to maximize the performance gain from the algorithm.