Angular Notes

Structure Information – Udemy course – https://www.udemy.com/the-complete-guide-to-angular-2/

Getting Started

  1. Need a good IDE – Visual Studio Code looks like it has a good bit of angular support.
  2. Need Node JS – Dont actually need node.js. BUT you need the node package manager (NPM) wihich comes with node.js
    • Download node.js from here –  https://nodejs.org/en/. Take the latest version whatever that may be. Install it, easy.
  3. Need Angular CLI (Commend Line Interface) – this is te easiest way to create stuff and compile projects
    • Open Visual Studio Code, select from the menu:  View -> Integrated Terminal. A terminal will open where you need to enter: npm install -g @angular/cli. This will only work if Node.js has been installed prior to this step.
    • NOTE: To update a projects CLI to the latest version, type npm install –save-dev @angular/cli@latest
    • More Anglar CLI information from here – https://cli.angular.io
  4. Need to create a project – using the CLI seems to be the standard way to create a project.
    • In the terminal, navigate to the directory where you want to create your project. Then type ng new my-project. Call the project what you want. The ng command is now available because you installed the CLI. This step will create a folder with the name of the project you specified and some files to get a quick start on your project.
      • NOTE: you can add parameters to this command to set up the project in many ways. For example, ng new ng6-proj –style=scss –routing will replace the default css with scss and will add routing. It means that it will set up the file structure and internal file links and imports. It will not install the components needed for routing. That you need to do yourself.
  5. Want to verify that project was created
    • Navigate to the folder created with the name of your project. Type ng serve. This starts a webserver on which an instance of localhost is created. The result of the command is an address which you can copy and paste into your browser. You should then see something like this … awesome

 

 


