embrace the angular 2 ethos in angular 1.x

126
Embrace the Angular 2 Ethos in Angular 1.x

Upload: lukas-ruebbelke

Post on 16-Apr-2017

1.556 views

Category:

Software


0 download

TRANSCRIPT

Page 1: Embrace the Angular 2 Ethos in Angular 1.x

Embrace the Angular 2 Ethosin Angular 1.x

Page 2: Embrace the Angular 2 Ethos in Angular 1.x

Ethosthe distinctive character, spirit, and attitudes of a people, culture, era, etc

Page 3: Embrace the Angular 2 Ethos in Angular 1.x
Page 4: Embrace the Angular 2 Ethos in Angular 1.x

GAP!

Page 5: Embrace the Angular 2 Ethos in Angular 1.x

GAP!

Page 6: Embrace the Angular 2 Ethos in Angular 1.x
Page 7: Embrace the Angular 2 Ethos in Angular 1.x

Angular 2 is not only a framework…

Page 8: Embrace the Angular 2 Ethos in Angular 1.x

…but a set of very useful patterns.

Page 9: Embrace the Angular 2 Ethos in Angular 1.x

essence details

Page 10: Embrace the Angular 2 Ethos in Angular 1.x

A Brief History of Angular

Page 11: Embrace the Angular 2 Ethos in Angular 1.x

tiny app == tiny view + tiny controller

Page 12: Embrace the Angular 2 Ethos in Angular 1.x

Growing Application

Growing View

Growing Controller

Page 13: Embrace the Angular 2 Ethos in Angular 1.x

Realistic Application

Growing View

Growing Controller

Page 14: Embrace the Angular 2 Ethos in Angular 1.x

Uh oh!

Page 15: Embrace the Angular 2 Ethos in Angular 1.x

Large 1.x Application

Named Route

Named Route

Named Route

Page 16: Embrace the Angular 2 Ethos in Angular 1.x

Large 1.x Application

Directive

Directive

Directive

Page 17: Embrace the Angular 2 Ethos in Angular 1.x

Any Angular 2 Application

Component

Component

Component

Page 18: Embrace the Angular 2 Ethos in Angular 1.x

Still one small problem…

Page 19: Embrace the Angular 2 Ethos in Angular 1.x
Page 20: Embrace the Angular 2 Ethos in Angular 1.x

Structure

Page 21: Embrace the Angular 2 Ethos in Angular 1.x

Communication

Page 22: Embrace the Angular 2 Ethos in Angular 1.x

Let's step back a moment...

Page 23: Embrace the Angular 2 Ethos in Angular 1.x

The Big Picture

viewcontroller

module

config

routes

$scope

service directive

Page 24: Embrace the Angular 2 Ethos in Angular 1.x

It has gotten even simpler...

Page 25: Embrace the Angular 2 Ethos in Angular 1.x

The Simplified Picture

component

module

config

routes

service

Page 26: Embrace the Angular 2 Ethos in Angular 1.x

Angular 1.x Application

Component

Component

Component

Page 27: Embrace the Angular 2 Ethos in Angular 1.x

The best way to become a great Angular developer

Page 28: Embrace the Angular 2 Ethos in Angular 1.x

is to focus on becoming a great developer

Page 29: Embrace the Angular 2 Ethos in Angular 1.x

Common sense

Page 30: Embrace the Angular 2 Ethos in Angular 1.x

Established practices

Page 31: Embrace the Angular 2 Ethos in Angular 1.x
Page 32: Embrace the Angular 2 Ethos in Angular 1.x

JOHN PAPA'S STYLE GUIDE

Page 33: Embrace the Angular 2 Ethos in Angular 1.x
Page 34: Embrace the Angular 2 Ethos in Angular 1.x

Hello Progression

Page 35: Embrace the Angular 2 Ethos in Angular 1.x

angular.module('app') .controller('CategoriesListCtrl', function($scope, CategoriesModel) { CategoriesModel.getCategories() .then(function(result){ $scope.categories = result; }); $scope.onCategorySelected = function(category) { CategoriesModel.setCurrentCategory(category); } });

Classic Controller

Page 36: Embrace the Angular 2 Ethos in Angular 1.x

