Webpack with Angular2

In a previous post I spoke about the configurations to manage bundles in angular 2 with Systemjs.

For this purphose, the latest versions of Angular CLI uses webpack and we can use it as module bundler in the new angular 2/4 applications.

The reason of this choice is that webpack has a lot of plugins that they help us with a refined management of the modules bundling.

Entries

When we configure webpack, the first thing that we need to do is define the entries of the bundles.

Every bundle has the main entry point, that is the module where webpack start to build the bundle hierarchy.

I like to build two different bundle, the first one for the vendors libraries and the second one for the application modules.

The  main entry for the vendors is a typescript class that looks like this:


import '@angular/platform-browser';
import '@angular/platform-browser-dynamic';
import '@angular/core';
import '@angular/common';
import '@angular/http';
import '@angular/router';
import '@angular/animations';

import 'jquery';
import 'lodash';
import 'ng2-toastr';
import 'ng2-translate';
import 'reflect-metadata';
import 'zone.js';
import 'rxjs';

The second one is the main module for the app:


export { PostModule } from "./post.module";

Webpack is able to load these modules and build the dependencies of the childs descendans, and process them.

Loaders

Loaders are specific webpack tools that are able to process specific file types and to produce the right bundle outputs.

For example, in order to make webpack to be able to resolve the angular html templates or the router modules, or more simply to compile typescript files, specific loaders are needed.

Below you can see a bunch of loaders used in an Angular 2 application:


module: {
rules: [
{
test: /\.ts$/,
loaders: [
{
loader: 'awesome-typescript-loader',
options: { configFileName: 'tsconfig.json' }
}, 'angular2-template-loader', 'angular2-router-loader'
]
},
{
test: /\.html$/,
loader: 'html-loader'
},
{
test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/,
loader: 'file-loader?name=images/[name].[hash].[ext]'
},
{
test: /\.css$/,
exclude: helpers.root('src', 'app'),
loader: ExtractTextPlugin.extract({ fallbackLoader: 'style-loader', loader: 'css-loader?sourceMap' })
},
{
test: /\.css$/,
include: helpers.root('src', 'app'),
loader: 'raw-loader'
}
]
}

Plugins

These are utilities that we can help us in some additional tasks of the webpack script.

For example, we would like to share a common chunk and we would want that the chunk, if used from different module of the application, is not repeated in the bundle but is present only once.

CommonsChunkPlugin deal with this work:


plugins: [

.....

new webpack.optimize.CommonsChunkPlugin({
name: ['vendor']
}),

.....

]

Another requirement could be write the script references in the index.html file of the application.

HtmlWebpackPlugin can help us in this work:


plugins: [

.....

new HtmlWebpackPlugin({
filename: 'index.html',
template: 'src/index.html',
chunks: ['app', 'vendor']
}),

new HtmlWebpackPlugin({
filename: 'index.admin.html',
template: 'src/index.admin.html',
chunks: ['app.admin', 'vendor']
}),

.....
]

Output

In this section we can configure the file results of the webpack process.

We have to produce these files in the public folder of the application; the output section is look like this:


output: {
path: helpers.root('wwwroot'),
filename: 'js/[name].js',
chunkFilename: 'js/[id].chunk.js'
}

 

You can find the source code here.

 

Webpack with Angular2

Translations in Angular 2

A common requirement in Angular 2 is configure and share a service used from all the components of the application.

In a multilanguage application, a service that we need to use is the provider for the language translations; obviously this module will be used in all the components, or at least in those that have an html template.

The service that we use is ng2-translate; what we need to do is installing the package, provide it with a shared module and use the translation service in the components.

Installation

First we install the package:

npm install ng2-translate –save

Once installed, we have to configure the package with a module loader like SystemJS (or Webpack):

(function (global) {
System.config({
paths: {
'npm:': 'node_modules/'
},
map: {
app: 'app',
'@angular/core': 'npm:@angular/core/bundles/core.umd.js',
.....
'ng2-translate': 'npm:ng2-translate/bundles/index.js'
},
packages: {
.....
}
});
})(this);

Now the package is installed and configured and we can going to provide the service.

Providing

As explained below, in order to provide the service we have to use a shared module of the application; we already have discussed in a previous post a module like that, and we can edit it:

