Alex Jones

Lead Frontend Developer - Toumetis

http://slides.com/alex-jones/deck

Angular

Workshop

   
    npm install -g @angular/cli

    ng new robot-army-management
    

Setup/Install

3 Goals for Workshop

Understand what Angular is and use cases

Develop a Robot Army Management (RAM) app

Be armed with what the next steps are

  alert('INTRUDER'); 

What is Angular?

A full feature, cross platform, performant, component based and developer focused framework.

Who's Using Angular

https://www.madewithangular.com

Angular Technologies

Libraries

Core.js

RXJS

Zones.js

Jasmine/Protractor

Tools

Typescript (ES6)

Webpack

Karma

Angular CLI

A Year In

Tough learning curve

Fantastic tooling

Easy setup

Observables, steep learning but great

Typescript is πŸ‘

Community/Tutorials

Trends

Angular vs React vs Elm vs Vue Interest Over the Last Year

https://trends.google.co.uk/trends/explore?cat=31&q=Angular,React,Elm,Vue

Trends

Β Most Wanted Frameworks, Libraries and Other TechnologiesΒ 

https://insights.stackoverflow.com/survey/2017

Develop RAM

Preview

Steps

Setup using the Angular CLI

Create robot component

Add inputs, outputs and bind some data

Add some styling

Fetch data from API

Hook up event handlers and emitters

   
    npm install -g @angular/cli

    ng new robot-army-management
    
    cd robot-army-management
    
    ng serve --open

Angular CLI

RUN IN THE TERMINAL

Application Structure

   
    src
    β”œβ”€β”€ app
    β”‚   β”œβ”€β”€ app.component.css
    β”‚   β”œβ”€β”€ app.component.html
    β”‚   β”œβ”€β”€ app.component.spec.ts
    β”‚   β”œβ”€β”€ app.component.ts
    β”‚   └── app.module.ts
    β”œβ”€β”€ assets
    β”œβ”€β”€ environments
    β”‚   β”œβ”€β”€ environment.prod.ts
    β”‚   └── environment.ts
    β”œβ”€β”€ favicon.ico
    β”œβ”€β”€ index.html
    β”œβ”€β”€ main.ts
    β”œβ”€β”€ polyfills.ts
    β”œβ”€β”€ styles.css
    β”œβ”€β”€ test.ts
    β”œβ”€β”€ tsconfig.app.json
    β”œβ”€β”€ tsconfig.spec.json
    └── typings.d.ts

Component


    ng generate component robot
PRETTY NEAT
    
   src
   β”œβ”€β”€ app
Β Β  β”œβ”€β”€ app.component.css
Β Β  β”œβ”€β”€ app.component.html
Β Β  β”œβ”€β”€ app.component.spec.ts
Β Β  β”œβ”€β”€ app.component.ts
Β Β  β”œβ”€β”€ app.module.ts                <---
Β Β  └── robot                        <---
Β Β      β”œβ”€β”€ robot.component.css
Β Β      β”œβ”€β”€ robot.component.html
Β Β      β”œβ”€β”€ robot.component.spec.ts
Β Β      └── robot.component.ts

Modules


    import { BrowserModule } from '@angular/platform-browser';
    import { NgModule } from '@angular/core';
    import { FormsModule } from '@angular/forms';
    import { HttpModule } from '@angular/http';
    
    import { AppComponent } from './app.component';
    import { RobotComponent } from './robot/robot.component';
    
    @NgModule({
      declarations: [
        AppComponent,
        RobotComponent   <---
      ],
      imports: [
        BrowserModule,
        FormsModule,
        HttpModule
      ],
      providers: [],
      bootstrap: [AppComponent]
    })
    export class AppModule { }

Robot Component


    import { Component, OnInit } from '@angular/core';
    
    @Component({
      selector: 'app-robot',
      templateUrl: './robot.component.html',
      styleUrls: ['./robot.component.css']
    })
    export class RobotComponent implements OnInit {
    
      constructor() { }
    
      ngOnInit() {

      }
    
    }

Robot Component

    
    // robot.component.ts

    import {Component, Input, OnInit} from '@angular/core';
    
    @Component({
      selector: 'app-robot',
      templateUrl: './robot.component.html',
      styleUrls: ['./robot.component.css']
    })
    export class RobotComponent implements OnInit {
    
      @Input('details') details: {
        name: string,
        type: string,
        active: boolean
      };
    
      constructor() {}
    
      ngOnInit() {
    
      }
    }
   
    <!--robot.component.html-->

    <h2>
      {{details.name}}
    </h2>
    <p>
      {{details.type}}
    </p>

