Skip to content
davidedelpapa edited this page Aug 27, 2020 · 8 revisions

YEW Megatutorial - Tut 03

Good things come to those who wait...

You can see the parts of this series here:

  • Tut 01 - Introduction
  • Tut 02 - Extending the example
  • Tut 03 - This article
  • Tut 04 - ...and services for all
  • Tut 05 - Drive through libraries
  • Tut 06 - Custom Custom Custom
  • Tut 07 - Custom like a bike
  • Tut 08 - Dr. Ferris, I presume? Web Geography, injected with Rust! [P. I]
  • Tut 09 - It rains cats and dogs... and crabs! Rusty Meto-Geography [P. II]

We keep on extending... With services and more.

Part 1: Intro to dirty talking (to the console)

Code to follow this tutorial

The code has been tagged with the relative tutorial and part.

git clone https://github.com/davidedelpapa/yew-tutorial.git
cd yew-tutorial
git checkout tags/v3p1a

I know you have been following me so far. So apart from typos, and some missing dependencies, I hope you have done well so far. Your only concern so far has been to copy and understand the code. What if you had to do it from scratch yourself? I've been doing the old programmer's way: try and error.

So it's time to talk a little about debugging. Speaking of which, debugging WASM is not that easy, so as all lazy programmers I try first the easy way around: printing and logging!

In javascript it's easy:

console.log("Something's happening");

We log straight to the console. This is the equivalent of our trusty println! used for the same debug purposes, but on the terminal (really the stdout). What about Yew?

We start by use-ing the appropriate module in app.rs

use rand::prelude::*;
use yew::prelude::*;
use yew::services::ConsoleService;

Then we need to update our App struct:

pub struct App {
    items: Vec<i64>,
    link: ComponentLink<Self>,
    console: ConsoleService,
}

Never forget to update our constructor, the create fn:

    fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
        App {
            link,
            items: Vec::new(),
            console: ConsoleService::new(),
        }
    }

Now we can use our trusty console, just as we would in JS:

    fn update(&mut self, msg: Self::Message) -> ShouldRender {
        match msg {
            Msg::AddOne => {
                let added: i64 = random();
                self.items.push(added);
                self.console.log(format!("Added {}", added).as_str());
            }
            Msg::RemoveOne => {
                let removed = self.items.pop();
                self.console
                    .log(format!("Removed {}", removed.unwrap_or_default()).as_str());
            }
        }
        true
    }

There is not much to that code but we just rearranged to have the random element to add in a variable to show, and we saved the popped element to show as well (using unwrap_or_default to handle the option)

Upon running we should see the javascript console being logged.

As you can see its is bundle.js who is actually logging, because there is where our WASM worker is called into play.

Code to follow this part

git checkout tags/v3p1b

Logging to the console as is is very primitive. We love our wine, our luxuries, and our logging levels, though. In fact, we can provide our WASM frontend and workers with the ability to log different levels of suspicious activities for debugging purposes

We can issue an info:

self.console.info("Added 1 elemet to the vec");

We can issue a warning, or we can even issue a more severe error

let removed = self.items.pop();
match removed {
    Some(x) => self.console.warn(format!("Removed {}", x).as_str()),
    None => self.console.error("No more elements to remove!"),
};

Of course we can delve a little deeper and discover some more interesting stuff, like set a counter (to see how many times a certain part of a loop has been called, for example), or set timers, to record elapsed time. You can also clear the console, group and ungroup with a level of indentation, and, better still: trace the calls and make javascript asserts. MAybe one day I'll write a tutorial on the subject.

Reference documentation is found on the ConsoleService documentation.

Part 2: some more dirty talk

Code to follow this part

git checkout tags/v3p2a

Lowering ourselves in the realm of services, we find a handy little service: a dialog.

This service corresponds really to 2 modal windows: alert, and confirm. Modal widows take focus out of the current page, so must be used wisely.

Alert

The alert is just a small window with a message and an OK button. It just really shows to the user important stuff, stealing the focus from the page. Thus we will test it on a trivial case, just to bother our user. Well, really just because right now there's nothing rally important to communicate, it's just for the sake of showing the modal.

In our src/app.rs we need first to update the services' use line:

use yew::services::{ConsoleService, DialogService};

Then we need to update our App struct, and our constructor, as usual:

pub struct App {
    items: Vec<i64>,
    link: ComponentLink<Self>,
    console: ConsoleService,
    dialog: DialogService,
}
    fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
        App {
            link,
            items: Vec::new(),
            console: ConsoleService::new(),
            dialog: DialogService::new(),
        }
    }

Now, just to be less frivolous than expected (I know you were thinking of updating the error log with a dialog prompting the f*ing user to stop deleting stuff when the list is empty; however, we are nice to our users, aren't we?)

pub enum Msg {
    AddOne,
    RemoveOne,
    About,
}

Now you do have a feeling of where I'm headed with this, don't ya?

<header>
    <h2>{"Items: "}</h2>
    <button onclick=self.link.callback(|_| Msg::About)>{ "About" }</button>
</header>

With our trusty useless modal now showing

Confirm

Code to follow this part

git checkout tags/v3p2b

The Confirm dialogue is actually the less useless of the two modals, since it returns something: it is a dialogue that steals the focus from the page, and shows a message with an OK and a CANCEL button. It returns a bool, with true meaning OK, flase all other cases.

I know you are thinking: there are two cases, really, so what's wrong with saying false for CANCEl? well, if the browser is set to ignore in-page dialogs, then cofirm returns also false, without asking any question.

Well, let's add an action, in the most useless place...

match removed {
    Some(x) => self.console.warn(format!("Removed {}", x).as_str()),
    None => {
        self.console.error("No more elements to remove!");
        let user_is_a_monkey = self
            .dialog
            .confirm("Are you dum? There are no more elements to remove!");
        if user_is_a_monkey {
            self.dialog.alert("I kenw it!");
        } else {
            self.dialog.alert(
                "Maybe it was an error, there are no more elements to remove!",
            );
        }
    }
};

And.. run!

As I was saying, afeter the first dialogue Firefox shows a tick box in the modal. If you mark it, the page will not show any more modals, thus the value of the confirm will always be false even when not showing.

We can confirm this confirm behavior by logging the value of user_is_a_monkey

match removed {
    Some(x) => self.console.warn(format!("Removed {}", x).as_str()),
    None => {
        self.console.error("No more elements to remove!");
        let user_is_a_monkey = self
            .dialog
            .confirm("Are you dum? There are no more elements to remove!");
        self.console
            .debug(format!("Confirm value: {}", user_is_a_monkey).as_str());
    }
};

As we can see if we select the tik to the don't bother me mode, we log falses even though the dialog's not showing.

We will continue in the next istallment of this tutorial talking about more services.

Yew Tutorial

Source

Clone this wiki locally