import { NgModule, ModuleWithProviders, Optional, SkipSelf } from "@angular/core";
.....
import { TranslateModule } from "ng2-translate";
.....
@NgModule ({
imports: [
.....
],
exports: [
.....
TranslateModule,
.....
]
})
export class SharedModule {}

We have to export the TraslateModule in order to make it shared; otherwise the others modules won’t be able to use it.

Now the translate module is provided and we can define the translations; by using the default configuration, we can create the i18n folder in the root of the project and create a json file for every language supported.

For example, en.json:

{
"NEW": "New",
"SAVE": "Save",
"CLOSE": "Close",
"DELETE": "Delete",
"NAME": "Name",
"ADDRESS": "Address",
"ATTACHMENT": "Attachment",
"ATTACHINVOICE": "Attach invoice",
"CITY": "City",
"ZIP": "Zip",
"DISTRICT": "District",
"COUNTRY": "Country",
"NUMBER": "Number",
"YEAR": "Year",
"CUSTOMER": "Customer",
"EMISSIONDATE": "Emission Date",
"DUEDATE": "Due Date",
"PAYMENTDATE": "Payment Date",
"CUSTOMERSLOADED": "Customers loaded successfully",
"CUSTOMERSAVED": "Customer saved",
"CUSTOMERDELETED": "Customer deleted",
"SEARCH": "Search",
"INVOICESLOADED": "Invoices loaded successfully",
"INVOICESAVED": "Invoice saved",
"INVOICEDELETED": "Invoice deleted"
}

Now we are ready to use the translations in the components of the application.

Translations

The service that make the work of translation is the TranslateService, and we import it:

import { Component, OnInit } from "@angular/core";
import { TranslateService } from "ng2-translate";
.....
@Component({
moduleId: module.id,
selector: "customer",
templateUrl: "customer.component.html"
})
export class CustomerComponent implements OnInit {
constructor(private customerService: CustomerService, private alertService: AlertService, private searchService: SearchService, <strong>private translateService: TranslateService</strong>) {}
public Load() {
this.alertService.isLoading = true;
this.customerService.GetAll().subscribe(
(data) => {
this.alertService.isLoading = false;
this.customers = data;
this.customer = null;
this.translateService.get("CUSTOMERSLOADED").subscribe((res: string) => {
this.alertService.Success(res);
});
},
(error) => this.alertService.Error(error));
}
.....
}

In this code, we inject the service in the component constructor and we use it to translate the CUSTOMERLOADED resource and show an alert message.

Another purpose of the translation service is translate the labels of a view:

<div [hidden]="edit">
<input type="button" class="btn btn-primary" value="New" (click)="New()" />
<table class="table table-striped">
<thead>
<tr>
<th>
{{ "NAME" | translate }}</th>
<th>
{{ "ADDRESS" | translate }}</th>
<th>
{{ "CITY" | translate }}</th>
</tr>
</thead>
<tbody>
.....</tbody>
</table>
</div>
.....

Here you can find the github repository with the source code.

 

Translations in Angular 2

Mandatory fields validation with knockoutjs extenders

Knockout is a lightweight javascript library that help us to bind the UI with a model and build rich web UI interactions.

The main behaviour of this library are the observable variables, that allow us to make a bidirectional binding between the html fields and the view model of the page.

An interesting feature is extend the observable variables, enriching them with some aditional functionalities; in this example we’ll extend the observable variables with a control for the mandatory fields.

The extender

The extenders allow us to “extend” an observable variable with some behaviours; in this case, we want to add a control for the mandatory fields:


ko.extenders.required = function(target, message) {
target.hasError = ko.observable(false);
target.errorMessage = ko.observable(message || "The field is mandatory.");

function validate(value) {
target.hasError(value ? false : true);
}

validate(target());
target.subscribe(validate);

return target;
};

The function accept two parameters, the target object and the message to show and has the related properties.

The function “validate” set the correct value of hasError property, is called when the main function is loaded and it has been binded to the target object.

We’ll use this extender in our web page.

The view model

We define the view model of the page.


function ViewModel() {
var self = this;

self.FirstName = ko.observable("").extend({ required: "" });
self.LastName = ko.observable("").extend({ required: "" });

self.OnSubmit = ko.observable(false);
self.OnSendData = ko.observable(false);

self.Submit = function () {
self.OnSubmit(true);

if (!self.FirstName.hasError() && !self.LastName.hasError()) {
self.SendData();
}
}

self.Unsubmit = function () {
self.OnSubmit(false);
}

self.SendData = function () {
self.OnSendData(true);
}
}