<div ng-controller="CategoriesListCtrl"> <!-- categories list markup --></div>

Classic View

Page 37: Embrace the Angular 2 Ethos in Angular 1.x

angular.module('app') .controller('CategoriesListCtrl', function(CategoriesModel) { CategoriesModel.getCategories() .then(function(result){ this.categories = result; }); this.onCategorySelected = function(category) { CategoriesModel.setCurrentCategory(category); } });

Moving to controller as

Page 38: Embrace the Angular 2 Ethos in Angular 1.x

<div ng-controller="CategoriesListCtrl as categoriesListCtrl"> <!-- categories list markup --></div>

Moving to controller as

Page 39: Embrace the Angular 2 Ethos in Angular 1.x

function CategoriesListCtrl(CategoriesModel) { CategoriesModel.getCategories() .then(function(result){ this.categories = result; }); this.onCategorySelected = function(category) { CategoriesModel.setCurrentCategory(category); }}angular.module('app') .controller('CategoriesListCtrl', CategoriesListCtrl);

Extract the controller function

Page 40: Embrace the Angular 2 Ethos in Angular 1.x

function CategoriesListCtrl(CategoriesModel) { CategoriesModel.getCategories() .then(function(result){ this.categories = result; }); this.onCategorySelected = function(category) { CategoriesModel.setCurrentCategory(category); }}var CategoriesList = { template: '<div><!-- categories list markup --></div>', controller: CategoriesListCtrl, controllerAs: 'CategoriesListCtrl' }angular.module('app') .component('categoriesList', CategoriesList);

Convert to component

Page 41: Embrace the Angular 2 Ethos in Angular 1.x

<categories-list></categories-list>

Convert to component

Page 42: Embrace the Angular 2 Ethos in Angular 1.x

class CategoriesListCtrl { constructor(CategoriesModel) { 'ngInject'; this.CategoriesModel = CategoriesModel; this.CategoriesModel.getCategories() .then(result => this.categories = result); } onCategorySelected(category) { this.CategoriesModel.setCurrentCategory(category); }}const CategoriesList = { template: '<div><!-- categories list markup --></div>', controller: CategoriesListCtrl, controllerAs: 'categoriesListCtrl'};angular.module('app') .component('categoriesList', CategoriesList);

Convert to ES6

Page 43: Embrace the Angular 2 Ethos in Angular 1.x

class CategoriesListCtrl { constructor(CategoriesModel) { 'ngInject'; this.CategoriesModel = CategoriesModel; } $onInit() { this.CategoriesModel.getCategories() .then(result => this.categories = result); } onCategorySelected(category) { this.CategoriesModel.setCurrentCategory(category); }}const CategoriesList = { template: '<div><!-- categories list markup --></div>', controller: CategoriesListCtrl, controllerAs: 'categoriesListCtrl'};angular.module('app') .component('categoriesList', CategoriesList);

Use lifecycle hooks

Page 44: Embrace the Angular 2 Ethos in Angular 1.x

@Component({ selector: 'categories-list', template: `<div>Hello Category List Component</div>`, providers: [CategoriesModel]})export class CategoriesList { constructor(CategoriesModel: CategoriesModel) { this.CategoriesModel = CategoriesModel; } ngOnInit() { this.CategoriesModel.getCategories() .then(result => this.categories = result); } onCategorySelected(category) { this.CategoriesModel.setCurrentCategory(category); }}

Angular 2 equivalent

Page 45: Embrace the Angular 2 Ethos in Angular 1.x

@Component({ selector: 'categories-list', template: `<div>Hello Category List Component</div>`, providers: [CategoriesModel]})export class CategoriesList { constructor(CategoriesModel: CategoriesModel) { this.CategoriesModel = CategoriesModel; } ngOnInit() { this.CategoriesModel.getCategories() .then(result => this.categories = result); } onCategorySelected(category) { this.CategoriesModel.setCurrentCategory(category); }}

