This post talk about the installation and configuration of Angular.io and Webpack in a bigger activity of upgrading an Angularjs application into an Hybrid one, where both the framework coesists and where we are free to implement both with the old or the new.
As we’ll see, in the Angular installation we’ll have to be careful about the versions of the packages to install; about Webpack the main activity will be the configuration, that consists to add features and packages to make the deployment process faster and easier.
The versions used for this project are Angular 8 and Webpack 4.
Angular
About the angular packages, this is the list that have to be installed with npm in the package.json file:
..... "dependencies": { "@angular/common": "^8.2.9", "@angular/compiler": "^8.2.9", "@angular/compiler-cli": "^8.2.9", "@angular/core": "^8.2.9", "@angular/forms": "^8.2.9", "@angular/platform-browser": "^8.2.9", "@angular/platform-browser-dynamic": "^8.2.9", "@angular/router": "^8.2.9", "@angular/upgrade": "^8.2.9", "@uirouter/angular-hybrid": "^9.0.0", "@uirouter/visualizer": "^6.0.2", "rxjs": "^6.5.4", "rxjs-compat": "^6.5.4", "tslib": "^1.11.1", "zone.js": "~0.9.1", ..... }
Among the packages installed in a classic Angular.io SPA, we have the @uirouter/angular-hybrid, that is the UI-Router version of an Hybrid app and is able to manage routes for both Angularjs and Angular.io componens.
We complete this step by adding two reference in the index.html page:
<script src="/node_modules/angular/angular.js"></script>; <script src="node_modules/reflect-metadata/Reflect.js"></script>
Now the Angular installation is completed and we can proceed with the next step, Webpack.
Webpack
Now we can install Webpack 4 and their dependencies as devDependencies in the package.json file:
"devDependencies": { "@ngtools/webpack": "^9.0.6", "@types/angular": "^1.6.57", "angular2-template-loader": "^0.6.2", "awesome-typescript-loader": "^5.2.1", "codelyzer": "^5.2.1", "html-webpack-plugin": "^4.2.0", "raw-loader": "^4.0.1", "shx": "^0.3.2", "source-map-loader": "^0.2.4", "typescript": "3.4.5", "webpack": "^4.42.1", "webpack-cli": "^3.3.11", "webpack-dev-server": "^3.10.3" },
We have some packages of which we can talk about:
- @ngtools/webpack is the Angular compiler for Webpack
- angular2-template-loader, with this package we can use html templates instead of inline html in the Angular components declarations
- awesome-typescript-loader, is a speed-up compiler for Typescript
- html-webpack-plugin, starting from an html template, we can generate the index.html page of our application with versioned bundles
- source-map-loader, extract source maps from the js files
- webpack-dev-server, the development server for webpack, particolar useful if you like hot reloading of the browser after typescript files changes
Now we can add the webpack.config.js file:
var webpack = require('webpack'); var AngularCompilerPlugin = require('@ngtools/webpack').AngularCompilerPlugin; var path = require('path'); var DEV_SERVER = process.argv[1].indexOf('webpack-dev-server') !== -1; var DEV = DEV_SERVER || process.env.DEV; module.exports = { mode: DEV ? 'development' : 'production', entry: { "hybridapp": "./app/bootstrap.ts" }, devtool: DEV ? 'eval' : 'source-map', output: { path: path.join(__dirname, "_bundles"), publicPath: '_bundles/', filename: "[name].js" }, resolve: { extensions: ['.js', '.ts'] }, optimization: { splitChunks: { chunks: 'all', }, }, plugins: [ new AngularCompilerPlugin({ "tsConfigPath": 'tsconfig.json', "mainPath": 'app/bootstrap.ts', "sourceMap": true }) ], module: { rules: [ { test: /\.tsx?$/, use: ["source-map-loader"], enforce: 'pre' }, { test: /(?:\.ngfactory\.js|\.ngstyle\.js|\.ts)$/, use: ["@ngtools/webpack"] } ] }, externals: { angular: 'angular' } };
This is the “standard” configuration suggested, where we’ve some definitions:
- the entry of our application, that is the bootstrap.ts file
- the output path of the file bundles, directory _bundles
- splitChunks enabled
- AngularCompilerPlugin configuration
Now let’s add some features; we add the HtmlWebpackPlugin:
plugins: [ ..... new HtmlWebpackPlugin({ hash: true, template: './index.template.html', filename: '../index.html' }) ],
We say that starting from the file index.template.html we want to produce the index.html, that the plugin itself will produce with the bundles references.
Then we configure the template loader:
{ test: /\.ts$/, loaders: ['awesome-typescript-loader', 'angular2-template-loader?keepUrl=true'], exclude: [/\.(spec|e2e)\.ts$/] }, { test: /\.(html|css)$/, loader: 'raw-loader', exclude: /\.async\.(html|css)$/ }
As I said before, used in conjunction with the raw-loader, it’s useful if we want to use in Angular components html templates instead of inline html.
Now we can make another customization; if we are in a situation were we want to use the webpack dev server and our SPA need to be call some server side api’s, we can configure the dev server proxy.
When we start the dev server, a new web site will start, the default hostname will be localhost and the default port 8080; what we have to do is tell to Webpack to redirect some calls to another hostname, where the server side api’s responds, and we can do that with the proxy setting:
devServer: { proxy: [ { context: ['/odata', '/api'], target: 'http://localhost:8092/', secure: false } ] }
So all the calls where the relative url starts with odata or api will be redirect to localhost:8092; it’s obvious that we will previous start the backend server site separately.
It’s clear that the use of the dev server is not mandatory and the difference is the command that you have to launch to the console; if you want to launch the webpack dev server you’ll use:
webpack-dev-server –open –progress
That will open a new browser window at localhost;8080.
Otherwise if you want only to launch the webpack build process you will execute the following command:
npx webpack
That will produce the the index.html file and the js bundles files in the output dir, and will be your task to start manually a new website.
Finally, if you need you can generate a single chunk file by removing the splitChunks setting and adding LimitChunkCountPlugin:
plugins: [ ..... new webpack.optimize.LimitChunkCountPlugin({ maxChunks: 1 }), ..... ],
You can find the final webpack.config.js configuration at this link.
Summary
The Angular installation is a very fast operation and once you know what you have to install, you take care only to the packages versions and the work is done.
The Webpack configurations is sightly more complex but in the same way, once you know what you want to do the operation is likely fast; the base version of the configuration file is usable if you don’t need particular customizations.
Now we miss the final step, the configuration of the boostrap module of our application, that we’ll see in the next post.
Part 1 and Part 2 of these series of post are availables and you can find the source code of the project here.