Maintain State with Dojo Stores

Dojo Stores and Processes

We previously looked at how you could maintain state with Dojo containers using a context for your application. To recap, containers are basically higher order components that you can use to add state management to widgets.

Using a context with containers makes the process fairly simple, however if you want to wire up multiple containers and routes with shared state, you might want to start looking at using Dojo Stores.

Dojo Stores work by using Processes to execute Commands with varying Operations against your state. You don’t need to define your operations, Dojo provides them for you. These operations are based on the JSON Patch format. They currently support add, remove, replace, and test. If you’re not familiar with these operations or they look intimidating, don’t worry, they are much simpler than they may seem at first.

In order to try and grasp how everything works, let’s create a small application that explores the Star Wars API.

API Explorer Widget

First thing we’ll do is create a widget that can explore the API. I’ll use a Listbox to display the endpoints of the API and a TextArea to display the raw JSON results of the selected endpoint.

// src/widgets/APIExplorer.tsx
import { tsx } from "@dojo/framework/widget-core/tsx";
import { WidgetBase } from "@dojo/framework/widget-core/WidgetBase";
import { watch } from "@dojo/framework/widget-core/decorators/watch";
import Listbox from "@dojo/widgets/listbox";
import TextArea from "@dojo/widgets/text-area";
import theme from "@dojo/themes/dojo";
import * as css from "./styles/APIExplorer.m.css";

import { ExplorerProperties, Item } from "../interfaces";

export class APIExplorer extends WidgetBase<ExplorerProperties> {
  @watch() index = 0;
  @watch() label = "";

  onAttach() {
    this.properties.fetchAllResults();
  }
  protected render() {
    return (
      <div classes={css.root}>
        <Listbox
          theme={theme}
          key="listbox1"
          activeIndex={this.index}
          widgetId="listbox1"
          optionData={this.properties.items}
          getOptionLabel={(option: Item) => option.label}
          getOptionSelected={(option: Item) => option.label === this.label}
          onActiveIndexChange={(index: number) => {
            this.index = index;
          }}
          onOptionSelect={(option: Item, index: number) => {
            this.label = option.label;
            this.properties.fetchData(option);
          }}
        />
        <TextArea rows={15} theme={theme} value={this.properties.result} />
      </div>
    );
  }
}

// src/interfaces.ts
export interface ExplorerProperties {
  items: Item[];
  result: string;
  fetchAllResults: () => void;
  fetchData: (item: Item) => void;
}

This widget has some local state to manage the selected value of the Listbox, but other than that, it relies on the properties passed to it in order to display anything useful. I’ll be using a container to pass those properties to this widget. But first, how would I make my queries and actually update my application state in the first place?

Processes

Processes are used to execute commands against your application store. It’s in the processes that you are going to be doing most of your heavy lifting for your application. This is where you can do queries, transformations, filtering, validation, and so on. There are factory functions in Dojo to help you create your commands.

// src/processes/starwarsProcesses.ts
import {
  createProcess,
  createCommandFactory
} from "@dojo/framework/stores/process";
import { replace } from "@dojo/framework/stores/state/operations";

import { State, Item } from "../interfaces";

// commandFactory typed to my application state
const commandFactory = createCommandFactory<State>();

In this application, I’m going to use the replace operation to make my state updates. If I were adding items to an array in my commands, I could do some interesting things using the at helper and add operation to insert new items into an array at specific indices.

Let me create a command that will fetch all the available endpoints of the Star Wars API.

// src/processes/starwarsProcesses.ts
const fetchItems = commandFactory<Item>(async ({ path }) => {
  const response = await fetch("https://swapi.co/api/");
  const json = await response.json();
  const items: Item[] = Object.keys(json).map(key => {
    return {
      label: key,
      value: json[key]
    };
  });
  return [replace(path("items"), items)];
});

I use my command factory to create a function that will make my query and then returns an array of operations. Why any array? Because I may want a single command do multiple state updates. In this case I use [replace(path(“items”), items)]. This may look odd at first, but the path method is a way to designate a property path in my state. If I want to use the value from my state I could use a get helper and write get(path(“items”)), which would return the items array. I was unsure how I felt about this method of accessing state at first, but it lends itself very well to a functional and reactive method of interacting with state that has really grown on me.

Now, I’ll create a command to fetch the results of one of the selected endpoints.

// src/processes/starwarsProcesses.ts
const fetchResult = commandFactory<Item>(async ({ path, payload }) => {
  const response = await fetch(payload.value);
  const result = await response.json();
  return [replace(path("result"), JSON.stringify(result, undefined, 2))];
});

Very similar to the previous command I am going to fetch the results of an endpoint, but that endpoint URL is coming from a payload object that was passed to my command. I’ve typed this payload to have a value property, but it will be any argument that has been passed to the process that will execute this command. We’ll see how that is done in a moment, but first we need to create a couple of processes that will be used to execute these commands.

