My learning curve with Angular

Image for post
Image for post

Introduction 🖊:

Happy learning :)

Image for post
Image for post

1) Recommended Angular project structure : ⛑

Image for post
Image for post
  • Modules:

This is the folder dedicated to features implemented inside the application, for example, if you have an Authentication module to handle all the (sign-in) and (signup feature), Module: Dashboard if you have feature Dashboard management, they can all be set under the folder “Modules” or “Features”

  • Core:

⚡ The core module is dedicated to containing all elements that continue to core like services, interceptors , guards , mocks , helpers , utils etc ..

  • ⚠️ The core module must be imported only in the root module. Other modules must not import the core modules. You can use the following code to stop the other modules from importing the core module
@NgModule({})export class CoreModule {constructor(@Optional() @SkipSelf() core:CoreModule ){if (core) {throw new Error("You should import core module only in the root module")}}}
  • Shared :

The shared module must declare the pipes, directives, services and alll the components and modules shared among different pages, such as a footer or the Material modules. You must declare them or import them in the _shared module, as well as export them.

@NgModule({imports: [CommonModule,FormsModule,ReactiveFormsModule,HttpClientModule,RouterModule,NgbModule,],exports: [CommonModule,FormsModule,ReactiveFormsModule,HttpClientModule,RouterModule,components,],declarations: [components, pipes, directives],providers: [],})export class SharedModule {}

2) Lazy Loading with Angular: ▶️ ◀️

Image for post
Image for post

Lazy loading is a strategy to identify resources as non-blocking (non-critical) and load these only when needed. It’s a way to shorten the length of the critical rendering path, which translates into reduced page load times.

So if you plan to implement a large scale application, it’s worthless to implement the lazy loading pattern, before to start coding you need to divide your features into modules and make some hierarchy for your application

Image for post
Image for post

Utility? ✔️✔️ ✔:

  • Generates a better user experience by shortening load times and only loading necessary info.
Image for post
Image for post

Implementation:

This is an example of a lazy loading implementation feature with Angular10

export const routes: Routes = [{ path: 'admin', loadChildren: () => import(`./admin/admin.module`).then(m =>m.AdminModule) },{ path: 'auth', loadChildren: () => import(`./authentication/authentication.module`).then(m => m.AuthenticationModule) },{path: '**',redirectTo: 'auth'}]

3) TypeScript module resolution with AngularCLI:🛠

You need to add paths to your tsconfig.json to make it easy to import your modules.!

"paths": {"@features/*": ["src/modules/*"],"@core/*": ["src/app/core/*"],"@shared/*": ["src/app/shared/*"],"@configs/*": ["src/configs/*"],"@angular/core/src/metadata/*": ["./node_modules/@angular/core"]}

4) Sub routing : 🏹🏹

What is Sub-routing :

const routes: Routes = [{path: '',redirectTo: 'dashboard',},{path: 'dashboard',component: DashboardComponent,children: [{path: 'test',component: DashboardComponent,},],},];

In our application, we will have a main root ( root route), and other routes for each module for its own children (component of course )

Image for post
Image for post

Implementation:

Routing Authentication Module :

import { NgModule } from '@angular/core';import { Routes, RouterModule } from '@angular/router';import { LoginComponent } from './login/login.component';import { SignupComponent } from './signup/signup.component';const routes: Routes = [{ path: '', component: LoginComponent },{ path: 'signup', component: SignupComponent }];@NgModule({imports: [RouterModule.forChild(routes)],exports: [RouterModule]})export class AuthenticationRoutingModule { }

Routing Admin Module :

import { NgModule } from '@angular/core';import { Routes, RouterModule } from '@angular/router';
import { DashboardComponent } from './dashboard/dashboard.component';
const routes: Routes = [{path: '',redirectTo: 'dashboard',},{path: 'dashboard',component: DashboardComponent,children: [{path: 'test',component: DashboardComponent,},],},];@NgModule({imports: [RouterModule.forChild(routes)],exports: [RouterModule],})export class AdminRoutingModule {}