Robot Component

    
    <!--src/app/app.component.html-->
    
    <app-robot [details]="{name: 'BEEP', type: 'attack-bot', active: true}"></app-robot>
WATCH OUT FOR THOSE ATTACK BOTS

Data Binding Props


    import { Component, Input, OnInit } from '@angular/core';
    
    @Component({
      selector: 'app-robot',
      templateUrl: './robot.component.html',
      styleUrls: ['./robot.component.css']
    })
    export class RobotComponent implements OnInit {
    
      @Input('details') details: {
        name: string,
        type: string,
        active: boolean
      };
    
      baseImageUrl = 'https://robohash.org/';
    
      constructor() { }
    
      ngOnInit() {
      }
    
    }

Data Binding Props


    <!--robot.component.html-->
    
    <h2>
      Name: {{details.name}}
    </h2>
    <p>
      Type: {{details.type}}
    </p>
    
    <img [src]="baseImageUrl + details.name">
HEY, I RECOGNIZE HIM

Two way binding


    <!--robot.component.html-->
    
    <h2>
      Name: {{details.name}}
    </h2>
    <p>
      Type: {{details.type}}
    </p>
    
    <img [src]="baseImageUrl + details.name">
    
    <p>
      <label for="override">Name Override:</label>
      <input type="text" id="override" [(ngModel)]="details.name">
    </p>
WAIT ... IS THAT ME?

Styling

    
    /* 
      src/app/robot/robot.component.css 
    */

    :host {
      display: inline-block;
      padding: 20px;
      background-color: black;
      color: white;
      margin: 10px;
      box-shadow: 0 14px 28px rgba(0,0,0,0.25), 0 10px 10px rgba(0,0,0,0.22);
      font-family: monospace;
    }

Service


    ng g service robotics-centre

    src
    β”œβ”€β”€ app.component.css
    β”œβ”€β”€ app.component.html
    β”œβ”€β”€ app.component.spec.ts
    β”œβ”€β”€ app.component.ts
    β”œβ”€β”€ app.module.ts
    β”œβ”€β”€ robot
    β”‚Β Β  β”œβ”€β”€ robot.component.css
    β”‚Β Β  β”œβ”€β”€ robot.component.html
    β”‚Β Β  β”œβ”€β”€ robot.component.spec.ts
    β”‚Β Β  └── robot.component.ts
    β”œβ”€β”€ robotics-centre.service.spec.ts
    └── robotics-centre.service.ts

Service


    import { Injectable } from '@angular/core';
    
    @Injectable()
    export class RoboticsCentreService {
    
      constructor() { }
    
    }

Service


    // src/app/app.module.ts
    
    import { BrowserModule } from '@angular/platform-browser';
    import { NgModule } from '@angular/core';
    import { FormsModule } from '@angular/forms';
    import { HttpModule } from '@angular/http';
    
    import { AppComponent } from './app.component';
    import { RobotComponent } from './robot/robot.component';
    import { RoboticsCentreService } from './robotics-centre.service';
    
    @NgModule({
        declarations: [
          AppComponent,
          RobotComponent
        ],
        imports: [
          BrowserModule,
          FormsModule,
          HttpModule
        ],
        providers: [RoboticsCentreService], <---
        bootstrap: [AppComponent]
    })
    export class AppModule { }

Service


    import { Injectable } from '@angular/core';
    import { Http } from '@angular/http';
    import 'rxjs/add/operator/map';
    
    @Injectable()
    export class RoboticsCentreService {
    
      constructor(private http: Http) { }
    
      getRobotMood() {
        const url = 'https://www.random.org/cgi-bin/randbyte?nbytes=10&format=h';
        return this.http.get(url)
          .map(res => res.text());
      }
    
    }

Observables Primer