class CategoriesListCtrl { constructor(CategoriesModel) { 'ngInject'; this.CategoriesModel = CategoriesModel; } $onInit() { this.CategoriesModel.getCategories() .then(result => this.categories = result); } onCategorySelected(category) { this.CategoriesModel.setCurrentCategory(category); }}const CategoriesList = { template: '<div><!-- categories list markup --></div>', controller: CategoriesListCtrl, controllerAs: 'categoriesListCtrl' };angular.module('app') .component('categoriesList', CategoriesList);

Similar shapes

Page 46: Embrace the Angular 2 Ethos in Angular 1.x

@Component({ selector: 'categories-list', template: `<div>Hello Category List Component</div>`, providers: [CategoriesModel]})

export class CategoriesList { constructor(CategoriesModel: CategoriesModel) { this.CategoriesModel = CategoriesModel; } ngOnInit() { this.CategoriesModel.getCategories() .then(result => this.categories = result); } onCategorySelected(category) { this.CategoriesModel.setCurrentCategory(category); }}

class CategoriesListCtrl { constructor(CategoriesModel) { 'ngInject'; this.CategoriesModel = CategoriesModel; } $onInit() { this.CategoriesModel.getCategories() .then(result => this.categories = result); } onCategorySelected(category) { this.CategoriesModel.setCurrentCategory(category); }}const CategoriesList = { template: '<div><!-- categories list markup --></div>', controller: CategoriesListCtrl, controllerAs: 'categoriesListCtrl' };angular.module('app') .component('categoriesList', CategoriesList);

Component configuration

Page 47: Embrace the Angular 2 Ethos in Angular 1.x

@Component({ selector: 'categories-list', template: `<div>Hello Category List Component</div>`, providers: [CategoriesModel]})

export class CategoriesList { constructor(CategoriesModel: CategoriesModel) { this.CategoriesModel = CategoriesModel; } ngOnInit() { this.CategoriesModel.getCategories() .then(result => this.categories = result); } onCategorySelected(category) { this.CategoriesModel.setCurrentCategory(category); }}

class CategoriesListCtrl { constructor(CategoriesModel) { 'ngInject'; this.CategoriesModel = CategoriesModel; } $onInit() { this.CategoriesModel.getCategories() .then(result => this.categories = result); } onCategorySelected(category) { this.CategoriesModel.setCurrentCategory(category); }}const CategoriesList = { template: '<div><!-- categories list markup --></div>', controller: CategoriesListCtrl, controllerAs: 'categoriesListCtrl' };angular.module('app') .component('categoriesList', CategoriesList);

Component controller

Page 48: Embrace the Angular 2 Ethos in Angular 1.x

@Component({ selector: 'categories-list', template: `<div>Hello Category List Component</div>`, providers: [CategoriesModel]})export class CategoriesList { constructor(CategoriesModel: CategoriesModel) { this.CategoriesModel = CategoriesModel; } ngOnInit() { this.CategoriesModel.getCategories() .then(result => this.categories = result); } onCategorySelected(category) { this.CategoriesModel.setCurrentCategory(category); }}

class CategoriesListCtrl { constructor(CategoriesModel) { 'ngInject'; this.CategoriesModel = CategoriesModel; } $onInit() { this.CategoriesModel.getCategories() .then(result => this.categories = result); } onCategorySelected(category) { this.CategoriesModel.setCurrentCategory(category); }}const CategoriesList = { template: '<div><!-- categories list markup --></div>', controller: CategoriesListCtrl, controllerAs: 'categoriesListCtrl' };angular.module('app') .component('categoriesList', CategoriesList);

Entry point into the application

Page 49: Embrace the Angular 2 Ethos in Angular 1.x

Use ES6 for classes and modules

Page 50: Embrace the Angular 2 Ethos in Angular 1.x

import {Injectable} from '@angular/core';@Injectable()export class MessageService { private message = 'Hello Message'; getMessage(): string { return this.message; }; setMessage(newMessage: string): void { this.message = newMessage; };}

Simple Angular 2 service

Page 51: Embrace the Angular 2 Ethos in Angular 1.x

class MessageService { constructor() { this.message = 'Hello Message' } getMessage() { return this.message; }; setMessage(newMessage) { this.message = newMessage; };}export default MessageService;

Simple Angular 1.x service

Page 52: Embrace the Angular 2 Ethos in Angular 1.x

