Advanced Element Construction
Let's take a look at a few slightly more advanced way of setting up elements using the html!
macro.
Accessing the real DOM node
If we wish to gain access to the underlying DOM node, we have to use the with_node!
macro provided by DOMINATOR.
With this, we gain direct access to all methods on the actual DOM node.
fn with_node_example(blur_signal: impl Signal<Item = ()> + 'static) -> Dom {
html!("div", {
.with_node!(element => {
.future(clone!(element => async move {
blur_signal.to_future().await;
// Call some functions on the real DOM node!
element.blur().expect_throw("Failed to blur!");
element.scroll_into_view()
}))
.apply(|builder| {
info!("Inserting a {} node!", element.tag_name());
builder
})
})
})
}
By using the with_node!
macro in the html macros method block, we gain access to the DOM node reference inside the with_node
s apply block.
A bit more concretely; the .future()
and .apply()
methods are applied to our newly constructed div
builder as usual, but inside the async body we have access to the element reference to call blur on it when the signal future resolves!
Note that the apply function allows us to access the DOM node reference before it is inserted into the DOM tree.
Constructing elements with multiple child nodes
Very often, we need to create an element with several child nodes. We may also need to mix text node with html element nodes in the child list. Some children may be dynamically created based on the content of a signal, with static siblings.
Luckily, the html!
macro makes this trivial to do from DOMINATOR!
Take a look at this multinode example:
html!("div", {
.child(html!("span", { .text("first child") }))
.text("A text node")
.child_signal(always(Some(html!("span", { .text("Some dynamic node") }))))
});
As you can see in the method block of the macro, we simply make multiple calls to the various child-inserting methods. This particular example expands into the following DOM structure:
<div>
<span>first child</span>
A text node
<span>Some dynamic node</span>
</div>
This even works with fragments
and children_vec
, which may expand into several dynamically allocated children:
(To read more about fragments and how they work, see the fragments)
let some_vec = MutableVec::new_with_values(vec![1, 2, 3 ]);
let some_other_vec = MutableVec::new_with_values(vec![1, 2, 3 ]);
let some_fragment = fragment!(move {
.text("Hi there")
.children_signal_vec(some_vec.signal_vec()
.map(|v|
html!("span", {
.text(format!("Dynamic child in fragment #{v}").as_str())
})))
});
html!("div", {
.fragment(&some_fragment)
.children_signal_vec(some_other_vec.signal_vec()
.map(|v|
html!("span", {
.text(format!("Dynamic child fragment #{v}").as_str())
})))
})