Angular 2 for Java
Developers
Yakov Fain, Farata Systems
March 10, 2016
Angular 2
Complete re-write of AngularJS
Component-based
Better performance
No controllers, scopes
Streamlined Dependency Injection
Integrated RxJS
Can write apps in TypeScript,
Dart, or JavaScript (ES5 or ES6)
An app is a tree of components
HTML
HTML
A TypeScript class
Importing
modules
Class annotations
Project Structure
App code
App dependencies
Config files
Project Structure
SystemJS
transpiles
TS and
loads JS
bootstrap(ApplicationComponent)
Project Structure
bootstrap(ApplicationComponent)
Navigation with Router
HomeComponent
import
import
import
import
import
{Component} from 'angular2/core';
{Product, ProductService} from 'app/services/product-service';
CarouselComponent from '../carousel/carousel';
ProductItemComponent from '../product-item/product-item';
{ProductService} from '../../services/product-service';
@Component({
selector: 'auction-home-page',
directives: [
CarouselComponent,
ProductItemComponent
],
styleUrls: ['app/components/home/home.css'],
template: `
<div class="row carousel-holder">
<div class="col-md-12">
<auction-carousel></auction-carousel>
</div>
</div>
<div class="row">
<div *ngFor="#product of products" class="col-sm-4 col-lg-4 col
<auction-product-item [product]="product"></auction-product-i
</div>
</div>
`
})
export default class HomeComponent {
products: Product[] = [];
Importing
Modules
Exporing a
module
constructor(private productService: ProductService) {
this.products = this.productService.getProducts();
}
}
HomeComponent
import
import
import
import
import
{Component} from 'angular2/core';
{Product, ProductService} from 'app/services/product-service';
CarouselComponent from '../carousel/carousel';
ProductItemComponent from '../product-item/product-item';
{ProductService} from '../../services/product-service';
Dependency
Injection
@Component({
selector: 'auction-home-page',
directives: [
CarouselComponent,
ProductItemComponent
],
styleUrls: ['app/components/home/home.css'],
template: `
<div class="row carousel-holder">
<div class="col-md-12">
<auction-carousel></auction-carousel>
</div>
</div>
<div class="row">
<div *ngFor="#product of products" class="col-sm-4 col-lg-4 col
<auction-product-item [product]="product"></auction-product-i
</div>
</div>
`
})
export default class HomeComponent {
products: Product[] = [];
constructor(private productService: ProductService) {
this.products = this.productService.getProducts();
}
}
Looping with *ngFor
import
import
import
import
import
{Component} from 'angular2/core';
{Product, ProductService} from 'app/services/product-service';
CarouselComponent from '../carousel/carousel';
ProductItemComponent from '../product-item/product-item';
{ProductService} from '../../services/product-service';
@Component({
selector: 'auction-home-page',
directives: [
CarouselComponent,
ProductItemComponent
],
styleUrls: ['app/components/home/home.css'],
template: `
<div class="row carousel-holder">
<div class="col-md-12">
<auction-carousel></auction-carousel>
</div>
</div>
<div class="row">
<div *ngFor="#product of products" class="col-sm-4 col-lg-4 col
<auction-product-item [product]="product"></auction-product-i
</div>
</div>
`
})
export default class HomeComponent {
products: Product[] = [];
constructor(private productService: ProductService) {
this.products = this.productService.getProducts();
}
}
A Sample Toolbox
Intro to TypeScript
http://www.typescriptlang.org
Whats TypeScript?
An open-source language developed by Microsoft
Designed by Anders Hejlsberg, the creator of C#, Delphi,
and Turbo Pascal
A superset of JavaScript
Well supported by IDEs
Benefits of Writing in TypeScript
Increases your productivity in producing JavaScript
Supports types, classes, interfaces, generics, annotations
Compiles into a human-readable JavaScript
Easy to learn by Java developers
Supports most of the ES6 and some ES7 syntax
Transpiling TypeScript Interactively
http://www.typescriptlang.org/Playground
TypeScript
JavaScript (E5)
Classes
TypeScript
JavaScript (E5)
A Class With Constructor
TypeScript
JavaScript (E5)
Inheritance
Classical syntax
Prototypal
Generics
Error
No Errors
Arrow Function Expressions (lambdas)
var getName = () => 'John Smith';
console.log(`The name is ` + getName());
Interfaces as Custom Types
Interfaces and implements
TypeScript Compiler: tsc
Install the typescript compiler with npm (comes with Node.js):
npm install -g typescript
Compile main.ts into main.js specifying target language syntax:
tsc t ES5 main.ts
Usually the compilers options are set in tsconfig.json file
The watch mode allows to auto-compile as files change:
tsc -w *.ts
HelloWorldComponent in Angular
import {bootstrap} from 'angular2/platform/browser';
import {Component} from 'angular2/core';
@Component({
selector: 'hello-world',
template: '<h1>Hello {{ name }}!</h1>'
})
class HelloWorldComponent {
name: string;
constructor() {
this.name = World!';
}
}
bootstrap(HelloWorldComponent);
In HTML:
<hello-world></hello-world>
SystemJS: a Universal Module Loader
ES6 defines modules, but not the loader
ES7 should include the System object for loading
modules
SystemJS is a polyfill that loads modules
Index.html Take 1
<!DOCTYPE html>
<html>
<head>
<script src="//npmcdn.com/[email protected]/bundles/angular2-polyfills.js"></script>
<script src="//npmcdn.com/[email protected]/lib/typescript.js"></script>
<script src="//npmcdn.com/[email protected]/dist/system.src.js"></script>
<script src="//npmcdn.com/[email protected]/bundles/Rx.js"></script>
<script src="//npmcdn.com/[email protected]/bundles/angular2.dev.js"></script>
<script>
System.config({
transpiler: 'typescript',
typescriptOptions: {emitDecoratorMetadata: true}
});
System.import('main.ts');
</script>
Angular from CDN
SystemJS
</head>
<body>
<hello-world></hello-world>
</body>
</html>
HelloWorldComponent
Starting a new project with npm
1. Generate package.json for your project:
npm init -y
2. Add Angular dependencies to package.json
3. Download dependencies into the dir node_modules:
npm install
4. Install live-server
npm install live-server -g
package.json
{
"name": "walkthrough5",
"version": "1.0.0",
"description": A sample package.json for the Angular app,
"scripts": {
"start": "live-server"
},
"dependencies": {
"es6-shim": "0.33.13",
"es6-promise": "^3.0.2",
"reflect-metadata": "0.1.2",
"rxjs": "5.0.0-beta.2",
"systemjs": "0.19.23",
"zone.js": "0.5.15",
"angular2": "2.0.0-beta.9"
},
"devDependencies": {
"live-server": "^0.9.0",
"typescript": "^1.7.5"
}
}
Index.html Take 2
<!DOCTYPE html>
Angulars local (after npm install)
<html>
<head>
<script src="node_modules/angular2/bundles/angular2-polyfills.js"></script>
<script src="node_modules/typescript/lib/typescript.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="node_modules/rxjs/bundles/Rx.js"></script>
<script src="node_modules/angular2/bundles/angular2.dev.js"></script>
<script>
System.config({
transpiler: 'typescript',
typescriptOptions: {emitDecoratorMetadata: true}
});
System.import('main.ts');
</script>
</head>
<body>
<hello-world></hello-world>
</body>
</html>
Templates
A place to write HTML
Rendering is separated from the core framework
Angular team works with Telerik on rendering for iOS and Android using NativeScript
@Component({
selector: 'auction-home-page',
styleUrls: ['app/components/home/home.css'],
template: `
<div class="row carousel-holder">
<div class="col-md-12">
<auction-carousel></auction-carousel>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="form-group">
<input placeholder="Filter products by title type="text">
</div>
</div>
</div>
`
})
Unidirectional Binding
From code to template:
<h1>Hello {{ name }}!</h1>
<span [hidden]=isZipcodeValid>Zip code is not valid</span>
From template to code:
<button (click)="placeBid()">Place Bid</button>
<input placeholder= "Product name" (input)="onInputEvent()">
Two-way Binding
Synchronizing Model and View:
<input [value]="myComponentProperty"
(input)=onInputEvent($event)>
<input [(ngModel)] = "myComponentProperty">
Dependency Injection
Angular can create services; no need to use the new
operator
Angular injects objects into components via constructors only
Each component has an injector
Providers specify how to inject
If a component has no provider defined, Angular checks its
parent
product-service.ts
export class Product {
constructor(
public id: number,
public title: string,
public price: number,
public description: string) {
}
}
export class ProductService {
getProduct(): Product {
return new Product( 0, "iPhone 7", 249.99,
"The latest iPhone, 7-inch screen");
}
}
Injecting ProductService
import {Component, bind} from 'angular2/core';
import {ProductService, Product} from "../services/product-service";
@Component({
selector: 'di-product-page',
template: `<div>
<h1>Product Details</h1>
<h2>Title: {{product.title}}</h2>
<h2>Description: {{product.description}}</h2>
<h2>Price: \${{product.price}}</h2>
</div>`,
A provider can be a
providers:[ProductService]
class, factory, or a value
})
export default class ProductComponent {
product: Product;
Injection
constructor( productService: ProductService) {
this.product = productService.getProduct();
Injecting Dependencies of Dependencies
Injecting Dependencies of Dependencies
import {Http} from 'angular2/http';
@Injectable
export class ProductService {
constructor(private http:Http){
let products = http.get('products.json');
}
Routing Bulding Blocks
RouterOutlet (<router-outlet>) - where the router should render the component
@RouteConfig - map URLs to components to be rendered inside the <router-outlet>
RouterLink ([routerLink]) - a link to a named route.
RouteParams - a map of key-value pairs to pass parameters to the route
RouteData - a map of key-value pairs used to pass additional data from
@RouteConfig to a route
Basic Routing
@Component({
selector: basic-routing',
template: `<a [routerLink]="['/Home']">Home</a>
<a [routerLink]="['/ProductDetail']">Product Details</a>
<router-outlet></router-outlet>`,
directives: [ ROUTER_DIRECTIVES]
})
@RouteConfig([
{path: '/',
component: HomeComponent, as: 'Home'},
{path: '/product', component: ProductDetailComponent, as: 'ProductDetail'}
])
class RootComponent{
}
bootstrap(RootComponent, [ROUTER_PROVIDERS, provide(LocationStrategy,
{useClass: HashLocationStrategy})]);
Passing Data to a Route
@Component({
selector: 'basic-routing',
template: `<a [routerLink]="['/Home']">Home</a>
<a [routerLink]="['/ProductDetail',
{id:1234}]">Product Details</a>
<router-outlet></router-outlet>`,
directives: [ ROUTER_DIRECTIVES]
})
@RouteConfig([
{path: '/',
component: HomeComponent, as: 'Home'},
{path: '/product/:id', component: ProductDetailComponent,
as: 'ProductDetail'}
])
class RootComponent{}
bootstrap(RootComponent, [ROUTER_PROVIDERS,
provide(LocationStrategy, {useClass: HashLocationStrategy})]);
Receiving Data in a Route
@Component({
selector: product',
template: `<h1 class="product">Product Detail for Product:
{{productID}}</h1>`,
styles: ['product {background: cyan}']
})
export class ProductDetailComponent {
productID: string;
1
constructor(params: RouteParams){
2
}
this.productID = params.get('id');
}
Reactive Programming
Angular comes with RxJS library of reactive extensions
A observable stream emits the data over time to the
subscriber.
Supports multiple operators to transform the streams data
Stream samples:
- UI events
- HTTP responses
- Data arriving over websockets
Observable Streams
An observable stream can:
Emit the next element
Throw an error
Send a signal that the streaming is over
Observers
An observer provides:
A function to handle streaming object
Error handling
End-of-stream handling
Transforming the stream
Observables vs Promises
Observable can return multiple values
Observables can be cancelled
Observables allow to handle end-of-stream
Observable Events
@Component({
selector: "app",
template: `
<h2>Observable events demo</h2>
<input type="text" placeholder="Enter stock" [ngFormControl]="searchInput">
`
})
class AppComponent {
searchInput: Control;
constructor(){
this.searchInput = new Control('');
Observable
this.searchInput.valueChanges
.debounceTime(500)
.subscribe(stock => this.getStockQuoteFromServer(stock));
}
getStockQuoteFromServer(stock) {
console.log(`The price of ${stock} is ${100*Math.random().toFixed(4)}`);
}
}
Http and Observables
class AppComponent {
products: Array<string> = [];
DI
constructor(private http: Http) {
this.http.get(http://localhost:8080/products')
.map(res => res.json())
.subscribe(
data => {
O
b
s
e
r
v
e
r
this.products=data;
},
err =>
console.log("Can't get products. Error code: %s, URL: %s ",
err.status, err.url),
() => console.log('Product(s) are retrieved')
);
}
}
Deployment
We use Webpack bundler for packaging
npm scripts for running the build scripts
The app
index.html
Frameworks
Preparing deployment with Webpack
Input: the entry point(s) of your app
Output: transpiled bundle (a .js file)
Resources (css, images, html) can be inlined in the bundle
Usually, the app will have at least two bundles:
- your code (e.g. bundle.js)
- frameworks and libraries (e.g. vendor.bundle.js)
Webpack Loaders & Plugins
Loaders operate on a single file (e.g. transpile TS into JS)
Plugins can operate on multiple files and be invoked at
different stages of the Webpack lifecycle
webpack.config.js
module.exports = {
debug: false,
devtool: 'source-map',
entry: {
'main' : './src/main.ts',
'vendor': './src/vendor.ts'
},
metadata: metadata,
module: {
loaders: [
{test: /\.css$/, loader: 'to-string!css'},
{test: /\.html$/, loader: 'raw'},
{test: /\.ts$/,
loader: 'ts'}
],
noParse: [path.join(__dirname, 'node_modules', 'angular2', 'bundles')]
},
output: {
path
: './dist',
filename: 'bundle.js'
},
plugins: [
new CommonsChunkPlugin({name: 'vendor', filename: 'vendor.bundle.js', minChunks: Infinity}),
new CompressionPlugin({regExp: /\.css$|\.html$|\.js$|\.map$/, threshold: 1500}),
new CopyWebpackPlugin([{from: './src/index.html', to: 'index.html'}]),
new DedupePlugin(),
new DefinePlugin({'webpack': {'ENV': JSON.stringify(metadata.ENV)}}),
new OccurenceOrderPlugin(true),
new UglifyJsPlugin({
compress : {screw_ie8 : true},
mangle: {
screw_ie8 : true,
except: ['RouterLink'] // TODO: Remove after #6678 fixed
}
})
],
resolve: {
extensions: ['', '.ts', '.js']
}
npm scripts in package.json
"scripts": {
"clean": "rimraf dist",
"postinstall": "typings install",
"prebuild": "npm run clean",
"build": "webpack --config webpack.prod.config.js --progress profile --display-cached",
"start": "webpack-dev-server --inline --progress --display-cached --port 8080",
"preserve:dist": "npm run build",
"serve:dist": "static dist -z"
}
To run a script from the command line:
npm start
or
npm run build
or
npm run serve:dist
Links
A two-day Angular 2 workshop, March 28-29, 2016, New York,
http://bit.ly/1R0FAhN
discount code: jugmember
Angular consulting/training: faratasystems.com
Blog: yakovfain.com
Our book: http://bit.ly/1QYeqL0