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.

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!

Up and running with @dojo/cli

If you haven’t heard, dojo has gotten an upgrade! The latest version of dojo ha some pretty awesome features, not just to build widgets, but it also includes a robust i18n package for your internationalization needs, a nice selection of out-of-the-box widgets and a pretty nice way to theme your applications.

A key tool provided by dojo is the @dojo/cli.

To get started you will want to use npm to install it globally.

npm install -g @dojo/cli @dojo/cli-create-app

Once it’s installed, you can type dojo --help into your command line and you should get this spiffy help message.

Nice

You’re one step closer to world domination.

Now you want to actually create an application, which you do with the following.

dojo create app --name awesome-dojo-app

This will take about a minute or so to create your application directory and install all the dependencies for your scaffold application.

Once it’s done, you cd into your application directory and use the following command to run it.

dojo build --mode dev --watch --serve

Under the hood, the @dojo/cli is using webpack to build and bundle your application. What we’re doing here is:

  • build – command to build the application
  • --mode dev – use dev mode so that the builds are quicker and not production optimized
  • --watch – watch for any file changes and recompile
  • --serve – serve the application in a local server for us

You should be able to open your browser to http://localhost:9999/ and you would see the following

Success

I’ll let you peruse the application provided, but let us take a look at the widget code real quick.

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

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

const logo = require('./../img/logo.svg');

export class HelloWorld extends WidgetBase {
    protected render() {
        return v('div', { classes: css.root }, [
            v('img', { src: logo, classes: css.logo }),
            v('div', { classes: css.label }, ['Hello, Dojo 2 World!'])
        ]);
    }
}

export default HelloWorld;


You may not notice right away, but it’s all TypeScript. Notice, in this widget their are no types or interfaces defined, that’s because it’s all inferred by the TypeScript compiler to make sure it’s valid. The power of TypeScript!

Dojo has it’s own virtual dom engine for building widgets and uses hyperscript as the default for creating your apps. If you are familiar with React, hyperscript is what it’s like writing it without the JSX. But the v method is much shorter for dev purposes.

I really like writing my widgets this way, and I won’t go into too much detail on building a widget, I’ll cover that in another post and you can read more in the dojo tutorials.

But I do realize, some people really love their JSX, so no worries, you can still use JSX if you want.

Just change the .ts file extension to .tsx and convert the hyperscript to JSX like this.

// src/widgets/HelloWorld.tsx
import WidgetBase from '@dojo/framework/widget-core/WidgetBase';
// tslint:disable-next-line
import { tsx } from "@dojo/framework/widget-core/tsx";
import * as css from './styles/helloWorld.m.css';

const logo = require('./../img/logo.svg');

export class HelloWorld extends WidgetBase {
    protected render() {
        return (
            <div classes={css.root}>
                <img src={logo} classes={css.logo}></img>
                <div classes={css.label}>Hello Dojo!</div>
            </div>
        );
    }
}

export default HelloWorld;

We need to import the tsx module so that the JSX can be compiled and the default tslint doesn’t like unused modules, so you’ll also need to add a comment for tslint to ignore that line.

But there you go, you still use JSX in your Dojo widgets!

Be sure to check out the dojo tutorials for more great lessons and also the docs if you really want to dig in! And don’t forget to sign up for the newsletter to keep up to date with our content!