Introduction
YouTube Video Link:
In this article, I’ll show how to create a TodoMVC application using Angular2 and Aurelia to compare these frameworks. The article is in a table format with 2 columns named “Angular2” and Aurelia, which contains the steps to create a TodoMVC application in both these frameworks. The projects are created using CLI (Command Line Interface) tools that are part of these frameworks. The source code for these projects are available in my github page:
Angular2 Source Code: https://github.com/rajajhansi/ng2-todo-app
Aurelia Source Code: https://github.com/rajajhansi/au-todo-app
Steps to create a Todo MVC Application in Angular2 and Aurelia
The table below lists the steps to create a Todo MVC Application in Angular2 and Aurelia.
Angular2 | Aurelia |
Installing the ng CLInpm install angular-cli -g | Installing the au CLInpm install aurelia-cli -g |
Creating a new applicationng new ng2-todo-app | Creating a new applicationau new au-todo-app |
Running the applicationcd ng2-todo-appng serve | Running the applicationcd au-todo-appau run --watch |
Creating a Todo classng g class Todo | Creating a Todo classYou can either run the command:au generate Todo and move src/resources/Todo.ts into src folder and delete src/resources/elements/Todo.html OR Create a new file named Todo.ts in src folder, which is easier. |
todo.tsexport class Todo { id: number; description: string = ''; done: boolean = false; constructor(values: Object = {}) { Object.assign(this, values); } } |
todo.tsexport class Todo { id: number; description: string = ''; done: boolean = false; constructor(values: Object = {}) { Object.assign(this, values); } } |
Creating unit test for Todo class using jasmine - Angular2
todo.spec.ts
/* tslint:disable:no-unused-variable */ import { TestBed, async } from '@angular/core/testing'; import {Todo} from './todo'; describe('Todo', () => { it('should create an instance', () => { expect(new Todo()).toBeTruthy(); }); it('should accept values in the constructor', () => { let todo = new Todo({ description: 'hello', done: true }); expect(todo.description).toEqual('hello'); expect(todo.done).toEqual(true); }); it('should ignore values sent in wrong properties in the constructor', () => { let todo = new Todo({ title: 'hello', done: true }); expect(todo.description).toEqual(''); expect(todo.done).toEqual(true); }); });
Creating unit test for Todo class using jasmine - Aurelia
todo.spec.ts
/* tslint:disable:no-unused-variable */ import {Todo} from '../../src/todo'; describe('Todo', () => { it('should create an instance', () => { expect(new Todo()).toBeTruthy(); }); it('should accept values in the constructor', () => { let todo = new Todo({ description: 'hello', done: true }); expect(todo.description).toEqual('hello'); expect(todo.done).toEqual(true); }); it('should ignore values sent in wrong properties in the constructor', () => { let todo = new Todo({ title: 'hello', done: true }); expect(todo.description).toEqual(''); expect(todo.done).toEqual(true); }); });
Running unit testsng test |
Running unit testsau buildau test |
Creating a Service class for Todong g service Todo |
Creating a Service class for Todocreate a new folder named “src\services” and a file named todo.service.ts |
Installing the TodoMVC Stylesheetsnpm install todomvc-app-css todomvc-common --saveAdding custom style for toolbar |
Installing the TodoMVC Stylesheetsnpm install todomvc-app-css todomvc-common --saveAdding custom style for toolbar |
styles.css - Angular2
/* You can add global styles to this file, and also import other style files */ @import url('../node_modules/todomvc-common/base.css'); @import url('../node_modules/todomvc-app-css/index.css'); .app-root-loader { text-align: center; padding: 30px; } .toolbar { color: #777; /*background-color: lightslategray;*/ padding: 5px 10px; height: 20px; text-align: center; border-top: 1px solid #e6e6e6; float: right; }
styles.css - Aurelia
/* You can add global styles to this file, and also import other style files */ @import url('../node_modules/todomvc-common/base.css'); @import url('../node_modules/todomvc-app-css/index.css'); .app-root-loader { text-align: center; padding: 30px; } .toolbar { color: #777; /*background-color: lightslategray;*/ padding: 5px 10px; height: 20px; text-align: center; border-top: 1px solid #e6e6e6; float: right; }
Adding a new Todo Item in text box after entering the description and pressing enter keyAngular2 has (keyup.enter) event to trigger a JavaScript function upon pressing enter key after entering the description for a new Todo item. |
Adding a new Todo Item in text box after entering the description and pressing enter keyAurelia recommends to use <form> and wire the submit.trigger to trigger a JavaScript function upon pressing enter key. It also requires a <button type=”submit”> as well. To attach enter keypress event to any element, we can easily do that with Aurelia Custom attribute. au generate attribute keyup-enter |
keyup-enter.ts - Aurelia
import {autoinject} from 'aurelia-framework'; @autoinject() export class KeyupEnterCustomAttribute { element: Element; value: Function; enterPressed: (e: KeyboardEvent) => void; constructor(element: Element) { this.element = element; this.enterPressed = e => { let key = e.which || e.keyCode; if (key === 13) { this.value();//'this' won't be changed so you have access to your VM properties in 'called' method } }; } attached() { this.element.addEventListener('keypress', this.enterPressed); } detached() { this.element.removeEventListener('keypress', this.enterPressed); } }
Creating the application componentng g component TodoApp |
Creating the application componentau generate element todo-appAurelia has the concept of global resources and by adding a resource into global resources, you can use them in any views without having to do <require from=”path of the resource”></require> src/resources/index.tsimport {FrameworkConfiguration} from 'aurelia-framework'; export function configure(config: FrameworkConfiguration) { config.globalResources([ "./elements/todo-app", "./attributes/keyup-enter" ]); } |
Wiring Todo Item functionalities into the App component - Angular2
todo-app.component.ts
import { Component, OnInit } from '@angular/core'; import { Todo } from '../todo'; import { TodoService } from '../todo.service'; @Component({ selector: 'todo-app', templateUrl: './todo-app.component.html', styleUrls: ['./todo-app.component.css'], providers: [TodoService] }) export class TodoAppComponent implements OnInit { newTodo: Todo = new Todo(); filter: string = 'all'; filteredTodos: Todo[] = []; constructor(private todoService: TodoService) { } ngOnInit() { } addTodo() { this.todoService.addTodo(this.newTodo); this.newTodo = new Todo(); this.filterTodo(this.filter); } toggleTodoDone(todo) { this.todoService.toggleTodoDone(todo); this.filterTodo(this.filter); } removeTodo(todo) { this.todoService.deleteTodo(todo.id); this.filterTodo(this.filter); } filterTodo(filterCriteria: string) { this.filter = filterCriteria; this.filteredTodos = this.todoService.filterTodo(filterCriteria); } completeAllTodos() { this.todoService.completeAllTodos(); // this.checkIfAllTodosAreCompleted(); this.filterTodo(this.filter); } removeAllTodos() { this.todoService.removeAllTodos(); this.filterTodo(this.filter); } removeDoneTodos() { this.todoService.removeDoneTodos(); this.filterTodo(this.filter); } }
Wiring Todo Item functionalities into the App component - Aurelia
todo-app.ts
import {inject} from 'aurelia-framework'; import {Todo} from '../../todo'; import {TodoService} from '../../todo.service'; @inject(TodoService) export class TodoApp { newTodo: Todo = new Todo(); filter: string = "all"; filteredTodos: Todo[] = []; constructor(private todoService: TodoService) { } addTodo() { this.todoService.addTodo(this.newTodo); this.newTodo = new Todo(); this.filterTodo(this.filter); } toggleTodoDone(todo) { this.todoService.toggleTodoDone(todo); this.filterTodo(this.filter); } removeTodo(todo) { this.todoService.deleteTodo(todo.id); this.filterTodo(this.filter); } filterTodo(filterCriteria: string) { this.filter = filterCriteria; this.filteredTodos = this.todoService.filterTodo(filterCriteria); } completeAllTodos() { this.todoService.completeAllTodos(); //this.checkIfAllTodosAreCompleted(); this.filterTodo(this.filter); } removeAllTodos() { this.todoService.removeAllTodos(); this.filterTodo(this.filter); } removeDoneTodos() { this.todoService.removeDoneTodos(); this.filterTodo(this.filter); } }
Wiring Todo Item functionalities into the App View - Angular2
todo-app.component.html
<section class="todoapp"> <header class="header"> <h1>Todos</h1> <div class="toolbar"> <a href="#" (click)="removeAllTodos()"> Remove All </a> | <a href="#" (click)="removeDoneTodos()"> Remove Completed </a> | <a href="#" (click)="completeAllTodos()"> Complete All </a> </div> <br/> <input type="text" class="new-todo" placeholder="what needs to be done?" autofocus="" [(ngModel)]="newTodo.description" (keyup.enter)="addTodo()"> </header> <section class="main" *ngIf="filteredTodos.length > 0"> <ul class="todo-list"> <li *ngFor="let todo of filteredTodos" [class.completed]="todo.done"> <div class="view"> <input type="checkbox" class="toggle" (click)="toggleTodoDone(todo)" [checked]="todo.done"> <label>{{todo.description}}</label> <button class="destroy" (click)="removeTodo(todo)"></button> </div> </li> </ul> </section> <footer class="footer" > <span class="todo-count"><strong>{{filteredTodos.length}}</strong> {{filteredTodos.length === 1 ? 'item': 'items'}} left</span> <ul class="filters"> <li> <a class="{{filter == 'all' ? 'selected' : ''}}" href="#" (click)="filterTodo('all')">All</a> </li> <li> <a class="{{filter == 'active' ? 'selected' : ''}}" href="#" (click)="filterTodo('active')">Active</a> </li> <li> <a class="{{filter == 'completed' ? 'selected' : ''}}" href="#" (click)="filterTodo('completed')">Completed</a> </li> </ul> </footer> </section>
Wiring Todo Item functionalities into the App View - Aurelia
todo-app.html
<template> <section class="todoapp"> <header class="header"> <h1>Todos</h1> <div class="toolbar"> <a href="#" click.trigger="removeAllTodos()"> Remove All </a> | <a href="#" click.trigger="removeDoneTodos()"> Remove Completed </a> | <a href="#" click.trigger="completeAllTodos()"> Complete All </a> </div> <br/> <input type="text" class="new-todo" placeholder="what needs to be done?" autofocus="" value.bind="newTodo.description" keyup-enter.call="addTodo()"> </header> <section class="main" show.bind="filteredTodos.length > 0"> <ul class="todo-list"> <li repeat.for="todo of filteredTodos" class="${todo.done ? 'completed' : ''}"> <div class="view"> <input type="checkbox" class="toggle" checked.bind="todo.done"> <label>${todo.description}</label> <button class="destroy" click.trigger="removeTodo(todo)"></button> </div> </li> </ul> </section> <footer class="footer"> <span class="todo-count"><strong>${filteredTodos.length}</strong>${filteredTodos.length === 1 ? ' item ': ' items '} left</span> <ul class="filters"> <li> <a class="${filter == 'all' ? 'selected' : ''}" href="" click.trigger="filterTodo('all')">All</a> </li> <li> <a class="${filter == 'active' ? 'selected' : ''}" href="" click.trigger="filterTodo('active')">Active</a> </li> <li> <a class="${filter == 'completed' ? 'selected' : ''}" href="" click.trigger="filterTodo('completed')">Completed</a> </li> </ul> </footer> </section> </template>
Using custom element in app.html - Angular2
app.html
<todo-app></todo-app>
Using custom element in app.html - Aurelia
app.html
<template> <require from="./styles.css"></require>
<todo-app></todo-app>
</template>
Comparison of Angular2 and Aurelia
Angular2 |
Aurelia |
I see the framework code explicitly in my application code because Angular2 uses explicit declarations via decorators |
I don’t see the framework polluting my application code because Aurelia uses conventions |
Hard to switch to another framework because my application has too much of framework code. |
Easy to switch to another framework because my application code is reusable JavaScript code |
Uses non-standard syntax like [(ngModel)], *ngFor, *ngIf, (event), {{ }} for interpolation etc., in the HTML markup that are not part of the standards. | Follows web standards like web components, ECMAScript 6 string interpolation etc. |
Angular2 has great support from 3rd party vendors like Telerik who are writing the Angular2 directives. | Aurelia has great support from Syncfusion(http://aureliajq.syncfusion.com/). Aurelia UX bridge project is a community run project that has created components for Telerik KendoUI and Materialize. |
Angular2 is more popular because it is from google and people hear about it everywhere be it a conference or other software companies’ web sites like Microsoft ASP.NET Core or MSDN or Channel 9 etc. | Aurelia is gaining momentum because its creators are presenting in popular conferences like NDC and sites like Channel 9 etc. Aurelia is also supported for .NET Core officially and talked about in MSDN magazine and on Channel 9. More Channel 9 content is coming soon. |
Angular2 has been in development for a very long time. Angular2 documentation has gotten much better compared to Angular 1 documentation. | Aurelia has a very aggressive release cycle and they have frequent releases. The Aurelia.io hub documentation has gotten much better, very detailed oriented, technical and useful for anyone who wants to learn the framework. |
Plugins are available for IntelliJ, VSCode and other popular editors out there. Plunkr supports Angular2 templates for quick online application development. | Plugins are available for IntelliJ, VSCode and other popular editors out there. Aurelia team built GistRun (https://gist.run/) using Aurelia itself that can run your gists and is very handy for online application development. Aurelia has the plunker resources (https://github.com/jdanyow/aurelia-plunker) created by core team member Jeremey Dayow for use as well. |
There are a lot of books out there (started using the early releases of Angular2 but are constantly updated) already for Angular2. | There are only 2 books available out there for Aurelia (both are still under development) now. There are more books and video trainings coming soon. |
There are many learning resources like video tutorials out there from Pluralsight, Wintellect, egghead etc. besides a lot of other tutorials on YouTube. There are so many enthusiasts who blog regularly on Angular. | There are so many enthusiasts who regularly blog about Aurelia besides Aurelia’s core team members. There aren’t that many learning resources out there but the ones that are available are really top quality resources. Rob Eisenberg’s official 3 part Aurelia Virtual Training videos (https://www.picatic.com/modern-javascript-and-aurelia) on Vimeo are awesome. |
It is a great move from the Angular2 team to use TypeScript, which is a typed superset of JavaScript instead of their proprietary Dart language. | Aurelia itself is written using ES6 & 7 and has great starter kits (aka their skeleton navigation projects) in both ES6 and TypeScript. Some of the new plugins are built using TypeScript. |
When it comes to hiring front end developers to work in Angular2 projects, you need to look for skills like TypeScript, understanding of module systems like Webpack or SystemJs, and give an in-depth training on Angular2 fundamentals because it has a steep learning curve. | When it comes to hiring front end developers to work in Aurelia projects, all you need to look for is skills like JavaScript (ES6 or TypeScript is a plus), understanding of module systems like Webpack or SystemJs or RequireJs and give some training on Aurelia fundamentals because it has a simple learning curve. |
Although you’ll find many front end developers who might have heard about Angular2, knowledge of Angular 1 is not required at all because the framework has changed so much from version 1. | You’ll not find that many front end developers who might have heard about Aurelia, which shouldn’t matter because the framework itself is about 1.5 years old. Since it is a conventions based framework, it should be very easy to train any front end developer on Aurelia. |
Conclusion
Both Angular2 and Aurelia are very modern JavaScript frameworks with very good open source community backing them up.
If I were to start a project today, I would pick a modern, modular and standards based framework like Aurelia. While others may have a different opinion which is perfectly fine, my suggestion to the readers of this blog post would be to pick a framework that works for you, your team and your company.
Happy Coding!