import angular from 'angular';import BookmarksModule from './bookmarks/bookmarks';import CategoriesModule from './categories/categories';const ComponentsModule = angular.module('app.components', [ BookmarksModule.name, CategoriesModule.name]);export default ComponentsModule;

Importing in Angular 1.x

Page 53: Embrace the Angular 2 Ethos in Angular 1.x

Create a top-level component

Page 54: Embrace the Angular 2 Ethos in Angular 1.x

import { Component } from '@angular/core';@Component({ selector: 'app', templateUrl: './app.component.html', styleUrls: ['./app.component.css']})export class AppComponent {}

Top-level component

Page 55: Embrace the Angular 2 Ethos in Angular 1.x

import { BrowserModule } from '@angular/platform-browser';import { NgModule } from '@angular/core';import { AppComponent } from './app.component';@NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule ], bootstrap: [ AppComponent ]})export class AppModule {}

Module

Page 56: Embrace the Angular 2 Ethos in Angular 1.x

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';import { enableProdMode } from '@angular/core';import { environment } from './environments/environment';import { AppModule } from './app/';if (environment.production) { enableProdMode();}platformBrowserDynamic().bootstrapModule(AppModule);

Bootstrap

Page 57: Embrace the Angular 2 Ethos in Angular 1.x

<body> <app>Loading…</app></body>

Entry point

Page 58: Embrace the Angular 2 Ethos in Angular 1.x

import template from './app.html';import './app.styl';const AppComponent = { template};export default AppComponent;

Top-level component

Page 59: Embrace the Angular 2 Ethos in Angular 1.x

import angular from 'angular';import appComponent from './app.component';import CommonModule from './common/common';import ComponentsModule from './components/components';angular.module('app', [ CommonModule.name, ComponentsModule.name]) .component('app', appComponent);

Module

Page 60: Embrace the Angular 2 Ethos in Angular 1.x

<body ng-app="app" ng-strict-di ng-cloak> <app>Loading...</app></body>

Entry point

Page 61: Embrace the Angular 2 Ethos in Angular 1.x

Organize your application with subcomponents

Page 62: Embrace the Angular 2 Ethos in Angular 1.x
Page 63: Embrace the Angular 2 Ethos in Angular 1.x

Directives vs Components

Components are simplified Directives

Components have a MUCH simpler configuration object

Components will generally accomplish everything you need

You still need directives when you need to do DOM manipulation or create a class based directive

Advanced functionality such as terminal, priority, etc require a directive

Page 64: Embrace the Angular 2 Ethos in Angular 1.x

Basic Component Structure

The basic structure for a component is module.component('name',{});

Notice that unlike a directive, component takes a configuration object and not a function

The most common component configuration properties will be controller, template, templateUrl and bindings

Page 65: Embrace the Angular 2 Ethos in Angular 1.x

Component Driven Architecture

Components only control their own View and Data

Components have a well-defined public API aka Inputs and Outputs

Components have a well-defined lifecycle

A well architected application is a tree of components

Page 66: Embrace the Angular 2 Ethos in Angular 1.x

import {Component} from '@angular/core';@Component({ moduleId: module.id, selector: 'items', templateUrl: 'items.component.html', styleUrls: ['items.component.css']})export class ItemsComponent {}

Parent component

Page 67: Embrace the Angular 2 Ethos in Angular 1.x

import {Component, Input} from '@angular/core';import {Item} from '../shared';@Component({ moduleId: module.id, selector: 'app-item-details', templateUrl: 'item-details.component.html', styleUrls: ['item-details.component.css']})export class ItemDetailsComponent { @Input() item: Item; addItem(): void { this.item.count += 1; };}

Child component

Page 68: Embrace the Angular 2 Ethos in Angular 1.x

<div> <div> <h2>{{ title }}</h2> </div> <div> {{ body }} </div></div><item-details *ngFor="let item of items" [item]="item"></item-details>

Parent component

Page 69: Embrace the Angular 2 Ethos in Angular 1.x

import template from './categories.html';import controller from './categories.controller';import './categories.styl';const categoriesComponent = { template, controller, controllerAs: 'categoriesListCtrl'};export default categoriesComponent;

