Click here to Skip to main content
15,868,016 members
Articles / Web Development / HTML

Learn Angular Tutorial - Part 5

Rate me:
Please Sign up or sign in to vote.
5.00/5 (6 votes)
22 Sep 2017CPOL10 min read 18.3K   437   10   1
Implementing lazy routing and using Jquery with Angular
This is Part 5 of Learn Angular Tutorial. In this part, we will see how to implement Lazy loading and how to use JQuery with Angular.

Contents

Links to Rest of the Articles

  • In the first part, we look into Node, TypeScript, Module loaders/Bundlers and VS Code.
  • In the second article, we create a simple basic Angular application with a screen and we run through important concepts like components and modules.
  • In the third article, we look into implementing SPA and validations.
  • In the fourth article, we understand how to make HTTP calls and how to create custom Angular components using Input and Outputs.
  • In the fifth part, we cover two labs - one is how to use Jquery with Angular and Lazy routing.
  • In the sixth part, we again cover two labs - Pipes and DI using providers.

In this article, we will see how to implement lazy routing and how to use JQuery with Angular.

Lab 11: Lazy Loading Using Dynamic Routes

Theory

Big projects will have lot of components and modules, in other words, we will end up with lot of JS files on the browser client side. Loading these JS files in ONE GO at the client browser side would really hit performance.

If you load the current application at this stage and see the developer tools you will see on the network tab all JS files are getting loaded at the start.

When the user comes to the site for the first time, we would like to just load the welcome component and master component JS only.

When the user clicks on supplier and customer, respective JS files should be loaded at that moment.

Image 1

Let’s investigate who is the culprit?

If you see the current architecture of our project, we have one module (MainModule.ts) and all components currently belong to this only ONE Module. So when this module loads it loads all components inside it.

In simple words, we need to BREAK MODULES into separate physical module files.

Image 2

Step 1: Creating Three Different Physical Modules

So as discussed in the previous part of the theory, we need to first divide our project into three different physical module files: MainModule, SupplierModule and CustomerModule.

Image 3

So in the module folder, let's create three different physical module files. We already have MainModule.ts, we need to create two more.

MainModule.ts: This module will load “MasterPageComponent.ts” and “WelcomeComponent.ts”.

SupplierModule.ts: This module will load “SupplierComponent.ts”.

CustomerModule.ts: This will load CustomerComponent and GridComponent. Remember grid is used only in Customer UI so this should load only when Customer functionality is loaded.

Image 4

Step 2: Removing Supplier and CustomerComponent from MainModule

The first thing is we need to remove all references of CustomerComponent, SupplierComponent and GridComponent from the MainModule. Below is the striked out source code which needs to be removed from the MainModule. In the main module, we only have reference to WelcomeComponent and MastePageComponent.

Two modules are decoupled from each other when import does not exist between those modules. Even if you do not use the component and there is an import, decoupling is not complete and the JS will be loaded.

JavaScript
Lot of Code has been removed for clarity. Please download source code
for full code.
import { CustomerComponent }   from '../Component/CustomerComponent';
import { SupplierComponent }   from '../Component/SupplierComponent';
import { WelcomeComponent }   from '../Component/WelcomeComponent';
import { GridComponent }   from '../Component/GridComponent';
import { MasterPageComponent }   from '../Component/MasterPageComponent';

@NgModule({
    imports: [RouterModule.forRoot(ApplicationRoutes), 
        InMemoryWebApiModule.forRoot(CustomerApiService),
             BrowserModule,ReactiveFormsModule,
             FormsModule,HttpModule],
    declarations: [CustomerComponent,
                MasterPageComponent,
                SupplierComponent,
                WelcomeComponent, 
                GridComponent],
    bootstrap: [MasterPageComponent]
})
export class MainModuleLibrary { }

Step 3: Creating Different Route Files

As said previously, “A SIMPLE IMPORT REFERENCE” will make two modules coupled. If the modules are coupled, those JS files will be loaded.

If you remember, “MainModule.ts” loads the Routes from “Routing.ts” and Routing.ts has import references to SupplierComponent and CustomerComponent.

So loading routing will load the other components as well and we will not be able to achieve lazy loading.

