IVAN SIVAK
  • About
  • Blog
  • CV
  • Contact

Angular 2 Intro - Pascal's Triangle

5/13/2016

0 Comments

 
With over 1.1 million developers world wide who use the Angular 1.x framework - it is a framework that undoubtedly became a major web technology used today. It's no surprise that upcoming Angular 2 raises lots of questions about its new features, enhancements and generally the framework itself.
​In this article we will focus on a small Angular 2 project where we will take a closer look on Angular 2 development and its key changes. To kick off I would recommend an introduction video presented by AngularConnect

Pascal's Triangle

OK, let's start. First of all - we should start with Pascal's Triangle itself. For those who doesn't know what it is - I would recommend a wikipedia page. The core of Pascal's Triangle is the binomial theorem. Basically, we will construct the triangle by applying the binomial coefficient, according to expression:
Picture
The binomial formula itself looks like:
Picture
Hence, to determine the particular row of our pascal's triangle we can use the following construction (as stated on wikipedia example):
Picture
Picture
Picture
..and so on. Simple. ​The result should produce something as follows image:
Picture
That's for the pascal's triangle. Let's move on.

Coding options

Before we proceed to actual coding it's worth to mention the coding options we have. You're certainly aware of JavaScript's ES5, ES6, ES2015 or TypeScript. While it's perfectly valid to use ES5 I will stick with TypeScript because of its types and interface features. The Angular 2 itself is actually written in TypeScript.

Angular 1.x - factory, service or provider?

Generally speaking if you need to share your code on a single place (DRY) in multiple controllers in Angular 1 you've had a couple of options such as factory, service or provider. All of them used in specific scenarios. 
Angular 2 is much more elegant and all you need to do is to just create a Class. The binomial calculations mentioned above are perfect example of a code that should be placed on a single place - in our example it is in binomial-service.ts. Fair enough. How to use it? Let's compare Angular 1 with Angular 2 by example. 
angular.module('app').service('BinomialService', BinomialService);
 
function BinomialService() {
  //..some internal methods..
  this.getPascalTriangle = function(size) {
      var triangle = [];
          for (var i=0;i < size;i++) {
            triangle.push(this.calc(i))
          }
          return triangle;
    }
}

import { Injectable } from '@angular/core';

@Injectable()
export class BinomialService {
  //..some internal methods..
  getPascalTriangle = (size) => {
   var triangle = [];
   for (var i = 0; i < size; i++) {
      triangle.push(this.calc(i));
   }
   return triangle;
  }
}
Notice two things:
  • @Injectable
  • export keyword
While export keyword is pretty straightforward (the class simply behaves as a module and is not seen outside without setting it as external) the question comes to oddly looking @Injectable line. This is called a decorator and comes from TypeScript. Angular 2 uses a couple of decorators frequently used, such as:
  • @Component
  • @Injectable
  • @Directive
  • @Pipe
All of them are nicely described on Angular 2 Cheat Sheet. As documentation says:
Declares that a class has dependencies that should be injected into the constructor when the dependency injector is creating an instance of this class.
..and that's exactly what is happening. As mentioned - out service needs to actually be injected to the constructor of consuming component or class. As you can see in app.component.ts. 
@Component({
  selector: 'pt-app',
  templateUrl: './pascal-triangle.html',
  directives: [TriangleParams],
  providers: [BinomialService]
})
export class AppComponent implements OnInits, AfterViewInit  {
  constructor(private _binomialService: BinomialService) { }
 
  // Methods
  refreshData(size: number){
    this.rows = this._binomialService.getPascalTriangle(size);  
  }
}
..couple of things to mention here. The first is the providers: [BinomialService] within the @Component decorator. This tells the Angular to consume the service class. Probably the most important is Dependency Injection part though. Generally speaking dependency injection is very important design pattern and as official Angular docs say: ..we really can't build an Angular application without it. Therefore this line with constructor is very important. Within the class is then accessed by this keyword since it sits on class itself.

Angular 2 basic differences

Before we move on let's just go through some basic differences
<input type="number" ng-model="sizeModel" />

<input type="number" [(ngModel)]="sizeModel" />

..just a note on model part. This annotation: [(ngModel)] is actually called a banana in a box :)
<div ng-bind="message" class="centered"></div>

<div [innerText]="message" class="centered"></div>

This actually is
one of the thing I like the most. Instead of having plenty of directives for any particular property you simply use square brackets [] to define any HTML property itself. Dozens of directives from Angular 1 are hence not needed anymore. 
<span ng-bind="num" class="num-box" ng-mouseenter="hoverNum = num"
                                    ng-mouseleave="hoverNum = ''"> </span>

<span [innerText]="num" class="num-box" (mouseenter)="hoverNum = num"
                                        (mouseleave)="hoverNum = ''"> </span>
Where square brackets [] define any HTML property then classic brackets () define any HTML element events. Very elegant.
<span ng-repeat="num in row" ng-bind="num" class="num-box"
      ng-mouseenter="hoverNum = num" ng-mouseleave="hoverNum = ''">
</span>

<span *ngFor="let num of row"  [innerText]="num" class="num-box"
      (mouseenter)="hoverNum = num" (mouseleave)="hoverNum = ''">
</span>
Here it's very important to notice a star * before ngFor. Any star represents a structural directive. Structural directive means it actually changes or defines the DOM structure itself. There are couple of other structural directives such as:
  • *ngFor
  • *ngIf
  • *ngSwitch
As mentioned earlier - it's highly recommended to check the Angular 2 cheat sheet which nicely summarizes features.

Unidirectional Data Flow