// src/processes/starwarsProcesses.ts
export const fetchItemsProcess = createProcess("fetch-items", [fetchItems]);
export const fetchResultProcess = createProcess("fetch-result", [fetchResult]);

We create two processes to do two distinct operations on my state. What I find interesting here is that I can have a single process execute multiple commands that could in turn perform multiple operations against my state. I haven’t done this yet, but I find the fact that I could to be awesome!

Container

Let’s wrap our widget in a container that will use our processes to wire up properties and methods.

// src/containers/APIExplorerContainer.ts
import { Store } from "@dojo/framework/stores/Store";
import { StoreContainer } from "@dojo/framework/stores/StoreInjector";
import { APIExplorer } from "../widgets/APIExplorer";

import { State } from "../interfaces";

import {
  fetchItemsProcess,
  fetchResultProcess
} from "../processes/starwarsProcesses";

function getProperties(store: Store<State>): any {
  const { get, path } = store;
  
  return {
    items: get(path("items")),
    result: get(path("result")),
    fetchAllResults: fetchItemsProcess(store),
    fetchData: fetchResultProcess(store)
  };
}
// Use a StoreContainer
export const APIExplorerContainer = StoreContainer(
  APIExplorer,
  "state",
  { getProperties }
);

We are using a specific container called a StoreContainer that will inject our store in to our getProperties method. This method is how you can pass properties to your wrapped widget from the container.

You can see here that the store has helper methods, get and path, that I mentioned before to access the values of properties on the store. I can now pass processes that will execute commands against the store as methods that my widget is expecting as part of its properties.

Once this is done, I need to register my store with my application and inject it.

// src/main.tsx
const store = new Store();
const registry = registerStoreInjector(store);

class App extends WidgetBase {
  protected render() {
    return <APIExplorerContainer />;
  }
}

const r = renderer(() => <App />);
r.mount({ registry });

I use a helper called registerStoreInjector and then inject that store into a named state that I used in my container.

What you end up with is an application like this one.

Summary

There is a lot happening here, but what it boils down to is the following steps.

  • Create processes to execute commands against a Store
  • Wrap widget in a container to pass processes and store properties
  • Inject store into application

Any subsequent updates the store will be passed down to the widget. There is even more you could do with your store. I could have wrapped my widget in a StoreProvider and passed store properties down, and manually subscribe to store changes. Subscribing to store changes could be a very useful tool inside your applications for some granular control of managing widget visibility for example.

Processes even provide the ability to add middleware so you can add logging and authentication, or maybe payload validation without having to pollute your processes with extra code and possibly extra sources of errors.

At the end of the day, I have had a fun time learning how to use processes and stores in my applications. They provide a lot of flexibility in managing state and I can already see how useful they would be in building out an application as it grows!

Be sure to subscribe to the newsletter and stay up to date with the latest content!

Style Dojo Widgets

dojo styling

There’s an entire Dojo tutorial on creating custom themes, and if you look at any of the guides on creating widgets you will learn how to create css modules to use with your widgets and your applications.

But how about if you want to use some of the out-of-the-box widgets with Dojo? There is an entire library of ready to use widgets you can start building off for your applications today. We saw this when we created a datepicker, and there’s even widgets like select and button. Check out the widget showcase for more!

If you just wanted to quickly get started with a nice looking theme, you can use the Dojo theme. I’ll cover how to create your own themes another time, but for now, let’s assume you have a Dojo widget in your application and you really like the Dojo theme, but you would like to slightly make some adjustments. Not enough to warrant a custom theme, but maybe add some italic font, change a background here and there.

Dojo provides a way to do this in a CSS modules friendly manner. Every widget has a section in its documentation on the CSS classes you can override, such as those in the select widget. To get started, let’s add the Dojo theme to this widget.

import theme from "@dojo/themes/dojo";
...

// in your render method
<Select
  theme={theme} // apply the Dojo theme
  options={names}
  value={this.selectedValue}
  placeholder="Pick a name"
  onChange={this.onSelectChange}
/>

At this point, you’re able use a nice looking theme provided by Dojo. However, maybe I would like the placeholder text to be italicized, and I’d like to change the background color of the button and some other colors a bit. I don’t want to change much, just a few things.

.arrow {
  background: #959595;
  color: #fff;
}

.focused {
  color: #005e95;
}

.placeholder {
  font-style: italic;
}

.inputWrapper {
  color: #6e6e6e;
}

Now I have some simple CSS that matches the class names in the documentation. You can now apply these class names to your Select widget using a plain old JavaScript object that will map the extra classes to the widget key. You can see more detail here.

const SelectClasses = {
  "@dojo/widgets/select": {
    arrow: [css.arrow],
    focused: [css.focused],
    placeholder: [css.placeholder],
    inputWrapper: [css.inputWrapper]
  }
};