So let us remove all references of Customer and Supplier component from MainModule.ts, see the striked out code down below:

Image 5

JavaScript
import {Component} from '@angular/core';
import {CustomerComponent} from '../Component/CustomerComponent';
import {SupplierComponent} from "../Component/SupplierComponent";
import {WelcomeComponent} from "../Component/WelcomeComponent";
export const ApplicationRoutes = [
    { path: 'Customer', component: CustomerComponent },
    { path: 'Supplier', component: SupplierComponent },
     { path: '', component:WelcomeComponent  },
    { path: 'UI/Index.html', component:WelcomeComponent  }
];

But still, we need to still define routes for “Customer” and “Supplier” and the same time, not use “import” statement as that makes the module coupled. If you look at the current syntax of defining route, we need to have that component in the import or else, we cannot define the route.

JavaScript
{ path: 'CustomerComponent', component:CustomerComponent  },

For that, Angular has given a nice property called as “loadChildren”. In “loadChildren”, we need to give the module in a single quote like a string. Which means that this thing will be evaluated during run time and now compile time.

JavaScript
{ 
path: 'Customer',
loadChildren:'../Module/CustomerModuleLibrary#CustomerModuleLibrary' 
}

The structure of “loadChildren” should follow this pattern:

Image 6

The full code of the route will look something as shown below:

JavaScript
import {Component} from '@angular/core';
import {WelcomeComponent} from "../Component/WelcomeComponent";
export const ApplicationRoutes = [
    { path: 'Customer', 
loadChildren:'../Module/CustomerModuleLibrary#CustomerModuleLibrary' },
    { path: 'Supplier', 
loadChildren: '../Module/SupplierModuleLibrary#SupplierModuleLibrary' },
     { path: '', component:WelcomeComponent  },
    { path: 'UI/Index.html', component:WelcomeComponent  },
    { path: 'UI', component:WelcomeComponent  }
];

We also need to create two more route files, one for “Customer” and one for “Supplier” as shown below:

JavaScript
import {Component} from '@angular/core';
import {CustomerComponent} from "../Component/CustomerComponent";
export const CustomerRoutes = [
    { path: 'Add', component:CustomerComponent  }
];
JavaScript
import {Component} from '@angular/core';
import {SupplierComponent} from "../Component/SupplierComponent";
export const SupplierRoutes = [
    { path: 'Add', component:SupplierComponent  }
];

SupplierRoutes” and “CustomerRoutes” are child routes while the “ApplicationRoutes” is the parent route.

Step 4: Calling Child Routes in Supplier and Customer Modules

In supplier module and customer modules, we need to load their respective routes defined in “Step 3”. To load child routes, we need to use “RouterModule.forChild”.

JavaScript
import { NgModule }      from '@angular/core';
import { CommonModule } from '@angular/common';
import {FormsModule , ReactiveFormsModule} from "@angular/forms"
import { SupplierComponent }   from '../Component/SupplierComponent';
import { RouterModule }   from '@angular/router';
import { SupplierRoutes }   from '../Routing/SupplierRouting';   
import {CustomerApiService} from "../Api/CustomerApi"
@NgModule({
    imports: [RouterModule.forChild(SupplierRoutes),
             CommonModule,ReactiveFormsModule,
             FormsModule],
    declarations: [SupplierComponent],
    bootstrap: [SupplierComponent]
})
export class SupplierModuleLibrary { }

Same way we need to for Customer Module.

JavaScript
import { NgModule }      from '@angular/core';
import { CommonModule } from '@angular/common';
import {FormsModule , ReactiveFormsModule} from "@angular/forms"
import { CustomerComponent }   from '../Component/CustomerComponent';
import { GridComponent }   from '../Component/GridComponent';
import { RouterModule }   from '@angular/router';
import { CustomerRoutes }   from '../Routing/CustomerRouting';   
import { InMemoryWebApiModule } from 'angular2-in-memory-web-api';
import {CustomerApiService} from "../Api/CustomerApi"
import { HttpModule } from '@angular/http';

@NgModule({
    imports: [RouterModule.forChild(CustomerRoutes), 
        InMemoryWebApiModule.forRoot(CustomerApiService),
             CommonModule,ReactiveFormsModule,
             FormsModule,HttpModule],
    declarations: [CustomerComponent, 
                GridComponent],
    bootstrap: [CustomerComponent]
})
export class CustomerModuleLibrary { }

