Skeleton Navigation: Replacing Bootstrap with Aurelia-Materialize bridge components

This post will guide you through a clean installation of aurelia-skeleton-navigation and changing that example app to use Aurelia-Materialize bridge instead of Bootstrap.

We will use the JSPM version in this guide.

  • Get the skeleton navigation project from here: https://github.com/aurelia/skeleton-navigation/releases
  • Extract the skeleton-esnext directory as this is the one using JSPM. You can also use skeleton-typescript but we’ll discuss esnext here.
  • Change your working directory to the directory you’ve just extracted.

Now you need to install the dependencies for npm and jspm. This might take a while.

Issue the following commands on the console:

$ npm install
$ jspm install

Next, remove Bootstrap:

$ jspm uninstall bootstrap

And remove all references to Bootstrap.
In src/main.js remove the first line:

import 'bootstrap';

In src/app.html remove this line:

<require from="bootstrap/css/bootstrap.css"></require>

Change your package.json and add the following override for jspm (as a child of the “jspm” property):

"overrides": {
  "npm:materialize-css@0.98.2": {
    "main": "dist/js/materialize",
    "format": "global",
    "shim": {
      "dist/js/materialize": {
        "deps": [
          "jquery",
          "../css/materialize.css!"
        ]
      }
    }
  }
}

Remember to adjust Materialize’s version to the one you’re installing – this line:

npm:materialize-css@0.98.2

Now install necessary JSPM plugins, Materialize-CSS and the bridge:

$ jspm install css aurelia-materialize-bridge npm:materialize-css aurelia-event-aggregator

Include a reference to Material Design icons in your index.html:

<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">

Change your src/main.js to look like this:

export function configure(aurelia) {
 return aurelia.loader.loadModule('materialize-css').then(() => {
   aurelia.use
   .standardConfiguration()
   .developmentLogging()
   .plugin('aurelia-materialize-bridge', bridge => bridge.useAll() );

   return aurelia.start().then(() => aurelia.setRoot());
 });
}

When you start gulp watch now, you should already see a “materialized” navigation skeleton app. What you will see immediately is a pretty borked layout, so let’s fix this.

Replace your src/nav-bar.html template with the following content:

<template bindable="router">
  <md-navbar fixed="true">
    <a href="#" class="brand-logo left"><span class="flow-text">${router.title}</span></a>
    <ul class="right hide-on-med-and-down">
      <li md-waves repeat.for="row of router.navigation" class="${row.isActive ? 'active' : ''}">
        <a href.bind="row.href">${row.title}</a>
      </li>
    </ul>
  </md-navbar>
</template>

Replace your src/child-router.html template with the following content:

<template>
  <section class="au-animate">
    <h2>${heading}</h2>
    <div class="row">
      <div class="col m2">
        <md-well router.bind="router"></md-well>
      </div>
      <div class="col m10" style="padding: 0">
        <router-view></router-view>
      </div>
    </div>
  </section>
</template>

Replace your src/users.html template with the following content:

<template>
  <require from="blur-image"></require>

  <section class="au-animate">
    <h2>${heading}</h2>
    <div class="row au-stagger">
      <div class="col s6 m3 card-container au-animate" repeat.for="user of users">
        <div class="card">
          <canvas class="header-bg" width="250" height="70" blur-image.bind="image"></canvas>
            <div class="avatar">
              <img src.bind="user.avatar_url" crossorigin ref="image"/>
            </div>
            <div class="content">
              <p class="name">${user.login}</p>
              <p><a target="_blank" class="btn btn-default" href.bind="user.html_url">Contact</a></p>
            </div>
          </div>
      </div>
    </div>
  </section>
</template>

You get the idea here: replace Bootstrap’s css grid with Materialize’s css grid. 🙂

Next, replace the Bootstrap controls of src/welcome.html with Materialize components:

<template>
  <section class="au-animate">
    <h2>${heading}</h2>
    <form role="form" submit.delegate="submit()">
      <md-input md-label="First Name" md-value.bind="firstName"></md-input>
      <md-input md-label="Last Name" md-value.bind="lastName"></md-input>
      <md-card md-title="Full name">
        <p class="help-block">${fullName | upper}</p>
      </md-card>
      <button md-button md-waves="color: light;" type="submit">Submit</button>
    </form>
  </section>
</template>

Now you have a materialized skeleton navigation.

Note: in some cases (it’s not clear to me when this happens), Materialize will not be initialized completely when Aurelia starts. This leads to several global functions or jQuery plugins not being available at the time a component is initialized.

In this case, you would need to wait for Materialize to finish. I’ve created a small brute-force-ish function to deal with this.

function waitForMaterialize() {
  return new Promise((resolve, reject) => {
    let iterations = 0;
    const handler = window.setInterval(() => {
      iterations++;
      let ma = window.Materialize;
      if (
        ma.elementOrParentIsFixed &&
        ma.escapeHash &&
        ma.fadeInImage &&
        ma.guid &&
        ma.scrollFire &&
        ma.showStaggeredList &&
        ma.toast &&
        ma.updateTextFields
      ) {
        console.log(`waited ${iterations} iterations for Materialize to finish loading`);
        window.clearInterval(handler);
        resolve();
      }
    }, 1);
  });
}

Add this function to main.js and call this method from the “configure” function:

export function configure(aurelia) {
 return aurelia.loader.loadModule('materialize-css').then(() => {
   aurelia.use
   .standardConfiguration()
   .developmentLogging()
   .plugin('aurelia-materialize-bridge', bridge => bridge.useAll() );

   return waitForMaterialize().then(() => {
     return aurelia.start().then(() => aurelia.setRoot());
   });
 });
}

Thanks to Steven Boyd for pointing this out!

 

2 thoughts on “Skeleton Navigation: Replacing Bootstrap with Aurelia-Materialize bridge components”

  1. Thanks for the post.

    I’m wondering whether or not waitForMaterialize() is still necessary or if there is/will be a cleaner way so that this method isn’t necessary?

    Thank you

  2. Hey Steven,

    it’s not always that straightforward, unfortunately. Sometimes it’s needed and sometimes it’s not needed. I followed the steps as I wrote it and it wasn’t necessary.

    But you’re right, I shall at least mention it.

    Thanks and Regards
    Daniel

Leave a Reply

Your email address will not be published. Required fields are marked *