// in your render method
<Select
  theme={theme} // apply the Dojo theme
  classes={SelectClasses}
  options={names}
  value={this.selectedValue}
  placeholder="Pick a name"
  onChange={this.onSelectChange}
/>

This will now maintain the Dojo theme you are already using, but also apply the classes you are adding the widget as well. You end up with an application that looks something like below.

As you can see, it doesn’t take a lot of work to use the provided Dojo theme with your applications and apply your own little CSS sugar to the out-of-the-box Dojo widgets. You can also take this a step further and create your own custom theme for your application and even a reusable theme for a suite of applications! Happy dev’ing!

Build Time Rendering in Dojo

Dojo Build Time Rendering

You may have worked with other frameworks that support server side rendering. What it basically does is render the HTML of your page and pass it down to the client as it would look when the initial JavaScript loads and then you can interact with it and the JavaScript stuff works its magic to make a cool interactive application.

The Dojo method of doing this is much simpler. Instead of rendering the pages on the server, you can create your pages during the build process, and then you can just upload it anywhere.

You can find some information about Build Time Rendering on the github page for dojo/cli-build-app. You can get started by using the dojo/cli to quickly scaffold an application and modify it a bit.

Once you have your template application ready to go, let’s make some modifications. First thing we need to do in src/index.html is add a root div that the build time rendering tools can work with.

<!DOCTYPE html>
<html lang="en-us">
<head>
  <title>dojo-btr</title>
  <meta name="theme-color" content="#222127">
  <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
  <div id="root"></div> <!-- Add this element -->
</body>
</html>

Now we can set up the configuration for build time rendering. One thing to note is that by default, the template application uses hash routing, meaning that routes look like myapp/#about. This will generate a single index.html file in your build that will quickly load those routes. If you use a different history manager, it will create an index.html for each route.

