[][src]Module mogwai::an_introduction

An introduction to the minimal, obvious, graphical web application interface.

Welcome!

Mogwai is a cute little library for building browser interfaces. It is cognitively small and runtime fast. It acheives these goals by doing very few things, but doing those things well.

The following is a short introduction to the concepts of Mogwai.

Constructing DOM Nodes

Building DOM is one of the main tasks of web development. In mogwai the quickest way to construct DOM nodes is by using one of the built in RSX macros.

builder! or view! are flavors of mogwai's RSX that evaluate to ViewBuilder or View respectively. RSX is a lot like react.js's JSX, except that it uses type checked rust expressions.

Most of the time you'll see the builder! macro used to create a ViewBuilder:

use::mogwai::prelude::*;

let my_div: ViewBuilder<HtmlElement> = builder!(
    <div class="my-div">
        <a href="http://zyghost.com">
            "Schellsan's website"
        </a>
    </div>
  );

The ViewBuilder can be converted into a View:


let view: View<HtmlElement> = View::from(my_div);

assert_eq!(
    view.html_string(),
    r#"<div class="my-div"><a href="http://zyghost.com">Schellsan's website</a></div>"#
);

As you can see the above example creates a DOM node with a link inside it:

<div class="my-div">
      <a href="http://zyghost.com">Schell's website</a>
</div>

Appending a View to the DOM

To append a View to the DOM's document.body we can use View::run:


view.run().unwrap();

View::run consumes the view, handing ownership to the browser window. Because of this it is only available on wasm32.

Dropping a View

By handing the View off to the window it never goes out of scope. This is important - when a View is dropped and all references to its inner DOM node are no longer in scope, that DOM node is removed from the DOM.

Wiring DOM

Views can be static like the one above, or they can change over time. Views get their dynamic values from the receiving end of a channel called a Receiver<T>. The transmitting end of the channel is called a Transmitter<T>. This should be somewhat familiar if you've ever used a channel in other rust libraries.

You can create a channel using the txrx() function. Then "wire" it into the DOM using RSX - simply assign it to an attribute or add it as a text node. You may even tuple it with an initial value.

Whenever the Transmitter<T> of the channel sends a value, the DOM is updated.

use::mogwai::prelude::*;

let (tx, rx) = txrx();

let my_view = view!(
    <div class="my-div">
        <a href="http://zyghost.com">
            // start with a value and update when a message
            // is received on rx.
            {("Schellsan's website", rx)}
        </a>
    </div>
);

tx.send(&"Gizmo's website".into());

A Transmitter can be used to send DOM events as messages, allowing your view to communicate with itself or other components:

use::mogwai::prelude::*;

let (tx, rx) = txrx();

let my_view = view!(
    <div class="my-div">
        <a href="#" on:click=tx.contra_map(|_: &Event| "Gizmo's website".to_string())>
            // start with a value and update when a message
            // is received on rx.
            {("Schellsan's website", rx)}
        </a>
    </div>
);

See txrx's module level documentation for more info on mapping Transmitters and Receivers.

Accessing the underlying DOM node

The View contains a reference to the raw DOM node, making it possible to manipulate the DOM by hand using Javascript FFI bindings and functions provided by the great web_sys crate:



let node: std::cell::Ref<HtmlElement> = my_view.dom_ref();
assert_eq!(
    node.inner_html(),
    r#"<a href="http://zyghost.com">Gizmo's website</a>"#
);

Components and more advanced wiring

For anything but the simplest view, it is recommended you use the Component trait to build your view components.

In bigger applications we often have circular dependencies between buttons, fields and other interface elements. When these complex situations arise we compartmentalize concerns into Components.

Other times we don't need a full component with an update cycle and instead we simply require transmitters, receivers and some handy folds and maps.

JavaScript interoperability

The library itself is a thin layer on top of the web-sys crate which provides raw bindings to tons of browser web APIs. Many of the DOM specific structs, enums and traits come from web-sys. Some of those types are re-exported by Mogwai's prelude. The most important trait to understand for the purposes of this introduction (and for writing web apps in Rust, in general) is the JsCast trait. Its dyn_into and dyn_ref functions are the primary way to cast JavaScript values as specific types.