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
structcomponents that own data - Wrap any data that can change in a
Mutable<T>,MutableVec<T>, orMutableBTreeMap<K, V> - Create render functions for each
structcomponent - 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§
- apply_
methods - Allows the application of methods to a type using the standard
dominatormacro syntax. Used internally by most of the other macroshtml!,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. SinceMutable<T>,MutableVec<T>, orMutableBTreeMap<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 macroswith_node!,with_cfg!,shadow_root!,pseudo!and will elide the firstthis:exprparameter in the macro calls. - class
- Creates a unique auto-generated CSS classname and injects it into
document.styleSheets. It wrapsClassBuilderand takes any of its methods. - clone
- Used as a shorthand syntax for calling
.clone()when parsing variables into functions. Due to'staticrequirements of many of the web apismoveandcloneare frequently required. - dom_
builder - Used to wrap an existing DOM node in a
DomBuilder<A>where the node being wrapped mustimpl AsRef<web_sys::Element>. - html
- Used to build a
Domand is a wrapper overDomBuilder<A>. By default the macro is internally typed toHtmlElement. Note if you want to generate SVG make sure to use the svg! macro. - link
- on_
click_ go_ to_ url - pseudo
- Used to generate pseudo classes and elements.
Usually called from within the
class!macro: - shadow_
root - Attaches DOM elements to the ShadowRoot of
a DOM element. The internal element type of the macro is
ShadowRootand it requiresShadowRootModeas a parameter. - stylesheet
- Creates a global CSS stylesheet: similar to creating a
.cssfile. It wrapsStylesheetBuilder, the first argument is the element selector(s) that needs toimplMultiStrand then any of theStylesheetBuildermethods. - svg
- Used to create SVG elements. Exactly like the
html!macro in that it wrapsDomBuilder<A>but it creates elements correctly namespaced to SVG. The default internal element type isSvgElement - with_
cfg - 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 requiredcargo buildarguments are added usingcargoArgs: ["--features", "foo"]. - with_
node - 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 withinhtml!where the first builder term is elided.
Structs§
Enums§
- Shadow
Root Mode - The
ShadowRootModeenum.