Step 5: Configuring routerlinks

In step 3 and 4, we have defined parent routes and child routes. Parent routes are defined in “Routing.ts” while child routes are defined in “CustomerRouting.ts” and “SupplierRouting.ts”. So now the router link has to be changed to “Supplier/Add” and “Customer/Add” as shown in the below code.

Image 7

HTML
<a [routerLink]="['Supplier/Add']">Supplier</a> <br />
<a [routerLink]="['Customer/Add']">Customer</a><br>

Now, the full code look of master page looks as shown below:

HTML
<table border="0" width="100%">
<tr>
<td width="20%"><img src="http://www.questpond.com/img/logo.jpg" alt="Alternate Text" />
</td>
<td width="80%">Questpond.com Private limited</td>
</tr>
<tr>
<td valign=top>Left Menu<br />
<a [routerLink]="['Supplier/Add']">Supplier</a> <br />
<a [routerLink]="['Customer/Add']">Customer</a><br>
<a [routerLink]="['']">Home</a>
</td>
<td>
<div id="dynamicscreen">
<router-outlet></router-outlet>
</div>
</td>
</tr>
<tr>
<td></td>
<td>Copy right @Questpond</td>
</tr>
</table>

Step 6: Replacing Browser Module With Common Module

BrowserModule” and “CommonModule” are modules of Angular. “BrowserModule” has code which starts up services and launches the application while “CommonModule” has directives like “NgIf” and “NgFor”.
BrowserModule” re-exports “CommonModule”. Or if I put in simple words, “BrowserModule” uses “CommonModule”. So if you are loading “BrowserModule”, you are loading “CommonModule” as well.

So now if you are loading “BrowserModule” in all three modules, then you will end uploading “CommonModule” three times. When you are doing Lazy Loading, you really do not want to load things three times, it should be loaded only once.

So if you have “BrowserModule” in all three modules, then you would end up getting such kind of error as shown in the below figure.

This error says “BrowserModule” has already been loaded in the “MainModule”. Please use “CommonModule” in “CustomerModule” and “SupplierModule”.

Image 8

Image 9

So in main module, load the browser module and in the rest of the modules, load “CommonModule”.

JavaScript
import { BrowserModule } from '@angular/platform-browser';
// Other imports have been removed for clarity
@NgModule({
    imports: [RouterModule.forRoot(ApplicationRoutes),
        InMemoryWebApiModule.forRoot(CustomerApiService),
             BrowserModule,ReactiveFormsModule,
             FormsModule,HttpModule],
    declarations: [
                MasterPageComponent,
                WelcomeComponent],
    bootstrap: [MasterPageComponent]
})
export class MainModuleLibrary { }

But in customer and supplier modules, just load common module.

JavaScript
import { CommonModule } from '@angular/common';
// other imports has been removed for clarity

@NgModule({
    imports: [RouterModule.forChild(SupplierRoutes),
             CommonModule,ReactiveFormsModule,
             FormsModule],
    declarations: [SupplierComponent],
    bootstrap: [SupplierComponent]
})
export class SupplierModuleLibrary { }
JavaScript
import { CommonModule } from '@angular/common';
// Other import has  been removed for claroty
@NgModule({
    imports: [RouterModule.forChild(CustomerRoutes),
        InMemoryWebApiModule.forRoot(CustomerApiService),
             CommonModule,ReactiveFormsModule,
             FormsModule,HttpModule],
    declarations: [CustomerComponent,
                GridComponent],
    bootstrap: [CustomerComponent]
})
export class CustomerModuleLibrary { }

Step 7: Check if Lazy Loading is Working

Now run your application, go to network tab and check if lazy loading is working. You can see when the application runs at the start, only “WelcomeComponent” and “MasterPageComponent” is loaded. Once you click on supplier and customer, the respective components will be loaded at that time.

Please put a proper filter so that you do not see all JS files in your network.

Image 10

Lab 12: Using jQuery with Angular

Introduction

jQuery is a very old and trusted JavaScript framework. It has lot of UI components which are stable and trusted. As a practice, you should not use jQuery with Angular because both of them can overlap with DOM manipulation causing confusion.