The data binding in Angular 1 was based on well known digest cycles. Angular 2 uses something called unidirectional data flow. Forget about $apply, repeated digest cycle, $watch. 
Picture
Facebook's Flux is also based on an idea of unidirectional data flow. Basically it says that you can no longer update the view after it has been composed. This process is much better when it comes to performance since it avoids repeated $digest cycles.

Component life cycle hooks

As stated in official documentation:

"A Component has a lifecycle managed by Angular itself. Angular creates it, renders it, creates and renders its children, checks it when its data-bound properties change, and destroys it before removing it from the DOM.
Angular offers component lifecycle hooks that give us visibility into these key moments and the ability to act when they occur."
When you look at the app.component.ts:
@Component({
  selector: 'pt-app',
  templateUrl: './pascal-triangle.html',
  directives: [TriangleParams],
  providers: [BinomialService]
})
export class AppComponent implements OnInits, AfterViewInit  {
@Component itself is a decorator that provides meta data for its class which is actually being exported. 
The OnInits and AfterViewInit are our "hook" moments. Angular follows its internal sequence of specific moments which forms its life cycle and hooks allow you to tap into these moments to actually build a project. 
Here we have a code of our main component where we also need to access the child component params.component.ts and also the binomial-service.ts:
import { Component, OnChanges, Input, Output, 
         View, EventEmitter, OnInit } from '@angular/core';
import { BinomialService } from './binomial-service';
import { ViewChild, AfterViewInit } from '@angular/core';
import { TriangleParams } from './params.component';

@Component({
  selector: 'pt-app',
  templateUrl: './pascal-triangle.html',
  directives: [TriangleParams],
  providers: [BinomialService]
})
export class AppComponent implements OnInits, AfterViewInit  {
  constructor(private _binomialService: BinomialService) { }
  @ViewChild(TriangleParams) triangleParams:TriangleParams;
  
  rows: [];
  // Methods
  refreshData(size: number){
    this.rows = this._binomialService.getPascalTriangle(size);  
  }
  // Lifecycle hooks
  ngOnInit(){
    this.rows = [];
  }
  
  ngAfterViewInit() {
    this.refreshData(this.triangleParams.triangleSize); 
  }
}
Notice that to access the params component we have to:
  • import { TriangleParams } from './params.component';
  • Set it in directives array - directives: [TriangleParams],
  • Inherit specific hooks to access our child component - implements AfterViewInit
  • Define @ViewChild(TriangleParams) triangleParams:TriangleParams;
  • ..and finally use it by this.triangleParams.triangleSize

Change Detection

While the official documentation still leaves the change detection section empty it is pretty natural to follow property setter as you can see here:
get sizeModel() {
      return this.triangleSize;
  }

  set sizeModel(value) {
    this.triangleSize = value;
    
    if ((this.valid = this.validate()) == false) return;
    
    this.sizeChange.emit({
      value: this.triangleSize
    });
  }
and a view which looks like this:
<nav class="centered">
  Triangle size = <input type="number" [(ngModel)]="sizeModel" />
</nav>
As you can see - every time model changes the property setter is triggered. You can also notice the event emitter being triggered which is going to be described on the following lines.

Communication between components

You've surely came across a need to communicate between controllers in Angular 1. Features such as $broadcast or $emit. These do not exist anymore in Angular 2. We have couple of other options though and they are well described on official documentation.

In our demo we just need to notify a parent component about changes in child component. For which we will EventEmitters. So basically within the child component:
import { Component, OnChanges, Input, Output, View, 
         EventEmitter, OnInit } from '@angular/core';

@Component({
  selector: 'triangle-params',
  templateUrl: './triangle-params.html'
})
export class TriangleParams {
  @Input() triangleSize;
  @Output() sizeChange = new EventEmitter(); 
  //..
  set sizeModel(value) {
    //..
    this.sizeChange.emit({
      value: this.triangleSize
    });
  }

}
..we have to define the target property by which the communication will be performed. That's the @Output() line. Again it is worth reading through the official documentation. To quote:
Input properties usually receive data values. Output properties expose event producers, such as EventEmitter objects.
Picture
..which is exactly what we do here. We are exposing an event procedure. So in the parent component's view we can define the (sizeChange) event:
<triangle-params [triangleSize]="6" 
                 (sizeChange)="sizeChanged($event);"></triangle-params>
..which triggers our sizeChanged method within the parent component class and passes $event as an argument which we emitted from the child component including the new value.
@Component({
  selector: 'pt-app',
  templateUrl: './pascal-triangle.html',
  directives: [TriangleParams],
  providers: [BinomialService]
})
export class AppComponent implements OnInits, AfterViewInit  {
  //..
  sizeChanged(event) {
    this.refreshData(event.value);
  }
  //..
}
As mentioned above. This process is well described on the official documentation. 

Summary

Angular 2 introduces lots of changes. It requires some time to "digest" them and be able to actually develop a project. It's all worth it though. It is much more elegant and faster. Check out the GitHub project and play with plunker where you can test and experiment. 
0 Comments



Leave a Reply.

    About

    Blog about my programming experiments, tests and so on.

    View my profile on LinkedIn

    Categories

    All
    Algorithms
    Angular 2
    ASP.NET
    ASP.NET Core
    Aurelia JS
    Cryptography
    Data Structures
    Gulp
    JavaScript
    Math
    MVC6
    .NET Core
    React JS
    Security
    SQL Server

    Archives

    November 2016
    October 2016
    September 2016
    May 2016
    March 2016
    February 2016
    January 2016
    December 2015
    October 2015

    RSS Feed

Ivan Sivak


Stack overflow

Stack overflow profile

LinkedIn

LinkedIn profile

Email

ivansivak@outlook.com

GitHub

GitHub repository
  • About
  • Blog
  • CV
  • Contact