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
        })
    }
}

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';

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