- SPA
- Build
- Components
- Injection + Services + Singleton
- REST API
- DOCS
- LU
- made by microsoft
- superset of javascript
- all ES5 and ES6 code is valid in typescript
- browser can NOT understand typescript
- transpiled down to es6 or es5 by tools
- types
- DOCS
- type safety vs javascript dynamic types
- can (should) be applied to:
- constants and variables
- function parameters
- function return values
- class properties
- interfaces
- DOCS
- small reusables "contracts"
- define what will be "public"
- interfaces are also types (advanced)
- classes
- DOCS
- concept of public vs private
- properties documented before the constructor
- class properties have types
- class method arguments and return values have types
- can implement more than one interface
- all methods and fields are public by default
- methods and fields can be declared as private
- private, public shortcut on constructors
- decorators
- DOCS
- can be applied to class, class property, class method, function param, getters/setters
- DO:
- add types everywhere
- function parameters
- function return values
- class properties
- use lower case for primitives
- string
- boolean
- number
- upper case for objects
- Object
- Array
- Date
- K.I.S.S. objects and arrays (prevents errors like
username is not a known property of newUser
)newEvent: any
events: Array<any>
- add types everywhere
- DON'T
- add
:void
to functions that return undefined - forget to initialize arrays
events: Array<any> = []
- prevents error when later doing
this.events.push(...)
- prevents error when later doing
- add the type declaration to primitives that are immediately initialized
- e.g.:
category: string = 'default'
- prevents tslint complaining
Type string trivially inferred from a string literal, remove type annotation (no-inferrable-types)
- e.g.:
- add
- DOCS
- made by google
- written in typescript
- single page application framework, including:
- views
- components
- form management
- routing
- dependency injection
- DOCS
$ ng new name-of-app
$ ng serve
$ ng serve --aot
$ ng g c components/name-of-component
$ ng g c pages/name-of-page
$ ng g s services/name-of-service
$ ng g g guards/name-of-guard
$ ng g p pipes/name-of-pipe
$ ng g class models/name-of-model
$ ng build
- DOCS
index.html
- is static
- contains
<app-root></app-root>
- can contain extra script and style tags
- app/app.module.ts (a.k.a. THE app)
- DOCS
- declares all the angular modules used in the app
- declares the routing
- declares all the components created by us
- declares all the services, pipes, etc... create by us
- app/app.component.* (a.k.a. main component or layout component)
- layout of the app (header, footer)
- where the
<router-outlet></router-outlet>
goes
- app/pages
- where we should store our page components
ng g c pages/login-page
- app/components
- where we should store our smaller components
ng g c components/auth-login-form
- DOCS
- interpolation
{{ expression }}
- property binding
<button [disabled]="processing">
- event binding
<button (click)="handleClick($event, index, true)">
- two way binding
- add
import { FormsModule } from '@angular/forms';
toapp.module.ts
- add
imports: [ ... FormsModule, ... ]
toapp.module.ts
<input type="text" [(ngModel)]="username" />
- add
- DOCS
- NOTE: angular has two types of forms
- template-driven forms (the one we use)
- reactive forms (the one we DON'T use)
- form and field state is available in
ngForm
ANDngModel
variables (see template vars below) - available properties:
- valid
- invalid
- dirty
- pristine
- touched
- untouched
- errors (e.g.
usernameField.errors.minlength
) - submitted (only in forms)
- template variables
- declared directly in templates
- in inputs
<input #username ...>
- in inputs with NgModel
<input #usernameField="ngModel" ...>
- in forms
<form #form="ngForm" ...>
- in inputs
- used directly in templates
- ngModel variables
<div *ngIf="usernameField.errors">
- ngForm variables
<div *ngIf="form.invalid">
- ngModel variables
- passed from template to component class
- input/ngModel variables
<input (keyup)="handleKeyUp(usernameField.value)" ...
- ngForm variables
<form (ngSubmit)="addAnimal(form)" ...
- input/ngModel variables
- declared directly in templates
- DOCS
- LU
- ngIf
*ngIf="expression"
- ngFor
*ngFor="let item of array"
*ngFor="let item of array, let ix = index"
*ngFor="let item of array, trackBy: trackFn"
(advanced)
- ngSwitch
[ngSwitch]="expression">
*ngSwitchCase="expression"
(if you want a string, make sure to wrap it with strings e.g:*ngSwitchCase="'not-found'"
)*ngSwitchDefault
- ngClass
- use with key value pairs
- key is classname
- value is expression, apply class if expression is truthy
[ngClass]="{ 'has-errors': usernameField.invalid }"
- use with array
[ngClass]="arrayOfStrings"
- use with key value pairs
- ngStyle
- don't use if you can just toggle css classes with ngClass
[ngStyle]="{ 'left': player.x, 'top': player.y }"
- components can be nested
- create components to reuse (DRY principle) and simplify (single responsability principle)
- top level components we call "page" components (convention)
- generate "page" components with
ng g c pages/login-page
- generate "other" components with
ng g c components/auth-login-form
- always route to a "page" component
- concetrate responsibilities in "page" components (e.g.: talk to services that connect to the API)
- keep the "other" components simple
- component input
[input]="data"
- send data from parent components to child components
- component outputs
(output)="handleFunction($event)"
- listen in parent components to events emitted from child components
- DOCS
- LU
- in the child component's class
import { Input } from '@angular/core'
@Input() restaurant: Object
- you can use
this.restaurant
in the component methods - you can use
{{restaurant.name}}
in the component template
- in the parent component's template
<app-restaurant-card [restaurant]="data">...
- DOCS
- LU
- in the child component's class
import { Output, EventEmitter } from '@angular/core'
@Output() change = new EventEmitter<string>();
- you can use
this.change.emit(this.terms)
- in the parent component's template
<app-restaurant-search (change)="handleSearchChange($event)"> ...
- in the parent component's class
handleSearchChange(event) { .... }
- DOCS
- LU
- DatePipe
- debug: {{users | json}} :-)
- date: {{user.birthday | date:'short'}}
- price: {{product.price | currency:'EUR':'symbol':'1.2-2'}}
- using variable as pipe arguments:
- price: {{product.price | currency:user.settings.currency:'symbol':'1.2-2'}}
- DOCS
- CHEAT SHEET
- in
app.module.ts
import { RouterModule, Routes } from '@angular/router';
const routes: Routes = [ ..., { path: 'movies/:id', component: MovieDetailPageComponent }, ... ]
imports: [ ..., RouterModule.forRoot(routes), ...],
- in
app.component.html
... <router-outlet></router-outlet> ...
- in component templates
<a [routerLink]="['/restaurants', restaurant._id]">
- in components, to read
:id
paramimport { ActivatedRoute } from '@angular/router';
constructor(... private route: ActivatedRoute ... )
this.route.params.subscribe((params) => { ... params.id ... }
- in components, to navigate
import { Router } from '@angular/router';
constructor(... private router: Router ... )
this.router.navigate(['/restaurants']);
- 404 page
- in
app.module.ts
{path: '**', component: NotFoundPageComponent}
- in
- DOCS
- LU
- generate with
ng g s services/name
- services are simple classes
- use services to:
- keep your code DRY - a place to keep methods that are used by other services/components
- store state that can be accessed/modified by other services/components
- create observables that other services/components can subscribe too
- services are injectables
@Injectable() export class RestaurantService {
- you can ask Angular to provide an instance of service to the constructor of other services/components
constructor(... private restaurantService: RestaurantService ...)
- services are singletons
- Angular only creates one instance of each service (and only once some service/component requires its injection)
- services sould be small and follow SRP (single responsibility principle)
- if making requests to a simple API, create one service with all the methods
- if making requests to a larger API (e.g., your Node.js REST API) create one service per domain (e.g.:
auth
,restaurants
, etc...)
- DOCS
- LU
- CHEAT SHEET
- generate with
ng g g guards/require-user
, etc. - guards determine if the user is allowed or forbidden to access a certain route
- should only have one function
canActivate()
canActivate(): boolean
returnstrue
(to allow) orfalse
(to forbid)- or
canActivate(): Promise<any>
returns a promise that rejects withtrue
(to allow) orfalse
(to forbid) - if returning/resolving with false, navigate somewhere else from within the guard
this.router.navigate(['/']); return false;
- register the guards in each route of
app.module.ts
{ path: '', component: HomePageComponent, canActivate: [ InitAuthGuard ] }, { path: 'login', component: AuthLoginPageComponent, canActivate: [ RequireAnonGuard ] }, { path: 'signup', component: AuthSignupPageComponent, canActivate: [ RequireAnonGuard ] }, { path: 'page', component: ... , canActivate: [ RequireUserGuard ] },
- and add them to the providers list of
app.module.ts
providers: [... ..., InitAuthGuard, RequireAnonGuard, RequireUserGuard, ... ],
- and add them to the providers list of
- for authentication you will need 3 guards:
require-user
where a logged in user is required to access a routerequire-anon
where an anonymous user is requiredinit-auth
where it's irrelevant whether the user is logged in or not (but we still want to initialize the auth service)
- DOCS
- LU
- CHEAT SHEET
- in
app.module.ts
import { HttpClientModule } from '@angular/common/http';
imports: [ ..., HttpClientModule, ...],
- in
**.service.ts
import { HttpClient } from '@angular/common/http';
constructor(... private httpClient: HttpClient ...) { }
- your service methods (e.g.:
getAll()
,create(data)
) should return the underlyingHttpClient
promisesreturn this.httpClient.METHOD...
- the
httpClient.get()
andhttpClient.delete()
methods take 2 argumentsreturn this.httpClient.get('http://...', options)
- the
httpClient.put()
andhttpClient.post()
method take 3 argumentsreturn this.httpClient.post('http://....', data, options)
- the
options
argument is always needed if making requests authenticated by cookies (e.g.: your Node.js REST API)const options = { withCredentials: true };
- without it, the HttpClient service does not send cookies in a CORS context
- cookies are required to keep a session in the Rest API