github.com/jroji
1joroji@gmail.com
@jnroji
Angular 1 is one of the most used
frameworks in the industry until now
Main advantages
> Dependency injection
> Scopes
> Two-way data binding
Main disadvantages
> Reusability low level
> Performance
> Strong hierarchy structure
Due to the comments of the community and the new tools provided by the browsers, the Angular Team decided to full rethink the new version of the framework
Scopes
Two-way data binding
ES5
Directives, services, factories
SCOPE VALUES
COMPONENT CLASS PROPERTIES
angular.module('scopeExample', [])
.controller('MyController', ['$scope', function($scope) {
$scope.username = 'World';
$scope.sayHello = function() {
$scope.greeting = 'Hello ' + $scope.username + '!';
};
}]);
@Component({selector: 'greet', template: 'Hello {{name}}!'})
class Greet {
name: string = 'World';
}
class AppDrawer extends HTMLElement {...}
window.customElements.define('app-drawer', AppDrawer);
const header = document.createElement('header');
const shadowRoot = header.attachShadow({mode: 'open'});
<template id="sdtemplate">
<style>
p { color: orange; }
</style>
<p>Hello</p>
</template>
@Component({
selector: 'my-component',
template: `<div>
Hello my name is {{name}}.
<button (click)="sayMyName()">Say my name</button>
</div>`
})
export class MyComponent {
constructor() {
this.name = 'Max'
}
sayMyName() {
console.log('My name is', this.name)
}
}
COMPONENTS!
TYPESCRIPT!
JIT / AOT!
DEPENDENCY INJECTION!
Routing
Lazy Load
HTTP
much more!
let declares a local scopped var
The scope of a variable defined with var is function scope or declared outside any function, global.
The scope of a variable defined with let is block scope.
const declares an "inmutable" var
//ES5
(function() {
console.log(x); // x not defined yet.
if(true) {
var x = "hello world";
}
console.log(x);
// Paints "hello world", because "var" make it global
})();
//ES6
(function() {
if(true) {
let x = "hello world";
}
console.log(x);
//Error, because x has been defined inside the if scope
})();
two main purposes: more concise syntax and sharing lexical this with the parent scope.
// BEFORE
var self = this;
function () { return self.quantity + 1; }
// AFTER
() => { return this.quantity + 1; }
() => this.quantity + 1
We can define default values for our parameters in functions definition
// ES5
function test(x, y) {
if(x == null) {
x = 5;
}
...
}
// ES6
function test(x = 5, y) {
...
}
With ES6, we don't need anymore the '' + '' syntax. We can use ${myVar} inside the code
// ES5
var x = 'Mr.' + name;
// ES6
let x = `Mr.${name}`;
And it supports multiline!
We can define functions and properties easier in a object (object-like syntax)
// ES5
obj = {
foo: function (a, b) {
…
},
bar: function (x, y) {
…
}
};
// ES6
obj = {
foo(a, b) {
…
},
bar(x, y) {
…
}
};
scoping (ts)
private
public
protected
inheritance
constructor
getter / setter
inmutable prop
export interface StringValidator {
isAcceptable(s: string): boolean;
}
import { StringValidator } from "./StringValidator";
// import { ZipCodeValidator as ZCV } from "./ZipCodeValidator";
// import * as validator from "./StringValidator";
let myValidator = new StringValidator();
StringValidator.ts
App.ts
JAVASCRIPT SUPERSET
STATIC TYPING
ECMASCRIPT 6 TRANSPILER
OBJECT ORIENTED
ERROR DETECTION BEFORE RUNTIME
MORE ROBUST CODE
BACKENDERS TECHNOLOGIES LIKE
JAVASCRIPT SUPERSET
STATIC TYPING
ECMASCRIPT 6 TRANSPILER
OBJECT ORIENTED
ERROR DETECTION BEFORE RUNTIME
MORE ROBUST CODE
BACKENDERS TECHNOLOGIES LIKE
JAVASCRIPT SUPERSET
STATIC TYPING
ECMASCRIPT 6 TRANSPILER
OBJECT ORIENTED
ERROR DETECTION BEFORE RUNTIME
MORE ROBUST CODE
BACKENDERS TECHNOLOGIES LIKE
let notSure: number|string;
lets use types in the input/output parameters
Specifies methods and properties without implementation. Usefull for require a concrete structure on a method
We can create a bigger class ussing more little classes. Usefull for a cleaner structure and cleaner code, even to improve the tests organization and execution
Decorators use the form @expression, where expression must evaluate to a function that will be called at runtime with information about the decorated declaration.
function sealed(constructor: Function) {
Object.seal(constructor);
Object.seal(constructor.prototype);
}
@sealed
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
configuration file in our project
{
"compilerOptions": {
"sourceMap": true,
"outDir": "dist"
},
"files": [
"src/Person.ts"
]
}
version Must match version exactly
'>'version Must be greater than version
'>'=version etc
'<'version
'<'=version
~version "Approximately equivalent to version"
^version "Compatible with version"
1.2.x 1.2.0, 1.2.1, etc., but not 1.3.0
"" (just an empty string) Same as *
version1 - version2 Same as >=version1 <=version2.
range1 || range2 Passes if either range1 or range2 are satisfied.
tag A specific version tagged and published as tag See npm-tag
$ mkdir npm-proj && cd npm-proj
$ npm init
$ npm install typescript
// OR
$ npm install typescript@2.2.0
// OR
$ npm install --save typescript
// OR
$ npm install --save-dev typescript
// OR
$ npm install -g typescript
@Component({
selector: 'my-component',
template: `<div>
Hello my name is {{name}}.
<button (click)="sayMyName()">Say my name</button>
</div>`
})
export class MyComponent {
constructor() {
this.name = 'Max'
}
sayMyName() {
console.log('My name is', this.name)
}
}
import { Component } from '@angular/core';
import { MyService } from './MyService';
@Component({
selector: 'my-component',
template: `<div>
Hello my name is {{name}}
</div>`,
providers: [ MyService ]
})
export class MyComponent {
constructor() {
this.name = 'Max'
}
sayMyName() {
console.log('My name is', this.name)
}
}
Import Component decorator from angular/core
Specify properties
selector, template...
Component controller
Component functions
import { Component } from '@angular/core';
@Component({
selector: 'my-component',
template: `<div>
Hello my name is {{name}}
</div>`
})
export class MyComponent {
constructor() {
this.name = 'Max'
}
sayMyName() {
console.log('My name is', this.name)
}
}
<body>
<div>
<my-component></my-component>
</div>
</body>
specifies the custom tag for our component. It will be registered in the DOM when the <my-component> tag is used
selector: 'my-component'
specifies the template of the component that will be rendered in the browser. A external template can be referenced by templateUrl, that will be compiled in execution / compilation time, depending on build system
template: '<h1>Hello</h1>',
templateUrl: './myComponent.html'
specifies the styles applyed to this component. External css files can be referenced using stylesUrl. These CSS are encapsulated by default with "emulated" strategy. The encapsulation strategy can be configured with the "encapsulation" property and can have any of the following values:
styles: `.test { color: red; }`,
stylesUrl: './myComponent.css'
import ViewEncapsulation from @angular/core
...
encapsulation: ViewEncapsulation.native
export class PeekABoo implements OnInit {
constructor(private logger: LoggerService) { }
// implement OnInit's `ngOnInit` method
ngOnInit() { this.logIt(`OnInit`); }
logIt(msg: string) {
this.logger.log(`#${nextId++} ${msg}`);
}
}
ngOnInit
Initialize the directive/component after Angular first displays the data-bound properties and sets the directive/component's input properties.
ngAfterContentInit
Respond after Angular projects external content into the component's view.
ngAfterViewInit
Respond after Angular initializes the component's views and child views.
ngOnDestroy
Cleanup just before Angular destroys the directive/component. Unsubscribe observables and detach event handlers to avoid memory leaks.
DATA / CLASS
my-component
binding
events
Angular component
<img [src] = "heroImageUrl">
Access to Angular context with [ ]
<h1> {{ title }} </h1>
Render information with {{ }}
<button (click)="myFunc()">
Listen to events with ( )
import { Component, Input } from '@angular/core';
@Component({
selector: 'hero-child',
template: `<p>I am at your service, {{masterName}}.</p>`
})
export class HeroChildComponent {
@Input() name: string;
@Input('master') masterName: string;
}
define properties that can be setted by the parent
the Inputs can have an input alias
<hero-child [master]="heroName"></hero-child>
import { Component, EventEmitter, Input, Output } from '@angular/core';
@Component({
selector: 'my-voter',
template: `<button (click)="vote(true)">Agree</button>`
})
export class VoterComponent {
@Output() onVoted = new EventEmitter<boolean>();
vote(agreed: boolean) {
this.onVoted.emit(agreed);
}
}
define and send events to child-parent communication
<my-voter (onVoted)="myFunction"></my-voter>
$ npm install -g @angular/cli
$ ng new test-app
$ cd test-app
$ ng serve
$ ng g component my-new-component
call controller class methods
<p>The sum of 1 + 1 is {{1+1}}</p>
<p>The sum of 1 + 1 is not {{1 + 1 + getVal()}}</p>
prop has "text" value
prop has context's text property value
don`t mess with the app performance
expression doesn't change app state
put the logic in the component if is too complex
<button (click)="readRainbow($event)">
NOT JUST CLICK
We can listen to ANY event created by the browser or developers on children
components
<input [(ngModel)]="name" >
<input [ngModel]="name" (ngModelChange)="name=$event">
equals to
<div [class.my-class]="isActive"> Hello </div>
<div [ngClass]="{active: isActive}"> Hello </div>
setCurrentClasses() {
// CSS classes: added/removed per current state of component properties
this.currentClasses = {
saveable: this.canSave,
modified: !this.isUnchanged,
special: this.isSpecial
};
}
<div [ngClass]="currentClasses"></div>
or as an object of classes
on template
<div [style.font-size]="isSpecial ? 'x-large' : 'smaller'" >
This div is x-large or smaller.
</div>
setCurrentStyles() {
// CSS styles: set per current state of component properties
this.currentStyles = {
'font-style': this.canSave ? 'italic' : 'normal',
'font-weight': !this.isUnchanged ? 'bold' : 'normal',
'font-size': this.isSpecial ? '24px' : '12px'
};
}
or as an object of classes
on template
<for-example *ngFor="let episode of episodes" [episode]="episode">
{{episode.title}}
</for-example>
*ngIf
Hiding an element is quite different from removing an element with NgIf.
<div *ngIf="errorCount > 0" class="error">
{{errorCount}} errors detected
</div>
<hero-detail *ngIf="isActive"></hero-detail>
*ngFor
*ngSwitch
<div [ngSwitch]="hero?.emotion">
<happy-hero *ngSwitchCase="'happy'" [hero]="hero"></happy-hero>
<sad-hero *ngSwitchCase="'sad'" [hero]="hero"></sad-hero>
<confused-hero *ngSwitchCase="'confused'" [hero]="hero"></confused-hero>
<unknown-hero *ngSwitchDefault [hero]="hero"></unknown-hero>
</div>
import { HEROES } from './mock-heroes';
@Injectable()
export class HeroService {
getHeroes(): Hero[] {
return HEROES;
}
}
// Component
constructor(private heroService: HeroService) { }
Simplify and reduce the necessary information that a dependency needs in order to simplify our component
import { Component } from '@angular/core';
import { Hamburger } from '../services/hamburger';
@Component({
selector: 'app',
template: `Bun Type: {{ bunType }}`
})
export class App {
bunType: string;
constructor(h: Hamburger) {
this.bunType = h.bun.type;
}
}
providers: [{ provide:Logger, useClass: TimestampLogger}]
Can use an alias in order to update the provider avoiding change it in the application
We can use the @Optional decorator in order to make that dependency not critical
import { Optional } from '@angular/core';
class AnotherService{
constructor(@Optional() private logger: Logger) {
if (this.logger) {
this.logger.log("I can log!!");
}
}
}
Very usefull in order to use dev dependencies without modifying code
import { Injectable } from '@angular/core';
@Injectable()
export class ChatService {
private messages: Array<Object>;
getMessages(): Array<Object> {
return this.messages;
}
}
import { ChatService } from '../chat.service.ts';
@Component({
selector: <chat-component></chat-component>,
template: '...'
})
export class ChatComponent {
private messages: Array<Object>;
constructor(private chatService:ChatService) {
this.messages = this.chatService.getMessages();
}
}
import { Http } from '@angular/http';
...
constructor (private http: HttpClient) {}
getHeroes (): Promise<Hero[]> {
return this.http.get(this.heroesUrl)
.toPromise();
}
// OR WITH OBSERVABLES
getHeroes (): Observable<Hero[]> {
return this.http.get(this.heroesUrl)
}
¿PROMISES OR OBSERVABLES?
https://angular.io/docs/ts/latest/guide/server-communication.html#!#promises
Observable is preferred over Promise because it provides the features of Promise and more. Doesn't matter if you want to handle 0, 1, or multiple events.
Observable also has the advantage over Promise to be cancelable
An Observable emits items or sends notifications to its observers when the observed item is modified
import { Observable } from 'rxjs/Observable';
// Create the observable
const data = new Observable(observer => {
setTimeout(function() {
observer.next(40);
}, 2000
});
....
// Subscribe the observable
data.subscribe(value => {
console.log(value);
});
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
@NgModule({
imports: [ BrowserModule ],
declarations: [ AppComponent ],
bootstrap: [ AppComponent ]
})
export class AppModule { }
Every Angular app has at least one module, the root module, conventionally named AppModule.
declarations - the view classes that belong to this module. Angular has three kinds of view classes: components, directives, and pipes.
exports - the subset of declarations that should be visible and usable in the component templates of other modules.
imports - other modules whose exported classes are needed by component templates declared in this module.
providers - creators of services that this module contributes to the global collection of services; they become accessible in all parts of the app.
FOR EACH PROVIDER, A NEW INSTANCE OF THE DEPENDENCY IS CREATED
bootstrap - the main application view, called the root component, that hosts all other app views. Only the root module should set this bootstrap property. (Solo para el mainmodule).
providers - creators of services that this module contributes to the global collection of services; they become accessible in all parts of the app.
bootstrap - the main application view, called the root component, that hosts all other app views. Only the root module should set this bootstrap property. (Solo para el mainmodule).
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
platformBrowserDynamic().bootstrapModule(AppModule);
Modules imported dinamically or statically for other sections or functionalities
Multiple pages
Load same resources in every page
section
resources
section
resources
section
resources
One page
Brings all resources
section 1
resources
section 2
section 3
One page
Brings CRITICAL resources
Brings resources on demand
section 1
resources
section 2
section 3
The first and more important step!
Add the base tag to your index.html in order to tell your angular application where to look for static resources
<base href="/">
Second! lets create our routes object
We can import the Routes interface in order to define the routes. The order matters!
import { RouterModule, Routes } from '@angular/router';
const appRoutes: Routes = [
{ path: 'crisis-center', component: CrisisListComponent },
{ path: 'hero/:id', component: HeroDetailComponent },
{
path: 'heroes',
component: HeroListComponent,
data: { title: 'Heroes List' }
},
{ path: '',
redirectTo: '/heroes',
pathMatch: 'full'
},
{ path: '**', component: PageNotFoundComponent }
];
Each Route maps a URL path to a component. There are no leading slashes in the path. The router parses and builds the final URL for you, allowing you to use both relative and absolute paths when navigating between application views.
The :id in the first route is a token for a route parameter. In a URL such as /hero/42, "42" is the value of the id parameter. The corresponding HeroDetailComponent will use that value to find and present the hero whose id is 42.
{ path: 'crisis-center', component: CrisisListComponent },
{ path: 'hero/:id', component: HeroDetailComponent },
{
path: 'heroes',
component: HeroListComponent,
data: { title: 'Heroes List' }
},
{ path: '',
redirectTo: '/heroes',
pathMatch: 'full'
},
{ path: '**', component: PageNotFoundComponent }
{
path: 'heroes',
component: HeroListComponent,
data: { title: 'Heroes List' },
children: {
path: 'Test',
component: TestComponent
}
}
The selected router will be displayed on the <router-outlet> tag of the "bootstrap component" in the root component, or the father component if we are using "children" routes
<router-outlet></router-outlet>
And finally, we add the routes object to our module, and we can define a "bootstrap" for contain our components
const routes: Routes = [
{ path: 'component-one', component: ComponentOne },
{ path: 'component-two', component: ComponentTwo }
];
export const routing = RouterModule.forRoot(routes);
@NgModule({
...
imports: [
BrowserModule,
routing
],
bootstrap: [ AppComponent ]
...
}
Now, we can use routerLink directive in our a elements to set the current url
<a routerLink="/heroes" routerLinkActive="active">Heroes</a>
We can use the routerLinkActive to tell Angular to add an active class by himself
Module
cmp
cmp
cmp
ROUTES
INJ
Module
cmp
cmp
cmp
ROUTES
INJ
Module 1
Module 2
Module 3
Do less. Be lazy
$ ng g module lazy
$ ng g component lazy-info
Lets create a new module in our application and a new component for the new module
{
path: 'lazy',
loadChildren: './lazy/lazy.module#LazyModule'
},
Modify the app.routes.ts in order to add a new route with "loadChildren"
Remove lazy-info component from the app.module declarations
import {Routes} from "@angular/router";
import {LazyInfoComponent} from "../lazy-info/lazy-info.component";
export const lazyRoutes: Routes = [
{
path: '',
component: LazyInfoComponent
}
]
Now lets create a lazy.routes.ts in the lazy module directory with the relative routes
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import {LazyInfoComponent} from "../lazy-info/lazy-info.component";
import {RouterModule} from "@angular/router";
import {lazyRoutes} from "./lazy.routes";
@NgModule({
imports: [
CommonModule,
RouterModule.forChild(lazyRoutes)
],
declarations: [
LazyInfoComponent
]
})
export class LazyModule { }
And import them into our new module. But CAREFUL!, now we will use the forChild function
With the previous steps, we will have an application loading only the critical resources in the first load, and the feature modules loading by request
@NgModule({
bootstrap: [TestComponent],
imports: [RouterModule.forRoot(ROUTES,
{preloadingStrategy: PreloadAllModules})]
})
class TestModule {}
We can use preloadingStrategy in order to preload modules
We can create custom preload strategies!
export class PreloadSelectedModulesList implements PreloadingStrategy {
preload(route: Route, load: Function): Observable<any> {
return route.data && route.data.preload ? load() : of(null);
}
}
[
{
path: 'moduleA',
loadChildren: './moduleA.module',
data: {preload: true}
}
]
Any user can navigate wherever he wants by our web and our modules. But maybe
In order to handle those scenarios, we can use guards. These mechanism allow us to "do things" before the state loads, such as prevent the module load based on the user role, or get some data before the component initialize
CanActivate executes to mediate in a navigation. Can prevent the navigation if the guard returns false.
CanDeactivate similar to the previous one but executes on the "away" navigation from state. Useful for unsuscribe observables, or stop watching certain vars
Resolve executes a function in order to get some data, required by the loading component.
CanLoad a executes a function in order to decide if the asynchronous module should be loaded or not.
import { Injectable } from '@angular/core';
import { CanActivate } from '@angular/router';
@Injectable()
export class AuthGuard implements CanActivate {
canActivate() {
console.log('AuthGuard#canActivate called');
return true;
}
}
import { AuthGuard } from '../auth-guard.service';
const adminRoutes: Routes = [
{
path: 'admin',
component: AdminComponent,
canActivate: [AuthGuard]
}
]
CanActivate
import { Injectable } from '@angular/core';
import { Router, Resolve, RouterStateSnapshot,
ActivatedRouteSnapshot } from '@angular/router';
import { Crisis, CrisisService } from './crisis.service';
@Injectable()
export class CrisisDetailResolver implements Resolve<Crisis> {
constructor(private cs: CrisisService, private router: Router) {}
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<Crisis> {
let id = route.params['id'];
// get data from a service
return this.cs.getCrisis(id).then(crisis => {
if (crisis) {
return crisis;
} else { // id not found
this.router.navigate(['/crisis-center']);
return null;
}
});
}
}
Resolver
// On my component
ngOnInit() {
this.route.data
.subscribe((data: { crisis: Crisis }) => {
this.editName = data.crisis.name;
this.crisis = data.crisis;
});
}
Resolver
@ngrx/store is a controlled state container designed to help write performant, consistent applications on top of Angular. Core tenets:
$ ng new store-app
npm install @ngrx/store --save
//main.state.ts
export interface State {
counter: number;
};
export const intitialState: State = {
counter: 10
};
Inside of the state folder let’s create a new file
import {ActionReducer, Action} from "@ngrx/store";
import {State, intitialState} from "../state/main-state";
export const mainStoreReducer: ActionReducer<State> = (state = intitialState, action: Action) => {
switch (action.type) {
case 'INCREMENT': {
return {
counter: state.counter + 1
}
}
case 'EVENT_FROM_EFFECT': {
return {
counter: 4
}
}
default: {
return state;
}
}
};
The reducer is the thing that catches and handles the actions that are dispatched from your smart components (or from your action reducers), and it is also the thing that actually resets the state to a new, modified state
import {ActionReducer, Action} from "@ngrx/store";
import {State, intitialState} from "../state/main-state";
export const mainStoreReducer: ActionReducer<State> = (state = intitialState, action: Action) => {
switch (action.type) {
case 'INCREMENT': {
return {
counter: state.counter + 1
}
}
case 'EVENT_FROM_EFFECT': {
return {
counter: 4
}
}
default: {
return state;
}
}
};
The reducer is the thing that catches and handles the actions that are dispatched from your smart components (or from your action reducers), and it is also the thing that actually resets the state to a new, modified state
//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 { mainStoreReducer } from "./state-management/reducers/main.reducer";
import { StoreModule } from "@ngrx/store";
const reducers = { counter: mainStoreReducer };
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
FormsModule,
HttpModule,
StoreModule.forRoot(reducers)
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
import { Component } from '@angular/core';
import { Store } from "@ngrx/store";
import { State } from "./state-management/state/main.state";
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'app works!';
constructor (private store:Store<State>) {
console.log('We have a store! ' + store);
}
}
import { Component } from '@angular/core';
import { Store } from "@ngrx/store";
import { State } from "./state-management/state/main.state";
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'app works!';
data = '';
constructor (private store:Store<State>) {
store.select('counter')
.subscribe( (data:State )=> {
this.data = 'data is' + data;
});
}
}
Reading data
import { Component } from '@angular/core';
import { Store } from "@ngrx/store";
import { State } from "./state-management/state/main.state";
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'app works!';
data = '';
constructor (private store:Store<State>) {
store.select('mainStoreReducer')
.subscribe( (data:State )=> {
this.data = 'data is ' + data;
});
}
public increment() {
this.store.dispatch({ type: 'INCREMENT' });
}
}
Calling reducer actions
//app.component.html
<h1>
{{title}}
</h1>
<h2>
{{data}}
</h2>
<button (click)="increment()"> + 1 </button>
"Firebase provides a realtime database and backend as a service. The service provides application developers an API that allows application data to be synchronized across clients and stored on Firebase's cloud."
// environment/environment.ts
export const environment = {
production: false,
firebase: {
apiKey: '<your-key>',
authDomain: '<your-project-authdomain>',
databaseURL: '<your-database-URL>',
projectId: '<your-project-id>',
storageBucket: '<your-storage-bucket>',
messagingSenderId: '<your-messaging-sender-id>'
}
};
configure the environment
import { AngularFireModule } from '@angular/fire';
import { AngularFirestoreModule } from '@angular/fire/firestore';
...
@NgModule({
imports: [
BrowserModule,
AngularFireModule.initializeApp(environment.firebase),
AngularFirestoreModule
],
declarations: [ AppComponent ],
bootstrap: [ AppComponent ]
})
import the module
import {Component} from '@angular/core';
import {AngularFireDatabase, FirebaseListObservable} from 'angularfire2/database';
@Component({
selector: 'project-name-app',
template: `
<ul>
<li *ngFor="let item of items | async">
{{ item.name }}
</li>
</ul>
`
})
export class MyApp {
items$;
constructor(private db: AngularFirestore) {
this.items$ = this.db.collection('messages').valueChanges();
}
}
import {Component} from '@angular/core';
import { AngularFirestore } from 'angularfire2/firestore';
@Component({
selector: 'project-name-app',
template: `
<ul>
<li *ngFor="let item of items | async">
{{ item.name }}
</li>
</ul>
`
})
export class MyApp {
fbMessages: any;
constructor(private db: AngularFirestore) {
this.fbMessages = this.db.collection('messages');
this.fbMessages.add(message);
}
}
<label for="name">Name</label>
<input type="text" id="name" required [(ngModel)]="model.name" name="name">
Angular forms module allow us to handle and attach easily the model of our inputs
<form #heroForm="ngForm">
<div>
<label for="name">Name</label>
<input type="text" id="name" required
[(ngModel)]=“myModel.name"
name="name">
<button type="submit">Submit</button>
</form>
ngModel directive, used in order to synchronize our model with the data inputted. Under the hood, Angular is making a two-way data binding, using the “Banana-in-a-box” notation [()]
The name attribute is always required, not just because of a11y. Although, Angular uses it in the model binding
<form>
<input [(ngModel)]="form.name" name="name">
<input [(ngModel)]="form.mail" name="mail">
</form>
We can handle an object with all the form data
<form #loginForm="ngForm">
<input name="name" required [(ngModel)]="form.name">
<input name="password" type="password" required [(ngModel)]="form.password">
<button type="submit"></button>
</form>
<p *ngIf="!loginForm.valid"> Fill all the fields</p>
We can create references to the full form in order to validate it fully
#loginForm = "ngForm"
import {Component, ViewChild} from 'angular2/core';
...
@ViewChild('loginForm') public form: NgForm;
...
public showFrm(): void{
console.log(this.form);
}
We can access to an Angular reference from the controller with
@ViewChild('frm')
<form #loginForm="ngForm" (ngSubmit)="login(loginForm.value)">
<input name="name" required [(ngModel)]="form.name" >
<input name="password" type="password" required [(ngModel)]="form.password">
<button type="submit"></button>
</form>
<p *ngIf="!loginForm.valid"> Fill all the fields</p>
An use (ngSubmit) to handle the form submission
<form #heroForm="ngForm">
<div>
<label for="name">Name</label>
<input type="text" id="name" required
[(ngModel)]=“myModel.name"
name="name">
<button type="submit">Submit</button>
</form>
Internally Angular creates FormControls and registers them with an NgForm directive that Angular attached to the
tag. Each FormControl is registered under the name we assigned to the name attribute
When using ngModel on an input, Angular modifies automatically the className attribute of the element, adding the followings based on the element`s state
We can use features in order to show error messages
<input type="text" id="name"
required
[(ngModel)]="model.name"
name="name"
#name="ngModel" >
<div [hidden]="name.valid || name.pristine"
class="alert alert-danger"> Name is required </div>
And we can add a ngSubmit to the form, using a custom action defined in our class
<form (ngSubmit)="onSubmit()" #heroForm="ngForm">
Now, the form is associated to the heroForm var, allowing us to reset it (heroForm.reset()), validate(heroForm.valid), or accessing (heroForm)
With reactive forms, you create a tree of Angular form control objects in the component class and bind them to native form control elements in the component template
Create and manipulate form control objects directly in the component class. As the component class has immediate access to both the data model and the form control structure, you can push data model values into the form controls and pull user-changed values back out
IMPORTANT!
We have to import the
ReactiveFormsModule
FormControl tracks the value and validity status of an individual form control. It corresponds to an HTML form control such as an input box or selector.
FormGroup tracks the value and validity state of a group. The group's properties include its child controls. The top-level form in your component is a FormGroup.
Imagine that we want to validate a new User
export interface User {
name: string; // required with minimum 5 chracters
address?: {
street?: string; // required
postcode?: string;
}
}
We can use FormGroup and FormControl to create validators and bind to the forms
this.myForm = new FormGroup({
name: new FormControl('', [<any>Validators.required, <any>Validators.minLength(5)]),
address: new FormGroup({
street: new FormControl('', <any>Validators.required),
postcode: new FormControl('8000')
})
});
Or use FormBuilder to the "short" way
constructor(private formBuilder: FormBuilder) {
this.myForm = this.formBuilder.group({
name: ['', [<any>Validators.required, <any>Validators.minLength(5)]],
address: this.formBuilder.group({
street: ['', <any>Validators.required],
postcode: ['']
})
});
}
And we can link class and view
<form [formGroup]="myForm">
<div>
<label>Name</label>
<input type="text" formControlName="name">
</div>
<div formGroupName="address">
<label>Address</label>
<input type="text" formControlName="street">
<small [hidden]="myForm.get('address.street').valid">
street not valid
</small>
</div>
<div formGroupName="address">
<label>Postcode</label>
<input type="text" formControlName="postcode">
</div>
</form>
We can use setValue to set one input, or the whole form!
const people = {
name: 'Jane',
address: {
street: 'High street',
postcode: '94043'
}
};
this.myForm.setValue(people, { onlySelf: true });
And we can subscribe to the form too!
// subscribe to the stream
this.myForm.valueChanges.subscribe(x => this.events
.push({ event: ‘STATUS CHANGED’, object: x }));
...
this.myForm.get('name').valueChanges.subscribe((data) => {
console.log(data);
});
Or create custom validators
import { FormControl } from '@angular/forms';
function validateEmail(c: FormControl) {
let EMAIL_REGEXP = ...
return EMAIL_REGEXP.test(c.value) ? null : {
validateEmail: {
valid: false
}
};
}
https://angular-maps.com/
Angular's animation system lets you build animations that run with the same kind of native performance found in pure CSS animations
Angular animations are built on top of the standard Web Animations API and run natively on browsers that support it
For other browsers, a polyfill is required
Angular's animation system lets you build animations that run with the same kind of native performance found in pure CSS animations
Angular animations are built on top of the standard Web Animations API and run natively on browsers that support it
For other browsers, a polyfill is required
import {
trigger,
state,
style,
animate,
transition
} from '@angular/animations';
Lets start importing the animations module from @angular/animations
animations: [
trigger('heroState', [
state('inactive', style({
backgroundColor: '#eee',
transform: 'scale(1)'
})),
state('active', style({
backgroundColor: '#cfd8dc',
transform: 'scale(1.1)'
})),
transition('inactive => active', animate('100ms ease-in')),
transition('active => inactive', animate('100ms ease-out'))
])
]
The angular animations module allow us to define transitions between states. The states are defined with the value of a binded var
// "void" is a reserved name for a state and is used to represent
// the state in which an element is detached from from the application.
state("void", style({ height: 0 }))
// user-defined states
state("closed", style({ height: 0 }))
state("open, visible", style({ height: "*" }))
We can define multiple states
And now, we can use it in the template to bind the animation to a concrete element
<ul>
<li *ngFor="let hero of heroes"
[@heroState]="hero.state"
(click)="hero.toggleState()">
{{hero.name}}
</li>
</ul>
The hero.state will define the value of the animation state
transition('inactive => active', animate('100ms ease-in')),
transition('active => inactive', animate('100ms ease-out')),
transition('* => inactive', animate('100ms ease-out')),
transition('* => *', animate('100ms ease-out')),
transition('* => void', animate('100ms ease-out')),
transition('void => in', animate('100ms ease-out')),
We can define multiple type of transitions, with diferent animations
animations: [
trigger('shrinkOut', [
state('in', style({height: '*'})),
transition('* => void', [
style({height: '*'}),
animate(250, style({height: 0}))
])
])
]
If we don't know the value of a dimensional style property, you can use a special * property value so that the value of the property is computed at runtime and then plugged into the animation.
animations: [
trigger('flyInOut', [
state('in', style({width: 120, transform: 'translateX(0)', opacity: 1})),
transition('void => *', [
style({width: 10, transform: 'translateX(50px)', opacity: 0}),
group([
animate('0.3s 0.1s ease', style({
transform: 'translateX(0)',
width: 120
})),
animate('0.3s ease', style({
opacity: 1
}))
])
])
We can make parallel executed animations using group
<ul>
<li *ngFor="let hero of heroes"
(@flyInOut.start)="animationStarted($event)"
(@flyInOut.done)="animationDone($event)"
[@flyInOut]="'in'">
{{hero.name}}
</li>
</ul>
One of the greates advantages of angular animations is that we can use callbacks in order to "do things" when the animation is complete
Normally, a SPA web page spend a lot of time interpreting and taking control of the view, and it takes a lot of time to see useful content on a web
Old devices can browse our site because the application returns HTML, which differs from common Single Page Applications (SPA), where the tag contains JavaScript.
The first page request is fast and subsequent ones are even faster; as opposed to common SPAs, where the first request is used to load the application and then a round trip is made to fetch what was requested.
The Angular Universal project consists of the base platform API and the surrounding tools that enables developer to do server side rendering(or pre-rendering) of Angular application
Webpack is a module bundler, a simple tool that takes just the needed resources of our app, transforms them based on our configuration files, and put them together in order to minimize the application modules and files
We can install webpack trough npm
npm install -D webpack ts-loader html-webpack-plugin tslint-loader
This way, we are installing some importante dependencies for the Angular development, such as the ts compiler for webpack, an html loader in order to minimize an “inlining” the html, and validate our code style with the tslint-loader
We can split our app in “bundles”, which means a composition of modules. For example, we can have a bundle for the external dependencies, another one for our main module, and one more by each lazy load module.
In webpack, we define the output bundles, and how are they constructed with the “entry” object
{
entry: {
app: './src/app/main.ts',
vendor: [
'es6-shim',
'angular2/bundles/angular2-polyfills',
'angular2/bootstrap',
'angular2/platform/browser',
'angular2/platform/common_dom',
'@angular/core',
'angular2/router',
'angular2/http',
]
}
}
And now, we can define how our bundles gonna be transformed, with loaders or plugins. For example, we are gonna use a loader, in order to compile typescript to javascript
{
test: /\.ts$/,
loader: 'ts-loader',
exclude: /node_modules/
}
We can use plugins too, in order to add some actions to the web pack bundler process, such as insert automatically the output bundlers in our app
new HtmlWebpackPlugin({
template: './src/index.html',
inject: 'body',
minify: false
});
Lets check of looks a webpack file
Angular applications are built with templates, which may be .html or .css files, or may be inline template attributes on Decorators like @Component.
These templates are compiled into executable JS at application runtime.
Compile all of this resources in runtime requires the angular compiler as part of the final bundle, and can be slow to compile huge amount of data, templating and necessary files.
This is known as JiT (Just In Time)
JiT is the simpler compilation method. We can use the angular cli to do it with
ng build --prod
If we look closer at the entry point of our app, we will se something like this
import { ViewEncapsulation } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app';
platformBrowserDynamic()
.bootstrapModule(AppModule);
We can see that we are importing AppModule directing from app, without compiling. Webpack will transform it with loaders and plugins
In order to minimize compilation time on runtime, and improve performance, Angular team has created a runtime compilation in order to avoid this process on the browser.
This compilation reduces the bundles size in 60%, and improves a lot the performance in the pages load
This is known as AoT (Ahead of time)
AoT is the "before browser" compilation method. We can use the angular cli to do it with
ng build --aot --prod
AoT is the "before browser" compilation method. We can use the angular cli to do it with
ng build --aot --prod
In order to use this kind of compilation without the cli, we should have something like this entrypoint
/*
* Angular bootstraping
*/
import { AppModuleNgFactory } from '../compiled/src/app/app.module.ngfactory';
import { platformBrowser } from '@angular/platform-browser';
platformBrowser()
.bootstrapModuleFactory(AppModuleNgFactory);
We can see that the import of AppModule comes from a "compiled" dir. This is the entry point of the AoT compiled modules.
In order to add this compilation step in our typescript config file, we can add a new "angularCompilerOptions" option
"angularCompilerOptions": {
"genDir": "aot",
"skipMetadataEmit" : true
}
This way we can tell our application to create compiled aot modules
Unit testing refers to the practice of testing certain functions and areas – or units – of our code. This gives us the ability to verify that our functions work as expected. That is to say that for any function and given a set of inputs, we can determine if the function is returning the proper values and will gracefully handle failures during the course of execution should invalid input be provided.
JavaScript test runner
Testing framework
suite: collection of test cases that are intended to be used to test a software program to show that it has some specified set of behaviours
spec: test case for a concrete specification of the component / file to test
describe("AppComponent", function() {
var a;
it("a should be true", function() {
a = true;
expect(a).toBe(true);
});
});
This is a basic test with Jasmine
Angular recomends this stack in order to test our componentss, and provide some tools to help us configuring our dependencies on our tests
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { DebugElement } from '@angular/core';
TestBed
It creates an Angular testing module—an @NgModule class—that you configure with the configureTestingModule method to produce the module environment for the class you want to test.
In effect, you detach the tested component from its own application module and re-attach it to a dynamically-constructed Angular test module tailored specifically for this battery of tests.
TestBed
TestBed.configureTestingModule({
declarations: [ BannerComponent ], // declare the test component
});
createComponent
After configuring TestBed, you tell it to create an instance of the component-under-test. We will use TestBed.createComponent to create an instance of a Component and returns a component test fixture.
The createComponent method closes the current TestBed instance to further configuration
TestBed
fixture = TestBed.createComponent(BannerComponent);
comp = fixture.componentInstance; // BannerComponent test instance
The Fixture, is a handle on the test environment surrounding the created component. It provides access to the component instance itself and to the DebugElement, which is a handle on the component's DOM element.
Basic test with angular
it('should increment number', async(() => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const elm = fixture.componentInstance;
expect(elm.value).toBe(5);
elm.testFuncion();
expect(elm.value).toBe(6);
}));
The automatic change detection is disabled in angular testing by default, in order to give the developer more control over the asynchronous and no controlled changes
Basic test with angular
import { ComponentFixtureAutoDetect } from '@angular/core/testing';
TestBed.configureTestingModule({
declarations: [ BannerComponent ],
providers: [
{ provide: ComponentFixtureAutoDetect, useValue: true }
]
})
We can activate automatic detection importing ComponentFixtureAutoDetect and adding it to providers
The automatic change detection is disabled in angular testing by default, in order to give the developer more control over the asynchronous and no controlled changes
it('should still see original title after comp.title change', () => {
const oldTitle = comp.title;
comp.title = 'Test Title';
// Displayed title is old because Angular didn't hear the change :(
expect(el.textContent).toContain(oldTitle);
});
it('should display updated title after detectChanges', () => {
comp.title = 'Test Title';
fixture.detectChanges(); // detect changes explicitly
expect(el.textContent).toContain(comp.title);
});
If we need to inject a service, we should use a copy, in order to no create or depends on a external element
const userServiceStub = {
isLoggedIn: true,
user: { name: 'Test User'}
};
TestBed.configureTestingModule({
declarations: [ WelcomeComponent ],
providers: [ {provide: UserService, useValue: userServiceStub } ]
});
userService = TestBed.get(UserService);
If we want to test an event, an async service or a service petition, we can use spies.
A spy can stub any function and tracks calls to it and all arguments. There are special matchers for interacting with spies. The toHaveBeenCalled matcher will return true if the spy was called
We can use a different value for a service in order to test it
let mockRouter = {
navigate: jasmine.createSpy('navigate')
};
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [ NavToolComponent ],
providers: [
{ provide: Router, useValue: mockRouter }
]})
})
...
expect(mockRouter).toHaveBeenCallWith(['/home']);
spyOn(callToService, 'getUsers').andReturn(fakeHttpPromise);
component.updateUser();
expect(callToService.getUsers).toHaveBeenCalled();
component.updateUser('admin');
expect(callToService.getUsers).toHaveBeenCalledWith('admin');
it('should emit on filter click', inject([], () => {
let valueAfterClick;
productListComp.itemClicked.subscribe(item => {
console.log(item);
valueAfterClick = item;
});
let el = fixture.debugElement.children[0];
let card = el.query(By.css('.global-position__personal')).nativeElement;
card.click();
expect(valueAfterClick).toBe('profile');
}));
Testing events
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import {TeoLoginComponent} from './teo-login.component';
describe('TeoLoginComponent', () => {
let component: TeoLoginComponent;
let fixture: ComponentFixture<TeoLoginComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ TeoLoginComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(TeoLoginComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
let httpClientSpy: { get: jasmine.Spy };
let heroService: HeroService;
beforeEach(() => {
// TODO: spy on other methods too
httpClientSpy = jasmine.createSpyObj('HttpClient', ['get']);
heroService = new HeroService(<any> httpClientSpy);
});
it('should return expected heroes (HttpClient called once)', () => {
const expectedHeroes: Hero[] =
[{ id: 1, name: 'A' }, { id: 2, name: 'B' }];
httpClientSpy.get.and.returnValue(asyncData(expectedHeroes));
heroService.getHeroes().subscribe(
heroes => expect(heroes).toEqual(expectedHeroes, 'expected heroes')
);
})
We can test our components with the cli
$ ng test
or test generating coverage report
$ ng test --code-coverage