Preparing Your Project

  1. Add Bootstrap 4 CCS Framwork to the project
    • In the terminal type npm install –save bootstrap@4 – this will install bootstrap locally within this project, it does not make it available to other projects
    • Open angular.json and go to the styles array
      • Add the line “node_modules/bootstrap/dist/css/bootstrap.min.css”, before the styles.css declaration (the styles.css is where you can put your application wide custom css)
    • To make sure it takes effect, run an ng serve and using google dev tools check the header of the index.html –> voila –>  * Bootstrap v4.1.3 (https://getbootstrap.com/)
  2. ….

 


The Basics

Loading Angular Apps – what happens?

  • After the project is created, you get a new folder, many subfolders and a lot of files, thousands by the looks of it. But these are mostly development resources and when deploying your app, the number of files is much much less. So how does it load, quick version: Angular only loads the index.html –  Looking inside this we see:

    Angular is specialised for SPA (Single Page Applications). This file is the single page. Everything happens on top of this file.

  • The <app-root></app-root> tags are important for this explanation. Now this is not the final index.html file. When the project is compiled, Angular produces an index.html which has pointers to some scripts at the end of it. It is these scripts on which Angular is brought into the browser.After creating a project you get the following file structure:Via the scripts inserted during compilation, the first file that gets loaded is main.ts. The code in this file loads our app module file. The instructing telling it to do this is the line – platformBrowserDynamic().bootstrapModule(AppModule);So then angular looks for the file called app.module.ts. In here you can see a couple of things.First of all there is a line – bootstrap: [AppComponent] – this basically means that the first (main) component in this application is called AppComponent. You can also see that AppComponent is imported and then also declared. In the import line, we see a path to a typescript file called app.component.ts (the .ts is always left out). So lets look at that file.
  • And there is app-root. We saw <app-root></app-root> in the index.html file.  This is a class/component file. Few things to point out:
    1. The @Component decorator is imported from @angular/core – you need to import different decorators from differernt sources as you go along.
    2. The @Component has 3 items:
      • selector – this is what we use to refernce this object in our HTML files
      • templateURL – this tells us what html file is associated with this component
      • styleUrls – you can define css specifically for this component – handy!
    3. class declaration – this is the logic or brain of the component.

     

    So lets look at templateUrl file – app. component.html.

    … and this is what is shown initially. There is some string interpolation going on there {{title}} – looking back at the app.component.ts file, we see that title = ‘app’;

    So — load application

    • index.html file is served which contains a bunch of scripts which get executed
    • the scripts look for the main.ts file which tells Angular where to find the bootstrap information – app.module.ts
    • inside the app.module.ts file we see that the bootstrap points to AppComponent
    • in the app.component.ts we see our first component 
    •  the component has decotators
      • Selector (<roof></root> as seen in the index.html file)
      • template
      • css
    • The component/object must then be exported
    • The file referenced by template (app.component.html) contains the first bit of html code

Components

The AppComponent selector is the only one added to index.html. All other components should be add to the app.component.html file

Creating a new component

  1. Create directory under the app directory called server. Best practice is to call the folder the same name as the class
  2. Create a file under the new folder called server.component.ts
  3. Type “export class ServerComponent{}
  4. Add a decorator to the Component by add an @ sign
  5. Typescript does not know about component types/prototypes by default so we need to let it know by importing the component using the command import { Component } from ‘@angular/core’ at the very top of the file.
  6. Now I need to configure my component. Between the import and the export, do this:
    • @Component({
      selector: ‘app-server’,    — this our tag
      templateUrl: ‘./server.component.html’,    — this where the code for this component will be
      styleUrls: [‘./server.component.css’]    — this is css specific to this component
      })
  7. Now you need to tell angular about this module, it will not know it automatically and will not look for it either. In the file app.module.ts we need to import our new component (import {ServerComponent} from ‘./server/server.component’) AND add it to the declarations list (after AppComponent). The declarations list is basically telling Angular which components it needs to be aware of on start up

SUMMARY – steps to create components manually:

  1. create folder with same name as component
  2. create file <component name>.component.ts
    • import
    • decorate
    • export
  3. create file <component name>.component.html to hold component code
  4. go to app.module.ts
    • import
    • add to list of declarations

IF YOU DO THIS VIA THE CLI ( ng g c <component name> ) – all of the above is done for you


Note about the component selector

The usual notation for the component selector is

selector: “app-selector”, which means that we identify it in the html file with <app-selector></app-selector>

However the following is also valid:

selector: “[app-selector]”, – define it as an attribute so then you could do this <div app-selector></div>

or

selector: “.app-selector”, – define it as a class so then you can do this <div class=”app-selector”></div>


Note about using Angular with Internet Explorer

Trying to run the app in Internet explorer will not work right now. A quick google search will tell you that some lines need to be uncommented in the file called polyfills.ts. You can find it under the ./src directory. Go there and uncomment the following lines …. highlight them and use the keyboard shortcut Ctrl+K Ctrl+U. Then you are good to go.

/** IE9, IE10 and IE11 requires all of the following polyfills. **/
// import 'core-js/es6/symbol';
// import 'core-js/es6/object';
// import 'core-js/es6/function';
// import 'core-js/es6/parse-int';
// import 'core-js/es6/parse-float';
// import 'core-js/es6/number';
// import 'core-js/es6/math';
// import 'core-js/es6/string';
// import 'core-js/es6/date';
// import 'core-js/es6/array';
// import 'core-js/es6/regexp';
// import 'core-js/es6/map';
// import 'core-js/es6/weak-map';
// import 'core-js/es6/set';

Notes on Databinding

Databinding is communication between the typescript code (business logic) and the template that the user sees. The template is all the user sees so there needs to be an infrastructure to faciliate communication between the template and the underlying typescript – and in both directions.

  1. Output Data (Typescript –> Template)
    • String Interpolation ( {{ data }} )
    • Property Binding ( [property]=”data” )
  2. React to user events (Template –> Typescript )
    • Event Binding ( (event)=”expression” )
  3. Combination of both (Typescript <–> Template)
    • Two way data binding ( [(ngModel)]=”data” )

String Interpolation ( {{ data }} )

Basically you have variables in the typescript file which can be outputted to the template that the user sees. Syntax on the template is {{ variable }}. The only condition for string interpolation is that the value has to be such that it can be resolved to a string in the end. You could call a function between the curly brackets but it must return a string.  Also, not possible to write multiline or conditional expressions here

Property Binding ( [property]=”data” ) – NOTE: no curly brackets

This allows us to set properties on HTML elements. Best example used is for a button whose property, disabled, is bound to a variable value in the typescript file.

in the typescript file class ….

isDisabled = true;

And then, inside the constructor …

setTimeout(() => { // like myFunction = function(){}
this.isDisabled=false;
 },2000);

On the template …

<button
class =" btn btn-primary "
[disabled]="isDisabled"> <!-- Angular recognises this -->
Toggle Disabled
</button>

 

Event Binding ( (event)=”expression” )

Angular uses parenthesis to indicate and event. For example a click event on a button as above would be

<button 
class =" btn btn-primary " 
[disabled]="isDisabled"
(click)="doSomething()"> <!-- Angular recognises this -->
Toggle Disabled 
</button>

The (click) event can trigger almost anything. You can call a function which does something, you can change a property of the element or you can directly change the value of a variable in the typescript file.

INTERESTING – $event – seen used in input  element as follows …

<input
type="text",
class="form-control"
(input)="onAddInput($event)"> <!-- Angular recognises this -->

$event is a reserved variable name that can be used in the template when using event binding. $event is the data that is emmitted with that event.

In the typescript file, we can capture the function and do something with some part(s) of date in the event.

onAddInput(event: Event){
this randomVariable = (<HTMLInputElement>event.target).value
// theoretically we should be able to access the value through event.target.value but Angular doesn't like this so we explicitly cast the event to a HTMLInputElement type. This is how casting works in Angular
}

If you console.log the event you get a whole bunch of data.

 

Two way data binding ( [(ngModel)]=”data” )

Two binding combines property and event binding – [ (  ) ] . To use two way binding we need to use a directive called ngModel.

ngModel is not available by default, you need to import it from @angular/forms. So in your app.module.ts file add the following:

import { FormsModule } from '@angular/forms';
.. and add FormModule to your list of inputs
imports: [
BrowserModule,
FormsModule
],
You cant call functions in two way data binding. It just mean that if the property changes in the template, it will be updated in the class variable, AND if the class variable changes, it will be updated in the template.
<input 
type="text", 
class="form-control" 
[(ngModel)]="someVariableName"> <!-- Angular recognises this -->


Directives

Directives are Instructions in the DOM. By placing the selector of our component somewhere in the DOM, we are telling Angular to add the content of our component template and the business logic of our typescript code in the place where the selector is being used. Components are directives, but directives with a template. There are also directives without a template.

The selector of a directive can be created just like the selector of a component but usually you can use the attribute style. You can make custom directives but there are a few built in directives which are very useful.

*ngIf – *ngIf=”variableIsTrue; else notTrue

The star is required because ngIf is a structural directive which changes the structure of the DOM. ngIf is a condition, any expression returning true or false – should this element be added or not.

It is also possible to have an ELSE part in the *ngIf. It is a bit complicated ….

<p 
*ngIf="variableIsTrue; else notTrue">
Variable is true
</p> // show this if variableIsTrue, else show the template for notTrue

<ng-template #notTrue>
Variable is not true
</ng-template>

This a very handy directive to use for conditional situations. <ngTemplate> will only be included if notTrue is true.

ngStyle – [ngStyle]=”css styling element: decision”

Notice that this is not a structural directive (no *), instead it is an attribute directive. That  means it only changes the element which it is on, it doesn’t add or remove from the DOM. How to use it??

<p [ngStyle]="{}">
Some random text
</p>

It is important to understand that the square brackets indicate property binding. ngStyle is the actual directive, but by itself it doesn’t do anything. The property name we want to bind to is also called ngStyle (strange!!).

The ngStyle property expects to get a javascript object of name value css property pairs.

Address:
{
Street: ‘Main’,
Number: 100,
Apartment:
{
Floor: 3,
Number: 301
}
}

<p 
[ngStyle]="{backgroundColor:'red',color:'white',paddingLeft:'25px'}">
Some random text
</p>
NOTE: you can use the real css class name – ‘background-color’ – with quotes, or you can use camelCase – backgroundColor- without quotes. You could also call a method to return the color:
<p 
[ngStyle]="{backgroundColor:getColor()}">
Some random text
</p>
GetColor could return a color based on another variable.
getColor(){
return this.someVariable === true ? 'green' : 'red';
}

ngClass – [ngClass]=”{‘CSS class name’:condition == true}”

 ngClass is also an attribute directive. It just changes the properties of the element it is on. ngClass is a bit like ngStyle in that it allows us to change the style of the element by dynamically adding/removint CSS classes. The general syntax is:
<p [ngClass]="{'CSS-class-name':wantToColor === 'yes'}">
Some random text
</p>
Note: if the CSS class name is comprised only of characters then you don’t need the quotes around it.

*ngFor – *ngFor=”let item of itemList”

 ngFor is preceded by a star which tells us that it is a structural directive which can change the structure of the DOM. It allows us to loop through arrays and create elements on the fly. The basic syntax is:
<customDirective <!-- component (directive with a template) -->
*ngFor="let item of itemList">
</customDirective>

<p <!-- regular HTML element -->
*ngFor="let item of itemList">
{{item}}
</p>
Important: item in the example above is the name of a variable property of the component/directive. itemList is an array property of the component/directive.
IMPORANT: in the same element you can also get hold of the index of the array item by doing this:
<p  
*ngFor="let item of itemList; let i = index"
[ngStyle]="backgroundColor: i < 4 ? 'blue' : 'red'">
{{item}}
</p>
In the above example the first 5 paragraphs will be blue and the rest will be red.

Components and Binding

A component can easily manage its own data and pass information between its model and its template – back and forward. That is important to understand but what about communication between components? How does that work?

On a high level (and we are only talking about parent/child communication currently), communication capabilities can be  this can be broken down into 2 areas:

  1. data made available to the parent component (one way only) – the parent component can fetch the data as it wants
  2. data emmited to the parent component – the parent component is listening for these emmitions and will react if one is picked up.

Either way, the data a component has is only really available to the component itself. It only becomes available to other components if component embedding is going on. For example:

Here Componet A and Component B exist independently. Each component may have its own data but as they are now, data/information cannot be passed between them and they cannot influence eachother.

Now component B has been embedded in component A. Component A is the parent of component B. Component B is the child of component A. A component can have many children at a time but only one parent at a time (for a particular transaction).

In the second picture, component B can make its data available to component A. It means that component A can read, reset, delete , etc those pieces of data that that component B makes available (data is explicitly made available – on purpose). Component B can also emit data to component A. Component A can listen for these emmisions and react accordingly.

Now component A has 2 child components. Each child component can communicate with its parent BUT they cannot communicate directly with each other in the manner described above

A good way to think about this is ………… the type of communication employed could depend on where the activity is happening at the time. If, for example, the activity is on the parent component, it may want to pass information to the child component for processing. The child component needs to make data (structures) available to allow the parent to add data there. The child component could be the one to display the data but needs information communicated to it in order to know what to dispay. On the other hand, if you click on something in the child component, you may want to tell the parent component about it. In that case the child component will emit upwards towards the parent.

PARENT 2 CHILD

Inside the childs typescript file, you need to declare a variable and give it a decorator called Input. E.g.

Inside the child component (component B) model, add @Input() myData: dataType; – the type can be a primitive like string or number or a model (data structure) you have created. The variable can be initialised or uninitialised. Either way, myData is now available to the parent component. NOTE: Input needs to be imported from @angular/core.
When component B is embedded in component A, component A (the parent in this case) can access data structures of component B – <app-component-b [myData]=”somedata”></app-component-b>

CHILD 2 PARENT

Child to parent works a little diffently. The most common scenario is probably where there is a click or an inpur event in the child which the parent needs to know about. In that case Angular uses eventEmitters.

Inside the childs typescript file, you need to do 2 things:

  1. creater a new emmiter.
  2. implement a function to fire the emitter.

Step 1 – The emitter looks like this

@Output() clickSelect = new EventEmitter<data-type-of value-that will be emmited -- can also be void>();

The type can be a primitive like string or number or a model (data structure) you have created.

Step 2 – The function to fire the emitter looks something like this:


onSelected() {
this.clickSelected.emit(data);
}

The data can contain something or it can be empty which makes it simply an event (a trigger if you like).

Inside the childs template file, we can add a click event to a button:


<a href="#" (click)="onSelected()">Click me</a>

So when the link/button is pressed, the function will be called which fires the event.

THATS IT! Now there is an event going up the chain which the parent can listen for.

Inside the parents template file
<app-component-b (clickSelected)="onClickSelected($event)"></app-component-b>
The above says that
if the event called clickSelected is emitted by the component called app-component-b, then this component (the parent) should call its own function and pass the data that has been emitted.

Directives

There are 2 types of directives:

  1. Attribute Directives
    • They sit on elements just like attributes. These ones never really change the DOM itself, you only change properties of the element that the directive is sitting on.
  2. Structural Directives
    • Also sit on elements like directives but these ones actually change the structure of the DOM. For example, ng-if is a structural directive. If this is false, the entire element is removed from the DOM (not just hidden). You can recognise structural directives straight away as they are preceeded by a *.

 

Ready made attribute directives – ngClass and ngstyle

Ready made structural directives – ngFor and ngIf

 

Components of an Attribute Directive

  
(1) import { Directive } from '@angular/core'; //Import
(2) @Directive({
selector:'[appDropdown]' // in square brackets because this will be an attribute directive
})
(3) export class DropdownDirective {

constructor() { }

}

Above is the skeleton of a directive. Note that the selector is done with square brackets so it is a attribute directive and can sit on top of any other component or element (like a div for example).

In order for a directive to be able to do anything, it needs to be able to get access to the element it is sitting on. Angular allows us to do this using ElementRefs. You can initialise a directive property inside the directive constructor …

constructor(private elementRef: ElementRef, private renderer Renderer2) { } // Remember to import ElementRef and Renderer2 from '@angular/core'
We could do something with this new property in the constructor but usually a better place is inside ngOnInit. Goes like this … note we are using the Renderer2 here instead of going directly to the element.
export class DropdownDirective implements OnInit { // implement OnInit and then can place code into the ngOnItit function
constructor(private elementRef: ElementRef, private renderer Renderer2) { 
} 

ngOnInit(){
this.renderer.setStyle(this.elementRef.nativeElement, ‘background-color’, ‘red’);
}
}

@HostListener is used to listen to events and can intercept the event data

@HostBinding is used to do something directly to the element that the directive is sitting on based on a condition

ElementRef allows us to grab a reference to the element that the directive is currently sitting on.

All 3 need to be imported from ‘@angular/core’

We may also make a directive dynamic by adding data to the directive via the element to an @Input element on the directive.

 

Components of an Structural Directive

Structural Directives are always preceded by a *. When angular processes a structural directive, the directive (complicated to explain why) takes the form of property binding. Angular changes the DOM to include the code but wraps it in a <ng-template [property]> element where property is the name of the directive without the *. That means that you are performing property binding to the directive itself.

Aside: Directive to use to open a dropdown menu

 

import { Directive, HostListener, HostBinding } from '@angular/core';

@Directive({
selector:'[appDropdown]'// in square brackets because this will be an attribute directive
})

export class DropdownDirective {
/*
whereever you put this directive, if isOpen is true,
the class open will be attached. We initialise the variable to false using @HostBinding
*/
@HostBinding('class.open') isOpen = false;
/*
the exercise is to attach a class called open when a menu dropdown button is clicked
to listen to a click event we use @HostListener
*/

@HostListener('click') toggleOpen(){
this.isOpen=!this.isOpen;
}

constructor() {}

}

 


Services and Dependency Injection

Typical use cases for services are:

  1. Code duplication
    • If you are doing the same thing in 2 different components, consider moving the code into a service that both components can access and use.
  2. Data retrieval and storage
    • If the code is requestion data from a data source and returning it in raw or modified form, consider putting this into a service. The data source connectivity can be controlled centrally and the service can be reused later if needed.

Create a Service

Either put all your services in a shared or service folder or, if appropriate, put each one in the folder of the component which will use it.

You can create it automatically by typing ng g s sample and a file with the name sample.service.ts will be created holding the following …

import { Injectable } from '@angular/core'; // this is put here automatically by Angular 6
@Injectable({ providedIn: 'root' }) // this is put here automatically by Angular 6

export class SampleService {

  constructor() { }
}
NOTE: This service is injectable by default. With earlier versions of Angular, you needed to write code into the service in order to facilitate injecting one service into another

This service can hold information or be used to fetch information. In order to use this service in a component, you need to go to the components parents typescript file and add the following …

import { SampleService} from './sample.service';

@Component({
  selector: 'app-test',
  templateUrl: './test.component.html',
  styleUrls: ['./test.component.css'],
  providers: [SampleService]
})
Now the service is available for the parent component and everything under it. One more thing though, in order to use the service in a component you need to import it and initialise it in the component’s constructor …
import { SampleService } from './sample.service';

export class TestComponent implements OnInit {
private stuff: StuffType[];
  constructor(private sampleService: SampleService) { }

  ngOnInit() {
    this.stuff = this.sampleService.someFetchFunction();
  }
Services are a very handy way to share data and functionality. I think the design of your application should somewhat centre around the services involved. If you design these first, then you remove a lot of the complexity and repetitiveness of what components are doing. I have seem many discussions around the best place to provide a service . Some provide it at the highest level (app.module.ts) which makes the service available globally. Some provide it within a component makeing it available to that component and all its child components. In any case, in Angular 6, when you create a service with the CLI (ng c s serviceName), @Injectable({ providedIn:’root’ }) is added to the generated service typescript file which makes the servic e available globally by default.

 


Routing

Routes are used to let us switch pages. Actually we are not switching pages but we load something different based on the selected route.

Basic Setup

In the file called app.module.ts add the following:

import { Routes, RouterModule } from "@angular/router";

const appRoutes: Routes = [
{ path: '', component: 'HomeComponent' }, // this path is empty. This is the default or start page
{ path: 'users', component: 'UsersComponent' }, // this path causes this component template to be loaded
{ path: 'servers', component: 'ServersComponent}
];

Imports: [
.....,
RouterModule.forRoot(appRoutes)
]

 

Now we have added routing functionality to our application. Now Angular knows our routes. But where do these “pages” get rendered? In your app.component.html file, you need to inform Angular where to load it by adding a special directive <router-outlet></router-outlet>. This marks the place where we want Angular to load the component of the currently selected route.

 

Adding Links to Select Routes

So we need some kind of links to select these routes and get Angular to load different pages/templates.

<a href="/">Home</a>
<a href="/users">Users</a>
<a href="/servers">Servers</a>

// this will work. BUT the problem is that it will actually cause the page to be reloaded. If we have some data that we want to be persistent, this solution will not work
// what we actually want is for Angular to load the correct template based on the selected route and not reload the page at all
// better to do it like this ...

<a routerLink="/">Home</a>
<a routerLink="/users">Users</a>
<a [routerLink]="['servers']">Servers</a> // this notation allows you to construct more complex paths

// now the page will not be reloaded because routerLink catches the click on the element, prevents the default behaviour (send load request) and instead analyses what we pass to the routerLink directive, parses it and looks for a fitting route in out configuration

 

Making Buttons, Links or Tabs Active

Using routerLink is fine and all but it doesnt care so much about the CSS part – setting the selected button, link or tab to active. Angular provides something for this called routerLinkActive. We use it like this

<li routerLinkActive="active"><a routerLink="/">Home</a></li>
<li routerLinkActive="active"><a routerLink="/path">Link</a></li>

What this does is that it looks at the path, analyses it and then adds the active class to the element if your current location contains the path in the routerLink – if this link is part of the path you are on.

This is ok most of the time but does not work for the route path – “/”. The reason for this is that this route will be part of every path, its always there, and the condition will always be satisfied (the element will always get the class “active”. We need to add some configuration to our routerLinkActive directive. We need to do like this …

<li routerLinkActive="active" [routerLinkActiveOptions]="{exact: true}"><a routerLink="/">Home</a></li>

Now the class “active” will only be added if the path is exactly the one described in routerLink.

Relative Vs Abolute Paths

When you tell Angular to load a route, it will always load it relative to the current path (where it is now). Thats why you need to be careful when creating your links and deciding what to put into your routerLinks. If you are already inside a route, putting “newpath” into your routerLink will simply add newpath to the current route like this

routerLink="newpath"
http://www.mysite.com/currentpath --> http://www.mysite.com/currentpath/newpath

That might not be what you want. If, however you put “/newpath” (note the forward slash) into your routerLink it goes like this

routerLink="/newpath"
http://www.mysite.com/currentpath --> http://www.mysite.com/newpath

Some caution is required here to make sure that the path address is correct at the end. All routes need to be defined exactly as they will be used

 

Loading Routes Programmatically

Just say you want a button on your page to trigger a function in your typescript file that will route you to a specific page. The routerLink directive is not part of the link anymore but there is a (click) event generated which triggers a function.

To achieve this, we need to inject the Router directive into our component and then use it in the function triggered by the click listener

import { Router } from '@angular/router';

constructor(private router: Router) {}

onFunctionTriggered() {
this.router.navigate(['/newpath']) 
// the path is relative to where you are now. Forward slash will bring you back to the root
}

 

Using Relative Paths

In the last section we talked about the navigate function and how the path can be specified there. Actually having the forward slash as part of your path is unnecessary there because, unlike routerLink, the navigate function, by default, assumes that the path is relative to root.

But this is something that can be used to build paths relative to your current path also. We need to add some configuration to our navigate function, like this:

import { Router, ActivatedRoute } from '@angular/router'; 

constructor(private router: Router, private route: ActivatedRoute) {} 
// route tells us where were are currently

onFunctionTriggered() { 

this.router.navigate(['newpath'], {relativeTo: this.route}) //if relativeTo is unspecified, Angular defaults to relativeTo: Root
// this basically tells angular that it should navigate to newpath and add it to the current route
// this also means that we need a route to exist that supports this combined route
 }

 

Passing Parameters to Routes

So sometimes we need to pass parameters embedded in out links or routes to give the endpoint some data to work with. So how do we add this kind of data in the route path.

We do this in the app.module.ts file where we have defined our routes in the first place

{ path: 'users/:id', component: 'UsersComponent' } // you will later be able to retrieve the data using the name you specify here
// if you want multiple parameters, it looks like this ...
{ path: 'users/:id/:name', component: 'UsersComponent' }

// or even like this ... where the parameter is in the middle of the route 
{ path: 'users/:id/edit', component: 'UsersComponent' }


Fetching Route Parameters

So how to get access to the data which is encoded in the url? In the typescript file of the recieving component. The first thing we need to do is to get a handle on the actie route. We do this by injecting this into the components constructor.

user: {id: number, name: string};

constructor(private route: ActivatedRoute) {}

// you can get access the id property via the following
ngOnOnit() {
this.user = {
id = this.route.snapshot.params['id]; // you will only have access to properties that have been defined in your route parameters
name = this.route.snapshot.params['name];
}
}

Fetching Route Parameters Reactively

If you set up a link/route inside your component that should basically load different information into that component (go from the currently displayed user to another), Angular will update the URL but it will not reload the data in your component. It will do it the first time you load the component but not after that. We need the tell Angular to watch out for these changes like this.

Export class UserComponent implements OnInit {
user: {id: number, name: string};

constructor(private route: ActivatedRoute) {}
ngOnOnit() {

this.user = {
id = this.route.snapshot.params['id]; // you will only have access to properties that have been defined in your route parameters
name = this.route.snapshot.params['name];
};
this.route.params.subscribe(
(params : Params) => { // you need to import Params from '@angular/router'
this.user.id = params['id];
this.user.name= params['name];
 }
);
}


}

Note: Suscriptions are set up using observables and are stored in memory. When the component is destroyed, Angular cleans it up in memory. The subcription is not tied to the component as such which means that even if the component is destroyed, the subscription is not destroyed automatically. To do this manually you need to implement the OnDestroy method like this

import { Component, OnInit, OnDestroy } form '@angular/core';

Export class UserComponent implements OnInit, OnDestroy {
user: {id: number, name: string};

paramSubscription: Subscription;
 constructor(private route: ActivatedRoute) {}
ngOnOnit() { 
this.user = { id = this.route.snapshot.params['id]; 
name = this.route.snapshot.params['name]; }; 

this.paramSubscription = this.route.params.subscribe( (params : Params) => {
this.user.id = params['id]; 
this.user.name= params['name]; 

 } ); 

}

// the below is done automatically by Angular, this for illustration purposes

ngOnDestroy() {
this.paramSubscription.unsubscribe(); 
}

}

Passing Query Parameters and Fragments

The section above talks about route params and how they can be included in the route address itself. Here are 2 more ways to add info to your route.

Query Parameters – these are the parameters seperated by a ?

Fragments – these are the parameters starting with a # – denotes an anchor in the html template

In the template, on the link itself, you can put …

// we have a route for 'newpath/:id/edit'
<a
[routerLink] = "['newpath', 5, 'edit']" // route is root/newpath/5/edit
[queryParams] = {allowEdit: '1'} // route is root/newpath/5/edit?allowEdit=1
fragment = "loading"// route is root/newpath/5/edit?allowEdit=1#loading
>Edit</a>

… and programatically …..

onSomefunction( id: number) {
this.router.navigate(['newpath', id, edit], {queryParams: {allowEdit: '1'}, fragment: 'loading'});
}

Sweet!

 

Retrieving Query Parameters and Fragments

When retrieving data from the route, you always need to import and inject the ActivatedRoute.

import { ActivatedRoute } from ‘@angular/router’;

constructor(private route: ActivatedRoute) {}

In ngOnInit there are a couple of ways of getting the information passed

this.route.snapshot.queryParams
this.route.snapshot.fragment

… or (and maybe better) …

this.route.queryParams.subscribe( //do something );
this.route.fragment.subscribe( //do something );

Notes

 

 

Observables

Forms

Pipes

Http

Authentication

Optimization and NgModules

Deployment

Animations and Testing