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!

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!

Creating a DatePicker with Dojo

I recently talked about getting started with the @dojo/cli. This time around, I wanted to dive a little deeper and talk about using some of the provided widgets with Dojo to build a useful DatePicker widget.

This sample covers some pretty interesting concepts of Dojo, including widget composition, styling, date formatting with internationalization, and more.

Before we start, delete the default HelloWorld.ts and widget css file that came with the @dojo/cli output.

Create the widget

Our first step is to install the widgets library.

npm install --save @dojo/widgets

Now we can create a DatePicker.ts in the src/widgets folder.

// 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';

interface DatePickerProperties {
  selectedDate: Date;
}

interface DatePickerState {
  month?: number;
  year?: number;
  selectedDate?: Date;
}

export class DatePicker extends WidgetBase<DatePickerProperties> {
  state: DatePickerState = {
    month: 1,
    selectedDate: this.properties.selectedDate,
    year: 2018
  };

  protected render() {
    return v('div', [
      v(
        'section',
        {},
        [
          w(Calendar, {
            month: this.state.month,
            selectedDate: this.state.selectedDate,
            year: this.state.year,
            onMonthChange: (month: number) => {
              this.setState({ month: month });
            },
            onYearChange: (year: number) => {
              this.setState({ year: year });
            },
            onDateSelect: (date: Date) => {
              this.setState({ selectedDate: date });
            }
          })
        ]
      )
    ]);
  }

  // helper method to set the state
  protected setState(state: DatePickerState) {
    this.state = { ...this.state, ...state };
    this.invalidate();
  }
}

export default DatePicker;

In this widget, we are going to make use of the Calendar widget we get in @dojo/widgets.

This widget will have some simple state properties related to picking a date.

state = {
  month: 1,
  selectedDate: this.properties.selectedDate,
  year: 2018
};

You may also notice the helper method I added to help me update the state of my widget.

// helper method to set the state
protected setState(state: any) {
  this.state = { ...this.state, ...state };
  this.invalidate();
}

This will update the state object of my widget and call a this.invalidate() method that will update my widget based on the new state changes. This invalidate() method will be replaced by a @watch() decorator on properties in a future version of Dojo to simplify updates. Other than that, I pretty much followed the Calendar example provided in the documentation.

But let’s make things a little more interesting. I want an input box that will display my selected date. For this, I’m going to use the EnhancedTextInput.

w(EnhancedTextInput, {
  addonAfter: [
    v(
      'button',
      {},
      [
        v('i', {
          classes: [
            'fa',
            'fa-chevron-down'
          ]
        })
      ]
    )
  ],
  label: 'Pick a date',
  value: this.state.selectedDate
})

I wanted to use the EnhancedTextInput because it lets me add a button to it using the addonAfter contents. I found this to be very useful! You’ll notice I’m using Font Awesome to help me out here. Since we’re at it, let’s talk about some styling.

CSS

Create a src/widgets/styles/datepicker.m.css file.

/* src/widgets/styles/datepicker.m.css */
.root {
    text-align: center;
    padding: 0.5em;
    color: #000;
}

.hidden {
  display: none;
}

.calendarcontainer {
  background: #fff;
}

Notice I added a hidden class. We’ll use that in a moment. But first, let’s modify the src/main.css file.

/* src/main.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');

body {
    font-family: 'Roboto', sans-serif;
    color: #fff;
    background: #1d1f20;
}
input {
  font-size: 1.2em;
}

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

Here, I provide some styling for my input, my buttons and some typography.

With the css ready for the application, we can start making the widget a little more interactive.

Interactivity

Part of the functionality of the DatePicker is to have the calendar open and close on a button click. We can add a visible property to our state object.

state = {
  month: 1,
  selectedDate: this.properties.selectedDate,
  year: 2018,
  visible: false
};

Lets modify the button to update the visible state.

v(
  'button',
  {
    onclick: () => {
      this.setState({ visible: !this.state.visible });
    }
  },
  [
    v('i', {
      classes: [
        'fa',
        this.state.visible ? 'fa-chevron-up' : 'fa-chevron-down'
      ]
    })
  ]
)

Now onclick will update the visible state and that will determine the button icon. We’ll also update the container for the Calendar so it can toggle the visibility.

v(
  'section',
  {
    classes: [this.state.visible ? '' : css.hidden, css.calendarcontainer]
  },
  [
    w(Calendar, {
      ...
    })
  ]
)

AWESOME!! We now have a fully interactive DatePicker. But we’re not updating the value of the EnhancedTextInput with the selected date. But we can’t just show the date any normal way. We want our DatePicker to support various locales. We can do with @dojo/framework/i18n.

Internationalization

I’m not going to get in to detail on using i18n, it’s incredibly powerful. But we’re going to use it to support formatting our dates accordingly.

First, we need cldr-data for our application to use.

npm install --save cldr-data

Next, update the .dojorc file to use it.

{
    "build-app": {
        "locale": "en",
        "supportedLocales": [ "es", "fr", "hi", "ar", "ja" ],
        "cldrPaths": [
            "cldr-data/main/{locale}/ca-gregorian",
            "cldr-data/main/{locale}/dateFields",
            "cldr-data/main/{locale}/numbers",
            "cldr-data/main/{locale}/timeZoneNames",
            "cldr-data/supplemental/likelySubtags",
            "cldr-data/supplemental/numberingSystems",
            "cldr-data/supplemental/ordinals",
            "cldr-data/supplemental/plurals",
            "cldr-data/supplemental/timeData",
            "cldr-data/supplemental/weekData"
        ]
    },
    "test-intern": {},
    "create-app": {}
}

Now we can support various locales for working with dates.

// src/widgets/DatePicker.ts
import { formatDate } from '@dojo/framework/i18n/date';

...

w(EnhancedTextInput, {
  addonAfter: [
    ...
  ],
  label: 'Pick a date',
  value: formatDate(
    this.state.selectedDate || this.properties.selectedDate,
    { date: 'short' }
  )
})

We are going to use the formatDate() method to format our date in the EnhancedTextInput accordingly. I could take this a step further and provide the Pick a date text in various locales, which isn’t difficult to do. You can read more about supporting various languages here.

Put it all together

Now we can add the DatePicker to our main application.

// src/main.ts
...

class App extends WidgetBase {
  protected render() {
    return v('div', [
      w(DatePicker, { selectedDate: new Date() })
    ]);
  }
}
...

And voila! You have a finished and useable DatePicker with styling and internationalization. I’m sure you could make this look better than I did, I never claimed to be a good designer, I just know enough to get things done.

You can see the source code for the complete application on github.

I also want to point out that you can try out some Dojo in codesandbox, although I have had some issues with i18n in the sandbox, which is why this sample isn’t provided there.

Now you can go forward and make some awesome widgets of your own!

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

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!