Getting started with Webpack in Angular Application
19 Dec 202224 minutes to read
To quick start with Syncfusion JavaScript Angular components run the below commands to clone the repository for Webpack starter and installing required dependency packages.
> git clone https://github.com/syncfusion/angular2-seeds
> cd angular2-seeds
> npm install
NOTE
The cloned application is fully configured to work with Essential Studio for JavaScript Angular components, in which we configured our ej-angular2 library and necessary changes to consume our Angular components.
NOTE
We already implemented the below Ahead-of-Time Compiler configuration in our Angular Seed. To quick start with AOT,clone the seed here to render the Syncfusion Angular Components.
Ahead-of-Time Compilation
The Angular Ahead-of-Time (AOT) compiler converts your Angular HTML and TypeScript code into efficient JavaScript code during the build phase before the browser downloads and runs that code.
AOT Compiler | JIT Compiler |
---|---|
The compiler runs once at build time using one set of libraries | It runs every time for every user at runtime using a different set of libraries. |
Speed in process | Late in process |
Why do Ahead-of-Time Compilation
- Faster Rendering
- Fewer asynchronous requests
- Smaller Angular framework download size
- Detect template errors earlier
- Better security
Configuration of Ahead-of-Time compiler in Webpack Seed
To start with the Ahead-of-Time Compiler, we need @angular/compiler-cli npm dependency. To install the dependency, run the below command in your application’s root directory.
npm install @angular/compiler-cli --save
It will install the ngc compiler to support the Ahead-of-Time Compilation.
NOTE
If you get an error like
TypeError:this.compiler.analyzeModulesAsync is not a function
, then the problem is with the mismatched versions of@angular/compiler-cli
andother angular packages
. So we suggest you to install@angular/compiler-cli
package in the same version of other angular packages in your application.
Configure @ngtools/webpack package in webpack configuration
The @ngtools/webpack
provides AOT Plugin and loader for Webpack which shares the context with other loaders/plugins. Run the below command to install NPM package @ngtools/webpack
.
npm install @ngtools/webpack --save-dev
The below steps depicts the configuration of @ngtools/webpack
in Angular application.
-
@ngtools/webpack
compiles the typescript files in application. It replaces other typescript loader likets-loader
orawesome-typescript-loader.
It works withAngularCompilerPlugin
together to enable AOT compilation.
Options for AngularCompilerPlugin
-
Add instance of AngularCompilerPlugin, which has an apply property. This apply property is called by the webpack compiler, giving access to the entire compilation life cycle. Add options
tsConfigPath
andÂentryModule
withAngularCompilerPlugin
instance.-
tsConfigPath
- The path to thetsconfig.json
file. In yourtsconfig.json
, you can pass options to the Angular Compiler withangularCompilerOptions
. -
entryModule
- Optional if specified inangularCompilerOptions
. The path and classname of the main application module. This follows the formatpath/to/file#ClassName
.
-
NOTE
To know more about Options in AOT refer here
Refer to the below AOT configuration code snippet for config/webpack.dev.js
file.
var helpers = require('./helpers');
const fs = require('fs');
const path = require('path');
const ProgressPlugin = require('webpack/lib/ProgressPlugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const rxPaths = require('rxjs/_esm5/path-mapping');
var webpack = require('webpack');
const { NoEmitOnErrorsPlugin, SourceMapDevToolPlugin, NamedModulesPlugin } = require('webpack');
const { CommonsChunkPlugin } = require('webpack').optimize;
var ExtractTextPlugin = require('extract-text-webpack-plugin');
const nodeModules = path.join(process.cwd(), 'node_modules');
const realNodeModules = fs.realpathSync(nodeModules);
const genDirNodeModules = path.join(process.cwd(), 'src', '$$_gendir', 'node_modules');
const entryPoints = ["inline", "polyfills", "sw-register", "styles", "vendor", "main"];
const { AngularCompilerPlugin } = require('@ngtools/webpack');
module.exports = {
// devtool: 'source-map',
"resolve": {
"extensions": [
".ts",
".js"
],
alias: {
jquery: "jquery/src/jquery"
},
"modules": [
"./node_modules",
"./node_modules"
],
"symlinks": true,
"mainFields": [
"browser",
"module",
"main"
]
},
"resolveLoader": {
"modules": [
"./node_modules",
"./node_modules"
],
"alias": rxPaths()
},
"entry": {
"main": [
"./src\\main.ts"
],
"polyfills": [
"./src\\polyfills.ts"
],
"vendor": [
"./src\\vendor.ts"
]
},
"output": {
"path": path.join(process.cwd(), "dist"),
"filename": "[name].bundle.js",
"chunkFilename": "[id].chunk.js",
"crossOriginLoading": false
},
"module": {
"rules": [
{
"test": /\.ts$/,
"loader": "@ngtools/webpack"
},
{
"test": /\.html$/,
"loader": "raw-loader"
},
{
"test": /\.(eot|svg|cur)$/,
"loader": "file-loader",
"options": {
"name": "[name].[hash:20].[ext]",
"limit": 10000
}
},
{
"test": /\.(jpg|png|webp|gif|otf|ttf|woff|woff2|ani)$/,
"loader": "url-loader",
"options": {
"name": "[name].[hash:20].[ext]",
"limit": 10000
}
},
{
test: /\.css$/,
exclude: helpers.root('src', 'app'),
loader: ExtractTextPlugin.extract({ fallback: 'style-loader', loader: 'css-loader?sourceMap' })
},
{
test: /\.css$/,
include: helpers.root('src', 'app'),
loader: 'raw-loader'
}
]
},
"plugins": [
new NoEmitOnErrorsPlugin(),
new ExtractTextPlugin('[name].css'),
new HtmlWebpackPlugin({
"template": "./src\\index.html",
"filename": "./index.html",
"hash": false,
"inject": true,
"compile": true,
"favicon": false,
"minify": false,
"cache": true,
"showErrors": true,
"chunks": "all",
"excludeChunks": [],
"title": "Webpack App",
"xhtml": true,
"chunksSortMode": function sort(left, right) {
let leftIndex = entryPoints.indexOf(left.names[0]);
let rightindex = entryPoints.indexOf(right.names[0]);
if (leftIndex > rightindex) {
return 1;
}
else if (leftIndex < rightindex) {
return -1;
}
else {
return 0;
}
}
}),
new CommonsChunkPlugin({
"name": [
"inline"
],
"minChunks": null
}),
new CommonsChunkPlugin({
"name": [
"vendor"
],
"minChunks": (module) => {
return module.resource
&& (module.resource.startsWith(nodeModules)
|| module.resource.startsWith(genDirNodeModules)
|| module.resource.startsWith(realNodeModules));
},
"chunks": [
"main"
]
}),
new SourceMapDevToolPlugin({
"filename": "[file].map[query]",
"moduleFilenameTemplate": "[resource-path]",
"fallbackModuleFilenameTemplate": "[resource-path]?[hash]",
"sourceRoot": "webpack:///"
}),
new CommonsChunkPlugin({
"name": [
"main"
],
"minChunks": 2,
"async": "common"
}),
new NamedModulesPlugin({}),
new webpack.HotModuleReplacementPlugin(),
new AngularCompilerPlugin({
"mainPath": "main.ts",
"platform": 0,
"sourceMap": true,
"tsConfigPath": "src\\tsconfig.json",
"skipCodeGeneration": true,
"compilerOptions": {}
})
],
"node": {
"fs": "empty",
"global": true,
"crypto": "empty",
"tls": "empty",
"net": "empty",
"process": true,
"module": false,
"clearImmediate": false,
"setImmediate": false
},
"devServer": {
"historyApiFallback": true
}
};
- Refer to the below AOT Production build configuration in
config/webpack.prod.js
.
var webpack = require('webpack');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var helpers = require('./helpers');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var path = require('path');
const { AngularCompilerPlugin } = require('@ngtools/webpack');
const ENV = process.env.NODE_ENV = process.env.ENV = 'production';
module.exports = {
devtool: 'source-map',
entry: {
'polyfills': './src/polyfills.ts',
'vendor': './src/vendor.ts',
'app': './src/main.ts'
},
resolve: {
extensions: ['.ts', '.js'],
alias: {
jquery: "jquery/src/jquery"
}
},
output: {
path: helpers.root('dist'),
publicPath: '',
filename: '[name].[hash].js',
chunkFilename: '[id].[hash].chunk.js'
},
module: {
rules: [
{
"test": /\.ts$/,
"loader": "@ngtools/webpack"
},
{
test: /\.html$/,
loader: 'html-loader'
},
{
test: /\.(png|jpe?g|gif|cur|svg|woff|woff2|ttf|eot|ico)$/,
loader: 'file-loader?name=assets/[name].[hash].[ext]'
},
{
test: /\.css$/,
exclude: helpers.root('src', 'app'),
loader: ExtractTextPlugin.extract({ fallback: 'style-loader', loader: 'css-loader?sourceMap' })
},
{
test: /\.css$/,
include: helpers.root('src', 'app'),
loader: 'raw-loader'
}
]
},
plugins: [
// Workaround for angular/angular#11580
new webpack.ContextReplacementPlugin(
/angular(\\|\/)core(\\|\/)@angular/,
path.resolve(__dirname, '../src')
),
new webpack.optimize.CommonsChunkPlugin({
name: ['app', 'vendor', 'polyfills']
}),
new HtmlWebpackPlugin({
template: 'src/index.html'
}),
new webpack.NoEmitOnErrorsPlugin(),
new webpack.optimize.UglifyJsPlugin({ // https://github.com/angular/angular/issues/10618
mangle: {
keep_fnames: true
}
}),
new ExtractTextPlugin('[name].[hash].css'),
new webpack.DefinePlugin({
'process.env': {
'ENV': JSON.stringify(ENV)
}
}),
new webpack.LoaderOptionsPlugin({
htmlLoader: {
minimize: false // workaround for ng2
}
}),
new AngularCompilerPlugin({
"mainPath": "main.ts",
"platform": 0,
"sourceMap": true,
"tsConfigPath": "src\\tsconfig.json",
"skipCodeGeneration": true,
"compilerOptions": {}
})
]
};
Import Require Component in Root Module
- To render Syncfusion Angular Components, import required components in
app.module.ts
file. Refer to the below code snippet for root module file.
import { NgModule, enableProdMode, ErrorHandler } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouterModule } from '@angular/router';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { EJ_GRID_COMPONENTS } from 'ej-angular2/src/ej/grid.component';
import { AppComponent } from './app.component';
import { HomeComponent } from './home/home.component';
import { GridComponent } from './grid/grid.component';
import { rootRouterConfig } from './app.routes';
enableProdMode();
class CustomErrorHandler implements ErrorHandler {
call(error, stackTrace = null, reason = null) {
console.log(error + "\n" + stackTrace);
}
handleError(error: any): void {
console.log(error);
}
}
@NgModule({
imports: [
BrowserModule, FormsModule, HttpModule, RouterModule.forRoot(rootRouterConfig, { useHash: true })
],
declarations: [
AppComponent, HomeComponent, GridComponent, EJ_GRID_COMPONENTS
],
bootstrap: [AppComponent]
})
export class AppModule { }
Build the application
- To build the webpack application with Ahead-of-Time compiler, run the below command. It will create
dist
folder for production build.
npm run build
Refer the below codes to create an application.
// Refer the code for app.component.ts file(src/app/app.component.ts)
import { Component } from '@angular/core';
@Component({
selector: 'ej-app',
templateUrl: './app.component.html',
})
export class AppComponent {
}
// Refer the code for app.module.ts file(src/app/app.module.ts)
import { NgModule, enableProdMode, ErrorHandler } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouterModule } from '@angular/router';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { EJ_GRID_COMPONENTS } from 'ej-angular2/src/ej/grid.component';
import { AppComponent } from './app.component';
import { HomeComponent } from './home/home.component';
import { GridComponent } from './grid/grid.component';
import { rootRouterConfig } from './app.routes';
enableProdMode();
class CustomErrorHandler implements ErrorHandler {
call(error, stackTrace = null, reason = null) {
console.log(error + "\n" + stackTrace);
}
handleError(error: any): void {
console.log(error);
}
}
@NgModule({
imports: [
BrowserModule, FormsModule, HttpModule, RouterModule.forRoot(rootRouterConfig, { useHash: true })
],
declarations: [
AppComponent, HomeComponent, GridComponent, EJ_GRID_COMPONENTS
],
bootstrap: [AppComponent]
})
export class AppModule { }
// Refer the code for main.ts file(src/main.ts)
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { enableProdMode } from '@angular/core';
import { AppModule } from './app/app.module';
if (process.env.ENV === 'production') {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule);
<!-- Refer the code for app.component.html file (src/app/app.component.html)-->
<nav class="navbar navbar-default navbar-fixed-top" role="navigation">
<div style="padding-left:0px;" class="collapse navbar-collapse" id="skeleton-navigation-navbar-collapse">
<ul class="nav navbar-nav">
<li><a data-toggle="collapse" data-target="#skeleton-navigation-navbar-collapse.in" href="#">Syncfusion Angular Seed</a></li>
<li><a data-toggle="collapse" data-target="#skeleton-navigation-navbar-collapse.in" href="#home">Home</a></li>
<li><a data-toggle="collapse" data-target="#skeleton-navigation-navbar-collapse.in" href="#grid">Grid</a></li>
</ul>
</div>
</nav>
<main>
<router-outlet></router-outlet>
</main>
// Refer the code for package.json file
{
"name": "ejangular-webpack-starter",
"version": "1.0.0",
"repository": {
"type": "git",
"url": "git+https://github.com/syncfusion/angular2-seeds.git"
},
"description": "A webpack starter for Angular",
"scripts": {
"start": "webpack-dev-server --inline --progress --port 3000",
"test": "karma start",
"build": "rimraf dist && webpack --config config/webpack.prod.js --progress --profile --bail"
},
"keywords": [
"syncfusion",
"ej",
"essential",
"javascript",
"Angular",
"Angular 2",
"angular2"
],
"author": "Syncfusion Inc",
"license": "SEE LICENSE IN README.md",
"bugs": {
"url": "https://github.com/syncfusion/angular2-seeds/issues"
},
"homepage": "https://github.com/syncfusion/angular2-seeds#readme",
"dependencies": {
"@angular/common": "~5.1.2",
"@angular/compiler": "~5.1.2",
"@angular/compiler-cli": "~5.1.2",
"@angular/core": "~5.1.2",
"@angular/forms": "~5.1.2",
"@angular/http": "~5.1.2",
"@angular/platform-browser": "~5.1.2",
"@angular/platform-browser-dynamic": "~5.1.2",
"@angular/router": "~5.1.2",
"core-js": "^2.4.1",
"rxjs": "^5.4.3",
"zone.js": "^0.7.4",
"bootstrap": "^3.3.6",
"jquery": "~3.2.1",
"jsrender": "~0.9.84",
"syncfusion-javascript": "^15.4.17",
"ej-angular2": "^15.4.18"
},
"devDependencies": {
"@ngtools/webpack": "^1.9.3",
"angular2-template-loader": "~0.6.2",
"awesome-typescript-loader": "~3.1.3",
"css-loader": "^0.26.1",
"extract-text-webpack-plugin": "~2.1.0",
"file-loader": "~0.11.1",
"html-loader": "~0.4.5",
"html-webpack-plugin": "^2.16.1",
"jasmine-core": "^2.4.1",
"karma": "^1.2.0",
"karma-jasmine": "^1.0.2",
"karma-phantomjs-launcher": "^1.0.2",
"karma-sourcemap-loader": "^0.3.7",
"karma-webpack": "^1.8.0",
"null-loader": "^0.1.1",
"phantomjs-prebuilt": "^2.1.7",
"protractor": "~4.0.14",
"raw-loader": "^0.5.1",
"rimraf": "^2.5.4",
"style-loader": "^0.13.1",
"typescript": "~2.4.2",
"url-loader": "^0.5.8",
"webpack": "~2.6.1",
"webpack-dev-server": "~2.4.5",
"@types/ej.web.all": "^15.4.0",
"@types/jquery": "^3.2.12",
"@types/node": "^6.0.46"
}
}
// Refer the code for app.routes.ts file(src/app/app.routes.ts)
import { Routes } from '@angular/router';
import { GridComponent } from './grid/grid.component';
import { HomeComponent } from './home/home.component';
export const rootRouterConfig: Routes = [
{ path: '', redirectTo: 'home', pathMatch: 'full' },
{ path: 'home', component: HomeComponent },
{ path: 'grid', component: GridComponent }
];
// Refer the below code for tsconfig.json(src/tsconfig.json)
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"removeComments": false,
"noImplicitAny": false,
"suppressImplicitAnyIndexErrors": true,
"lib": [
"es2015",
"dom"
],
"typeRoots": [
"./../node_modules/@types/"
],
"types": [
"jquery",
"ej.web.all",
"node"
]
}
}
Running the application
- Now run the application with below command and navigate to http://localhost:3000/ to see the output window.
npm start