ddraper

Vanilla Angular 2 Client

Blog Post created by ddraper on Jan 12, 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.

Having previously created my basic Alfresco Repository browsing application in Vue.js, React and Aurelia I'm on something of a roll. The next framework I wanted to look at was Angular 2 (except that you can't call it that anymore, it's just Angular). Initially I'm going to look at vanilla Angular 2 before reviewing Alfresco's own ADF.

 

Once again there is a CLI that aims to get you up and running as quickly as possible, and as with React and Aurelia it's geared around Single Page Application creation so there was a requirement to handle authentication against the Alfresco Repository. 

 

The authentication strategy is the same (a familiar theme in reviewing all these frameworks) where a router is used to ensure that only authenticated users gain access to Alfresco content and the approach is well described in this blog post.

 

I had a few issues initially working through the instructions but most (such as this issue) were solved by a quick Google. The error messages seem good, but you still have to know what they mean... for example I hadn't declared my "HomeComponent" and "LoginComponent" in my declarations array - the error message made a lot more sense after I'd resolved the problem!

 

Creating the authentication router was quite straightforward (from app.routes.ts):

export const routes = [
  { path: 'login', component: LoginComponent },
  { path: '', component: HomeComponent, pathMatch: 'full', canActivate: [LoggedInGuard] }
];

 

...and the UserService approach (from the previously mentioned blog post) was easily adapted to make use of the Authentication utility functions from my alfresco-js-utils NPM package (despite me needing to use "require" rather than "import" for some reason I couldn't resolve).

 

It was definitely harder to get the template for the login component working (although I'm sure there are alternative ways in which it could have been done)...

@Component({
  selector: 'login',
  template: `<form (ngSubmit)="onSubmit()">
      <label><input name="username" [(ngModel)]="username" placeholder="username" /></label>
      <label><input name="password" [(ngModel)]="password" placeholder="password" /></label><br/>
      <button type="submit">login</button>
   </form>`
})

 

Compare with the template in Aurelia: 

<template>
   <form submit.trigger="handleSubmit()">
      <label><input ref="username" placeholder="username" /></label>
      <label><input ref="pass" placeholder="password" /></label><br/>
      <button type="submit">login</button>
   </form>
</template>

 

It wasn't intuitive to have to use "ngSubmit" and "ngModel" but again this is probably something that you get used to over time.

 

It was necessary once again to configure proxies for the server to redirect REST API requests to the Alfresco Repository. This was done following the instructions in this video to create a proxy.config.json file and to update the npm script for "start" to include " --proxy-config proxy.config.json".

 

With authentication handled it was time to start building out the same set of components as before.... this was when things began to get frustrating. Due to a combination of TypeScript and the opinionated coding style enforced by Angular I became quickly bogged down in problems that were not fast to resolve. I wasn't able to easily reuse the NodeService from alfresco-js-utils and instead had to follow the patterns described in the documentation.


I also had issues with getting changes to my bound data to trigger a reactive rendering of the list (something that I'd had no problems with in the other frameworks) and on the whole I found the development process frustrating and restrictive. Passing data and events were more usefully described in this post than in the official documentation.

 

Once I'd implemented the List and ListView components it was much more straightforward to create the Toolbar and Breadcrumb. Working in Angular does feel restrictive but I can see how those restrictions might ultimately result in more robust code. Although there is lots of documentation and blog posts it can be quite difficult to find the right answer to problems as there were so many changes through the beta and release candidates phase of Angular development. 

 

Some things I found confusing (but I'm sure there will be a simple explanation for) such as why all my component dependencies needed to be in the app.module.ts "declarations" array and couldn't be declared in the appropriate location (so ListView, Breadcrumb and Toolbar should just be dependencies of List).

 

One advantage I found in Aurelia over Angular was in change detection of bound properties. In Angular you have to implement the "ngOnChanges" life-cycle function and figure out for yourself if the property you're interested in has changed, but in Aurelia you can just create a function with "Changed" appended to the property, for example compare...

 

Angular:

ngOnChanges(changes: any) {
   let newPath = changes["relativePath"];
   if (newPath)
   {
      let breadcrumbData = BreadcrumbUtil.createBreadcrumbs({
         relativePath: newPath.currentValue
      });
      this.breadcrumbs = breadcrumbData.breadcrumbs;
   }
}

 

...with Aurelia:

relativePathChanged(newPath, oldPath) {
   let breadcrumbData = BreadcrumbUtil.createBreadcrumbs({
      relativePath: newPath
   });
   this.breadcrumbs = breadcrumbData.breadcrumbs;
}

 

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

Outcomes