Pro Angular Learn to harness the power of modern web browsers from within your application’s code — Second Edition — Adam Freeman Pro Angular Second Edition Adam Freeman Pro Angular Adam Freeman MILTON KEYNES MK6 3PA, United Kingdom ISBN-13 (pbk): 978-1-4842-2306-2 DOI 10.1007/978-1-4842-2307-9 ISBN-13 (electronic): 978-1-4842-2307-9 Library of Congress Control Number: 2017932375 Copyright © 2017 by Adam Freeman This work is subject to copyright All rights are reserved by the Publisher, whether the whole or part of the material is concerned, specifically the rights of translation, reprinting, reuse of illustrations, recitation, broadcasting, reproduction on microfilms or in any other physical way, and transmission or information storage and retrieval, electronic adaptation, computer software, or by similar or dissimilar methodology now known or hereafter developed Trademarked names, logos, and images may appear in this book Rather than use a trademark symbol with every occurrence of a trademarked name, logo, or image we use the names, logos, and images only in an editorial fashion and to the benefit of the trademark owner, with no intention of infringement of the trademark The use in this publication of trade names, trademarks, service marks, and similar terms, even if they are not identified as such, is not to be taken as an expression of opinion as to whether or not they are subject to proprietary rights While the advice and information in this book are believed to be true and accurate at the date of publication, neither the authors nor the editors nor the publisher can accept any legal responsibility for any errors or omissions that may be made The publisher makes no warranty, express or implied, with respect to the material contained herein Managing Director: Welmoed Spahr Editorial Director: Todd Green Acquisitions Editor: Gwenan Spearing Development Editor: Laura Berendson Technical Reviewer: Fabio Claudio Ferracchiati Coordinating Editor: Mark Powers Copy Editor: Kim Wimpsett Compositor: SPi Global Indexer: SPi Global Artist: SPi Global Cover image designed by Freepik Distributed to the book trade worldwide by Springer Science+Business Media New York, 233 Spring Street, 6th Floor, New York, NY 10013 Phone 1-800-SPRINGER, fax (201) 348-4505, e-mail orders-ny@springer-sbm.com, or visit www.springeronline.com Apress Media, LLC is a California LLC and the sole member (owner) is Springer Science + Business Media Finance Inc (SSBM Finance Inc) SSBM Finance Inc is a Delaware corporation For information on translations, please e-mail rights@apress.com, or visit http://www.apress.com/ rights-permissions Apress titles may be purchased in bulk for academic, corporate, or promotional use eBook versions and licenses are also available for most titles For more information, reference our Print and eBook Bulk Sales web page at http://www.apress.com/bulk-sales Any source code or other supplementary material referenced by the author in this book is available to readers on GitHub via the book's product page, located at www.apress.com/9781484223062 For more detailed information, please visit http://www.apress.com/source-code Printed on acid-free paper Dedicated to my lovely wife, Jacqui Griffyth And to Peanut, who can also be lovely when she tries Contents at a Glance About the Author���������������������������������������������������������������������������������������������������xxv About the Technical Reviewer�����������������������������������������������������������������������������xxvii ■Chapter ■ 1: Getting Ready��������������������������������������������������������������������������������������� ■Chapter ■ 2: Your First Angular App������������������������������������������������������������������������� ■Chapter ■ 3: Putting Angular in Context����������������������������������������������������������������� 33 ■Chapter ■ 4: An HTML and CSS Primer������������������������������������������������������������������� 45 ■Chapter ■ 5: JavaScript and TypeScript: Part 1������������������������������������������������������ 65 ■Chapter ■ 6: JavaScript and TypeScript: Part 2������������������������������������������������������ 89 ■Chapter ■ 7: SportsStore: A Real Application������������������������������������������������������� 109 ■Chapter ■ 8: SportsStore: Orders and Checkout��������������������������������������������������� 141 ■Chapter ■ 9: SportsStore: Administration������������������������������������������������������������ 169 ■Chapter ■ 10: SportsStore: Deployment��������������������������������������������������������������� 195 ■Chapter ■ 11: Creating an Angular Project����������������������������������������������������������� 207 ■Chapter ■ 12: Using Data Bindings����������������������������������������������������������������������� 237 ■Chapter ■ 13: Using the Built-In in Directives������������������������������������������������������ 261 ■Chapter ■ 14: Using Events and Forms���������������������������������������������������������������� 289 ■Chapter ■ 15: Creating Attribute Directives��������������������������������������������������������� 337 ■Chapter ■ 16: Creating Structural Directives�������������������������������������������������������� 363 ■Chapter ■ 17: Understanding Components����������������������������������������������������������� 399 ■Chapter ■ 18: Using and Creating Pipes��������������������������������������������������������������� 433 v ■ Contents at a Glance ■Chapter ■ 19: Using Services������������������������������������������������������������������������������� 465 ■Chapter ■ 20: Using Service Providers����������������������������������������������������������������� 493 ■Chapter ■ 21: Using and Creating Modules���������������������������������������������������������� 525 ■Chapter ■ 22: Creating the Example Project��������������������������������������������������������� 551 ■Chapter ■ 23: Using Reactive Extensions������������������������������������������������������������� 569 ■Chapter ■ 24: Making Asynchronous HTTP Requests������������������������������������������� 593 ■Chapter ■ 25: Routing and Navigation: Part 1������������������������������������������������������ 621 ■Chapter ■ 26: Routing and Navigation: Part 2������������������������������������������������������ 655 ■Chapter ■ 27: Routing and Navigation: Part 3������������������������������������������������������ 683 ■Chapter ■ 28: Using Animation����������������������������������������������������������������������������� 721 ■Chapter ■ 29: Angular Unit Testing����������������������������������������������������������������������� 755 Index��������������������������������������������������������������������������������������������������������������������� 785 vi Contents About the Author���������������������������������������������������������������������������������������������������xxv About the Technical Reviewer�����������������������������������������������������������������������������xxvii ■Chapter ■ 1: Getting Ready��������������������������������������������������������������������������������������� What Do You Need to Know?�������������������������������������������������������������������������������������������� What Is the Structure of This Book?��������������������������������������������������������������������������������� Part 1: Getting Ready������������������������������������������������������������������������������������������������������������������������������ Part 2: Working with Angular������������������������������������������������������������������������������������������������������������������ Part 3: Advanced Angular Features��������������������������������������������������������������������������������������������������������� Are There Lots of Examples?�������������������������������������������������������������������������������������������� Where Can You Get the Example Code?��������������������������������������������������������������������������� How Do You Set Up Your Development Environment?������������������������������������������������������ Contacting the Author������������������������������������������������������������������������������������������������������� Summary�������������������������������������������������������������������������������������������������������������������������� ■Chapter ■ 2: Your First Angular App������������������������������������������������������������������������� Preparing the Development Environment������������������������������������������������������������������������� Installing Node.js and NPM��������������������������������������������������������������������������������������������������������������������� Installing an Editor���������������������������������������������������������������������������������������������������������������������������������� Installing a Browser�������������������������������������������������������������������������������������������������������������������������������� Creating and Preparing the Project���������������������������������������������������������������������������������� Creating the Package File����������������������������������������������������������������������������������������������������������������������� Installing the NPM Packages������������������������������������������������������������������������������������������������������������������ Configuring the TypeScript Compiler������������������������������������������������������������������������������������������������������ vii ■ Contents Creating the HTML File������������������������������������������������������������������������������������������������������������������������� 10 Starting the Server������������������������������������������������������������������������������������������������������������������������������� 11 Replacing the HTML Content���������������������������������������������������������������������������������������������������������������� 11 Adding Angular to the Project����������������������������������������������������������������������������������������� 14 Preparing the HTML File����������������������������������������������������������������������������������������������������������������������� 14 Creating a Data Model�������������������������������������������������������������������������������������������������������������������������� 15 Creating a Template������������������������������������������������������������������������������������������������������������������������������ 18 Creating a Component�������������������������������������������������������������������������������������������������������������������������� 18 Putting the Application Together����������������������������������������������������������������������������������������������������������� 20 Running the Application������������������������������������������������������������������������������������������������������������������������ 22 Adding Features to the Example Application������������������������������������������������������������������ 23 Adding the To-Do Table������������������������������������������������������������������������������������������������������������������������� 23 Creating a Two-Way Data Binding�������������������������������������������������������������������������������������������������������� 27 Adding To-Do Items������������������������������������������������������������������������������������������������������������������������������ 29 Summary������������������������������������������������������������������������������������������������������������������������ 31 ■Chapter ■ 3: Putting Angular in Context����������������������������������������������������������������� 33 Understanding Where Angular Excels���������������������������������������������������������������������������� 34 Understanding Round-Trip and Single-Page Applications�������������������������������������������������������������������� 34 Understanding the MVC Pattern������������������������������������������������������������������������������������� 36 Understanding Models�������������������������������������������������������������������������������������������������������������������������� 38 Understanding Controllers/Components����������������������������������������������������������������������������������������������� 40 Understanding Views/Templates���������������������������������������������������������������������������������������������������������� 41 Understanding RESTful Services������������������������������������������������������������������������������������ 41 Common Design Pitfalls������������������������������������������������������������������������������������������������� 43 Putting the Logic in the Wrong Place���������������������������������������������������������������������������������������������������� 43 Adopting the Data Store Data Format��������������������������������������������������������������������������������������������������� 44 Just Enough Knowledge to Cause Trouble�������������������������������������������������������������������������������������������� 44 Summary������������������������������������������������������������������������������������������������������������������������ 44 viii ■ Contents ■Chapter ■ 4: An HTML and CSS Primer������������������������������������������������������������������� 45 Preparing the Example Project��������������������������������������������������������������������������������������� 45 Understanding HTML������������������������������������������������������������������������������������������������������ 47 Understanding Void Elements��������������������������������������������������������������������������������������������������������������� 48 Understanding Attributes���������������������������������������������������������������������������������������������������������������������� 48 Applying Attributes Without Values������������������������������������������������������������������������������������������������������� 48 Quoting Literal Values in Attributes������������������������������������������������������������������������������������������������������ 49 Understanding Element Content����������������������������������������������������������������������������������������������������������� 49 Understanding the Document Structure����������������������������������������������������������������������������������������������� 49 Understanding Bootstrap������������������������������������������������������������������������������������������������ 51 Applying Basic Bootstrap Classes�������������������������������������������������������������������������������������������������������� 52 Using Bootstrap to Style Tables������������������������������������������������������������������������������������������������������������ 56 Using Bootstrap to Create Forms���������������������������������������������������������������������������������������������������������� 57 Using Bootstrap to Create Grids����������������������������������������������������������������������������������������������������������� 59 Summary������������������������������������������������������������������������������������������������������������������������ 64 ■Chapter ■ 5: JavaScript and TypeScript: Part 1������������������������������������������������������ 65 Preparing the Example Project��������������������������������������������������������������������������������������� 67 Creating the HTML and JavaScript Files����������������������������������������������������������������������������������������������� 67 Configuring the TypeScript Compiler���������������������������������������������������������������������������������������������������� 68 Running the Example Project��������������������������������������������������������������������������������������������������������������� 68 Understanding the Script Element��������������������������������������������������������������������������������� 69 Using a JavaScript Module Loader������������������������������������������������������������������������������������������������������� 70 Understanding the Basic Workflow������������������������������������������������������������������������������������������������������� 71 Using Statements����������������������������������������������������������������������������������������������������������� 72 Defining and Using Functions���������������������������������������������������������������������������������������� 72 Defining Functions with Parameters���������������������������������������������������������������������������������������������������� 74 Defining Functions That Return Results����������������������������������������������������������������������������������������������� 75 Using Functions as Arguments to Other Functions������������������������������������������������������������������������������� 76 ix ■ Contents Using Variables and Types���������������������������������������������������������������������������������������������� 77 Using the Primitive Types��������������������������������������������������������������������������������������������������������������������� 79 Using JavaScript Operators�������������������������������������������������������������������������������������������� 81 Using Conditional Statements�������������������������������������������������������������������������������������������������������������� 81 The Equality Operator vs the Identity Operator������������������������������������������������������������������������������������ 82 Explicitly Converting Types������������������������������������������������������������������������������������������������������������������� 83 Working with Arrays������������������������������������������������������������������������������������������������������� 85 Using an Array Literal��������������������������������������������������������������������������������������������������������������������������� 85 Reading and Modifying the Contents of an Array��������������������������������������������������������������������������������� 86 Enumerating the Contents of an Array�������������������������������������������������������������������������������������������������� 86 Using the Built-in Array Methods���������������������������������������������������������������������������������������������������������� 87 Summary������������������������������������������������������������������������������������������������������������������������ 88 ■Chapter ■ 6: JavaScript and TypeScript: Part 2������������������������������������������������������ 89 Preparing the Example Project��������������������������������������������������������������������������������������� 89 Working with Objects����������������������������������������������������������������������������������������������������� 90 Using Object Literals����������������������������������������������������������������������������������������������������������������������������� 91 Using Functions as Methods���������������������������������������������������������������������������������������������������������������� 91 Defining Classes����������������������������������������������������������������������������������������������������������������������������������� 92 Working with JavaScript Modules���������������������������������������������������������������������������������� 96 Creating Modules���������������������������������������������������������������������������������������������������������������������������������� 96 Importing from JavaScript Modules����������������������������������������������������������������������������������������������������� 97 Useful TypeScript Features������������������������������������������������������������������������������������������� 100 Using Type Annotations����������������������������������������������������������������������������������������������������������������������� 100 Using Tuples��������������������������������������������������������������������������������������������������������������������������������������� 106 Using Indexable Types������������������������������������������������������������������������������������������������������������������������ 106 Using Access Modifiers���������������������������������������������������������������������������������������������������������������������� 107 Summary���������������������������������������������������������������������������������������������������������������������� 108 x Chapter 29 ■ Angular Unit Testing Components are compiled using the TestBed.compileComponents method The compilation process is asynchronous, and the compileComponents method returns a Promise, which must be used to complete the test setup when the compilation is complete To make it easier to work with asynchronous operations in unit tests, the @angular/core/testing module contains a function called async, which is used with the beforeEach method Testing Component Events To demonstrate how to test for a component’s response to events, I defined a new property in the FirstComponent class and added a method to which the @HostBinding decorator has been applied, as shown in Listing 29-17 Listing 29-17. Adding Event Handling in the first.component.ts File import { Component, HostListener} from "@angular/core"; import { Product } from " /model/product.model"; import { Model } from " /model/repository.model"; @Component({ selector: "first", moduleId: module.id, templateUrl: "first.component.html" }) export class FirstComponent { constructor(private repository: Model) {} category: string = "Soccer"; highlighted: boolean = false; getProducts(): Product[] { return this.repository.getProducts() .filter(p => p.category == this.category); } @HostListener("mouseenter", ["$event.type"]) @HostListener("mouseleave", ["$event.type"]) setHighlight(type: string) { this.highlighted = type == "mouseenter"; } } The setHighlight method has configured so that it will be invoked when the host element’s mouseenter and mouseleave events are triggered Listing 29-18 updates the component’s template so that it uses the new property in a data binding Listing 29-18. Binding to a Property in the first.component.html File There are {{getProducts().length}} products 774 Chapter 29 ■ Angular Unit Testing Events can be triggered in unit tests through the triggerEventHandler method defined by the DebugElement class, as shown in Listing 29-19 Listing 29-19. Triggering Events in the first.component.spec.ts File import import import import import import { { { { { { TestBed, ComponentFixture, async } from "@angular/core/testing"; FirstComponent } from " /app/ondemand/first.component"; Product } from " /app/model/product.model"; Model } from " /app/model/repository.model"; DebugElement } from "@angular/core"; By } from "@angular/platform-browser"; describe("FirstComponent", () => { let let let let fixture: ComponentFixture; component: FirstComponent; debugElement: DebugElement; divElement: HTMLDivElement; let mockRepository = { getProducts: function () { return [ new Product(1, "test1", "Soccer", 100), new Product(2, "test2", "Chess", 100), new Product(3, "test3", "Soccer", 100), ] } } beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [FirstComponent], providers: [ { provide: Model, useValue: mockRepository } ] }); TestBed.compileComponents().then(() => { fixture = TestBed.createComponent(FirstComponent); component = fixture.componentInstance; debugElement = fixture.debugElement; divElement = debugElement.children[0].nativeElement; }); })); it("handles mouse events", () => { expect(component.highlighted).toBeFalsy(); expect(divElement.classList.contains("bg-success")).toBeFalsy(); debugElement.triggerEventHandler("mouseenter", new Event("mouseenter")); fixture.detectChanges(); expect(component.highlighted).toBeTruthy(); expect(divElement.classList.contains("bg-success")).toBeTruthy(); debugElement.triggerEventHandler("mouseleave", new Event("mouseleave")); 775 Chapter 29 ■ Angular Unit Testing fixture.detectChanges(); expect(component.highlighted).toBeFalsy(); expect(divElement.classList.contains("bg-success")).toBeFalsy(); }); }); The test in this listing checks the initial state of the component and the template and then triggers the mouseenter and mouseleave events, checking the effect that each has Testing Output Properties Testing output properties is a simple process because the EventEmitter objects used to implement them are Observable objects that can be subscribed to in unit tests Listing 29-20 adds an output property to the component under test Listing 29-20. Adding an Output Property in the first.component.ts File import { Component, HostListener, Output, EventEmitter} from "@angular/core"; import { Product } from " /model/product.model"; import { Model } from " /model/repository.model"; @Component({ selector: "first", moduleId: module.id, templateUrl: "first.component.html" }) export class FirstComponent { constructor(private repository: Model) {} category: string = "Soccer"; highlighted: boolean = false; @Output("pa-highlight") change = new EventEmitter(); getProducts(): Product[] { return this.repository.getProducts() .filter(p => p.category == this.category); } @HostListener("mouseenter", ["$event.type"]) @HostListener("mouseleave", ["$event.type"]) setHighlight(type: string) { this.highlighted = type == "mouseenter"; this.change.emit(this.highlighted); } } The component defines an output property called change, which is used to emit an event when the setHighlight method is called Listing 29-21 shows a unit test that targets the output property 776 Chapter 29 ■ Angular Unit Testing Listing 29-21. Testing an Output Property in the first.component.spec.ts File import import import import import import { { { { { { TestBed, ComponentFixture, async } from "@angular/core/testing"; FirstComponent } from " /app/ondemand/first.component"; Product } from " /app/model/product.model"; Model } from " /app/model/repository.model"; DebugElement } from "@angular/core"; By } from "@angular/platform-browser"; describe("FirstComponent", () => { let fixture: ComponentFixture; let component: FirstComponent; let debugElement: DebugElement; let mockRepository = { getProducts: function () { return [ new Product(1, "test1", "Soccer", 100), new Product(2, "test2", "Chess", 100), new Product(3, "test3", "Soccer", 100), ] } } beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [FirstComponent], providers: [ { provide: Model, useValue: mockRepository } ] }); TestBed.compileComponents().then(() => { fixture = TestBed.createComponent(FirstComponent); component = fixture.componentInstance; debugElement = fixture.debugElement; }); })); it("implements output property", () => { let highlighted: boolean; component.change.subscribe(value => highlighted = value); debugElement.triggerEventHandler("mouseenter", new Event("mouseenter")); expect(highlighted).toBeTruthy(); debugElement.triggerEventHandler("mouseleave", new Event("mouseleave")); expect(highlighted).toBeFalsy(); }); }); I could have invoked the component’s setHighlight method directly in the unit test, but instead I have chosen to trigger the mouseenter and mouseleave events, which will activate the output property indirectly Before triggering the events, I use the subscribe method to receive the event from the output property, which is then used to check for the expected outcomes 777 Chapter 29 ■ Angular Unit Testing Testing Input Properties The process for testing input properties requires a little extra work To get started, I added an input property to the FirstComponent class that is used to receive the data model repository, replacing the service that was received by the constructor, as shown in Listing 29-22 I have also removed the host event bindings and the output property in order to keep the example simple Listing 29-22. Adding an Input Property in the first.component.ts File import { Component, HostListener, Input } from "@angular/core"; import { Product } from " /model/product.model"; import { Model } from " /model/repository.model"; @Component({ selector: "first", moduleId: module.id, templateUrl: "first.component.html" }) export class FirstComponent { category: string = "Soccer"; highlighted: boolean = false; getProducts(): Product[] { return this.model == null ? [] : this.model.getProducts() .filter(p => p.category == this.category); } @Input("pa-model") model: Model; } The input property is set using an attribute called pa-model and is used within the getProducts method Listing 29-23 shows how to write a unit test that targets the input property Listing 29-23. Testing an Input Property in the first.component.spec.ts File import import import import import import import { { { { { { { TestBed, ComponentFixture, async } from "@angular/core/testing"; FirstComponent } from " /app/ondemand/first.component"; Product } from " /app/model/product.model"; Model } from " /app/model/repository.model"; DebugElement } from "@angular/core"; By } from "@angular/platform-browser"; Component, ViewChild } from "@angular/core"; @Component({ template: `` }) class TestComponent { constructor(public model: Model) { } 778 Chapter 29 ■ Angular Unit Testing @ViewChild(FirstComponent) firstComponent: FirstComponent; } describe("FirstComponent", () => { let fixture: ComponentFixture; let component: FirstComponent; let debugElement: DebugElement; let mockRepository = { getProducts: function () { return [ new Product(1, "test1", "Soccer", 100), new Product(2, "test2", "Chess", 100), new Product(3, "test3", "Soccer", 100), ] } } beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [FirstComponent, TestComponent], providers: [ { provide: Model, useValue: mockRepository } ] }); TestBed.compileComponents().then(() => { fixture = TestBed.createComponent(TestComponent); component = fixture.componentInstance.firstComponent; debugElement = fixture.debugElement.query(By.directive(FirstComponent)); }); })); it("receives the model through an input property", () => { component.category = "Chess"; fixture.detectChanges(); let products = mockRepository.getProducts() .filter(p => p.category == component.category); let componentProducts = component.getProducts(); for (let i = 0; i < componentProducts.length; i++) { expect(componentProducts[i]).toEqual(products[i]); } expect(debugElement.query(By.css("span")).nativeElement.textContent) .toContain(products.length); }); }); The trick here is to define a component that is only required to set up the test and whose template contains an element that matches the selector of the component you want to target In this example, I defined a component class called TestComponent with an inline template defined in the @Component 779 Chapter 29 ■ Angular Unit Testing decorator that contains a first element with a pa-model attribute, which corresponds to the @Input decorator applied to the FirstComponent class in Listing 29-22 The test component class is added to the declarations array for the testing module, and an instance is created using the TestBed.createComponent method I used the @ViewChild decorator in the TestComponent class so that I can get hold of the FirstComponent instance I require for the test To get the FirstComponent root element, I used the DebugElement.query method with the By.directive method The result is that I am able to access both the component and its root element for the test, which sets the category property and then validates the results both from the component and through the data binding in its template Testing with Asynchronous Operations Another area that requires special measures is dealing with asynchronous operations To demonstrate how this is done, Listing 29-24 modifies the component under test so that it uses the RestDataSource class, defined in Chapter 24, to get its data This isn’t a class that was intended for use outside of the model feature module, but it provides a useful set of asynchronous methods that return Observable objects, so I have broken through the intended structure of the application so that I can demonstrate the test technique Listing 29-24. Performing an Asynchronous Operation in the first.component.ts File import import import import import { { { { { Component, HostListener, Input } from "@angular/core"; Product } from " /model/product.model"; Model } from " /model/repository.model"; RestDataSource } from " /model/rest.datasource"; Observable } from "rxjs/Observable"; @Component({ selector: "first", moduleId: module.id, templateUrl: "first.component.html" }) export class FirstComponent { _category: string = "Soccer"; _products: Product[] = []; highlighted: boolean = false; constructor(public datasource: RestDataSource) {} ngOnInit() { this.updateData(); } getProducts(): Product[] { return this._products; } set category(newValue: string) { this._category; this.updateData(); } 780 Chapter 29 ■ Angular Unit Testing updateData() { this.datasource.getData() .subscribe(data => this._products = data .filter(p => p.category == this._category)); } } The component gets its data through the data source’s getData method, which returns an Observable object The component subscribes to the Observable and updates its _product property with the data objects, which is exposed to the template through the getProducts method Listing 29-25 shows how this kind of component can be tested using the tools Angular provides for working with asynchronous operations in unit tests Listing 29-25. Testing an Asynchronous Operation in the first.component.spec.ts File import import import import import import import import import import import { TestBed, ComponentFixture, async } from "@angular/core/testing"; { FirstComponent } from " /app/ondemand/first.component"; { Product } from " /app/model/product.model"; { Model } from " /app/model/repository.model"; { DebugElement } from "@angular/core"; { By } from "@angular/platform-browser"; { Component, ViewChild } from "@angular/core"; { RestDataSource } from " /app/model/rest.datasource"; { Observable } from "rxjs/Observable"; "rxjs/add/observable/from"; { Injectable } from "@angular/core"; @Injectable() class MockDataSource { public data = [ new Product(1, "test1", "Soccer", 100), new Product(2, "test2", "Chess", 100), new Product(3, "test3", "Soccer", 100), ]; getData(): Observable { return new Observable(obs => { setTimeout(() => obs.next(this.data), 1000); }) } } describe("FirstComponent", () => { let fixture: ComponentFixture; let component: FirstComponent; let dataSource = new MockDataSource(); beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [FirstComponent], 781 Chapter 29 ■ Angular Unit Testing providers: [ { provide: RestDataSource, useValue: dataSource } ] }); TestBed.compileComponents().then(() => { fixture = TestBed.createComponent(FirstComponent); component = fixture.componentInstance; }); })); it("performs async op", () => { dataSource.data.push(new Product(100, "test100", "Soccer", 100)); fixture.detectChanges(); fixture.whenStable().then(() => { expect(component.getProducts().length).toBe(3); }); }); }); The mock object in this example is more fully formed than the one I created previously, just to show different ways of achieving the same goal The important point to note is that the getData method it implements introduces a one-second delay before it returns the sample data This delay is important because it means that the effect of calling the detectChanges method in the unit test won’t affect the component immediately To deal with this, I call the whenStable method defined by the ComponentFixture class, which returns a Promise that resolves when all the changes have been fully processed This allows me to defer the assessment of the outcome of the test until the Observable returned by the mock data source has delivered its data to the component Testing an Angular Directive The process for testing directives is similar to the one required to test input properties, in that a test component and template are used to create an environment for testing in which the directive can be applied In order that I have a directive to test, I added a file called attr.directive.ts to the exampleApp/ app/ondemand folder and added the code shown in Listing 29-26 ■■Note I have shown an attribute directive in this example, but the technique in this section can be used to test structural directives equally well Listing 29-26. The Contents of the attr.directive.ts File in the exampleApp/app/ondemand Folder import { Directive, ElementRef, Attribute, Input, SimpleChange } from "@angular/core"; @Directive({ selector: "[pa-attr]" }) 782 Chapter 29 ■ Angular Unit Testing export class PaAttrDirective { constructor(private element: ElementRef) { } @Input("pa-attr") bgClass: string; ngOnChanges(changes: { [property: string]: SimpleChange }) { let change = changes["bgClass"]; let classList = this.element.nativeElement.classList; if (!change.isFirstChange() && classList.contains(change.previousValue)) { classList.remove(change.previousValue); } if (!classList.contains(change.currentValue)) { classList.add(change.currentValue); } } } This is an attribute directive based on an example from Chapter 15 To create a unit test that targets the directive, I added a file called attr.directive.spec.ts to the exampleApp/tests folder and added the code shown in Listing 29-27 Listing 29-27. The Contents of the attr.directive.spec.ts File in the exampleApp/tests Folder import import import import { { { { TestBed, ComponentFixture } from "@angular/core/testing"; Component, DebugElement, ViewChild } from "@angular/core"; By } from "@angular/platform-browser"; PaAttrDirective } from " /app/ondemand/attr.directive"; @Component({ template: `Test Content` }) class TestComponent { className = "initialClass" @ViewChild(PaAttrDirective) attrDirective: PaAttrDirective; } describe("PaAttrDirective", () => { let fixture: ComponentFixture; let directive: PaAttrDirective; let spanElement: HTMLSpanElement; beforeEach(() => { TestBed.configureTestingModule({ declarations: [TestComponent, PaAttrDirective], }); 783 Chapter 29 ■ Angular Unit Testing fixture = TestBed.createComponent(TestComponent); directive = fixture.componentInstance.attrDirective; spanElement = fixture.debugElement.query(By.css("span")).nativeElement; }); it("generates the correct number of elements", () => { fixture.detectChanges(); expect(directive.bgClass).toBe("initialClass"); expect(spanElement.className).toBe("initialClass"); fixture.componentInstance.className = "nextClass"; fixture.detectChanges(); expect(directive.bgClass).toBe("nextClass"); expect(spanElement.className).toBe("nextClass"); }); }); The text component has an inline template that applies the directive and a property that is referred to in the data binding The @ViewChild decorator provides access to the directive object that Angular creates when it processes the template, and the unit test is able to check that changing the value used by the data binding has an effect on the directive object and the element it has been applied to Summary In this chapter, I demonstrated the different ways in which Angular components and directives can be unit tested I explained the process for installing the test framework and tools and how to create the test bed through which tests are applied I demonstrated how to test the different aspects of components and how the same techniques can be applied to directives as well And that is all I have to teach you about Angular I started by creating a simple application and then took you on a comprehensive tour of the different building blocks in the framework, showing you how they can be created, configured, and applied to create web applications I wish you every success in your Angular projects, and I can only hope that you have enjoyed reading this book as much as I enjoyed writing it 784 Index A D Ahead-of-Time Compiler, 197 Ajax,41 See also HTTP Requests Animation, 721 element transitions, 737 parallel animations, 743 state transitions, 730 style groups, 744 timing functions, 739 trigger events, 750 triggers, 728, 731 @Attribute decorator, 346 Attribute directives, 337 host element attributes, 343 lifecycle, 348 Data bindings, 237 attribute bindings, 247 banana in a box, 304 class bindings, 249 event bindings, 293 expressions, 242 forms, 305 model-based, 324 validation, 307 one-way, 239 restrictions, 282 property bindings, 241 standard property bindings, 244 string interpolation, 246 style bindings, 254 two-way, 301 Decorators @Attribute, 346 @Component, 403 @Directive, 341 @HostBinding, 354 @HostListener, 354 @Inject, 501 @Injectable, 472 @Input, 347 @NgModule, 528 @Output, 351 @Pipe, 441 @ViewChild, 429 @ViewChildren, 429 Dependency injection See Services Development HTTP server, 216 @Directive decorator, 341 Directives, 261 attribute directives (see (Attribute Directives)) micro-templates, 265 ngClass, 251 B Banana in a box, 304 Bootstrap package, 51, 211 C classlist.js package, 211 @Component decorator, 403 Components, 399 child components, 409 projecting content, 414 styles, 417 templates, 406 external, 407 view children, 428 view encapsulation, 420 concurrently package, 212 Content children, 390 core-js package, 211 Cross-origin requests See HTTP Requests Cross-site scripting (CSS), 45 © Adam Freeman 2017 A Freeman, Pro Angular, DOI 10.1007/978-1-4842-2307-9 785 ■ INDEX Directives (cont.) ngFor, 269 ngIf, 264 ngModel, 303 ngStyle, 256 ngSwitch, 266 ngTemplateOutlet, 279 structural directives (see Structural Directives) Docker, 203 E Events, 293 custom events, 351 F, G Forms, 305 model-based, 324 validation, 307 H @HostBinding decorator, 354 Host Element Bindings, 354 @HostListener decorator, 354 HTML, 45 attributes, 48 document structure, 49 elements, 47 void elements, 48 HTTP Requests, 593 authentication, 175 configuring, 596 consolidating requests, 608 cross-origin requests, 610 errors, 615 headers, 613 HTTP service, 600 JSONP requests, 611 making requests, 600 responses, 601 RESTful web services, 598 classes, 92 getters and setters, 94 inheritance, 95 closures, 78 functions, 72 parameters, 74 results, 75 modules, 96 objects, 90 methods, 91 object literals, 91 operators, 81 primitives, 79 statements, 72 template strings, 80 variables, 77 JavaScript Module Loader, 228 jQuery, 35 JSON web token, 175 L lite-server package, 212, 216 M Maps, 388 Model View Controller pattern, 36 Modules, 525 Angular vs JavaScript modules, 529 feature modules, 533 root module, 528 MVC See Model View Controller pattern N @Injectable decorator, 472 @Inject decorator, 501 @Input decorator, 347 Input properties, 347 ngClass directive, 251 ngFor directive, 269 ngIf directive, 264 ngModel directive, 303 @NgModule decorator, 528 ngStyle directive, 256 ngSwitch directive, 266 ngTemplateOutlet directive, 279 Node.js, node package manager, Node package manager, 5, 209 J, K O JavaScript, 65 arrays, 85 arrow functions, 77 Observables See Reactive Extensions @Output decorator, 351 Output properties, 351 I 786 ■ INDEX P, Q @Pipe decorator, 441 Pipes, 433 arguments, 439 built-in, 448 async, 578 currency, 451 date, 456 json, 460 lowercase, 459 number, 449 percent, 454 slice, 461 uppercase, 459 combining, 443 pure and impure, 444 vertical bar, 439 Project app folder, 208 component overview, 220 creating, 207 data model, 223 index.html file, 208 overview, 220 package.json, 209 dependencies, 209 devDependencies, 209 scripts, 209 packages bootstrap, 211 classlist.js, 211 concurrently, 212, 219 core-js, 211 intl.js, 436 lite-server, 212 reflect-metadata, 211 rxjs, 211 systemjs, 211 typescript (see TypeScript) typings, 212 zone.js, 211 tsconfig.json file, 214 typings.json file, 213 watch processes, 219 R Reactive Extensions, 569 async pipe, 578 Observables, 574 Observers, 576 Subjects, 577 reflect-metadata package, 211 RESTful web services, 41, 598 authentication, 175 Routing, 621 child routes, 674 configuration, 627 properties, 627 dynamic feature modules, 707 events, 648 guarding, 685 preventing navigation, 693 navigation within a component, 666 ongoing route changes, 668 redirections, 665 resolvers, 686 route outlets, 676 route parameters, 639 routing component, 629 routing in code, 636, 645 routing links, 631 styles, 670 wildcards, 662 rxjs package See Reactive Extensions S Services, 465 declaring dependency, 473 registering, 475 service providers, 493 class provider, 498 existing service provider, 511 factory provider, 508 injectors, 512 local providers, 512 value provider, 506 using for testing, 484 Shaking the tree, 201 SportsStore, 109 bootstrap file, 120 data model, 121 JavaScript module loader, 120 root component, 118 root module, 119 routing, 148 guarding, 154 navigation, 151 web service, 166 authentication, 175 Structural directives, 363 change detection, 383 concise syntax, 370 content children, 390 context data, 374 iterating directives, 371 systemjs package See JavaScript Module Loader 787 ■ INDEX T tsconfig.json file, 214 TypeScript,65 See also JavaScript access modifiers, 107 indexable types, 106 tuples, 106 type annotations, 100 typescript package See TypeScript typings.json file, 213 typings package, 212 U Unit testing, 755 asynchronous operations, 780 components events, 774 templates, 772 788 data bindings, 769 directives, 782 input properties, 778 Jasmine, 763 output properties, 776 test beds, 765 URL Routing See Routing V @ViewChild decorator, 429 View children, 428 @ViewChildren decorator, 429 W, X, Y Webservices See HTTP Requests Z zone.js package, 211 .. .Pro Angular Second Edition Adam Freeman Pro Angular Adam Freeman MILTON KEYNES MK6 3PA, United Kingdom ISBN-13 (pbk):... translations, please e-mail rights @apress. com, or visit http://www .apress. com/ rights-permissions Apress titles may be purchased in bulk for academic, corporate, or promotional use eBook versions... Summary���������������������������������������������������������������������������������������������������������������������� 206 ■Chapter ■ 11: Creating an Angular Project����������������������������������������������������������� 207 Preparing a TypeScript Angular Development Project��������������������������������������������������