AngularSutra

angular

SECTION 1


9 – What is TypeScript

Its a super-set of vanilla JavaScript. It has more features like Types, Classes, Interfaces, etc. But as because the browser does not support Typescript, it is first compiled to JavaScript via the CLI prior running in the browser.

11 – Adding Bootstrap to Angular project

While this can be added directly to the index file, it is better to do it the angular way. While in the project folder, type “npm install –save bootstrap” in the command line and press enter. This will install the bootstrap into the current project setup and save it as a production dependency.
Then open the .angular-cli.json file and add the link to the bootstrap file in the styles tag: ../node_modules/bootstrap/dist/css/bootstrap.min.css


SECTION 2


14 – How an Angular app gets loaded and started

  1. The src/index.html file gets loaded which has a tag
  2. When the Angular compiles, it makes sure to include the compiled main.ts in the index file. This can be checked while running the app and checking the source.
  3. The main.ts file tells angular to include the AppModule from the app.module.ts
  4. The app.module.ts contains the declaration for AppComponent module from the app.component.ts file
  5. The app.component.ts file has the html and css declarations for the view, which are app.component.html and app.component.css respectively.
  6. Any tags added to the app.component.html file gets rendered in the view and any styles given in the app.component.css file gets applied.

16 – Creating a Component

  1. Create a new folder, conventionally the name of the component. my-own
  2. Create a new file inside the folder named my-own.component.ts
  3. Create a new file in the same folder named my-own.component.html
  4. Add the following lines to the my-own.component.ts file:
// import the component module
import { Component } from '@angular/core';
// Define the selectors, so Angular knows what to do with this class
@Component({
    // Required parameters
    // The selector should not clash with any other selectors
    selector: 'my-component',
    // selector: '[my-component]'
    // selector: '.my-component'
    templateUrl: './my-own.component.html' // relative link to the html file
    // Optional Parameters // styles
    styleUrls: ['./my-own.component.css', ''] // This is an array of relative paths
})
// Export the class for use
export class MyOwnComponent {
}

5. Add the following lines to my-own.component.html

<h3>This is MyOwnComponent</h3>

17 – Understanding the role of AppModule and Component Declaration

The AppModule can be seen to have a @NgModule declaration which holds 4 key parameters:
1. declarations
2. imports
3. providers
4. bootstrap

The MyOwnComponent that we created in the previous part won’t be recognized by Angular as till now it has no idea that the component exists.
To make sure that Angular knows about the custom component, it is required that it is added to the declarations tag of the AppModule.
Note: Adding the MyOwnComponent name to the declarations tag should throw a compiler error, so make sure to import it first.

The imports parameter makes it easy to import multiple modules to the app, so as to keep the AppModule leaner when the application becomes very large.

18 – Using Custom Components

To know if the MyOwnComponent that we created earlier is working, open the app.component.html file and add the custom selector tag we defined for our component in the file.
Run the application and you should see the “This is MyOwnComponent” on the page

19 – Creating components with the CLI and nexting components

To create components, one can also use the CLI

ng generate component servers // or in short
ng g c servers

This will create a folder named servers and create a few files, namely:
servers.component.ts
servers.component.html
servers.component.css
servers.component.spec.ts – This is the Test Script file and has nothing to do with development and hence can be deleted.

20 – Working with Component Templates

While an external templateUrl can be provided in the Custom Component declaration, one can also use inline templates.
So instead of using templateUrl: ‘/custom.component.html’,
one can also do template: ‘<h1>Custom Tags are allowed</h1>’
also using back-ticks template: ` <h1>Custom Tags are allowed</h1> `

