In this article, I am going to show you the real time uses of Custom Attribute. I have used Custom Attributes in 2 places, and they are,
- To secure the HTTP requests
- Show/Hide spinner during the service call
To secure the HTTP requests
Let say we have a requirement to validate every HTTP request before processing the request. The best solution I can think of is to extend the Http class and make a custom Http provider which automatically adds the authentication token to every http request. I understand that Angular2 is having multiple providers for ConnectionBackend, they are XHRBackend and JSONPBackend_. Here I am going to use XHRBackend from @angular/http module.
Create Custom Http Class
First, let's create our own custom Http provider class by extending Http class and then write our own business logic as per the need. If you observe the below code, I have created a class by extending Http class and named it as "HttpService". Inside the constructor I have configured the token to the request options and then called the Http class's constructor with the keyword super by passing the configured request options as an input parameter to the Http class's constructor.
In the same way, we can create our own HTTP requests (Get, Post, etc.) as per the required business logic and then inside the HTTP requests, call the base class's method by using super keyword. In the below code snippet, I have created a method called "get" and applied the token to both "url" and "RequestOptionsArgs" as per my need and then called the base class's "get" method. Similarly you can create your own HTTP request methods
http.interceptor.ts
- import { Injectable } from '@angular/core';
- import {
- Http,
- ConnectionBackend,
- RequestOptions,
- RequestOptionsArgs,
- Response
- } from '@angular/http';
- import { Observable } from 'rxjs/rx';
-
- @Injectable()
- export class HttpService extends Http {
- constructor(backend: ConnectionBackend, defaultOptions: RequestOptions) {
- let _token = localStorage.getItem('auth_token');
- defaultOptions.headers.set('Authorization', 'Bearer ${_token}');
- super(backend, defaultOptions);
- }
-
- get(url: string, optionsArgs?: RequestOptionsArgs): Observable<Response> {
- console.log("Interceptor: ", url, optionsArgs);
-
-
-
-
-
-
-
-
-
-
-
-
- return super.get(url, optionsArgs).catch(this.catchException(this));
- }
- private catchException (self: HttpService) {
- return (resp: Response) => {
- console.log(resp);
- if (resp.status === 401 || resp.status === 403) {
-
- console.log(resp);
- }
- return Observable.throw(resp);
- };
- }
- }
Configure the Custom Http Class
We have learned the creation of custom Http class. To use the created custom Http class, now we have to configure our main module to provide the XHRBackend to our own custom Http class. In your main module declaration, add the following to the providers array:
app.module.ts
- import { NgModule } from '@angular/core';
- import { HttpModule, XHRBackend, RequestOptions } from '@angular/http';
- import { HttpService } from './http.interceptor';
- -------------
- -------------
- -------------
- @NgModule({
- imports: [......],
- declarations: [......],
- bootstrap: [AppComponent],
- providers: [UserService, AuthService, RouteGaurd, NewUserGaurd,
- {
- provide: HttpService,
- useFactory: function httpServiceFactory(backend: XHRBackend,
- defaultOptions: RequestOptions) {
- return new HttpService(backend, defaultOptions);
- },
- deps: [XHRBackend, RequestOptions]
- }
- ]
- })
- export class AppModule { }
To use the custom Http class, import the custom Http class and then inject in the constructor as a parameter. After that, we can now use our custom Http provider in our services. For example,
user.service.ts
- import { Injectable } from '@angular/core';
- import { HttpService } from '../http.interceptor';
- import { Observable } from 'rxjs/Observable';
- import 'rxjs/Rx';
-
- @Injectable()
- export class UserService {
- private _serviceURL = 'http://jsonplaceholder.typicode.com/';
- private users: any[] = [];
- count = 0;
- constructor(private _http: HttpService) { }
-
- getUsers(): Observable<any> {
- this.count += 1;
- console.log(this.count);
-
- let url = this._serviceURL + 'users';
- return this._http.get(url)
- .map((response) => response.json())
- .catch((error) => {
- console.log("User Service - Error");
- return Observable.throw(error || 'Server error: User Service failed to retrieve information.')
- });
- }
- }
Output
After executing the above code, observe the below screenshot, our custom Http class called and log the message in browser console,
Show/Hide spinner during the service call
For this example, I am not going to discuss each block of code because the below code snippets are written in the same way which I have shown you for the above example:
preloader-service.ts
- import { Injectable } from '@angular/core';
-
- @Injectable()
- export class PreloaderService {
- public static fullLoadingCount: number = 0;
- public static smallLoadingCount: number = 0;
-
- getPreloaderCount(preloaderType = 'full'): number {
- if (preloaderType === 'full') {
- return PreloaderService.fullLoadingCount;
- } else if (preloaderType === 'small') {
- return PreloaderService.smallLoadingCount;
- }
- }
-
- showPreloader(preloaderType = 'full'): void {
- if (preloaderType === 'full') {
- PreloaderService.fullLoadingCount++;
- } else if (preloaderType === 'small') {
- PreloaderService.smallLoadingCount++;
- }
- }
-
- hidePreloader(preloaderType = 'full'): void {
- if (preloaderType === 'full') {
- PreloaderService.fullLoadingCount--;
- } else if (preloaderType === 'small') {
- PreloaderService.smallLoadingCount--;
- }
- }
- }
http-service.ts
- import { Injectable } from '@angular/core';
- import { Http, ConnectionBackend, Request, RequestOptions, RequestOptionsArgs, Response, Headers } from '@angular/http';
- import { Observable } from 'rxjs/Observable';
- import 'rxjs/add/observable/throw';
- import 'rxjs/Rx';
- import { PreloaderService } from './preloader-service';
- import { Config } from '../config';
-
- @Injectable()
- export class HttpService extends Http {
-
- constructor(backend: ConnectionBackend, defaultOptions: RequestOptions,
- private preloaderService: PreloaderService) {
- super(backend, defaultOptions);
- }
-
-
-
-
-
-
-
- request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {
- return super.request(url, options);
- }
-
-
-
-
-
-
-
-
- get(url: string, options?: RequestOptionsArgs, preloaderType?: string): Observable<any> {
- this.requestInterceptor(preloaderType);
- let fullUrl = this.getFullUrl(url);
-
- return super.get(fullUrl, this.requestOptions(options))
- .catch(this.onCatch)
- .do((res: Response) => {
- this.onSubscribeSuccess(res);
- }, (error: any) => {
- this.onSubscribeError(error);
- })
- .finally(() => {
- this.onFinally(preloaderType);
- });
- }
-
-
-
-
-
-
- private getFullUrl(str): string {
- return Config.api + str;
- }
-
-
-
-
- private requestInterceptor(preloaderType = 'full'): void {
- this.preloaderService.showPreloader(preloaderType);
- }
-
-
-
-
- private responseInterceptor(preloaderType = 'full'): void {
- this.preloaderService.hidePreloader(preloaderType);
- }
-
-
-
-
-
-
- private onCatch(error: any): Observable<any> {
- console.log('onCatch');
- return Observable.throw(error);
- }
-
-
-
-
- private onFinally(preloaderType = 'full'): void {
- this.responseInterceptor(preloaderType);
- }
- }
preloader-small.ts
- import { Component } from '@angular/core';
- import { PreloaderService } from '../../services/preloader-service';
-
- @Component({
- selector: 'preloader-small',
- styleUrls: [
- './preloader-small.scss'
- ],
- templateUrl: './preloader-small.html'
- })
- export class PreloaderSmall {
- constructor(public preloaderService: PreloaderService) {
- }
- }
preloader-small.html
- <div class="preloader-small"
- *ngIf="preloaderService.getPreloaderCount('small') > 0 && preloaderService.getPreloaderCount('full') == 0"
- ></div>
preloader-full.ts
- import { Component } from '@angular/core';
- import { PreloaderService } from '../../services/preloader-service';
-
- @Component({
- selector: 'preloader-full',
- styleUrls: [
- './preloader-full.scss'
- ],
- templateUrl: './preloader-full.html'
- })
- export class PreloaderFull {
- constructor(public preloaderService: PreloaderService) {
- }
- }
preloader-full.html
- <div class="preloader-full"
- *ngIf="preloaderService.getPreloaderCount('full') > 0">
- </div>
home.component.ts
- import { Component } from '@angular/core';
- import { PostService } from '../../services/post-service';
-
- @Component({
- selector: 'home',
- templateUrl: './home.component.html'
- })
- export class HomeComponent {
- public posts = [];
-
- constructor(private postService: PostService) {
- this.postService.getPosts('full');
- }
-
- ngOnInit() {
- this.postService.posts.subscribe(data => {
- if (data) {
- this.posts = _.concat(this.posts, data);
- }
- });
- }
-
- getPosts(preloaderType) {
- this.postService.getPosts(preloaderType);
- }
- }
home.component.html
- <h2>Latest posts</h2>
-
- <div *ngIf="posts">
- <p *ngFor="let post of posts;">{{post.title}}</p>
- </div>
-
- <preloader-small></preloader-small>
-
- <button class="courses__load-more"
- (click)="getPosts('small')">
- Load with small preloader
- </button>
-
- <button class="courses__load-more"
- (click)="getPosts('full')">
- Load with full preloader
- </button>
Output
Once the page loads, when we click on "Load with small preloader" button,
Below is the output when we click on "Load with full preloader" button,
I have uploaded the entire code in zip format, you can the download the code and play around with it.
Thanks for reading.