Parent component

Page 70: Embrace the Angular 2 Ethos in Angular 1.x

import template from './category-item.html';import './category-item.styl';const categoryItemComponent = { bindings: { category: '<', selected: '&' }, template, controllerAs: 'categoryItemCtrl'};export default categoryItemComponent;

Child component

Page 71: Embrace the Angular 2 Ethos in Angular 1.x

<ul class="nav nav-sidebar"> <li ng-repeat="category in categoriesListCtrl.categories"> <category-item category="category" selected="categoriesListCtrl.onCategorySelected(category)"> </category-item> </li></ul>

Parent component

Page 72: Embrace the Angular 2 Ethos in Angular 1.x

<div class="categoryItem" ng-click="categoryItemCtrl.selected({category:categoryItemCtrl.category})"> {{categoryItemCtrl.category.name}}</div>

Child component

Page 73: Embrace the Angular 2 Ethos in Angular 1.x

Create a component controller in ES6

Page 74: Embrace the Angular 2 Ethos in Angular 1.x

import {Component, OnInit} from '@angular/core';import {MessageService} from '../shared';@Component({ moduleId: module.id, selector: 'home', templateUrl: 'home.component.html', styleUrls: ['home.component.css']})export class HomeComponent {}

Basic component

Page 75: Embrace the Angular 2 Ethos in Angular 1.x

export class HomeComponent implements OnInit { title: string = 'Home Page'; body: string = 'This is the about home body'; message: string; constructor(private messageService: MessageService) { } ngOnInit() { this.message = this.messageService.getMessage(); } updateMessage(m: string): void { this.messageService.setMessage(m); }}

Basic component class

Page 76: Embrace the Angular 2 Ethos in Angular 1.x

import template from './categories.html';import './categories.styl';const categoriesComponent = { template};export default categoriesComponent;

Basic component

Page 77: Embrace the Angular 2 Ethos in Angular 1.x

class CategoriesController { constructor(CategoriesModel) { 'ngInject'; this.CategoriesModel = CategoriesModel; } $onInit() { this.CategoriesModel.getCategories() .then(result => this.categories = result); } onCategorySelected(category) { this.CategoriesModel.setCurrentCategory(category); } isCurrentCategory(category) { return this.CategoriesModel.getCurrentCategory() && this.CategoriesModel.getCurrentCategory().id === category.id; }}export default CategoriesController;

Basic controller class

Page 78: Embrace the Angular 2 Ethos in Angular 1.x

import template from './categories.html';import controller from './categories.controller';import './categories.styl';const categoriesComponent = { template, controller, controllerAs: 'categoriesListCtrl'};export default categoriesComponent;

Basic component with controller

Page 79: Embrace the Angular 2 Ethos in Angular 1.x

Refactor controller logic to services

Page 80: Embrace the Angular 2 Ethos in Angular 1.x

class CategoriesController { constructor() { this.categories = [ {"id": 0, "name": "Development"}, {"id": 1, "name": "Design"}, {"id": 2, "name": "Exercise"}, {"id": 3, "name": "Humor"} ]; }}export default CategoriesController;

Basic controller

Page 81: Embrace the Angular 2 Ethos in Angular 1.x

class CategoriesModel { constructor() { this.categories = [ {"id": 0, "name": "Development"}, {"id": 1, "name": "Design"}, {"id": 2, "name": "Exercise"}, {"id": 3, "name": "Humor"} ]; }}export default CategoriesModel;

Basic service

Page 82: Embrace the Angular 2 Ethos in Angular 1.x

@Injectable()export class CategoriesModel { private categories = [ {"id": 0, "name": "Development"}, {"id": 1, "name": "Design"}, {"id": 2, "name": "Exercise"}, {"id": 3, "name": "Humor"} ]; getCategories() { return this.categories; };}

Basic service

Page 83: Embrace the Angular 2 Ethos in Angular 1.x

Dependency injection in ES6

Page 84: Embrace the Angular 2 Ethos in Angular 1.x