Has you can see, we are using the extender in the properties definition; we extend the FirstName and LastName properties with the “required” extender; the syntax:

self.FirstName = ko.observable(“”).extend({ required: “” });

means that we pass to the extender two parameters, the first one is the target property (the observable) and the second one is the message (the empty string).

In the submit function we check the value of the hasError property and then we send the data.

The page

Now we are ready to implement the web page.


<!DOCTYPE html>
<html>
<head>
<title></title>
<meta charset="utf-8" />

		<link rel="stylesheet" type="text/css" href="Content/main.css"/>
<script type="text/javascript" src="scripts/jquery-3.1.1.js"></script>
 <script type="text/javascript" src="scripts/knockout-3.4.0.js"></script>
<script type="text/javascript" src="scripts/extenders.js"></script>
 <script type="text/javascript" src="scripts/viewModel.js"></script>

<script type="text/javascript">
 $(function () {
 var vm = new ViewModel();
 ko.applyBindings(vm);
 });
 </script>
</head>
<body>
<script type="text/html" id="field-error">


<div>
 <input type="text" data-bind="value: field, css: { fieldError: $root.OnSubmit() && field.hasError }" />
 <span data-bind="text: field.errorMessage, visible: $root.OnSubmit() && field.hasError" class="labelError"></span>
</div>


 </script>
<div>
<label>First name</label>
<div data-bind="template: { name: 'field-error', data: { field: FirstName } }"></div>
</div>
<div>
<label>Last name</label>
<div data-bind="template: { name: 'field-error', data: { field: LastName } }"></div>
</div>
<div>
<input type="button" data-bind="click: Submit" value="submit" /></div>
<div>
<label data-bind="visible: OnSendData">Send Data!</label></div>
</body>
</html>

We have defined a template “field-error” that is composed by an input text field and by an error label.

The field has a bind to the value and to the css as well:

<input type=”text” data-bind=”value: field, css: { fieldError: $root.OnSubmit() && field.hasError }” />

The css is conditioned to the OnSubmit event and to the hasError property of the extender.

If all of these properties are true, the fieldError class need to be applied.

The error label is similar:

<span data-bind=”text: field.errorMessage, visible: $root.OnSubmit() && field.hasError” class=”labelError”></span>

The only difference is the text, binded to the errorMessage property of the extender, that we have initialized to an empty string in the view model above.

Then we use the template to define the fields:

Finally, the small css used in this example:


.labelError {
color: #ff0000;
}

.fieldError {
border-color: #ff0000;
}

 

Mandatory fields validation with knockoutjs extenders

Bootstrapping an Angular 2 application

In an Angular 2 application we have different options to load application and modules, and one of these is SystemJS.

This is an open source project that give us the ability to load bunch of modules and define for each one the corresponding default main module.

Therefore you can use this library to define a default main module for an Angular 2 application; the first step is install this with npm, by adding as a dependency in the package.json file of the project:


{
 ...
 },
 "license": "ISC",
 "dependencies": {
 ...
 "systemjs": "0.19.27",
 ...
 },
 "devDependencies": {
 ...
 }
}

This library allows you to configure you application startup and defaults by using a specific json file.

The file that we need to create is called systemjs.config.js:

(function (global) {
 var map = {
 'app': 'app',
 ...
 };
 var packages = {
 'app': { main: 'main.js', defaultExtension: 'js' },
 ...
 };
var config = {
 map: map,
 packages: packages
 };
 System.config(config);
})(this);

Notice that one of the packages property is app, that is our app selector, and for that we specify the main property, that is the default loader; you can also use this file to configure other stuff, such as bundling or other default main modules.

The last step is create the loader file, that we can call, for example, Main.ts:

import { bootstrap } from '@angular/platform-browser-dynamic';
import { AppComponent } from './App.Component';
bootstrap(AppComponent);

With the last row, we will load as default the AppComponent, that is the main component of our app.

Finally, this is the App.Component.ts file:

import { Component } from '@angular/core';

@Component({
 selector: 'app',
 templateUrl: 'app/views/app.html'
})

export class AppComponent {
 title: string = 'New App';
}

The selector property of the component is the value that we have assigned in the fourth row of the systemjs.config.js above.

 

Bootstrapping an Angular 2 application