Bonus : The HTTP Client
the documentation
changes to get to Angular 5 and the new HttpClient
for complete documentation on the new HttpClient go to
angular.io
check node.js version
    node -v
    
and update node as necessary
update/install the latest NodeJs
uninstall NodeJs if installed and version is not the latest from nodejs.org
to check installed version use the command prompt
    node -v
        
  1. Uninstall from Programs & Features with the uninstaller
  2. reboot
  3. Look for these folders and remove them (and their contents) if any still exist. Depending on the version you installed, UAC settings, and CPU architecture, these may or may not exist:
    • C:\Program Files (x86)\Nodejs
    • C:\Program Files\Nodejs
    • C:\Users\{User}\AppData\Roaming\npm (or %appdata%\npm)
    • C:\Users\{User}\AppData\Roaming\npm-cache (or %appdata%\npm-cache)
    • C:\Users\{User}\.npmrc (and possibly check for that without the . prefix too)
    • reboot
  4. Check your %PATH% environment variable to ensure no references to Node.js or npm exist.
install latest version of NodeJS using the msi from nodejs.org

update/install the latest version of Angular's CLI (command line interface)
to determine the installed version of CLI

        ng --version
        
to update Angular CLI to a new version, both the global package and the project's local package must be uninstalled
global
    npm uninstall -g @angular/cli
    npm cache clean
    npm install -g @angular/cli@latest        
        
node versions change quickly going from 6.11.1 to 8.9.1 LTS in just the feek weeks of course time
the many steps are tedious, with W10 the installed node.js version's msi cleans the up the uninstall quite well
each step just verifies how well everything cleans up
update angular CLI to latest version
    npm uninstall -g @angular/cli
    npm cache clean
    npm install -g @angular/cli@latest        
        
new apps created using CLI 1.5 use angular 5 which is global as well so no warnings

Top

Index

unlocking
in app.module imports property includes HttpModule
change to HttpClientMode and change import statement accordingly
    ...
    // import { HttpModule } from '@angular/http';
    import { HttpClientModule } from '@angular/common/http';
    ...
    @NgModule({
      declarations: [
        AppComponent,
      ],
      imports: [
        BrowserModule,
        //    HttpModule,
        HttpClientModule,
        ...
      ],
      bootstrap: [AppComponent]
    })
    export class AppModule { }
        
the HttpClient has a generic get function
    getRecipes() {
        const token = this.authService.getToken();
        this.httpService.get<Recipe[]>('https://ng-recipe-book-47065.firebaseio.com/recipes.json?auth=' + token)
            .map(                
            (recipes) => {
                for (let recipe of recipes) {
                    if (!recipe['ingredients']) {
                        recipe['ingredients'] = [];
                    }
                }
                return recipes;
            })
            .subscribe(
                (recipes) => {
                    this.recipeService.setRecipes(recipes);
                }
            );
    }
        

Top

Index

request configuration and response
HttpClient's generic get used when response is json data
overloaded get and put methods can be used for other data formats
additional JSON arg named options
snippet from data-storage.service
    getRecipes() {
        const token = this.authService.getToken();
        // use overloaded generic method so mapping doesn't change
        this.httpService.get<Recipe[]>('https://ng-recipe-book-47065.firebaseio.com/recipes.json?auth=' + token,
            // options are to return the body as JSON data
            { 'observe': 'body', 'responseType': 'json' }
        ).map(
            (recipes) => {
                for (const recipe of recipes) {
                    if (!recipe['ingredients']) {
	                    recipe['ingredients'] = [];
                    }
                }
                return recipes;
            }
        ).subscribe(
	        (recipes) => {
		        this.recipeService.setRecipes(recipes);
	        }
        );
    }  
        
when no observe value is set in the options then the body is returned

Top

Index

requesting events
add options arg to put request
set observe property to events snippet from data-storage.service
    storeRecipes() {
        const token = this.authService.getToken();
        const recipes = this.recipeService.getRecipes();
        return this.httpService.put(
            'https://ng-recipe-book-47065.firebaseio.com/recipes.json?auth=' + token, recipes,
            { observe: 'events' });
    }
        
the header component subscribes to the events
    onSaveData() {
        this.dataStorageService.storeRecipes()
            .subscribe(
                (event: HttpEvent<object>) => {
                    console.log(event.type);
                }
            );
    }
        

Top

Index

settings headers
in options object can use headers property
in data-storage.service the function below appends a custom header to the request
    storeRecipes() {
        const token = this.authService.getToken();
        const recipes = this.recipeService.getRecipes();
        return this.httpService.put(
            'https://ng-recipe-book-47065.firebaseio.com/recipes.json?auth=' + token, recipes,
            {
                observe: 'events',
                headers: new HttpHeaders().append('foo', 'bar')
            });
    }
        