A few things to keep in mind:
1. At least one of them should be present, i.e.- template or templateUrl
2. If defining template, they should not be wrapped to multi-line, as typescript doesn’t support that in a string.
3. If multi-line string is required, then one can use back-ticks ` to wrap code templates.

21 – Working with component styles

Similar to that of the template, the styles can also be defined inline.

styles: [
    h3 {
        color: blue;
    }
]

22 – Fully understanding the Component Selector TODO

While the only selector that has been used till now is:
selector: ‘app-servers’ – This makes for a tag selector, so needs to be used as

<app-servers></app-servers>

This can also be replaced with
selector: ‘[app-servers]’ – This would make it an attribute selector and can be used as

TODO

selector: ‘.app-servers’ – This would make it a class selector and can be used as

<div class="app-servers"></div>

23 – What is DataBinding

DataBinding is required for communications between the client and the server.
To output data, one can use:
1. String Interpolation – {{ data }}
2. Property Binding – [property]=”data”
To react to user interactions
1. Event Binding – (event)=”expression”

While the above ways are 1-way binding, angualr can be used for 2-way binding by combining the above:
[(ngModel)]=”data”

24 – String Interpolation

The string interpolation can be done by wrapping the expression between double curly braces –
{{ expression }}
The expression can be anything which will return a string, or can be converted to a string, but not multi levels of code. It can be:

  1. A variable name – {{ userName }}
  2. A direct string – {{ ‘my name is Anthony’ }}
  3. A function which would return a string – getUserName() – A function which would be returning a string
  4. Ternary operations – userName ? userName : ‘anonymous’

25 – Property Binding

Every HTML element has a lot of properties, but not all can be accessed/set via the attributes.
For example, ‘disabled’ is an attribute which when set disables the element. But this is also a property which can be accessed using the [disabled] in the HTML tag and can be used as a property to set.

<button class="btn" disabled>Click Me</button> Setting the attribute 'disabled'
<button class="btn" [disabled]=true>Click Me</button> Section the property 'disabled'

26 – Property Binding vs String Interpolation

Use string interpolation when we have to show some text only.
Use property binding, when there is requirement to bind the property of an element to some variable.

27 – Event Binding

Events can be bound in Angular using the moon brackets.
(click)=”myFunction()” will call the myFunction() available in the .ts file.
Don’t bind to onclick. This is for javascript and not Angular.

28 – Bindable properties and events

A console.log() on the element will provide the events available for binding.

29 – Passing and Using Data with Event Binding

The event data can also be passed on to the function using the $event reserved keyword.
Usage:

<input type="text" (input)="onUpdateServerName($event)">

This will pass the event data to the function and can be used in the following way:

onUpdateServerName(event:Event) {
  this.serverName = (event.target).value;
  // The (event.target) is the way to typecast to make sure that the compiler doesn't throw compiling error.
}

30 – Two Way Data-Binding

Two Way Binding can be implemented combining the square brackets as well as the moon brackets.
Usage:

<input type="text" [(ngModel)]="serverName">

31 – Important Notice for Two-Way Data-binding

For Two-Way Data-binding to work, make sure to import the FormsModule from ‘@angular/forms’ in the app.module.ts, and add it to the imports array.

33 – Understanding Directives

Directives are instructions in the DOM
Receives a Green Background

@Directive({
    selector: '[appTurnGreen]'
})
export class TurnGreenDirective {
    …
}

34 – Using ngIf to output data conditionally TODO

Usage:
Created Server {{ serverName }}
TODO
Anything can be passed to the *ngIf directive that can return a Boolean. Based on true/false, the element is either added/removed from the DOM.

35 – Enhanced ngIf with the Else condition

<p *ngIf="serverCreated else noServer">Created Server {{ serverName }}</p>
<ng-template #noServer>
    <p>No Server Created</p>
</ng-template>

Using the #noServer marks a template for using somewhere else.

36 – Styling elements Dynamically with ngStyle

<p [ngStyle]="{backgroundColor: getColor()}">

The ngStyle is a directive and has been implemented with the square brackets to make sure that it is bound to the property ngStyle
** More information required

37 – Applying CSS classes Dynamically with ngClass

<p [ngClass]="{online: serverStatus === 'online'}">

In case of ngClass, it will take a list of key-value pairs, where:
key = the class to add
value = the condition

38 – Outputting lists with ngFor

<app-server *ngFor="let server of servers"></app-server>

The element in which the *ngFor is used will get replicated for the number of items in the ‘servers’ array.

39 – Getting the index when using ngFor

The index can be fetched using:

<p *ngFor="let item of clickCounter; let i = index"></p>

The i can be replaced with any variable but the index is a reserved keyword.


SECTION 3


Use the CLI to create the components
ng g c component-name –spec false – This will not create the testing file.

47 – Create a Recipe model

Create a new file named recipe.model.ts

export class Recipe {
    public name:string;
    public desc:string;

    constructor(name:string, desc:string) {
        this.name = name;     this.desc = desc;
    }
}

This can also be shortened to: // Learnt earlier

export class Recipe {
    constructor(public name:string, public desc:string) {    }
}

SECTION 4


57 – Debugging Code in the Browser using Sourcemaps

The compiled files are available in ‘sources’ section of the Developer tools.
The most important file in them is the main.bundle.js
While trying to put a debug point somewhere in the file, the debug point moves to the typescript file. This feature is an addition to JavaScript (source-mapping).
The typescript files can be directly accessed from the webpack in the sources tab.

58 – Using Augury to dive into Angular Apps

Augury is a chrome extension to check all the relations and data of a running Angular App and can be accessed via the Developer Tools.


SECTION 5


60 – Splitting Apps Into Components

The app has been split into components and their children. Now it is needed to know how to pass the data in between them.

62 – Binding to Custom Properties

Even when a property inside a class is public, the property can’t be directly accessed from outside (the components hosting it).
To make sure that they are accessible, add @Input() before the variable declaration.

@Input() element:ServerElement;

Make sure to import Input from @angular/core.
Now it an be bound like this:

<app-server-element *ngFor="let serverElement of serverElements" [element]="serverElement"></app-server-element>

63 – Assigning an Alias to Custom Properties

An alias can be provided by updating the element definition as:

@Input('srvElement') element:ServerElement;

And can be used in the calling component as:

<app-server-element *ngFor="let serverElement of serverElements" [srvElement]="serverElement"></app-server-element>

Note: After using the above method, the ‘element’ can’t be used anymore and the ‘srvElement’ will only work.

64 – Binding to Custom Events

To create a custom event:

serverAdded = new EventEmitter<{serverName:string, serverContent:string}>();

Defining a variable as an EventEmitter will make sure that the event can be emitted.
To make sure that it can accessed from outside, add the @Output() before declaration:

@Output() serverAdded = new EventEmitter<{serverName:string, serverContent:string}>();

And can emit by using:

this.serverAdded.emit({serverName: this.newServerName, serverContent: this.newServerContent});

Now it can be used as:

<app-cockpit (serverAdded)="onServerAdded($event)"></app-cockpit>

65 – Assigning and Alias to Custom Events

Alias to be defined as:

@Output('srvAdded') serverAdded = new EventEmitter<{serverName:string, serverContent:string}>();

And to be used as:

<app-cockpit (srvAdded)="onServerAdded($event)"></app-cockpit>

All other information remains similar to Lecture 62.

66 – Custom Property and Event Binding Summary

While the Input and Output declarations are easy to setup and data can be passed in between views, but when the views are very far linked, these Input/Output can become very difficult to manage.
In that case, services will come to play.

67 – Understanding View Encapsulation

Angular implements View Encapsulation by using a custom attribute to all the elements inside a particular component. This is known as Shadow DOM.
Shadow DOM is a new method being applied by browsers, btu it is not fully implemented by all browsers. So Angular emulates it by its own by adding a specific attribute to each of its component, making sure that everything that has been defined by the component is only used by the component, mainly css.

68 – More on View Encapsulation

The encapsulation can be overridden by defining ‘encapsulation’ in the Component descriptor.

@Component({

encapsulation: ViewEncapsulation.None
// other options are also available - Native/Emulated
// Native uses the browser capability
// Emulated uses the Angular's Shadow DOM
})

69 – Using Local References in Templates

Local references can be made by adding the reference to the element by using a # followed by the reference name.

<input type="text" class="form-control" #serverNameElement>

This reference can be accessed anywhere inside the template, but not outside, not even the ts file.

70 – Getting Access to the Template and DOM with @ViewChild

There is a way of getting access to any element:
Add the id in the element:

<input type="text" class="form-control" #serverContentInput>

And access it using the following in the ts file:

@ViewChild('serverContentInput') svrContentInput:ElementRef;

Rather than using ‘serverContentInput’, one can also use

@ViewChild(CockpitComponent) svrContentInput:ElementRef;

This will return the first instance of the particular element. This is generally not used as it is not very specific.
While one can also use the @ViewChild to set the value like:

this.svrContentInput.nativeElement.value = "Something";

one should not use this as it is very risky and the DOM should never be manipulated directly.

71 – Projecting Content into Components with ng-content

Anything placed between our own component will get lost as Angular doesn’t care about it.

<app-cockpit>
    <h1>This is my own area</h1>
</app-cockpit>

In the above case, the h1 tag gets lost and won’t be included in the rendering.
This can be avoided by using the ng-content directive in the cockpit.component.html file:

<ng-content><ng-content>

By using this, whatever is placed within the tag will get rendered at the position where tag is used in the component.

This will help in rendering different types of views based on requirements, depending on where the CustomComponent is used.

72 – Understanding the Component Lifecycle

  1. ngOnChanges – Called after a bound input property changes. Called during initialization. Called multiple times.
  2. ngOnInit – Called when the component is initialized. it will run after the constructor
  3. ngDoCheck – Called during every change detection run
  4. ngAfterContentInit – Called after content (ng-content) has been projected into view
  5. ngAfterContentChecked – Called every time the projected component has been checked.
  6. ngAfterViewInit – Called after the components view and its children view has been initialized
  7. ngAfterViewChecked – Called every time the view and child views have been checked.
  8. ngOnDestroy – Called once the component is about to be destroyed.

73 – Seeing Lifecycle Hooks in Action

While the Lifecycle functions can be called directly, it is better to ‘implement’ them on the component to be explicit about what functions are going to be accessed.

…
export class MyOwnComponent implements OnInit, OnChanges {
    ngOnChanges(change:SimpleChange) { }
    ngOnInit() { }
}

This is unchecked and can be rerun and noted down. TODO

74 – Lifecycle Hooks and Template Access

If and any hook applied to templates and being accessed via the @Input() tag in the TypeScript file, the values are only available after the ngAfterViewInit() is called.

75 – Getting Access to ng-content with @Content-Child

The template content that has been defined within the CustomComponent area to be projected into the ng-content area can also be accessed. This can be done by passing a hook #hookname, and can be accessed by using the Content-Child descriptor to access the ElementRef and use it after ngAfterContentInit() is called.

<app-cockpit>
    <p #theHook>
        …
    </p>
</app-cockpit>

And creating the reference in .ts file by using:

@ContentChild('theHook') element:ElementRef;

And access it after the ngAfterContentInit() is called:

this.element.nativeElement;

SECTION 7


82 – Module Introduction

Attribute Directives vs Structural Directives
Attribute Directive – Looks like a normal HTML attribute, which only affects the element they are added to.
Structural Directive – Looks like a normal HTML attribute with a leading * (for desugaring), and affects the whole DOM, as the elements get added/removed based on it. For example: *ngIf

83 – ngFor and ngIf recap

We can’t have more than one structural directive on one element

85 – Creating a Basic Attribute Directive

basic-highlight.directive.ts

import { Directive, ElementRef, OnInit } from '@angular/core';

@Directive({
    selector: '[appBasicDirective]'
})
export class BasicHighlightDirective {
    constructor(private elementRef:ElementRef) { }
    ngOnInit() {
        this.elementRef.nativeElement.style.backgroundColor = 'green';
    }
}

To make this directive available for use in the application, this has to added to AppModule in the declarations tag

@NgModule({
    declarations: [
       AppComponent,
       BasicHighlightDirective
    ]
    …
})

And to use it, simply do:

<p appBasicDirective>This is a highlighted element.</p>

86 – Using the Renderer to build a Better Attribute Directive

The previous method is not a good practice, as it directly access and sets the values in the DOM
better-highlight.directive.ts

import { Directive, ElementRef, OnInit, Renderer2 } from '@angular/core';
@Directive({
selector: '[appBetterDirective]'
})
export class BetterHighlightDirective {
constructor(private elementRef:ElementRef, private renderer:Renderer2) {
}
ngOnInit() {
this.renderer.setStyle(this.elementRef.nativeElement, 'background-color', 'blue');
// The fourth argument is optional.
}
}

To make this directive available for use in the application, this has to added to AppModule in the declarations tag

@NgModule({
    declarations: [
       AppComponent,
       BetterHighlightDirective
    ]
    …
})

And to use it, simply do:

<p appBetterDirective>This is a highlighted element.</p>

Its better to use the BetterDirective as because Angular runs on multiple platforms, like the service workers, where there is no access to the DOM, so the BasicDirective method won’t work.

88 – Using HostListener to Listen to Host Events

The class BetterHighlightDirective can be upgraded to listen to Host Events by using:

@HostListener('event-name') localEventName(event:Event) {
    // Do the specified tasks.
}

For example:

@HostListener('mouseenter') onMouseEnter(event:Event) {
    this.renderer.setStyle(this.elementRef.nativeElement, 'background-color', 'blue', false, false);
    // Check why there are multiple false arguments.

}
@HostListener('mouseleave') onMouseLeave(event:Event) {
    this.renderer.setStyle(this.elementRef.nativeElement, 'background-color', 'transparent', false);
}

89 – Using HostBinding to Bind to Host Properties

There is also another easy way to update the view without using the renderer.

@HostBinding('propertt-to-bind-to') localPropertyName:string = 'default-value';

For example:

@HostBinding('style.backgroundColor') myBackgroundColor:string = 'transparent';
…
@HostListener('mouseenter') onMouseEnter(event:Event) {
    this.myBackgroundColor = "blue";
}
@HostListener('mouseleave') onMouseLeave(event:Event) {
    this.myBackgroundColor = 'transaparent';
}

90 – Binding to Directive Properties

In the above method, the data are hard-coded and could not be passed by other developers from other classes, or this directive might not be reusable in multiple places.

@Input() defaultColor:string = 'transparent';
@Input() hoverColor:string = 'green';

And can be used to set in the Directive class:

this.myBackgroundColor = hoverColor;

Now this can be accessed from outside:

<p appBetterHighlight [defaultColor]="'yellow'" [hoverColor]="'red'">better-directives works!</p>

Even if the there are multiple directives in the above line, it might be confusing as to how Angular knows that the directives are of the element or the AttributeDirective. The fact is that Angular automatically knows it by looking at the properties available.

There is also another method to use the Attribute Selector. If the AttributeSelector is also used as an alias in one of the Input’s, then the element where the attribute is used will need some change.

@Input('appBetterHighlight') defaultColor:string = 'transparent';

Then it needs to be used as: (Not a preferrable method)

<p [appBetterHighlight]="'red'" [defaultColor]="'yellow'">better-directives works!</p>

Also in the above cases, it might be noted that the attribute binding is being used by placing square-brackets around it. They can be removed as for using a shortcut, but then the single-quotation-marks around the values will also need to be removed.

<p [appBetterHighlight]="'red'" defaultColor="yellow">better-directives works!</p>

But before using this, one needs to be very sure that the keys don’t pertain to any of the actual attributes of the element. And it is a custom attribute for property binding.

91 – What happens behind the scenes on Structural Directives

When using a * in Structural Directives, Angular converts the given piece of template for its own use.

<div *ngIf="isOdd">
    <p>This is odd</p>
</div>

Gets converted to:

<ng-template>
    <div [ngIf]="isOdd">
        <p>This is odd</p>
    </div>
</ng-template>

92 – Building a Structural Directive

@Directive({
  selector: '[appUnless]'
})
export class UnlessDirective {
    @Input() set appUnless(value:boolean) {
        if (value) {
          this.vcRef.clear();
        }
        else {
          this.vcRef.createEmbeddedView(this.templateRef);
        }
    }
    constructor(private templateRef:TemplateRef, private vcRef:ViewContainerRef) { }
}

One needs to make sure tha the name of the Input matches the name of the Directive to be set, as internally Angular internally converts the StructuralDirective tag to a PropertyBinding tag.
Now this Structural Directive can be used as:

<p *appUnless="false">Show this conditionally</p>

93 – Understanding ngSwitch

<div [ngSwitch]="myValue">
    <p *ngSwitchCase="5">value is 5</p>
    <p *ngSwitchCase="10">value is 10</p>
    <p *ngSwitchDefault>value is Default</p>
</div>
where myValue is a variable.

SECTION 8


SECTION 9 – USING SERVICES AND DEPENDENCY INJECTION


95 – Module Introduction

To write reusable tasks that can be used across multiple components. This is called as Services in Angular.
Duplication of Code and Data persistence are two use-cases of Services in Angular.

96 – Why would you need Services

When app becomes large, it is very difficult to manage the code and replicate them at multiple places. Services come to rescue.

97 – Creating a Logging Service

A Service file is just a normal Typescript file and doesn’t need any declaration like that of @Component or @Directive.
One can import, initialize and access the functions in it.
mycomponent.component.ts

onAccountCreated(accountStatus:string) {
    const loggingService:LogginService = new LoggingService();
    logginService.logStatusChange(accountStatus);
}

While one can use the service the above way, it is not the right way of doing it in Angular.

98 – Injecting the Logging Service into Components

This can be done by instantiating it in the constructor

@Component({
    templateUrl: …
    …
    providers: [LoggingService]
})
export class AccountComponent {
    // This line in the constructor tells Angular that an instance of the LoggingService is required by this class.
    constructor(private loggingService:LoggingService) { }
    someFunction() {
        this.loggingService.printInfo("Some string");
    }
}

TODO: While it is explained in this lecture about why the providers is required to use the LoggingService, it is not quite clear. Search for more info.

99 – Creating a Data Service

While multiple services were injected to the required classes, changes were getting logged, but the given accounts array isn’t getting updated.

100 – Understanding the Hierarchical Injector

When something is injected to a component, it is initialized for that component and all its child components.
The highest possible level is AppModule. It will provide the same level throughout the app. With it, the same instance of the Service is available throughout the Application.
Then is the AppComponent. If injected to it, the same instance of the Service will be available for all the components, but not for the other Services.
If injected to any other component, it will only be available to the component as well its children.
If the same Service is provided in any Component and in any of its Parent, then the Service in the Child Component (available by the Parent component) will get overwritten with the one provided in the component.

Lecture 101 – How many Instances of Service it should be

So to make it work what we created in Lecture 98, the concept of Lecture 99 need to be applied.
As we already had provided the AccountsService in the AppComponent, and were again providing it in the ChildComponents, the instance was getting overwritten. So there were two different instances running in the AppComponent as well as the ChildComponent.
To make sure that things work, we need not provide the AccountsService in the ChildComponent again. But the constructor declaration is required to make the variable accessible.

Lecture 102 – Injecting Services into Services

One service can be used inside another by injecting it in the other.
The service which needs to be injected in any other service needs to be Provided in the AppModule.
Make the Service Injectable in which the other service is going to be called/initialized.
Then initialze it in the constructor.

@Injectable() // Import Injectable from '@angular/core'
export class AccountsService {
    constructor(private loggingService:LoggingService) { }
    ...
}

Lecture 103 – Using Services for Cross-Component Communication

AccountsService {
    ...
    statusUpdated = new EventEmitter();
    ...
}
AccountComponent {
    ...
    onSetTo(status) {
        this.accountsService.statusUpdated.emit(status);
    }
    ...
}
NewAccountComponent {
    ...
    someFunction() {
        this.accountsService.statusUpdated.subscribe(
            (status:string) => {
                alert("New status: " + status);
            });
    }
}

SECTION 10 – COURSE PROJECT – SERVICES AND DEPENDENCY INJECTION


Lecture 104 – Introduction

Lecture 105 – Setting Up the Services

Lecture 106 – Managing Recipes in a Recipe Service

Lecture 107 – Using a Service for Cross-Component Communication

Events are being caught in the hierarchial mode. See if they can be caught by anyone implementing the service.

Lecture 108 – Adding the Shopping List Service

Lecture 109 – Using Services for Push Notifications

Lecture 110 – Adding Ingredients to Recipes

Lecture 111 – Passing Ingredients from Recipes to the Shopping List (via a service)

Spread operator …
Normally if we have an array of items, TypeScript provides a way in which they can be added to another array without looping through them all.

array1.push(...array2);

This will push the elements of array2 into array1.


SECTION 11 – CHANGING PAGES WITH ROUTING


Lecture 112 – Module Introduction

Lecture 113 – Why do we need a Router

Lecture 114 – Understanding the Example Project

In the app, there are 3 sections:
1. Home
2. Servers
– View and Edit Servers
– A service is used to load and update Servers
3. Users
– View users.

Lecture 115 – Setting Up and Loading Routes

In the AppModule

import { Routes, RouterModel } from '@angular/router';
const appRoutes:Routes = [
    {path: '', component: HomeComponent},
    {path: 'users', component: UsersComponent},
    {path: 'servers', component: ServersComponent}
]

In the imports, add

RouterModule.forRoot(appRoutes)

Add directive to the place where you want to render the routed views.

<div class="row">
    <router-outlet></router-outlet>
</div>

Lecture 116 – Navigating with Router Links

And in <a> tags, pass the href relative paths.
Using href reloads the page while going into the different views. This will rsset all the data the application had in the previous state ( the app state).
To avoid this problem, there is another way in which routing can be made to work:

<a routerLink="/services">Services</a>

In this manner, the page won’t reload when the link is clicked.
There is also another way, by use of PropertyBinding:

<a [routerLink]="['/users']">Users</a>

This is helpful for managing multiple level of url paths that can be passed as an array.

Lecture 117 – Understanding Navigation Paths

Appending / before a path, makes it absolute
Not appending / , will make it relative
./ is same to not adding the / at all
../ works similar to that of folder paths and moves one route back

Lecture 118 – Styling Active Router Links

The currently selected router links can be made active by using routerLinkActive attribute and setting it to the active css class.

<li role="presentation"
    routerLinkActive="active"> // active is the bootstrap css class
    <a routerLink="/">Home</a>
</li>
<li role="presentation"
    routerLinkActive="active">
    <a routerLink="/servers">Servers</a>
</li>
<li role="presentation"
    routerLinkActive="active">
    <a routerLink="/users">Users</a>
</li>

The above method will work, but one think to notice is that when the Servers or Users tab is clicked, they get selected, but the Home tab also remains selected at those times.
This is because, when the routerLinkActive is set, it checks for the availability of the router in the whole url. So when /servers or /users is clicked, it also contains the / url. So the Home also gets the active css class.
To fix this issue, there is another attribute called [routerLinkActiveOptions] which has to be set via property-binding as it requires an object.

<li role="presentation"
    routerLinkActive="active"
    [routerLinkActiveOptions]="{ exact: true }"> // active is the bootstrap css class
    <a routerLink="/">Home</a>
</li>
<li role="presentation"
    routerLinkActive="active">
    <a routerLink="/servers">Servers</a>
</li>
<li role="presentation"
    routerLinkActive="active">
    <a routerLink="/users">Users</a>
</li>

This will make sure that the css class is only applied when the link exactly matches with the route.

Lecture 119 – Navigating Programmatically

Inject the router to the constructor of whatever class it is required in.

import { Router } from '@angular/router';
export class HomeComponent {
    constructor(private router:Router) { }

    loadServers() {
        this.router.navigate(['/servers']); // It takes an array of routes to construct the final route
    }
}

Lecture 120 – Using Relative Paths in Programmatic Navigation

If in the above loadServers(), one removes the absolute url denotion / to:

this.router.navigate(['servers']);

It still wouldn’t break the app and Angular would still load “www.app.com/servers”
This is because, while calling the navigate function, Angular doesn’t know where is the route is currently at.
To know that, one needs to pass on that information which can be found in the ActivatedRoute class. So one needs to inject it into the constructor whenever it is required, and then pass on the information to the navigate() function.
So the above class would become:

import { Router, ActivatedRoute } from '@angular/router';
export class ServersComponent {
    constructor(private router:Router, private activatedRoute:ActivatedRoute) { }

    loadServers() {
        this.router.navigate(['/servers'], { relativeTo: this.activatedRoute });
    }
}

Lecture 121 – Passing Parameters to Routes

Anything passed with a colon beofe it in a route will be considered as an argument.
/users/:id – Here anything passed after the /users/ wil be considered as an id.
Multiple parameters can also be passed.

{ path: 'users/:id/:name', component: UserComponent }

Lecture 122 – Fetching Route Parameters

To get access to the parameters, in the component rendering the route, inject the ActivatedRoute in the constructor (imported from @angular/router).
And in ngOnInit, fetch the parameter from the ActivatedRoute

this.activatedRoute.snapshot.paramas["user_id"]

If multiple parameters have been passed: /users/:user_id/:user_name, these can also be parsed by using

export class UserComponent {
    constructor(private activatedRoute:ActivatedRoute) { }
    ngOnInit() {
        this.user = {
            id: this.activatedRoute.snapshot.params['id'],
            name: this.activatedRoute.snapshot.params['name']
        };
    }
}

Lecture 123 – Fetching Route Parameters Reactively

In the previous example, if the same route is called from within the view, then the view will not update with the updated data.

<a [routerLink]="['/users', 10, 'Anna']">Load Anna</a>

This link when clicked will update the url, but the data in the view won’t update. This is because, we only fetched the activatedRoute.snapshot on ngOnInit() of the view, and if we just change the data internally, the view is not getting initialized again, and hence has no idea that the data changed.
This happens because when the params get updated, Angular searches for the view to render and if it is the same as the current view, it doesn’t reinitialize it.
So as because in the previous step we only updated the user data on ngOnInit(), the data didn’t get updated on url change.
To make sure that the view works fine, one needs to subscribe to the changes happening to the route.

export class UserComponent {
    constructor(private activatedRoute:ActivatedRoute) { }
    ngOnInit() {
        this.user = {
            id: this.activatedRoute.snapshot.params['id'],
            name: this.activatedRoute.snapshot.params['name']
        };
        this.activatedRoute.params.subscribe((params:Params) => {
            this.user.id = params['id'];
            this.user.name = params['name'];
        });
    }
}

In the above line of code, params is an observable.
Observables is not a feature of Angular, but is heavily used by Angular to look forward for asynchronous tasks.
Observable is something that might happen in the future, so we subscribe to it to know if it happens and whenever it happens, we want to execute some code.

Lecture 124 – An Important Note About Route Observables

When the view is removed from view, Angular removes the view, its associated children and the event listeners in there. So one doesn’t need to do that manually.
But in case when custom observables are being created, it is required to remove everything manually in the ngOnDestroy method.
To do that, store the subscription in a variable (of type Subscription whih needs to be imported from erxjs/Subscription) and unsubscrive from that in the ngOnDestory method of the class.

import { Subscription } from 'rxjs/Subscription';
export class UserComponent implements OnInit, OnDestroy {
    ngOnInit() {
        this.paramsSubscription = this.activatedRoute.params.subscribe(
            (params:Params) => {
                this.user.id = params["user_id"];
                this.user.name = params["user_name"];
            }
        );
    }

    ngOnDestory() {
        this.paramsSubscription.unsubscribe();
    }
}

Lecture 125 – Passing Query Parameters and Fragments

In a url like http://localhost/users/1/someone?mode=edit#load
The part that follows the ? in a url (mode=edit) is the query parameter. There can be multiple of them, separated by &
The part that follows the # is the Fragment. There can only be one fragment.

<a
    [routerLink]="['/servers', 5, 'edit]"
    [queryParams]="{allowEdit: 1, sure: 'yes' }"
    fragment="loading" //[fragment]="['loading']" - Can also be passed as this
    class="list-group-item">
    {{ server.name }}
</a>

queryParams is a bindable property of the routerLink directive.
In typescript can be used as

this.router.navigate(
    ['/servers', id, 'edit'],
    {
        queryParams: { allowEdit: 1 },
        fragment: 'loadingnow'
    }
);

Lecture 126 – Retrieving Query Parameters and Fragments

Similar to the routerLinks, the query parameters as well as the fragment can be accessed from the snapshot property, or changes can be registered by subscribing to changes of the specific properties.

    console.log(this.activatedRoute.snapshot.queryParams['allowEdit']);
    console.log(this.activatedRoute.snapshot.fragment);

    this.activatedRoute.queryParams.subscribe(
      (data:Params) => {
        console.log(data['allowEdit']);
      }
    );

    this.activatedRoute.fragment.subscribe(
      (data:Params) => {
        console.log(data);
      }
    );

Lecture 127 – Practicing and Some Common Gotchas

In this case where the user_id or the server_id is a string, it is sometime possible that one might try to directly ser it somehwere expecting a number.

getServerByID(id:number) { }

If one tries to:

var id = this.activatedRoute.snapshot.params['id']
getServerByID(id) // This would fail.

That’s because the id when fetched from the url is a string by default and trying to pass a string to a function expecting a number would throw an error.
To resolved this, the string can be typecasted to a number by adding a + before it.
So,

var id = this.activatedRoute.snapshot.params['id']
getServerByID(+id) // This would work

Lecture 128 – Setting up Child (Nested) Routes

const appRoutes:Routes = [
    {path: 'servers', component: ServersComponent},
    {path: 'servers/:id', component: ServerComponent},
    {path: 'servers/:id/edit', component: EditServerComponent}
];

To nest the routes, one can do

const appRoutes:Routes = [
    {path: 'servers', component: ServersComponent, children[
        {path: ':id', component: ServerComponent},
        {path: ':id/edit', component: EditServerComponent}
    ]},
];

While this would simiplify things a bit, but would throw a runtime error.
To resolve this, and outlet needs to be provided for the children. This can be done by adding:
<router-outlet></router-outlet>
to the parent view.
TODO – See what can be done if the same router view needs to be added inside a parent router and needs to be accessed directly.

Lecture 129 – Using Query Parameters – Practice

When moving around using the navigate method, the query parameters get lost.

Lecture 130 – Configuring the Handling of Query Parameters

A new attribute needs to be added to the second parameter of the navigate function.
this.router.navigate([‘edit’], { relativeTo: this.activatedRoute, queryParamsHandling: ‘preserve’ });
queryParamsHandling – takes a string
Can be set to ‘preserve’ or ‘merge’
‘preserve’ will drop the new ones and override with the old ones.
‘merge’ will merge the old query params with any new query params being added to the currently navigate method.

Lecture 131 – Redirecting and Wildcard Routes

When we enter a route in the url abr when it is not available, the Angular throws an error.
To do that create a page-not-found component, and add it as a route to something like ‘404’ or ‘not-found’.
And in the last add a ** route and redirectTo the 404 route.

const appRoutes:Routes = [
    ...,
    { path: '404', component: PageNotFoundComponent },
    { path: '**', redirectTo: '/404' }
];

Make sure to add the ** path as the last route in the Routes array.

Lecture 132 – Important: Redirection Path Matching

A little difficult to understand/implement

Lecture 133 – Outsourcing the Route Configuration

When the routes count increases, it is better to outsource it to another file.
To do that create a new file named app-routing.module.ts

const appRoutes:Routes = [
    { path: '', component: HomeComponent },
    { path: 'users', component: UsersComponent, children: [
          { path: ':id', component: UserComponent },
          { path: ':id/:name', component: UserComponent }
    ]},
    { path: 'servers', component: ServersComponent, children: [
          { path: ':id', component: ServerComponent },
          { path: ':id/edit', component: EditServerComponent }
    ]},
    { path: '404', component: PageNotFoundComponent },
    { path: '**', redirectTo: '/404' }
];
@NgModule({
    imports: [
        RouterModule.forRoot(appRoutes)
    ],
    exports: [
        RouterModule
    ]
})
export class AppRoutingModule { }

And this can be then added to AppModule imports []

Lecture 134 – An Introduction to Guards

When authentication is required, it would be cumbersome to replicate the check everywhere in the code.
So Angular provides a way to run some code before the route gets activated using canActivateGuard

Lecture 135 – Protecting Routes with canActivate

Create a file named auth-guard.service.ts
Make it Injectable, so that the AuthService can be injected into it.

@Injectable()
export class AuthGuard implements CanActivate {
    constructor(private authService:AuthService, private router:Router) {   }

    canActivate(activatedRouteSnapshot:ActivatedRouteSnapshot, routerStateSnapshot:RouterStateSnapshot):Observable | Promise | boolean {
        return this.authService.isAuthenticated().then(
            (authenticated:boolean) => {
                if (authenticated)
                    return true;
                else
                    this.router.navigate(['/']);
            }
        );
    }
}

Another file named auth.service.ts This fakes a real authentication module.

export class AuthService {
    loggedIn = false;

    isAuthenticated() {
        const promise = new Promise(
            (resolve, reject) => {
                setTimeout(() => { resolve(this.loggedIn); }, 800);
            }
        );        
        return promise;
    }

    logIn() { this.loggedIn = true; }

    logOut() { this.loggedIn = false; }
}

The canActivate function can return an Observable or a Promise or simply a ‘boolean’
So this function can work both asynchronously (Observable, Promise) or synchronously (boolean)
Add this 2 services to the providers list in AppModule.
Then, in the AppRoutes, provide as

{ path: 'servers', canActivate: [AuthGuard], component: ServersComponent, children: [
    { path: ':id', component: ServerComponent },
    { path: ':id/edit', component: EditServerComponent }
]},

The canActivate can have an array of Guards to pass through before proceeding onto the Route view.
In the above case, it will always redirect to home page on clicking Servers, because the loggedIn has been set to false.

Lecture 136 – Protecting Child (Nested) Routes with canActivateChild

The canActivateChild can be used to guard the children of a particular route.
For this the AuthGuard needs to implement the CanActivateChild interface. So the earlier AuthGuard class becomes:

@Injectable()
export class AuthGuard implements CanActivate, CanActivateChild {
    constructor(private authService:AuthService, private router:Router) {   }

    canActivate(activatedRouteSnapshot:ActivatedRouteSnapshot, routerStateSnapshot:RouterStateSnapshot):Observable | Promise | boolean {
        return this.authService.isAuthenticated().then(
            (authenticated:boolean) => {
                if (authenticated)
                    return true;
                else
                    this.router.navigate(['/']);
            }
        );
    }

    canActivateChild(activatedRouteSnapshot:ActivatedRouteSnapshot, routerStateSnapshot:RouterStateSnapshot):Observable | Promise | boolean {
        return this.canActivate(activatedRouteSnapshot, routerStateSnapshot);
    }
}

Now the AppRoutes can be updated as:

{ path: 'servers', canActivateChild: [AuthGuard], component: ServersComponent, children: [
    { path: ':id', component: ServerComponent },
    { path: ':id/edit', component: EditServerComponent }
]},

If the canActivate is not guarded and the canActivateChild is passed, then the /servers/ can be opened without authorization, but not the children.

Lecture 137 – Using a Fake Auth Service

Lecture 138 – Controlling Navigation with canDeactivate

As we can control if we can navigate into a router, we can also manage if someone can navigate away from a router.
This comes helpful if the user has done some changes and haven’t saved it yet.
In the edit-server folder add a new file named can-deactivate-guard.service.ts with the following code:

export interface ICanDeactivateComponent {
    canDeactivate:() => Observable | Promise | boolean;
}

CanDeactivate is a generic type and will wrap our own interface

export class CanDeactivateGuard implements CanDeactivate {
    canDeactivate(component:ICanDeactivateComponent,
        activatedRouteSnapshot:ActivatedRouteSnapshot,
        currentStateSnapshot:RouterStateSnapshot,
        nextStateSnapshot?:RouterStateSnapshot):Observable | Promise | boolean {

        return component.canDeactivate();
    }
}

Now the component needs to also implement the ICanComponentDeactivate interface, and have the canDeactivate() which will contain the logic to allow/disallow the user from redirecting to the new Route.

export class EditServerComponent implements OnInit, ICanDeactivateComponent {
    canDeactivate():Observable | Promise | boolean {
        if (this.allowEdit) {
          if ((this.serverName !== this.server.name || this.serverStatus !== this.server.status) && !this.changesSaved) {
            return confirm("Do you want to discard the changes?");
          }
        }
        return true;
    }
}

Add the CanDeactivateRoute to the route’s parameters

{ path: ':id/edit', component: EditServerComponent, canDeactivate: [CanDeactivateGuard] }

and also add it to the AppModule providers array.

Lecture 139 – Passing Static Data to Route

One can also pass data to the route via the data property in the route.
For example in an error page, one might need to some dynamic message that has been passed from the route.

{ path: '404', component: PageNotFoundComponent, data: { message: 'Page Not Found!'} },

And in the component, the data can be fetched by accessing it from the ActivatedRoute.

export class PageNotFoundComponent implements OnInit {
  // The errorMessage can be string-interpolated to the html view.
  errorMessage:string;

  constructor(private activatedRoute:ActivatedRoute) { }

  ngOnInit() {
    this.errorMessage = this.activatedRoute.snapshot.data['message'];
    // One can also subscribe to changes in the data.
    this.activatedRoute.data.subscribe((data:Data) => {
      this.errorMessage = data['message'];
    });
  }
}

Lecture 140 – Resolving Dynamic Data with the Resolve Guard

Sometimes it is required to wait for the data to load and then only update the screen based on the data.
server-resolver.service.ts

interface Server {
    id:number;
    name:string;
    status:string;
}
@Injectable()
export class ServerResolver implements Resolve {
    constructor(private serversService:ServersService) {}
    resolve(activatedRouteSnapshot:ActivatedRouteSnapshot, routerStateSnapshot:RouterStateSnapshot):Observable | Promise | Server {

        return this.serversService.getServer(+activatedRouteSnapshot.params['id']);
    }
}

Add this ServerResolver to the AppModule in the providers array.
In the AppRouting module, add the resolve parameter. Anything passed in the key can be accessed from the activatedRoute.data

{ path: ':id', component: ServerComponent, resolve: { server: ServerResolver} },

Now the server.component.ts can be updated for:

ngOnInit() {    
    this.activatedRoute.data.subscribe((data:Data) => {
        this.server = data['server'];
    });
}

Lecture 141 – Understanding Location Strategies

The urls are always handled by the server first. So mostly when we hit a url in a real server
www.xyz.com/servers, the server will look for the route in the server side and would try to return a 404 page.
To bypass this, one needs to make sure that the 404 redirects to the index.html file such that Angular can take care of it from then on.
But there are also some browsers where this wouldn’t work. In those cases, there is a second approach where a # would be appended after the root url.
RouterModule.forRoot(appRoutes, { useHash: true })
This would return something like
www.xyz.com/#/servers
Using this, the browser would igniore anything after the #, and the rest can be handled by Angular.
But this is not a pretty way and one should try to avoid this manner.

Lecture 142 – Wrap Up


SECTION 12 – COURSE PROJECT – ROUTING


Lecture 144 – Planning the General Structure

Lecture 145 – Setting Up Routes

Lecture 146 – Adding Navigation to the App

Lecture 147 – Marking Active Routes

Using routerLinkActive in the li tag in a nav can be used to keep the link css active with the css class passed into it.

<ul>
     <li class="active"><a>Recipes</a></li>
</ul>

to

<ul>
     <li><a>Recipes</a></li>
     <li><a>Shopping List</a></li>
</ul>

Lecture 148 – Fixing Page Reload Issues

Note: Should use routerLink instead of href to make sure that the page dopesn’t reload and the redirection is handled by Angular

Lecture 149 – Child Routes – Challenge

Lecture 150 – Adding Child Roiuting Topgether

Lecture 151 – Configuring Route Parameters

Lecture 152 – Passing Dynamnic Parameters to Links

Lecture 153 – Styling Active Recipe Items

Use the routerLinkActive

Lecture 154 – Adding Editing Routes

In the case of how the routes should follow each other, a declaration of

{ path: ':id', component: RecipeDetailComponent },
{ path: 'new', component: RecipeEditComponent },

would create problems as if you pass recipes/new in the url, it would try to parse the new route as an id and would throw an error, because it wouldn’t be able to find an item with new index.
So the new url route should come before the :id
It should be made sure that angular understands to take the url as a hardcoded route or a dynamic variable

Lecture 155 – Retrieving Route Parameters

Lecture 156 – Programmatic Navigation to the Edit Page

Lecture 157 – One Note ABout Route Observables

One should clear the Observables onDestroy, specfically the ones created by self

Lecture 158 – Project Cleanup


SECTION 13 – UNDERSTANDING OBSERVABLES


Lecture 159 – Module Introduction

Lecture 160 – Angular 6 and RxJS 6

If one is using Angular 6 and RxJS 6, and wnat to have their older codes work, need to use rxjs-compat
npm install rxjs-compat –save

Lecture 161 – Analyzing a build-in Angular Observable

A subscriber takes in 3 parameters as follows:

this.route.params.subscribe(
    () => {}, // On Result
    () => {}, // On Error
    () => {} // On Complete
);

Lecture 162 – Building and Using a First Simple Observable

// Create this app

import { Observable } from 'rxjs/Observable';
import 'rxjs/Rx'; // This provides some utility functions for use.

ngOnInit() {
    const myNumbers = Observable.interval(1000);
    myNumbers.subscribe(
        (number: number) => {
            console.log(number);
    });
}

Lecture 163 – Building and using a Custom Observable from Scratch

const myObservable = Observable.create((observer: Observer) => {
    setTimeout(() => {
        observer.next('first package');
    }, 2000);
    setTimeout(() => {
        observer.next('second package');
    }, 4000);
    setTimeout(() => {
        observer.error('some error occured');
    }, 5000);
    setTimeout(() => {
        observer.complete();
    }, 6000);
    setTimeout(() => {
        // This won't execute as the complete has been called earlier.
        observer.next('third package');
    }, 7000);
});

Lecture 164 – Unsubscribe

For the custom Observables created in the previous lectures, if one navigates out of the page holding the Observables, one would notice that the Observables are still active and continue to emit.
To make sure that there is no memory leaks, one should definitely unsubscribe from them. To do that, save the observables to a subscription and unsubscribe.

this.myCustomSubscription = myBumbers.subscribe(() => {
    ...
});

ngOnDestory() {
    this.myCustomSubscription.unsubscribe();
}

While Angular clears up its own subscriptions, its better to clean them manually.

Lecture 165 – Where to Learn More

reactivex.io/rxjs

Lecture 166 – Using Subjects to Pass and Listen to Data

Subject is extended from Observable and it implements both the Observer and the Subscriber. This makes it easy to use.
Note: Angular EventEmitter is based upon Subject, but it is preferable to use the Subject instead of EventEmitter to perform cross-component communication.

import { Subject } from 'rxjs/Subject';
export class UsersService {
    userActivated = new Subject();
}

UserComponent.html

<button>Activate</button>

UserComponent.ts

onActivate() {
    // Acts as Observer
    this.usersService.userActivated.next(this.user.id);
}

AppComponent.ts

ngOnInit() {
    // Acts as Observable
    this.usersService.userActivated.subscribe((id: number) => {
        // Do something
    });
}

Lecture 167 – Understanding Observable Operators

The operators allow to transform the data we receive to something else and still remain in the Observable world.

const myNumbers =
        Observable
            .interval(1000)
            .map((data: number) => {
                return data * 2;
            });

In this case map is being used. map operator maps the data you pack into a new Observable with any transaformation we want in the function.
As because every operator returns an Observable, so they can be chained as much as required with as many operators as possible.

Lecture 168 – RxJS 6 without rxjs-compat

To make it work with the recent RxJS 6, one needs to get the Observable, Observer, direcytly from ‘rxjs’ module

import { Observer, Observable, Subject, Subscription, interval ... } from 'rxjs';

Also need to import the operators manually as requried.

import { map, ... } from 'rxjs/operators';

Also the interval needs to be imported form the rxjs module and should be used directly.
Also the operators can’t be sued directly any more. They need to pass through a pipe now.

const myNumbers = 
        Observable
            .interval(1000)
            .map(...);

gets modified to

const myNumbers = 
        interval(1000)
            .pipe(
                map(...)
            );

TODO – Need to know how to pass multiple operators. Do they need to go through a single pipe or multiple pipes.
Ans – They pass through a single pipe with comma separated calls.

Lecture 169 – Wrap Up


SECTION 14 – COURSE PROJECT – OBSERVABLES


Lecture 170 – Improving the Reactive Services with Observables (Subjects)

– IMPLEMENT THIS IN C9


SECTION 15 – HANDLING FORMS IN ANGULAR APPS


Lecture 171 – Module Introduction

Lecture 172 – Why do we need Angular’s Help

A typical structure how Angular handles forms

{
    value: {
        name: 'Murari',
        email: 'someone@gmail.com'
    },
    valid: true
}

Lecture 173 – Tempalte Driven (TD) vs Reactive Approach

Template Drived – Angular infers the Form Object from the DOM.
Reactive Approach – Form is created programmatically as well as in the HTML, giving more granular control over the form.

Lecture 174 – An Example Form – Can get the code from here

Lecture 175 – TD: Creating the Form and Reguistering the Controls

Import the FormsModule in the ApPComopnent imports
This will make sure that Angular is able to parse any form tag that is available in the HTML.
Considering an example form:

<form>
    <div>
        <label for="username">Username: </label>
        <input id="username" type="text" />
    </div>
    <div>
        <label for="email">Email: </label>
        <input id="email" type="text" />
    </div>
    <button type="submit">SUBMIT</button>
</form>
...

This will create a javascript form representation in Angular. But the form representation would be blank even if there are elements inside the form tag. This is because, at that point of time Angular hasn’e been informed about which controls are to be included in the form. Even if Angular would be able to parse the form and know about the controls, but not necessarily all the controls in the form need to be submitted.
So to make Angular know about which controls it should consider as to be part of the form, we need to specify them. This can be done as

...
<input id="username" name="myname" type="text" />
// ngModel -> This tells Angular to include this as part of the form. Tnis is like 2-way binding but a little advanced. Will talk about later.
// name="username" -> This is the name by which Angular specifically recognises the control. name attribute is a part of the HTMLElement and is nothing special about Angular
...

Sidenote: To know the structyural details of the form, it needs to be submitted

Lecture 176 – TD: Submitting and Using the Form

Create an onSubmit function in ts file to be called when the user needs to submit the form.

onSubmit() {

}

While the button onclick migjht look a good place to add the onSubmit listener to, but probably isn;t the right palce. This is because as this is an HTML Form, the default behaviour of the submit type button in a form will get triggered, which will try to submit the form to the service as well as a javsacript submit event.
Angular takes advantage of this event and we can use it to call the onSubmit. This is done by binding to the ngSubmit event on the Angular form.

...
<form>...</form>...
To get access to the form in the TS, one needs to create a local reference in the HTML file.
...

<form>...
onSubmit(form: HTMLElementRef) {
    console.log(form);
}
...

This would just print out the HTML representation of the form, which is not the way we would want the values.
To get the javacript notation of the form, Angular comes to help.
While create the form in Angular, it gives us a directive to access the form the way Angular sees it. To do that, assing the ngForm to the local form reference.

...<form></form>...
onSubmit(form: NgForm) {
    console.log(form);
}
...

This will give the full jhavascript representation of the form.
One can find all the formcontrols and their values in form.value</form>

Lecture 177 – TD: Understanding Form State

A lot of attributes can be found in the ngForm isntance and can be used to perform a lot of tasks.
The state is also maintained at the individual controls which can be found in form.controls
Example
control.touched specifies if the control was touched by the user
control.dirty speifies if anything was changed

Lecture 178 – Accessing the Form with @ViewChild

ViewChild can also be used to get access to the form from TS

...
<form>...
@ViewChild('f') myForm: NgForm;
onSubmit() {
    console.log(this.myForm);
}
...

This method is useful to get access to the form not only when the button is cliekd, but at any point of time.
Also helpful in validations.</form>

Lecture 179 – TD: Adding Validation to Check User Input

The form can be modified as:

...
<input id="email" name="email" required="" type="text" />
...

required and email are directives that is going to be used by Angular to manage validations.
If one enters something in the email input that is not of tupe email, then the form.valid and form.controls.email.valid would be false
The invalid would also force ng-touched ng-vinvalid … and a couple of classes to the form-input class and can be used for styling of the controls.

Lecture 180 – Build-in Validators and using HTML5 Validation

Validators can be found ar https://angular.io/docs/ts/latest/api/forms/index/Validators-class.html and can also be used in Reactivbe forms
Directives for Template Driven forms can be found at https://angular.io/api?type=directive
Angular disables native HTML5 validation and can be enabled by adding ngNativeValidate to a control in the template.

Lecture 181 – TD: Using the Form State

The form states like ng-touched, ng-valid, etc can be used to perform many tasks on the form.
Most importantly, it can be used to disable the Submit button.
To do that just add the disabled property on the button based on the form’s validity

<button disabled="disabled" type="submit">SUBMIT</button>

The classes that get added to the control atuomatically can be used to style them.

input.ng-invalid.ng-touched {
    border: 1px solid red;
}

Lecture 182 – TD: Outputting Validation Error Messages

To show the user validation errors as warning texts, one can do the following:

<input id="email" name="email" required="" type="text" />

Here also we can pass in the ngModel to the instance of the input and can use it to show/hide other elements in the form.
So we use the input’s valid state to determine the visibility of our validation message.

<span class="help-block">Invalid Email</span>

Lecture 183 – TD: Set Default Values with ngModel Property Binding

To pass in default values, one can pass the default value to the ngModel directive in the form cotnrol, and pass as one-way attribute binding.

...
defaultEmail: string = 'someone@email.com';
...
<input id="email" name="email" required="" type="text" />
...

Lecture 184 – TD: Using ngModel with 2-Way Binding

2-Way binding can also be done in the case of NgForm controls.

...
<input id="email" name="email" required="" type="text" />
...

This will make sure that the value of defaultEmail is updated on every keystroke in the input field.

Lecture 185 – TD: Grouping Form Controls

One can also have groups of form-controls with each having their validations. This is easy to implement by adding the ngModelGroup directive to the group holding the form-controls.

...
<div>
    <input id="username" type="text" />
    <input id="email" name="email" required="" type="text" />
</div>
<input id="firstName" type="text" />
...

Now the form will have a structure of something like:

form: {
    ...
    value: {
        firstName: 'sometext',
        userDataGroup: {
            username: 'someothedata',
            email: 'some@one.co'
        }
    }
    ...
}

The form.controls will also update to something like the above structure.

Lecture 186 – TD: Handling Radio Buttons

...
genders = ['male, 'female']
...
<div class="radio">
    <label>
        <input name="gender" type="radio" value="" />
        {{ gender }}
    </label>
</div>

Lecture 187 – TD: Setting and Patching Form Values

There are 2 methods to update the form values from the TS file.
One method is to use the ViewChild way to get access to the form element and use the setValue function of the NgForm

@ViewChild() signupForm: NgForm;
...
this.signupForm.setValue({
    userData: {
        firstName: 'Name 1',
        secondName: ''
    }
    email: ''
});
...

The problem with this method is that one has to pass the values for all the controls in the form.
The better way is to use the patchValue method available on the form. In the case of patchValue(), it will only update the controls for whom the information is passed in the form’s javascript object.

this.signupForm.form.patchValue({
    userData: {
        firstName: 'Name 1'
    }
});

The above line will only update the firstName control of the form and every other control will remain as they were earlier.

Lecture 188 – TD: Using Form Data

onSubmit() {
    this.user.name = this.signupForm.value.userData.firstName + this.signupForm.value.userData.lastName;
    this.user.email = this.signupForm.value.email;
    ...
}

Lecture 189 – TD: Resetting Forms

this.signupForm.reset()

Assignment 6 – Practising Template Drived Forms – COMPLETE IT

Lecture 190 – Introduction to the Reactive Approach

Programmatically create the form in TS code.

Lecture 191 – Reactive: Setup

Need to import ReactiveFormsModule in the app.module imports rather than the FormsModule
Need to declare a var signupForm: FormGroup in the TS file where the FormModule need to be created.

Lecture 192 – Reactive: Creating a Form in Code

ngOnInit() {
    this.signupForm = new FormGroup({
        'username': new FormControl(null),
        'email': new FormControl(null),
        'gender': new FormControl('male')
    });
}

Lecture 193 – Reactive: Syncing HTML and Form

To synchronize the HTML form with the FormGroup created in TS, we need to pass the formGroup to the HTML Form

<form>…
This will inform Angular not to use its own FormGroup but use the FormGroup that we created in the Ts file.
Now a similar pattern is to be used for binding the individual controls. To do this, use the formControlName attribute.

<input id="email" type="text" />

Lecture 194 – Reactive: Submitting the Form

To submit we can use the local signupForm and no need to pass the form from the HTML. Simply fire a function on ngSubmit function

</form><form>...
onSubmit() {
    console.log(this.signupForm);
}

Lecture 195 – Reactive: Adding Validation

this.signupForm = new FormGroup({
    'username': new FormControl(null, Validators.required), // single validator
    'email': new FormControl(null, [Validators.required, Validators.email]), // multiple validators in array
    'gender': new FormControl('male')
});

Lecture 196 – Reactive: Getting Access to Controls

To get access to the element, we can use the get method on the Form.

signupForm.get(elementName)

For the overall form, use signupForm.valid

Lecture 197 – Reactive: Grouping Controls

For creating groups of controls, the TS code can be modified to:

this.signupForm = new FormGroup({
    'userData': new FormGroup({
        'username': new FormControl(null, Validators.required), // single validator
        'email': new FormControl(null, [Validators.required, Validators.email]) // multiple validators in array
    }),
    'gender': new FormControl('male')
});
This should also be reflected in the HTML code, else it would break.
...

<form>
    <div>
        <input name="username" type="text" />
        <input name="email" type="text" />
    </div>
    <input name="gender" type="text" />
</form>

Now to access the controls, one needs to add formGroup path to access,

signupForm.get('userData.username')

Lecture 198 – Reactive: Arrays of Form Controls (Form Array)

...
<form>...
    <input name="gender" type="text" />
    <div>
        <h4>Your Hobbies</h4>
        <button type="button">Add Hobby</button>
        <div>
            <input type="text" /> // need to do attribute binding as the passed parameter is a variable</div>
        </div>
    </div>
</form><form>
...
this.signupForm = new FormGroup({
    'userData': new FormGroup({
        'username': new FormControl(null, Validators.required), // single validator
        'email': new FormControl(null, [Validators.required, Validators.email]) // multiple validators in array
    }),
    'gender': new FormControl('male'),
    'hobbies': new FormArray([]) // FormControls can be added here in the array also.
});

onAddHobby() {
    const control = new FormControl(null, Validators.required);
    (this.signupFrom.get('hobbies')).push(control);
}
...

Lecture 199 – Reactive: Creating Custom Validators

forbiddenUserNames = ['Chris', 'Anna'];

forbiddenNames(control: FormControl): { [key: string}: boolean } {
    if (this.forbiddenUserNames.indexOf(formControl.value) !== -1) {
        return { 'nameIsForbidden': true };
    }
    return null; // This is for valid entry
}

this.signupForm = new FormGroup({
    'userData': new FormGroup({
        'username': new FormControl(null, [Validators.required, this.forbiddenNames] )
    ...
    })
});

This would not work as the keyword this in the forbiddenNames function isn’t a part of the class that is calling it. So to make sure that it works, one needs to make sure that the class referenCe is passed into the function while calling it. To do this, use:

...
'username': new FormControl(null, [Validators.required, this.forbiddenNames.bind(this)] )
...

Lecture 200 – Reactive: Using Error Codes

When the validations fails in Angular, these are stored in an errors object in the particular FormControl. It can be fetched using

signupForm.get('userData.username').errors['nameIsForbidden']

Using these we cab show custom error messages.
This name is invalid
This field is required

Lecture 201 – Reactive: Creating a Custom Async validator

Sometimes validations need to be done on the server side and the client-side needs to wait for the validation to compelte before determining if the form is valid or not.

forbiddenEmails(control: FormControl): Promise | Observable {
    const promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        // Forcing a server behavior with time.
        if (control.value === 'test@test.com') {
            resolve({'emailIsForbidden': true});
        } else {
            resolve(null);
        }
        }, 2000);
    });
    return promise;
}

Now this function has to be used in the asyncValidators array in the Form Control

this.signupForm = new FormGroup({
    'userData': new FormGroup({
        'username': new FormControl(null, [Validators.required, this.forbiddenNames] )
        'email': new FormControl(null, [Validators.required, Validators.email], this.forbiddenEmails) // the third param is the asyncValidators array
    })
    ...
});

One can also notice ng-pending in the class list of the FormControl when the service is reaching out to the web for validation and then switches to ng-valid/ng-invalid based on result.

Lecture 202 – Reactive: Reacting to Status or Value Changes

There are two Subsriptions available on the form and its controls tjhat can help track the changes to it.

ngOnInit() {
    ...
    this.signupForm.valueChanges.subscribe((control) => console.log(value));
    this.signupForm.statusChanges.subscribe((status) => console.log(status));
    // This can also be run on the individual controls:
    this.signupForm.get('control-name').valueChanges.subscribe((control) => console.log(value));
    ...
}

Lecture 203 – Reactive: Setting and Patching Values

// To set the complete form

this.signupForm.setValue({
    'userData': {
        'username': 'soeone',
        'email: 'some@some.com'
    }
    'gender': 'male'
});
// To set some spescific part of the form:
this.signupForm.patchValue({
    'userData': {
        'username': 'mur'
    }
});
// To reset the form
this.signupForm.reset()

ASSIGNMENT 7 – PRACTICING REACTIVE FORMS – TODO COMPLETE


SECTION 16 – COURSE PROJECT – FORMS


Lecture 204 – Introduction

Lecture 205 – TD: Adding the Shopping List Form

Lecture 206 – Adding Validation to the Form

pattern="^[1-9]+[0-9]*$"

// This will make sure that no negative numbers can be entered to the field

Lecture 207 – Allowing the Selection of Items in the List

Lecture 208 – Loading the Shopping List Items to the Form

Lecture 209 – Udpating Exiting Items

Lecture 210 – Restting the Form

Lecture 211 – Allowing the User to Clear (Cancel) the Form

Lecture 212 – Allowing the Deletion of Shopping List Items

Lecture 213 – Creating the Template for the Recipe Edit Form (Reactive)

Lecture 214 – Creating the Form for Editing Recipes

Lecture 215 – Syncing HTML with the Form

Lecture 216 – Adding Ingredients Controls to a FormArray

Lecture 217 – Adding New Ingredients Control

Lecture 218 – Validating User Input

Lecture 219 – Submitting the Recipe Edit Form

// If the recipeForm and the model have the same variable names, then the recipeForm.value can be directly passed to the service
// this.recipeService.addRecpe(this.recipeForm);

Lecture 220 – Adding a Delete and Clear (cancel) Functionality

Lecture 221 – Redirecting the User (After Deleting the Recipe)

Lecture 222 – Adding an Image Preview

Lecture 223 – Providing the Recipe Service correctly

Providing the service into a particular compoenent will make it available as a singleton to that comopnent and its child components, buta s soon as we move away from the component and it is destroyed, the instanc eiof the service also gets destroyed. Thus the next instance created will be a fresh instance. The better way is to provide the service at the module level. In this current case, the aAppModule suits the best.

Lecture 224 – Deleting Ingredients and some Finishing Touches


SECTION 17 – USING PIPES TO TRANSFORM OUTPUT


Lecture 225 – Introduction and Why Pipes are Useful

Pipes are used to transform the output.
For a case where one has a name as a string ‘Max’ and wants to output all in uppercase without making it uppercase throughotut the application.
It would be better to modify the value just on display. For that one can use a builtin pipe ‘uppercase’
Using it is simple

{{ username | uppercase }}

This would transform the username in whatever case to uppercase just for displaying and without affecting the original value
As the job of a pipe is to just visually transformt he output, the best place for them to be used is the HTML code.

Lecture 226 – Using Pipes

Pipe to display dates ‘date’

Lecture 227 – Parameterizing Pipes

There are also ways to pass parameters to pipes.
This can be done using a colon after the pipe name and pass the parameter afetr that.

date:'fullDate'

One should also make sure that the pipe is able to handle the parameter. In the case of the date pipe, it expects a string parameter.
Additional parameters can also be passed using multiple colons.

pipename:param1:param2...

Lecture 228 – Where to Learn More About Pipes

All the build-in pipes can be fopuind the PAI documentation in the official Angular.io website.

Lecture 229 – Chaining Multiple Pipes

Multiple pipes can be chained together by using the pipes one after another seprated by the evrtical bar

{{ server.date | date:'fullDate' | uppercase }}

One thing to keep in mind is the sequence of placing the pipes as the execution happens from eft to right.
The first pipe will be applied to the expression and then the second pipe would be applied to the result of the applciation of the first pipe and continued.
This behavior can be confirmed by applying the uppercase pipe to the date objhect directly which will throw an error becasue it wouldn’t be able to apply the uppercase pipe over ad ate as the uppercase can only be apllied on a string.

Lecture 230 – Creating a Custom Pipe

import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
    name: 'shorten' // this is name that is to be used to use the pipe
})
export class ShortenPipe implements PipeTransform {
    transform(value: string) {
        if (value.length > 10) {
            return value.substr(0, 10) + '...';
        }
        return value;
    }
}

Before using this pipe, it needs to be added to the declatations array in the AppModule.

Lecture 231 – Parameterizing a Custom Pipe

transform(value: string, limit: number, anotherArg: any) {
...
}

This can be used as

{{ server.name | shorten:15:'..' }}

Lecture 232 – Example: Creating a Filter Pipe

ng generate pipe filter
This would generate filter.pipe.ts
To add a filter string, add an input area to the template

<input type="text" />

Add the filteredStatus variable to the TS file

Pipe({
    name: 'filter'
})
export class FilterPipe implements PipeTransform {
    transform(value: any, filterString: string, propName: string) {
        if (value.length === 0 || filterString === '') {
            return value;
        }
        let resultArray = [];
        for (const item of value) {
            if (item[propName) === filterString) {
                resultArray.push(item);
            }
        }
        return resultArray;
    }
}

Can be used as

<ul>
     <li>...</li>
</ul>

Lecture 233 – Pure and Impure Pipes (or How to Fix the Filter Pipe)

If we use the filter pipe on a list of items and then there are additions/deletions on the list, then the filter doesn’t update the list. This is because this would cause the pipe to rerun on the data for every change in data. This would cause performance issues. If this is a requiredment of utmost importance, this behaviour can be achieved by using the pure tag in the Pipe declaration

Pipe({
    name: 'filter',
    pure: false
})
export class FilterPipe implements PipeTransform {
    ...
}

Lecture 234 – Understanding the async Pipe

In some cases, we need to wait for the data to return before we could output it.
For a typical case like a Promise,

appStatus = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('live');
        ), 2000);
    });

In this case, if we directly output the appStatus in string interpolation, the output string will show as [object Object]. This is right as because the Promise is an Object type, but when the Promise returns the string, the string interpolation has no way to know that the data type has changed. To make sure that the string interpolation knows that something has changed, the ‘async’ pipe can be used.

{{ appStatus | async }}

This way when the data gets updated after the promise resolves, the paragraph shows the output.
The async can be used with Promise and Observables.

ASSIGNMENT 8 – PRACTICING PIPES


SECTION 18 – MAKING HTTP REQUESTS


Lecture 235 – MUST READ: Angular 6 and HTTP

Angular 6 is currently the latest version of Angular and it deprecates the Http-access method taught in this module.
What does this mean?
It means that the method still works, still is secure – you can use it! But there is a better Http module to use now: HttpClient.
I added a module (section 23) on that new client months ago, even before Angular 5 was released. You’ll meet it later in the course and we’ll easily update all our Http calls with the new client there.
So for now, follow along with this module here – the core concepts taught here will still apply (i.e. how it works etc).
And later in the course, we’ll revisit this solution and update it to HttpClient.

Lecture 236 – Introduction and How HTTP Requests work in SPA

In traditional apps, the front end used to request some data, and the server gave the response and it was loaded in a page.
But in SPA, the data shopuld come back to the apge itself to make sure that the page doesntt get reloaded.

Lecture 237 – Example App and Backend Setup

User Firbase for the backend.
One needs tos etup the read/write of the database to true to bea able to work on the database.

Lecture 238 – Sending Requests (Example: POST Request)

this.http.post(…) doesn’t actually make the request but returns an Observable.
To fire the request one needs to subscribe to the Observable returned by http.post(…)
To make a request to the firebase, paste the url and append something.json where soemthing stands for the table name, and json is the extension required for the firebase service to work.
www.firebase.url/something.json

Lecture 239 – Adjusting Request Headers

To send headers, one can

const myHeaders = new Headers({
    'Content-Type': 'application/json'
});
this.http.post('service_url', dataToPost, { headers: myHeaders });

Lecture 240 – Sending GET Requests

getServers() {
    return this.http.get('service_url');
}

And use it as,

myService.getServers().subscribe(
    (response) => console.log(response),
    (error) => console.log(error)
);

If one inspects the data that is returned in the body of the response, it is of type JSON
The response object that is returned from the Subscriber is of type Response which can be imported form @angular/http
And it provides a way to parse the JSON directly to an object. This can be done by:

(response: Response) => {
    console.log(response.json());
}

Lecture 241 – Sending a PUT Request

this.http.put('service_url', dataToPost, { headers: myHeaders });

PUT request will overwrite the existing data and has the process similar to that of the POST request.

Lecture 242 – RxJS 6 Without rxjs-compat

If using RxJS 6, one can’t use pipe directly on the response, but need to use the pipe and then map over it.
So instead of ..map(..), one needs to use …pipe(map(…))
And also import ‘rxjs/Rx’ changes to import { map } from ‘rxjs/operators’;

Lecture 243 – Transform Responses Easily with Observable Operators (map())

The map operator will simply tak ethe old Observable and wrap the data that returns back, transform the data and wrap that transformed data into a new Observable.
With this the earlier way
service.ts

getServers() {
    this.http.get('api_url');
}

component.ts
myService.getServers().subscribe(
    (response: Response) => {
        var data = response.json();
        console.log(data);
    }
);

Will get converted to:
service.ts

getServers() {
    this.http.get('api_url')
                    .map(
                        (response: Response) => {
                            var data = response.json();
                            return data;
                        })
    };
}

component.ts

myService.getServers().subscribe(
    (servers: any[]) => {
        console.log(data);
    }
);

Modifying the response at the service level is required to be able to send the request from multiple places and not need to parse the result at every calling place.

Lecture 244 – Using the Returned Data

this.http.get('api_url')
            .map(
                (response: Response) => {
                    var data = response.json();
                    for (const server of data) {
                        server.name = 'FETCHED_' + server.name;
                    }
                    return data;
                })
        };

This just shows what we can do with the map operator.
todo: When the user added a new server and did a PUT request to firebase, it was able to retain the previous servers and add a new one.
It should be working because he overwrote the previous ones. Need to check.

Lecture 245 – Catching Errors without rxjs-compat

For Angular 6 / RxJS 6

Instead of
import 'rxjs/Rx';
import { Observable } from 'rxjs/Observable';
...catch(error => {
    return Observable.throw(...)
})
one needs to use
import { catchError } from 'rxjs/operators';
import { throwError } from 'rxjs';
...pipe(catchError(error => {
    return throwError(...)
}))

Lecture 246 – Catching HTTP Errors

If some error happens while doing some requests, one can catch them using the catch operator

this.http.get('wrong_api_url')
        .map(...)
        .catch(
            (error: Response) => {
                return Observable.throw(error);
            }
        )
    };

Using Observable.throw(…) will wrap the error in an Observable and be send to the class observing it.
Similar to the map, the error can also be transformed before sending it as an Observable

...catch(
    (error: Response) => {
        return Observable.throw('Something went wrong');
    }
)

Lecture 247 – Using the async pipe with HTTP Requests

service.ts

getAppName() {
    return this.http.get('_url_to_get_app_name')
        .map((response: Response) => {
            return reponse.json();
        });
}

component.ts

appName = service.getAppName();

This would return an Observable, and to be able to use this directly the HTML component, async pipe would turn very handy
component.html

<h2>{{ appName | async }}</h2>

SECTION 19 – COURSE PROJECT – HTTP


Lecture 248 – Introduction

Would be connecting the Save Data and Fetch Data buttons to the service.

Lecture 249 – Setting up Firebase as a Dummy Backend

Created the firebase setup for the project

Lecture 250 – Sending PUT Requests to save Data

Generated my own firebase setup to save the data using the PUT method.

Lecture 251 – GETting Back the Recipes

Similarly GET method was also implemented for the fetchRecipes functionality.

Lecture 252 – Transforming Response Data to Prevent Errors

If the reponse array has any attribute missing, we can use the map to put that back in so that it doesn’t generate any error

.map(
    (response: Response) => {
        const recipes: Recipe[] = response.json();
        for (recipe of recipes) {
            if (!recipe['ingredients']) {
                recipe['ingredients'] = [];
            }
        }
        return recipes;
    }
)

SECTION 20 – AUTHENTICATION & ROUTE PROTECTION IN ANGULAR APPS


Lecture 253 – Module Introduction

While most of the authentication logic is setup in the backend,w e only need to know how to use the authentication system in the frontend.
As we have already used firebase and it has a pretty easy setup for authentication system in the backend, we are going to use that.

Lecture 254 – How Authentication Works in Single Page Applications

In a traditional web app,normally all the view generation is done in the server and the frontend receives the fully built html and just displays it.
But in the case of SPA, thes erver only sends one html file, and Angular is responsible for dynamically changing the content.
In traditional web apps, when a user logs in, the credentials are sent tot he server, who in turn validates the data, and if valid, creates a session. Then it returns the session cookie to the client who for every request sends back the session cokie to the sever for validation for any further requests.
In the case of SPA’s, when the user sends the validation credentials, the server sends back a hashed token that has been generated with soem keys. So for every request from then on, the frontend will send back the token with the request so that the server can validate the information.
Traditional – Server creates a session and remebers the client as the frontend and backend are tightly coupled.
SPA – Server doesn’t create any session and uses the token to recognise the user. Frontend and backend are loosely coupled.

Lecture 255 – More About JWT

Want to learn about the Token which is exchanged?
The following page should be helpful: https://jwt.io/ – specifically, the introduction: https://jwt.io/introduction/

Lecture 256 – Creating a Signup Page and Route

Lecture 257 – Setting up the Firebase SDK

Lecture 258 – Signing Users Up

npm install firebase –save
Firebase need to be initialized at app startup. So add the configuration in the app.component

firebase.initializeApp({
    apiKey: '....',
    authDomain: '...'
});

There are others whuich can be found in the firebase console, btu these two are enough to get started.
For signup, can be used for email/password combo as

firebase.auth().createUserWithEmailAndPassword(email, password)
    .catch(error => console.log(error));

Lecture 259 – Signin Users In

To signin,

firebase.auth().signInWithEmailAndPassword(email, password)
        .then(response => console.log(response))
        .catch(error => console.log(error));

The response contains a lot of details about the authorization and also contains the userid and the access tokens and it is stored in the localStorage by firebase also.

Lecture 260 – Requiring a Token (on the Backend)

In firebase to limit the authenticated users to read/write to the database can be managed by updating the rules for the database.

{
    "rules": {
        ".read": "auth != null",
        ".write": "auth != null"
    }
}

Now the requests to firebase will require the token to do any operations.

Lecture 261 – Sending the Token

To send the token, in the AuthService set the token that is fetched by

firebase.auth().currentUser.getToken() // this will return a promise
.then(reponse => {
    this.token = reponse;
});

Now this token needs to be sent in every request by appending the token at the end of the request url

this.http.get/put('...some_url.../items.json?token=' + this.token);

Lecture 262 – Checking and Using Authentication Status

To check the authentication status, one can check for the token to make sure that the authentication information is available for performing tasks. In the AuthService

isAuthenticated() {
    return this.token != null;
}

Now this can be used in the HTML to show/hide items.

Lecture 263 – Adding a Logout Button

In the AuthServicem add

logout() {
    this.auth().signOut();
    this.token = null;
}

Lecture 264 – Route Protection and Redirection Example

On the signIn function in AuthService, we can use the router to redirect the user to the page we want to.
Create an AuthGuard

export class AuthGuard implements CanActivate {
    constructor(authService: AuthService) { }

    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
        return this.authService.isAuthenticated();
    }
}

And now this AuthGuard can be used in the routing module.

{ path: 'recipes/new', component, 'RecipeEditComponent', canActivate: [AuthGuard] }

Lecture 265 – Wrap Up

Lecture 266 – Possible Improvements

One can of course improve this app even more. Some ideas:
Check if a token is present at application startup (check the localStorage manually or use the Firebase SDK to do so – just make sure that you somehow wait for the SDK to finish its initialization)
Redirect the user if he want to access a protected route (right now, nothing happens) – inject the router and call this.router.navigate(…) to do so
Redirect the user on logout so that he’s not able to stay on pages which are reserved for authenticated users – you can simply inject the router and call this.router.navigate(…) in the logout() method


SECTION 21 – USING ANGULAR MODULES AND OPTIMIZING APPS


Lecture 269 – Understanding the App Module

Imports are a language feature that tells the compiler to where to look for the files and features that are required.
Modules are a part of Angular which it sees as a way of binding multiple things together.
Modules are a bunch of codes bundled together and imported as a single statement in the imports array, thus minizing the number of codes that are written in the AppModule.
Everything provided in providers array in the AppModule gives a single instance on which the whole app works.
Bootstrap array defines which is the root element of the application.

Lecture 270 – Understanding Feature Modules

If a particular part/feature of the ap can be fully outsourced to another library, t can be craeted as a feature module.
In the current aplication we are building, the recipes and its related features can make up a feature module.

Lecture 271 – Creating a Recipe Feature Module

CommonModule needs to be imported in almost all feature modules because it gived access to the Angular build-in modules like the ngClass.
The AppModule by default doesn’t have the CommonModule but in place has the BrowserModule. This is because the BrowserModule contains the CommonModule as well as some addtional modules that are required to run the application in the browser.

Lecture 272 – Registering Routes in a Feature Module

To register routes in the FeatureModule, we need to register our routes in the RouterModule. Thisc an be done by using the RouterModule.forChild function.

imports: [
    RouterModule.forChild(featureModuleRoutes)
]
exports: [ RouterModule ]

While registering Routes in the AppModule , one needs to use the RouterModule.forRoot functionality, but in any other module , one needs to use the RouterModule.forChild property
One thing to notice is that the Dropdown Directive won’t be working in the FeatureModule even if it is provided in the AppModule, because Angular doesn’t know if it has to use that in the FeatureModule.
We can’t import the Directive in both the AppModule and the FeatureModule because Angular will complain about it being imported in multiple modules.

TODO ISSUE – ** in the AppRoutingModule creates problem when browsing routes defined in the FeatureModule.

Lecture 273 – Understanding Shared Modules

When any module needs to be used across multiple modules, it should be put into a SharedModule.

Lecture 274 – Creating a Shared Module

While there can be multiple shared modules in an application, typically there is only one.
We use the declarations and the imports in the SharedModule and export the ones which we need to share around the application.

@NgModule({
    declarations: [
        DropdownDirective
    ],
    imports: [
        ... // Not required mostly
    ],
    exports: [
        CommonModule,
        DropdownDirective
    ]
})
export class MySharedModule { }

Now importing this module into other modules will make sure that the common componets/directives are available throughout the applciation without having to import them multiple times.

Lecture 275 – Creating as Shopping List Feature Module

In the case of Shopping List Module, we didn’t move the route to its own module as because there was just one route that needed to be moved and it doesn’t make sense to create another file for it. Details will be given in the next module.

Lecture 276 – Loading Component via Selectors vs Routing

When we use selectors for vieweing a component, we need to delcare that component in the AppModule, but in the case of routing (using router-outlet), one doesn’t need to declare the component in the same module, but just needs to be initialized before getting used.

Lecture 277 – A Common Gotcha

Normally if we remove a module that is being used in the AppModule components, we can get errors. While we can export the required module from another modulke being used in the application, it isn’t the right way of doing it.

Lecture 278 – Creating the Auth Feature Module

Lecture 279 – Understanding Lazy Loading

In a normal ANgular application, all the code gets downlaoded at the beginning. SO it swould also download many FeatureModules which will never be visited by the user.
Lazy loading in ANgular makes sure that the Feature Modules and the associated routes are only loaded when the user actually visits any of those pages.

Lecture 280 – Adding Lazy Loading to the Recipes Module

To make the RecipesModule lazy loaded, we will first need to get a default HomePage as the current setup redirects the user to the recipes page by default.
First remove the module from the imports array of the AppModule, ebcause anything provided there will be loaded at runtiume.
Then in the AppRoutingModule, add a path for the recipes,

{ path: 'recipes', loadChildren: './recipes/recipes.module#RecipesModule' }

Normally in the routes, we provide the component declaration for the path, but for lazy loading, we need to pass the loadChildren attribute, along with the path to the module as a string. Also attach the Module name at the end preceded by a #.
Now to make sure that this works, we need to remove the ‘recipes’ route from the RecipesRoutingModule, and make it an empty path.

Lecture 281 – Protecting Lazy Loaded Routes with canLoad

What if you want to use route protection (canActivate to be precise) on lazily loaded routes?
You can add canActivate to the lazy loaded routes but that of course means, that you might load code which in the end can’t get accessed anyways. It would be better to check that BEFORE loading the code.
You can enforce this behavior by adding the canLoad guard to the route which points to the lazily loaded module:

{ path: 'recipes', loadChildren: './recipes/recipes.module#RecipesModule', canLoad: [AuthGuard] }

In this example, the AuthGuard should implement the CanLoad interface.

Lecture 282 – How Modules and Services Work Together

– REVISIT
When we provide a service to both the AppModule and a FeatureModule (not lazily loaded), there is only one instance of the service created as becaseu Angular creates only One Root Injector during the start of the aplpication, and so only one instance of the service will be created.
So if you access the service anywehere including the laziliy loaded module where the service hasn’t been provided, each one will get a single isntance.

In case where the service is also provided in the lazily loaded module, another instance of the servbice will be created and forced into the Child Injector created by Angular. So one will get a different intance oft he service in the lazily loaded module than that of the AppModule or any other intancly loaded modules.
One can also create different instance of asevice at a acompn\oment level by providing the sevice at the component declaration rather than the Module declaration.

In a case where a SharedModule is created and the service is injected into the SharedModule. Then if the SharedModule is imported to non-lazily loaded module as well as a lazily loaded module, one would assume that bnoth the mpdules will sahre the same service. BUt in relaity, the non-lazily module will get the instance from the Root Injector in the AppModule, whereas for the laily loaded module, a new Child Injector would be created. This is because providing the service in a SharedMModule and providing the module to the lazily loaded module is same to that of providing thse service directly to the module.
So this is one of the major reasons, why a servbice should never be provided in a SharedModule. It would be quite difficult ot figure out what one is going to get with this setup.
Note: DOn’t priovide a service in a Shared Module. Specially when you plan to use them in Lazy Loaded Modules.

Lecture 283 – Understanding the Core Modules

There are some parts of the application that are required through the lifetime of the aplication, like a header.
Components like this form the core of the applciation and should be moved to a Core Module to make it easy for managing the AppModuole.

Lecture 284 – Creating a Basic Core Module

The header and the home components and any other components/directives that are part of the AppCOmponent can be moved to the Core Module.
Created a core folder and move all the required stuff in there and create the CoreModule and export the required elements out of the COre Module.
NOrmally you should only have the AppCOmponent ion the AppModule declarations array.

Lecture 285 – Restructing Services to use the Child Injector

All the services in the providers array of the AppModule can also be moved to the CoreModule as because it is anyway going to be bootstrapped to the Root Injector of the app.
In the currenl application, the AuthGurad can be specifally moved to the recipes-routing.module becaseue it si the only place where it is being used.
This would make the code more leaner and also make it a little fastera s all the Auth related items get moved to the lazlily loaded module.
AuthGurad is the only service mofule wchih needs to go in the Routing Module. ALl other serbices module shoudl go directly into the feature module. It’s not required but is a better structure to use it.

Lecture 286 – Using Ahead-of-Time Compilation

There are 2 types of compilation procedures angular follows. Just-in-Time compilation and Ahead-of-Time compilation.
Compilation is a process by which Angular converts our template files to javacript keeping the dynamicity itact. This is made to sure that the rendering is fast as it is earier to manipulate javacript and render it, that manipulating the HTML DOM directly.
In the case of Just-in-Time compilation, when the application code is downloaded in the browser, Angular compiles the code there to javacript, thus making the rendering a little slow.
In the case of AOT comipaltion, Angular compiles the code when we build it for production. This saves us the time that Angular takes when compiling the code at runtime in the browser. This makes the application startp faster.
We also save applciation size as the compiler code is not shipped with our code in the produced build. Also Angular strips out unused code from the build when we run the AOT compilation. FOex example, if we don’;t use the ngIF statement anywhere, Angular will remove the total functioanlity of ngIf from the code.
One more advantage is that all the templates get checked during development and one can fix the error before it appears in the production environment.

Lecture 287 – How to use AOT Compilation with the CLI

ng build --prod --aot

–prod minifies the code but doesn’t use AOT (most probably it has started doing so, need to check)
–aot compiles the code in the AOT mode,making the vendor.js and main.js files very small.

Lecture 288 – Preloading Lazy Loaded Routes

The implementation of Lazy Laoading also introduces a mninor problem that when we visit the to be Lazy Loaded module, it takes some time to download the module and the app may hand for that moment of time. To ake sure that this doesn’t happen, nbut the Lazy Loaded modules do get downloaded at a later point of time of downloading the main bundle, we can use the pre loading lazy loaded modules strategy. To do this,
app-routing.module.ts

imports: [
    RouterModule.forRoot(appRoutes, {preloadingStrategy: PreloadAllModules})
]

PreloadAllModules need to be imported from @angular/router and is the strategy to make sure thatthe lazy loaded modules get preloaded before their time.

Lecture 289 – Wrap Up


SECTION 22 – DEPLOYING AN ANGULAR APP


Lecture 290 – Introduction

Will be deploying on AWS

Lecture 291 – Deployment Preparations and Important Steps

Steps:
a) Build the app for production. Better to consider AIT or whatevr method we follow.
ng build –prod –aot
b) Set the correct element
If Angular is being served form any folder that is not the root of the server, then the base href needs to be set to that folder.
For an Angular app that is hosted on www.xyz.com/myapp, then we should set in the index.html file
or add it in the build script like

ng build --prod --aot --base-href=/myapp/

c) Need to make sure that the server always return index.html on 404 error.
Whenever we hit an url in the browser, the server is the first to parse the url. So if we are hitting a router directly, then the server won’t be able to find it as that is registerd in the Angular product. So the server would redirect to a 404. So the server setting should be such that the 404 gets redirected to index.htmnl which holds the information about all the routes. This would make sure that the redirection information reaches Angular and it redirects the user to the particular route inside that of the Angular App.

Lecture 292 – Example: Deploying to AWS S3

Deployment steps given for this.


SECTION 23 – BONUS: THE HttpClient


Lecture 293 – Module Introduction

In Anular 4.2 a new HttpCLient was released and it’s just an alternative to the original Http.
It is built up of with the modern web api’s and has better and advanced features, interceptors for example.

Lecture 294 – The Documentation

angular.io > Docs > Fundamentals > HttpClient

Lecture 295 – Unlocking

To start using the HttpClient, we would need to remove the HttpModule if implemented and use the HttpClientModule instead. This can be imported from @angular/common/http
Now eherever we are using the Http, we can uise the HttpClient instead. The pway the parameters ahndled are the same.

this.http.put('some_url', data) gets updated to
this.httpClient.put('some_url', data)

and

this.http.get('some_url') gets updated to
this.httpClient.get('some_url')

One thing to notice about the get method in HttpClient is that now we can be explicit about what type of data we are expecting from the service.
this.httpClient.get<Recipe[]>(‘some_url’)
So if we are using a map to parse the result, that can be overwrittyen by

this.httpClient.get<Recipe[]>('some_url')
    .map(recipes => {
        for (let recipe of recipes) {
            if (!recipe['ingredients']) {
                recipe['ingredients'] = [];
            }
        }
    });

Lecture 296 – Request Configutation and Response

The request/response can be configured by providing the options parameter in the GET/PUT request. IT takes an object.

this.httpClient.get(
    'some_url',
    {
        observe: 'response', // default is body
        responeType: 'text' // can be type json/text/blob. default is json
    }
)

In the response we will get the data of the value we set in the observe parameter in the format we sopecify in the responseType parameter.

Lecture 297 – Requesting Events

We can pass the events string in the observe parameter of the options obect in a reqquest. This would return all the events that get fiored while processing a request.

this.httpClient.put(
    'some_url',
    someData,
    {
        observe: 'events' // other values are body/response
    }
)

The first event that will be fired would have a type of 0, which stands for the HttpEventType.Sent

Lecture 298 – Setting Headers

To pass custom headers we will need to pass the headers attribute in the request’s option parameter

this.httpClient.get(
    'some_url',
    {
        headers: 
                new HttpHeaders()
                .append('Authorization', 'bearer asfhaskghkasghaksdg234235sadgsd')
                .append(....)
    }
)

Lecture 299 – Http Parameters

Sometimes query params need not be directly attached to the url string. We can pass it to the options parameter the same way we pass the headers.

this.httpClient.get(
    'some_url',
    {
        params: new HttpParams()
            .append('auth', firebase_token)
            .append(....)
    }
)

Lecture 300 – Progress

HttpEventType 1 – Upload
HttpEventType 3 – Download
While we can use the HttpClient directly to fire requests, we can also create a fullynconfigured request manually.

const req = new HttpRequest(
    'PUT',
    'some_url',
    dataToSave,
    {
        reportProgress: true, // This will give the progress of the request thus making it earier to track file uiploads/downloads.
        params: new HttpParams().set('auth': firebase_auth_token)
    });
return this.httpClient.request(req);

Lecture 301 – Interceptors

Read more here.

Typically when we want to do soemthing with every request, it can be passe on to be done by an interceptor. In the current application case, the firebase auth token in the params.
auth.interceptor.ts

import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';

export class AuthInterceptor implements HttpInterceptor {
    intercept(req: HttpRequest, next: HttpHandler): Observable<HttpEvent> {
        console.log('Intercepted ', req);
        return next.handle(req);
    }
}

// HttpHandler gives a special method to let the request continue its journer\y, and if not done, the request won’t proceed.
Now to make it work, we need to tell Angular to use this interceptor
core.module.ts

providers: [
    ...
    { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true } // multi tells Angular that we are going to use multiple interceptors.
    { provide: HTTP_INTERCEPTORS, useClass: AnotherInterceptor, multi: true }
]

Lecture 302 – Modifying Request in Interceptors

The HttpRequest is an inmutable bject. So we should not be changing the request directly.
What we can do is create a clone of that request by using

const clonedReq = req.clone()

But the principle of inmutability stands on the cloned request too. So one can’t direcvtly modify the attributes of the cloned request.
But the clone function gives the power to modify attributes of the request.

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
    constructor(private authService: AuthService) { }

    intercept(req: HttpRequest, next: HttpHandler): Observable<HttpEvent> {
        const reqClone = req.clone({
            params: req.params.set('auth', this.authServiuce.getToken())
                .append(...)
        });
        return next.handle(reqClone);
    }
}

Lecture 303 – Modifying Response in Interceptors

The same process can be followed for the response, but we need to atatcht he do operator to the request to check for the responses.

export class LoggingInterceptor implements HttpInterceptor {
    constructor(private authService: AuthService) { }

    intercept(req: HttpRequest, next: HttpHandler): Observable<HttpEvent> {
        return next.handle(req)
            .do(
                event => console.log('Loggin ', req);
            );
    }

For RxJS 6,one needs to do:
// tap needs to be imported form rxjs/operators

return next.handle(req)
    .pipe(tap(...))

Now this inteceptor needs to be provided in the Core Service
When there are multiple interceptors, they are run in the manner they havbe been privided in the providers array.

Lecture 304 – Wrap Up

Lecture 305 – Useful Resources & Links

Link to official HttpClient Documentation: https://angular.io/guide/http


SECTION 24 – BONUS: WORKING WITH NgRx IN OUR PROJECT


Lecture 306 – Module Introduction

To manage states of the application and be able to get to an elaborate approach to store and retrieve data, wemight nee to use ngrx. This is not default available in the angular liv\brary, but can be installed and it provuides a lot of funcitoanalities. This store approach follows the Redux approach of storing data.

Lecture 307 – Important: Angular 6, RxJS 6 and this section!

Using Angular 6 and therefore RxJS 6+?
I recommend that you go through this module with rxjs-compat installed.
There’ll be a video at the end of the module where we together update everything to work with RxJS 6 without rxjs-compat. But to prevent unnecessary issues with the import path adjustments and pipe(), it’s easier to simply use rxjs-compat by running npm install –save rxjs-compat in the project folder.

—————————-

[OPTIONAL] Still want to go without that package?
You find detailed update instructions (Angular 5 => Angular 6) on this page: https://www.academind.com/learn/angular/snippets/angular-6-whats-new-angular-upgrade
You’ll have to adjust a couple of imports, operator names and use pipe() in this case. Refer to the “Understanding Observables” section of this course (section 13) to learn how you may update your code to use RxJS 6 WITHOUT rxjs-compat .
Essentially, your imports have to change.
For example,
import { Observable } from ‘rxjs/Observable’;
becomes
import { Observable } from ‘rxjs’;
Additionally, you use operators differently.
import ‘rxjs/add/operator/map’;
import ‘rxjs/add/operator/switchMap’;
myObservable.map(…).subscribe(…)
becomes
import { map, switchMap } from ‘rxjs/operators’;
myObservable.pipe(map(…), switchMap(…)).subscribe(…);
Last but not least, you’ll encounter operators in this module, for which the name changed:
do() => tap()
catch() => catchError()
An observable-creation method (throw ) was also renamed:
import { Observable } from ‘rxjs/Observable’;
import ‘rxjs/add/observable/throw’;

myObservable.pipe(catchError(error => Observable.throw(error));
becomes
import { throwError } from ‘rxjs’;

myObservable.pipe(catchError(throwError(error));

Lecture 308 – State Challenges

In an app, everything that the user does are storedas data, but as soon as the app refreshes, all the data is lost. THis is known as the state of the application.
The database is the only palce with the consistent state.
When the application has multiple moving modules and same data is requried at multiple palces, the state needs to ebmanaged so that all the places display the smae synchroznied data. In the current application we are using RecipeServbice and a Subject inside of it to inform the lsiteners about the change in the data. While this is good for smaller applications, it can getdifficult ot manage in larger applications.
For this case we need to follow the Redux approach.
In the redux approach, there is one central store (application state). The components/Services connect to the store as well as to each other.
To change the state in the store, we need to dispatch Actions (SaveItem, GetItem). Then these Actions would reach a reducer. A Reducer is a self writtenf unction whcich would take an Action and a Payload(data) and manipulate thr orifginal state in an inmutable way. By that, it wouldn’t modify the original state, but they overwrite it with a new javascript object by taking the old object and modifying the required information.
The NgRx also supports Lazy Loading making it possible to only load some data to the store when some module is loaded.

Lecture 309 – Getting Started With Reducers

npm install --save @ngrx/store

The main module of ngrx
The format for store implementation:
Create a task.actions.ts file
Add all the tasks as export class which implements the Action interface from @ngrx/store
Each class should have a type and payload where type is just a string determining what action is going to be peformed and payload is the data that is going to be passed to the constructor.
tasks.actions.ts

export const GET_TASKS = 'GET_TASKS';
export const ADD_TASK = 'ADD_TASK';
export const UPDATE_TASK = 'UPDATE_TASK';

export class GetTasks implements Action {
    type = GET_TASKS;
    constructor(public payload: Task[]) {}
}
export class UpdateTask implemetns Action {
    type = UPDATE_TASK;
    constructor(public payload: {index: number, updatedTask: Task}) {}
}

export type RecipeActions = GetTasks } UpdateTask | ...

Then we need to create the reducer which will process the actions that have been initiated

import * as RecipeActions from './recipe.actions';

export interface FeatureState {
    recipes: Recipe[]:
}

export function recipeReducer(state = initialState, action: RecipeActions.RecipeActions) {
    switch(action.type) {
        case RecipeActions.SET_RECIPES:
            return {
                ...state,
                recipes: [...action.payload]
            };

        case RecipeActions.ADD_RECIPE:
            return {
                ...state,
                recipes: [...state.recipes, action.payload]
            };

        case RecipeActions.UPDATE_RECIPE:
            const recipe = state.recipes[action.payload.index];
            const updatedRecipe = {
                ...recipe,
                ...action.payload.updatedRecipe
            };          
            const recipes = [...state.recipes];
            recipes[action.payload.index] = updatedRecipe;
            return {
                ...state,
                recipes: recipes;
            };

            case RecipeActions.DELETE_RECIPE:
                const oldRecipes = [...state.recipes];
                oldRecipes.splice(action.payload, 1);
                return {
                    ...state,
                    recipes: oldRecipes
                };

            default:
                return state;
    }
}

SECTION 25 – BONUS: ANGULAR UNIVERSAL


Lecture 354 – Module Introduction

The page source just has a app-root tag and some imports. This is bad for the search engines, because they see that only while the users will see everything due to being rendering in Angular.
To make sure that the server also keeps the same source as that of the client, one needs to use the Angular Universal.

Lecture 357 – Adding a Server-Side Build Workflow

Lecture 358 – Adding a NodeJS Server

Lecture 359 – Prerending the APp on the Server

Lecture 360 – Next Steps

Lecture 361 – Angular Universal Gotchas

You typically use Angular Universal to pre-render Angular pages on the server. Once the app then runs on the client, it’s a normal SPA again.
Server-side rendering (SSR) can make sense because of SEO considerations (crawler should see what your users see) or because you want to deliver a finished page to your users (rather than creating the page in the browser).
But that also has one important implication: You MUST NOT use any browser-only APIs like document.querySelector() in your Angular code!
Simply because it will execute on the server and there, such APIs are not available.
That’s why, in this course, I recommended to use Angular features only: These features are safe to use because Angular will check if a certain API can be used before it uses it.


SECTION 26 – ANGULAR ANIMATIONS


Lecture 362 – Making Animations Work with Angular 4+

With the release of Angular 4, the general syntax of Angular Animations didn’t change.
However, the animation functions were moved into their own package and you now also need to add a special module to your imports[] array in the AppModule.
Specifically, the following adjustments are required:
You probably need to install the new animations package (running the command never hurts): npm install –save @angular/animations
Add the BrowserAnimationsModule to your imports[] array in AppModule
This Module needs to be imported from @angular/platform-browser/animations’ => import { BrowserAnimationsModule } from ‘@angular/platform-browser/animations’ (in the AppModule!)
You then import trigger , state , style etc from @angular/animations instead of @angular/core
That’s all!

Lecture 363 – Introduction

Lecture 364 – Setting up the Starting Project

Lecture 365 – Animations Triggers and State

Lecture 366 – Switching Between States

Lecture 367 – Tramsitions

@Component({
    ...
    animations: [
        trigger('divState', [
            state('normal', style({
                'background-color': 'red',
                transform: 'translateX(0)'
            })),
            state('highlighted', style({
                'background-color': 'blue',
                transform: 'translateX(100px)'
            })),
            transition('normal => highlighted', animate(300)),
            transition('highlighted => normal ', animate(600))
        ]
    ]
})

Lecture 368 – Advanced Transitions

transition('normal <=> highlighted', animate(300)),
...
state('shrunken', style: ({
    'background-color': 'green',
    transform: 'translateX(0) scale(0.5)'
})),
transition('shrunken <=> *', animate(500)) // shrunken to-fro any state will apply this transition.

Lecture 369 – Transition Phases

transition('shrunken <=> *', [
    style({
        'background-color': 'orange'
    }), // The start phase
    animate(1000, style({
            borderRadius: '50px'
        })
    ), // The second phase
    animate(500) // The end phase
])

Lecture 370 – The “void” state

trigger('list1', [
    state('in', style({
            opacity: 1,
            transform: 'translateX(0)'
        })
    ),
    transition('void => *', [
            style({
                opacity: 0,
                transform: 'translateX(-100px)'
            }),
            animate(300)
        ]
    ),
transition('* => void', [
        animate(300, style({
                transform: 'translateX(100px)',
                opacity: 0
            })
        )
])

Lecture 371 – Using Keyframes for Animations


SECTION 28: A Basic Introduction to Unit Testing in Angular Apps


Lecture 378 – About this Section

This Section only provides an Introduction to Unit Testing an Angular 2+ App.
It’s NOT a detailed Guide about that neither is it a detailed Introduction to Unit Testing in general. Check out the “Further Resources” (last Lecture in this Module) to dive deeper into these topics.

Lecture 379 – Introduction

Nothing

Lecture 380 – Why Unit Tests?

Does the Component/Service/Pipe work as intended?
Does the Input work as intended or the Injection works as intended?

Unit tests allows to Guard Against Breaking Changes
Alalyze code behaviour (expected and unexpected)
Reveal design mistakes.

Lecture 381 – Analyzing the Testing Setup (as created by the CLI)

describe('App: MyApp', () => {
    beforeEach(() => {
        TestBed.configureTestingModule({
            declarations: [
                AppComponent
            ]
        });
    });

    it('should create the app', async(() => {
        let fixture = TestBed.createComponent(AppComponent);
        let app = fixture.debugElement.componentIntance;
        expect(app).toBeTruthy();
    });

    it(`should have the title 'app works!'`, async(() => {
        let fixture = TestBed.createComponent(AppComponent);
        let app = fixture.debugElement.componentIntance;
        expect(app.title).toEqual('app works!');
    });

    it('should render title in a h1 tag', async(() => {
        let fixture = TestBed.createComponent(AppComponent);
        fixture.detectChanegs(); // This makes sure that this is updated after the veiw rendering is done to get the elements // Need further read on this topic.
        let app = fixture.debugElement.nativeElement;
        expect(compiled.querySelector('h1').textContent).toContain('app works!');
    });
);

The testing file starts with a describe function which takes a string as the app name and then a function specifying the test cases.
beforeEach function is run before any test cases is run.
Every statement starting with an /it/ is a test case.
Each test case is run independently of each other.

Lecture 382 – Running Tests (with the CLI)

ng test

This command will run the tests. By default it should run using karma.

Lecture 383 – Adding a Component and some Fitting Tests

When using a non-webpack based setup where the tets need to be run, then the beforeEach function must be accompanied with the compileCOmponets function so that the tests can be run.

beforeEach(() => {
    ...
}).compileComponets();

Lecture 384 – Testing Dependencies: Components and Services

To use a service that has been injected to the component:

it('should getch the user from the service', () => {
    let fixture = TestBed.createComponent(UserComponent);
    let app = fixture.debugElement.componentIntance;
    let userService = fixture.debugElement.injector.get(UserService);
    fixture.detectChanges(); // The change detection cycle needs to run to udpate the properties.
    expect(userService.user.name).toEqual(app.user.name);
}};

it('should diusplay the username if user is logged in', ()=> {
    let fixture = TestBed.createComponent(UserComponent);
    let app = fixture.debugElement.componentIntance;
    app.isLoggedIn = true;
    fixture.detectChanges();
    let compiled = fixture.debugElement.nativeElement;
    expect(compiled.querySelector('p').textContent).toContain(app.user.name);
}};

it('shouldn\'t display the username if user is not logged in', ()=> {
    let fixture = TestBed.createComponent(UserComponent);
    let app = fixture.debugElement.componentIntance;
    fixture.detectChanges();
    let compiled = fixture.debugElement.nativeElement;
    expect(compiled.querySelector('p').textContent).not.toContain(app.user.name);
}};

Lecture 385 – Simulating async Tasks

One might not want to connect to the services while running the tests. So normally it is better to run fake service calls that will run return data su\imilar to the services, in the test files.

it('Should not fetch data if called asynchronously', () => {
    let fixture = TestBed.createComponent(UserComponent);
    let app = fixture.debugElement.componentIntance;
    let dataService = fixture.debugElement.injector.get(DataService);
    let spy = spyOn(dataService, 'getDetails').and.returnValue('Promise.resolve('data')); // This spies on the DataService class and waits for the getDetails function to be executed. Once it is executed,. we return our value using the returnValue function.
    fixture.detectChanges();
    expect(app.data).toBe(undefined); // This is because the async data won't be retunred in the synchronous mode.
});

To make this useful for asynchronous processes, alittle variation is required:

it('Should fetch data if called asynchronously', async(() => {
    let fixture = TestBed.createComponent(UserComponent);
    let app = fixture.debugElement.componentIntance;
    let dataService = fixture.debugElement.injector.get(DataService);
    let spy = spyOn(dataService, 'getDetails').and.returnValue('Promise.resolve('data')); 
    fixture.detectChanges();
    fixture.whenStable().then(() => {
        expect(app.data).toBe('Data');
    ));
}));

Even if the tests will be run in a synchronous environment, the async() will be abnle to fake an asynchronous operation
whenStable() is executed when all the asynchronous tasks have been completed.

Lecture 386 – Using “fakeAsync” and “tick”

As we are already not performing async tasks, we can directlye execute functions by using the fakeAsync method instead of the async method. To make this work, we need to run the tick() method which wil finish allthe async tasks.

it('Should fetch data if called asynchronously', fakeAsync(() => {
    let fixture = TestBed.createComponent(UserComponent);
    let app = fixture.debugElement.componentIntance;
    let dataService = fixture.debugElement.injector.get(DataService);
    let spy = spyOn(dataService, 'getDetails').and.returnValue('Promise.resolve('data')); 
    fixture.detectChanges();
    tick();
    expect(app.data).toBe('Data');
}));

Lecture 387 – Isolated vs Non-Isolated Tests

Some tests can be run without invoiving Angular in it. Things where there is no dependancy on other compoenets/services and just need some inoput to give some output can be tested independently and hence known as isolated tests.
For example a Pipe can be run in an isolated manner.
reverse.pipe.ts

@Pipe({
    name: 'reverse'
})
export class ReversePipe {
    transform(input: string) {
        return input.split('').reverse.join('');
    }
}
reverse.pipe.spec.ts
describe('Pipe: Reverse', () => {
    it('should reverse the string', () => {
        let reversePipe = new ReversePipe();
        expect(reversePipe.transform('hello')).toEqual('olleh');
    });
});

When a test requires components to be instantiated, servbices to be injected, these require the help of the Angular testing tools like the TestBed, async, ets. So these can’t be run in an isolated mannera nd hence known as non-isolated tests.

Lecture 388 – Further Resources & Where to Go Next

This Module only provides a brief and basic Introduction to Angular 2 Unit Tests and the Angular 2 Testing Suite. This Course isn’t focused on Testing.
If you want to dive deeper, the official Docs actually are a great place to start. There you’ll also find a Non-CLI Setup!
Official Docs: https://angular.io/docs/ts/latest/guide/testing.html
I can also recommend the following Article: https://semaphoreci.com/community/tutorials/testing-components-in-angular-2-with-jasmine
For more Information on how to run Tests with the CLI have a look at their official Docs:
=> Unit Tests: https://github.com/angular/angular-cli/wiki/test
=> E2E Tests: https://github.com/angular/angular-cli/wiki/e2e


SECTION 29 – COURSE ROUNDUP


Lecture 389 – Course Roundup

Practice what you learnt.
Also can share what you built.


Section: 30 – Angular 6 Changes & New Features


Lecture 390 – What changed with Angular 6?

No breaking changes were released in Angulkar 6, except RxJS.
RxJS version 6 was released which changes how the library was built to make sure that the new library is smaller.
RxJS is used for Observables which is used a lot by Angular in the back.
The change in the library requireds the projects to udpate the import statements as wella s how they are used.
There is also a library rxjs-compat to be able to have backwards copatibility. RxJS 6 has backward compatibnility built in. to unlockit we just need to isntall the rxjs-compat module.
import { Observable } from ‘rxjs/Observable’;
import ‘rxjs/add/observable/of’;
import ‘rxjs/add/observable/throw’;
are now
import { Observable, of, throwError } from ‘rxjs’;

import ‘rxjs/add/observable/map’;
import ‘rxjs/add/observable/mergeMap’;
These imports have now been udpated to
import { map, catchError } from ‘rxjs/opreators’;
Some operators were renamed because they matched with reserved keywords of javascript, sow ould have created problems. As these operators are now beinga ccessed as functions, so the keyword name class error happens.
Now to use the operators, one needs to use the pipe method provided by rxjs, which takes an infinite number of operators to be applied to the data.

this,.http.get('some_url')
        .map(...)
        .error(...);
Now gets udpated to:
this.http.get('some_url')
        .pipe(
            map(...),
            catchError(...)
            ...
        );
Observable.throw(...) and Observable.of(...) gets updated to:
throwError(...) and of(...)

Deprecations and Removals
1. removed and need to use
2. @angular/http deprecated and can use @angular/common/http
3. Rather than providing a service in the provicders array of the app.module.ts, one can also make chanegs to the service so that it can be priovded to the AppModule.

@Injectable({
    providedIn: 'root'
})
export class MyService { ... }

Even if we use Injectable only when some service is going to be injected into the current service, we can now use it to provide the service itseld to the AppModuole.
4. Angular Elements can be used as normal Web Elements. This will come in handy when we try to render custom templates after the ocntent has been loaded. For example in Text Editors with cuistom Angular Elements that we created.
5. Ivy: New Renderer – This is to decrease the bunmdle size. Still in development phase, unstable.
6. ng udpate should update the Angular app and all of its dependancies.

Lecture 391 – A First Look At Angular Elements

npm install –save @angular/elements
<div></div>
this.content = < Can be reread to know how to create the custom web components. 392 – Additional Resources & Articles Angular 6 was released, here are some useful resources (also mentioned in the two videos before this lecture): [Article + Video] Angular 6 – What’s New? What Changed? => https://www.academind.com/learn/angular/snippets/angular-6-whats-new-angular-upgrade
[Article + Video] RxJS 6 – What’s New? What Changed? => https://www.academind.com/learn/javascript/rxjs-6-what-changed/
[Article + Video] Angular Elements – A First Look => https://www.academind.com/learn/angular/snippets/angular-elements-tutorial-introduction
Official Release Blog Post: https://medium.com/@stephenfluin/cc56b0efa7a4
Use the Angular Update Tool: https://update.angular.io/
Ivy Renderer Demo: https://ng-ivy-demo.firebaseapp.com/


SECTION 31 – CUSTOM PROJECT & WORKFLOW SETUP


Lecture 393 – Introduction

We don’t necessarily need to use the andulat cli to create the projects. We can use the webpack to create an Angular project form scratch

Lecture 394 – Initializing the Project

In a balnk project folder, open terminal and hit npm init.
It should ask a few questions, fill or leave blank and contniue. It should create a package.json file

Lecture 395 – Setting up the Basic Projecvt Files

Create the folder structure. We can create our own structurem, but let’s start with the default angular cli way.
Create a folder named src and then another named app within src.
project-name/src/app
Add files named
main.ts
app.module.ts
app-routing.module.ts
app.component.html
app.component.ts
app.component.css
home.component.ts // to use the routing feature

folder src/app/users/
users.module.ts
users.component.ts
users.component.html
users-routing.module.ts
users.component.css

folder src/
index.html
polyfills.ts

Lecture 396 – Installing the Core Dependencies

npm install –save @angular.core @angular./common @angular/compiler @angular./compiler-cli @angular/forms @angular/http@angular/platform-browser @angular/platform-browser-dynamic @angular/platform-server @angular/router @angular/upgrade @angular/animations
nopm install –save rxjs zone.js core-js

Lecture 397 – Filling the Project Files with some Life

main.ts
import { platformBrowserDynamic } from ‘@angular/platform-browser-dynamic’;
import { AppModule } from ‘./app.module’;

platformBrowserDynamic().bootstrapModule(AppModule);

app.module.ts
@NgModule({
    declarations: [
        AppComponent,
        HomeComponent
    ],
    imports: [
        BrowserModule,
        AppRoutingModule
    ],
    providers: [],
    bootstrap: [ AppComponent ]
})
export class AppModule {}

app.component.ts
@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent {}

app.compoennt.html
<h1>Hellor World</h1>
<a>Home</a>
<a>Users</a>

app.component.css
h1 {
    color: red;
}

app-routing.module.ts
const appRoutes: Routes = [
    { path: '', component: HomeComponent },
    { path: 'users', loadChildren: './users/user.module#UsersModule' }
];
@NgModule({
    imports: [ RouterModule.forRoot(appRoutes) ],
    exports: [ RouterModule ]
})
export class AppRoutingModule {
}

home.component.ts
@Component({
    template: '<h1>The homepage</h1>'
})
export class HomeComponent {
}

users.component.ts
@Component({
    templateUrl: './users.component.html',
    styleUrls: [ './users.component.css' ]
})
export class UsersComponent {}

users.component.html
<h1>This is users component</h1>
users.component.css
h1 {
    color: green;
}

users-routing.module.ts
@NgModule({
    imports: [ RouterModule.forChild([{ path: '', component: UsersComponent ])],
    exports: [ RouterModule }
})
export class UsersRoutingModule {
}

users.module.ts
@NgModule({
    declarations: [ UsersComponent ],
    imports: [ UsersRoutingModule, CommonModule ]
})
export class UsersModule {}

<h4>Lecture 398 - index.html and Polyfills</h4>
index.html
....
<app-root>loading...</app-root>

js files needs not be imported as the webpack will do uit automatically.

polyfills.ts
import 'core-js/es6';
import 'core-js/es7/reflect';
require('zone.js/dist/zone');

Update main.ts
import './polyfills';
...

Lecture 399 – Installing Development Dependecies

For develomoent purposes we need the webpack

npm install --save-dev 
    webpack webpack-dev-server angular-router-loader angular2-tempalte-loader
    awesome-typescript-loader html-loader raw-loader typescript del-cli

Lecture 400 – Setting up a Development Workflow

tsconfig.json // This states how the typescript should be compiled

{
    "compilerOptions": {
        "moduleResolution": "node",
        "emitDecoratorMetadata": true,
        "experimentalDecorators": true,
        "target": "es5",
        "lib": [
            "es5",
            "dom"
        ]
    }
}

webpack.config.common.js

var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
    entry: './src/app/main.ts',
    resolve: {
        extensions: ['.js', '.ts']
    },
    module: {
        rules: [
            {
                test: /\.html$/,
                loaders: ['html-loader']
            },
            {
                test: /\.css$/,
                loaders: ['raw-loader']
            }
        ],
        exprContextCritical: false
    },
    // npm install --save-dev html-webpack-plugin
    // This can be used to copy the content of the html files, and is required for us to use ourd defined index.html file and add the other files as required to it.
    plugins: [
        new HtmlWebpackPlugin({
            template: 'src/index.html'
        })
    ]
}

We need to merge the fields of common config to that of the dev and prod config. To do that we need another module
npm install –save-dev webpack-merge

webpack.common.dev.js

var webpackMerge = require('webpack-merge');
var commonConfig = require('./webpack.coinfig.common');
module.exports = webpackMerge[commonConfig, {
    devtool: 'cheap-module-eval-source-map',
    output: {
        path: path.resolve(__dirname, 'dist'),
        publicPath: '/',
        filename: 'bundle.js',
        chunkFilename: '[id].chunk.js'
    },
    module: {
        rules: [
            {
                test: /\.ts$/,
                use: [
                    {
                        loader: 'awesome-typescript-loader',
                        options: {
                            transpileOnly: true // If this true, it will only compile the files that are required for creating the bundle. Else it will use all the ts files present in the projhect and it would create problems while using AOT compilation.
                        }
                    },
                    {
                        loader: 'angular2-template-loader'
                    },
                    {
                        loader: 'angular-router-loader'
                    }
                ]
            }
        ]
    },
    devServer: {
        historyApiFallback: true,
        stats: 'minimal'
    }
}];

webpack.config.prod.js

Lecture 401 – Updating to Angular 6 + Webpack 4

Important – Read before proceeding!
With the latest update to Angular 6 as well as Webpack now being updated to version 4, some adjustments to the development and production workflows are required to make everything work again. Make sure to implement the following steps AFTER going through the next lectures (i.e. come back to this lecture to update the code – only some adjustments are required, no worries). Running the workflow as we set it up there will break, but no worries only very few steps are required.
0a) Make sure you’re using Webpack version 4: npm install –save-dev webpack@4
0b) You also need the webpack-cli package then: npm install –save-dev webpack-cli
1) Install one extra dev-dependency: @ngtools/webpack
npm install –save-dev @ngtools/webpack
2) Remove outDir from tsconfig.aot.json
3) Update webpack.config.prod.js
Add one import at the top (e.g. right after importing webpack.config.common ):

var ngw = require('@ngtools/webpack');
Add one extra rule (right before the old rule we had there - don't delete the old rule!):
{
    test: /(?:\.ngfactory\.js|\.ngstyle\.js|\.ts)$/,
    loader: '@ngtools/webpack'
},

Add one extra plugin (right before the old plugin we had there – don’t delete that old plugin!):

new ngw.AngularCompilerPlugin({
    tsConfigPath: './tsconfig.aot.json',
    entryModule: './src/app/app.module#AppModule'
}),

4) Update the build:prod script to look like this (only the cleanup part – del-cli – changed a bit):

"build:prod": "del-cli dist && ngc -p tsconfig.aot.json && ngc -p tsconfig.aot.json && webpack 
        --config webpack.config.prod.js --progress --profile --bail && del-cli 'src/app/**/*.js' 'src/app/**/*.ngfactory.ts'
        'src/app/**/*.js.map' 'src/app/**/*.shim.ts' 'src/app/**/*.ngsummary.json' 'src/app/**/*.ngstyle.ts' 'dist/app' 'src/polyfills.ngsummary.json'",

5) Add mode: ‘development’ to your webpack.config.dev.js file (e.g. right after the output property):
output: {

},
mode: ‘development’,
6) Add mode: ‘production’ to webpack.config.prod.js file
7) Remove the
new webpack.optimize.UglifyJsPlugin()
line in plugins[] in webpack.config.prod.js . This is not required anymore since mode: ‘production’ will automatically optimize and minify your code.
With these steps, npm run build:prod and npm run build should work fine.

Lecture 402 – Finishing and Using the Development Workflow

package.json

...
"scripts": {
    "test": ...,
    "build": "webpack-dev-server --inline --progress --port 8080 --config --webpack.config.dev.js"
}

Set the in the head section of the index.html

Now if we run “npm build”, then we should be able to run the application at localhost:8080

Lecture 403 – Setting up a Production Workflow

app/main.aot.ts

import './polyfills';
import { platformBrowser } from '@angular/platform-browser';
import { enableProdMode } from '@angular/core';
import { AppModuleNgFactory } from './app.module.ngfactory';

enableProdMode();

platformBrowser().bootstrapModuleFactory(AppModuleNgFactory);

tsconfig.aot.json // This states how the typescript should be compiled
{
    "compilerOptions": {
        "module": "es2015", // this will help in treeshaking, removing unused codes
        "outDir": "./dist",
        "moduleResolution": "node",
        "emitDecoratorMetadata": true,
        "experimentalDecorators": true,
        "target": "es5",
        "lib": [
            "es5",
            "dom"
        ]
    },
    "angularCompilerOptions": {
        "skipMetadataEmit": true
    }
}

webpack.config.prod.ts

var path = require('path');
var webpackMerge = require('webpack-merge');
var commonConfig = required('./webpack.config.common');
var webpack = require('webpack');

module.exports = webpackMerge(commonConfig, {
    entry: './src/app/main.aot.ts',
    output: {
        path: path.resolve(__dirname, 'dist'),
        publicPath: '/',
        filename: 'bundle.js',
        chunkFilename: '[id].chunk.js'
    },
    module: {
        rules: [
            {
                test: /\.ts$/,
                use: [
                    {
                        loader: 'awesome-typescript-loader'
                    },
                    {
                        loader: 'angular2-template-loader'
                    },
                    {
                        loader: 'angular-router-loader?aot=true' // to make sure that angular knows that aot is being implemented
                    }
                ]
            }
        ]
    },
    plugins: [
        new webpack.optimize.UglifyJsPlugin() // this will minify the code and optimize it
    ]
});

package.json

"scripts": {
    ...
    "build:prod": "del-cli dist && ngc -p tsconfig.aot.json && ngc -p tsconfig.aot.json && webpack --config webpack.config.prod.js --progress --profile --bail && del-cli 'src/app/**/*.js' 'src/app/**/*.ngfactory.ts' 'src/app/**/*.js.map' 'src/app/**/*shim.ts' 'src/app/**/' 'src/app/**/*.ngstyle.ts' 'dist/app'"
}

Lecture 404 – Adding Types and Fixing Bugs

npm install --save-dev @types/core-js @types/node

tsconfig.aot.json

{
    "compilerOptions" {
        ...
        "typeRoots": [
            "node_modules/@types"
        ]
    }
}

npm run build:prod should be able to build the app for production environment.
We wouldn’t be able to test the development build for now and we need to use another way to do so in future lecture.

Lecture 405 – Finishing Touches

npm install --save-dev lite-server

"scripts": {
    ...
    "server": "lite-server"
}

project-folder/bs-config.js

module.exports = {
    server: {
        baseDir: './dist',
        middleware: {
            1: require('connent-hostiry-api-fallback')({ index: '/index.html', verbose: true })
        }
    }
}

npm run serve to serve the prod built application.


SECTION 32 – BONUS: TYPESCRIPT INTRODUCTION (FOR ANGULAR 2 USAGE)


Lecture 406 – Introduction

Typescript is a superset to javascript and in the end is compiled to javascript.

Lecture 407 – Using Types

To compile a Typescript file, run
tsc filename //without the ts extension
When trying to assign some type to a variable that was defined to be some other type and compilethe file, the compiler will compile the file, but also throw coimpile errors. This is due to the fact, the filename.js with randomly assigned types is valid in javascript, so it would compile.
If some value is assigned to a variable at declaration type, then typescript is able to infer the type. So manula declaration of the type isn’t required.
We can also define a variable without giving it eaither a type or value. In this case, typescript won’t be able to know the type and would run normally as that of javascript. In this case, the type any is assigned to the variable.
The basic types in Typescript are:
1. string
2. number
3. boolean
4. Array
5. any

Lecture 408 – Classes

While class is now being implemented in vanilla javascript, Typescript does compile its own class type definitions to old javascript which is readable by old browsers.
Class is a blueprint of an object that might get created in the future.
static methods are created to be called for, without instantialing the class.

Lecture 409 – Interfaces

interface User {
    username: string;
    password: string;
    confirmPassword?: string; // the question mark makes it an optional property of the interface and may/mayn't be used in the class implemented the User Interface
}

let user: User; // this is a valid implementation of the interace and it can be used without creating a class for it.
user = { username: 'murari', password: 'mypass' }; // this is also valid

interface CanDrive {
    accelerate(speed: number): void;
}

Lecture 410 – Generics

Generics allow us to be flexible with the type of objects that can be used.
let numberArray: Array; // this will force the array to hold only numbers and if anyother type is tried to opushed in, then it wil throw error.

Lecture 411 – Wrap Up and Modules

export class ExportedClass {
    ...
}

The export keyword is required to make sure that this file and its functionalities are accessibnle from other modules. Other modules can use this file by importing it using the import keyword.
Modules are used to divide the code in between several connected parts.

Lecture 412 – Deep Dive into TypeScript

How to learn TypeScript
Throughout this course we’re always using TypeScript and I am convinced that you’ll be able to learn it ‘on the fly’. But a little head start is never wrong.
What is TypeScript?
TypeScript is a superset to JavaScript, which means, that it compiles into pure JavaScript in the end. Why do we use it then?
First, it provides ‘strong typing’ (that’s where the name comes from). This means that we can (and should) assign types to our variables and class members. These types won’t compile to JavaScript (as JS does not know types) but we will get compilation errors if we assign wrong types or make any other type-related errors. This is a HUGE help in the daily development work and should not be underestimated!
Second, TypeScript introduces some nice features, JS does not have out of the box (at least in the ES5 specification). This includes classes (‘class’ keyword), interfaces, generics and modules. Being able to use these constructs makes our code cleaner, easier to read and helps us avoid nasty errors. Especially in combination with the strong typing we are really able to write high quality code and track down errors quickly.
Where can I learn all the TypeScript fundamentals?
There are a lot of great resources out there which will get you started very quickly.
The official documentation is not too bad to be honest, so you may give it a try: http://www.typescriptlang.org/Handbook
There’s also a course here on Udemy, though I have not tested it! https://www.udemy.com/typescript/
Can we mix TypeScript and JavaScript?
Yes, we can. No one is preventing us from not setting types, using ‘var’ instead of ‘let’ or using pure JavaScript libraries (i.e. libraries which don’t offer a TypeScript version/ implementation).
Can’t I use ‘normal’ JavaScript to write Angular 2 applications?
You can absolutely do that. But currently finding good documentation and examples on Angular 2 using plain JavaScript is extremely hard. And to be honest: TypeScript will be the standard ‘language’ to be used when developing Angular 2 applications. So I definitely recommend using TypeScript



OTHER NOTES:

Note 1:
To make angular working on c9, replace this in the start script in package.json

ng serve --host 0.0.0.0 --port 8080 --public-host $url --disableHostCheck true

and run ‘npm start’ instead of ‘ng serve’ to start the application

npm list | grep 'angular'
// npm list -g can be used to check global packages

Note 2:
The this scope isn’t available in anonymous functions, so as a workaround a variable is defined in the function where the anonymous function is defined and a reference to the this keyword is saved. Now the anonymous function can use this variable to get access to the variables defined in the class.
While in the case of arrow functions, it stores the this variable as a local reference to the class.

export class GameControl {
    onStartClick() {
        // Doing it the normal way
        var self = this;
        setInterval(function() {
            console.log("Working " + (++self.counter));
        }, 1000);
        // Doing it the arrow function way
        setInterval(() => {
            console.log("Working " + (++this.counter));
        }, 1000);
    }
}

Note 3:
In Javascript, object are passed around as references. So workarounds should be used:
For Arrays, one could use the arrayName.slice() to get a copy of the array and not its reference.

Note 4:
Optional parameters can be passed by using the ? after the argument name.
stateSnapshot?:RouterStateSnapshot

Leave a Reply