jQuery manipulates HTML DOM directly using “$” syntaxes while Angular creates a sugar-coated DOM over HTML DOM and does the manipulation via this sugar-coated DOM.

So jQuery can manipulate HTML DOM without Angular not having knowledge about this manipulation creating more confusion.

Image 11

But then, there are instances when we would like to use jQuery UI components like jQuery grids, jQuery calendar and so on which probably are not available in Angular.

If Angular is your prime framework, then first see that you get your solution inside Angular, if not then use jQuery or any other framework.

In this lab, we will use jQuery to fade away and fade in our grid component. So let’s create a button with name Hide Grid. When the end user clicks on Hide Grid, the grid should gradually become visible and invisible.

Image 12

Step 1: Install JQuery

So the first step is to get jQuery. Let’s fire up the node command and also let’s get jQuery as well as let's save the entry into “package.json” file.

JavaScript
npm install jquery –save

Step 2: Install JQuery typings

JavaScript is divided into two generations, one generation before typescript, i.e., pure JavaScript and other generation after typescript. JavaScript is a dynamic and an untyped language while typescript is strongly typed. We can call methods which do not exist, assign variables which are not created as so on.

On the other side, typescript is strongly typed. Everything is done during design time / compile time, typescript has to know the methods, parameters everything upfront.

Now frameworks like jQuery are made in pure JavaScript, so if they need to be used in typescript, we need to expose their types, parameters and so on. That’s where we need to create typing files. Typings are typescript files which expose the shape and structure of JavaScript objects so that typescript can understand JavaScript types during design time.

Image 13

You must be wondering so do we need to create the typing’s file manually? No, you do not need to. jQuery already has typings, on the contrary almost all popular JavaScript frameworks have their typings file.

So to get the jQuery typings, we need to do npm install by pointing at “@types/jquery”. In fact, you can load any types by using “@types” for example, if you want to load lodash, you can do “npm install” on “@types/lodash”.

JavaScript
npm install @types/jquery –save

Step 3: Providing IDs in the UI

jQuery references HTML UI elements using selectors. In selectors, we need to provide name or ids by which we can get reference of that HTML element. So let’s wrap our “grid” component inside a DIV tag and let's assign some “id” value to it , like the one we have given in the below code “divgrid”.

Also, we have created a button which calls the “Fade” methods from the component.

HTML
<input (click)="Fade()" type="button" value="Hide Grid"/>
<div id="divgrid">
<grid-ui 
      [grid-columns]="[{'colName':'CustomerCode'},
      {'colName':'CustomerName'},{'colName':'CustomerAmount'}]" 
    [grid-data]="Customers" 
    (grid-selected)="Select($event)"></grid-ui>
<div>

Step 4: Importing and using Jquery in Component

Now the first thing is to import jQuery into your component file. For that, we need to use the below code. You can see the import statement is bit differently used. We are using “*” and “as” keyword. “*” means we want to refer full JQuery file and “$” is the alias by which we will be referring jQuery syntax in the code.

JavaScript
import * as $ from "jquery";

Once jQuery has been imported, we can now use “$” to execute jQuery syntaxes. You can see that we have created a method called as “Fade” in which we are referring the HTML DIV ID and calling the “fadeToggle” method to fade in and out.

JavaScript
import * as $ from "jquery";
export class CustomerComponent {
   // Code removed for clarity
   Fade(){
        $("#divgrid").fadeToggle(3000);
    }
	   }

Step 5: Make Entry in to Systemjs.config.js

Our JS files are loaded using SystemJS module loader. So we need to make an entry into “Systemjs.config.js” file stating where the jQuery file is located.

JavaScript
'jquery': '../node_modules/jquery/dist/jquery.js'

If you see, we have specified the folder path as “dist”. “dist” stands for distribution folder. The final compiled copy of jQuery is in this folder so we have specified the same.

Now run the program and see the output. If you click on the “Hide” button, you should see the grid fading out and in.

For further reading, do watch the below interview preparation videos and step by step video series.

History

  • 22nd September, 2017: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect https://www.questpond.com
India India

Comments and Discussions

 
-- No messages could be retrieved (timeout) --