Top

Index

HTTP parameters
in data-storage.service the function below the query string has been removed from the URL
the parameters are passed in the options arg's params property
    storeRecipes() {
        const token = this.authService.getToken();
        const recipes = this.recipeService.getRecipes();
        return this.httpService.put(
            // 'https://ng-recipe-book-47065.firebaseio.com/recipes.json?auth=' + token, recipes,
            'https://ng-recipe-book-47065.firebaseio.com/recipes.json', recipes,
            {
                observe: 'events',
                headers: new HttpHeaders().append('foo', 'bar'),
                params: new HttpParams().set('auth', token)
            });
    }
        

Top

Index

progress
HttpClient's built-in HttpRequest does not support progress events
a manually created HttpRequest will support progress events
use HttpClient's request method to post the request
    import { Injectable } from '@angular/core';
    import { HttpClient, HttpHeaders, HttpParams, HttpRequest } from '@angular/common/http';
    ...

    @Injectable()
    export class DataStorageService {
        constructor(
            private httpService: HttpClient,
            private recipeService: RecipeService,
            private authService: AuthService) { }

        storeRecipes() {
            const token = this.authService.getToken();
            const url = 'https://ng-recipe-book-47065.firebaseio.com/recipes.json';
            const recipes = this.recipeService.getRecipes();
            const req = new HttpRequest('PUT', url, recipes, {
                reportProgress: true,
                params: new HttpParams().set('auth', token)
            });
            return this.httpService.request(req);
        }
        ...
    }
        

Top

Index

interceptors
typical use case to do something with every http request
example is adding auth token to request params
create new file in shared directory
    import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
    import { Observable } from 'rxjs/Observable';

    export class AuthInterceptor implements HttpInterceptor {
        intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
            console.log('intercepted :', req);
            // call to let request continue its journey
            return next.handle(req);
        }
    } 
        
in core.module add AuthInterceptor to providers array property using JSON data
format show in comment below
    import { NgModule } from '@angular/core';
    import { HTTP_INTERCEPTORS } from '@angular/common/http';

    ...
    import { AuthInterceptor } from '../shared/auth.interceptor';

    @NgModule({
        declarations: [
            ...
        ],
        imports: [
            ...
        ],
        exports: [
            ...
        ],
        providers: [
            ShoppingListService,
            RecipeService,
            DataStorageService,
            AuthService,
            AuthGuard,
            // purpose of provider, type to use, multiple instances?
            { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }
        ]
    })
    export class CoreModule { }
        

Top

Index

modifying requests in interceptors
HttpRequests are immutable
to change a request it must be cloned
the clone method takes a configuration info as JSON data
add Injectable decoration to AuthInterception
inject AuthService to be able to get the token
clone the request passing a configuration object
set the auth param to the token using the request's param's set method
in the cloned request's configuration object set the params property to the modified request params
    import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
    import { Observable } from 'rxjs/Observable';
    import { Injectable } from '@angular/core';

    import {AuthService} from '../auth/auth.service';


    @Injectable()
    export class AuthInterceptor implements HttpInterceptor {

        constructor(private authService: AuthService) {}

        intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
            console.log('intercepted :', req);
            const token = this.authService.getToken();
            const copiedRequest = req.clone({ params: req.params.set('auth', token) });
            // call to let request continue its journey
            return next.handle(copiedRequest);
        }
    }
        

Top

Index

multiple interceptors
add new file logging.interceptor.ts in shared directory
when the call to next.handle returns the chained do method logs the event
    import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
    import { Observable } from 'rxjs/Observable';
    import { Injectable } from '@angular/core';
    import 'rxjs/add/operator/do';

    export class LoggingInterceptor implements HttpInterceptor {
        intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
            console.log('intercepted :', req);
            return next.handle(req).do(
                event => {
                    console.log('logging interceptor :', event);
                }
            );
        }
    }
        
in core.module add the new interceptor as a provider
    import { NgModule } from '@angular/core';
    import { HTTP_INTERCEPTORS } from '@angular/common/http';
    ...
    import { AuthInterceptor } from '../shared/auth.interceptor';
    import { LoggingInterceptor } from '../shared/logging.interceptor';

    @NgModule({
        declarations: [
            ...
        ],
        imports: [
            ...
        ],
        exports: [
            ...
        ],
        providers: [
            ...
            // order here determines chain
            { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true },
            { provide: HTTP_INTERCEPTORS, useClass: LoggingInterceptor, multi: true }
        ]
    })
    export class CoreModule { }
        

Top

Index

n4jvp.com