- 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: anyevents: Array<any>
- add types everywhere
- DON'T
- add
:voidto 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
ngFormANDngModelvariables (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.restaurantin 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.tsimport { 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
:idparamimport { 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(): booleanreturnstrue(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-userwhere a logged in user is required to access a routerequire-anonwhere an anonymous user is requiredinit-authwhere 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.tsimport { HttpClientModule } from '@angular/common/http';imports: [ ..., HttpClientModule, ...],
- in
**.service.tsimport { HttpClient } from '@angular/common/http';constructor(... private httpClient: HttpClient ...) { }- your service methods (e.g.:
getAll(),create(data)) should return the underlyingHttpClientpromisesreturn 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
optionsargument 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