Crate dominator2
source · [−]Expand description
Zero-cost ultra-high-performance declarative DOM library using FRP signals for Rust!
Getting Started
Can be used as a standalone DOM rendering library but comes into its own when paired with
futures-signals. The best place to start is the
Signals tutorial
and then become acquainted with DomBuilder<A>
and html!
.
Idiomatic use is:
- Create
struct
components that own data - Wrap any data that can change in a
Mutable<T>
,MutableVec<T>
, orMutableBTreeMap<K, V>
- Create render functions for each
struct
component - Avoid local state in the DOM: all flags, triggers, data etc. for rendering should be stored in the
struct
- Call the render functions for each component in a chain all the way down
dominator
handles any necessary DOM updates on changes to the data. A basic app looks something like the
below, but refer to the examples for a
comprehensive set of best practices.
use dominator::{events::Click, html, Dom};
use futures_signals::signal::Mutable;
use wasm_bindgen::prelude::*;
// Top level App component
struct App {
name: String,
info: Info, // Info sub-component
}
impl App {
fn new(msg: &str) -> Self {
Self {
name: String::from("Dominator App"),
info: Info::new(msg),
}
}
fn render(app: Self) -> Dom {
html!("div", {
.text(&app.name)
// Call sub-component rendering functions
.child(Info::render(&app.info))
})
}
}
// Info sub-component
struct Info {
msg: Mutable<String>, // String value that can change over time
}
impl Info {
fn new(msg: &str) -> Self {
Self {
msg: Mutable::new(String::from(msg)),
}
}
fn render(info: &Self) -> Dom {
html!("button", {
// Text will automatically update on any changes to `msg`
.text_signal(info.msg.signal_cloned())
.event({
// Clone due to move + 'static
let msg = info.msg.clone();
// Update `msg` when clicked
move |_: Click| msg.set(String::from("Clicked"))
})
})
}
}
#[wasm_bindgen(start)]
pub fn main_js() -> Result<(), JsValue> {
let app = App::new("Hello world");
dominator::append_dom(&dominator::body(), App::render(app));
Ok(())
}
Handling 'static
web apis
There are lots of 'static
lifetime requirements for web apis and therefore calls to move
and clone
: so
Arc<T>
and Rc<T>
quickly come in handy. Which to use? As a general rule Arc<T>
for rust types unless they !Send
:
currently on WASM rust uses single threaded primitives
so there is no cost to using atomics and you will be ready for multi-threaded wasm;
use Rc<T>
for any JS values which are not thread safe and likely never will be.
There is the clone!
macro which is a nice shorthand for the many calls to .clone()
.
MutableVec<T>
and
MutableBTreeMap<K, V>
do
not impl Clone
so if you want to make them cloneable you will need to wrap them in an Arc<T>
or Rc<T>
.
Clone and Mutable<T>
Mutable<T>
uses Arc<T>
internally so cloning it
calls Arc::clone
and
will create another pointer to the same allocation. This can have subtle consequences when cloning any
structs with Mutable<T>
in.
#[derive(Clone)]
struct Info {
msg: Mutable<String>,
}
let item = Info {
msg: Mutable::new(String::from("original")),
};
let item_clone = item.clone();
// All updates to `msg` on the clone will update it on the original
item_clone.msg.set(String::from("both changed"));
assert_eq!(item.msg.get_cloned(), item_clone.msg.get_cloned());
// All updates to `msg` on the original will update it on the clone
item.msg.set(String::from("changed again"));
assert_eq!(item.msg.get_cloned(), item_clone.msg.get_cloned());
Mixins
dominator
has a great way of creating reusable functionalities and components: create a function with
the signature DomBuilder<A> -> DomBuilder<A>
.
fn mixin<A>(builder: DomBuilder<A>) -> DomBuilder<A> {
// Do some stuff with the builder and then return it
builder
}
It can then be called from within the html!
macro using the apply
or
apply_if
methods:
html!("div", {
.apply(mixin)
.apply_if(true, mixin)
})
js Bundler
dominator
works really well when paired with the rollup.js and the
rust rollup plugin. See the
examples folders for how to get setup.
Modules
Macros
Allows the application of methods to a type using the standard dominator
macro syntax. Used internally by
most of the other macros html!
, with_node!
, with_cfg!
,
shadow_root!
, dom_builder!
, stylesheet!
,
class!
. It puts each method call in a separate statement: this is to ensure that a
lock created inside one method call will not extend
to the rest of the method calls.
Since Mutable<T>
,
MutableVec<T>
, or
MutableBTreeMap<K, V>
all use
RwLock internally this is important for subsequent method calls
that may want to access the same data.
It also accepts some of the other macros with_node!
, with_cfg!
,
shadow_root!
, pseudo!
and will elide the first this:expr
parameter
in the macro calls.
Creates a unique auto-generated CSS classname and injects it into document.styleSheets
.
It wraps ClassBuilder
and takes any of its methods.
Used as a shorthand syntax for calling .clone()
when parsing variables into functions. Due to
'static
requirements of many of the web apis move
and clone
are frequently required.
Used to wrap an existing DOM node in a DomBuilder<A>
where the node being wrapped must impl AsRef<web_sys::Element>
.
Used to build a Dom
and is a wrapper over DomBuilder<A>
.
By default the macro is internally typed to HtmlElement
.
Note if you want to generate SVG make sure to use the svg! macro.
Used to generate pseudo classes and elements.
Usually called from within the class!
macro:
Attaches DOM elements to the ShadowRoot of
a DOM element. The internal element type of the macro is ShadowRoot
and it requires ShadowRootMode
as a parameter.
Creates a global CSS stylesheet: similar to creating a .css
file. It wraps StylesheetBuilder
,
the first argument is the element selector(s) that needs to impl
MultiStr
and then
any of the StylesheetBuilder
methods.
Used to create SVG elements. Exactly like the html!
macro in that it wraps DomBuilder<A>
but it creates elements correctly namespaced to SVG. The default internal element
type is SvgElement
Used to apply methods to a builder based upon a standard rust #[cfg(predicate)]
.
Typically used within one of the other builder macros where the first builder term is elided.
If used with the rollup-plugin-rust the
required cargo build
arguments are added using cargoArgs: ["--features", "foo"]
.
Provides owned access to the internal element of a dom_builder!
and is used for
passing the element into other builder methods. Runs before the element is inserted into the DOM.
Typically used within html!
where the first builder term is elided.
Structs
Enums
The ShadowRootMode
enum.