{
  "build-app": {
    "build-time-render": {
      "root": "root",
      "paths": [
        "#home",
        "#about",
        "#profile"
      ]
  }
}

Note that I have prefixed my paths with a # so that the BTR can generate the pages correctly. The output of this is pretty interesting. Each route is stored in an array as strings, and as you change your route at runtime, it will load the HTML of that route as needed.

The benefit here is that your HTML is ready to go and the JavaScript parts just do their thing without having to do an initial render of your page. It makes for a very responsive experience. You get a lot of benefit from Build Time Rendering with some simple configuration, so take advantage of it!

You can see a sample of how this looks in a sample application I put together here. I also have it running live here.

Building a simple app in Dojo

dojo app

I’ve been thinking about how I could demonstrate building a basic Dojo application beyond a hello world app or a Todo app. There are some really good samples in the dojo/examples repo. Then I came across this react application for searching for emojis and who doesn’t have to search for emojis regularly, so I knew I found my demo. It also helps that the dojo template on Code Sandbox now uses TSX/JSX as the default.

Because the dojo template app uses JSX by default, it made this sample almost a complete one to one of the react sample. I won’t go into detail of this application line by line, but I do want to cover some core concepts it shows.

Get Meta

Meta in Dojo is meta information about your widget. Pretty meta right?

When you build Dojo widgets, you never touch the output HTML of your application. There is no widget method to get a reference to the DOM. This prevents you from inadvertently changing a DOM element that is referenced by Dojos virtual DOM engine, which would be bad. So don’t get too crazy here. But there are valid reasons for wanting to access a DOM node in your application. In the case of my emoji application, I am using a small library called clipboardjs to let me copy emojis to my clipboard from my application. This library requires I pass a DOM node it will use to copy data to the clipboard.

You can get this information in Dojo is via a meta. Dojo provides some metas out of the box for you, like Dimensions, Animations, Intersection, and more. You can implement your own custom meta to access DOM nodes using `@dojo/framework/widget-core/meta/Base`.

// src/widgets/ElementMeta.ts
import { Base as MetaBase } from "@dojo/framework/widget-core/meta/Base";

class ElementMeta extends MetaBase {
  get(key: string): Element {
    const node = this.getNode(key);
    return node as Element;
  }
}

export default ElementMeta;

The meta implements a get() method that will get the DOM node via a given key and return that DOM node. Now in my application, where I use clipboardjs, I can use my meta in combination with the this.meta() method of the Widget to get a referenced DOM node.

// src/widgets/EmojiResultsRow.tsx
import { tsx } from "@dojo/framework/widget-core/tsx";
import { WidgetBase } from "@dojo/framework/widget-core/WidgetBase";

import * as css from "./styles/EmojiResultsRow.m.css";

import ElementMeta from "./ElementMeta";
import * as Clipboard from "clipboard";

export interface EmojiResultsRowProperties {
  title: string;
  symbol: string;
}

export class EmojiResultsRow extends WidgetBase<EmojiResultsRowProperties> {
  clipboard: Clipboard = null;

  onAttach() {
    // use my meta to get a DOM node
    const element = this.meta(ElementMeta).get(this.properties.title);
    this.clipboard = new Clipboard(element);
  }
  onDetach() {
    this.clipboard.destroy();
  }

  protected render() {
    const { title, symbol } = this.properties;
    const codePointHex = symbol.codePointAt(0).toString(16);
    const src = `//cdn.jsdelivr.net/emojione/assets/png/${codePointHex}.png`;
    // provide a `key` property to my widget element to
    // reference with my meta
    return (
      <div
        key={title}
        classes={[css.root, "copy-to-clipboard"]}
        data-clipboard-text={symbol}
      >
        <img alt={title} src={src} />
        <span classes={[css.title]}>{title}</span>
        <span classes={[css.info]}>Click to copy emoji</span>
      </div>
    );
  }
}

export default EmojiResultsRow;

Now I am able to use my custom meta to get a DOM node created by my widget. This makes access to output DOM nodes flexible, but also protects me from shooting myself in the foot unintentionally. If I break my DOM, it is totally my fault now.

Core Widgets

Dojo provides a suite of widgets you can use for your own applications. This includes items like TimePicker, Select and layout widgets. For my application, I’m interested in having an input that I can use for search. Every time I update the input element, I want to filter the list of emojis shown in my application. So I’m going to wrap a TextInput widget so I can manage some local state and pass the value of the input to a filter method.

// src/widgets/SearchInput.tsx
...
export class SearchInput extends WidgetBase<SearchInputProperties> {
  @watch() private searchValue = "";

  private onChange(value) {
    if (!value) {
      return;
    }
    this.searchValue = value;
    const { handleChange } = this.properties;
    handleChange(value);
  }

  protected render() {
    return (
      <div classes={[css.root]}>
        <div>
          <TextInput
            placeholder="Search for emoji"
            value={this.searchValue}
            onInput={this.onChange}
          />
        </div>
      </div>
    );
  }
}

Yes, I could have used a regular <input type="text" /> here, but the TextInput is very convenient as it already has an onInput method I can use that passes the value of the input directly, and not an event I would need to do event.target.value which, because I am lazy, I can really appreciate. Then I would need to use a keyup event, and maybe do some handling for different keys to on whether I want to get my value and why hassle with all that when Dojo provides a nice way to do it already.

I am also taking advantage of the @watch decorator to manage local state in my widget. I talked about this method in more detail here. This makes it very simple to manage the value of my input at all times.

You can see the full application in action here.

You can see that building applications in Dojo provides some safety and flexibility for you to piece together everything you need to build solid, and awesome applications. Dojo isn’t just a toolkit anymore, it’s a full blown framework and has a lot to offer!

Intro to the Dojo Router

We took a quick look at the Dojo router when we reviewed the template application from the dojo cli. The template application provides almost everything you need to know about the Dojo router. But let’s take a little deeper look at routing.

Defining Routes

The template application does a great job of providing a clear way to configure your routes.

export default [
  {
    path: "home",
    outlet: "home",
    defaultRoute: true
  },
  {
    path: "about",
    outlet: "about"
  },
  {
    path: "profile/{username}",
    outlet: "profile"
  }
];

The routes are defined as an array of objects. each Route object has a RouteConfig interface with properties you can define. In the snippet above I have made one change. I have set the path for the profile route as profile/{username}. This means I will need to define a parameter to that route, which we’ll get to in a moment, but first let’s look at the options for a route config.

// dojo/framework/src/routing/interfaces.d.ts
export interface RouteConfig {
  path: string;
  outlet: string;
  children?: RouteConfig[];
  defaultParams?: Params;
  defaultRoute?: boolean;
}

That’s the beauty of working with TypeScript and Dojo, you can look at the types and interfaces of the code and use them as a guide as to how you should use the tools. The only required properties are path and outlet. One of the other properties we see defined in our configuration is the defaultRoute, which as you may have guessed is the default route of your application. Who says naming things is hard?!

The children property would be used if you had nested routes. You could also define some default parameters, which is really useful if you have a route the depends on parameters, and your route needs them to behave correctly.

Outlet

The first part of routing we need to look at is the Outlet. The Outlet is a higher order component that you use to wrap up widgets that are part of a designated route.

// src/App.ts
...
export default class App extends WidgetBase {
  protected render() {
    return v("div", { classes: [css.root] }, [
      w(Menu, {}),
      v("div", [
        // Outlet is where routes go
        // the decide which widgets
        // match each route
        w(Outlet, {
          key: "home",
          id: "home",
          renderer: () => w(Home, {})
        }),
        w(Outlet, {
          key: "about",
          id: "about",
          renderer: () => w(About, {})
        }),
        w(Outlet, {
          key: "profile",
          id: "profile",
          renderer: () => w(Profile, { username: "Dojo User" })
        })
      ])
    ]);
  }
}

Looking at the outlet, you can see that we define the id of the Outlet to match the Route configuration we defined. The actual widget rendered in the Route doesn’t have to match the id, but as you can see, it’s pretty good practice to do so. Keep the code readable please.

Outlets are pretty straightforward. Since they render the widget for a Route, they can also handle passing any URL parameters as properties to the widget.

Link and Parameters

Before we dive in to URL parameters, first we need to talk about how you can create a link to a route that is expecting parameters. We can define those parameters with a specific component in Dojo for working with routes, the Link component.

// src/widgets/Menu.ts
w(
  Link,
  {
    to: 'profile',
    key: 'profile',
    classes: [css.link],
    activeClasses: [css.selected],
    params: {
      username: 'odoe'
    }
  },
  ['Profile']
)

The Link component is designed specifically for creating links to routes and static paths in your application. They provide some sugar to regular anchor tags you can take advantage of in your apps. In this case, I am providing a value to the username parameter we defined for our route. This means that is will pass the object { username: ‘odoe’ } to my Outlet that I can then use to pass to my child widget.

// src/App.ts
w(Outlet, {
  key: 'profile',
  id: 'profile',
  renderer: ({ params }: MatchDetails) => {
    return w(Profile, { username: params.username });
  }
})

When you pass parameters to a URL in the Dojo router, your render method is passed the parameters for you to use in your application as needed. Now, although this method works fine, you can be more explicit in how you use your route parameters.

You can define query parameters in your routes and use them for more advanced usage. Let’s update the route configuration.

// src/routes.ts
export default [
  ...
  {
    path: "profile/{param}?{value}",
    outlet: "profile"
  }
];

Maybe we have different ways of searching for users in our backend API. We can search by name or id,.

// src/widgets/Menu.ts
w(
  Link,
  {
    to: 'profile',
    key: 'profile',
    classes: [css.link],
    activeClasses: [css.selected],
    params: {
      param: 'name',
      value: 'odoe'
    }
  },
  ['Profile']
)

Now we can update our Outlet to pass the correct information to the child widget.

// src/App.ts
w(Outlet, {
  key: 'profile',
  id: 'profile',
  renderer: ({ params, queryParams }: MatchDetails) => {
    const user = users.find((user: User) => {
      return user[params.param] == queryParams.value;
    }) as User;
    return w(Profile, { username: `${user.name} ${user.lastName}` });
  }
})

Now we have built a fairly generic way of passing parameters and values to our Outlet and being able to search for the correct username to use in our widget. We can search by the name value or an id value.

Default Parameters

So far we have been defining parameters in our Link, but maybe we want to define some default parameters directly in our route instead.

// src/routes.ts
export default [
  ...
  {
    path: 'profile/{param}?{value}',
    outlet: 'profile',
    defaultParams: {
      param: 'id',
      value: '2'
    }
  }
];

For out default route, we can decide to search by id with a value of 2. When you start dealing with URL parameters, everything is a string, so if you wanted to use real numbers, you would need to do some additional sanitization in your application, but I think we’ve dived pretty deep into setting up the Dojo router for starter use. Big thanks to Anthony Gubler for helping me out with some of my router questions, it was a big help.

Summary

As you can see, the Dojo router is very flexible in how you want to define your routes and parameters. Depending on how your backend APIs are defined, you could create some very powerful and scalable applications!

Intro to the Dojo Registry

dojo registry

The Dojo Registry is a powerful way you can work with widgets in your applications. We’ve had the opportunity to touch on the Registry in previous posts. We used it to inject context into our application with Dojo Containers and it’s also used when you want to use routing in your applications.

Basic Registry

The Registry allows you to do a few different things in your application. You can register widgets with the registry as string values and then reference the string values anywhere throughout your app.

For example, maybe I want to define a string value for an AboutMe widget used in my About page.

// src/main.ts
import Registry from '@dojo/framework/widget-core/Registry';
...
// define widgets
import AboutMe from './widgets/AboutMe';

const registry = new Registry();
registry.define('about-me', AboutMe);

// src/widgets/About.ts
import WidgetBase from '@dojo/framework/widget-core/WidgetBase';
import { w, v } from '@dojo/framework/widget-core/d';

import * as css from './styles/About.m.css';

export default class About extends WidgetBase {
  protected render() {
    return v('h1', { classes: [css.root] }, [
      'About Page',
      // can reference the string value
      // of widget without importing it
      w('about-me', {}, [])
    ]);
  }
}

This is pretty useful, as it can keep some of your widget code clean and you can define a series of widgets in a single location as you register them. Things start to get really interesting when we use the local registry of the widget to handle lazy loading via a registry decorator.

Registry Decorator

Each widget has it’s own local registry that you can use to lazy load a widget until you need it. Maybe I want to have a button on my page that when clicked will load some other widget in its place. For the cli template application, maybe I have some profile information I want to show.

// src/widgets/MyProfile.ts
import WidgetBase from '@dojo/framework/widget-core/WidgetBase';
import { v } from '@dojo/framework/widget-core/d';

export default class MyProfile extends WidgetBase {
  protected render() {
    return v('aside', {}, [
      v('p', {}, [
        `I don't believe in the moon,
         I think it's just the back of the sun.`
      ])
    ]);
  }
}

This is a simple little widget, nothing too fancy. Now let’s add it to our profile widget with the registry decorator.

// src/widgets/Profile.ts
import { registry } from '@dojo/framework/widget-core/decorators/registry';
import { watch } from '@dojo/framework/widget-core/decorators/watch';

...

// the decorator allows us to use a dynamic import
// to lazy load this widget until it's needed
@registry('my-profile', () => import('./MyProfile'))
export default class Profile extends WidgetBase<ProfileProperties> {
  @watch() private _showProfile = false;

  private onButtonClick() {
    this._showProfile = true;
  }

  protected render() {
    const { username } = this.properties;
    // check if the `_showProfile` property
    // is true to determine if I should load
    // the `MyProfile` widget
    const node = this._showProfile ?
                  w('my-profile', {}) :
                  v('button', {
                    onclick: this.onButtonClick
                    }, [ 'Show Profile' ]);
    return v('h1', { classes: [css.root] }, [
      `Welcome ${username}!`,
      v('p', {} , [
        node
      ])
    ]);
  }
}

We can take advantage of the watch decorator to update a value of our widget to determine what our widget should look like. You can read more about the watch decorator in my previous blog post. That’s pretty awesome right! Because the registry decorator lets us use a dynamic import, the Dojo build system will create a bundle for that widget that can be lazy loaded. You can even do multiple entries with the registry decorator to allow access to multiple widgets in your own widgets.

You can see a demo of how this works in the following code sandbox.

Summary

The Registry is a pretty powerful tool you have at your disposal in Dojo. We’ve seen it used for routing, predefining a series of widgets you can use throughout your application, lazy-loading, and with containers and injectors. Once you get your feet wet with it, you’ll be swimming Registry goodness to help you build awesome applications!

Dojo CLI Template App

dojo cli template application

The release of Dojo 4 introduced some really nice new features in their build pipeline to optimize for progressive web apps, some performance improvements under the hood in their rendering engine, and more.

However, one of the really cool things I haven’t seen talked about too much is the new template application you get with the dojo cli. You can check out my earlier post on using the @dojo/cli to learn how to get started.

The previous template application gave a you a nice introduction to basic widgets and how to display the widget in your app. It was fine as an introduction, but if you wanted to do a little more, like routing, you had to do a little more research. Not anymore! The new template application comes with routing out of the box so you can quickly get up and running with a feature that you will probably end up using at some point in a larger application.

Unfortunately, the latest template app is not on code sandbox, most likely due to the routing not working correctly in that environment, at least not the last time I tried.

Here is what the template application looks like.

I have put up the untouched source for the template application on github.

Now let’s take a look at what you get with the new template app.

I’ll do a more detailed post on routing in the future, but you can read more details in the Dojo documentation. The key here is that each view for a route is defined by an Outlet. An Outlet is just a wrapper for widgets that will be displayed in that routes view.

// src/App.ts
import WidgetBase from "@dojo/framework/widget-core/WidgetBase";
import { v, w } from "@dojo/framework/widget-core/d";
import Outlet from "@dojo/framework/routing/Outlet";

import Menu from "./widgets/Menu";
import Home from "./widgets/Home";
import About from "./widgets/About";
import Profile from "./widgets/Profile";

import * as css from "./App.m.css";

export default class App extends WidgetBase {
  protected render() {
    return v("div", { classes: [css.root] }, [
      w(Menu, {}),
      v("div", [
        w(Outlet, {
          key: "home", id: "home", renderer: () => w(Home, {})
        }),
        w(Outlet, {
          key: "about", id: "about", renderer: () => w(About, {})
        }),
        w(Outlet, {
          key: "profile",
          id: "profile",
          renderer: () => w(Profile, {
            username: "Dojo User"
          })
        })
      ])
    ]);
  }
}

Ok, so let’s break this down a little bit. The w is a function to render widgets and v will create virtual dom nodes. You can see that in this case, what is happening is there is a top level menu, with a div underneath. In this div is where each Outlet is defined, with an id, key (optional), and what to display in the render method.

I won’t go in to detail on each view. They are fairly standard widgets, but let’s take a look at the routing part. The routes are defined in a simple object.

// src/routes.ts
export default [
  {
    path: "home",
    outlet: "home",
    defaultRoute: true
  },
  {
    path: "about",
    outlet: "about"
  },
  {
    path: "profile",
    outlet: "profile"
  }
];

Each route has a path, with the name of the outlet id, which coincides with the id of the outlet defined in the previous snippet. Super simple and straight forward. You can also see that the home route is defined as the defaultRoute.

Here is how the whole thing is put together.

// src/main.ts
import renderer from '@dojo/framework/widget-core/vdom';
import Registry from '@dojo/framework/widget-core/Registry';
import { w } from '@dojo/framework/widget-core/d';
import { registerRouterInjector } from '@dojo/framework/routing/RouterInjector';
import {
  registerThemeInjector
} from '@dojo/framework/widget-core/mixins/Themed';
import dojo from '@dojo/themes/dojo';
import '@dojo/themes/dojo/index.css';

import routes from './routes';
import App from './App';

const registry = new Registry();
registerRouterInjector(routes, registry);
registerThemeInjector(dojo, registry);

const r = renderer(() => w(App, {}));
r.mount({ registry });

I’ll go into more detail in the future, but you register your route with the Registry, which is a way that you can do more configuration with your widgets beyond just display them on the page. You even get a taste of working with themes via the ThemeInjector.

Once your routes are registered, you can then mount the application with the registry. If I were to do anything different here, it would probably be to do all the Registry work in a separate module, but that is just a preference.

I am really glad to see the new dojo cli template app giving users a solid start with routing and an introduction to the registry, which in my opinion are key components of building scalable applications.

Now, why is routing important in progressive web apps? It allows you to lazy load parts of your application until you need them. For example, in the template application some users may never click on the profile page, so why should your application load the files for that page unnecessarily . You can see what I mean in this animated image.

Here, you can see that the files for the pages are not loaded until I click on them. This is code splitting, something Dojo 1 was fantastic at and that the new Dojo takes advantage of webpack under the hood in their build tools to handle as well.

Watch for property changes in Widgets

dojo watch decorator

We’ve seen how you can manage more complex state in your Dojo applications with Containers, but with the release of Dojo 4 we now have access to a new @watch decorator.

This very useful for managing the internal state of your widgets, because you no longer have to concern yourself with having to call a widgets invalidate() method if you don’t want to.

For example, let’s say that I want to have a simple clock widget in my application that is just going to display the current time. For demo purposes, I’ll display the time up to the second.

I can create a Clock widget that will do exactly that.

class Clock extends WidgetBase {
  // use watch decorator so that any updates
  // to this property will now call the
  // internal invalidate() method and
  // rerender my widget
  @watch() private _currentTime = new Date();

  // a widget lifecycle method that is called
  // when a widget is added to the DOM
  onAttach() {
    // update time every second
    setInterval(() => {
      this._currentTime = new Date();
    }, 1000);
  }

  protected render() {
    return v("h1", { classes: css.root }, [
      `Time: ${this._currentTime.toLocaleTimeString()}!`
    ]);
  }
}

As you can see, this greatly simplifies my widget so that I can just update my internal state without having to worry about invalidating my widget. This is powerful stuff!

Here is a live demo of this application.

There have been some other great updates to Dojo 4 such as a simplified render method to mount Dojo widgets and much more!

Dojo Containers

Once you start building applications that begin to compose multiple widgets and you are trying to manage state across those widgets, you might want to start looking at Dojo Containers. Containers allow you to inject values into widget properties, without having to import state directly into your widget.

To do this, Dojo provides a higher order component, similar to what you might use with React. That HOC is located in the @dojo/framework/widget-core/Container.

Let’s say that you wanted to work with a streaming API and update your widget when the stream returns new data. We want to display this data in a simple list.

// src/widgets/Items.ts
export class Items extends WidgetBase<ItemsProperties> {
  protected render() {
    const { items } = this.properties;
    return v(
      "ul",
      { classes: css.root },
      items.map((x, idx) =>
        v("li", { innerHTML: x.name, key: `${x.name}-${idx}` })
      )
    );
  }
}

This widget has an items array in the properties. You could bind this widget directly a data store and update the widget when new data comes in, but again, maybe we want that data available in the parent widget, or other widgets in use.

Let’s create a parent Application container that will render this widget.

// src/containers/AppContainer.ts
class AppContainer extends WidgetBase<ItemsProperties> {
  protected render() {
    return v("div", {}, [w(Items, { items: this.properties.items })]);
  }
}

This particular container is not doing much other than passing its properties to the child Items widget.

To use the Dojo Container, we need to create a getProperties function that defines the properties returned to the Container.

// src/containers/AppContainer.ts
function getProperties(inject: Context, properties: any) {
  const { items } = inject;
  return { items };
}

Now we can wrap our AppContainer in the Dojo Container.

// src/containers/AppContainer.ts
export default Container(AppContainer, "state", { getProperties });

In this case "state" is the name I’m providing for my context, which I refer to as my injector since it allows me to inject values into my widgets.

At this point, you have an option for how to manage your state. You can use Dojo stores or you can create a class that accepts an invalidator and you can use this invalidator to let the higher order component know that state has changed and it will pass it to the widget that it has wrapped.

For now, let’s go with a class that takes an invalidator and call it a context for our container. We can cover Dojo stores in another post.

// src/context.ts
export default class Context {
  private _items: Item[];

  private _invalidator: () => void;

  constructor(invalidator: () => {}, items: Item[] = []) {
    this._items = items;
    this._invalidator = invalidator;
    // subscribe to updates from our stream
    stream.subscribe((a: Item) => {
      this._addItem(a);
    });
  }

  get items(): Item[] {
    return this._items;
  }

  private _addItem(item: Item) {
    this._items = [...this._items, item];
    // call the invalidator to update wrapped container
    this._invalidator();
  }
}

It’s in this Context that I am subscribing to my data stream and updating the items array when new data is streamed in.

Ok, let’s tie it all together in our main.ts that kick starts the whole application.

// src/main.ts
const registry = new Registry();
// the `defineInjector` will provider the invalidator
registry.defineInjector("state", (invalidator: () => any) => {
  // create a new context and return it
  const context = new Context(invalidator);
  return () => context;
});

const Projector = ProjectorMixin(AppContainer);
const projector = new Projector();
// pass the registry to the projector
projector.setProperties({ registry });

projector.append();

When the Registry is passed to the projector, it will make sure everything is wired up as needed.

This may seem like a few steps, but it makes state management very flexible in your widgets without having to bind widgets to a data source, which makes them incredibly reusable.

You could create containers for each individual widget in your application and manage their state independently, this would be very powerful!

You can see a sample of this application above here:

Be sure to subscribe to the newsletter and stay up to date with the latest content!

Web Components with Dojo

We previously saw how we can create a custom date picker with Dojo. If building custom widgets with Dojo wasn’t cool enough, one of the features that Dojo provides that set it apart is the ability to export your custom widgets to Web Components.

This is incredibly useful because you could create a encapsulated widget in Dojo, including all business logic and now reuse this component anywhere you want.

You can review the documentation on how to export widgets to web components here.

Firs thing you need to do is npm install @dojo/cli-build-widget in our DatePicker project. Once that is done, we just need to make some updates to the widget in our application to encapsulate some styles and other small bits.

First, let’s update src/widgets/styles/datepicker.m.css.

@import url("https://fonts.googleapis.com/css?family=Roboto");
@import url("https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css");

.root {
    font-family: "Roboto", sans-serif;
  text-align: center;
  padding: 0.5em;
  color: #000;
}

input {
  font-size: 1.2em;
}

button {
  color: #fff;
  background: rgb(16, 184, 10);
  padding: 1em;
}

.hidden {
  display: none;
}

.calendarcontainer {
  background: #fff;
}

Now we have included all the styling for our widget, including the required fonts into the css for the widget.

Next, we need to add the customElement decorator to our widget to define the tags and any attribute and event information.

// src/widgets/DatePicker.ts

import { v, w } from '@dojo/framework/widget-core/d';
import { WidgetBase } from '@dojo/framework/widget-core/WidgetBase';
import Calendar from '@dojo/widgets/calendar';
import EnhancedTextInput from '@dojo/widgets/enhanced-text-input';
// Used to define web component element tag name
import customElement from '@dojo/framework/widget-core/decorators/customElement';

import * as css from './styles/datePicker.m.css';

...

@customElement<DatePickerProperties>({
  tag: 'date-picker' // custom element can be defined as <date-picker></date-picker>
})
export class DatePicker extends WidgetBase<DatePickerProperties> {
  ...
}

export default DatePicker;

Now the last thing we need to do is update the .dojorc with a "build-widget" section.

{
  "build-widget": {
    "elements": ["src/widgets/DatePicker"]
  }
}

With all these changes in place, you should be able to now run dojo build widget --mode dist.

To test that our web component works correctly, we can create a new HTML file that uses it.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <link rel="stylesheet" href="./output/dist/date-picker/date-picker-1.0.0.css">
  <script src="./output/dist/date-picker/date-picker-1.0.0.js"></script>
  <title>Date Picker Demo</title>
</head>
<body>
  <!-- Custom Element -->
  <date-picker></date-picker>
</body>
</html>

You should see a page with your date picker component now!

You can find the source code here.

And there you go, we just exported a Dojo widget that we previously built into a reusable web component. Try it out with your own components and feel confident that you can build awesome widgets that you can use anywhere!