Task: navigation from sign-up — -> dashboard?

Solution :

this._router.navigate([‘admin/dashboard’]);

5) Advanced Interceptors to our Angular project:📞

What is an interceptor :

Image for post
Image for post

There are a lot of uses cases that I discovered and it looks interesting to me that’ why I want to share here :

  • HTTP Error Handler : 💥 💥

First, we can retry the HTTP call.

constructor(private toastr: ToastrService) {}intercept(req: HttpRequest<any>,next: HttpHandler): Observable<HttpEvent<any>> {if (!req.url.includes('error')) {return next.handle(req);}console.warn('ErrorInterceptor');return next.handle(req).pipe(retry(2),

Secondly, we can check the status of the exception. And depending on the status, we can decide what we should do.

catchError((error: HttpErrorResponse) => {if (error.error instanceof ErrorEvent) {console.error('An error occurred:', error.error.message);} else {switch (error.status) {case 400:console.log('400 Error Server !');break;case 405:console.log('405 Error Server !');break;case 500:console.log('500 Error Server !');break;case 403:console.log('403 Error Server !');break;default:console.log('Unknown  Error Server !');}}return throwError(error);})
  • Converting CSV TO JSON : 💥 💥

This an example of an Interceptor that will convert from some HTTTP Calls that will get a CSV format response and you will need it to display it JSON, so let’s intercept it!

import { Injectable } from '@angular/core';import {HttpInterceptor,HttpEvent,HttpHandler,HttpRequest,HttpResponse,} from '@angular/common/http';import { Observable } from 'rxjs';import { map } from 'rxjs/operators';@Injectable()export class ConverterInterceptor implements HttpInterceptor {intercept(req: HttpRequest<any>,next: HttpHandler): Observable<HttpEvent<any>> {if (!req.url.includes('/api/csv')) {return next.handle(req);}console.warn('ConvertInterceptor');return next.handle(req).pipe(map((event: HttpEvent<any>) => {if (event instanceof HttpResponse) {// --Convert body from csv to jsonconst jsonData = this.CsvToJSON(event.body);const modifiedBody = event.clone({ body: jsonData });return modifiedBody;}}));}CsvToJSON(csv) {const lines = csv.split('\n');const result = [];const headers = lines[0].split(',');for (var i = 1; i < lines.length; i++) {const obj = {};const currentLine = lines[i].split(',');for (let j = 0; j < headers.length; j++) {obj[headers[j]] = currentLine[j];}result.push(obj);}return JSON.stringify(result);}}
  • Caching: 💥

Another cool use case is HTTP response caching ,

import { Injectable } from '@angular/core';import {HttpInterceptor,HttpEvent,HttpHandler,HttpRequest,HttpResponse,} from '@angular/common/http';import { Observable, of } from 'rxjs';import { tap } from 'rxjs/operators';@Injectable()export class CacheInterceptor implements HttpInterceptor {private cache = new Map<string, any>();intercept(req: HttpRequest<any>,next: HttpHandler): Observable<HttpEvent<any>> {if (!req.url.includes('/api/cache')) {return next.handle(req);}console.warn('CacheInterceptor');if (req.method !== 'GET') {return next.handle(req);}const cachedResponse = this.cache.get(req.url);if (cachedResponse) {return of(cachedResponse);}return next.handle(req).pipe(tap((event) => {if (event instanceof HttpResponse) {this.cache.set(req.url, event);}}));}}

7) Auth Guard :

Image for post
Image for post

In this section, I will try to explain some basic examples of the ‘Auth Guard ‘ concept used to check access and authorization for a route based on a role!

There are five different types of Guards:

  • CanActivate: checks to see if a user can visit a route.
  • CanActivateChild: checks to see if a user can visit a route’s children.
  • CanDeactivate: checks to see if a user can exit a route.
  • Resolve: performs route data retrieval before route activation.
  • CanLoad: checks to see if a user can route to a module that is lazy loaded.
canActivateChild(next: ActivatedRouteSnapshot,state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {// redirect and return falseif (!this.auth.isSuperAdmin) {this.router.navigate(['']);return false;}return true;}

And the next step is to Add it to the routing module :

const routes: Routes = [{path: '',redirectTo: 'dashboard',},{path: 'dashboard',component: DashboardComponent,children: [{path: 'super-user',component: SuperUserComponent,canActivateChild: [AdminGuard],},],},];

8) Rxjs 📕 :

Image for post
Image for post

In this section, I will explain some Rxjs used operators that are so helpful for javascript developers and I thinks we should care about this powerful library

Whats is Rxjs 📕 :

it is defined as a library for composing asynchronous and event-based programs by using observable sequences. It provides one core type, the Observable, satellite types (Observer, Schedulers, Subjects) and operators inspired by Array#extras (map, filter, reduce, every, etc.) to allow handling asynchronous events as collections.

Image for post
Image for post

Some Rxjs operations:

“An observable is a function that creates an observer and attaches it to the source where values are expected, for example, clicks, mouse events from a dom element or an HTTP request, etc.”

  • You can often use observables instead of promises to deliver values asynchronously.

Example implementation :

search(term:string): Observable<SearchItem[]> {
let apiURL = `${this.apiRoot}?term=${term}&media=music&limit=20`;
return this.http.get(apiURL)
}
  • forkJoin :

This operator is best used when you have a group of observables and only care about the final emitted value of each.

image houni

Example implementation:

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { forkJoin } from 'rxjs'; // RxJS 6 syntax

@Injectable()
export class DataService {

constructor(private http: HttpClient) { }

public requestDataFromMultipleSources(): Observable<any[]> {
let response1 = this.http.get(requestUrl1);
let response2 = this.http.get(requestUrl2);
let response3 = this.http.get(requestUrl3);
// Observable.forkJoin (RxJS 5) changes to just forkJoin() in RxJS 6
return forkJoin([response1, response2, response3]);
}
}
  • retry:

-Useful for retrying HTTP requests!

  • If you want to repeat FAILED data fetching wrapped in observable — use retry.

Implementation :

import { Http } from '@angular/http';import { Injectable } from '@angular/core';import { Observable } from 'rxjs/Rx';@Injectable()export class SearchService {constructor(private http: Http) {}search(term: string) {let tryCount = 0;return this.http.get('https://api.spotify.com/v1/dsds?q=' + term + '&type=artist').map(response => response.json()).retry(3);}}
  • catch / catchError:

CatchError is an RxJs Operator, which we can use to handle the errors thrown by the Angular Observable.

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

private SERVER = "http://server.com/api/products";

constructor(private httpClient: HttpClient) { }

handleError(error: HttpErrorResponse) {
let errorMessage = 'Unknown error!';
if (error.error instanceof ErrorEvent) {
// Client-side errors
errorMessage = `Error: ${error.error.message}`;
} else {
// Server-side errors
errorMessage = `Error Code: ${error.status}\nMessage: ${error.message}`;
}
window.alert(errorMessage);
return throwError(errorMessage);
}

public fetchData(){
return this.httpClient.get(this.SERVER).pipe(catchError(this.handleError));
}
}

9) Never forgot to write Clean Code : ❎

Image for post
Image for post

Write readable code. Focus on writing code that is easy to understand.

Separation of concerns. Encapsulate and limit logic inside your components, services, and directives. Each file should only be responsible for a single functionality.

Utilize TypeScript. TypeScript provides advanced autocompletion, navigation, and refactoring. Having such a tool at your disposal shouldn’t be taken for granted.

Use TSLint. TSLint checks if TypeScript code complies with the coding rules in place. Combined with Prettier and Husky makes for an excellent workflow.

Next Step Add state management to my App?

Image for post
Image for post

In the Next Blog, I will talk about State Management with Angular application: concepts, advantages, implementations, best tools, and some discussion about when we should add state management to my app.

Conclusion :

“Develop a passion for learning. If you do, you will never cease to grow.”

You can find the Github repository called learning-angular-advanced ✌️✌️

Image for post
Image for post

Written by

<script>alert('try your best')</script>

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store