Sunday, July 19, 2015

aurelia ABCs

I saw Rob Eisenberg speak on his creation, aurelia, on Wednesday night at the Rockstar Developers meetup.com meeting, and, no, that's not Rob Eisenberg pictured, it's Eric McVicker introducing him. Rob was elsewhere and his voice was piped into the room. We looked at his code on the TV pictured and another one just like it. Mr. Eisenberg was part of the team rewriting Angular to make it better and he suggests it needed it given that it uses dirty checking to manage its two-way binding allowing the performance hit that one takes to use two-way binding to balloon horribly as an application scales in size. He didn't like how things went in the rewrite (they're just gonna give up on two-way binding) and thus he just rolled his own solution which has a means of two-binding that doesn't suck. Beyond this benefit there will be support for aurelia. One cannot get a techie from Google on the phone to field questions about AngularJS, but aurelia will be supported by his Durandal team. Rob commented on how comic it was that "million dollar" software is architected around some of the goofy modern-day JS freebie frameworks for which there is no support whatsoever. react.js, like the new Angular to be, will perform fast because it also just doesn't use databinding, but who says databinding is an unsolvable problem. If you want to stick with it aurelia is now the way to go. Alright, we were told that aurelia is easy to install and while I know firsthand that's not true, we were also told that it is going to get easier in a couple of months. I will be patient because after seeing how awesome aurelia is I do want to work with it (instead of learning Angular) and I hope I get the chance. The markup here represents the equivalent of how you might slap an np-app tag on an html element in AngularJS to define the Angular mechanics as to be restrained within the tag. As with Angular, you don't necessarily have to decorate the body tag to make the framework take over everything in your markup, but instead the whole application may just bubble up inside of a div tag too.

<body aurelia-app>
   <script src="jspm_packages/system.js"></script>
   <script src="config.js"></script>
   <script>
      System.import('aurelia-bootstrapper');
   </script>
</body>

 
 

The Script.import line of JavaScript above is of an ES6 module loader API. aurelia is modular, and, like Angular, not AMD modular. It has this other approach for wiring stuff up. A template is going to be brought up into the tag slapped with aurelia-app (an html template enclosed in template tags) and every .html template has a sister .js file which is sort of its code behind to draw an ASP.NET web forms parallel. We are gonna find app.js and app.html by default. The first app.js the audience was shown looked like this:

export class Welcome {
   heading = 'Welcome to the Aurelia Navigation App!';
   firstName = 'John';
   lastName = 'Doe';
   
   get fullName(){
      return '${this.firstName} ${this.lastName}';
   }
   
   submit(){
      alert('Welcome, ${this.fullName}!');
   }
}

 
 

In our first component/app-level component setup we are defining some variables and, yes, we are going to surface these in our template. Just enclosing the variable name in curly braces and placing a dollar sign to the left of the left curly braces does this in the aurelia markup.

<template>
   <section>
      <h2>${heading}</h2>
      
      <form role="form" submit.trigger="submit()">
         <div class="form-group">
            <label for="fn">First Name</label>
            <input type="text" value.bind="firstName" class="form-control" id="fn"...
         </div>
         <div class="form-group">
            <label for="ln">Last Name</label>
            <input type="text" value.bind="lastName" class="form-control" id="ln"...
         </div>
         <div class="form-group">
            <label>Full Name</label>
            <p class="help-block">${fullName}</p>
         </div>
         <button type="submit" class="btn btn-default">Submit</button>
      </form>
   </section>
</template>

 
 

The curly braces thing is an example of one-way data binding. value.bind will sometimes give you two-way data binding. It depends on the situation. If used in an a tag for giving a link, well, there really is no reason for that circumstance to demand two-way binding so one-way binding only is afforded. In an input tag however, value.bind provides two-way data binding allowing you to affect the object slated back in the .js file. In contrast, value.two-way may be alternatively used to always have two-way binding without ambiguity and value.one-way is its dumber brother while the runt of the litter is value.one-time which will not refresh itself even if something changes the .js object hydrating the content. One-way binding will refresh what the user sees if other forces such as two-way data binding acts happening elsewhere change up the underlying data, not so with value.one-way though. An event may be wired up to the DOM and submit.trigger above is an example. It will call the submit function in app.js. If we want app.js to instead be a router allowing us to bring in other modules in lieu of having a one module app, we may do so like this:

export class App {
   configureRouter(config, router){
      config.title = 'Aurelia';
      config.map([
         { route: ['','welcome'], moduleId: './welcome', nav: true, title: 'Welcome' }
      ]);
      
      this.router = router;
   }
}

 
 

Our originally app.js and app.html become "Welcome" instead of "App" to become the first in a series of navigable pages. Above where the app.js starts out with Welcome, as much might be an oversight in Rob Eisenberg's presentation where he got ahead of himself. I'm not sure. I thought I'd leave it be rather than redact it. Eventually, the originally, app.js/app.html became the welcome content. Does the new app.js have an .html page to accommodate the routing? Yes it does and it looks like this:

<template>
   <nav class="navbar navbar-default navbar-fixed-top" role="navigation">
   
   <div class="page-host">
      <router-view></router-view>
   </div>
</template>

 
 

He had some markup for interfacing with the router. Here router.isNavigating allows us to conditionally (when applicable) see a spinner if we are attempting to load another "page" and the repeat.for is going to loop through a collection to show us list items with links and thus build out navigation.

      <i class="fa fa-home"></i>
      <span>${router.title}</span>
      </a>
   </div>
   
   <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
      <ul class="nav navbar-nav">
         <li repeat.for="row of router.navigation" class="${row.isActive ? 'active' : ''}">
            <a data-toggle="collapse" data-target="#bs-example-navbar-collapse-1.in"...
         </li>
      </ul>
      
      <ul class="nav navbar-nav navbar-right">
         <li class="loader" if.bind="router.isNavigating">
            <i class="fa fa-spinner fa-spin fa-2x"></i>
         </li>
      </ul>
   </div>
</nav>  
<div class="page-host">
   <router-view></router-view>
</div>

 
 

That HTML markup for the nav bar is brought into the new app.html as a consistent menu (regardless of what content is transitioned to in routing) like so:

<template>
   <compose view="./nav-bar.html" containerless></compose>
   
   <div class="page-host">
      <router-view></router-view>
   </div>
</template>

 
 

If you slap containerless on a tag as shown above it will not appear in the HTML markup that makes its way up to the DOM allowing the tag to only exist for your coordination and bookkeeping at the aurelia side of things. That's really all of the basics. I cut out of this tech talk a little early and especially so when I suspected it was crossing out of the basics into the deeper stuff. The following stuff brought in a bunch of photos of tacos. It shows how to bring in other .js libraries for aurelia to do extra stuff too.

import {inject} from 'aurelia-framework';
import {HttpClient} from 'aurelia-http-client';  
@inject(HttpClient)
export class Tacos{
   heading = 'Tacos';
   images = [];
   url = 'http://api.flickr.com/services/feeds/photos_public.gne?tags=tacos...
   
   constructor(http){
      this.http = http;
   }
   
   activate(){
      return this.http.jsonp(this.url).then(response => {
         this.images = response.content.items;
      });
   }
}

No comments:

Post a Comment