Using Service


    import { Component, Input, OnInit } from '@angular/core';
    import { RoboticsCentreService } from '../robotics-centre.service';
    
    @Component({
      selector: 'app-robot',
      templateUrl: './robot.component.html',
      styleUrls: ['./robot.component.css']
    })
    export class RobotComponent implements OnInit {
    
      @Input('details') details: {
        name: string,
        type: string,
        active: boolean,
        mood?: string
      };
    
      baseImageUrl = 'https://robohash.org/';
    
      constructor(private roboticsCentre: RoboticsCentreService) { }
    
      ngOnInit() {
        this.roboticsCentre.getRobotMood()
          .subscribe(mood => this.details.mood = mood);
      }
    
    }

Using Service


    <!--robot.component.html-->
    
    <h2>
      Name: {{details.name}}
    </h2>
    <p>
      Type: {{details.type}}
    </p>
    <p>
      Mood: {{details.mood}}
    </p>
    
    <img [src]="baseImageUrl + details.name">
    
    <p>
      <label for="override">Name Override:</label>
      <input type="text" id="override" [(ngModel)]="details.name">
    </p>
LOOKING GOOD!

Template Directives


    // src/app/app.component
    
    import { Component } from '@angular/core';
    
    @Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.css']
    })
    export class AppComponent {
      robots = [
        {name: 'JoBloop', type: 'loving-friend', active: true},
        {name: 'FS4Dv', type: 'chatty', active: true},
        {name: 'Bruce', type: 'attack-bot', active: false},
        {name: 'DAS@423', type: 'cat-loving', active: true}
      ];
    }

Template Directives


    <!--src/app/app.component.html-->
    
    <app-robot *ngFor="let robot of robots" [details]="robot"></app-robot>

Template Directives


    <!--robot.component.html-->
    
    <div class="active" *ngIf="details.active"></div>
    
    <h2>
      Name: {{details.name}}
    </h2>
    <p>
      Type: {{details.type}}
    </p>
    <p>
      Mood: {{details.mood}}
    </p>
    
    <img [src]="baseImageUrl + details.name">
    
    <p>
      <label for="override">Name Override:</label>
      <input type="text" id="override" [(ngModel)]="details.name">
    </p>

Template Directives


    /*
      src/app/robot/robot.component.css
    */
    
    :host {
      display: inline-block;
      padding: 20px;
      background-color: black;
      color: white;
      margin: 10px;
      box-shadow: 0 14px 28px rgba(0,0,0,0.25), 0 10px 10px rgba(0,0,0,0.22);
      font-family: monospace;
      width: 20%;
    }
    
    .active {
      height: 10px;
      width: 10px;
      border-radius: 6px;
      background-color: green;
      float: right;
      border: 1px #CCC solid
    }

Event Handling


    <!--robot.component.html-->
    
    <div class="active" *ngIf="details.active"></div>
    
    <h2>
      Name: {{details.name}}
    </h2>
    <p>
      Type: {{details.type}}
    </p>
    <p>
      Mood: {{details.mood}}
    </p>
    
    <img [src]="baseImageUrl + details.name">
    
    <p>
      <label for="override">Name Override:</label>
      <input type="text" id="override" [(ngModel)]="details.name">
    </p>
    
    <button (click)="deactive()">Deactivate</button>

Outputs

 
    import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
    import { RoboticsCentreService } from '../robotics-centre.service';
    
    @Component({
      selector: 'app-robot',
      templateUrl: './robot.component.html',
      styleUrls: ['./robot.component.css']
    })
    export class RobotComponent implements OnInit {
    
      @Input('details') details: {
        name: string,
        type: string,
        active: boolean,
        mood?: string
      };
    
      @Output() deactiveEmitter = new EventEmitter();
    
      baseImageUrl = 'https://robohash.org/';
    
      constructor(private roboticsCentre: RoboticsCentreService) { }
    
      ngOnInit() {
        this.roboticsCentre.getRobotMood()
          .subscribe(mood => this.details.mood = mood);
      }
    
      deactive() {
        this.deactiveEmitter.emit('KILL');
      }
    
    }

Outputs


    <!--src/app/app.component.html-->
    
    <app-robot *ngFor="let robot of robots; let idx = index" [details]="robot"
               (deactiveEmitter)="robots.splice(idx, 1)">
FIN

Review

Used Angular CLI

Created component

Hooked up app module

Input, output, event handlers, event emitter

Template directives

Connecting to APIs (observables)

Single/Two way data binding

Styled app

Next Steps

Routing

Animation

Pipes

Async as pipe

Lazy loading modules

Directives

Typescript

Observables

Thank you.

Made with Slides.com