My learning curve with Angular

Image for post
Image for post

Introduction 🖊:

Image for post
Image for post

1) Recommended Angular project structure : ⛑

Image for post
Image for post
@NgModule({})export class CoreModule {constructor(@Optional() @SkipSelf() core:CoreModule ){if (core) {throw new Error("You should import core module only in the root module")}}}
@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
Image for post
Image for post

Utility? ✔️✔️ ✔:

Image for post
Image for post
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:🛠

"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,},],},];
Image for post
Image for post

Implementation:

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 { }
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 {}
this._router.navigate([‘admin/dashboard’]);

5) Advanced Interceptors to our Angular project:📞

What is an interceptor :

Image for post
Image for post
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),
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);})
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);}}
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
canActivateChild(next: ActivatedRouteSnapshot,state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {// redirect and return falseif (!this.auth.isSuperAdmin) {this.router.navigate(['']);return false;}return true;}
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

Whats is Rxjs 📕 :

Image for post
Image for post

Some Rxjs operations:

search(term:string): Observable<SearchItem[]> {
let apiURL = `${this.apiRoot}?term=${term}&media=music&limit=20`;
return this.http.get(apiURL)
}
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]);
}
}
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);}}
@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

Next Step Add state management to my App?

Image for post
Image for post

Conclusion :

Image for post
Image for post

<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