class CategoriesModel { constructor($q) { // $q is scoped to the constructor only this.categories = [ {"id": 0, "name": "Development"}, {"id": 1, "name": "Design"}, {"id": 2, "name": "Exercise"}, {"id": 3, "name": "Humor"} ]; } getCategories() { return $q.when(this.categories); // wont work! }}export default CategoriesModel;

Scoped to function

Page 85: Embrace the Angular 2 Ethos in Angular 1.x

class CategoriesModel { constructor($q) { 'ngInject'; // ng-annotate ftw this.$q = $q; // constructor assignment this.categories = [ {"id": 0, "name": "Development"}, {"id": 1, "name": "Design"}, {"id": 2, "name": "Exercise"}, {"id": 3, "name": "Humor"} ]; } getCategories() { return this.$q.when(this.categories); // now we can use $q }}export default CategoriesModel;

Local assignment

Page 86: Embrace the Angular 2 Ethos in Angular 1.x

class CategoriesModel { constructor($q) {}}

var CategoriesModel = (function () { function CategoriesModel($q) { } return CategoriesModel;}());

Exhibit A: TypeScript

Page 87: Embrace the Angular 2 Ethos in Angular 1.x

class CategoriesModel { constructor(private $q) {}}

var CategoriesModel = (function () { function CategoriesModel($q) { this.$q = $q; } return CategoriesModel;}());

Exhibit B: TypeScript

Page 88: Embrace the Angular 2 Ethos in Angular 1.x

Initialize components with lifecycle hooks

Page 89: Embrace the Angular 2 Ethos in Angular 1.x

Component Lifecycle Hooks

Components have well defined lifecycle hooks that allow us to perform specific operations during the lifespan of our component

We can use $onInit to know when our controller has been has been constructed and its bindings initialized

We can also use $onInit to know when a dependent component is available

We can use $onDestroy to perform clean when our component is removed

Page 90: Embrace the Angular 2 Ethos in Angular 1.x

class CategoriesController { constructor(CategoriesModel) { 'ngInject'; this.CategoriesModel = CategoriesModel; this.CategoriesModel.getCategories() .then(categories => this.categories = categories); }}export default CategoriesController;

Logic in constructor

Page 91: Embrace the Angular 2 Ethos in Angular 1.x

class CategoriesController { constructor(CategoriesModel) { 'ngInject'; this.CategoriesModel = CategoriesModel; } $onInit() { this.CategoriesModel.getCategories() .then(categories => this.categories = categories); }}export default CategoriesController;

Logic in lifecycle hook

Page 92: Embrace the Angular 2 Ethos in Angular 1.x

export class HomeComponent implements OnInit { message: string; constructor(private messageService: MessageService) { } ngOnInit() { this.message = this.messageService.getMessage(); }}

Logic in lifecycle hook

Page 93: Embrace the Angular 2 Ethos in Angular 1.x

Container and presentational components

Page 94: Embrace the Angular 2 Ethos in Angular 1.x
Page 95: Embrace the Angular 2 Ethos in Angular 1.x

Isolated Scope

Isolated scope secures the perimeter of your component so that you can control what goes in and out

Isolated scope is great for defining an API to your directive

There are now four ways to interact with isolated scope: via an attribute, one-way binding, two-way binding or an expression

Page 96: Embrace the Angular 2 Ethos in Angular 1.x

Inputs and Outputs

Inputs are denoted with an < or @ symbol

Outputs are denoted with an & symbol

@ indicates an attribute binding which is one-way and string based

< indicates a one-way binding that is object based

& is an expression binding which fires a callback on the parent component

Page 97: Embrace the Angular 2 Ethos in Angular 1.x
Page 98: Embrace the Angular 2 Ethos in Angular 1.x

bindings: { hero: '<', comment: '@'},bindings: { onDelete: '&', onUpdate: '&'},

Inputs and outputs

Page 99: Embrace the Angular 2 Ethos in Angular 1.x

<editable-field on-update="$ctrl.update('location', value)"></editable-field><button ng-click="$ctrl.onDelete({hero: $ctrl.hero})"> Delete</button>

Outputs

Page 100: Embrace the Angular 2 Ethos in Angular 1.x

import template from './category-item.html';import './category-item.styl';let categoryItemComponent = { bindings: { category: '<', selected: '&' }, template, controllerAs: 'categoryItemCtrl'};export default categoryItemComponent;

Inputs and outputs via bindings

Page 101: Embrace the Angular 2 Ethos in Angular 1.x

<div class="categoryItem" ng-click="categoryItemCtrl.selected({category:categoryItemCtrl.category})"> {{categoryItemCtrl.category.name}}</div>

Inputs and outputs in child template

Page 102: Embrace the Angular 2 Ethos in Angular 1.x

<ul class="nav nav-sidebar"> <li ng-repeat="category in categoriesListCtrl.categories"> <category-item category="category" selected="categoriesListCtrl.onCategorySelected(category)"> </category-item> </li></ul>

Inputs and outputs in parent template

Page 103: Embrace the Angular 2 Ethos in Angular 1.x

class CategoriesController { constructor(CategoriesModel) { 'ngInject'; this.CategoriesModel = CategoriesModel; } onCategorySelected(category) { console.log('CATEGORY SELECTED', category); }}export default CategoriesController;

Parent controller

Page 104: Embrace the Angular 2 Ethos in Angular 1.x

import {Component, Input} from '@angular/core';import {Item} from '../shared';@Component({ moduleId: module.id, selector: 'app-item-details', templateUrl: 'item-details.component.html', styleUrls: ['item-details.component.css']})export class ItemDetailsComponent { @Input() item: Item; addItem(): void { this.item.count += 1; };}

Inputs in child component

Page 105: Embrace the Angular 2 Ethos in Angular 1.x

<div> <div> <h2>{{ title }}</h2> </div> <div> {{ body }} </div></div><item-details *ngFor="let item of items" [item]="item"></item-details>

Inputs in parent template

Page 106: Embrace the Angular 2 Ethos in Angular 1.x

Create lightweight controllers by binding to models

Page 107: Embrace the Angular 2 Ethos in Angular 1.x

class BookmarksController { //... $onInit() { this.BookmarksModel.getBookmarks() .then(bookmarks => this.bookmarks = bookmarks); this.getCurrentCategory = this.CategoriesModel.getCurrentCategory.bind(this.CategoriesModel); // Lexical scope! :( }}export default BookmarksController;

Bind to model in controller

Page 108: Embrace the Angular 2 Ethos in Angular 1.x

<div class="bookmarks"> <div ng-repeat="bookmark in bookmarksListCtrl.bookmarks | filter:{category:bookmarksListCtrl.getCurrentCategory().name}"> <button type="button" class="close">&times;</button> <button type="button" class="btn btn-link"> <span class="glyphicon glyphicon-pencil"></span> </button> <a href="{{bookmark.url}}" target="_blank">{{bookmark.title}}</a> </div></div>

Transparent in template

Page 109: Embrace the Angular 2 Ethos in Angular 1.x

Isolating state mutations in components

Page 110: Embrace the Angular 2 Ethos in Angular 1.x

import template from './save-bookmark.html';import controller from './save-bookmark.controller';let saveBookmarkComponent = { bindings: { bookmark: '<', save: '&', cancel: '&' }, template, controller, controllerAs: 'saveBookmarkCtrl'};export default saveBookmarkComponent;

Child component

Page 111: Embrace the Angular 2 Ethos in Angular 1.x

<div class="save-bookmark"> <form ng-submit="saveBookmarkCtrl.save({bookmark:saveBookmarkCtrl.bookmark})" > <div class="form-group"> <label>Bookmark Title</label> <input type="text" ng-model="saveBookmarkCtrl.bookmark.title"> </div> <div class="form-group"> <label>Bookmark URL</label> <input type="text" ng-model="saveBookmarkCtrl.bookmark.url"> </div> <button type="submit">Save</button> <button type="button" ng-click="saveBookmarkCtrl.cancel()">Cancel</button> </form></div>

Child template

Page 112: Embrace the Angular 2 Ethos in Angular 1.x

class SaveController { $onChanges() { this.editedBookmark = Object.assign({}, this.bookmark); }}export default SaveController;

Lifecycle hook FTW!

Page 113: Embrace the Angular 2 Ethos in Angular 1.x

<div class="save-bookmark"> <form ng-submit="saveBookmarkCtrl.save({bookmark:saveBookmarkCtrl.editedBookmark})"> <div class="form-group"> <label>Bookmark Title</label> <input type="text" ng-model="saveBookmarkCtrl.editedBookmark.title"> </div> <div class="form-group"> <label>Bookmark URL</label> <input type="text" ng-model="saveBookmarkCtrl.editedBookmark.url"> </div> <button type="submit">Save</button> <button type="button" ng-click="saveBookmarkCtrl.cancel()">Cancel</button> </form> </div>

Updated template

Page 114: Embrace the Angular 2 Ethos in Angular 1.x

Communicate state changes with an event bus

Page 115: Embrace the Angular 2 Ethos in Angular 1.x

class CategoriesModel { constructor($q, $rootScope) { 'ngInject'; this.$q = $q; this.$rootScope = $rootScope; this.currentCategory = null; } setCurrentCategory(category) { this.currentCategory = category; this.$rootScope.$broadcast('onCurrentCategoryUpdated'); }}export default CategoriesModel;

Broadcast

Page 116: Embrace the Angular 2 Ethos in Angular 1.x

class BookmarksController { constructor($scope, CategoriesModel, BookmarksModel) { 'ngInject'; this.$scope = $scope; this.CategoriesModel = CategoriesModel; this.BookmarksModel = BookmarksModel; } $onInit() { this.BookmarksModel.getBookmarks() .then(bookmarks => this.bookmarks = bookmarks;); this.$scope.$on('onCurrentCategoryUpdated', this.reset.bind(this)); } reset() { this.currentBookmark = null; }}export default BookmarksController;

Listen

Page 117: Embrace the Angular 2 Ethos in Angular 1.x

Testing components with $componentController

Page 118: Embrace the Angular 2 Ethos in Angular 1.x

describe('Categories', () => { let component, $componentController, CategoriesModel; beforeEach(() => { window.module('categories'); window.module($provide => { $provide.value('CategoriesModel', { getCategories: () => { return { then: () => {} }; } }); }); }); beforeEach(inject((_$componentController_, _CategoriesModel_) => { CategoriesModel = _CategoriesModel_; $componentController = _$componentController_; })); describe('Controller', () => { it('calls CategoriesModel.getCategories immediately', () => { spyOn(CategoriesModel, 'getCategories').and.callThrough(); component = $componentController('categories', { CategoriesModel }); component.$onInit(); expect(CategoriesModel.getCategories).toHaveBeenCalled(); }); });});

Testing a component controller

Page 119: Embrace the Angular 2 Ethos in Angular 1.x

describe('Categories', () => { let component, $componentController, CategoriesModel; beforeEach(() => { window.module('categories'); window.module($provide => { $provide.value('CategoriesModel', { getCategories: () => { return { then: () => {} }; } }); }); }); beforeEach(inject((_$componentController_, _CategoriesModel_) => { CategoriesModel = _CategoriesModel_; $componentController = _$componentController_; })); describe('Controller', () => { it('calls CategoriesModel.getCategories immediately', () => { spyOn(CategoriesModel, 'getCategories').and.callThrough(); component = $componentController('categories', { CategoriesModel }); component.$onInit(); expect(CategoriesModel.getCategories).toHaveBeenCalled(); }); });});

Testing a component controller

Page 120: Embrace the Angular 2 Ethos in Angular 1.x

https://github.com/simpulton/dashing

Page 121: Embrace the Angular 2 Ethos in Angular 1.x

https://github.com/toddmotto/angular-1-5-components-app

Page 122: Embrace the Angular 2 Ethos in Angular 1.x

http://ngmigrate.telerik.com/

Page 123: Embrace the Angular 2 Ethos in Angular 1.x

https://www.angular2patterns.com/

Page 124: Embrace the Angular 2 Ethos in Angular 1.x

https://egghead.io/courses/using-angular-2-patterns-in-angular-1-x-apps

Page 125: Embrace the Angular 2 Ethos in Angular 1.x

https://ultimateangular.com/

Page 126: Embrace the Angular 2 Ethos in Angular 1.x

Thanks!