CodeCraft angular 2 from theory to practice

790 721 0
CodeCraft angular 2 from theory to practice

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

COPYRIGHT Copyright © 2016 Daolrevo Ltd trading as Code Craft All rights reserved This book or any portion thereof may not be reproduced or used in any manner whatsoever without the express written permission of the publisher except for the use of brief quotations in a book review codecraft.tv  CHANGELOG 1.0.1 (2017-01-05) ▪ Sample code and plunkers updated to Angular 2.4.1 ▪ Added practice activities to (most) sections ▪ Bug fix for child routing: empty child paths now also require a pathMatch: 'full' param to the route config ▪ Various spelling mistakes and typos corrected 1.0.0 (2016-11-16) ▪ First Release supporting Angular 2.1.0 ABOUT This book has been funded in large part through the generous support of my backers through kickstarter and is therefore released for FREE Please ensure you have the latest version by visiting ng2.codecraft.tv There is a also a video course version of this book, again please check ng2.codecraft.tv for details Cheers, Asim Hussain @jawache  OVERVIEW The course is going to begin with a quickstart We use a web editor called plunker and create a basic Angular application from scratch In this quickstart you get a 50,000 foot view of the major features of Angular and since we use a web editor it means you can get stuck in writing code ASAP without having to spend ages setting up your computer Let’s get started!  PLUNKER Later on I’ll be showing you how to install Angular and run the required development tools locally on your computer But for now we are going to use an online editor called plunker[http://plnkr.co/] The goal of this chapter is to explain how plunker works and the different files that make up an Angular 2 plunker Learning Outcomes ▪ Understand what plunker is, why and how we use it ▪ Understand the structure of an Angular plunker ▪ Know what each of the included libraries do What is plunker and how to use it? ▪ It’s a web based editor and development environment With it you can create, edit and run HTML, css and JavaScript files directly from your browser ▪ No setup required, everyone can code in the browser instantly ▪ Each plunker has it’s own unique URL which you can share with others so it’s a useful way to show others your work ▪ You can’t edit someone else’s plunker but you can fork it Forking creates a new plunker that you own with all the files copied across ▪ If your plunker has an index.html file pressing the Run button will preview the plunker in the preview pane Structure of an Angular Plunker An Angular 2 plunker is composed of: index.html The main HTML file which includes the required libraries and bootstraps our Angular application script.ts The main file in which we’ll be placing our Angular 2 code system.config.js Configuration for SystemJS which handles module loading (*) and compilation of TypeScript into JavaScript tsconfig.json Configuration for the TypeScript transpiler (*) (*) We will be covering these topics in more detail later In our index.html we are including 4 javascript files, like so: core-js The version of javascript that has the broadest support across all browsers is ES5 Then next version of javascript is ES6 ES6 does not have full support across all browsers yet (See https://kangax.github.io/compat-table/es6/) This library enables a browser which doesn’t have support for ES6 to run ES6 code, just maybe not in the most efficient way For further details take a look at the project homepage: https://github.com/zloirock/core-js zone "Zone is a mechanism for intercepting and keeping track of asynchronous work" https://github.com/angular/zone.js You don’t need to know details about this yet, it’s an advanced topic, but to summarise One of the problems with Angular 1 was that Angular didn’t know when things happened outside of Angular, for example in asynchronous callbacks When Angular 1 knew about callbacks it could do wonderful things, like automatically update the page When it didn’t know about callbacks it frustratingly didn’t update the page No matter how experienced you were with Angular 1 bugs with these kinds of issues cropped up time and time again Zones solves this problem, it keeps track of all pending asynchronous work and tells Angular when things happen So in Angular 2 you don’t have to worry about whether Angular knows about your callback or not, zones tracks this for you and notifies Angular when something happens reflect Angular 2 is written in TypeScript, this file lets us use a feature of TypeScript called annotations (or decorators) You’ll learn a lot more about annotations later on in this course SystemJS Instead of including all the files we need as script tags in our index.html, in Angular we break up all our code into files called modules We then leave it to a module loader to load the modules when they are needed, in the order they are needed It’s a complicated problem, in a browser we can’t make hundreds of requests to load JavaScript modules one at a time when a module loader requests them so a module loader needs to be clever It will become part of the core JavaScript language but until then we use a module loader, there are several available but the one we use in our plunker is SystemJS  If we build an application locally with the Angular command line tools it will use another module loader called Webpack systemjs.config.js SystemJS needs help to figure out when and how to load up certain modules, e.g if we ask for a module called @angular what does that mean? What should it load up? Which version? This configuration is stored in the systemjs.config.js file System.import Now we’ve loaded up the SystemJS library and configured it by loading up the systemjs.config.js file, we can use the System.import function to load up our script.ts file, like so: System.import('script.ts').catch(function(err) { console.error(err); }); Why not just add script.ts as a script tag?: Because in script.ts we include other modules, if we loaded via a script tag the browser doesn’t know how to load those other dependant js files By loading with System.import we make SystemJS figure out which dependant modules are required and loads the files for those automatically Summary We can code up Angular in the browser using an online editor called plunker It gives us the ability to try our Angular quickly without requiring complex setup It also gives us a unique URL so: We can quickly take a look at some code another person has written We can share our code with other people, which is especially useful when we are stuck with some broken code and need help Listing http://plnkr.co/edit/NzQ1skgIrliMIGgEPkp8?p=preview System.import('script.ts').catch(function (err) { {provide: 'RequiredDomain', useValue: 'codecraft.tv'} ] }) Then lets update our directive so that it uses both the provided RequiredDomain and the CodeCraftValidators.emailDomain factory function 10 11 12 class EmailDomainValidator { private valFn = ValidatorFn; constructor(@Inject('RequiredDomain') requiredDomain: string) { ① this.valFn = CodeCraftValidators.emailDomain(requiredDomain) ② } validate(control: FormControl) { ③ return this.valFn(control); } } ① We inject into the constructor the RequiredDomain token that we configured on the NgModule ② We use the CodeCraftValidators.emailDomain factory function to create a configured validator function and store that locally ③ Now whenever validate is called we simply call our configured validator function We also need to change the directives provider so it now points to using the class instead of the validator function we used previously: @Directive({ selector: '[emailDomain][ngModel]', providers: [ { provide: NG_VALIDATORS, useClass: EmailDomainValidator, ① multi: true 10 } ] }) ① This provider now provides an instance of the EmailDomainValidator class instead The validation error messages were also hardcoded to show codecraft.tv we need to make them show the required domain instead We can simply return the required domain in the error object and then use that in our error message, like so: 10 11 12 13 14 15 16 17 18 19 20 class CodeCraftValidators { static emailDomain(requiredDomain) { return function (control: FormControl) { console.log("here"); let email = control.value; if (email && email.indexOf("@") != -1) { let [_, domain] = email.split("@"); if (domain !== requiredDomain) { return { emailDomain: { parsedDomain: domain, requiredDomain: requiredDomain ① } } } } return null; } } } ① Pass the requiredDomain back in the error object Then we just just the requiredDomain variable in our validation message:

Email must be on the {{ email.errors.emailDomain.requiredDomain }} domain

Now if we change the domain name in the global provider like so: @NgModule({ providers: [ {provide: 'RequiredDomain', useValue: 'example.com'} ] }) The email domain validator now check against this new domain instead: Bindable template driven validators We can also configure out template validator directive via property binding in the template instead, like so: ① We can configure the validator via template property binding Then we update our EmailDomainValidator class so it can take the required domain as an input: 10 11 12 13 14 15 16 class EmailDomainValidator { @Input('emailDomain') emailDomain: string; ① private valFn = Validators.nullValidator; ngOnChanges(): void { ② if (this.emailDomain) { this.valFn = CodeCraftValidators.emailDomain(this.emailDomain) } else { this.valFn = Validators.nullValidator; } } validate(control: FormControl) { return this.valFn(control); } } ① First we create an input property on our directive named the same as our selector ② We know the emailDomain input property has been set when the ngOnChanges lifecycle function is called, this is where we now initialise our directive with the configured validator function Our directive needs one more change in order to function, we are providing this as a class, we need to provide as an alias so we get exactly the same instance provided to the validators 10 @Directive({ selector: '[emailDomain][ngModel]', providers: [ { provide: NG_VALIDATORS, useExisting: EmailDomainValidator, multi: true } ] }) Now we have a template driven form validator which can be configured via input property binding Summary For model driven forms we use a factory function which returns a validator function configured as we want For template driven forms we create a validator class, often re-using the same factory function as was used in model driven forms Model Driven Listing http://plnkr.co/edit/mLdGJG23cyPkhzncye5t?p=preview Listing 1 script.ts import { NgModule, Component, Pipe, OnInit } from '@angular/core'; import { ReactiveFormsModule, FormsModule, FormGroup, FormControl, Validators, FormBuilder } from '@angular/forms'; import {BrowserModule} from '@angular/platform-browser'; import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; function emailDomainValidator(control: FormControl) { let email = control.value; if (email && email.indexOf("@") != -1) { let [_, domain] = email.split("@"); if (domain !== "codecraft.tv") { return { emailDomain: { parsedDomain: domain } } } } return null; } @Component({ selector: 'model-form', template: ` First Name

Last Name is required

Last Name

Last Name is required

Email

Email is required

The email address must contain at least the @ character

Email must be on the codecraft.tv domain

Password

Password is required

Password must be 8 characters long, we need another {{password.errors.minlength.requiredLength - password.errors.minlength.actualLength}} characters

Language Please select a language {{lang}} {{myform.value | json}} ` }) class ModelFormComponent implements OnInit { langs: string[] = [ 'English', 'French', 'German', ]; myform: FormGroup; firstName: FormControl; lastName: FormControl; email: FormControl; password: FormControl; language: FormControl; ngOnInit() { this.createFormControls(); this.createForm(); } createFormControls() { this.firstName = new FormControl('', Validators.required); this.lastName = new FormControl('', Validators.required); this.email = new FormControl('', [ Validators.required, Validators.pattern("[^ @]*@[^ @]*"), emailDomainValidator ]); this.password = new FormControl('', [ Validators.required, Validators.minLength(8) ]); this.language = new FormControl(''); } createForm() { this.myform = new FormGroup({ name: new FormGroup({ firstName: this.firstName, lastName: this.lastName, }), email: this.email, password: this.password, language: this.language }); } } @Component({ selector: 'app', template: `` }) class AppComponent { } @NgModule({ imports: [ BrowserModule, FormsModule, ReactiveFormsModule], declarations: [ AppComponent, ModelFormComponent ], bootstrap: [ AppComponent ] }) class AppModule { } platformBrowserDynamic().bootstrapModule(AppModule); Template Driven Listing http://plnkr.co/edit/iXI6fJaV02xPN9PcRY3b?p=preview Listing 2 script.ts import { NgModule, Component, OnInit, ViewChild, Directive, Inject, Input, } from '@angular/core'; import { NG_VALIDATORS, FormsModule, FormGroup, FormControl, ValidatorFn, Validators } from '@angular/forms'; import {BrowserModule} from '@angular/platform-browser'; import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; class Signup { constructor(public firstName: string = '', public lastName: string = '', public email: string = '', public password: string = '', public language: string = '') { } } function emailDomainValidator(control: FormControl) { let email = control.value; if (email && email.indexOf("@") != -1) { let [_, domain] = email.split("@"); if (domain !== "codecraft.tv") { return { emailDomain: { parsedDomain: domain } } } } return null; } @Directive({ selector: '[emailDomain][ngModel]', providers: [ { provide: NG_VALIDATORS, useValue: emailDomainValidator, multi: true } ] }) class EmailDomainValidator { } @Component({ selector: 'template-form', template: ` First Name

First name is required

Last Name

Last name is required

Email

Email is required

Email must contain at least the @ character

Email must be on the codecraft.tv domain

>

Email must be on the {{ email.errors.emailDomain.requiredDomain }} domain

Password

Password is required

Password must be at least 8 characters long

Language Please select a language {{lang}} Submit {{f.value | json}} ` }) class TemplateFormComponent { model: Signup = new Signup(); @ViewChild('f') form: any; langs: string[] = [ 'English', 'French', 'German', ]; onSubmit() { if (this.form.valid) { console.log("Form Submitted!"); this.form.reset(); } } } @Component({ selector: 'app', template: `` }) class AppComponent { } @NgModule({ imports: [ BrowserModule, FormsModule ], declarations: [ AppComponent, TemplateFormComponent, EmailDomainValidator ], bootstrap: [ AppComponent ] }) class AppModule { } platformBrowserDynamic().bootstrapModule(AppModule); ... our example above @Component has one parameter called selector, this tells Angular 2 which tag to link this class too By setting the selector to joke we’ve told angular that whenever it finds a tag in the HTML like to use an instance of the JokeComponent class to control it... We’ve defined a component with a custom tag, added the tag to our HTML but we haven’t told Angular that we want to use Angular on this page To do that we need to do something called bootstrapping  In Angular 1 when we added np-app="module-name" to the top of the HTML page it bootstrapped the application... You might be asking yourself why Angular 2 doesn’t do this for us like it did in Angular 1? In Angular 2 bootstrapping is platform specific Angular 1 assumed that Angular would only ever be run in a browser, Angular 2 makes no such

Ngày đăng: 11/05/2017, 15:39

Từ khóa liên quan

Mục lục

  • Copyright

  • Changelog

  • About

  • Overview

  • Plunker

  • Intro to TypeScript

  • Writing our first app

  • String Interpolation

  • Looping

  • Property & Event Binding

  • Domain Model

  • Nesting Components & Inputs

  • User Interaction & Outputs

  • Wrapping Up

  • Activity

  • TypeScript Setup

  • Overview

  • Let

  • Const

  • Template Strings

Tài liệu cùng người dùng

Tài liệu liên quan