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.
Angular 4 Workshop
By Alex Jones
Angular 4 Workshop
Going over the fundamentals of Angular 4
- 1,908