learning Angular
Notes from the course with Deborah Kurata in Pluralsight
https://github.com/deborahk/angular-gettingstarted
What is a Component?
Component =
- Template
- View Layout
- Created with HTML
- Includes binding and directives
+
- Class with properties and methods
- Code supporting the view
- Created with typescript
- Properties: data
- Methods: logic
+
- Metadata aditional information
- Extra data for data
- Defined with a decorator
With Metadata we can create a Angular Component
app.component.ts
import { Component } from '@angular/core';
@Component({
selector: `pm-root`,
template: '
<div>
<h1>{ { pageTitle } }</h1>
<div>My first component</div>
</div>
'
})
export class AppComponent {
pageTitle: string = 'PageTitle '
}
Strong Typing
pageTitle: string = 'Product List';
imageWidth: number = 50;
imageMargin: number = 2;
showImage: boolean = false;
listFilter: string = 'cart';
products: any[] = []
Interface: A Specification indentifying a related set of properties and methods.
A class commits to supporting the specification by implementing the interface.
Use the interface as a data type. Only for Typescript, so, only used in development
Ej
export interface IProduct {
productId: number;
productName : string;
productCode: string;
releaseDate: Date;
price: number;
description: string;
starRating : number;
imageUrl: string;
calculateDiscount(percent: number) : number;
}
So, you can use it like this
import { IProduct } from './product';
...
products: IProduct[] = [...];
Angular Module
AppModule
- AppComponent
Templates, Interpolation and Directives
Binding
Coordinates comunication between the component’s class and its template and often involves passing data
<img [src]='product.imageUrl' />
// or
<img src={{product.imageUrl}} />
//or
<img src='http://url/{{product.imageUrl}}' />
Two-way binding
<input [(ngModel)]='listFilter' >
export class ListComponent {
listFilter: string = 'cart';
}
Interpolation
<h1>{{pageTitle}}</h1>
{{'Title: ' + pageTitle}}
{{2*20+1}}
{{'Title: ' + getTitle()}}
<h1 innertext="{{pageTitle}}"></h1>
Directives
Structural Directives
*ngIf="booleanSentence"
Improving Components
- Strong typing and interfaces
- Encapsulating Styles
- Lifecycle hooks
- Custom pipes
- Nested Components
Strong Typing
The strong typing helps you to avoid errors with a checking tooling. The type is defined before the var name like the following.
pageTitle: string = 'Product List';
imageWidth: number = 50;
imageMargin: number = 2;
showImage: boolean = false;
listFilter: string = 'cart';
products: any[] = [...];
The methods also have strong typing like this
onRateClicked(message: string) : void {
}
defining the input and output type format.
for more complex formats we can use any
or create a interface. The Interface is a specification identifying a related set of properties and methods. A class commits to supporting the specification by implementing the interface.
Use the interface as a data type. So, is used only in development.
export interface IProduct {
productId: number;
productName : string;
productCode: string;
releaseDate: string;
price: number;
description: string;
starRating : number;
imageUrl: string;
calculateDiscount(percent: number) : number;
}
// Usage
products: IProduct[] = [...];
Encapsulating Component Styles
You can add styles in the component or separate in a diferent file
// Styles
@Component({
selector: 'pm-products',
templateUrl: './product-list.component.html',
styles: ['thead {color: #337AB7;}']
})
export class ProductListComponent{ ... }
// StyleUrls
@Component({
selector: 'pm-products',
templateUrl: './product-list.component.html',
styleUrls: ['./product-list.component.css']
})
export class ProductListComponent{ ... }
Component Lifecycle
Component lifecycle hooks,
- OnInit -> Perform component inicialization, retrive data.
- OnChanges => Perform action after change to input properties
- OnDestroy => Perform cleanup
Transforming Data with Pipes
There are several pipes built-in in angular, like date, number and so on … the idea with this part is to build our own custom pipes to transform data.
@Pipe({
name: 'convertToSpaces'
})
export class ConvertToSpacesPipe implements PipeTransform
{
//Perform the transform
transform( value: string, // The value to transform
character: string ) // Some Arguments (Ej: The caracter to replace)
: string { // The returned value
}
}
// To Use
{{ product.productCode | convertToSpaces:'-'}}
// Note: Add the pipe to the angular module on the declarations part
Nested Components
Using a Component
- As a Directive
App Component OR Nested Component - As a Routing Target
Full page style view
What makes a Component Nest-able?
- Its template only manages a fragment of a larger view
- It has a selector
- it optionally communicates with its container
Building and Using a Nested Component
import { Component, OnChanges } from '@angular/core';
@Component({
selector: 'pm-star',
templateUrl: './star.component.html',
styleUrls: ['./star.component.css']
})
export class StarComponent implements OnChanges {
rating: number = 4;
starWith: number;
ngOnChanges(): void {
this.starWith = this.rating * 75 / 5;
}
}
And Using on product-list.component.html
<td>
<pm-star></pm-star>
</td>
Passing data to a Nested Component Using @input
import { Component, OnChanges, Input } from '@angular/core';
@Component({
selector: 'pm-star',
templateUrl: './star.component.html',
styleUrls: ['./star.component.css']
})
export class StarComponent implements OnChanges {
@Input() rating: number = 4;
starWith: number;
ngOnChanges(): void {
this.starWith = this.rating * 75 / 5;
}
}
And Using on product-list.component.html
<td>
<pm-star [rating]='product.starRating' ></pm-star>
</td>
Raising an Event from a Nested Component using @Output
Using a EventEmitter Events.
We create the property @Output called ratingClicked
, then we create a HTML click event called onClick
, and use the this.ratingClicked.emit()
to send the message necesary.
<div class="crop"
[style.width.px]="starWidth"
[title]="rating"
(click)="onClick()">
import { Component, OnChanges, Input, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'pm-star',
templateUrl: './star.component.html',
styleUrls: ['./star.component.css']
})
export class StarComponent implements OnChanges {
// ...
@Output() ratingClicked: EventEmitter<string> =
new EventEmitter<string>();
onClick(): void {
this.ratingClicked.emit(`The rating ${this.rating} was clicked!`);
}
}
</string></string>
then, in the parent component we delegate the new incoming event ratingClicked
in a new method call onRatingClicked($event)
<pm-star [rating]='product.starRating'
(ratingClicked)='onRatingClicked($event)'>
</pm-star>
Then, We just create that method in the ts file
onRatingClicked(message: string ) : void {
this.pageTitle = `Product List: ${message}`;
}
Services and Dependency Injection
Used to handle data or logic that is not associated with a specific view or that we whant to share across services.
Service
A class with a focused propose, Used for features that:
- Are independient from any particular component
- Provide shared data or logic across components
- Encapsulate external interactions
In Angular we use the Angular Injector, like a dependency injector.
Dependency Injection: A coding pattern in which a class receives the instances of objects it needs (Called Dependencies) from an external source rather than creating them itself
Building a Service.
// product.service.ts
import { Injectable } from '@angular/core';
@Injectable()
export class ProductService {
// Logic
}
Registering a Service
There are two ways:
// product.service.ts
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root' // Root means access this Service from any Component or other service
})
export class ProductService { }
And then, we can use it in the component
// product-list.component.ts
import { ProductService } from './product.service';
@Component({
selector: 'pm-products',
templateUrl: './product-list.component.html',
})
export class ProductListComponent implements OnInit {
/* OLD
private _productService;
constructor(productService: ProductService) {
this._productService = productService;
}
*/
//NEW
constructor(private productService: ProductService) {
}
ngOnInit(): void {
this.products = this.productService.getProducts();
}
}
and then, you can use it
ngOnInit(): void {
this.products = this.productService.getProducts();
}
Retrieving Data Using Http
Observables and Reactive Extensions
- Reactive Extensions (RxJS)
- Help Manage asynchronous data
- Treat events as a Collection
- An array whose items arrive asynchronously over time
- Subcribe to receive notifications
Observable Operators
- Methods on observables that compose new Observables
- Transform the source observable in some way
- Process each value as it is emitted
- EJ: Map, filter, take, marge, …
Composing Operators
We can Compose Operators with the pipe method. Often Called Pipeable Operators.
import { Observable, range } from 'rxjs';
import { map, filter } from 'rxjs/operators';
//...
const source$: Observable<number> = range(0,10);
source$.pipe(
map(x => x * 3),
filter(x => x % 2 === 0)
).subscribe(x => console.log(x));
</number>
Retrieving Data Using Http
Fist, update the service to remove the moq data.
import { Injectable } from '@angular/core';
import { IProduct } from './product';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError, tap } from 'rxjs/operators';
@Injectable({
providedIn: 'root'
})
export class ProductService {
private productUrl = 'api/products/products.json';
constructor(private http: HttpClient) { }
getProducts(): Observable<iproduct[]> {
return this.http.get<iproduct[]>(this.productUrl).pipe(
tap(data => console.log(`All: ${JSON.stringify(data)}`)),
catchError(this.handleError)
);
}
private handleError(err: HttpErrorResponse){
let errorMessage = '';
if(err.error instanceof ErrorEvent){
errorMessage = `An Error ocurred: ${err.error.message}`;
}else {
errorMessage = `Server returned code: ${err.status}, error message is ${err.message}`;
}
console.error(errorMessage);
return throwError(errorMessage);
}
}
</iproduct[]></iproduct[]>
An add HttpClientModule
into the app.module.ts
//...
import { HttpClientModule } from "@angular/common/http";
@NgModule({
declarations: [
//...
],
imports: [
//...
HttpClientModule
],
bootstrap: [AppComponent]
})
export class AppModule { }
And then, use the service to get the data.
// product-list.component.ts
import { ProductService } from './product.service';
export class ProductListComponent implements OnInit {
// ...
ngOnInit(): void {
this.productService.getProducts().subscribe({
next: products => {
this.products = products;
this.filteredProducts = this.products;
},
error: err => this.errorMessage = err
})
}
}
Navigation and Routing Basics
Routes to navigate between multiple views in our application.
- How does routing works?
- Configure a route for each component
- Define options/actions
- Tie a route to each option/action
- Activate the route based on user action
- Activating a route displays the component’s view
- Configuring Routes
//Add RouterModule into app.module.ts
import { RouterModule } from "@angular/router";
@NgModule({
declarations: [
//...
],
imports: [
RouterModule.forRoot([
{ path : 'products', component: ProductListComponent },
{ path : 'products/:id', component: ProductDetailComponent },
{ path : 'welcome', component: WelcomeComponent },
{ path : '', redirectTo: 'welcome' , pathMatch: 'full' },
{ path : '**', component: PageNotFoundComponent },
], { useHash: true })
],
})
export class AppModule { }
- Link options with the routerLink
<nav class="navbar navbar-expand navbar-light bg-light">
<a href="#" class="navbar-brand">{{pageTitle}}</a>
<ul class="nav nav-pills">
<li><a href="" [routerLink]="['/welcome']" class="nav-link">Home</a></li>
<li><a href="" [routerLink]="['/products']" class="nav-link">Product List</a></li>
</ul>
</nav>
<div class="container">
<router-outlet></router-outlet>
</div>
Passing Parameters
https://medium.com/better-programming/angular-6-url-parameters-860db789db85
Protecting routes with Guards
- CanActivate: Guard navitation to a route
- CanDeactivate: Guard navigation from a route
- Resolve: Pre-fetch data before activating a route
- CanLoad: Prevent asynchronous routing
ProTip CLI: ng g g products/product-detail
import { CanActivate } from '@anguar/router';
@Injectable({
providedIn: 'root'
})
export class ProductDetailGuard implements CanActivate {
canActivate() : boolean {
//...
}
}
// app.module.ts
@NgModule({
declarations: [
//...
],
imports: [
RouterModule.forRoot([
{ path : 'products', component: ProductListComponent },
{ path : 'products/:id',
canActivate: [ProductDetailGuard],
component: ProductDetailComponent
},
], { useHash: true })
],
})
export class AppModule { }
Angular Modules
- An angular module is just a class with an NgModule decorator
Its purpose:
- Organize the pieces of our application
- Arrange them into blocks
- Extend our application with capabilities from external libraries
- provide a template resolution environment
- Aggregate and re-export
Notes:
- Every component, directive and pipe we create must belong to one and only one Angular Module.
- Only declare components, directives and pipes… dont add clases, services or modules to the declarations array.
- Never re-declare components, directives or pipes that belong to another module.
- All declared componets, directives and pipes are private by default.
- They are only accessible to other components, directives, and pipes declared in the same module.
- Is necesary to export them.
- The angular module provides the template resolution environment for its component templates.
Exports Array
- Export any component, directive, or pipe if other components need it.
- Re-export modules to re-export their components, directives and pipes.
- We can re-export something without importing it first.
- Never export a service.
Utils
$ npm install bootstrap font-awesome
On the Style.css@import '~bootstrap/dist/css/bootstrap.min.css';
@import '~font-awesome/css/font-awesome.min.css';
Useful Links
https://www.youtube.com/watch?v=OL3JppHRluE | Angular 8 Tutorial | Learn Angular from Scratch | Angular Tutorial | Edureka - YouTube
https://angular.io/guide/lifecycle-hooks | Angular - Lifecycle Hooks
https://angular.io/guide/observables | Angular - Observables
https://angular.io/guide/rx-library | Angular - The RxJS library
https://angular.io/guide/pipes | Angular - Pipes
https://stackoverflow.com/questions/39800616/asp-net-core-application-lifecycle | c# - ASP.NET Core Application Lifecycle - Stack Overflow
https://gist.github.com/Whistler092/5b784894ce551f3450d2efcc30f6c8f8#file-preguntas_entrevista-txt | preguntas entrevista
https://medium.com/frontend-fun/angular-unit-testing-jasmine-karma-step-by-step-e3376d110ab4 | Angular: Unit Testing Jasmine, Karma (step by step)
https://scotch.io/tutorials/testing-angular-with-jasmine-and-karma-part-1 | Testing Angular with Jasmine and Karma (Part 1) ― Scotch.io
https://frontendmasters.com/courses/angular-core/creating-the-nx-workspace/ | Learn Creating the Nx Workspace – Angular Core
https://www.udemy.com/home/my-courses/search/?p=2&q=angular | Cursos online - en cualquier momento y en cualquier lugar | Udemy
https://www.udemy.com/course/draft/833398/learn/lecture/5317694#overview | Angular Masterclass | Udemy
https://www.udemy.com/course/the-complete-angular-master-class/learn/lecture/7251988#overview | The Complete Angular Course: Beginner to Advanced | Udemy
https://www.udemy.com/course/build-amazon-clone-angular5-node/learn/lecture/9112636#overview | Complete Modern Amazon clone: Angular 5 and Node.js | Udemy
https://www.udemy.com/course/angular-con-devops-tdd-pruebas-unitarias-pipelines-git/learn/lecture/11624584#overview | Angular con DevOps, TDD, Pruebas Unitarias, Pipelines, Azure | Udemy
https://onedrive.live.com/?cid=59ADDE0BBFFF7842&id=59ADDE0BBFFF7842%21420582&parId=59ADDE0BBFFF7842%21420552&o=OneUp | 001 Antes de continuar aprendiendo Angular.mp4 - OneDrive
https://angular.io/start | Angular - Getting Started with Angular: Your First App
https://stackblitz.com/angular/eporrqrnoxe | Angular Example - Getting Started - StackBlitz
https://angular.io/cli | Angular - CLI Overview and Command Reference
https://angular.io/tutorial/toh-pt3 | Angular - Master/Detail Components
https://angular.io/guide/template-syntax#event-binding | Angular - Template Syntax
https://angular.io/api/common | Angular - @angular/common
https://angular.io/guide/lifecycle-hooks#oninit | Angular - Lifecycle Hooks
How to Avoid Observables in Angular
https://dev.to/angular/how-to-avoid-observables-in-angular-273h