ddraper

First Steps with Aurelia

Blog Post created by ddraper on Jan 11, 2017
This is a personal blog post that is primarily intended for tracking my own learning rather than provided to the Alfresco Community for educational purposes. However if you find it useful, informative or have any comments on it then please comment below.

I've previously created a simple Alfresco Repository browser using both Vue.js and React and now wanted to explore using Aurelia. Again a CLI is provided and the documentation initially seems to be very good.

 

I wanted to follow the same implementation of components as before but the first hurdle was to configure authentication in order to be able to access the V1 REST APIs provided by the Alfresco Repository.

 

My first problem was accessing the additional modules that I had installed - namely "axios" (for XHR requests) and my "alfresco-js-utils" module. The issue was that my standard ES6 import was not working... I was getting errors with the following import code:

 

import auth from "alfresco-js-utils/lib/Authentication";

 

It turns out that the issue is that it is necessary to configure the aurelia.json file to manage the dependencies.  Once I'd added the following:

{
   "name": "alfresco-js-utils",
   "path": "../node_modules/alfresco-js-utils",
   "main": "index"
}

 

...then the import statement worked correctly. I'm sure there is a good reason for this, but I don't fully understand it yet. 

 

The next setup was to configure the proxy for handling REST API calls. This was done (following these instructions) by inserting the following code into the run.js file:

var proxyMiddleware = require("http-proxy-middleware");
var proxy = proxyMiddleware("/service", {
    target: "http://localhost:8080/alfresco",
    changeOrigin: true // for vhosted sites, changes host header to match to target's host
});

 

The next stage was to setup an router to ensure that users were logged in before they could access the Alfresco Repository data.

 

It was simple enough to setup routing in the main app.js component:

import {Redirect} from "aurelia-router";

class AuthorizeStep {
   run(navigationInstruction, next) {
      if (navigationInstruction.getAllInstructions().some(i => i.config.settings.roles.indexOf("authenticated") !== -1)) {
         var isLoggedIn = !!localStorage.ticket;
         if (!isLoggedIn) {
           return next.cancel(new Redirect("login"));
         }
      }
      return next();
   }
}

export class App {
   configureRouter(config, router) {
      config.title = "Aurelia Test";
      config.addPipelineStep("authorize", AuthorizeStep);
      config.map([
         { route: ["login"], moduleId: "components/login", title: "Login", settings: { roles: [] } },
         { route: ["", "home"], moduleId: "components/home", title: "Home", settings: { roles: ["authenticated"] } }
      ]);
   }
}

 

...however I did struggle in how to handle the login operation. The login component used the Authentication export from alfresco-js-utils. The solution was to ensure that the router was injected and that I used "navigate" rather than "navigateToRoute" (as described here).

import { inject } from "aurelia-framework";
import { Router } from "aurelia-router";
import auth from "alfresco-js-utils/lib/Authentication";

@inject(Router)
export class Login {

   constructor(router) {
      this.router = router;
   }

   handleSubmit() {
      const username = this.username.value;
      const pass = this.pass.value;

      auth.login(username, pass).then((loggedIn) => {
         if (!loggedIn)
         {
           
         }
         else
         {
            this.router.navigate("home");
         }
      });
   }
}

 

Having handled authentication it was just a case of building out the components as before and at this stage I have to admit that I found Aurelia less intuitive than both Vue.js and React. Some aspects are component development are very good like the simple association between similarly named JavaScript and HTML files (so List.js and List.html are implicitly part of the same component) which means that no additional syntax handling is required.

 

The templates in the HTML files are relatively straight-forward (but perhaps not quite so much as in Vue.js)... an example of the ListView component template looking like this:

<template bindable="list">
   <ul>
      <li repeat.for="item of list.entries" click.delegate="navigate(item)">${item.entry.name}</li>
   </ul>
</template>

 

The part that caused confusion was in nesting components and passing data between them. This required a lot of additional annotations that I found weren't so well documented.  The associated ListView.js file looking like this:

import {bindable, customElement, inject} from "aurelia-framework";

@customElement("listview")
@inject(Element)
export class ListView {
  
   constructor(element) {
      this.element = element;
   }

   navigate(item) {
      if (item.entry.isFolder)
      {
         let changeEvent = new CustomEvent("navigate", {
            detail: item,
            bubbles: true
         });
         this.element.dispatchEvent(changeEvent);
      }
   }

   @bindable list;
}

 

Although Aurelia recommends the use of a publication/subscription model via the the Event Aggregator (as described late on in this blog post) I actually opted for a custom event solution (as described in this blog post) as I think this is a better model - and because this was just using native custom events the solution would be able to bubble through multiple components. An alternative option would have been to use the 2-way binding features provided but I don't thing that this is such a good model so have avoided it for the time being.

 

By the time I'd successfully implemented the ListView and Breadcrumb components I found implementing the Toolbar very straightforward. I think that once you get a feel for how to build components Aurelia becomes more straightforward but there is definitely something of a learning curve and although there is a lot of documentation it is not necessarily easy to find exactly what you need.

 

Once again the actual structure of the components was almost identical to the implementation in both Vue.js and React. The main difference being the names of the life-cycle functions, the templating syntax and data binding. The fact that the Aurelia components are just plain ES6 classes is nice (and you have the option to work in TypeScript if you prefer) and the templating syntax is very readable and easy to understand.

 

You can review the state of the client at the time of writing by checking out this tag from this GitHub repository.

Outcomes