dl.ebooksworld.ir€¦ · Table of Contents Switching to Angular 2 Credits Foreword About the...

Preview:

Citation preview

SwitchingtoAngular2

TableofContents

SwitchingtoAngular2

Credits

Foreword

AbouttheAuthor

AbouttheReviewers

www.PacktPub.com

eBooks,discountoffers,andmore

Whysubscribe?

Preface

Whatthisbookcovers

Whatyouneedforthisbook

Whothisbookisfor

Conventions

Readerfeedback

Customersupport

Downloadingtheexamplecode

Errata

Piracy

Questions

1.GettingStartedwithAngular2

TheevolutionoftheWeb–timeforanewframework

TheevolutionofECMAScript

WebComponents

WebWorkers

LessonslearnedfromAngularJS1.xinthewild

Controllers

Scope

DependencyInjection

Server-siderendering

Applicationsthatscale

Templates

Changedetection

Summary

2.TheBuildingBlocksofanAngular2Application

AconceptualoverviewofAngular2

Changingdirectives

GettingtoknowAngular2components

Componentsinaction

ComponentsinAngular2

Pipes

Definingpipes

Changedetection

Classicalchangedetection

AngularJS1.xchangedetection

Inthezone.js

Simplifieddataflow

EnhancingAngularJS1.x’schangedetection

Understandingservices

Understandingthenewcomponent-basedrouter

Angular2routedefinitionsyntax

Summary

3.TypeScriptCrashCourse

IntroductiontoTypeScript

Compile-timetypechecking

BettersupportbytexteditorsandIDEs

There’sevenmoretoTypeScript

UsingTypeScript

InstallingTypeScriptwithnpm

RunningourfirstTypeScriptprogram

TypeScriptsyntaxandfeaturesintroducedbyES2015andES2016

ES2015arrowfunctions

UsingtheES2015andES2016classes

Definingvariableswithblockscope

Meta-programmingwithES2016decorators

Usingconfigurabledecorators

WritingmodularcodewithES2015

UsingtheES2015modulesyntax

Takingadvantageoftheimplicitasynchronousbehavior

Usingaliases

Importingallthemoduleexports

Defaultexports

ES2015moduleloader

ES2015andES2016recap

Takingadvantageofstatictyping

Usingexplicittypedefinitions

Thetypeany

UnderstandingthePrimitivetypes

TheEnumtypes

UnderstandingtheObjecttypes

TheArraytypes

TheFunctiontypes

Definingclasses

Usingaccessmodifiers

Defininginterfaces

Interfaceinheritance

Implementingmultipleinterfaces

FurtherexpressivenesswithTypeScriptdecorators

Writinggenericcodebyusingtypeparameters

Usinggenericfunctions

Havingmultipletypeparameters

WritinglessverbosecodewithTypeScript’stypeinference

Bestcommontype

Contextualtypeinference

Usingambienttypedefinitions

Usingpredefinedambienttypedefinitions

Customambienttypedefinitions

Definingts.dfiles

Summary

4.GettingStartedwithAngular2ComponentsandDirectives

TheHelloworld!applicationinAngular2

Settingupourenvironment

Installingourprojectrepository

PlayingwithAngular2andTypeScript

Diggingintotheindex

UsingAngular2directives

ThengFordirective

Improvedsemanticsofthedirectivessyntax

Declaringvariablesinsideatemplate

Usingsyntaxsugarintemplates

DefiningAngular2directives

Settingthedirective’sinputs

Understandingthedirective’sconstructor

Betterencapsulationofdirectives

UsingAngular2’sbuilt-indirectives

Introducingthecomponent’sviewencapsulation

Implementingthecomponent’scontrollers

Handlinguseractions

Usingadirectives’inputsandoutputs

Findingoutdirectives’inputsandoutputs

Definingthecomponent’sinputsandoutputs

Passinginputsandconsumingtheoutputs

Eventbubbling

Renamingtheinputsandoutputsofadirective

Analternativesyntaxtodefineinputsandoutputs

ExplainingAngular2’scontentprojection

BasiccontentprojectioninAngular2

Projectingmultiplecontentchunks

Nestingcomponents

UsingViewChildrenandContentChildren

ViewChildversusContentChild

Hookingintothecomponent’slifecycle

Theorderofexecution

DefininggenericviewswithTemplateRef

Understandingandenhancingthechangedetection

Theorderofexecutionofthechangedetectors

Changedetectionstrategies

PerformanceboostingwithimmutabledataandOnPush

UsingimmutabledatastructuresinAngular

Summary

5.DependencyInjectioninAngular2

WhydoIneedDependencyInjection?

DependencyInjectioninAngular2

BenefitsofDIinAngular2

Configuringaninjector

Dependencyresolutionwithgeneratedmetadata

Instantiatinganinjector

Introducingforwardreferences

Configuringproviders

Usingexistingproviders

Definingfactoriesforinstantiatingservices

Childinjectorsandvisibility

Buildingahierarchyofinjectors

Configuringdependencies

Usingthe@Selfdecorator

Skippingtheselfinjector

Havingoptionaldependencies

Usingmultiproviders

UsingDIwithcomponentsanddirectives

Introducingtheelementinjectors

Declaringprovidersfortheelementinjectors

ExploringDIwithcomponents

viewProvidersversusproviders

UsingAngular’sDIwithES5

Summary

6.WorkingwiththeAngular2RouterandForms

Developingthe“Codersrepository”application

ExploringtheAngular2router

Definingtherootcomponentandbootstrappingtheapplication

UsingPathLocationStrategy

Configuringrouteswith@RouteConfig

UsingrouterLinkandrouter-outlet

Lazy-loadingwithAsyncRoute

UsingAngular2forms

Developingtemplate-drivenforms

Diggingintothetemplate-drivenform’smarkup

Usingthebuilt-informvalidators

Definingcustomcontrolvalidators

UsingselectinputswithAngular

UsingtheNgFormdirective

Two-waydata-bindingwithAngular2

Storingtheformdata

Listingallthestoreddevelopers

Summary

7.ExplainingPipesandCommunicatingwithRESTfulServices

Developingmodel-drivenformsinAngular2

Usingcompositionofcontrolvalidators

ExploringtheHTTPmoduleofAngular

UsingAngular’sHTTPmodule

Definingparameterizedviews

Definingnestedroutes

Transformingdatawithpipes

Developingstatelesspipes

UsingAngular’sbuilt-inpipes

Developingstatefulpipes

Usingstatefulpipes

UsingAngular’sAsyncPipe

UsingAsyncPipewithobservables

Summary

8.DevelopmentExperienceandServer-SideRendering

RunningapplicationsinWebWorkers

WebWorkersandAngular2

BootstrappinganapplicationrunninginWebWorker

MigratinganapplicationtoWebWorker

MakinganapplicationcompatiblewithWebWorkers

Initialloadofasingle-pageapplication

InitialloadofaSPAwithserver-siderendering

Server-siderenderingwithAngular2

Enhancingourdevelopmentexperience

TexteditorsandIDEs

Hotreloading

HotreloadinginAngular2

Bootstrappingaprojectwithangular-cli

Usingangular-cli

Angular2quickstarters

Angular2seed

Angular2Webpackstarter

Summary

Index

SwitchingtoAngular2

SwitchingtoAngular2Copyright©2016PacktPublishing

Allrightsreserved.Nopartofthisbookmaybereproduced,storedinaretrievalsystem,ortransmittedinanyformorbyanymeans,withoutthepriorwrittenpermissionofthepublisher,exceptinthecaseofbriefquotationsembeddedincriticalarticlesorreviews.

Everyefforthasbeenmadeinthepreparationofthisbooktoensuretheaccuracyoftheinformationpresented.However,theinformationcontainedinthisbookissoldwithoutwarranty,eitherexpressorimplied.Neithertheauthor,norPacktPublishing,anditsdealersanddistributorswillbeheldliableforanydamagescausedorallegedtobecauseddirectlyorindirectlybythisbook.

PacktPublishinghasendeavoredtoprovidetrademarkinformationaboutallofthecompaniesandproductsmentionedinthisbookbytheappropriateuseofcapitals.However,PacktPublishingcannotguaranteetheaccuracyofthisinformation.

Firstpublished:March2016

Productionreference:1220316

PublishedbyPacktPublishingLtd.

LiveryPlace

35LiveryStreet

BirminghamB32PB,UK.

ISBN978-1-78588-620-1

www.packtpub.com

CreditsAuthor

MinkoGechev

Reviewers

MiškoHevery

DanielLamb

CommissioningEditor

EdwardGordon

AcquisitionEditor

KirkD’costa

ContentDevelopmentEditor

ShwetaPant

TechnicalEditor

MohitaVyas

CopyEditor

AkshataLobo

ProjectCoordinator

KinjalBari

Proofreader

SafisEditing

Indexer

MariammalChettiyar

Graphics

DishaHaria

ProductionCoordinator

NileshMohite

CoverWork

NileshMohite

ForewordAngular2isstillAngular,justbetter.ItisstillbuiltonthesameprinciplesthatmadeyouloveAngularJS:aquickandpowerfulsolutiontobuildingSinglePageApplications.InAngular2,theapplicationsarefaster,morevisibletoSEOandmobile,andarecross-platformready.SowhilstAngular2hasimprovedmanyoftheconceptsoverAngularJS,thephilosophyremainstruetotheoriginalvision.

SwitchingtoAngular2isabookthatrecognizesthis.Minko’sbooksuccessfullyhelpsyoutoswitchyourthinkingfromAngularJS1.xtoAngular2.FromyourfirstinteractionswithAngular2tothelast,thecoreconceptsofAngulararemaintainedthroughout.ThisguidewillhelpyoutoswitchtoAngular’snewwayofdoingthings.Minkoguidesyouthroughthechangesandnewfeaturesthathavebeenintroduced—components,directives,TypeScript,thenewrouter,andeverythingelseyouneedtostartusingAngular2foryournextproject.

AsAngular2takesupthechallengesetbytoday’schangingwebdevelopmentlandscapeandbuildsonthelegacyofAngularJS,it’sincrediblyimportantfortheAngularcommunitythattherearehighqualitylearningmaterialssuchasMinko’sbooktohelpAngulardevelopersmakethatfirstswitchovertothefuture.

MiškoHevery

CreatorofAngularJSandAngular2

AbouttheAuthorMinkoGechevisasoftwareengineerwhostronglybelievesinopensourcesoftware.Hehasdevelopednumeroussuchprojects,includingAngularJS1.xandAngular2styleguides,angular2-seed,astaticcodeanalyzerforAngular2projects,aspect.js,angular-aop,andmanyothers.HerunstrainingcoursesinJavaScript,Angular,andotherwebtechnologies.

Minkolovestoexperimentwiththeoreticalconceptsfromcomputerscienceandapplytheminpractice.HehasspokenaboutAngularandsoftwaredevelopmentatworldwideconferencesandmeetups,includingng-vegas,AngularConnect,ITWeekendKiev,AngularJS-SF,andAngularBerlin.

IwanttothankMiškoHeveryforhisgreatcontributionsinsoftwareengineeringandthetechnicalreviewofthisbook.Hehelpedmeprovideasprecisecontentaspossible.Tomakethecodesamplesforthebookeasytorun,Iusedangular2-seed.CorecontributoroftheprojectisLudovicHénin,whohelpedmakeitmuchmorethananAngular2starter.IalsowanttothanktoDanielLamb,RadoslavKirovandTeroParviainenwhogavemeextremelyvaluablefeedback!.

Icouldn’tcompletethebookwithoutthededicatedworkofthePacktPublishingteam.

Finally,IwanttothanktheteamatGoogleforgivingusAngular.Theyareaconstantinspiration.

AbouttheReviewersMiškoHeveryisthecreatoroftheAngularJSframework.Hehasapassionformakingcomplexthingssimple.HecurrentlyworksatGoogle,buthaspreviouslyworkedatAdobe,SunMicrosystems,Intel,andXerox,wherehebecameanexpertinbuildingwebapplicationsinweb-relatedtechnologies,suchasJava,JavaScript,Flex,andActionScript.

DanielLambisaseniorsoftwaredevelopmentprofessionalandanauthorwhoenjoyssharingtheknowledgehehasgained,specializinginlarge-scalearchitectureandfrontendwebdevelopment.Hisworkoverthelast16yearshasenabledhundredsofmillionstoengageandinteractduringbillionsofvisits.

www.PacktPub.com

eBooks,discountoffers,andmoreDidyouknowthatPacktofferseBookversionsofeverybookpublished,withPDFandePubfilesavailable?YoucanupgradetotheeBookversionatwww.PacktPub.comandasaprintbookcustomer,youareentitledtoadiscountontheeBookcopy.Getintouchwithusat<customercare@packtpub.com>formoredetails.

Atwww.PacktPub.com,youcanalsoreadacollectionoffreetechnicalarticles,signupforarangeoffreenewslettersandreceiveexclusivediscountsandoffersonPacktbooksandeBooks.

https://www2.packtpub.com/books/subscription/packtlib

DoyouneedinstantsolutionstoyourITquestions?PacktLibisPackt’sonlinedigitalbooklibrary.Here,youcansearch,access,andreadPackt’sentirelibraryofbooks.

Whysubscribe?FullysearchableacrosseverybookpublishedbyPacktCopyandpaste,print,andbookmarkcontentOndemandandaccessibleviaawebbrowser

PrefaceAngularJSisaJavaScriptdevelopmentframeworkthatmakesbuildingwebapplicationseasier.Itisusedtodayinlarge-scale,high-trafficwebsitesthatstrugglewithunderperformanceandportabilityissues,aswellasSEOunfriendlinessandcomplexityatscale.Angular2changesthese.

Itisthemodernframeworkyouneedtobuildperformantandrobustwebapplications.SwitchingtoAngular2isthequickestwaytogettogripswithAngular2,anditwillhelpyoutransitionintothebravenewworldofAngular2.

Bytheendofthebook,you’llbereadytostartbuildingquickandefficientAngular2applicationsthattakeadvantageofallthenewfeaturesonoffer.

WhatthisbookcoversChapter1,GettingstartedwithAngular2,kicksoffourjourneyintotheworldofAngular2.Itdescribesthemainreasonsbehindthedesigndecisionsoftheframework.Wewilllookintothetwomaindriversbehindtheshapeoftheframework—thecurrentstateoftheWebandtheevolutionoffrontenddevelopment.

Chapter2,TheBuildingBlocksofanAngular2Application,givesanoverviewofthecoreconceptsintroducedbyAngular2.We’llexplorehowthefoundationalbuildingblocksforthedevelopmentofapplicationsprovidedbyAngularJS1.xdifferfromtheonesinthelastmajorversionoftheframework.

Chapter3,TypeScriptCrashCourse,explainsthatalthoughAngular2islanguageagnostic,Google’srecommendationistotakeadvantageofthestatictypingofTypeScript.Inthischapter,you’lllearnalltheessentialsyntaxyouneedtodevelopAngular2applicationsinTypeScript!

Chapter4,GettingStartedwithAngular2ComponentsandDirectives,describesthecorebuildingblocksfordevelopingtheuserinterfaceofourapplications—directivesandcomponents.Wewilldiveintoconceptssuchasviewencapsulation,contentprojection,inputsandoutputs,changedetectionstrategies,andmore.We’lldiscussadvancedtopicssuchastemplatereferencesandspeedingupourapplicationsusingimmutabledata.

Chapter5,DependencyInjectioninAngular2,coversoneofthemostpowerfulfeaturesintheframework,whichwasinitiallyintroducedbyAngularJS1.x:itsdependencyinjectionmechanism.Itallowsustowritemoremaintainable,testable,andunderstandablecode.Bytheendofthischapter,wewillknowhowtodefinethebusinesslogicinservicesandgluethemtogetherwiththeUIthroughtheDImechanism.Wewillalsolookintosomemoreadvancedconcepts,suchastheinjectorshierarchy,configuringproviders,andmore.

Chapter6,WorkingwiththeAngular2RouterandForms,exploresthenewmoduleformanagingformsintheprocessofdevelopingareal-lifeapplication.Wewillalsoimplementapagethatshowstheenteredthroughtheformdata.Intheend,wewillgluetheindividualpagestogetherintoanapplicationbyusingthecomponent-basedrouter.

Chapter7,ExplainingPipesandCommunicatingwithRESTfulservices,divesintotherouterandtheformsmodulesindetail.Here,wewillexplorehowwecandevelopmodel-drivenformsanddefineparameterizedandchildroutes.WewillalsoexplaintheHTTPmoduleandseehowwecandeveloppureandimpurepipes.

Chapter8,SEOandAngular2intheRealWorld,exploressomeadvancedtopicsintheAngular2applicationdevelopment,suchasrunninganapplicationinWebWorkersandserver-siderendering.Inthesecondpartofthechapter,wewillexploretoolsthatcaneaseourdailylifeasdevelopers,suchasangular-cli,andangular2-seed,explaintheconceptofhotreloading,andmore.

WhatyouneedforthisbookAllyouneedtoworkthroughmostoftheexamplesinthisbookisasimpletexteditororanIDE,Node.js,TypeScriptinstalled,Internetaccess,andabrowser.

Eachchapterintroducesthesoftwarerequirementsforrunningtheprovidedsnippets.

WhothisbookisforDoyouwanttojumpinatthedeependofAngular2?Orperhapsyou’reinterestedinassessingthechangesbeforemovingover?Ifso,thenSwitchingtoAngular2isthebookforyou.

Togetthemostoutofthebook,you’llneedtohavebasicunderstandingofAngularJS1.xandhaveagoodunderstandingofJavaScript.NoknowledgeofthechangesmadetoAngular2isrequiredtofollowalong.

ConventionsInthisbook,youwillfindanumberoftextstylesthatdistinguishbetweendifferentkindsofinformation.Herearesomeexamplesofthesestylesandanexplanationoftheirmeaning.

Codewordsintext,databasetablenames,foldernames,filenames,fileextensions,pathnames,dummyURLs,userinput,andTwitterhandlesareshownasfollows:“Youshouldseethesameresult,butwithoutthetest.jsfilestoredonthedisk.”

Ablockofcodeissetasfollows:

@Injectable()

classSocket{

constructor(privatebuffer:Buffer){}

}

letinjector=Injector.resolveAndCreate([

provide(BUFFER_SIZE,{useValue:42}),

Buffer,

Socket

]);

injector.get(Socket);

Whenwewishtodrawyourattentiontoaparticularpartofacodeblock,therelevantlinesoritemsaresetinbold:

letinjector=Injector.resolveAndCreate([

provide(BUFFER_SIZE,{useValue:42}),

Buffer,

Socket

]);

Eachcodesnippetwhichisintherepositorywiththecodefromthisbookstartswithacommentwithitscorrespondingfilelocation,relativetotheappdirectory:

//ch5/ts/injector-basics/forward-ref.ts

@Injectable()

classSocket{

constructor(privatebuffer:Buffer){…}

}

Newtermsandimportantwordsareshowninbold.Wordsthatyouseeonthescreen,forexample,inmenusordialogboxes,appearinthetextlikethis:“Whenthemarkupisrenderedontothescreen,allthattheuserwillseeisthelabel:Loading….”

NoteWarningsorimportantnotesappearinaboxlikethis.

TipTipsandtricksappearlikethis.

ReaderfeedbackFeedbackfromourreadersisalwayswelcome.Letusknowwhatyouthinkaboutthisbook—whatyoulikedordisliked.Readerfeedbackisimportantforusasithelpsusdeveloptitlesthatyouwillreallygetthemostoutof.

Tosendusgeneralfeedback,simplye-mail<feedback@packtpub.com>,andmentionthebook’stitleinthesubjectofyourmessage.

Ifthereisatopicthatyouhaveexpertiseinandyouareinterestedineitherwritingorcontributingtoabook,seeourauthorguideatwww.packtpub.com/authors.

CustomersupportNowthatyouaretheproudownerofaPacktbook,wehaveanumberofthingstohelpyoutogetthemostfromyourpurchase.

DownloadingtheexamplecodeYoucandownloadtheexamplecodefilesforthisbookfromGitHubathttps://github.com/mgechev/switching-to-angular2.

Youcandownloadthecodefilesbyfollowingthesesteps:

1. EntertheURLinyourbrowser’saddressbar.2. Clickonthe“DownloadZIP”buttonlocatedinthemid-rightpartofthescreen.

Youcanalsodownloadtheexamplecodefilesforthisbookfromyouraccountathttp://www.packtpub.com.Ifyoupurchasedthisbookelsewhere,youcanvisithttp://www.packtpub.com/supportandregistertohavethefilese-maileddirectlytoyou.

Youcanalsodownloadthecodefilesbyfollowingthesesteps:

1. Loginorregistertoourwebsiteusingyoure-mailaddressandpassword.2. HoverthemousepointerontheSUPPORTtabatthetop.3. ClickonCodeDownloads&Errata.4. EnterthenameofthebookintheSearchbox.5. Selectthebookforwhichyou’relookingtodownloadthecodefiles.6. Choosefromthedrop-downmenuwhereyoupurchasedthisbookfrom.7. ClickonCodeDownload.

Oncethefileisdownloaded,pleasemakesurethatyouunziporextractthefolderusingthelatestversionof:

WinRAR/7-ZipforWindowsZipeg/iZip/UnRarXforMac7-Zip/PeaZipforLinuxChapters3and4containfurtherinformationfortheinstallationprocess.

ErrataAlthoughwehavetakeneverycaretoensuretheaccuracyofourcontent,mistakesdohappen.Ifyoufindamistakeinoneofourbooks—maybeamistakeinthetextorthecode—wewouldbegratefulifyoucouldreportthistous.Bydoingso,youcansaveotherreadersfromfrustrationandhelpusimprovesubsequentversionsofthisbook.Ifyoufindanyerrata,pleasereportthembyvisitinghttp://www.packtpub.com/submit-errata,selectingyourbook,clickingontheErrataSubmissionFormlink,andenteringthedetailsofyourerrata.Onceyourerrataareverified,yoursubmissionwillbeacceptedandtheerratawillbeuploadedtoourwebsiteoraddedtoanylistofexistingerrataundertheErratasectionofthattitle.

Toviewthepreviouslysubmittederrata,gotohttps://www.packtpub.com/books/content/supportandenterthenameofthebookinthesearchfield.TherequiredinformationwillappearundertheErratasection.

PiracyPiracyofcopyrightedmaterialontheInternetisanongoingproblemacrossallmedia.AtPackt,wetaketheprotectionofourcopyrightandlicensesveryseriously.IfyoucomeacrossanyillegalcopiesofourworksinanyformontheInternet,pleaseprovideuswiththelocationaddressorwebsitenameimmediatelysothatwecanpursuearemedy.

Pleasecontactusat<copyright@packtpub.com>withalinktothesuspectedpiratedmaterial.

Weappreciateyourhelpinprotectingourauthorsandourabilitytobringyouvaluablecontent.

QuestionsIfyouhaveaproblemwithanyaspectofthisbook,youcancontactusat<questions@packtpub.com>,andwewilldoourbesttoaddresstheproblem.

Chapter1.GettingStartedwithAngular2OnSeptember18,2014,thefirstpubliccommitwaspushedtotheAngular2repository.Afewweekslater,atng-europe,IgorandTobiasfromthecoreteamgaveashortoverviewofwhatAngular2wasexpectedtobe.Thevisionatthattimewasfarfromfinal;however,onethingwascertain—thenewversionoftheframeworkwouldbeentirelydifferentfromAngularJS1.x.

Thisannouncementbroughtalotofquestionsandcontroversy.Thereasonsbehindthedrasticchangeswerequiteclear—AngularJS1.xwasnolongerabletotakefulladvantageoftheevolvedWebandtocompletelysatisfytherequirementsoflarge-scaleJavaScriptapplications.AnewframeworkwouldletAngulardeveloperscapitalizeondevelopmentsinwebtechnologyinsimplerandmoredirectways.Yet,peoplewereconcerned.Oneofthebiggestnightmareswithbackwardincompatibilityfordevelopersisthemigrationoftheircurrentcodebasestothenewversionofthethird-partysoftwaretheyuse.InAngular’scase,afterthatfirstannouncement,migrationlookeddaunting,evenimpossible.Later,atng-conf2015andng-vegas,differentmigrationstrategieswereintroduced.TheAngularcommunitycametogetherandsharedadditionalideas,anticipatingthebenefitsofAngular2whilepreservingthethingslearnedfromAngularJS1.x.

Thisbookisapartofthatproject.MakingtheupgradetoAngular2isnon-trivial,butitisworthit.ThemaindriversbehindthedrasticchangesinAngular2anditslackofbackwardcompatibilityaretheevolutionoftheWeb,andthelessonslearnedfromtheusageofAngularJS1.xinthewild.SwitchingtoAngular2willhelpyoutolearnthenewframeworkbyunderstandinghowwegothereandwhyAngular’snewfeaturesmakeintuitivesenseforthemodernWebinbuildinghigh-performance,scalable,single-pageapplications.

TheevolutionoftheWeb–timeforanewframeworkInthelastcoupleofyears,theWebhasevolvedinbigsteps.DuringtheimplementationofECMAScript5,theECMAScript6standardstarteditsdevelopment(nowknownasECMAScript2015orES2015).ES2015introducedmanychangesinthelanguagesuchasaddingbuilt-inlanguagesupportformodules,blockscopevariabledefinition,andalotofsyntacticalsugar,suchasclassesanddestructuring.

Meanwhile,WebComponentswereinvented.WebComponentsallowustodefinecustomHTMLelementsandattachbehaviortothem.SinceitishardtoextendtheexistingHTMLelementswithnewones(suchasdialogs,charts,grids,andmore)mostlybecauseofthetimerequiredforconsolidationandstandardizationoftheirAPIs,abettersolutionistoallowdeveloperstoextendtheexistingelementsthewaytheywant.WebComponentsprovideuswithanumberofbenefits,includingbetterencapsulation,bettersemanticsofthemarkupweproduce,bettermodularity,andeasiercommunicationbetweendevelopersanddesigners.

WeknowthatJavaScriptisasingle-threadedlanguage.Initially,itwasdevelopedforsimpleclient-sidescripting,butovertime,itsrolehasshiftedquiteabit.NowwithHTML5,wehavedifferentAPIsthatallowaudioandvideoprocessing,communicationwithexternalservicesthroughatwo-directionalcommunicationchannel,transferringandprocessingbigchunksofrawdata,andmore.Alltheseheavycomputationsinthemainthreadmaycreateapooruserexperience.Theymayintroducefreezingoftheuserinterfacewhentime-consumingcomputationsarebeingperformed.ThisledtothedevelopmentofWebWorkers,whichallowtheexecutionofthescriptsinthebackgroundthatcommunicatewiththemainthreadthroughmessagepassing.Thisway,multi-threadedprogramminghasbeenbroughttothebrowser.

SomeoftheseAPIswereintroducedafterthedevelopmentofAngularJS1.xhadbegun;that’swhytheframeworkwasn’tbuildwithmostoftheminmind.However,exploitingtheAPIsgivesdevelopersmanybenefits,suchas:

Significantperformanceimprovements.Developmentofsoftwarewithbetterqualitycharacteristics.

Nowlet’sbrieflydiscusshoweachofthesetechnologieshasbeenmadepartofthenewAngularcoreandwhy.

TheevolutionofECMAScriptNowadays,browservendorsarereleasingnewfeaturesinshortiterations,andusersreceiveupdatesquiteoften.ThishelpsmovetheWebforwardbyallowingdeveloperstotakeadvantageofthebleeding-edgetechnologies,aimingtoimprovetheWeb.ES2015isalreadystandardized.Theimplementationofthelatestversionofthelanguagehasalreadystartedinthemajorbrowsers.Learningthenewsyntaxandtakingadvantageofitwillnotonlyincreaseourproductivityasdevelopers,butalsoprepareusforthenearfuturewhenallthebrowserswillhavefullsupportforit.Thismakesitessentialtostartusingthelatestsyntaxnow.

Someprojects’requirementsmayenforceustosupportolderbrowsers,whichdoesnotsupportanyES2015features.Inthiscase,wecandirectlywriteECMAScript5,whichhasdifferentsyntaxbutequivalentsemanticstoES2015.However,wecantakeadvantageoftheprocessoftranspilation.UsingatranspilerinourbuildprocessallowsustotakeadvantageofthenewsyntaxbywritingES2015andtranslatingittoatargetlanguagethatissupportedbythebrowsers.

AngularJShasbeenaroundsince2009.Backthen,thefrontendofmostwebsiteswaspoweredbyECMAScript3,thelastmainreleaseofECMAScriptpriortoECMAScript5.Thisautomaticallymeantthatthelanguageusedfortheframework’simplementationwasECMAScript3.TakingadvantageofthenewversionofthelanguagerequiresportingoftheentiretyofAngularJS1.xtoES2015.

Fromthebeginning,Angular2tookintoaccountthecurrentstateoftheWebbybringingthelatestsyntaxintheframework.AlthoughAngular2iswrittenwithasupersetofES2016(TypeScript,whichwe’regoingtotakealookatinamoment),itallowsdeveloperstouselanguageoftheirownpreference.WecanuseES2015or,ifweprefernottohaveanyintermediatepreprocessingofourcodeandsimplifythebuildprocess,evenECMAScript5.

WebComponentsThefirstpublicdraftofWebComponentswaspublishedonMay22,2012,aboutthreeyearsafterthereleaseofAngularJS1.x.Asmentioned,theWebComponentsstandardallowsustocreatecustomelementsandattachbehaviortothem.Itsoundsfamiliar;we’vealreadyusedsimilarconceptinthedevelopmentoftheuserinterfaceinAngularJS1.xapplications.WebComponentssoundlikeanalternativetoAngulardirectives;however,theyhavemoreintuitiveAPI,richerfunctionality,andbuilt-inbrowsersupport.Theyintroducedafewotherbenefitssuchasbetterencapsulation,whichisveryimportant,forexample,inhandlingCSS-stylecollisions.

ApossiblestrategyforaddingWebComponentssupportinAngularJS1.xistochangethedirectivesimplementationandintroduceprimitivesofthenewstandardintheDOMcompiler.AsAngulardevelopers,weknowhowpowerfulbutalsocomplexthedirectivesAPIis.ItincludesalotofpropertiessuchaspostLink,preLink,compile,restrict,scope,controller,andmanymore,andofcourse,ourfavoritetransclude.Approvedasstandard,WebComponentswillbeimplementedonamuchlowerlevelinthebrowsers,whichintroducesplentyofbenefitssuchasbetterperformanceandnativeAPI.

DuringtheimplementationofWebComponents,alotofwebspecialistsmetthesameproblemstheAngularteamdidwhendevelopingthedirectivesAPIandcameupwithsimilarideas.GooddesigndecisionsbehindWebComponentsincludethecontentelement,whichdealswiththeinfamoustransclusionprobleminAngularJS1.x.SinceboththedirectivesAPIandWebComponentssolvesimilarproblemsindifferentways,keepingthedirectivesAPIontopofWebComponentswouldhavebeenredundantandaddedunnecessarycomplexity.That’swhytheAngularcoreteamdecidedtostartfromthebeginningbybuildingontopofWebComponentsandtakingfulladvantageofthenewstandard.WebComponentsinvolvesnewfeatures,someofthemnotyetimplementedbyallbrowsers.Incaseourapplicationisruninabrowser,whichdoesnotsupportanyofthesefeaturesnatively,Angular2emulatesthem.Anexampleforthisisthecontentelementpolyfilledwiththedirective,ng-content.

WebWorkersJavaScriptisknownforitseventloop.UsuallyJavaScriptprogramsareexecutedinasinglethreadanddifferenteventsarescheduledbybeingpushedinaqueueandprocessedsequentially,intheorderoftheirarrival.However,thiscomputationalstrategyisnoteffectivewhenoneofthescheduledeventsrequiresalotofcomputationaltime.Insuchcasestheevent’shandlingisgoingtoblockthemainthreadandallothereventsarenotgoingtobehandleduntilthetimeconsumingcomputationiscompleteandpassestheexecutiontothenextoneinthequeue.Asimpleexampleofthisisamouseclickthattriggersanevent,inwhichcallbackwedosomeaudioprocessingusingtheHTML5audioAPI.Iftheprocessedaudiotrackisbigandthealgorithmrunningoveritisheavy,thiswillaffecttheuser’sexperiencebyfreezingtheUIuntiltheexecutioniscomplete.

TheWebWorkerAPIwasintroducedinordertopreventsuchpitfalls.Itallowsexecutionofheavycomputationsinsidethecontextofdifferentthread,whichleavesthemainthreadofexecutionfree,capableofhandlinguserinputandrenderingtheuserinterface.

HowcanwetakeadvantageofthisinAngular?Inordertoanswerthisquestion,let’sthinkabouthowthingsworkinAngularJS1.x.Whatifwehaveanenterpriseapplication,whichprocessesahugeamountofdatathatneedstoberenderedonthescreenusingdatabinding?Foreachbinding,anewwatcherwillbeadded.Oncethedigestloopisrun,itwillloopoverallthewatchers,executetheexpressionsassociatedwiththem,andcomparethereturnedresultswiththeresultsgainedfromthepreviousiteration.Wehaveafewslowdownshere:

Theiterationoverlargenumberofwatchers.Evaluationofexpressioningivencontext.Copyofthereturnedresult.Comparisonbetweenthecurrentresultoftheexpression’sevaluationandthepreviousone.

Allthesestepscouldbequiteslowdependingonthesizeoftheinput.Ifthedigestloopinvolvesheavycomputations,whynotmoveittoaWebWorker?WhynotrunthedigestloopinsideWebWorker,getthechangedbindings,andapplythemtotheDOM?

Therewereexperimentsbythecommunity,whichaimedforthisresult.However,theirintegrationintotheframeworkwasn’ttrivial.OneofthemainreasonsbehindthelackofsatisfyingresultswasthecouplingoftheframeworkwiththeDOM.Often,insidethewatchers’callbacks,AngulardirectlymanipulatestheDOM,whichmakesitimpossibletomovethewatchersinsideWebWorkerssincetheWebWorkersareinvokedinanisolatedcontext,withoutaccesstotheDOM.InAngularJS1.x,wemayhaveimplicitorexplicitdependenciesbetweenthedifferentwatchers,whichrequiremultipleiterationsofthedigestloopinordertogetstableresults.Combiningthelasttwopoints,itisquitehardtoachievepracticalresultsincalculatingthechangesinthreadsotherthanthemainthreadofexecution.

FixingthisinAngularJS1.xintroducesagreatdealofcomplexityintheinternalimplementation.Theframeworksimplywasnotbuiltwiththisinmind.Since

WebWorkerswereintroducedbeforetheAngular2designprocessstarted,thecoreteamtooktheminmindfromthebeginning.

LessonslearnedfromAngularJS1.xinthewildAlthoughtheprevioussectionintroducedalotofargumentsfortherequiredreimplementationoftheframeworkrespondingtothelatesttrends,it’simportanttorememberthatwe’renotstartingcompletelyfromscratch.We’retakingwhatwe’velearnedfromAngularJS1.xwithus.Intheperiodsince2009,theWebisnottheonlythingthatevolved.Wealsostartedbuildingmoreandmorecomplexapplications.Today,single-pageapplicationsarenotsomethingexotic,butmorelikeastrictrequirementforallthewebapplicationssolvingbusinessproblems,whichareaimingforhighperformanceandgooduserexperience.

AngularJS1.xhelpedustobuildhighly-efficientandlarge-scalesingle-pageapplications.However,byapplyingitinvarioususecases,we’vealsodiscoveredsomeofitspitfalls.Learningfromthecommunity’sexperience,Angular’scoreteamworkedonnewideasaimingtoanswerthenewrequirements.AswelookatthenewfeaturesofAngular2,let’sconsidertheminthelightofthecurrentimplementationofAngularJS1.xandthinkaboutthethingswithwhichwe,asAngulardevelopers,havestruggledandwhichwehavemodifiedoverthelastfewyears.

ControllersAngularJS1.xfollowstheModelViewController(MVC)micro-architecturalpattern.SomemayarguethatitlooksmorelikeModelViewViewModel(MVVM)becauseoftheviewmodelattachedaspropertiestothescopeorthecurrentcontextincaseofcontrollerassyntax.ItcouldbeapproacheddifferentlyagainifweusetheModelViewPresenterpattern(MVP).BecauseofallthedifferentvariationsofhowwecanstructurethelogicinourapplicationsthecoreteamcalledAngularJS1.xaModelViewWhatever(MVW)framework.

TheviewinanyAngularJSapplicationissupposedtobeacompositionofdirectives.Thedirectivescollaboratetogetherinordertodeliverfullyfunctionaluserinterfaces.Servicesareresponsibleforencapsulatingthebusinesslogicoftheapplications.That’stheplacewhereweshouldputthecommunicationwithRESTfulservicesthroughHTTP,real-timecommunicationwithWebSocketsandevenWebRTC.Servicesarethebuildingblockwhereweshouldimplementthedomainmodelsandbusinessrulesofourapplications.There’sonemorecomponent,whichismostlyresponsibleforhandlinguserinputanddelegatingtheexecutiontotheservices—thecontroller.

Althoughtheservicesanddirectiveshavewell-definedroles,wecanoftenseetheanti-patternoftheMassiveViewController,whichiscommoniniOSapplications.Occasionally,developersaretemptedtoaccessorevenmanipulatetheDOMdirectlyfromtheircontrollers.Initially,thishappensforachievingsomethingsimple,suchaschangingthesizeofanelement,orquickanddirtychangingelements’styles.Anothernoticeableanti-patternisduplicationofbusinesslogicacrosscontrollers.Oftendeveloperstendtocopyandpastelogic,whichshouldbeencapsulatedinsideservices.

ThebestpracticesforbuildingAngularJSapplicationsstateisthatthecontrollersshouldnotmanipulatetheDOMatall,insteadallDOMaccessandmanipulationsshouldbeisolatedindirectives.Ifwehavesomerepetitivelogicbetweencontrollers,mostlikelywewanttoencapsulateitintoaserviceandinjectthisservicewiththedependencyinjectionmechanismofAngularJSinallthecontrollersthatneedthatfunctionality.

Thisiswherewe’recomingfrominAngularJS1.x.Allthissaid,itseemsthatthefunctionalityofcontrollerscouldbemovedintothedirective’scontrollers.SincedirectivessupportthedependencyinjectionAPI,afterreceivingtheuser’sinput,wecandirectlydelegatetheexecutiontoaspecificservice,alreadyinjected.ThisisthemainreasonAngular2usesadifferentapproachbyremovingtheabilitytoputcontrollerseverywherebyusingtheng-controllerdirective.We’lltakealookathowAngularJS1.xcontrollers’responsibilitiescouldbetakenfromAngular2componentsanddirectivesinChapter4,GettingStartedwithAngular2ComponentsandDirectives.

ScopeThedata-bindinginAngularJSisachievedusingthescopeobject.Wecanattachpropertiestoitandexplicitlydeclareinthetemplatethatwewanttobindtotheseproperties(oneortwo-way).Althoughtheideaofthescopeseemsclear,thescopehastwomoreresponsibilities,includingeventdispatchingandthechangedetection-relatedbehavior.Angularbeginnershaveahardtimeunderstandingwhatscopereallyisandhowitshouldbeused.AngularJS1.2introducedsomethingcalledcontrollerassyntax.Itallowsustoaddpropertiestothecurrentcontextinsidethegivencontroller(this),insteadofexplicitlyinjectingthescopeobjectandlateraddingpropertiestoit.Thissimplifiedsyntaxcanbedemonstratedfromthefollowingsnippet:

<divng-controller="MainCtrlasmain">

<buttonng-click="main.clicked()">Click</button>

</div>

functionMainCtrl(){

this.name='Foobar';

}

MainCtrl.prototype.clicked=function(){

alert('Youclickedme!');

};

Angular2tookthisevenfurtherbyremovingthescopeobject.AlltheexpressionsareevaluatedinthecontextofgivenUIcomponent.RemovingtheentirescopeAPIintroduceshighersimplicity;wedon’tneedtoexplicitlyinjectitanymoreandweaddpropertiestotheUIcomponentstowhichwecanlaterbind.ThisAPIfeelsmuchsimplerandmorenatural.

We’regoingtotakemoredetailedlookatthecomponentsandthechangedetectionmechanismofAngular2inChapter4,GettingStartedwithAngular2ComponentsandDirectives.

DependencyInjectionMaybethefirstframeworkonthemarketthatincludedinversionofcontrol(IoC)throughdependencyinjection(DI)intheJavaScriptworldwasAngularJS1.x.DIprovidesanumberofbenefits,suchaseasiertestability,bettercodeorganizationandmodularization,andsimplicity.AlthoughtheDIin1.xdoesanamazingjob,Angular2takesthisevenfurther.SinceAngular2isontopofthelatestwebstandards,itusestheECMAScript2016decorators’syntaxforannotatingthecodeforusingDI.DecoratorsarequitesimilartothedecoratorsinPythonorannotationsinJava.Theyallowustodecoratethebehaviorofagivenobjectbyusingreflection.Sincedecoratorsarenotyetstandardizedandsupportedbymajorbrowsers,theirusagerequiresanintermediatetranspilationstep;however,ifyoudon’twanttotakeit,youcandirectlywritealittlebitmoreverbosecodewithECMAScript5syntaxandachievethesamesemantics.

ThenewDIismuchmoreflexibleandfeature-rich.ItalsofixessomeofthepitfallsofAngularJS1.xsuchasthedifferentAPIs;in1.x,someobjectsareinjectedbyposition(suchasthescope,element,attributes,andcontrollerinthedirectives’linkfunction)andothers,byname(usingparametersnamesincontrollers,directives,services,andfilters).

WewilltakeafurtherlookattheAngular2’sdependencyinjectionAPIinChapter5,DependencyInjectioninAngular2.

Server-siderenderingThebiggertherequirementsoftheWebare,themorecomplexthewebapplicationsbecome.Buildingareal-life,single-pageapplicationrequireswritingahugeamountofJavaScript,andincludingalltherequiredexternallibrariesmayincreasethesizeofthescriptsonourpagetoafewmegabytes.Theinitializationoftheapplicationmaytakeuptoseveralsecondsoreventensofsecondsonmobileuntilalltheresourcesgetfetchedfromtheserver,theJavaScriptisparsedandexecuted,thepagegetsrendered,andallthestylesareapplied.Onlow-endmobiledevicesthatuseamobileInternetconnection,thisprocessmaymaketheusersgiveuponvisitingourapplication.Althoughthereareafewpracticesthatspeedupthisprocess,incomplexapplications,there’snosilverbullet.

Intheprocessoftryingtoimprovetheuserexperience,developersdiscoveredsomethingcalledserver-siderendering.Itallowsustorendertherequestedviewofasingle-pageapplicationontheserveranddirectlyprovidetheHTMLforthepagetotheuser.Later,oncealltheresourcesareprocessed,theeventlistenersandbindingscanbeaddedbythescriptfiles.Thissoundslikeagoodwaytoboosttheperformanceofourapplication.OneofthepioneersinthiswasReactJS,whichallowedpre-renderingoftheuserinterfaceontheserversideusingNode.jsDOMimplementations.Unfortunately,thearchitectureofAngularJS1.xdoesnotallowthis.TheshowstopperisthestrongcouplingbetweentheframeworkandthebrowserAPIs,thesameissuewehadinrunningthechangedetectioninWebWorkers.

Anothertypicalusecasefortheserver-siderenderingisforbuildingSearchEngineOptimization(SEO)-friendlyapplications.TherewereacoupleofhacksusedinthepastformakingtheAngularJS1.xapplicationsindexablebythesearchengines.Onesuchpractice,forinstance,istraversaloftheapplicationwithaheadlessbrowser,whichexecutesthescriptsoneachpageandcachestherenderedoutputintoHTMLfiles,makingitaccessiblebythesearchengines.

AlthoughthisworkaroundforbuildingSEO-friendlyapplicationsworks,server-siderenderingsolvesbothofthementionedissues,improvingtheuserexperienceandallowingustobuildSEO-friendlyapplicationsmuchmoreeasilyandfarmoreelegantly.

ThedecouplingofAngular2withtheDOMallowsustorunourAngular2applicationsoutsidethecontextofthebrowser.Thecommunitytookadvantageofthisbybuildingatool,allowingustoprerendertheviewsofoursingle-pageapplicationontheserversideandforwardthemtothebrowser.Atthetimeofwritingthefollowingcontent,thetoolisstillintheearlyphasesofitsdevelopmentandisoutsidetheframework’score.We’regoingtotakeafurtherlookatitinChapter8,DevelopmentExperienceandServer-SideRendering.

ApplicationsthatscaleMVWhasbeenthedefaultchoiceforbuildingsingle-pageapplicationssinceBackbone.jsappeared.Itallowsseparationofconcernsbyisolatingthebusinesslogicfromtheview,allowingustobuildwell-designedapplications.Exploitingtheobserverpattern,MVWallowslisteningformodelchangesintheviewandupdatingitwhenchangesaredetected.However,therearesomeexplicitandimplicitdependenciesbetweentheseeventhandlers,whichmakethedataflowinourapplicationsnotobviousandhardtoreasonabout.InAngularJS1.x,weareallowedtohavedependenciesbetweenthedifferentwatchers,whichrequiresthedigestlooptoiterateoverallofthemacoupleoftimesuntiltheexpressions’resultsgetstable.Angular2makesthedata-flowone-directional,whichhasanumberofbenefits,including:

Moreexplicitdata-flow.Nodependenciesbetweenbindings,sonotimetolive(TTL)ofthedigest.Betterperformance:

Thedigestloopisrunonlyonce.Wecancreateapps,whicharefriendlytoimmutable/observablemodels,thatallowsustomakefurtheroptimizations.

Thechangeinthedata-flowintroducesonemorefundamentalchangeinAngularJS1.xarchitecture.

WemaytakeanotherperspectiveonthisproblemwhenweneedtomaintainalargecodebasewritteninJavaScript.AlthoughJavaScript’sducktypingmakesthelanguagequiteflexible,italsomakesitsanalysisandsupportbyIDEsandtexteditorsharder.Refactoringoflargeprojectsgetsveryhardanderror-pronebecauseinmostcases,thestaticanalysisandtypeinferenceareimpossible.Thelackofcompilermakestyposalltooeasy,whicharehardtonoticeuntilwerunourtestsuiteorruntheapplication.

TheAngularcoreteamdecidedtouseTypeScriptbecauseofthebettertoolingpossiblewithitandthecompile-timetypechecking,whichhelpusbemoreproductiveandlesserror-prone.Astheprecedingfigureshows,TypeScriptisasupersetofECMAScript;itintroducesexplicittypeannotationsandacompiler.TheTypeScriptlanguageiscompiledtoplainJavaScript,supportedbytoday’sbrowsers.Sinceversion1.6,TypeScriptimplementstheECMAScript2016decorators,whichmakesittheperfectchoiceforAngular2.

TheusageofTypeScriptallowsmuchbetterIDEandtexteditorssupportwithstaticcodeanalysisandtypechecking.Allthisincreasesourproductivitydramaticallybyreducingthemistakeswemakeandsimplifyingtherefactoringprocess.AnotherimportantbenefitofTypeScriptistheperformanceimprovementweimplicitlygetbythestatictyping,whichallowsrun-timeoptimizationsbytheJavaScriptvirtualmachine.

We’llbetalkingaboutTypeScriptindetailinChapter3,TypeScriptCrashCourse.

TemplatesTemplatesareoneofthekeyfeaturesinAngularJS1.x.TheyaresimpleHTMLanddonotrequireanyintermediateprocessingandcompilation,unlikemosttemplateenginessuchasmustache.TemplatesinAngularJScombinesimplicitywithpowerbyallowingustoextendHTMLbycreatinganinternalDomainSpecificLanguage(DSL)insideit,withcustomelementsandattributes.

However,thisisoneofthemainpurposesbehindwebcomponentsaswell.WealreadymentionedhowandwhyAngular2takesadvantageofthisnewtechnology.AlthoughAngularJS1.xtemplatesaregreat,theycanstillgetbetter!Angular2templatestookthebestpartsoftheonesinthepreviousreleaseoftheframeworkandenhancedthembyfixingsomeoftheirconfusingparts.

Forexample,let’ssaywebuiltadirectiveandwewanttoallowtheusertopassapropertytoitbyusinganattribute.InAngularJS1.x,wecanapproachthisinthreedifferentways:

<username="literal"></user>

<username="expression"></user>

<username="{{interpolate}}"></user>

Ifwehaveadirectiveuserandwewanttopassthenameproperty,wecanapproachinthreedifferentways.Wecaneitherpassaliteral(inthiscase,thestring"literal"),astring,whichwillbeevaluatedasanexpression(inourcase"expression"),oranexpressioninside{{}}.Whichsyntaxshouldbeusedcompletelydependsonthedirective’simplementation,whichmakesitsAPI-tangledandhardtoremember.

Itisafrustratingtasktodealwithlargeamountofcomponentswithdifferentdesigndecisionsonadailybasis.Byintroducingacommonconvention,wecandealwithsuchproblems.However,inordertohavegoodresultsandconsistentAPIs,theentirecommunityneedstoagreewithit.

Angular2dealswiththisproblemaswellbyprovidingspecialsyntaxforattributes,whosevaluesneedtobeevaluatedinthecontextofthecurrentcomponent,andadifferentsyntaxforpassingliterals.

Anotherthingwe’reusedto,basedonourAngularJS1.xexperience,isthemicrosyntaxusedintemplatedirectivessuchasng-if,ng-for.Forinstance,ifwewanttoiterateoveralistofusersanddisplaytheirnamesinAngularJS1.x,wecanuse:

<divng-for="userinusers">{{user.name}}</div>

Althoughthissyntaxlooksintuitivetous,itallowslimitedtoolingsupport.However,Angular2approachedthisdifferentlybybringingalittlebitmoreexplicitsyntaxwithrichersemantics:

<templatengForvar-user[ngForOf]="users">

{{user.name}}

</template>

Theprecedingsnippetexplicitlydefinestheproperty,whichhastobecreatedinthe

contextofthecurrentiteration(user),theoneweiterateover(users).

However,thissyntaxistooverbosefortyping.Developerscanusethefollowingsyntax,whichlatergetstranslatedtothemoreverboseone:

<li*ngFor="#userofusers">

{{user.name}}

</li>

TheimprovementsinthenewtemplateswillalsoallowbettertoolingforadvancedsupportbytexteditorsandIDEs.We’regoingtodiscussAngular2’stemplatesinChapter4,GettingStartedwithAngular2ComponentsandDirectives.

ChangedetectionIntheWebWorkerssection,wealreadymentionedtheopportunitytorunthedigestloopinthecontextofadifferentthread,instantiatedasWebWorker.However,theimplementationofthedigestloopinAngularJS1.xisnotquitememory-efficientandpreventstheJavaScriptvirtualmachinefromdoingfurthercodeoptimizations,whichallowssignificantperformanceimprovements.Onesuchoptimizationistheinlinecaching(http://mrale.ph/blog/2012/06/03/explaining-js-vms-in-js-inline-caches.html).TheAngularteamdidalotofresearchdiscoveringdifferentwaystheperformanceandtheefficiencyofthedigestloopcouldbeimproved.Thisledtothedevelopmentofabrandnewchangedetectionmechanism.

Inordertoallowfurtherflexibility,theAngularteamabstractedthechangedetectionanddecoupleditsimplementationfromtheframework’score.Thisallowedthedevelopmentofdifferentchangedetectionstrategies,empoweringdifferentfeaturesindifferentenvironments.

Asaresult,Angular2hastwobuilt-inchangedetectionmechanisms:

Dynamicchangedetection:ThisissimilartothechangedetectionmechanismusedbyAngularJS1.x.Itisusedinsystemswithdisallowedeval(),suchasCSPandChromeextensions.JITchangedetection:Thisgeneratesthecodethatperformsthechangedetectionrun-time,allowingtheJavaScriptvirtualmachinetoperformfurthercodeoptimizations.

We’regoingtotakealookatthenewchangedetectionmechanismsandhowwecanconfiguretheminChapter4,GettingStartedwithAngular2ComponentsandDirectives.

SummaryInthischapter,weconsideredthemainreasonsbehindthedecisionstakenbytheAngularcoreteamandthelackofbackwardcompatibilitybetweenthelasttwomajorversionsoftheframework.Wesawthatthesedecisionswerefueledbytwothings—theevolutionoftheWebandtheevolutionofthefrontenddevelopment,withthelessonslearnedfromthedevelopmentofAngularJS1.xapplications.

Inthefirstsection,welearnedwhyweneedtousethelatestversionoftheJavaScriptlanguage,whywewanttotakeadvantageofWebComponentsandWebWorkers,andwhyit’snotworthittointegrateallthesepowerfultoolsinversion1.x.

Weobservedthecurrentdirectionoffrontenddevelopmentandthelessonslearnedinthelastfewyears.WedescribedwhythecontrollerandscopewereremovedfromAngular2,andwhyAngularJS1.x’sarchitecturewaschangedinordertoallowserver-siderenderingforSEO-friendly,high-performance,single-pageapplications.Anotherfundamentaltopicwetookalookatwasbuildinglarge-scaleapplications,andhowthatmotivatedsingle-waydata-flowintheframeworkandthechoiceofthestatically-typedlanguageTypeScript.

Inthenextchapter,we’regoingtolookatthemainbuildingblocksofanAngular2application—howtheycanbeusedandhowtheyrelatetoeachother.Angular2reusessomeofthenamingofthecomponentsintroducedbyAngularJS1.x,butgenerallychangesthebuildingblocksofoursingle-pageapplicationscompletely.We’regoingtopeekatthenewcomponents,andcomparethemwiththeonesinthepreviousversionoftheframework.We’llmakeaquickintroductiontodirectives,components,routers,pipes,andservices,anddescribehowtheycouldbecombinedforbuildingclassy,single-pageapplications.

TipDownloadingtheexamplecode

Youcandownloadtheexamplecodefilesforthisbookfromyouraccountathttp://www.packtpub.com.Ifyoupurchasedthisbookelsewhere,youcanvisithttp://www.packtpub.com/supportandregistertohavethefilese-maileddirectlytoyou.

Youcandownloadthecodefilesbyfollowingthesesteps:

Loginorregistertoourwebsiteusingyoure-mailaddressandpassword.HoverthemousepointerontheSUPPORTtabatthetop.ClickonCodeDownloads&Errata.EnterthenameofthebookintheSearchbox.Selectthebookforwhichyou’relookingtodownloadthecodefiles.Choosefromthedrop-downmenuwhereyoupurchasedthisbookfrom.ClickonCodeDownload.

Oncethefileisdownloaded,pleasemakesurethatyouunziporextractthefolderusingthelatestversionof:

WinRAR/7-ZipforWindowsZipeg/iZip/UnRarXforMac7-Zip/PeaZipforLinux

Chapter2.TheBuildingBlocksofanAngular2ApplicationInthepreviouschapter,welookedatthedriversforthedesigndecisionsbehindAngular2.Wedescribedthemainreasonsthatledtothedevelopmentofabrandnewframework;Angular2takesadvantageofwebstandardswhilekeepingthepastlessonsinmind.Althoughwearefamiliarwiththemaindrivers,westillhaven’tdescribedthecoreAngular2concepts.ThelastmajorreleaseoftheframeworktookadifferentpathfromAngularJS1.xandintroducedalotofchangesinthefundamentalbuildingblocksusedforthedevelopmentofsingle-pageapplications.

Inthischapter,we’lllookattheframework’scoreandmakeabriefintroductiontothemaincomponentsofAngular2.Anotherimportantpurposeofthischapteristotakeanoverviewofhowtheseconceptscanbeputtogethertohelpusbuildprofessionaluserinterfacesforourwebapplications.Thefollowingsectionswillgiveusanoverviewofeverythingthatwearegoingtotakealookatinmoredetaillaterinthebook.

Inthischapter,we’regoingtolookat:

Aconceptualoverviewoftheframework,showinghowdifferentconceptsrelatetoeachother.Howwecanbuildauserinterfaceasacompositionofcomponents.WhatpaththedirectivestookinAngular2,andhowtheirinterfacechangedcomparedtothepreviousmajorversionoftheframework.Thereasonsfortheenforcedseparationofconcerns,whichledtothedecompositionofthedirectivesintotwodifferentcomponents.Inordertogetbettersenseofthesetwoconcepts,we’regoingtodemonstratebasicsyntaxfortheirdefinition.Anoverviewoftheimprovedchangedetection,andhowitinvolvesthecontextthatdirectivesprovide.Whatzonesare,andwhytheycanmakeourdailydevelopmentprocesseasier.Whatpipesare,andhowaretheyrelatedtotheAngularJS1.xfilters.Thebrand-newdependencyinjection(DI)mechanisminAngular2andhowitisrelatedtotheservicecomponent.

AconceptualoverviewofAngular2BeforewediveintodifferentpartsofAngular2,let’sgetaconceptualoverviewofhoweverythingfitstogether.Let’shavealookatthefollowingdiagram:

Fig.1

Fig.1toFig.4showsthemainAngular2conceptsandtheconnectionsbetweenthem.Themainpurposeofthesediagramsistoillustratethecoreblocksforbuildingsingle-pageapplicationswithAngular2,andtheirrelations.

TheComponentisthemainbuildingblockwe’regoingtousetocreatetheuserinterfaceofourapplicationswithAngular2.TheComponentisadirectsuccessoroftheDirective,whichistheprimitiveforattachingbehaviortotheDOM.ComponentsextendDirectivesbyprovidingfurtherfeatures,suchasaviewwithanattachedtemplate,whichcanbeusedforrenderingcompositionofdirectives.Insidethetemplateoftheviewcanresidedifferentexpressions.

Fig.2

TheprecedingdiagramillustratesconceptuallytheChangeDetectionmechanismofAngular2.Itrunsthedigestloop,whichevaluatestheregisteredexpressionsinthecontextofspecificUIcomponents.SincetheconceptofscopehasbeenremovedfromAngular2,theexecutioncontextoftheexpressionsarethecontrollersofthecomponentsassociatedwiththem.

TheChangeDetectionmechanismcanbeenhancedusingDiffers;that’swhythere’sadirectrelationbetweenthesetwoelementsonthediagram.

PipesareanothercomponentofAngular2.WecanthinkofthePipesasthefiltersfromAngularJS1.x.Pipescanbeusedtogetherwithcomponents.Wecanincludethemintheexpressions,whicharedefinedinthecontextofanycomponent:

Fig.3

Nowlet’stakealookattheprecedingdiagram.DirectivesandComponentsdelegatethebusinesslogictoServices.Thisenforcesbetterseparationofconcerns,maintainability,andreusabilityofcode.DirectivesreceivereferencestoinstancesofspecificservicesdeclaredasdependenciesusingtheDImechanismoftheframework,anddelegatetheexecutionofthebusinessrelatedlogictothem.BothDirectivesandComponentsmayusetheDImechanism,notonlytoinjectservices,butalsotoinjectDOMelementsand/orotherComponentsorDirectives.

Fig.4

Lastly,thecomponent-basedrouterisusedfordefiningtheroutesinourapplication.SinceDirectivesdonotownatemplate,onlytheComponentscanberenderedbytherouter,representingthedifferentviewsinourapplication.Therouteralsousesthepredefineddirectives,whichallowustodefinehyperlinksbetweenthedifferentviewsandthecontainerwheretheyshouldberendered.

Nowwe’regoingtolookmorecloselyattheseconcepts,seehowtheyworktogethertomakeAngular2applications,andhowthey’vechangedfromtheirAngularJS1.xpredecessors.

ChangingdirectivesAngularJS1.xintroducedtheconceptofdirectivesinthedevelopmentofsingle-pageapplications.ThepurposeofdirectivesistoencapsulatetheDOM-relatedlogic,andallowustobuilduserinterfacesascompositionsofsuchcomponentsbyextendingthesyntaxandthesemanticsofHTML.Initially,likemostinnovativeconcepts,directiveswereviewedcontroversiallybecausetheypredisposeustowriteinvalidHTMLwhenusingcustomelementsorattributeswithoutthedata-prefix.However,overtime,thisconcepthasgraduallybeenaccepted,andhasprovedthatitisheretostay.

AnotherdrawbackoftheimplementationofdirectivesinAngularJS1.xarethedifferentwayswecanusethem.Thisrequiresunderstandingoftheattributevalues,whichcanbeliterals,expressions,callbacks,ormicrosyntax.Thismakestoolingessentiallyimpossible.

Angular2keepstheconceptofdirectives,buttakesthebestpartsfromAngularJS1.xandaddssomenewideasandsyntax.ThemainpurposeofAngular2’sdirectivesistoattachbehaviortotheDOMbyextendingitwithcustomlogicdefinedinanES2015class.Wecanthinkoftheseclassesascontrollersassociatedtothedirectives,andthinkoftheirconstructorsassimilartothelinkingfunctionofthedirectivesfromAngularJS1.x.However,thenewdirectiveshavelimitedconfigurability.Theydonotallowforthedefinitionofatemplate,whichmakesmostofthealreadyknownpropertiesfordefiningdirectivesunnecessary.ThesimplicityofthedirectivesAPIdoesnotlimittheirbehavior,butonlyenforcesstrongerseparationofconcerns.TocomplementthismoresimpledirectiveAPI,Angular2hasintroducedaricherinterfaceforthedefinitionofUIelements,calledcomponents.Componentsextendthefunctionalityofdirectivesbyallowingthemtoownatemplate,throughtheComponentmetadata.We’regoingtotakeafurtherlookatcomponentslater.

ThesyntaxusedforAngular2directivesinvolvesES2016decorators.However,wecanalsouseTypeScript,ES2015,orevenECMAScript5(ES5)inordertoachievethesameresultwithalittlebitmoretyping.Thefollowingcodedefinesasimpledirective,writteninTypeScript:

@Directive({

selector:'[tooltip]'

})

exportclassTooltip{

privateoverlay:Overlay;

@Input()

privatetooltip:string;

constructor(privateel:ElementRef,manager:OverlayManager){

this.overlay=manager.get();

}

@HostListener('mouseenter')

onMouseEnter(){

this.overlay.open(this.el.nativeElement,this.tooltip);

}

@HostListener('mouseleave')

onMouseLeave(){

this.overlay.close();

}

}

Thedirectivecanbeusedwiththefollowingmarkupinourtemplate:

<divtooltip="42">Tellmetheanswer!</div>

Oncetheuserpointsoverthethelabel,Tellmetheanswer!,Angularwillinvokethemethod,definedunderthe@HostListenerdecoratorinthedirective’sdefinition.Intheend,theopenmethodoftheoverlaymanagerwillbeexecuted.Sincewecanhavemultipledirectivesonasingleelement,thebestpracticesstatethatweshoulduseanattributeasaselector.

AnalternativeECMAScript5syntaxforthedefinitionofthisdirectiveis:

varTooltip=ng.core.Directive({

selector:'[tooltip]',

inputs:['tooltip'],

host:{

'(mouseenter)':'onMouseEnter()',

'(mouseleave)':'onMouseLeave()'

}

})

.Class({

constructor:[ng.core.ElementRef,Overlay,function(tooltip,el,

manager){

this.el=el;

this.overlay=manager.get();

}],

onMouseEnter(){

this.overlay.open(this.el.nativeElement,this.tooltip);

},

onMouseLeave(){

this.overlay.close();

}

});

TheprecedingES5syntaxdemonstratestheinternalJavaScriptDomainSpecificLanguage(DSL)thatAngular2providesinordertoallowustowriteourcodewithoutthesyntax,whichisnotyetsupportedbymodernbrowsers.

WecansummarizethatAngular2haskepttheconceptofdirectivesbymaintainingtheideaofattachingbehaviortotheDOM.Thecoredifferencesbetween1.xand2arethenewsyntax,andthefurtherseparationofconcernsintroducedbybringingthecomponents.InChapter4,GettingStartedwithAngular2ComponentsandDirectives,wewilltakeafurtherlookatdirectives’API.We’llalsocomparethedirectives’definitionsyntaxusingES2016andES5.Nowlet’shavealookatthebigchangetoAngular2components.

GettingtoknowAngular2componentsModelViewController(MVC)isamicro-architecturalpatterninitiallyintroducedfortheimplementationofuserinterfaces.AsAngularJSdevelopers,weusedifferentvariationsofthispatternonadailybasis,mostoftenModelViewViewModel(MVVM).InMVC,wehavethemodel,whichencapsulatesthebusinesslogicofourapplication,andtheview,whichisresponsibleforrenderingtheuserinterface,acceptinguserinput,aswellasdelegatingtheuserinteractionlogictothecontroller.Theviewisrepresentedasthecompositionofcomponents,whichisformallyknownasthecompositedesignpattern.

Let’stakealookatthefollowingstructuraldiagram,whichshowsthecompositedesignpattern:

Fig.5

Herewehavethreeclasses:

AnabstractclasscalledComponent.TwoconcreteclassescalledLeafandComposite.TheLeafclassisasimpleterminalcomponentinthecomponenttreethatwe’regoingtobuildsoon.

TheComponentclassdefinesanabstractoperationcalledoperation.BothLeafandCompositeinheritfromtheComponentclass.However,theCompositeclassalsoownsreferencestoit.WecantakethisevenfurtherandallowCompositetoownalistofreferencestoComponentinstances,asshowninthediagram.ThecomponentslistinsideCompositecanholdreferencestodifferentCompositeorLeafinstances,orinstancesofotherclasses,whichextendtheComponentclassoranyofitssuccessors.Intheimplementationofthemethod,operation,insideComposite,theinvokedoperationofthedifferentinstancesinsidetheloopcanbehavedifferently.Thisisbecauseofthelatebindingmechanismusedfortheimplementationofthepolymorphisminobject-orientedprogramminglanguages.

ComponentsinactionEnoughoftheory!Let’sbuildacomponenttreebasedontheclasshierarchyillustratedinthediagram.Thisway,we’regoingtodemonstratehowwecantakeadvantageofthecompositepatternforbuilding,userinterfacebyusingsimplifiedsyntax.We’regoingtotakealookatasimilarexampleinthecontextofAngular2inChapter4,GettingStartedwithAngular2ComponentsandDirectives:

Compositec1=newComposite();

Compositec2=newComposite();

Compositec3=newComposite();

c1.components.push(c2);

c1.components.push(c3);

Leafl1=newLeaf();

Leafl2=newLeaf();

Leafl3=newLeaf();

c2.components.push(l1);

c2.components.push(l2);

c3.components.push(l3);

Theprecedingpseudo-codecreatesthreeinstancesoftheCompositeclassandthreeinstancesoftheLeafclass.Theinstance,c1,holdsreferencestoc2andc3insidethecomponentslist.Theinstance,c2,holdsreferencestol1andl2,andc3holdsreferencetol3:

Fig.6

Theprecedingdiagramisagraphicalrepresentationofthecomponenttreewebuiltinthesnippet.ThisisaquitesimplifiedversionofwhattheviewinthemodernJavaScriptframeworkslookssimilarto.However,itillustratestheverybasicsofhowwecancomposedirectivesandcomponents.Forinstance,inthecontextofAngular2wecanthinkofdirectivesasinstancesoftheprecedingLeafclass(sincetheydon’townviewandthuscannotcomposeotherdirectivesandcomponents),andcomponentsasinstancesoftheCompositeclass.

IfwethinkmoreabstractlyfortheuserinterfaceinAngularJS1.x,wecannoticethatweusequiteasimilarapproach.Thetemplatesofourviewsarecomposingdifferentdirectivestogetherinordertodeliverfullyafunctionaluserinterfacetotheenduserofourapplication.

ComponentsinAngular2Angular2tookthisapproachbyintroducingnewbuildingblockscalledcomponents.Componentsextendthedirectiveconceptwedescribedintheprevioussection,andprovidebroaderfunctionality.Hereisthedefinitionofabasichello-worldcomponent:

@Component({

selector:'hello-world',

template:'<h1>Hello,{{this.target}}!</h1>'

})

classHelloWorld{

target:string;

constructor(){

this.target='world';

}

}

Wecanuseitbyinsertingthefollowingmarkupinourview:

<hello-world></hello-world>

Accordingtothebestpractices,weshoulduseanelementasaselectorforourcomponentssincewemayhaveonlyasinglecomponentperDOMelement.

ThealternativeES5syntaxusingtheDSLAngularprovidesis:

varHelloWorld=ng.core.

Component({

selector:'hello-world',

template:'<h1>Hello,{{target}}!</h1>'

})

.Class({

constructor:function(){

this.target='world';

}

});

Wewilltakealookattheprecedingsyntaxinmoredetaillaterinthebook.However,let’sbrieflydescribethefunctionality,whichthiscomponentprovides.OncetheAngular2applicationhasbeenbootstrapped,itwilllookatalltheelementsofourDOMtreeandprocessthem.Onceitfindstheelementcalledhello-world,itwillinvokethelogicassociatedwithitsdefinition,whichmeansthatthetemplateofthecomponentwillberenderedandtheexpressionbetweenthecurlybracketswillbeevaluated.Thiswillresulttothemarkup,<h1>Hello,world!</h1>.

Sotosummarize,theAngularcoreteamseparatedoutthedirectivesfromAngularJS1.xintotwodifferentparts—ComponentsandDirectives.DirectivesprovideaneasywaytoattachbehaviortoDOMelementswithoutdefiningaview.ComponentsinAngular2provideapowerful,andyetsimple-to-learnAPI,whichmakesiteasiertodefinetheuserinterfaceofourapplications.Angular2ComponentsallowustodothesameamazingthingsasAngularJS1.xdirectives,butwithlesstypingandfewerthingstolearn.ComponentsextendtheAngular2directiveconceptbyaddingaviewtoit.Wecanthink

oftherelationbetweenAngular2componentsanddirectivesthesamewayastherelationbetweenCompositeandLeaffromthediagramwesawinFig.5.

IfwestartillustratingtheconceptualmodelofthebuildingblocksAngular2provides,wecanpresenttherelationbetweenDirectiveandComponentasinheritance.Chapter4,GettingStartedwithAngular2ComponentsandDirectivesdescribesthesetwoconceptsinfurtherdetails.

PipesInbusinessapplications,weoftenneedtohavedifferentvisualrepresentationsofthesamepieceofdata.Forexample,ifwehavethenumber100,000andwewanttoformatitascurrency,mostlikelywewon’twanttodisplayitasplaindata;morelikely,we’llwantsomethinglike$100,000.

TheresponsibilityforformattingdatainAngularJS1.xwasassignedtofilters.Anotherexampleforadataformattingrequirementiswhenweusecollectionsofitems.Forinstance,ifwehavealistofitems,wemaywanttofilteritbasedonapredicate(abooleanfunction);inalistofnumbers,wemaywanttodisplayonlyprimenumbers.AngularJS1.xhasafiltercalledfilter,whichallowsustodothis.However,theduplicationofthenamesoftenleadstoconfusion.That’sanotherreasonthecoreteamrenamedthefiltercomponenttopipe.

Themotivationbehindthenewnameisthesyntaxusedforpipesandfilters:

{{expression|decimal|currency}}

Intheprecedingexample,weapplythepipes,decimalandcurrency,tothevaluereturnedbyexpression.TheentireexpressionbetweenthecurlybraceslookslikeUnixpipesyntax.

DefiningpipesThesyntaxfordefiningpipesissimilartotheoneusedforthedefinitionofdirectivesandcomponents.Inordertocreateanewpipe,wecanusetheES2015decorator,@Pipe.Itallowsustoaddmetadatatoaclass,declaringitasapipe.Allweneedtodoistoprovideanameforthepipeanddefinethedataformattinglogic.There’salsoanalternativeES5syntax,whichcanbeusedifwewanttoskiptheprocessoftranspilation.

Duringruntime,oncetheAngular2expressioninterpreterfindsoutthatagivenexpressionincludesacallofapipe,itwillretrieveitoutofthepipescollectionallocatedwithinthecomponentandinvokeitwiththeappropriatearguments.

Thefollowingexampleillustrateshowwecandefineasimplepipecalledlowercase1,whichtransformsthegivenstring,passedasargumenttoitslowercaserepresentation:

@Pipe({name:'lowercase1'})

classLowerCasePipe1implementsPipeTransform{

transform(value:string):string{

if(!value)returnvalue;

if(typeofvalue!=='string'){

thrownewError('Invalidpipevalue',value);

}

returnvalue.toLowerCase();

}

}

Inordertobeconsistent,let’sshowtheECMAScript5syntaxfordefiningpipes:

varLowercasePipe1=ng.core.

Pipe({

name:'lowercase'

})

.Class({

constructor:function(){},

transform:function(value){

if(!value)returnvalue;

if(typeofvalue==='string'){

thrownewError('Invalidpipevalue',value);

}

returnvalue.toLowerCase();

}

});

IntheTypeScriptsyntax,weimplementthePipeTransforminterfaceanddefinethetransformmethoddeclaredinsideit.However,inECMAScript5,wedonothavesupportforinterfaces,butwestillneedtoimplementthetransformmethodinordertodefineavalidAngular2pipe.WearegoingtoexplaintheTypeScriptinterfacesinthenextchapter.

Nowlet’sdemonstratehowwecanusethelowercase1pipeinsideacomponent:

@Component({

selector:'app',

pipes:[LowercasePipe1],

template:'<h1>{{"SAMPLE"|lowercase1}}</h1>'

})

classApp{}

And,thealternativeECMAScript5syntaxforthisis:

varApp=ng.core.Component({

selector:'app',

pipes:[LowercasePipe1],

template:'<h1>{{"SAMPLE"|lowercase1}}</h1>'

})

.Class({

constructor:function(){}

});

WecanusetheAppcomponentwiththefollowingmarkup:

<app></app>

Theresultwearegoingtoseeonthescreenisthetextsamplewithinanh1element.

Bykeepingthedataformattinglogicasaseparatecomponent,Angular2keepsthestrongseparationofconcernsthatcanbeseenthroughout.WewilltakealookathowwecandefinestatefulandstatelesspipesforourapplicationinChapter7,Buildingareal-lifeapplicationwhileexploringpipesandhttp.

ChangedetectionAswesawearlier,theviewinMVCupdatesitself,basedonchangeeventsitreceivesfromthemodel.AnumberofModelViewWhatever(MVW)frameworkstookthisapproach,andembeddedtheobserverpatterninthecoreoftheirchangedetectionmechanism.

ClassicalchangedetectionLet’stakealookatasimpleexample,whichdoesn’tuseanyframework.SupposewehaveamodelcalledUser,whichhasapropertycalledname:

classUserextendsEventEmitter{

privatename:string;

setName(name:string){

this.name=name;

this.emit('change');

}

getName():string{

returnthis.name;}

}

TheprecedingsnippetusesTypeScript.Donotworryifthesyntaxdoesnotlookfamiliartoyou,we’regoingtomakeanintroductiontothelanguageinthenextchapter.

Theuserclassextendstheclass,EventEmitter.Thisprovidesprimitivesforemittingandsubscribingtoevents.

Nowlet’sdefineaview,whichdisplaysthenameofaninstanceoftheUserclass,passedasanargumenttoitsconstructor:

classView{

constructor(user:User,el:Element/*aDOMelement*/){

el.innerHTML=user.getName();

}

}

Wecaninitializetheviewelementby:

letuser=newUser();

user.setName('foo');

letview=newView(user,document.getElementById('label'));

Astheendresult,theuserwillseealabelwiththecontent,foo.However,changesinuserwillnotbereflectedbytheview.Inordertoupdatetheviewwhenthenameoftheuserchanges,weneedtosubscribetothechangeeventandthenupdatethecontentoftheDOMelement.WeneedtoupdatetheViewdefinitioninthefollowingway:

classView{

constructor(user:User,el:any/*aDOMelement*/){

el.innerHTML=user.getName();

user.on('change',()=>{

el.innerHTML=user.getName();

});

}

}

ThisishowmostframeworksusedtoimplementtheirchangedetectionbeforetheeraofAngularJS1.x.

AngularJS1.xchangedetectionMostbeginnersarefascinatedbythedata-bindingmechanisminAngularJS1.x.ThebasicHelloWorldexamplelookssimilartothis:

functionMainCtrl($scope){

$scope.label='Helloworld!';

}

<bodyng-appng-controller="MainCtrl">

{{label}}

</body>

Ifyourunthis,Helloworld!magicallyappearsontothescreen.However,thatisnoteventhemostimpressivething!Ifweaddatextinputandwebindittothelabelpropertyofthescope,eachchangewillreflectthecontentdisplayedbytheinterpolationdirective:

<bodyng-controller="MainCtrl">

<inputng-model="label">

{{label}}

</body>

Howawesomeisthat!ThisisoneofthemainsellingpointsofAngularJS1.x—theextremeeaseofachievingdata-binding.Weaddtwo(fourifwecountng-controllerandng-app)attributesinourmarkup,addpropertytoamysticalobjectcalled$scope,whichismagicallypassedtoacustomfunctionwedefine,andeverythingsimplyworks!

However,themoreexperiencedAngulardeveloperhasabetterunderstandingofwhatisactuallygoingonbehindthescene.Intheprecedingexample,insidethedirectives,ng-modelandng-bind(inourcase,theinterpolationdirective,{{}}),Angularaddswatcherswithdifferentbehaviorassociatedtothesameexpression—label.ThesewatchersarequitesimilartotheobserversintheclassicalMVCpattern.Onsomespecificevents(inourcase,changeofthecontentofthetextinput),AngularJSwillloopoverallsuchwatchers,evaluatetheexpressionsassociatedtotheminthecontextofagivenscope,andstoretheirresults.Thisloopisknownasthedigestloop.

Intheprecedingexamples,theevaluationoftheexpression,label,inthecontextofthescopewillreturnthetext,Helloworld!.Oneachiteration,AngularJSwillcomparethecurrentresultoftheevaluationwiththepreviousresult,andwillinvoketheassociatedcallbackincasethevaluesdiffer.Forinstance,thecallbackaddedbytheinterpolationdirectivewillsetthecontentoftheelementtobethenewresultoftheexpression’sevaluation.Thisisanexampleofthedependencybetweenthecallbacksofthewatchersoftwodirectives.Thecallbackofthewatcheraddedbyng-modelmodifiestheresultoftheexpressionassociatedtothewatcheraddedbytheinterpolationdirective.

However,thisapproachhasitsowndrawbacks.Wesaidthatthedigestloopwillbeinvokedonsomespecificevents,butwhatiftheseeventshappenoutsidetheframework,forexample?WhatifweusesetTimeoutandinsidethecallback,passedasthefirstargument,wechangepropertiesattachedtothescopethatwe’rewatching?AngularJSwillbeunawareofthechangeandwon’tinvokethedigestloop,soweneedtodothat

explicitlyusing$scope.$apply.Butwhatiftheframeworkknewaboutalltheasynchronouseventshappeninginthebrowser,suchasuserevents,XMLHttpRequestevents,WebSocketsrelatedevents,andothers?Insuchacase,AngularJSwouldbeabletointercepttheevent’shandlingandcouldinvokethedigestloopwithoutforcingustodoso!

Inthezone.jsThat’sexactlythecaseinAngular2.Thisfunctionalityisimplementedwithzonesusingzone.js.

Atng-confin2014,BrianFordgaveatalkaboutzones.Brianpresentedzonesasmeta-monkeypatchingofbrowserAPIs.RecentlyMiškoHeveryproposedtoTC39morematurezonesAPIforstandardization.Zone.jsisalibrarydevelopedbytheAngularteam,whichimplementszonesinJavaScript.Theyrepresentanexecutioncontext,whichallowustointerceptasynchronousbrowsercalls.Basically,byusingzones,weareabletoinvokeapieceoflogicjustafterthegivenXMLHttpRequestcompletesorwhenwereceiveanewWebSocketevent.Angular2tookadvantageofzone.jsbyinterceptingasynchronousbrowsereventsandinvokingthedigestloopjustattherighttime.ThistotallyeliminatestheneedofexplicitcallsofthedigestloopbythedeveloperusingAngular.

SimplifieddataflowThecross-watcherdependenciesmaycreateatangleddataflowinourapplication,whichishardtofollow.Thismayleadtounpredictablebehaviorandbugs,whicharehardtofind.AlthoughAngular2keptthedirtycheckingasawayforachievingchangedetection,itenforcedunidirectionaldataflow.Thishappenedbydisallowingdependenciesbetweenthedifferentwatchers,whichallowsthedigestlooptoberunonlyonce.Thisstrategyincreasestheperformanceofourapplicationsdramatically,andreducesthecomplexityofthedataflow.Angular2alsomadeimprovementstomemoryefficiencyandtheperformanceofthedigestloop.FurtherdetailsonAngular2’schangedetectionandthedifferentstrategiesusedforitsimplementationcanbefoundinChapter4,GettingStartedwithAngular2ComponentsandDirectives.

EnhancingAngularJS1.x’schangedetectionNowlet’stakeastepbackandagainthinkaboutthechangedetectionmechanismoftheframework.

Wesaidthatinsidethedigestloop,Angularevaluatesregisteredexpressionsandcomparestheevaluatedvalueswiththevaluesassociatedwiththesameexpressionsinthepreviousiterationoftheloop.

Themostoptimalalgorithmusedforthecomparisonmaydifferdependingonthetypeofthevaluereturnedfromtheexpression’sevaluation.Forinstance,ifwegetamutablelistofitems,weneedtoloopovertheentirecollectionandcomparetheitemsinthecollectionsonebyoneinordertoverifythatthereisachangeornot.However,ifwehaveanimmutablelist,wecanperformacheckwithaconstantcomplexity,onlybycomparingreferences.Thisisthecasebecausetheinstancesofimmutabledatastructurescannotchange.Insteadofapplyinganoperation,whichintendstomodifysuchinstances,we’llgetanewreferencewiththemodificationapplied.

InAngularJS1.x,wecanaddwatchersusingafewmethods.Twoofthemare$watch(exp,fn,deep)or$watchCollection(exp,fn).Thesemethodsgiveussomelevelofcontroloverthewaythechangedetectionwillperformtheequalitycheck.Forexample,addingawatcherbyusing$watchandpassingafalsevalueasathirdargumentwillmakeAngularJSperformareferencecheck(thatiscomparethecurrentvaluewiththepreviousoneusing===).However,ifwepassatruthy(anytruevalue),thecheckwillbedeep(thatisusingangular.equals).Thisway,dependingontheexpectedtypeofthereturnedbytheexpressionvalue,wecanaddlistenersinthemostappropriatewayinordertoallowtheframeworktoperformequalitycheckswiththemostoptimalalgorithmavailable.ThisAPIhastwolimitations:

Itdoesnotallowyoutochoosethemostappropriateequalitycheckalgorithmatruntime.Itdoesnotallowyoutoextendthechangedetectiontothird-partiesfortheirspecificdatastructures.

TheAngularcoreteamassignedthisresponsibilitytodiffers,allowingthemtoextendthechangedetectionmechanismandoptimizeit,basedonthedataweuseinourapplications.Angular2definestwobaseclasses,whichwecanextendinordertodefinecustomalgorithms:

KeyValueDiffer:Thisallowsustoperformadvanceddiffingoverkey-value-baseddatastructures.IterableDiffer:Thisallowsustoperformadvanceddiffingoverlist-likedatastructures.

Angular2allowsustotakefullcontroloverthechangedetectionmechanismbyextendingitwithcustomalgorithms,whichwasn’tpossibleinthepreviousversionoftheframework.We’lltakeafurtherlookintothechangedetectionandhowwecanconfigureitinChapter4,GettingStartedwithAngular2ComponentsandDirectives.

UnderstandingservicesServicesarethebuildingblocksthatAngularprovidesforthedefinitionofthebusinesslogicofourapplications.InAngularJS1.x,wehadthreedifferentwaysfordefiningservices:

//TheFactorymethod

module.factory('ServiceName',function(dep1,dep2,…){

return{

//publicAPI

};

});

//TheServicemethod

module.service('ServiceName',function(dep1,dep2,…){

//publicAPI

this.publicProp=val;

});

//TheProvidermethod

module.provider('ServiceName',function(){

return{

$get:function(dep1,dep2,…){

return{

//publicAPI

};

}

};

});

Althoughthefirsttwosyntacticalvariationsprovidesimilarfunctionality,theydifferinthewaytheregistereddirectivewillbeinstantiated.Thethirdsyntaxallowsfurtherconfigurationoftheregisteredproviderduringconfigurationtime.

HavingthreedifferentmethodsfordefiningservicesisquiteconfusingfortheAngularJS1.xbeginners.Let’sthinkforasecondwhatnecessitatedtheintroductionofthesemethodsforregisteringservices.Whycan’twesimplyuseJavaScriptconstructorfunctions,objectliterals,orES2015classesinstead,whichAngularwillnotbeawareof?WecouldencapsulateourbusinesslogicinsideacustomJavaScriptconstructorfunctionlikethis:

functionUserTransactions(id){

this.userId=id;

}

UserTransactions.prototype.makeTransaction=function(amount){

//methodlogic

};

module.controller('MainCtrl',function(){

this.submitClick=function(){

newUserTransactions(this.userId).makeTransaction(this.amount);

};

});

Thiscodeiscompletelyvalid.However,itdoesn’ttakeadvantageofoneofthekeyfeaturesthatAngularJS1.xprovides—theDImechanism.TheMainCtrlfunctionusestheconstructorfunction,UserTransaction,whichisvisibleinitsbody.Theprecedingcodehastwomainpitfalls:

We’recoupledwiththelogicusedfortheservice’sinstantiation.Thecodeisnottestable.InordertomockUserTransactions,weneedtomonkeypatchit.

HowdoesAngularJSdealwiththesetwothings?Whenagivenserviceisrequired,throughtheDImechanismoftheframework,AngularJSresolvesallofitsdependenciesandinstantiatesitbypassingthemtothefactoryfunction,whichencapsulatesthelogicforitscreation.Thefactoryfunctionispassedasthesecondargumenttothefactoryandservicemethods.Theprovidermethodallowsdefinitionofaserviceonlowerlevel;thefactorymethodthereistheoneunderthe$getpropertyoftheprovider.

JustlikeAngularJS1.x,Angular2toleratesthisseparationofconcernsaswell,sothecoreteamkepttheservices.IncontrasttoAngularJS1.x,thelastmajorversionoftheframeworkprovidesamuchsimplerinterfaceforthedefinitionofservicesbyallowingustouseplainES2015classesorES5constructorfunctions.Wecannotescapefromthefactthatweneedtoexplicitlystatewhichservicesshouldbeavailableforinjectionandsomehowspecifyinstructionsfortheirinstantiation.However,Angular2usestheES2016decorator’ssyntaxforthispurposeinsteadofthemethodsfamiliartousfromAngularJS1.x.ThisallowsustodefinetheservicesinourapplicationsassimpleasES2015classes,withdecoratorsforconfigurationoftheDI:

import{Inject,Injectable}from'angular2/core';

@Injectable()

classHttpService{

constructor(){/*…*/}

}

@Injectable()

classUser{

constructor(privateservice:HttpService){}

save(){

returnthis.service.post('/users')

.then(res=>{

this.id=res.id;

returnthis;

});

}

}

ThealternativeECMAScript5syntaxis:

varHttpService=ng.core.Class({

constructor:function(){}

});

varUser=ng.core.Class({

constructor:[HttpService,function(service){

this.service=service;

}],

save:function(){

returnthis.service.post('/users')

.then(function(res){

this.id=res.id;

returnthis;

});

}

});

Servicesarerelatedtothecomponentsandthedirectivesdescribedintheprevioussections.FordevelopinghighlycoherentandreusableUIcomponents,weneedtomoveallthebusiness-relatedlogictoinsideourservices.And,inordertodeveloptestablecomponents,weneedtotakeadvantageoftheDImechanismforresolvingalltheirdependencies.

AcoredifferencebetweentheservicesinAngular2andAngularJS1.xisthewaytheirdependenciesarebeingresolvedandrepresentedinternally.AngularJS1.xisusingstringsforidentifyingthedifferentservicesandtheassociatedfactoriesusedfortheirinstantiation.However,Angular2useskeysinstead.Usuallythekeysarethetypesofthedistinctservices.Anothercoredifferenceintheinstantiationisthehierarchicalstructureofinjectors,whichencapsulatedifferentdependencyproviderswithdifferentvisibility.

Anotherdistinctionbetweentheservicesinthelasttwomajorversionsoftheframeworkisthesimplifiedsyntax.AlthoughAngular2usesES2015classesforthedefinitionofourbusinesslogic,youcanuseECMAScript5constructorfunctionsaswellorusetheDSLprovidedbytheframework.TheDIinAngular2hasacompletelydifferentsyntaxandhasimprovedbehaviorbyprovidingaconsistentwayofinjectingdependencies.ThesyntaxusedintheprecedingexampleusesES2016decorators,andinChapter5,DependencyInjectioninAngular2,we’lltakealookatalternativesyntax,whichusesECMAScript5.YoucanalsofindmoredetailedexplanationofAngular2servicesandDIinChapter5,DependencyInjectioninAngular2.

Understandingthenewcomponent-basedrouterIntraditionalwebapplications,allthepagechangesareassociatedwithafull-pagereload,whichfetchesallofthereferencedresourcesanddataandrenderstheentirepageontothescreen.However,requirementsforwebapplicationshaveevolvedovertime.

Single-pageapplications(SPAs)thatwebuildwithAngularsimulatedesktopuserexperiences.Thisofteninvolveincrementalloadingoftheresourcesanddatarequiredbytheapplication,andnofull-pagereloadsaftertheinitialpageload.OftenthedifferentpagesorviewsinSPAsarerepresentedbydifferenttemplates,whichareloadedasynchronouslyandrenderedonaspecificpositiononthescreen.Later,whenthetemplatewithalltherequiredresourcesisloadedandtherouteischanged,thelogicattachedtotheselectedpageisinvokedandpopulatesthetemplatewithdata.IftheuserpressestherefreshbuttonafterthegivenpageinourSPAisloaded,thesamepageneedstobere-renderedaftertherefreshoftheviewcompletes.Thisinvolvessimilarbehavior—findingtherequestedview,fetchingtherequiredtemplatewithallreferencedresources,andinvokingthelogicassociatedwiththatview.

Whattemplateneedstobefetched,andthelogicwhichshouldbeinvokedafterthepagereloadssuccessfully,dependsontheselectedviewbeforetheuserpressedtherefreshbutton.TheframeworkdeterminesthisbyparsingthepageURL,whichcontainstheidentifierofthecurrentlyselectedpage,representedinthehierarchicalstructure.

Alltheresponsibilitiesrelatedtothenavigation,changingtheURL,loadingtheappropriatetemplate,andinvokingspecificlogicwhentheviewisloadedareassignedtotheroutercomponent.Thesearesomequitechallengingtasks,andsupportfordifferentnavigationAPIsrequiredforcross-browsercompatibilitymakestheimplementationofroutinginmodernSPAsanon-trivialproblem.

AngularJS1.xintroducedtherouterinitscore,whichwaslaterexternalizedintothengRoutecomponent.ItallowsadeclarativewayfordefiningthedifferentviewsinourSPA,byprovidingatemplateforeachpageandapieceoflogicthatneedstobeinvokedwhenapageisselected.However,thefunctionalityoftherouterislimited.Itdoesnotsupportessentialfeaturessuchasnestedviewrouting.That’soneofthereasonsmostdeveloperspreferredtouseui-router,developedbythecommunity.BothAngularJS1.x’srouter,andui-router,route-definitionsincludearouteconfigurationobject,whichdefinesatemplateandacontrollerassociatedwiththepage.

Asdescribedintheprevioussections,Angular2changedthebuildingblocksitprovidesforthedevelopmentofSPAs.Angular2removesthefloatingcontrollers,andinsteadrepresentsviewsasacompositionofcomponents.Thisnecessitatesthedevelopmentofabrandnewrouter,whichempowersthesenewconcepts.

ThecoredifferencesbetweentheAngularJS1.xrouterandtheAngular2routerare:

TheAngular2routeriscomponentbased,ngRouteisnot.

Thereisnowsupportfornestedviews.DifferentsyntaxempoweredbyES2016decorators.

Angular2routedefinitionsyntaxLet’stakeabrieflookatthenewsyntaxusedbytheAngular2routertodefineroutesinourapplications:

import{Component}from'angular2/core';

import{bootstrap}from'angular2/platform/browser';

import{RouteConfig,ROUTER_DIRECTIVES,ROUTER_BINDINGS}from

'angular2/router';

import{Home}from'./components/home/home';

import{About}from'./components/about/about';

@Component({

selector:'app',

templateUrl:'./app.html',

directives:[ROUTER_DIRECTIVES]

})

@RouteConfig([

{path:'/',component:Home,name:'home'},

{path:'/about',component:About,name:'about'}

])

classApp{}

bootstrap(App,[ROUTER_PROVIDERS]);

Wewon’tgointotoomuchdetailheresinceChapter6,Angular2formsandthenewcomponent-basedrouterandChapter7,Buildingareal-lifeapplicationwhileexploringpipesandhttp,arededicatedtothenewrouter,butlet’smentionthemainpointsintheprecedingcodesnippet.

Therouterlivesinthemodule,angular2/router.There,wecanfindthedirectivesitdefines,thedecoratorusedfortheconfigurationoftheroutesandROUTER_PROVIDERS.

NoteWe’lltakeafurtherlookatROUTER_PROVIDERSinChapter7,Buildingareal-lifeapplicationwhileexploringpipesandhttp.

Theparameterpassedtothe@RouteConfigdecoratorshowshowwedefinetheroutesinourapplication.Weuseanarraywithobjects,whichdefinesthemappingsbetweenroutesandthecomponentsassociatedwiththem.InsidetheComponentdecorator,weexplicitlystatethatwewanttousethedirectivescontainedwithinROUTER_DIRECTIVES,whicharerelatedtotherouter’susageinsidethetemplates.

SummaryInthischapter,wetookaquickoverviewofthemainbuildingblocksfordevelopingSPAsprovidedbyAngular2.WepointedoutthecoredifferencesbetweenthesecomponentsinAngularJS1.xandAngular2.

AlthoughwecanuseES2015,orevenES5,forbuildingAngular2applications,therecommendationfromGoogleistotakeadvantageofthelanguageusedforthedevelopmentoftheframework—TypeScript.

Inthenextchapter,we’lltakealookatTypeScriptandhowwecanstartusingitinyournextapplication.WewillalsoexplainhowwecantakeadvantageofthestatictypingintheJavaScriptlibrariesandframeworkswritteninvanillaJavaScript,withambienttypeannotations.

Chapter3.TypeScriptCrashCourseInthischapter,wearegoingtostartworkingwithTypeScript,thelanguageAngular2recommendsforscripting.AllthefeaturesECMAScript2015andrespectivelyECMAScript2016provides,suchasfunctions,classes,modules,anddecorators,arealreadyimplementedinoraddedtotheroadmapofTypeScript.Becauseoftheextratypeannotations,therearesomesyntacticaladditionscomparedtoJavaScript.

Forsmoothertransitionfromthelanguagewealreadyknow-ES5,wewillstartwithsomecommonfeaturesbetweenES2016andTypeScript.WheretherearedifferencesbetweentheESsyntaxandTypeScript,we’llexplicitlymentionit.Inthesecondhalfofthechapter,we’lladdthetypeannotationstoeverythingwe’velearneduntilthispoint.

Laterinthechapter,wewillexplaintheextrafeaturesTypeScriptprovides,suchasstatictypingandextendedsyntax.Wewilldiscussthedifferentconsequencesbasedonthesefeatures,whichwillhelpusbemoreproductiveandlesserror-prone.Let’sgetgoing!

IntroductiontoTypeScriptTypeScriptisanopensourceprogramminglanguagethatisdevelopedandmaintainedbyMicrosoft.ItsinitialpublicreleasewasinOctober2012.TypeScriptisasupersetofECMAScript,supportingallofthesyntaxandsemanticsofJavaScriptwithsomeextrafeaturesontop,suchasstatictypingandrichersyntax.

Fig.1showstherelationshipbetweenES5,ES2015,ES2016,andTypeScript.

Fig.1

BecauseTypeScriptisstaticallytyped,itcanprovideanumberofbenefitstousasJavaScriptdevelopers.Let’shaveaquicklookatthosebenefitsnow.

Compile-timetypecheckingSomeofthemostcommonmistakeswemakewhilewritingJavaScriptcodeistomisspellapropertyoramethodname.We’llfindoutaboutthemistakewhenwegetaruntimeerror.Thiscanhappenduringdevelopmentaswellasinproduction.Hopingwewillknowabouttheerrorbeforewedeployourcodetoproductionenvironmentisn’tacomfortablefeeling!However,thisisnotaproblemspecifictoJavaScript;itissomethingcommontoallthedynamiclanguages.Evenwithlotsofunittests,theseerrorscanslipby.

TypeScriptprovidesacompiler,whichtakescareofsuchmistakesforusbyusingstaticcodeanalysis.Ifwetakeadvantageofstatictyping,TypeScriptwillbeawareoftheexistingpropertiesagivenobjecthas,andifwemisspellanyofthem,thecompilerwillwarnuswithacompile-timeerror.

AnothergreatbenefitofTypeScriptisthatitallowslargeteamstocollaborate,sinceitprovidesformal,verifiablenaming.Thisway,itallowsustowriteeasy-to-understandcode.

BettersupportbytexteditorsandIDEsThereareanumberoftools,suchasTernorGoogleClosureCompiler,thataretryingtobringbetterautocompletionsupportforJavaScriptintexteditorsandIDEs.However,asJavaScriptisadynamiclanguage,itisimpossiblefortheIDEsandtexteditorstomakesophisticatedsuggestionswithoutanymetadata.

Annotatingthecodewithsuchmetadataisabuilt-infeatureofTypeScriptknownastypeannotations.Basedonthem,texteditorsandIDEscanperformbetterstaticanalysisoverourcode.Thisprovidesbetterrefactoringtoolsandautocompletion,whichincreasesourproductivityandallowsustomakefewermistakeswhilewritingthesourcecodeforourapplications.

There’sevenmoretoTypeScriptTypeScriptbyitselfhasanumberofotherbenefits:

ItisasupersetofJavaScript:AllJavaScript(ES5andES2015)programsarealreadyvalidTypeScriptones.Inessence,youhavealreadybeenwritingTypeScriptcode.SinceitisbasedonthelatestversionoftheECMAScriptstandard,itallowsustotakeadvantageofthelatestbleedingedgesyntaxprovidedbythelanguage.Supportsoptionaltypechecking:If,foranyreason,wedecidethatwedon’twanttoexplicitlydefinethetypeofavariableoramethod,wecanjustskipthetypedefinition.However,weshouldbeawarethatthismeanswearenolongertakingadvantageofthestatictyping,sowearegivinguponallthebenefitsmentionedearlier.DevelopedandmaintainedbyMicrosoft:Thequalityoftheimplementationofthelanguageisveryhighanditisunlikelythatsupportwillbedroppedunexpectedly.TypeScriptisbasedontheworkofsomeoftheworld’sbestexpertsinprogramminglanguagedevelopment.Itisopensource:Thisallowsthecommunitytofreelycontributetothelanguageandsuggestfeatures,whicharediscussedinanopenmanner.ThefactthatTypeScriptisopensourceallowsfortheeasierdevelopmentofthird-partyextensionsandtools,whichextendsfurtherthescopeofitsusage.

SincemodernbrowsersdonotsupportTypeScriptnatively,thereisacompilerthattranslatestheTypeScriptcodewewriteintoreadableJavaScriptinapredefinedtargetversionofECMAScript.Oncethecodeiscompiled,allthetypeannotationsareremoved.

UsingTypeScriptLet’sstartwritingsomeTypeScript!

Inthefollowingsections,wearegoingtotakealookatdifferentsnippetsshowingsomeofthefeaturesofTypeScript.Inordertobeabletorunthesnippetsandplaywiththemyourself,you’llneedtoinstalltheTypeScriptcompileronyourcomputer.Let’stakealookathowtodothis.

TypeScriptisbestinstalledusingNodePackageManager(npm).I’drecommendyoutousenpmVersion3.0.0ornewer.Ifyoudon’thavenode.jsandnpminstalledalready,youcanvisithttps://nodejs.organdfollowtheinstructionsthere.

InstallingTypeScriptwithnpmOnceyouhavenpminstalledandrunning,verifythatyouhavethelatestversionbyopeningyourterminalwindowandrunningthefollowingcommand:

$npm–v

InordertoinstallTypeScript1.8,use:

$npminstall-gtypescript@1.8

TheprecedingcommandwillinstalltheTypeScriptcompilerandadditsexecutable(tsc)asglobaltoyourpath.

Inordertoverifythateverythingworksproperly,youcanuse:

$tsc–v

Version1.8.0

Theoutputshouldbesimilartotheprecedingone,thoughpossiblywithadifferentversion.

RunningourfirstTypeScriptprogramNoteYoucanfindthecodeforthisbookonthefollowingURL:https://github.com/mgechev/switching-to-angular2.Asacommentinmostcodesnippetsyou’llfindarelativetotheappdirectoryfilepathwhereyoucanfindthem.

Now,let’scompileourfirstTypeScriptprogram!Createafilecalledhello.tsandenterthefollowingcontent:

//ch3/hello-world/hello-world.ts

console.log('Helloworld!');

Sinceyou’vealreadyinstalledtheTypeScriptcompiler,youshouldhaveaglobalexecutablecommandcalledtsc.Youcanuseitinordertocompilethefile:

$tschello.ts

Now,youshouldseethefilehello.jsinthesamedirectorywherehello.tsis.hello.jsistheoutputoftheTypeScriptcompiler;itcontainstheJavaScriptequivalenttotheTypeScriptyouwrote.Youcanrunthisfileusingthefollowingcommand:

$nodehello.js

Now,you’llseethestringHelloworld!printedonthescreen.Inordertocombinetheprocessofcompilingandrunningtheprogram,youcanusethepackagets-node:

$npminstall-tts-node

Nowyoucanrun:

$ts-nodehello.ts

Youshouldseethesameresult,butwithoutthets-nodefilestoredonthedisk.

TypeScriptsyntaxandfeaturesintroducedbyES2015andES2016AsTypeScriptisasupersetofJavaScript,beforewestartlearningaboutitssyntax,it’salittleeasiertostartbyintroducingsomeofthebiggerchangesinES2015andES2016;tounderstandTypeScript,wefirstmustunderstandES2015andES2016.We’regoingtohaveawhistle-stoptourthroughthesechangesbeforedivingintoTypeScriptproperlater.

AdetailedexplanationofES2015andES2016isoutsidethescopeofthisbook.Inordertogetfamiliarwithallthenewfeaturesandsyntaxes,IstronglyrecommendyoutotakealookatExploringES6:upgradetothenextversionofJavaScriptbyDr.AxelRauschmayer.

Thenextcoupleofpageswillintroducenewstandardsandallowyoutotakeadvantageofmostofthefeaturesyou’regoingtoneedinthedevelopmentofAngular2applications.

ES2015arrowfunctionsJavaScripthasfirstclassfunctions,whichmeansthattheycanbepassedaroundlikeanyothervalue:

//ch3/arrow-functions/simple-reduce.ts

varresult=[1,2,3].reduce(function(total,current){

returntotal+current;

},0);//6

Thissyntaxisgreat;however,itisabittooverbose.ES2015introducedanewsyntaxtodefineanonymousfunctionscalledthearrowfunctionsyntax.Usingit,wecancreateanonymousfunctions,asseeninthefollowingexamples:

//ch3/arrow-functions/arrow-functions.ts

//example1

varresult=[1,2,3]

.reduce((total,current)=>total+current,0);

console.log(result);

//example2

vareven=[3,1,56,7].filter(el=>!(el%2));

console.log(even);

//example3

varsorted=data.sort((a,b)=>{

vardiff=a.price-b.price;

if(diff!==0){

returndiff;

}

returna.total-b.total;

});

Inthefirstexample,wegotthetotalsumoftheelementsinthearray[1,2,3].Inthesecondexample,wegotalltheevennumbersinsidethearray[3,1,56,7].Inthethirdexample,wesortedanarraybytheproperties’priceandtotalintheascendingorder.

Arrowfunctionshaveafewmorefeaturesthatweneedtolookat.Themostimportantoneofthemisthattheykeepthecontext(this)fromthesurroundingcode:

//ch3/arrow-functions/context-demo.ts

functionMyComponent(){

this.age=42;

setTimeout(()=>{

this.age+=1;

console.log(this.age);

},100);

}

newMyComponent();//43in100ms.

Forexample,whenweinvokethefunctionMyComponentwiththeoperatornew,thiswillpointtothenewobjectinstantiatedbythecall.Thearrowfunctionwillkeepthecontext(this),inthecallbackofsetTimeout,andprint43onthescreen.

ThisisextremelyusefulinAngular2,sincethebindingcontextforagivencomponentisitsinstance(thatis,itsthis).IfwedefineMyComponentasanAngular2componentandwehaveabindingtotheageproperty,theprecedingcodewillbevalidandallthebindingswillwork(noticethatwedon’thavescope,neitherdowehaveexplicitcallstothe$digestloopalthoughwehavecalledsetTimeoutdirectly).

UsingtheES2015andES2016classesWhendevelopersnewtoJavaScripthearthatthelanguageempowerstheobject-oriented(OO)paradigm,they’renormallyconfusedwhentheydiscoverthatthere’snosyntaxforthedefinitionofclasses.Thisperceptionwasbornbythefactthatsomeofthemostpopularprogramminglanguages,suchasJava,C#,andC++,havetheconceptofclassesusedfortheconstructionofobjects.However,JavaScriptimplementstheOOparadigmdifferently.JavaScripthasaprototype-based,object-orientedprogrammingmodel,wherewecaninstantiateobjectsusingtheobjectliteralsyntaxorfunctions(alsoknownastheconstructorfunctions)andwecantakeadvantageoftheinheritanceusingtheso-calledprototypechain.

ThoughthisisavalidwaytoimplementtheOOparadigmandthesemanticsaresimilartotheoneintheclassicalobject-orientedmodel,itisconfusingforinexperiencedJavaScriptdeveloperswhoarenotsurehowtoprocessthisproperly.ThisisoneofthereasonsTC39decidedtoprovideanalternativesyntaxtoexploittheobject-orientedparadigminthelanguage.Behindthescenes,thenewsyntaxhasthesamesemanticsastheonewe’reusedto,likeusingtheconstructorfunctionsandtheprototype-basedinheritance.However,itprovidesamoreconvenientsyntaxtoempowertheOOparadigm’sfeatureswithlessboilerplate.

ES2016addssomeextrasyntaxtotheES2015classes,suchasstaticandinstancepropertydeclaration.

HereisanexamplethatdemonstratesthesyntaxusedtodefinetheclassesinES2016:

//ch3/es6-classes/sample-classes.ts

classHuman{

statictotalPeople=0;

_name;//ES2016propertydeclarationsyntax

constructor(name){

this._name=name;

Human.totalPeople+=1;

}

getname(){

returnthis._name;

}

setname(val){

this._name=val;

}

talk(){

return`Hi,I'm${this.name}!`;

}

}

classDeveloperextendsHuman{

_languages;//ES2016propertydeclarationsyntax

constructor(name,languages){

super(name);

this._languages=languages;

}

getlanguages(){

returnthis._languages;

}

talk(){

return`${super.talk()}AndIknow

${this.languages.join(',')}.`;

}

}

InES2015,theexplicitdeclarationofthe_namepropertyisnotrequired;however,sincetheTypeScriptcompilershouldbeawareduringcompile-timeoftheexistingpropertiesoftheinstancesofagivenclass,wewouldneedtoaddthedeclarationofthepropertytotheclassdeclarationitself.

TheprecedingsnippetisbothavalidTypeScriptandJavaScriptcode.Init,wedefinedaclasscalledHuman,whichaddsasinglepropertytotheobjectsinstantiatedbyit.Itdoesthisbysettingitsvaluetotheparameternamepassedtoitsconstructor.

Now,openthech3/es6-classes/sample-classes.tsfileandplayaroundwithit!Youcancreatedifferentinstancesoftheclassesthesamewayyoucreateobjectsusingconstructorfunctions:

varhuman=newHuman("foobar");

vardev=newDeveloper("bar",["JavaScript"]);

console.log(dev.talk());

Inordertoexecutethecode,runthefollowingcommand:

$ts-nodesample-classes.ts

ClassesarecommonlyusedinAngular2.Youcanusethemtodefineyourcomponents,directives,services,andpipes.However,youcanalsousethealternativeES5syntax,whichtakesadvantageoftheconstructorfunctions.Underthehood,oncetheTypeScriptcodeiscompiled,therewouldbenosuchsignificantdifferencebetweenboththesyntaxes,becausetheES2015classesarebeingtranspiledtoconstructorfunctionsanyway.

DefiningvariableswithblockscopeAnotherconfusingpointofJavaScriptfordeveloperswithadifferentbackgroundisthevariablescopeinthelanguage.InJavaandC++,forexample,we’reusedtotheblocklexicalscope.Thismeansthatagivenvariabledefinedinsideaspecificblockwillbevisibleonlyinsidethatblockandallofthenestedblocksinsideofit.

However,inJavaScript,thingsarealittlebitdifferent.ECMAScriptdefinesafunctionallexicalscopethathassimilarsemanticstotheblocklexicalscope,butitusesfunctionsinsteadofblocks.Thismeansthatwehavethefollowing:

//ch3/let/var.ts

varfns=[];

for(vari=0;i<5;i+=1){

fns.push(function(){

console.log(i);

})

}

fns.forEach(fn=>fn());

Thishassomeweirdimplications.Oncethecodehasbeenexecuted,itwilllogfivetimesthenumber5.

ES2015addedanewsyntaxtodefinethevariableswithblock-scopevisibility.Thesyntaxissimilartothecurrentone.However,insteadofvar,itusesthekeywordlet:

//ch3/let/let.ts

varfns=[];

for(leti=0;i<5;i+=1){

fns.push(function(){

console.log(i);

})

}

fns.forEach(fn=>fn());

Meta-programmingwithES2016decoratorsJavaScriptisadynamiclanguagethatallowsustoeasilymodifyand/oralterthebehaviortosuittheprogramswewrite.DecoratorsareaproposaltoES2016,whichaccordingtothedesigndocumenthttps://github.com/wycats/javascript-decorators:

“…makeitpossibletoannotateandmodifyclassesandpropertiesatdesigntime.”

TheirsyntaxesarequitesimilartotheannotationsinJava,andtheyareevenclosertothedecoratorsinPython.ES2016decoratorsareusedcommonlyinAngular2todefinecomponents,directives,andpipes,andtotakeadvantageofthedependencyinjectionmechanismoftheframework.Essentially,mostusecasesofdecoratorsinvolvealteringthebehaviortopredefinedlogicoraddingsomemetadatatodifferentconstructs.

ES2016decoratorsallowustodoalotoffancythingsbychangingthebehaviorofourprograms.Typicalusecasescouldbetoannotatethegivenmethodsorpropertiesasdeprecatedorread-only.AsetofpredefineddecoratorsthatcanimprovethereadabilityofthecodeweproducecanbefoundinaprojectbyJayPhelpscalledcore-decorators.js.Anotherusecaseistakingadvantageoftheproxy-basedaspect-orientedprogrammingusingadeclarativesyntax.Thelibraryprovidingthisfunctionalityisaspect.js.

Ingeneral,ES2016decoratorsarejustanothersyntaxsugar,whichtranslatestothecodewe’realreadyfamiliarwithfromthepreviousversionsofJavaScript.Let’stakealookatasimpleexamplefromtheproposal’sdraft:

//ch3/decorators/nonenumerable.ts

classPerson{

@nonenumerable

getkidCount(){

return42;

}

}

functionnonenumerable(target,name,descriptor){

descriptor.enumerable=false;

returndescriptor;

}

varperson=newPerson();

for(letpropinperson){

console.log(prop);

}

Inthiscase,wehaveanES2015classcalledPersonwithasinglegettercalledkidCount.OverthekidCountgetter,wehaveappliedthenonenumerabledecorator.Thedecoratorisafunctionthatacceptsatarget(thePersonclass),thenameofthetargetpropertywe

intendtodecorate(kidCount),andthedescriptorofthetargetproperty.Afterwechangethedescriptor,weneedtoreturnitinordertoapplythemodification.Basically,thedecorator’sapplicationcouldbetranslatedintoECMAScript5inthefollowingway:

descriptor=nonenumerable(Person.prototype,'kidCount',descriptor)||

descriptor;

Object.defineProperty(Person.prototype,'kidCount',descriptor);

UsingconfigurabledecoratorsHereisanexampleonusingthedecoratorsdefinedbyAngular2:

@Component({

selector:'app',

providers:[NamesList],

templateUrl:'./app.html',

directives:[RouterOutlet,RouterLink]

})

@RouteConfig([

{path:'/',component:Home,name:'home'},

{path:'/about',component:About,name:'about'}

])

exportclassApp{}

Whendecoratorsacceptarguments(justlikeComponent,RouteConfig,andViewintheprecedingexample),theyneedtobedefinedasfunctionsthatacceptargumentsandreturntheactualdecorator:

functionComponent(config){

//validateproperties

return(componentCtrl)=>{

//applydecorator

};

}

Inthisexample,wedefinedaconfigurabledecoratorcalledComponentthatacceptsasingleargumentcalledconfigandreturnsadecorator.

WritingmodularcodewithES2015AnotherproblemthatJavaScriptprofessionalshaveexperiencedalongtheyearsisthelackofamodulesysteminthelanguage.Initially,thecommunitydevelopeddifferentpatterns,aimingtoenforcethemodularityandtheencapsulationofthesoftwareweproduce.Suchpatternsincludedthemodulepattern,whichtakesadvantageofthefunctionallexicalscopeandclosures.Anotherexampleisthenamespacepattern,whichrepresentsthedifferentnamespacesasnestedobjects.AngularJS1.xintroduceditsownmodulesystemthatunfortunatelydoesn’tprovidefeatureslikelazymoduleloading.However,thesepatternsweremorelikeworkaroundsratherthanrealsolutions.

CommonJS(usedinnode.js)andAMD(AsynchronousModuleDefinition)werelaterinvented.Theyarestillinwideusetodayandprovidefeatures,suchashandlingofcirculardependencies,asynchronousmoduleloading(inAMD),andsoon.

TC39tookthebestoftheexistingmodulesystemsandintroducedthisconceptonalanguagelevel.ES2015providestwoAPIstodefineandconsumemodules.Theyareasfollows:

DeclarativeAPI.ImperativeAPIusingthemoduleloader.

Angular2takesfulladvantageoftheES2015modulesystem,solet’sdiveintoit!Inthissection,wearegoingtotakealookatthesyntaxusedforthedeclarativedefinitionandconsumptionofmodules.Wearealsogoingtopeekatthemoduleloader’sAPIinordertoseehowwecanprogrammaticallyloadmodulesinanexplicitasynchronousmanner.

UsingtheES2015modulesyntaxLet’stakealookatanexample:

//ch3/modules/math.ts

exportfunctionsquare(x){

returnMath.pow(x,2);

};

exportfunctionlog10(x){

returnMath.log10(x);

};

exportconstPI=Math.PI;

Intheprecedingsnippet,wedefinedasimpleES2015moduleinthefilemath.ts.WecanthinkofitasasamplemathAngular2utilitymodule.Insideit,wedefinedandexportedthefunctionssquareandlog10,andtheconstantPI.TheconstkeywordisanotherkeywordbroughtbyES2015thatisusedtodefineconstants.Asyoucansee,whatwedoisnothingmorethanprefixingthefunction’sdefinitionswiththekeywordexport.Ifwewanttoexporttheentirefunctionalityintheendandskiptheduplicateexplicitusageofexport,wecan:

//ch3/modules/math2.ts

functionsquare(x){

returnMath.pow(x,2);

};

functionlog10(x){

returnMath.log10(x);

};

constPI=Math.PI;

export{square,log10,PI};

Thesyntaxonthelastlineisnothingmorethananenhancedobjectliteralsyntax,introducedbyES2015.Now,let’stakealookathowwecanconsumethismodule:

//ch3/modules/app.ts

import{square,log10}from'./math';

console.log(square(2));//4

console.log(log10(10));//1

Asanidentifierofthemodule,weuseditsrelativepathtothecurrentfile.Byusingdestructuring,weimportedtherequiredfunctions—inthiscase,squareandlog10.

TakingadvantageoftheimplicitasynchronousbehaviorItisimportanttonotethattheES2015modulesyntaxhasimplicitasynchronousbehavior.

Let’ssaywehavemodulesA,B,andC.ModuleAusesmodulesBandC,soitdependsonthem.OncetheuserrequiresmoduleA,theJavaScriptmoduleloaderwouldneedtoloadmodulesBandCbeforebeingabletoinvokeanyofthelogicthatresidesinmoduleAbecauseofthedependencieswehave.However,modulesBandCwillbeloadedasynchronously.Oncetheyareloadedcompletely,theJavaScriptvirtualmachinewillbeabletoexecutemoduleA.

UsingaliasesAnothertypicalsituationiswhenwewanttouseanaliasforagivenexport.Forexample,ifweuseathird-partylibrary,wemaywanttorenameanyofitsexportsinordertoescapenamecollisionsorjusttohaveamoreconvenientnaming:

import{bootstrapasinitialize}from'angular2/platform/browser';

ImportingallthemoduleexportsWecanimporttheentiremathmoduleusing:

//ch3/modules/app2.ts

import*asmathfrom'./math';

console.log(math.square(2));//4

console.log(math.log10(10));//1

console.log(math.PI);//3.141592653589793

ThesemanticsbehindthissyntaxisquitesimilartoCommonJS,althoughinthebrowser,wehaveimplicitasynchronousbehavior.

DefaultexportsIfagivenmoduledefinesanexport,whichwouldquitelikelybeusedbyanyofitsconsumermodules,wecantakeadvantageofthedefaultexportsyntax:

//ch3/modules/math3.ts

exportdefaultfunctioncube(x){

returnMath.pow(x,3);

};

exportfunctionsquare(x){

returnMath.pow(x,2);

};

Inordertoconsumethismodule,wecanusethefollowingapp.tsfile:

//ch3/modules/app3.ts

importcubefrom'./math3';

console.log(cube(3));//27

Or,ifwewanttoimportthedefaultexportaswellassomeotherexports,wecanuse:

//ch3/modules/app4.ts

importcube,{square}from'./math3';

console.log(square(2));//4

console.log(cube(3));//27

Ingeneral,thedefaultexportisnothingmorethananamedexportnamedwiththereservedworddefault:

//ch3/modules/app5.ts

import{defaultascube}from'./math3';

console.log(cube(3));//27

ES2015moduleloaderThenewversionofthestandarddefinesaprogrammaticAPItoworkwithmodules.Thisistheso-calledmoduleloaderAPI.Itallowsustodefineandimportmodules,orconfigurethemoduleloading.

Let’ssupposewehavethefollowingmoduledefinitioninthefileapp.js:

import{square}from'./math';

exportfunctionmain(){

console.log(square(2));//4

}

Fromthefileinit.js,wecanprogrammaticallyloadtheappmoduleandinvokeitsmainfunctionusing:

System.import('./app')

.then(app=>{

app.main();

})

.catch(error=>{

console.log('Terribleerrorhappened',error);

});

TheglobalobjectSystemhasamethodcalledimportthatallowsustoimportmodulesusingtheiridentifier.Intheprecedingsnippet,weimportedthemoduleappdefinedinapp.js.System.importreturnsapromisethatcouldberesolvedonsuccessorrejectedincaseofanerror.Oncethepromiseisresolvedasthefirstparameterofthecallbackpassedtothen,wewillgetthemoduleitself.Thefirstparameterofthecallbackregisteredincaseofrejectionistheerrorthathappened.

ThecodefromthelastsnippetdoesnotexistintheGitHubrepository,sinceitrequiressomeadditionalconfiguration.WearegoingtoapplythemoduleloadermoreexplicitlyintheAngular2examplesinthenextchaptersofthebook.

ES2015andES2016recapCongratulations!We’remorethanhalfwaytowardlearningTypeScript.Allthefeatureswe’vejustseenareapartofTypeScript,sinceitimplementsasupersetofJavaScriptandsinceallthesefeaturesareanupgradeontopofthecurrentsyntax,theyareeasytograspbyexperiencedJavaScriptdevelopers.

Inthenextsections,wewilldescribealltheamazingfeaturesofTypeScriptthatareoutsidetheintersectionwithECMAScript.

TakingadvantageofstatictypingStatictypingiswhatcanprovidebettertoolingforourdevelopmentprocess.WhilewritingJavaScript,themostthatIDEsandtexteditorscandoissyntaxhighlightingandprovidingsomebasicautocompletionsuggestionsbasedonthesophisticatedstaticanalysisofourcode.Thismeansthatwecanonlyverifythatwehaven’tmadeanytyposbyrunningthecode.

Intheprevioussections,wedescribedonlythenewfeaturesprovidedbyECMAScriptexpectedtobeimplementedbybrowsersinthenearfuture.Inthissection,wewilltakealookatwhatTypeScriptprovidesinordertohelpusbelesserror-proneandmoreproductive.Atthetimeofthiswriting,there’renoplanstoimplementbuilt-insupportforstatictypinginthebrowsers.

TheTypeScriptcodegoesthroughintermediatepreprocessingthatperformsthetypecheckinganddropsallthetypeannotationsinordertoprovidevalidJavaScriptsupportedbymodernbrowsers.

UsingexplicittypedefinitionsJustlikeJavaandC++,TypeScriptallowsustoexplicitlydeclarethetypeofthegivenvariable:

letfoo:number=42;

Theprecedinglinedefinesthevariablefoointhecurrentblockusingtheletsyntax.Weexplicitlydeclarethatwewantfootobeofthetypenumberandwesetthevalueoffooto42.

Nowlet’strytochangethevalueoffoo:

letfoo:number=42;

foo='42';

Here,afterthedeclarationoffoo,wewillsetitsvaluetothestring'42'.ThisisaperfectlyvalidJavaScriptcode;however,ifwecompileitusingtheTypeScript’scompiler,wewillget:

$tscbasic.ts

basic.ts(2,1):errorTS2322:Type'string'isnotassignabletotype

'number'.

Oncefoohasbeenassociatedwiththegiventype,wecannotassignitvaluesbelongingtodifferenttypes.Thisisoneofthereasonswecanskiptheexplicittypedefinitionincaseweassignavaluetothegivenvariable:

letfoo=42;

foo='42';

ThesemanticsbehindthiscodewillbethesameastheonewiththeexplicittypedefinitionbecauseofthetypeinferenceofTypeScript.We’llfurthertakealookatitattheendofthischapter.

ThetypeanyAllthetypesinTypeScriptaresubtypesofatypecalledany.Wecandeclarevariablesbelongingtotheanytypebyusingtheanykeyword.Suchvariablescanholdthevalueofanytype:

letfoo:any;

foo={};

foo='bar';

foo+=42;

console.log(foo);//"bar42"

TheprecedingcodeisavalidTypeScript,anditwillnotthrowanyerrorduringcompilationorruntime.Ifweusethetypeanyforallofourvariables,wewillbebasicallywritingthecodewithdynamictyping,whichdropsallthebenefitsoftheTypeScript’scompiler.That’swhywehavetobecarefulwithanyanduseitonlywhenitisnecessary.

AlltheothertypesinTypeScriptbelongtooneofthefollowingcategories:

Primitivetypes:ThisincludesNumber,String,Boolean,Void,Null,Undefined,andEnumtypes.Uniontypes:Uniontypesareoutofthescopeofthisbook.YoucantakealookattheminthespecificationofTypeScript.Objecttypes:ThisincludesFunctiontypes,classesandinterfacetypereferences,arraytypes,tupletypes,functiontypes,andconstructortypes.Typeparameters:ThisincludesGenericsthataregoingtobedescribedintheWritinggenericcodebyusingtypeparameterssection.

UnderstandingthePrimitivetypesMostoftheprimitivetypesinTypeScriptaretheoneswearealreadyfamiliarwithinJavaScript:Number,String,Boolean,Null,andUndefined.So,wearegoingtoskiptheirformalexplanationhere.AnothersetoftypesthatishandywhiledevelopingAngular2applicationsistheEnumtypesdefinedbyusers.

TheEnumtypesTheEnumtypesareprimitiveuser-definedtypesthat,accordingtothespecification,aresubclassesofNumber.TheconceptofenumsexistsintheJava,C++,andC#languages,andithasthesamesemanticsinTypeScript—user-definedtypesconsistingofsetsofnamedvaluescalledelements.InTypeScript,wecandefineenumusingthefollowingsyntax:

enumSTATES{

CONNECTING,

CONNECTED,

DISCONNECTING,

WAITING,

DISCONNECTED

};

ThisisgoingtobetranslatedtothefollowingJavaScript:

varSTATES;

(function(STATES){

STATES[STATES["CONNECTING"]=0]="CONNECTING";

STATES[STATES["CONNECTED"]=1]="CONNECTED";

STATES[STATES["DISCONNECTING"]=2]="DISCONNECTING";

STATES[STATES["WAITING"]=3]="WAITING";

STATES[STATES["DISCONNECTED"]=4]="DISCONNECTED";

})(STATES||(STATES={}));

Wecanusetheenumtypeasfollows:

if(this.state===STATES.CONNECTING){

console.log('Thesystemisconnecting');

}

UnderstandingtheObjecttypesInthissection,we’regoingtotakealookattheArraytypesandFunctiontypes,whichbelongtothemoregenericclassofObjecttypes.Wewillalsoexplorehowwecandefineclassesandinterfaces.TupletypeswereintroducedbyTypeScript1.3,andtheirmainpurposeistoallowthelanguagetobegintypingthenewfeaturesintroducedbyES2015,suchasdestructuring.Wewillnotdescribetheminthisbook.Forfurtherreadingyoucantakealookatthelanguage’sspecificationathttp://www.typescriptlang.org.

TheArraytypesInTypeScript,arraysareJavaScriptarrayswithacommonelementtype.Thismeansthatwecannothaveelementsfromdifferenttypesinagivenarray.Wehavedifferentarraytypesforallthebuilt-intypesinTypeScript,plusallthecustomtypesthatwedefine.

Wecandefineanarrayofnumbersasfollows:

letprimes:number[]=[];

primes.push(2);

primes.push(3);

Ifwewanttohaveanarray,whichseemsheterogeneous,similartothearraysinJavaScript,wecanusethetypereferencetoany:

letrandomItems:any[]=[];

randomItems.push(1);

randomItems.push("foo");

randomItems.push([]);

randomItems.push({});

Thisispossible,sincethetypesofallthevalueswe’repushingtothearrayaresubtypesoftheanytypeandthearraywe’vedeclaredcontainsvaluesofthetypeany.

Wecanusethearraymethodswe’refamiliarwithinJavaScriptwithalltheTypeScriptArraytypes:

letrandomItems:any[]=[];

randomItems.push("foo");

randomItems.push("bar");

randomItems.join('');//foobar

randomItems.splice(1,0,"baz");

randomItems.join('');//foobazbar

Wealsohavethesquare-bracketsoperatorthatgivesusrandomaccesstothearray’selements:

letrandomItems:any[]=[];

randomItems.push("foo");

randomItems.push("bar");

randomItems[0]==="foo"

randomItems[1]==="bar"

TheFunctiontypes

Thefunctiontypesareasetofallthefunctionswithdifferentsignatures,includingthedifferentnumberofarguments,differentarguments’types,ordifferenttypesofthereturnresult.

We’realreadyfamiliarwithhowtocreateanewfunctioninJavaScript.Wecanusefunctionexpressionorfunctiondeclaration:

//functionexpression

varisPrime=function(n){

//body

};

//functiondeclaration

functionisPrime(n){

//body

};

Or,wecanusethenewarrowfunctionsyntax:

varisPrime=n=>{

//body

};

TheonlythingTypeScriptaltersisthefeaturetodefinethetypesofthefunction’sargumentsandthetypeofitsreturnresult.Afterthecompilerofthelanguageperformsitstypecheckingandtranspilation,allthetypeannotationswillberemoved.Ifweusefunctionexpressionandweassignafunctiontoavariable,wewillbeabletodefinethevariabletypeinthefollowingway:

letvariable:(arg1:type1,arg2:type2,…,argn:typen)=>returnType

Forexample:

letisPrime:(n:number)=>boolean=n=>{

//body

};

Incaseoffunctiondeclaration,we’llhave:

functionisPrime(n:number):boolean{

//body

}

Ifwewanttodefineamethodinaobjectliteral,wecanprocessitinthefollowingway:

letmath={

squareRoot(n:number):number{

//…

},

};

Intheprecedingexample,wedefinedanobjectliteralusingtheES2015syntaxthatdefinesthemethodsquareRoot.

Incasewewanttodefineafunctionthatproducessomesideeffectsinsteadofreturningaresult,wecandefineitasavoidfunction:

letperson={

_name:null,

setName(name:string):void{

this._name=name;

}

};

DefiningclassesTypeScriptclassesaresimilartowhatES2015offers.However,italtersthetypedeclarationsandcreatesmoresyntaxsugar.Forexample,let’staketheHumanclasswedefinedearlierandmakeitavalidTypeScriptclass:

classHuman{

statictotalPeople=0;

_name:string;

constructor(name){

this._name=name;

Human.totalPeople+=1;

}

getname(){

returnthis._name;

}

setname(val){

this._name=val;

}

talk(){

return`Hi,I'm${this.name}!`;

}

}

ThereisnodifferencebetweenthecurrentTypeScriptdefinitionwiththeonewealreadyintroduced,however,inthiscasethedeclarationofthe_namepropertyismandatory.Hereishowwecanusetheclass:

lethuman=newHuman('foo');

console.log(human._name);

UsingaccessmodifiersSimilarly,formostconventionalobject-orientedlanguagesthatsupportclasses,TypeScriptallowsdefinitionofaccessmodifiers.Inordertodenydirectaccesstothe_namepropertyoutsidetheclassitisdefinedin,wecandeclareitasprivate:

classHuman{

statictotalPeople=0;

private_name:string;

//…

}

ThesupportedaccessmodifiersbyTypeScriptare:

Public:Allthepropertiesandmethodsdeclaredaspubliccouldbeaccessedanywhere.Private:Allthepropertiesandmethodsdeclaredasprivatecanbeaccessedonlyfrominsidetheclass’definitionitself.Protected:Allthepropertiesandmethodsdeclaredasprotectedcanbeaccessedfrominsidetheclass’definitionorthedefinitionofanyotherclassextendingtheonethatownsthepropertyorthemethod.

AccessmodifiersareagreatwaytoimplementAngular2serviceswithgoodencapsulationandawell-definedinterface.Inordertounderstanditbetter,let’stakealookatanexampleusingtheclass’hierarchydefinedearlier,whichisportedtoTypeScript:

classHuman{

statictotalPeople=0;

constructor(protectedname:string,privateage:number){

Human.totalPeople+=1;

}

talk(){

return`Hi,I'm${this.name}!`;

}

}

classDeveloperextendsHuman{

constructor(name:string,privatelanguages:string[],age:number){

super(name,age);

}

talk(){

return`${super.talk()}AndIknow${this.languages.join(',')}.`;

}

}

JustlikeES2015,TypeScriptsupportstheextendskeywordanddesugarsittotheprototypalJavaScriptinheritance.

Intheprecedingexample,wesettheaccessmodifiersofthenameandagepropertiesdirectlyinsidetheconstructorfunction.Thesemanticsbehindthissyntaxdiffersfromtheoneusedinthepreviousexample.Ithasthefollowingmeaning:defineaprotected

propertycallednameofthetypestringandassignthefirstvaluepassedtotheconstructorcalltoit.Itisthesamefortheprivateageproperty.Thissavesusfromexplicitlysettingthevalueintheconstructoritself.IfwetakealookattheconstructoroftheDeveloperclass,wecanseethatwecanusethemixturebetweenthesesyntaxes.Wecanexplicitlydefinethepropertyintheconstructor’ssignatureorwecanonlydefinethattheconstructoracceptstheparametersofthegiventypes.

Now,let’screateanewinstanceoftheDeveloperclass:

letdev=newDeveloper("foo",["JavaScript","Go"],42);

dev.languages=["Java"];

Duringcompilation,TypeScriptwillthrowanerrortellingusthatPropertylanguagesisprivateandonlyaccessibleinsideclass“Developer”.Now,let’sseewhat’sgoingtohappenifwecreateanewHumanclassandtrytoaccessitspropertiesfromoutsideitsdefinition:

lethuman=newHuman("foo",42);

human.age=42;

human.name="bar";

Inthiscase,we’llgetthefollowingtwoerrors:

Propertyageisprivateandisonlyaccessibleinsideclass“Human”andthePropertynameisaprotectedandonlyaccessibleinsideclass“Human”anditssubclasses.

However,ifwetrytoaccessthe_namepropertyfrominsidethedefinitionofDeveloper,thecompilerwon’tthrowanyerrors.

InordertogetabettersenseofwhattheTypeScriptcompilerwillproduceoutofatypeannotatedclass,let’stakealookattheJavaScriptproducedbythefollowingdefinition:

classHuman{

constructor(privatename:string){}

}

TheresultingECMAScript5willbe:

varHuman=(function(){

functionHuman(name){

this.name=name;

}

returnHuman;

})();

Thedefinedpropertyisaddeddirectlytotheobjectsinstantiatedbycallingtheconstructorfunctionwiththeoperatornew.Thismeansthatoncethecodeiscompiled,wecandirectlyaccesstheprivatemembersofthecreatedobjects.Inordertowrapthisup,accessmodifiersareaddedinthelanguageinordertohelpusenforcebetterencapsulationandgetcompile-timeerrorsincaseweviolateit.

DefininginterfacesSubtypinginprogramminglanguagesallowsustotreatobjectsinthesamewaybasedontheobservationthattheyarespecializedversionsofagenericobject.Thisdoesn’tmeanthattheyhavetobeinstancesofthesameclassofobjects,orthattheyhavecompleteintersectionbetweentheirinterfaces.Theobjectsmighthaveonlyafewcommonpropertiesandstillbetreatedthesamewayinaspecificcontext.InJavaScript,weusuallyuseducktyping.Wemayinvokespecificmethodsforalltheobjectspassedtoafunctionbasedontheassumptionthatthesemethodsexist.However,allofushaveexperiencedtheundefinedisnotafunctionerrorthrownbytheJavaScriptinterpreter.

Object-orientedprogrammingandTypeScriptcomewithasolution.Theyallowustomakesureourobjectshavesimilarbehavioriftheyimplementinterfacesthatdeclarethesubsetofthepropertiestheyown.

Forexample,wecandefineourinterfaceAccountable:

interfaceAccountable{

getIncome():number;

}

Now,wecanmakesurebothIndividualandFirmimplementthisinterfacebydoingasfollows:

classFirmimplementsAccountable{

getIncome():number{

//…

}

}

classIndividualimplementsAccountable{

getIncome():number{

//…

}

}

Incaseweimplementagiveninterface,weneedtoprovideimplementationforallthemethodsdefinedinsideit,otherwisetheTypeScriptcompilerwillthrowanerror.Themethodsweimplementmusthavethesamesignatureastheonesdeclaredintheinterfacedefinition.

TypeScriptinterfacesalsosupportproperties.IntheAccountableinterface,wecanincludeafieldcalledaccountNumberwithatypeofstring:

interfaceAccountable{

accountNumber:string;

getIncome():number;

}

Wecandefineitinourclassasafieldoragetter.

InterfaceinheritanceInterfacesmayalsoextendeachother.Forexample,wemayturnourIndividualclass

intoaninterfacethathasasocialsecuritynumber:

interfaceAccountable{

accountNumber:string;

getIncome():number;

}

interfaceIndividualextendsAccountable{

ssn:string;

}

Sinceinterfacessupportmultipleinheritances,IndividualmayalsoextendtheinterfaceHumanthathasthenameandageproperties:

interfaceAccountable{

accountNumber:string;

getIncome():number;

}

interfaceHuman{

age:number;

name:number;

}

interfaceIndividualextendsAccountable,Human{

ssn:string;

}

ImplementingmultipleinterfacesIncasetheclass’sbehaviorisaunionofthepropertiesdefinedinacoupleofinterfaces,itmayimplementallofthem:

classPersonimplementsHuman,Accountable{

age:number;

name:string;

accountNumber:string;

getIncome():number{

//...

}

}

Inthiscase,weneedtoprovidetheimplementationofallthemethodsdeclaredinsidetheinterfacesourclassimplements,otherwisethecompilerwillthrowacompile-timeerror.

FurtherexpressivenesswithTypeScriptdecoratorsInES2015,weareabletodecorateonlyclasses,properties,methods,getters,andsetters.TypeScripttakesthisfurtherbyallowingustodecoratefunctionsormethodparameters:

classHttp{

//…

}

classGitHubApi{

constructor(@Inject(Http)http){

//…

}

}

However,theparameterdecoratorsshouldnotalteranyadditionalbehavior.Instead,theyareusedtogeneratemetadata.ThemosttypicalusecaseofthesedecoratorsisthedependencyinjectionmechanismofAngular2.

WritinggenericcodebyusingtypeparametersInthebeginningofthesectiononusingstatictyping,wementionedthetypeparameters.Inordertogetabetterunderstandingofthem,let’sbeginwithanexample.Let’ssupposewewanttoimplementtheclassicaldata-structureBinarySearchTree.Let’sdefineitsinterfaceusingaclasswithoutapplyinganymethodimplementations:

classNode{

value:any;

left:Node;

right:Node;

}

classBinarySearchTree{

privateroot:Node;

insert(any:value):void{/*…*/}

remove(any:value):void{/*…*/}

exists(any:value):boolean{/*…*/}

inorder(callback:{(value:any):void}):void{/*…*/}

}

Intheprecedingsnippet,wedefinedaclasscalledNode.Theinstancesofthisclassrepresenttheindividualnodesinourtree.Eachnodehasaleftandarightchildnodeandavalueofthetypeany;weuseanyinordertobeabletostoredataofanytypeinsideournodesandrespectivelyinsideBinarySearchTree.

Althoughtheearlierimplementationlooksreasonable,we’regivinguponusingthemostimportantfeaturethatTypeScriptprovides—statictyping.ByusinganyasatypeofthevaluefieldinsidetheNodeclass,wecan’ttakecompleteadvantageofthecompile-timetypechecking.ThisalsolimitsthefeaturesthatIDEsandtexteditorsprovidewhenweaccessthevaluepropertyoftheinstancesoftheNodeclass.

TypeScriptcomeswithanelegantsolutionthatisalreadywidelypopularintheworldofstatictyping—typeparameters.Usinggenerics,wecanparameterizetheclasseswecreatewiththetypeparameters.Forexample,wecanturnourNodeclassintothefollowing:

classNode<T>{

value:T;

left:Node<T>;

right:Node<T>;

}

Node<T>indicatesthatthisclasshasasingletypeparametercalledTthatisusedsomewhereinsidetheclass’sdefinition.WecanuseNodebydoingasfollows:

letnumberNode=newNode<number>();

letstringNode=newNode<string>();

numberNode.right=newNode<number>();

numberNode.value=42;

numberNode.value="42";//Type"string"isnotassignabletotype

"number"

numberNode.left=stringNode;//TypeNode<string>isnotassignableto

typeNode<number>

Intheprecedingsnippet,wecreatedthreenodes:numberNode,stringNode,andanothernodeofthetypeNode<number>,assigningitsvaluetotherightchildofnumberNode.NoticethatsincenumberNodeisofthetypeNode<number>,wecansetitsvalueto42,butwecan’tusethestring"42".Thesameisapplicabletoitsleftchild.Inthedefinition,we’veexplicitlydeclaredthatwewanttheleftandrightchildrentobeofthetypeNode<number>.ThismeansthatwecannotassignvaluesofthetypeNode<string>tothem;that’swhywegetthesecondcompile-timeerror.

UsinggenericfunctionsAnothertypicaluseofgenericsisfordefiningfunctionsthatoperateoverasetoftypes.Forexample,wemaydefineanidentityfunctionthatacceptsanargumentoftypeTandreturnsit:

functionidentity<T>(arg:T){

returnarg;

}

However,insomecases,wemaywanttouseonlytheinstancesofthetypesthathavesomespecificproperties.Forachievingthis,wecanuseanextendedsyntaxthatallowsustodeclaresubtypesofthetypesthatshouldbethetypeparameters:

interfaceComparable{

compare(a:Comparable):number;

}

functionsort<TextendsComparable>(arr:Comparable[]):Comparable[]{

//…

}

Forexample,here,wedefinedaninterfacecalledComparable.Ithasasingleoperationcalledcompare.TheclassesthatimplementtheinterfaceComparableneedtoimplementtheoperationcompare.Whencompareiscalledwithagivenargument,itreturns1ifthetargetobjectisbiggerthanthepassedargument,0iftheyareequal,and-1ifthetargetobjectissmallerthanthepassedargument.

HavingmultipletypeparametersTypeScriptallowsustousemultipletypeparameters:

classPair<K,V>{

key:K;

value:V;

}

Inthiscase,wecancreateaninstanceoftheclassPair<K,V>usingthefollowingsyntax:

letpair=newPair<string,number>();

pair.key="foo";

pair.value=42;

WritinglessverbosecodewithTypeScript’stypeinferenceStatictypinghasanumberofbenefits;however,itmakesuswriteamoreverbosecodebyaddingalltherequiredtypeannotations.

Insomecases,theTypeScript’scompilerisabletoguessthetypesofexpressionsinsideourcode,forinstance:

letanswer=42;

answer="42";//Type"string"isnotassignabletotype"number"

Intheprecedingexample,wedefinedavariableanswerandweassignedthevalue42toit.SinceTypeScriptisstaticallytypedandthetypeofavariablecannotchangeoncedeclared,thecompilerissmartenoughtoguessthatthetypeofanswerisnumber.

Ifwedon’tassignavaluetoavariablewithinitsdefinition,thecompilerwillsetitstypetoany:

letanswer;

answer=42;

answer="42";

Theprecedingsnippetwillcompilewithoutanycompile-timeerrors.

BestcommontypeSometimes,thetypeinferencecouldbearesultofseveralexpressions.Suchisthecasewhenweassignaheterogeneousarraytoavariable:

letx=["42",42];

Inthiscase,thetypeofxwillbeany[].However,supposewehavethefollowing:

letx=[42,null,32];

Thetypeofxwillthenbenumber[],sincethetypeNumberisasubtypeofNull.

ContextualtypeinferenceContextualtypingoccurswhenthetypeofanexpressionisimpliedfromitslocation,forexample:

document.body.addEventListener("mousedown",e=>{

e.foo();//Property"foo"doesnotexistsonatype"MouseEvent"

},false);

Inthiscase,thetypeoftheargumentofthecallbackeisguessedbythecompilerbasedonthecontextinwhichitisused.ThecompilerunderstandswhatthetypeofeisbasedonthecallofaddEventListenerandtheargumentspassedtothemethod.Incasewewereusingakeyboardevent(keydown,forexample),TypeScriptwouldhavebeenawarethateisofthetypeKeyboardEvent.

TypeinferenceisamechanismthatallowsustowritelessverbosecodebytakingadvantageofthestaticanalysisperformedbyTypeScript.Basedonthecontext,TypeScript’scompilerisabletoguessthetypeofagivenexpressionwithoutexplicitdefinition.

UsingambienttypedefinitionsAlthoughstatictypingisamazing,mostofthefrontendlibrariesweusearebuiltwithJavaScript,whichisdynamicallytyped.Sincewe’dwanttouseTypeScriptinAngular2,nothavingcompile-typinginthecodethatusesexternallibrariesisabigissue;itpreventsusfromtakingadvantageofthecompile-timetype-checking.

TypeScriptwasbuiltkeepingthesepointsinmind.InordertoallowtheTypeScriptcompilertotakecareofwhatitdoesbest,wecanusetheso-calledambienttypedefinitions.TheyallowustoprovideexternaltypedefinitionsoftheexistingJavaScriptlibraries.Thisway,theyprovidehintstothecompiler.

UsingpredefinedambienttypedefinitionsFortunately,wedon’thavetocreateambienttypedefinitionsforallJavaScriptlibrariesandframeworksweuse.Thecommunityand/ortheauthorsoftheselibrarieshavealreadypublishedsuchdefinitionsonline;thebiggestrepositoryresidesat:https://github.com/DefinitelyTyped/DefinitelyTyped.There’salsoatoolformanagingthemcalledtypings.Wecaninstallitusingnpmbythefollowingcommand:

npminstall–gtypings

Theconfigurationoftypingsisdefinedinafilecalledtypings.jsonandallinstalledambienttypings,bydefault,willbeinthedirectory./typings.

Inordertocreatetypings.jsonfilewithbasicconfigurationuse:

typingsinit

Wecaninstallnewtypedefinitionusing:

typingsinstallangularjs--ambient

TheprecedingcommandwilldownloadthetypedefinitionsforAngularJS1.xandsavetheminbothbrowser/ambient/angular/angular.d.tsandmain/ambient/angular/angular.d.tsunderthetypingsdirectory.

NoteHavingbothmain/ambientandbrowser/ambientdirectoriesisduetopreventingtypecollisions.Forinstance,ifweuseTypeScriptinboththebackend/buildofourproject,anditsfrontendtherecouldbeintroducedduplicationsoftypedefinitionswhichwillleadtocompile-timeerrors.Byhavingtwodirectoriesfortheambienttypingsoftheindividualpartsoftheproject,wecanincludeonlyoneofthemusingrespectivelymain.d.tsandbrowser.d.ts.ForfurtherinformationontypingsyoucanvisittheofficialrepositoryoftheprojectonGitHubhttps://github.com/typings/typings.

Inordertodownloadatypedefinitionandaddentryforitinsidetypings.jsonyoucanuse:

typingsinstallangular--ambient--save

Afterrunningtheprecedingcommandyourtypings.jsonfileshouldlooksimilarto:

{

"dependencies":{},

"devDependencies":{},

"ambientDependencies":{

"angular":

"github:DefinitelyTyped/DefinitelyTyped/angularjs/angular.d.ts#1c4a34873c9e

70cce86edd0e61c559e43dfa5f75"

}

}

NowinordertouseAngularJS1.xwithTypeScriptcreateapp.tsandenterthefollowingcontent:

///<referencepath="./typings/browser.d.ts"/>

varmodule=angular.module("module",[]);

module.controller("MainCtrl",

functionMainCtrl($scope:angular.IScope){

});

Tocompileapp.tsuse:

tscapp.ts

TheTypeScriptcompilewilloutputthecompiledcontentintoapp.js.InordertoaddextraautomationandinvoketheTypeScriptcompilereachtimeyouchangeanyofthefilesinyourproject,youcanuseataskrunnerlikegulporgrunt,orpassthe-woptiontotsc.

NoteSinceusingthereferenceelementforincludingtypedefinitionsisconsideredbadpracticewecanuseatsconfig.jsonfileinstead.Therewecanconfigurewhichdirectoriesneedtobeincludedinthecompilationprocessbytsc.Formoreinformationvisithttps://github.com/Microsoft/TypeScript/wiki/tsconfig.json.

CustomambienttypedefinitionsTounderstandhoweverythingworkstogether,let’stakealookatanexample.SupposewehavethefollowinginterfaceofaJavaScriptlibrary:

varDOM={

//Returnsasetofelementswhichmatchthepassedselector

selectElements:function(selector){

//…

},

hide:function(element){

//…

},

show:function(element){

//…

}

};

WehaveanobjectliteralassignedtoavariablecalledDOM.Theobjecthasthefollowingmethods:

selectElements:AcceptsasingleargumentwithtypestringandreturnsasetofDOMelements.hide:AcceptsaDOMnodeasanargumentandreturnsnothing.show:AcceptsaDOMnodeasanargumentandreturnsnothing.

InTypeScript,theprecedingdefinitionwouldlookasfollows:

varDOM={

//Returnsasetofelementswhichmatchthepassedselector

selectElements:function(selector:string):HTMLElement[]{

return[];

},

hide:function(element:HTMLElement):void{

element.hidden=true;

},

show:function(element:HTMLElement):void{

element.hidden=false;

}

};

Thismeansthatwecandefineourlibrary’sinterfaceasfollows:

interfaceLibraryInterface{

selectElements(selector:string):HTMLElement[]

hide(element:HTMLElement):void

show(element:HTMLElement):void

}

Definingts.dfilesAfterwehavetheinterfaceofourlibrary,itwillbeeasytocreatetheambienttypedefinition;wejusthavetocreateafilewithanextensionts.dcalleddomandenterthefollowingcontent:

//inside"dom.d.ts"

interfaceDOMLibraryInterface{

selectElements(selector:string):HTMLElement[]

hide(element:HTMLElement):void

show(element:HTMLElement):void

}

declarevarDOM:DOMLibraryInterface;

Intheprecedingsnippet,wedefinedtheinterfacecalledDOMLibraryInterfaceanddeclaredthevariableDOMofthetypeDOMLibraryInterface.

TheonlythingleftbeforebeingabletoexploitstatictypingwithourJavaScriptlibraryisincludingtheexternaltypedefinitioninthescriptfileswewanttouseourlibraryin.Wecandoitasfollows:

///<referencepath="dom.d.ts"/>

Theprecedingsnippethintsthecompileronwheretofindtheambienttypedefinitions.

SummaryInthischapter,wepeekedattheTypeScriptlanguagethatisusedfortheimplementationofAngular2.AlthoughwecandevelopourAngular2applicationsusingECMAScript5,Google’srecommendationistouseTypeScriptinordertotakeadvantageofthestatictypingitprovides.

Whileexploringthelanguage,welookedatsomeofthecorefeaturesofES2015andES2016.WeexplainedtheES2015andES2016classes,arrowfunctions,blockscopevariabledefinitions,destructuring,andmodules.SinceAngular2takesadvantageoftheES2016decoratorsandmoreaccuratelytheirextensioninTypeScript,asectionwasdedicatedtothem.

Afterthis,wetookalookathowwecantakeadvantageofstatictypingbyusingexplicittypedefinitions.Wedescribedsomeofthebuilt-intypesinTypeScriptandhowwecandefineclassesinthelanguagebyspecifyingaccessmodifiersfortheirmembers.Ournextstopwastheinterfaces.WeendedouradventuresinTypeScriptbyexplainingthetypeparametersandtheambienttypedefinitions.

Inthenextchapter,wearegoingtostartexploringAngular2indepthbyusingtheframework’scomponentsanddirectives.

Chapter4.GettingStartedwithAngular2ComponentsandDirectivesBythispoint,you’realreadyfamiliarwiththecorebuildingblocksthatAngular2providesforthedevelopmentofsingle-pageapplicationsandtherelationsbetweenthem.However,we’vetouchedonlythesurfacebyintroducingthegeneralideabehindAngular’sconceptsandthebasicsyntaxusedfortheirdefinition.Inthischapter,we’lltakeadeepdiveintoAngular2’scomponentsanddirectives.

Inthefollowingsections,wewillcoverthesetopics:

EnforcedseparationofconcernsofthebuildingblocksthatAngular2providesfordevelopingapplications.TheappropriateuseofdirectivesorcomponentswheninteractingwiththeDOM.Built-indirectivesanddevelopingcustomones.Anin-depthlookatcomponentsandtheirtemplates.Contentprojection.Viewchildrenversuscontentchildren.Thecomponent’slifecycle.Usingtemplatereferences.ConfiguringAngular’schangedetection.

TheHelloworld!applicationinAngular2Now,let’sbuildourfirst“Helloworld!”appinAngular2!Inordertogeteverythingupandrunningaseasyandquicklyaspossible,forourfirstapplication,wewillusetheECMAScript5syntaxwiththetranspiledbundleofAngular2.First,createtheindex.htmlfilewiththefollowingcontent:

<!--ch4/es5/hello-world/index.html-->

<!DOCTYPEhtml>

<htmllang="en">

<head>

<metacharset="UTF-8">

<title></title>

</head>

<body>

<scriptsrc="https://code.angularjs.org/2.0.0-beta.9/angular2-

polyfills.min.js"></script>

<scriptsrc="https://code.angularjs.org/2.0.0-beta.9/Rx.umd.min.js">

</script>

<scriptsrc="https://code.angularjs.org/2.0.0-beta.9/angular2-

all.umd.min.js"></script>

<scriptsrc="./app.js"></script>

</body>

</html>

TheprecedingHTMLfiledefinesthebasicstructureofourpage.Justbeforeclosingthebodytag,wehavereferencestofourscriptfiles:thepolyfillsrequiredbytheframework(includingES2015shim,zone.js,andothers),RxJS,theES5bundleofAngular2,andthefilethatcontainstheapplicationwe’regoingtobuild.

NoteRxJSisusedbyAngular’scoreinordertoallowustoempowerthereactiveprogrammingparadigminourapplications.Inthefollowingcontent,wewilltakeonlyashallowlookathowwecantakeadvantageofobservables.Forfurtherinformation,youcanvisittheRxJSGitHubrepositoryathttps://github.com/Reactive-Extensions/RxJS.

Inthesamedirectorywhereyourindex.htmlresides,createafilecalledapp.jsandenterthefollowingcontentinsideit:

//ch4/es5/hello-world/app.js

varApp=ng.core.Component({

selector:'app',

template:'<h1>Hello{{target}}!</h1>'

})

.Class({

constructor:function(){

this.target='world';

}

});

ng.platform.browser.bootstrap(App);

Intheprecedingsnippet,wedefineacomponentcalledAppwithanappselector.Thisselectorwillmatchalltheappelementsinsideourtemplatesthatareinthescopeoftheapplication.Thecomponenthasthefollowingtemplate:

'<h1>Hello{{target}}!</h1>'

ThissyntaxshouldalreadybefamiliartoyoufromAngularJS1.x.Whencompiledinthecontextofthegivencomponent,theprecedingsnippetwillinterpolatethetemplatewiththeresultoftheexpressioninsidethecurlybrackets.Inourcase,theexpressionissimplythetargetvariable.

ToClass,wepassanobjectliteral,whichhasasinglemethodcalledconstructor.ThisDSLprovidesanalternativewaytodefineclassesinECMAScript5.Inthebodyoftheconstructorfunction,weaddapropertycalledtargetwithavalueofthe"world"string.Inthelastlineofthesnippet,weinvokethebootstrapmethodinordertoinitializeourapplicationwithAppasarootcomponent.

Notethatbootstrapislocatedunderng.platform.browser.Thisisduetothefactthattheframeworkisbuiltfordifferentplatformsinmind,suchasabrowser,NativeScript,andsoon.Byplacingthebootstrapmethodsusedbythedifferentplatformsunderaseparatenamespace,Angular2canimplementdifferentlogictoinitializetheapplicationandalsoincludedifferentsetsofprovidersanddirectivesthatareplatformspecific.

Now,ifyouopenindex.htmlwithyourbrowser,youshouldseesomeerrors,asshowninthefollowingscreenshot:

Thishappenedbecausewemissedsomethingquiteimportant.Wedidn’tusetherootcomponentanywhereinsideindex.html.Inordertofinishtheapplication,addthefollowingHTMLelementaftertheopentagofthebodyelement:

<app></app>

Now,youcanrefreshyourbrowsertoseethefollowingresult:

NoteUsingTypeScript

AlthoughwealreadyhaveanAngular2applicationrunning,wecandomuchbetter!Wedidn’tuseanypackagemanagerormoduleloader.WespentallofChapter3,TypeScriptCrashCourse,talkingaboutTypeScript;however,wedidn’twriteasinglelineofitintheprecedingapplication.AlthoughitisnotrequiredthatyouuseTypeScriptwithAngular2,it’smuchconvenienttotakeadvantageofallthebonusesthatstatictypingprovides.

SettingupourenvironmentThecoreteamofAngulardevelopedabrandnewCLItoolforAngular2,whichallowsustobootstrapourapplicationswithafewcommands.Althoughwearegoingtointroduceitinthelastchapter,bythen,inordertoboostourlearningexperience,wearegoingtousethecodelocatedathttps://github.com/mgechev/switching-to-angular2.ItincludesalltheexamplesinthisbookandallowsustoquicklybootstrapourAngular2application(youcanknowmoreonhowtoquicklystartdevelopingwebapplicationswithAngular2inChapter5,DependencyInjectioninAngular2.).Ithasalltherequireddependenciesdeclaredinpackage.json,thedefinitionofbasicgulptasks,suchasthedevelopmentserver,thetranspilationofyourTypeScriptcodetoECMAScript5,live-reload,andsoon.Ourupcomingexamplesaregoingtobebasedonit.

Inordertosetuptheswitching-to-angular2project,you’llneedGit,Node.jsv5.x.x,andnpmupandrunningonyourcomputer.IfyouhaveadifferentversionoftheNode.jsinstalled,Irecommendthatyoutakealookatnvm(theNode.jsversionmanager,whichisavailableathttps://www.npmjs.com/package/nvm)orn(https://www.npmjs.com/package/n).Usingthesetools,you’llbeabletohavemultipleversionsofNode.jsonyourmachineandswitchbetweenthemwithasinglecommandviathecommandline.

InstallingourprojectrepositoryLet’sstartbysettinguptheswitching-to-angular2project.Openyourterminalandenterthefollowingcommands:

#Willclonetherepositoryandsaveittodirectorycalled

#switching-to-angular2

gitclonehttps://github.com/mgechev/switching-to-angular2.git

cdswitching-to-angular2

npminstall

Thefirstlinewillclonetheswitching-to-angular2projectintoadirectorycalledswitching-to-angular2.

Thelaststepbeforebeingabletoruntheseedprojectistoinstallalltherequireddependenciesusingnpm.ThisstepmaytakeawhiledependingonyourInternetconnection,sobepatientanddonotinterruptit.Ifyouencounteranyproblems,donothesitatetoraisetheissuesathttps://github.com/mgechev/switching-to-angular2/issues.

Thelaststepleftistostartthedevelopmentserver:

npmstart

Whentheprocessofthetranspilationiscompleted,yourbrowserwillautomaticallyopenwiththisURL:http://localhost:5555/dist/dev.Youshouldnowseeaviewsimilartowhatisshowninthefollowingscreenshot:

PlayingwithAngular2andTypeScriptNow,let’splayaroundwiththefileswealreadyhave!Navigatetotheapp/ch4/ts/hello-worlddirectoryinsideswitching-to-angular2.Then,openapp.tsandreplaceitscontentwiththefollowingsnippet:

//ch4/ts/hello-world/app.ts

import{Component}from'angular2/core';

import{bootstrap}from'angular2/platform/browser';

@Component({

selector:'app',

templateUrl:'./app.html'

})

classApp{

target:string;

constructor(){

this.target='world';

}

}

bootstrap(App);

Let’stakealookatthecodelinebyline:

import{Component}from'angular2/core';

import{bootstrap}from'angular2/platform/browser';

Initially,weimportthe@Componentdecoratorfromtheangular2/coremoduleandthebootstrapfunctionfromangular2/platform/browser.Later,weuse@ComponenttodecoratetheAppclass.Tothe@Componentdecorator,wepassalmostthesameobjectliteralthatweusedintheECMAScript5versionoftheapplication,andthisway,wedefinetheCSSselectorforthecomponent.

Asanextstep,wedefinetheviewofthecomponent.However,notethatinthiscase,weusetemplateUrlinsteadofsimplyinliningthecomponent’stemplate.

Openapp.htmlandreplacethefile’scontentwith<h1>Hello{{target}}!</h1>.Thecontentofapp.htmlshouldbethesameastheinlinedtemplateweusedpreviously.Sincewecanuseatemplatebybothinliningit(withtemplate)andsettingitsURL(templateUrl),thecomponent’sAPIisquitesimilartotheAngularJS1.xdirectivesAPI.

Inthelastlineofthesnippet,webootstraptheapplicationbyprovidingtherootcomponent.

DiggingintotheindexNow,let’stakealookatindex.htmlinordertogetasenseofwhatgoesonwhenwestarttheapplication:

<!--ch4/ts/hello-world/index.html-->

<!DOCTYPEhtml>

<htmllang="en">

<head>

<metacharset="utf-8">

<metahttp-equiv="X-UA-Compatible"content="IE=edge">

<title><%=TITLE%></title>

<metaname="description"content="">

<metaname="viewport"content="width=device-width,initial-scale=1">

<!--inject:css-->

<!--endinject-->

</head>

<body>

<app>Loading…</app>

<!--inject:js-->

<!--endinject-->

<%=INIT%>

</body>

</html>

Notethatinsidethebodyofthepage,weusetheappelementwiththecontentofthetextnode,"Loading…",inside.The"Loading…"labelwillbevisibleuntiltheapplicationgetsbootstrappedandthemaincomponentgetsrendered.

NoteTherearetemplateplaceholders<%=INIT%>and<--inject:js…thatinjectcontentthatisspecifictoindividualdemos.TheyarenotAngularspecificbutinsteadaimtopreventcodeduplicationsinthecodesamplesattachedtothebookbecauseofthesharedstructurebetweenthem.InordertoseehowthisspecificHTMLfilehasbeentransformed,open/dist/dev/ch4/ts/hello-world/index.html.

UsingAngular2directivesWealreadybuiltoursimple“Helloworld!”app.Now,let’sstartbuildingsomethingthatisclosertoareal-lifeapplication.Bytheendofthissection,we’llhaveasimpleapplicationthatlistsanumberofitemsweneedtodoandgreetsusattheheaderofthepage.

Let’sstartbydevelopingourappcomponent.Thetwomodificationsfromthepreviousexamplethatweneedtomakearetorenamethetargetpropertytonameandaddalistoftodostothecomponent’scontrollerdefinition:

//ch4/ts/ng-for/detailed-syntax/app.ts

import{Component}from'angular2/core';

import{bootstrap}from'angular2/platform/browser';

@Component({

selector:'app',

templateUrl:'./app.html',

})

classApp{

todos:string[];

name:string;

constructor(){

this.name='John';

this.todos=['Buymilk','Savetheworld'];

}

}

bootstrap(App);

Theonlythingleftistochangethetemplateinordertoconsumetheprovideddata.We’realreadyfamiliarwiththeng-repeatdirectivefromAngularJS1.x.Itallowsustoloopalistofitemsusingamicrosyntax,whichislaterinterpretedbyAngularJS1.x.However,thedirectivedoesn’tcarryenoughsemantics,soitishardtobuildtoolsthatperformstaticcodeanalysisandhelpusimproveourdevelopmentexperience.Sincetheng-repeatdirectiveisquiteuseful,Angular2tooktheideaandimproveditfurtherinordertoallowmoresophisticatedtoolingbyintroducingfurthersemanticsontopofit.ItallowsbetterstaticcodeanalysistobeperformedbyIDEsandtexteditors.Suchsupportwillpreventusfrommakingtyposinthecodewewriteandallowustohavesmootherdevelopmentexperience.

Inapp.html,addthefollowingcontent:

<!--ch4/ts/ng-for/detailed-syntax/app.html-->

<h1>Hello{{name}}!</h1>

<p>

Here'salistofthethingsyouneedtodo:

</p>

<ul>

<templatengForvar-todo[ngForOf]="todos">

<li>{{todo}}</li>

</template>

</ul>

NoteThetemplateelementisaplacewherewecanholdmarkupandmakesurethatitwon’tberenderedbythebrowser.Thisisquiteusefulifweneedtoembedthetemplatesofourapplicationdirectlyintothemarkupofthepageandletthetemplateenginewe’reusingtoprocessthemlater.Inthecurrentexample,thismeansthatiftheAngular2DOMcompilerdoesn’tprocesstheDOMtree,allwe’regoingtoseeonthescreenaretheh1,pelementsandtheulelementwithoutanylistitems.

Now,afteryourefreshyourbrowser,youshouldseethefollowingresult:

Sofar,sogood!Theonlynewthingsleftintheprecedingsnippetsaretheattributesofthetemplateelementthatwe’renotfamiliarwith,suchasngFor,var-todo,and[ngForOf].Let’stakealookatthem.

ThengFordirectiveThengFordirectiveisadirectivethatallowsustoloopoveracollectionofitemsanddoesexactlywhatng-repeatdoesinAngularJS1.x,butitbringssomeextrasemantics.NotethatthengForOfattributeissurroundedbybrackets.Atfirst,thesebracketsmightseemlikeinvalidHTML.However,accordingtotheHTMLspecification,theiruseispermittedinattributenames.TheonlythingtheW3Cvalidatorisgoingtocomplainaboutisthefactthatthetemplateelementdoesn’townsuchattributes;however,browserswon’thaveproblemsprocessingthemarkup.

Thesemanticsbehindthesebracketsisthatthevalueoftheattributesurroundedbythemisanexpression,whichneedstobeevaluated.

ImprovedsemanticsofthedirectivessyntaxInChapter1,GettingStartedwithAngular2,wementionedtheopportunityforimprovedtoolinginAngular2.AbigissueinAngularJS1.xisthedifferentwaysinwhichwecanusedirectives.Thisrequiresanunderstandingoftheattributevalues,whichcanbeliterals,expressions,callbacks,oramicrosyntax.Angular2eliminatesthisproblembyintroducingafewsimpleconventionsthatarebuiltintotheframework:

propertyName="value"

[propertyName]="expression"

(eventName)="handler()"

Inthefirstline,thepropertyNameattributeacceptsastringliteralasavalue.Angularwillnotprocesstheattribute’svalueanyfurther;itwilluseitthewayitissetinthetemplate.

Thesecondsyntax,[propertyName]="expression",givesahinttoAngular2thatthevalueoftheattributesshouldbehandledasanexpression.WhenAngular2findsanattributesurroundedbybrackets,itwillinterprettheexpressioninthecontextofthecomponentassociatedtothetemplate.Inshort,ifwewanttosetanon-stringvalueorresultofanexpressionasvalueofgivenpropertyweneedtousethissyntax.

Thelastexampleshowshowwecanbindtoevents.Thesemanticsbehind(eventName)="handler()"isthatwewanttohandlealleventscalledeventNamethataretriggeredbythegivencomponentwiththehandler()expression.

We’regoingtodiscussmoreexampleslaterinthischapter.

NoteAngularprovidesalternativecanonicalsyntax,whichallowsustodefinethebindingsoftheelementswithoutusingbrackets.Forinstance,thepropertybindingcanbeexpressedusingthefollowingcode:

<input[value]="foo">

Itcanalsobeexpressedusingthis:

<inputbind-value="foo">

Similarly,wecanexpresstheeventbindingswiththefollowingcode:

<button(click)="handle()">Clickme</button>

Theycanalsobeexpressedusingthis:

<buttonon-click="handle()">Clickme</button>

DeclaringvariablesinsideatemplateThelastthingleftfromtheprecedingtemplateisthevar-todoattribute.WhatwearetellingAngularusingthissyntaxisthatwewanttodeclareanewvariablecalledtodoandbindittotheindividualitemsfromthecollectionwegetfromtheevaluationoftheexpressionsetasavalueof[ngForOf].

UsingsyntaxsugarintemplatesAlthoughthetemplatesyntaxisawesomeandprovidesmuchmoremeaningofthecodetotheIDEsortexteditorsweuse,itisquiteverbose.Angular2providesanalternativesyntax,whichwillbedesugaredtotheoneshowninthepreceding.Insteadofusingvar-todo,forexample,wecanuse#todo,whichhasthesamesemantics.

ThereareafewAngular2directivesthatrequiretheusageofatemplateelement,forexample,ngForOf,ngIf,andngSwitch.Sincesuchdirectivesareusedoften,there’sanalternativesyntaxforthem.Insteadoftypingdowntheentiretemplateelementexplicitly,wecansimplyprefixthedirectivewith*.ThiswillallowustoturnourngForOfdirectivesyntaxusageintothefollowing:

<!--ch4/ts/ng-for/syntax-sugar/app.html-->

<ul>

<li*ngFor="#todooftodos">{{todo}}</li>

</ul>

Later,thistemplatewillbedesugaredbyAngular2tothemoreverbosesyntaxdescribedearlier.Sincethelessverbosesyntaxiseasiertoreadandwrite,itsuseisconsideredasbestpractice.

NoteThe*characterallowsyoutoremovethetemplateelementandputthedirectivedirectlyontherootofthetemplateelement(intheprecedingexample,thelistitem,li).

DefiningAngular2directivesNowthatwe’vebuiltasimpleAngular2component,let’scontinueourjourneybyunderstandingtheAngular2directives.

UsingAngular2directives,wecanapplydifferentbehavioralorstructuralchangesovertheDOM.Inthisexample,we’regoingtobuildasimpletooltipdirective.

Incontrasttocomponents,directivesdonothaveviewsandtemplates,respectively.AnothercoredifferencebetweenthesetwoconceptsisthatthegivenHTMLelementmayhaveonlyasinglecomponentbutmultipledirectivesonit.Inotherwords,directivesaugmenttheelementscomparedtocomponentsthataretheactualelementsinourviews.

Angular’scoreteam’srecommendationistousedirectivesasattributes,prefixedwithanamespace.Keepingthisinmind,wewillusethetooltipdirectiveinthefollowingway:

<divsaTooltip="Helloworld!"></div>

Intheprecedingsnippet,weusethetooltipdirectiveoverthedivelement.Asanamespace,itsselectorusesthesastring.

NoteForsimplicity,intherestofthebookwemaynotprefixalltheselectorsofourcomponentsanddirectives.However,forproductionapplicationsfollowingbestpracticesisessential.YoucanfindanAngular2styleguidewhichpointsoutsuchpracticesathttps://github.com/mgechev/angular2-style-guide.

Beforeimplementingourtooltip,weneedtoimportacoupleofthingsfromangular2/core.OpenanewTypeScriptfilecalledapp.tsandenterthefollowingcontent;we’llfilltheplaceholderslater:

import{Directive,ElementRef,HostListener…}from'angular2/core';

Intheprecedingline,weimportthefollowingdefinitions:

ElementRef:Thisallowsustoinjecttheelementreference(we’renotlimitedtotheDOMonly)tothehostelement.Inthesampleusageoftheprecedingtooltip,wegetanAngularwrapperofthedivelement,whichholdsthetooltipattribute.Directive:Thisdecoratorallowsustoaddthemetadatarequiredforthenewdirectiveswedefine.HostListener(eventname):Thisisamethoddecoratorthatacceptsaneventnameasanargument.Duringinitializationofthedirective,Angular2willaddthedecoratedmethodasaneventhandlerfortheeventnameeventofthehostelement.

Let’slookatourimplementation;thisiswhatthedirective’sdefinitionlookslike:

//ch4/ts/tooltip/app.ts

@Directive({

selector:'[saTooltip]'

})

exportclassTooltip{

@Input()

saTooltip:string;

constructor(privateel:ElementRef,privateoverlay:Overlay){

this.overlay.attach(el.nativeElement);

}

@HostListener('mouseenter')

onMouseEnter(){

this.overlay.open(this.el,this.saTooltip);

}

@HostListener('mouseleave')

onMouseLeave(){

this.overlay.close();

}

}

Settingthedirective’sinputsIntheprecedingexample,wedeclareadirectivewiththesaTooltipselector.NotethatAngular’sHTMLcompileriscasesensitive,whichmeansthatitwilldistinguishthe[satooltip]and[saTooltip]selectors.Later,wewilldeclaretheinputofthedirectiveusingthe@InputdecoratoroverthesaTooltipproperty.Thesemanticsbehindthiscodeis:declareapropertycalledsaTooltipandbindittothevalueoftheresultthatwegotfromtheevaluationoftheexpressionpassedtothesaTooltipattribute.

The@Inputdecoratoracceptsasingleargument—thenameoftheattributewewanttobindto.Incasewedon’tpassanargument,Angularwillcreateabindingbetweentheattributewiththesamenameasthepropertyitself.Wewillexplaintheconceptofinputandoutputindetaillaterinthischapter.

Understandingthedirective’sconstructorTheconstructordeclarestwoprivateproperties:eloftheElementReftypeandoverlayoftheOverlaytype.TheOverlayclassimplementslogictomanagethetooltips’overlaysandisgoingtobeinjectedusingtheDImechanismofAngular.Inordertodeclareitasavailableforinjection,weneedtodeclarethetop-levelcomponentinthefollowingway:

@Component({

selector:'app',

templateUrl:'./app.html',

providers:[Overlay],

//...

})

classApp{}

NoteWe’regoingtotakealookatthedependencyinjectionmechanismofAngular2inthenextchapter,wherewewillexplainthewayinwhichwecandeclarethedependenciesofourservices,directives,andcomponents.

TheimplementationoftheOverlayclassisnotimportantforthepurposeofthischapter.However,ifyou’reinterestedinit,youcanfindtheimplementationin:ch4/ts/tooltip/app.ts.

BetterencapsulationofdirectivesInordertomakethetooltipdirectiveavailabletotheAngular’scompiler,weneedtoexplicitlydeclarewhereweintendtouseit.Forinstance,takealookattheAppclassatch4/ts/tooltip/app.ts;there,youcannoticethefollowing:

@Component({

selector:'app',

templateUrl:'./app.html',

providers:[Overlay],

directives:[Tooltip]

})

classApp{}

Tothe@Componentdecorator,wepassanobjectliteralthathasthedirectivesproperty.Thispropertycontainsalistofallthedirectivesthatshouldbeavailableintheentirecomponentsubtreewiththerootofthegivencomponent.

Atfirst,itmightseemannoyingthatyoushouldexplicitlydeclareallthedirectivesthatyourcomponentuses;however,thisenforcesbetterencapsulation.InAngularJS1.x,alldirectivesareinaglobalnamespace.Thismeansthatallthedirectivesdefinedintheapplicationareaccessibleinallthetemplates.Thisbringsinsomeproblems,forexample,namecollision.Inordertodealwiththisissue,we’veintroducednamingconventions,forinstance,the“ng-”prefixofallthedirectivesdefinedbyAngularJS1.xand“ui-”foralldirectivescomingwiththeAngularUI.

Thisway,byexplicitlydeclaringallthedirectives,thegivencomponentusesinAngular2,wecreateanamespacespecifictotheindividualcomponents’subtrees(thatis,thedirectiveswillbevisibletothegivenrootcomponentandallofitssuccessorcomponents).Preventingnamecollisionsisnottheonlybenefitweget;italsohelpsuswithbettersemanticsofthecodethatweproduce,sincewe’realwaysawareofthedirectivesaccessiblebythegivencomponent.Wecanfindalltheaccessibledirectivesofthegivencomponentbyfollowingthepathfromthecomponenttothetopofthecomponenttreeandtakingtheunionofallthevaluesofdirectivesarrayssetinthe@Componentdecorators.Giventhatcomponentsareextendedfromdirectives,weneedtoexplicitlydeclarealltheusedcomponentsaswell.

SinceAngular2definesasetofbuilt-indirectives,thebootstrapmethodpassestheminasimilarwayinordertomakethemavailableintheentireapplicationinordertopreventusfromcodeduplications.ThislistofpredefineddirectivesincludesNgClass,NgFor,NgIf,NgStyle,NgSwitch,NgSwitchWhen,andNgSwitchDefault.Theirnamesarequiteself-explanatory;we’lltakealookathowwecanusesomeofthemlaterinthischapter.

UsingAngular2’sbuilt-indirectivesNow,let’sbuildasimpleto-doapplicationinordertodemonstratethesyntaxtodefinecomponentsfurther!

Ourto-doitemswillhavethefollowingformat:

interfaceTodo{

completed:boolean;

label:string;

}

Let’sstartbyimportingeverythingwearegoingtoneed:

import{Component,ViewEncapsulation}from'angular2/core';

import{bootstrap}from'angular2/platform/browser';

Now,let’sdeclarethecomponentandthemetadataassociatedwithit:

@Component({

selector:'todo-app',

templateUrl:'./app.html',

styles:[

`ulli{

list-style:none;

}

.completed{

text-decoration:line-through;

}`

],

encapsulation:ViewEncapsulation.Emulated

})

Here,wespecifythattheselectoroftheTodocomponentwillbethetodo-appelement.Later,weaddthetemplateURL,whichpointstotheapp.htmlfile.Afterthat,weusethestylesproperty;thisisthefirsttimeweencounterit.Aswecanguessfromitsname,itisusedtosetthestylesofthecomponent.

Introducingthecomponent’sviewencapsulationAsweknow,Angular2isinspiredfromWebComponents,whosecorefeatureistheshadowDOM.TheshadowDOMallowsustoencapsulatethestylesofourWebComponentswithoutallowingthemtoleakoutsidethecomponent’sscope.Angular2providesthisfeature.IfwewantAngular’srenderertousetheshadowDOM,wecanuseViewEncapsulation.Native.However,theshadowDOMisnotsupportedbyallbrowsers;ifwewanttohavethesamelevelofencapsulationwithoutusingtheshadowDOM,wecanuseViewEncapsulation.Emulated.Ifwedon’twanttohaveanyencapsulationatall,wecanuseViewEncapsulation.None.Bydefault,therendererusesencapsulationofthetypeEmulated.

Implementingthecomponent’scontrollersNow,let’scontinuewiththeimplementationoftheapplication:

//ch4/ts/todo-app/app.ts

classTodoCtrl{

todos:Todo[]=[{

label:'Buymilk',

completed:false

},{

label:'Savetheworld',

completed:false

}];

name:string='John';

addTodo(label){…}

removeTodo(idx){…}

toggleCompletion(idx){…}

}

HereispartoftheimplementationofthecontrollerassociatedwiththetemplateoftheTodoapplication.

Insidetheclassdeclaration,weinitializedthetodospropertytoanarraywithtwotodoitems:

{

label:'Buymilk',

completed:false

},{

label:'Savetheworld',

completed:false

}

Now,let’supdatethetemplateandrendertheseitems!Here’showthisisdone:

<ul>

<li*ngFor="#todooftodos;varindex=index"

[class.completed]="todo.completed">

<inputtype="checkbox"[checked]="todo.completed"

(change)="toggleCompletion(index)">

{{todo.label}}

</li>

</ul>

Intheprecedingtemplate,weloopedallthetodoitemsinsidethetodospropertyofthecontroller.Foreachtodoitem,wecreatedacheckboxthatcantoggletheitem’scompletionstatus;wealsorenderedthetodoitem’slabelwiththeinterpolationdirective.Here,wecannoticethesyntaxthatwasexplainedearlier:

Webindtothechangeeventofthecheckboxusing(change)="statement".Webindtothepropertyofthetodoitemusing[checked]="expr".

Inordertohavealineacrossthecompletedtodoitems,webindtotheclass.completedpropertyoftheelement.Sincewewanttoapplythecompletedclasstoallthecompleted

to-doitems,weuse[class.completed]="todo.completed".Thisway,wedeclarethatwewanttoapplythecompletedclassdependingonthevalueofthetodo.completedexpression.Hereishowourapplicationlooksnow:

NoteSimilartotheclassbindingsyntax,Angularallowsustobindtotheelement’sstylesandattributes.Forinstance,wecanbindtothetdelement’scolspanattributeusingthefollowinglineofcode:

<td[attr.colspan]="colspanCount"></td>

Inthesameway,wecanbindtoanystylepropertyusingthislineofcode:

<div[style.backgroundImage]="expression"></td>

HandlinguseractionsSofar,sogood!Now,let’simplementthetoggleCompletionmethod.Thismethodacceptstheindexoftheto-doitemasasingleargument:

toggleCompletion(idx){

lettodo=this.todos[idx];

todo.completed=!todo.completed;

}

IntoggleCompletion,wesimplytogglethecompletedBooleanvalueassociatedwiththecurrentto-doitem,whichisspecifiedbytheindexpassedasanargumenttothemethod.

Now,let’saddatextinputtoaddthenewto-doitems:

<p>

Addanewtodo:

<input#newtodotype="text">

<button(click)="addTodo(newtodo.value);newtodo.value=''">

Add

</button>

</p>

Theinputheredefinesanewidentifiercallednewtodo.Wecanreferencetheinputusingthenewtodoidentifierinsidethetemplate.Oncetheuserclicksonthebutton,theaddTodomethoddefinedinthecontrollerwillbeinvokedwiththevalueofthenewtodoinputasanargument.Insidethestatementthatispassedtothe(click)attribute,wealsoresetthevalueofthenewtodoinputbysettingittotheemptystring.

NoteNotethatdirectlymanipulatingDOMelementsisnotconsideredasbestpracticesinceitwillpreventourcomponentfromrunningproperlyoutsidethebrowserenvironment.WewillexplainhowwecanmigratethisapplicationtoWebWorkersinChapter8,DevelopmentExperienceandServer-SideRendering.

Now,let’sdefinetheaddTodomethod:

addTodo(label){

this.todos.push({

label,

completed:false

});

}

Insideit,wecreateanewto-doitemusingtheobjectliteralsyntax.

Theonlythingleftoutofourapplicationistoimplementremovalofexistingto-doitems.Sinceitisquitesimilartothefunctionalityusedtotogglethecompletionoftheto-doitems,I’llleaveitsimplementationasasimpleexerciseforthereader.

Usingadirectives’inputsandoutputsByrefactoringourtodoapplication,wearegoingtodemonstratehowwecantakeadvantageofthedirectives’inputsandoutputs:

Wecanthinkoftheinputsasproperties(orevenarguments)thatthegivendirectiveaccepts.Theoutputscouldbeconsideredaseventsthatittriggers.Whenweuseadirectiveprovidedbyathird-partylibrary,mostlywecareaboutisitsinputsandoutputsbecausetheydefineitsAPI.

Inputsreferstovaluesthatparameterizethedirective’sbehaviorand/orview.Ontheotherhand,outputsreferstoeventsthatthedirectivefireswhensomethingspecialhappens.

Findingoutdirectives’inputsandoutputsNow,let’sdivideourmonolithicto-doapplicationintoseparatecomponentsthatcommunicatewitheachother.Inthefollowingscreenshot,youcanseetheindividualcomponentsthatwhencomposedtogetherimplementthefunctionalityoftheapplication:

TheouterrectanglerepresentstheentireTodoapplication.Thefirstnestedrectanglecontainsthecomponentthatisresponsibleforenteringlabelsofthenewto-doitems,andtheonebelowitliststheindividualitemsthatarestoredintherootcomponent.

Havingsaidthis,wecandefinethesethreecomponentsasfollows:

TodoApp:Responsibleformaintainingthelistofto-doitems(addingnewitemsandtogglingthecompletionstatus).InputBox:Responsibleforenteringthelabelofthenewto-doitem.Ithasthefollowinginputsandoutputs:

Input:Aplaceholderforthetextboxandalabelforthesubmitbutton.Output:Itshouldemitthecontentoftheinputoncethesubmitbuttonisclicked.

TodoList:Thisisresponsibleforrenderingtheindividualto-doitems.Ithasthefollowinginputsandoutputs:

Input:Thelistofto-doitems.Output:Oncethecompletionstatusofanyoftheto-doitemschanges,thecomponentshouldemitthechange.

Now,let’sbeginwiththeimplementation!

Definingthecomponent’sinputsandoutputsLet’suseabottom-upapproachandstartwiththeInputBoxcomponent.Beforethat,weneedacoupleofimportsfromAngular’sangular2/corepackage:

import{

Component,

Input,

Output,

EventEmitter

}from'angular2/core';

Intheprecedingcode,weimportedthe@Component,@Input,and@OutputdecoratorsandtheEventEmitterclass.Astheirnamesstate,@Inputand@Outputareusedfordeclaringthedirective’sinputsandoutputs.EventEmitterisagenericclass(thatis,acceptingtypeparameter)whichcombinedwiththe@Outputdecoratorhelpsusemitoutputs.

Asthenextstep,let’stakealookattheInputBoxcomponent’sdeclaration:

//ch4/ts/inputs-outputs/app.ts

@Component({

selector:'text-input',

template:`

<input#todoInput[placeholder]="inputPlaceholder">

<button(click)="emitText(todoInput.value);

todoInput.value='';">

{{buttonLabel}}

</button>

`

})

classInputBox{...}

Notethatinthetemplate,wedeclareatextinputcalledtodoInputandsetitsplaceholderpropertytothevaluethatwegotfromtheevaluationoftheinputPlaceholderexpression.ThevalueoftheexpressionisthevalueoftheinputPlaceholderpropertydefinedinthecomponent’scontroller.Thisisthefirstinputthatweneedtodefine:

classInputBox{

@Input()inputPlaceholder:string;

...

}

Similarly,wedeclaretheotherinputofthebuttonLabelcomponent,whichweuseasavalueofthelabelofthebutton:

classInputBox{

@Input()inputPlaceholder:string;

@Input()buttonLabel:string;

...

}

Intheprecedingtemplate,webindtheclickeventofthebuttontothisexpression:emitText(todoInput.value);todoInput.value='';.TheemitTextmethodis

supposedtobedefinedinthecomponent’scontroller;onceitisinvoked,itshouldemitthevalueofthetextinput.Hereishowwecanimplementthisbehavior:

classInputBox{

...

@Output()inputText=newEventEmitter<string>();

emitText(text:string){

this.inputText.emit(text);

}

}

Initially,wedeclareanoutputcalledinputText.Asitsvalue,wesetanewinstanceofthetypeEventEmitter<string>thatwecreate.

NoteNotethatalltheoutputsofallthecomponentsneedtobeinstancesofEventEmitter.

InsidetheemitTextmethod,weinvoketheemitmethodoftheinputTextinstancewiththeargumentofthevalueofthetextinput.

Now,let’sdefinetheTodoListcomponentinthesamefashion:

@Component(...)

classTodoList{

@Input()todos:Todo[];

@Output()toggle=newEventEmitter<Todo>();

toggleCompletion(index:number){

lettodo=this.todos[index];

this.toggle.emit(todo);

}

}

Sincethevalueoftheobjectliteralpassedtothe@Componentdecoratorisnotessentialforthepurposeofthissection,we’veomittedit.Thecompleteimplementationofthisexamplecouldbefoundatch4/ts/inputs-outputs/app.ts.Let’stakealookatthebodyoftheTodoListclass.Similarly,fortheInputBoxcomponent,wedefinethetodosinput.Wealsodefinethetoggleoutputbydeclaringthetoggleproperty,settingitsvaluetoanewinstanceofthetypeEventEmitter<Todo>anddecoratingitwiththe@Outputdecorator.

PassinginputsandconsumingtheoutputsNow,let’scombinethecomponentswedefinedintheprecedingsectionandimplementourcompleteapplication!

ThelastcomponentweneedtotakealookatisTodoApp:

@Component({

selector:'todo-app',

directives:[TodoList,InputBox],

template:`

<h1>Hello{{name}}!</h1>

<p>

Addanewtodo:

<input-boxinputPlaceholder="Newtodo…"

buttonLabel="Add"

(inputText)="addTodo($event)">

</input-box>

</p>

<p>Here'sthelistofpendingtodoitems:</p>

<todo-list[todos]="todos"(toggle)="toggleCompletion($event)"></todo-

list>

`

})

classTodoApp{...}

Initially,wedefinetheTodoAppclassanddecorateitwiththe@Componentdecorator.Notethatinthelistofthedirectivesusedbythecomponent,weincludeInputBoxandTodoList.Themagicofhowthesecomponentscollaboratetogetherhappensinthetemplate:

<input-boxinputPlaceholder="Newtodo…"

buttonLabel="Add"

(inputText)="addTodo($event)">

</input-box>

First,weusetheInputBoxcomponentandpassvaluestotheinputs:inputPlaceholderandbuttonLabel.Notethatjustlikewesawearlier,ifwewanttopassanexpressionasavaluetoanyoftheseinputs,weneedtosurroundthemwithbrackets(thatis,[inputPlaceholder]="expression").Inthiscase,theexpressionwillbeevaluatedinthecontextofthecomponentthatownsthetemplate,anditwillbepassedasaninputtothecomponentthatownsthegivenproperty.

RightafterwepassthevalueforthebuttonLabelinput,weconsumetheinputTextoutputbysettingthevalueofthe(inputText)attributetotheaddTodo($event)expression.Thevalueof$eventwillequalthevaluewepassedtotheemitmethodoftheinputTextobjectinsidetheemitTextmethodofInputBox(incasewebindtoanativeevent,thevalueoftheeventobjectwillbethenativeeventobjectitself).

Inthesameway,wepasstheinputoftheTodoListcomponentandhandleitstoggleoutput.Now,let’sdefinethelogicbehindtheTodoAppcomponent:

classTodoApp{

todos:Todo[]=[];

name:string='John';

addTodo(label:string){

this.todos.push({

label,

completed:false

});

}

toggleCompletion(todo:Todo){

todo.completed=!todo.completed;

}

}

IntheaddTodomethod,wesimplypushanewto-doitemtothetodosarray.TheimplementationoftoggleCompletionisevensimpler—wetogglethevalueofthecompletedflagthatispassedasanargumenttotheto-doitem.Now,wearefamiliarwiththebasicsofthecomponents’inputsandoutputs!

EventbubblingInAngular,wehavethesamebubblingbehaviorwehaveintheDOM.Forinstance,ifwehavethefollowingtemplate:

<input-boxinputPlaceholder="Newtodo…"

buttonLabel="Add"

(click)="handleClick($event)"

(inputText)="addTodo($event)">

</input-box>

Thedeclarationofinput-boxlookslikethis:

<input#todoInput[placeholder]="inputPlaceholder">

<button(click)="emitText(todoInput.value);

todoInput.value='';">

{{buttonLabel}}

</button>

Oncetheuserclicksonthebuttondefinedwithinthetemplateoftheinput-boxcomponent,thehandleClick($event)expressionwillbeevaluated.

Further,thetargetpropertyofthefirstargumentofhandleClickwillbethebuttonitself,butthecurrentTargetpropertywillbetheinput-boxelement.

NoteNotethatunlikenativeevents,onestriggeredbyEventEmitterwillnotbubble.

RenamingtheinputsandoutputsofadirectiveNow,wewillexplorehowwecanrenamethedirectives’inputsandoutputs!Let’ssupposethatwehavethefollowingdefinitionoftheTodoListcomponent:

classTodoList{

...

@Output()toggle=newEventEmitter<Todo>();

toggle(index:number){

...

}

}

Theoutputofthecomponentiscalledtoggle;themethodthathandleschangesinthecheckboxesresponsiblefortogglingcompletionoftheindividualto-doitemsiscalledtoggleaswell.ThiscodewillnotbecompiledasintheTodoListcontroller,wehavetwoidentifiersnamedinthesameway.Wehavetwooptionshere:wecaneitherrenamethemethodortheproperty.Ifwerenametheproperty,thiswillchangethenameofthecomponent’soutputaswell.So,thefollowinglineofcodewillnolongerwork:

<todo-list[toggle]="foobar($event)"...></todo-list>

Whatwecandoinsteadisrenamethetogglepropertyandexplicitlysetthenameoftheoutputusingthe@Outputdecorator:

classTodoList{

...

@Output('toggle')toggleEvent=newEventEmitter<Todo>();

toggle(index:number){

...

}

}

Thisway,wewillbeabletotriggerthetoggleoutputusingthetoggleEventproperty.

NoteNotethatsuchrenamescouldbeconfusingandarenotconsideredasbestpractices.Foracompletesetofbestpracticesvisithttps://github.com/mgechev/angular2-style-guide.

Similarly,wecanrenamecomponent’sinputsusingthefollowingcodesnippet:

classTodoList{

@Input('todos')todoList:Todo[];

@Output('toggle')toggleEvent=newEventEmitter<Todo>();

toggle(index:number){

...

}

}

Now,nomatterthatwerenamedtheinputandoutputpropertiesofTodoList,itstillhasthesamepublicinterface:

<todo-list[todos]="todos"

(toggle)="toggleCompletion($event)">

</todo-list>

AnalternativesyntaxtodefineinputsandoutputsThe@Inputand@Outputdecoratorsaresyntaxsugarforeasierdeclarationofthedirective’sinputsandoutputs.Theoriginalsyntaxforthispurposeisasfollows:

@Directive({

outputs:['outputName:outputAlias'],

inputs:['inputName:inputAlias']

})

classDir{

outputName=newEventEmitter();

}

Using@Inputand@Output,theprecedingsyntaxisequivalenttothis:

@Directive(...)

classDir{

@Output('outputAlias')outputName=newEventEmitter();

@Input('inputAlias')inputName;

}

Althoughbothhavethesamesemantics,accordingtothebestpractices,weshouldusethelatteronebecauseitiseasiertoreadandunderstand.

ExplainingAngular2’scontentprojectionContentprojectionisanimportantconceptwhendevelopinguserinterfaces.Itallowsustoprojectpiecesofcontentintodifferentplacesoftheuserinterfaceofourapplication.WebComponentssolvethisproblemwiththecontentelement.InAngularJS1.x,itisimplementedwiththeinfamoustransclusion.

Angular2isinspiredbymodernwebstandards,especiallyWebComponents,whichledtotheadoptionofsomeofthemethodsofcontentprojectionusedthere.Inthissection,we’lllookattheminthecontextofAngular2usingtheng-contentdirective.

BasiccontentprojectioninAngular2Let’ssupposewe’rebuildingacomponentcalledfancy-button.ThiscomponentwillusethestandardHTMLbuttonelementandaddsomeextrabehaviortoit.Hereisthedefinitionofthefancy-buttoncomponent:

@Component({

selector:'fancy-button',

template:'<button>Clickme</button>'

})

classFancyButton{…}

Insideofthe@Componentdecorator,wesettheinlinetemplateofthecomponenttogetherwithitsselector.Now,wecanusethecomponentwiththefollowingmarkup:

<fancy-button></fancy-button>

Onthescreen,wearegoingtoseeastandardHTMLbuttonthathasalabelwiththecontentClickme.ThisisnotaveryflexiblewaytodefinereusableUIcomponents.Mostlikely,theusersofthefancybuttonwillneedtochangethecontentofthelabeltosomething,dependingontheirapplication.

InAngularJS1.x,wewereabletoachievethisresultwithng-transclude:

//AngularJS1.xexample

app.directive('fancyButton',function(){

return{

restrict:'E',

transclude:true,

template:'<button><ng-transclude></ng-transclude></button>'

};

});

InAngular2,wehavetheng-contentelement:

//ch4/ts/ng-content/app.ts

@Component({

selector:'fancy-button',

template:'<button><ng-content></ng-content></button>'

})

classFancyButton{/*Extrabehavior*/}

Now,wecanpasscustomcontenttothefancybuttonbyexecutingthis:

<fancy-button>Click<i>me</i>now!</fancy-button>

Asaresult,thecontentbetweentheopeningandtheclosingfancy-buttontagswillbeplacedwheretheng-contentdirectiveresides.

ProjectingmultiplecontentchunksAnothertypicalusecaseofcontentprojectioniswhenwepasscontenttoacustomAngular2componentorAngularJS1.xdirectiveandwewantdifferentpartsofthiscontenttobeprojectedtodifferentlocationsinthetemplate.

Forinstance,let’ssupposewehaveapanelcomponentthathasatitleandabody:

<panel>

<panel-title>Sampletitle</panel-title>

<panel-content>Content</panel-content>

</panel>

Andwehavethefollowingtemplateofourcomponent:

<divclass="panel">

<divclass="panel-title">

<!--Projectthecontentofpanel-titlehere-->

</div>

<divclass="panel-content">

<!--Projectthecontentofpanel-contenthere-->

</div>

</div>`

InAngularJS1.5,weareabletodothisusingmulti-slottransclusion,whichwasimplementedinordertoallowustohaveasmoothertransitiontoAngular2.Let’stakealookathowwecanproceedinAngular2inordertodefinesuchapanelcomponent:

//ch4/ts/ng-content/app.ts

@Component({

selector:'panel',

styles:[…],

template:`

<divclass="panel">

<divclass="panel-title">

<ng-contentselect="panel-title"></ng-content>

</div>

<divclass="panel-content">

<ng-contentselect="panel-content"></ng-content>

</div>

</div>`

})

classPanel{}

Wehavealreadydescribedtheselectorandstylesproperties,solet’stakealookatthecomponent’stemplate.Wehaveadivelementwiththepanelclass,whichwrapsthetwonesteddivelements,respectively:oneforthetitleofpanelandoneforthecontentofpanel.Inordertograbthecontentfromthepanel-titleelementandprojectitwherethetitleofthepanelissupposedtobeintherenderedpanel,weneedtousetheng-contentelementwiththeselectorattribute,whichhasthepanel-titlevalue.ThevalueoftheselectorattributeisaCSSselector,whichinthiscaseisgoingtomatchallthepanel-titleelementsthatresideinsidethetargetpanelelement.Afterthis,ng-contentwillgrabtheircontentandsetthemasitsowncontent.

NestingcomponentsWe’vealreadybuiltafewsimpleapplicationsasacompositionofcomponentsanddirectives.Wesawthatcomponentsarebasicallydirectiveswithviews,sowecanimplementthembynesting/composingotherdirectivesandcomponents.Thefollowingfigureillustratesthiswithastructuraldiagram:

Thecompositioncouldbeachievedbynestingdirectivesandcomponentswithinthecomponents’templates,takingadvantageofthenestednatureoftheusedmarkup.Forinstance,let’ssaywehaveacomponentwiththesample-componentselector,whichhasthefollowingdefinition:

@Component({

selector:'sample-component',

template:'<view-child></view-child>'

})

classSample{}

Thetemplateofthesample-componentselectorhasasinglechildelementwiththetagnameview-child.

Ontheotherhand,wecanusethesample-componentselectorinsidethetemplateofanothercomponent,andsinceitcanbeusedasanelement,wecannestothercomponentsordirectivesinsideit:

<sample-component>

<content-child1></content-child1>

<content-child2></content-child2>

</sample-component>

Thisway,thesample-componentcomponenthastwodifferenttypesofsuccessors:

Thesuccessordefinedwithinitstemplate.Thesuccessorthatispassedasnestedelementsbetweenitsopeningandclosingtags.

InthecontextofAngular2,thedirectchildrenelementsdefinedwithinthecomponent’stemplatearecalledviewchildrenandtheonesnestedbetweenitsopeningandclosingtagsarecalledcontentchildren.

UsingViewChildrenandContentChildrenLet’stakealookattheimplementationoftheTabscomponent,whichusesthefollowingstructure:

<tabs(changed)="tabChanged($event)">

<tab-title>Tab1</tab-title>

<tab-content>Content1</tab-content>

<tab-title>Tab2</tab-title>

<tab-content>Content2</tab-content>

</tabs>

Theprecedingstructureiscomposedofthreecomponents:

TheTabcomponent.TheTabTitlecomponent.TheTabContentcomponent.

Let’slookattheimplementationoftheTabTitlecomponent:

@Component({

selector:'tab-title',

styles:[…],

template:`

<divclass="tab-title"(click)="handleClick()">

<ng-content></ng-content>

</div>

`

})

classTabTitle{

tabSelected:EventEmitter<TabTitle>=

newEventEmitter<TabTitle>();

handleClick(){

this.tabSelected.emit(this);

}

}

There’snothingnewinthisimplementation.WedefineaTabTitlecomponent,whichhasasinglepropertycalledtabSelected.ItisofthetypeEventEmitterandwillbetriggeredoncetheuserclicksonthetabtitle.

Now,let’stakealookattheTabContentcomponent:

@Component({

selector:'tab-content',

styles:[…],

template:`

<divclass="tab-content"[hidden]="!isActive">

<ng-content></ng-content>

</div>

`

})

classTabContent{

isActive:boolean=false;

}

Thishasanevensimplerimplementation—allwedoisprojecttheDOMpassedtothetab-contentelementinsideng-contentandhideitoncethevalueoftheisActivepropertybecomesfalse.

TheinterestingpartoftheimplementationistheTabscomponentitself:

//ch4/ts/basic-tab-content-children/app.ts

@Component({

selector:'tabs',

styles:[…],

template:`

<divclass="tab">

<divclass="tab-nav">

<ng-contentselect="tab-title"></ng-content>

</div>

<ng-contentselect="tab-content"></ng-content>

</div>

`

})

classTabs{

@Output('changed')

tabChanged:EventEmitter<number>=newEventEmitter<number>();

@ContentChildren(TabTitle)

tabTitles:QueryList<TabTitle>;

@ContentChildren(TabContent)

tabContents:QueryList<TabContent>;

active:number;

select(index:number){…}

ngAfterViewInit(){…}

}

Inthisimplementation,wehaveadecoratorthatwehaven’tusedyet—the@ContentChildrendecorator.The@ContentChildrenpropertydecoratorfetchesthecontentchildrenofthegivencomponent.ThismeansthatwecangetreferencestoallTabTitleandTabContentinstancesfromwithintheinstanceoftheTabscomponentandgetthemintheorderinwhichtheyaredeclaredinthemarkup.There’sanalternativedecoratorcalled@ViewChildren,whichfetchesalltheviewchildrenofthegivenelement.Let’stakealookatthedifferencebetweenthembeforeweexplaintheimplementationfurther.

ViewChildversusContentChildAlthoughbothconceptssoundsimilar,theyhavequitedifferentsemantics.Inordertounderstandthembetter,let’stakealookatthefollowingexample:

//ch4/ts/view-child-content-child/app.ts

@Component({

selector:'user-badge',

template:'…'

})

classUserBadge{}

@Component({

selector:'user-rating',

template:'…'

})

classUserRating{}

Here,we’vedefinedtwocomponents:UserBadgeandUserRating.Let’sdefineaparentcomponent,whichcomprisesboththecomponents:

@Component({

selector:'user-panel',

template:'<user-badge></user-badge>',

directives:[UserBadge]

})

classUserPanel{…}

NotethatthetemplateoftheviewofUserPanelcontainsonlytheUserBadgecomponent’sselector.Now,let’susetheUserPanelcomponentinourapplication:

@Component({

selector:'app',

template:`<user-panel>

<user-rating></user-rating>

</user-panel>`,

directives:[CORE_DIRECTIVES,UserPanel,UserRating]

})

classApp{

constructor(){}

}

ThetemplateofourmainAppcomponentusestheUserPanelcomponentandneststheUserRatingcomponentinsideit.Now,let’ssupposewewanttogetareferencetotheinstanceoftheUserRatingcomponentthatisusedinsidetheuser-panelelementintheAppcomponentandareferencetotheUserBadgecomponent,whichisusedinsidetheUserPaneltemplate.Inordertodothis,wecanaddtwomorepropertiestotheUserPanelcontrollerandaddthe@ContentChildand@ViewChilddecoratorstothemwiththeappropriatearguments:

classUserPanel{

@ViewChild(UserBadge)

badge:UserBadge;

@ContentChild(UserRating)

rating:UserRating;

constructor(){

//

}

}

Thesemanticsofthebadgepropertydeclarationisthis:“gettheinstanceofthefirstchildcomponentofthetypeUserBadge,whichisusedinsidetheUserPaneltemplate”.Accordingly,thesemanticsoftheratingproperty’sdeclarationisthis:“gettheinstanceofthefirstchildcomponentofthetypeUserRating,whichisnestedinsidetheUserPanelhostelement”.

Now,ifyourunthiscode,you’llnotethatthevaluesofthebadgeandratingpropertiesarestillequaltotheundefinedvalueinsidethecontroller’sconstructor.Thisisbecausetheyarestillnotinitializedinthisphaseofthecomponent’slifecycle.ThelifecyclehooksthatwecanuseinordertogetareferencetothesechildcomponentsarengAfterViewInitandngAfterContentInit.WecanusethesehookssimplybyaddingdefinitionsofthengAfterViewInitandngAfterContentInitmethodstothecomponent’scontroller.WewillmakeacompleteoverviewofthelifecyclehooksthatAngular2providesshortly.

Torecap,wecansaythatthecontentchildrenofthegivencomponentsarethechildelementsthatarenestedwithinthecomponent’shostelement.Incontrast,theviewchildrendirectivesofthegivencomponentaretheelementsusedwithinitstemplate.

NoteInordertogetplatformindependentreferencetoaDOMelement,again,wecanuse@ContentChildrenand@ViewChildren.Forinstance,ifwehavethefollowingtemplate:<input#todo>wecangetareferencetotheinputbyusing:@ViewChild('todo').

Sincewearealreadyfamiliarwiththecoredifferencesbetweenviewchildrenandcontentchildrennow,wecancontinuewithourtabsimplementation.

Inthetabscomponent,insteadofusingthe@ContentChilddecorator,weuse@ContentChildren.Wedothisbecausewehavemultiplecontentchildrenandwewanttogetthemall:

@ContentChildren(TabTitle)

tabTitles:QueryList<TabTitle>;

@ContentChildren(TabContent)

tabContents:QueryList<TabContent>;

AnothermaindifferencewecannoticeisthatthetypesofthetabTitlesandtabContentspropertiesareQueryListwiththerespectivetypeparameterandnotthecomponent’stypeitself.WecanthinkoftheQueryListdatastructureasaJavaScriptarray—wecanapplythesamehigh-orderfunctions(map,filter,reduce,andsoon)overitandloopoveritselements;however,QueryListisalsoobservable,thatis,wecanobserveitforchanges.

AsthefinalstepofourTabsdefinition,let’stakeapeekattheimplementationofthengAfterContentInitandselectmethods:

ngAfterContentInit(){

this.tabTitles

.map(t=>t.tabSelected)

.forEach((t,i)=>{

t.subscribe(_=>{

this.select(i)

});

});

this.active=0;

this.select(0);

}

Inthefirstlineofthemethod’simplementation,weloopalltabTitlesandtaketheobservable’sreferences.Theseobjectshaveamethodcalledsubscribe,whichacceptsacallbackasanargument.Oncethe.emit()methodoftheEventEmitterinstance(thatis,thetabSelectedpropertyofanytab)iscalled,thecallbackpassedtothesubscribemethodwillbeinvoked.

Now,let’stakealookattheselectmethod’simplementation:

select(index:number){

letcontents:TabContent[]=this.tabContents.toArray();

contents[this.active].isActive=false;

this.active=index;

contents[this.active].isActive=true;

this.tabChanged.emit(index);

}

Inthefirstline,wegetanarrayrepresentationoftabContents,whichisofthetypeQueryList<TabContent>.Afterthat,wesettheisActiveflagofthecurrentactivetabtofalseandselectthenextactiveone.Inthelastlineintheselectmethod’simplementation,wetriggertheselectedeventoftheTabscomponentbyinvokingthis.tabChanged.emitwiththeindexofthecurrentlyselectedtab.

Hookingintothecomponent’slifecycleComponentsinAngular2haveawell-definedlifecycle,whichallowsustohookintodifferentphasesofitandhavefurthercontroloverourapplication.Wecandothisbyimplementingspecificmethodsinthecomponent’scontroller.Inordertobemoreexplicit,thankstotheexpressivenessofTypeScript,wecanimplementdifferentinterfacesassociatedwiththelifecycle’sphases.Eachoftheseinterfaceshasasinglemethod,whichisassociatedwiththephaseitself.

Althoughcodewrittenwithexplicitinterfaceimplementationwillhavebettersemantics,sinceAngular2supportsES5aswellwithinthecomponent,wecansimplydefinemethodswiththesamenamesasthelifecyclehooks(butthistime,prefixedwithng)andtakeadvantageofducktyping.

Thefollowingdiagramshowsallthephaseswecanhookinto:

Let’stakealookatthedifferentlifecyclehooks:

OnChanges:Thishookwillbeinvokedonceachangeintheinputpropertiesofagivencomponenthasbeendetected.Forinstance,let’stakealookatthefollowingcomponent:

@Component({

selector:'panel',

inputs:['title']

})

classPanel{…}

Wecanuseitlikethis:

<panel[title]="expression"></panel>

Oncethevalueoftheexpressionassociatedwiththe[title]attributehasbeenchanged,thengOnChangeshookwillbeinvoked.Wecanimplementitusingthiscodesnippet:

@Component(…)

classPanel{

ngOnChanges(changes){

Object.keys(changes).forEach(prop=>{

console.log(prop,'changed.Previousvalue',

changes[prop].previousValue);

});

}

}

Theprecedingsnippetwilldisplayallthechangedbindingsandtheiroldvalues.Inordertobemoreexplicitintheimplementationofthehook,wecanuseinterfaces:

import{Component,OnChanges}from'angular2/core';

@Component(…)

classPanelimplementsOnChanges{

ngOnChanges(changes){…}

}

Alltheinterfacesrepresentingtheindividuallifecyclehooksdefineasinglemethodwiththenameoftheinterfaceitselfprefixedwithng.Intheupcominglist,we’llusethetermlifecyclehook,bothforinterfaceand/orthemethod,exceptifwewon’timplyanythingspecificallyforonlyoneofthem.

OnInit:Thishookwillbeinvokedoncethegivencomponenthasbeeninitialized.WecanimplementitusingtheOnInitinterfacewithitsngOnInitmethod.DoCheck:Thiswillbeinvokedwhenthechangedetectorofthegivencomponentisinvoked.Itallowsustoimplementourownchangedetectionalgorithmforthegivencomponent.NotethatDoCheckandOnChangesshouldnotbeimplementedtogetheronthesamedirective.OnDestroy:IfweimplementtheOnDestroyinterfacewithitssinglengOnDestroymethod,wecanhookintothedestroylifecyclephaseofacomponent.Thismethodwillbeinvokedoncethecomponentisdetachedfromthecomponenttree.

Now,let’stakealookatthelifecyclehooksassociatedwiththecomponent’scontentandviewchildren:

AfterContentInit:IfweimplementthengAfterContentInitlifecyclehook,wewillbenotifiedwhenthecomponent’scontenthasbeenfullyinitialized.ThisisthephasewhenthepropertiesdecoratedwithContentChildorContentChildrenwillbeinitialized.AfterContentChecked:Byimplementingthishook,we’llgetnotifiedeachtimethecontentofthegivencomponenthasbeencheckedbythechangedetectionmechanismofAngular2.AfterViewInit:IfweimplementthengAfterViewInitlifecyclehook,wewillbenotifiedwhenthecomponent’sviewhasbeenfullyinitialized.ThisisthephasewhenthepropertiesdecoratedwithViewChildorViewChildrenwillbeinitialized.AfterViewChecked:ThisissimilartoAfterContentChecked.TheAfterViewCheckedhookwillbeinvokedoncetheviewofyourcomponenthasbeenchecked.

TheorderofexecutionInordertotracetheorderofexecutionofthecallbacksassociatedwitheachhook,let’stakeapeekatthech4/ts/life-cycle/app.tsexample:

@Component({

selector:'panel',

inputs:['title','caption'],

template:'<ng-content></ng-content>'

})

classPanel{

ngOnChanges(changes){…}

ngOnInit(){…}

ngDoCheck(){…}

ngOnDestroy(){…}

ngAfterContentInit(){…}

ngAfterContentChecked(){…}

ngAfterViewInit(){…}

ngAfterViewChecked(){…}

}

ThePanelcomponentimplementsallthehookswithoutexplicitlyimplementingtheinterfacesassociatedwiththem.

Wecanusethecomponentinthefollowingtemplate:

<button(click)="toggle()">Toggle</button>

<div*ngIf="counter%2==0">

<panelcaption="Samplecaption"title="Sample">Helloworld!</panel>

</div>

Intheprecedingexample,wehaveapanelandabutton.Uponeachclickonthebutton,thepanelwillbeeitherremovedorappendedtotheviewbythengIfdirective.

Duringtheapplicationinitialization,iftheresultofthe"counter%2==0"expressionisevaluatedtotrue,thengOnChangesmethodwillbeinvoked.Thishappensbecausethevaluesofthetitleandcaptionpropertiesaregoingtobesetforthefirsttime.

Rightafterthis,thengOnInitmethodwillbecalled,sincethecomponenthasbeeninitialized.Oncethecomponent’sinitializationiscompleted,thechangedetectionwillbetriggered,whichwillleadtotheinvocationofthengDoCheckmethodthatallowsustohookcustomlogicfordetectingchangesinthestate.

NoteNotethatyouarenotsupposedtoimplementbothngDoCheckandngOnChangesmethodsforthesamecomponent,sincetheyaremutuallyexclusive.Theexampleheredoesthisforlearningpurposesonly.

AfterthengDoCheckmethod,thecomponent’scontentwillbefollowedbyperformingacheckonit(ngAfterContentInitandngAfterContentCheckedwillbeinvokedinthisorder).Rightafterthis,thesamewillhappenforthecomponent’sview(ngAfterViewInitfollowedbyngAfterViewChecked).

OncetheexpressionofthengIfdirectiveisevaluatedtofalse,theentirecomponentwillbedetachedfromtheview,whichwillleadtotheinvocationofthengOnDestroyhook.

Onthenextbuttonclick,ifthevalueoftheexpressionofngIfisequaltotrue,thesamesequenceofcallsofthelifecyclehooksastheoneduringtheinitializationphasewillbeexecuted.

DefininggenericviewswithTemplateRefWearealreadyfamiliarwiththeconceptsofinputs,content,andviewchildren,andwealsoknowwhenwecangetareferencetotheminthecomponent’slifecycle.Now,wewillcombinethemandintroduceanewconcept:TemplateRef.

Let’stakeastepbackandtakealookatthelastto-doapplicationwedevelopedearlierinthischapter.Inthefollowingscreenshot,youcanseewhatitsUIlookslike:

Ifwetakealookatitsimplementationinch4/ts/inputs-outputs/app.ts,we’llseethatthetemplateusedtorendertheindividualto-doitemsisdefinedinsidethetemplateoftheentireto-doapplication.

Whatifwewanttouseadifferentlayouttorendertheto-doitems?WecandothisbycreatinganothercomponentcalledTodo,whichencapsulatestheresponsibilityofrenderingthem.Then,wecandefineseparateTodocomponentsforthedifferentlayoutswewanttosupport.Thisway,weneedtohavendifferentcomponentsforndifferentlayouts,eventhoughweuseonlytheirtemplates.

Angular2comeswithamoreelegantsolution.Earlierinthischapter,wealreadydiscussedthetemplateelement.WesaidthatitallowsustodefineachunkofHTMLthatwillnotbeprocessedbythebrowser.Angular2allowsustoreferencesuchtemplateelementsandusethembypassingthemascontentchildren!

Hereishowwecanpassthecustomlayouttoourrefactoredtodo-appcomponent:

//ch4/ts/template-ref/app.ts

<todo-app>

<templatevar-todo>

<inputtype="checkbox"[checked]="todo.completed"

(change)="todo.completed=!todo.completed;">

<span[class.completed]="todo.completed">

{{todo.label}}

</span><br>

</template>

</todo-app>

Inthetemplate,wedeclareavariablecalledtodo.Laterinthetemplate,wecanuseittospecifythewayinwhichwewanttovisualizethecontent.

Now,let’sseehowwecangetareferencetothistemplateinthecontrolleroftheTodoAppcomponent:

//ch4/ts/template-ref/app.ts

classTodoApp{

@ContentChild(TemplateRef)

privateitemsTemplate:TemplateRef;

//…

}

AllwedohereisdefineapropertycalleditemsTemplateanddecorateitwiththe@ContentChilddecorator.Duringthecomponent’slifecycle(moreaccurately,inngAfterContentInit),thevalueofitemsTemplatewillbesetasareferenceofthetemplatethatwepassedasthecontentofthetodo-appelement.

Thereisonemoreproblemthough—weneedthetemplateintheTodoListcomponent,sincethat’stheplacewherewerendertheindividualto-doitems.WhatwecandoisdefineanotherinputoftheTodoListcomponentandpassthetemplatedirectlyfromTodoApp:

//ch4/ts/template-ref/app.ts

classTodoList{

@Input()todos:Todo[];

@Input()itemsTemplate:TemplateRef;

@Output()toggle=newEventEmitter<Todo>();

}

WeneedtopassitasaninputfromthetemplateofTodoApp:

...

<todo-list[todos]="todos"

[itemsTemplate]="itemsTemplate">

</todo-list>

TheonlythingleftistousethistemplatereferenceinthetemplateoftheTodoListapplication:

<!--…-->

<template*ngFor="vartodooftodos;template:itemsTemplate"></template>

WeexplainedtheextendedsyntaxofthengForOfdirectiveintheprevioussectionsofthischapter.Thissnippetshowsonemorepropertyofthisdirectivethatwecanset:thengForTemplateproperty.Bydefault,thetemplateofthengForOfdirectiveistheelementitisusedon.ByspecifyingatemplatereferencetothengForTemplateproperty,wecanusethepassedTemplateRefinstead.

UnderstandingandenhancingthechangedetectionWealreadybrieflydescribedthechangedetectionmechanismoftheframework.WesaidthatcomparedtoAngularJS1.x,whereitrunsinthecontextofthescope,inAngular2,itrunsinthecontextoftheindividualcomponents.Anotherconceptwementionedisthezones,whichbasicallyinterceptalltheasynchronouscallsthatwecanmakeusingthebrowserAPIsandprovideexecutioncontextforthechangedetectionmechanismoftheframework.ZonesfixtheannoyingproblemwehaveinAngularJS1.x,wherewhenweuseAPIsoutsideofAngular,weneedtoexplicitlyinvokethedigestloop.

InChapters1,GettingStartedwithAngular2andChapter2,TheBuildingBlocksofanAngular2Application,wediscussedthattherearetwomainimplementationsofthechangedetector:DynamicChangeDetectorandJitChangeDetector.ThefirstoneworksgreatforenvironmentswithstrictCSP(Content-Security-Policy)becauseofthedisableddynamicevaluationofJavaScript.Thesecondonetakesgreatbenefitsfromtheinline-cachingmechanismoftheJavaScriptvirtualmachineandthereforebringsgreatperformance!

Inthissection,we’llexploreanotherpropertyofthe@Componentdecorator’sconfigurationobject,whichprovidesusfurthercontroloverthechangedetectionmechanismoftheframeworkbychangingitsstrategy.Byexplicitlysettingthestrategy,weareabletopreventthechangedetectionmechanismfromrunningoveracomponent’ssubtrees,whichinsomecasescanbringgreatperformancebenefits.

TheorderofexecutionofthechangedetectorsNow,let’sbrieflydescribetheorderinwhichthechangedetectorsareinvokedinagivencomponenttree.

Forthispurpose,wewillusethelastimplementationoftheto-doapplicationwehave,butthistime,we’llextractthelogictorendertheindividualto-doitemsintoaseparatecomponentcalledTodoItem.Inthefollowingfigure,wecanseetheapplication’sstructure:

AtthetoplevelistheTodoAppcomponent,whichhastwochildren:InputBoxandTodoList.TheTodoListcomponentrenderstheindividualto-doitemsinTodoItemcomponents.Theimplementationdetailsarenotimportantforourpurpose,sowearegoingtoignorethem.

Now,weneedtorealizethatthereisanimplicitdependencybetweenthestateoftheparentcomponentanditschildren.Forinstance,thestateoftheTodoListcomponentdependscompletelyontheto-doitemsthatarelocatedatitsparent:theTodoAppcomponent.There’sasimilardependencybetweenTodoItemandTodoList,sincetheTodoListcomponentpassestheindividualto-doitemstoaseparateinstanceoftheTodoItemcomponent.

Becauseofourlastobservation,theorderofexecutionofthechangedetectorsattachedtotheindividualcomponentsisliketheoneshownontheprecedingfigure.Oncethechangedetectionmechanismrun,initiallyitwillperformacheckovertheTodoAppcomponent.Rightafterthis,theInputBoxcomponentwillbecheckedforchanges,followedbytheTodoListcomponent.Intheend,AngularwillinvokethechangedetectoroftheTodoItemcomponent.

Youcantracetheorderofexecutioninthech4/ts/change_detection_strategy_order/app.tsexample,whereeachindividualcomponentlogsamessageonceitsngDoCheckmethodisinvoked.

NoteNotethatonlythecomponentshaveaninstanceofachangedetectorattachedtothem;directivesusethechangedetectoroftheirparentcomponent.

ChangedetectionstrategiesThechangedetectionstrategiesthatAngular2providesare:CheckOnce,Checked,CheckAlways,Detached,Default,andOnPush.WewilldescribehowwecantakeadvantageofOnPushindetail,sinceitisverypowerfulwhenworkingwithimmutabledata.BeforetakingadeepdiveintoOnPush,let’sbrieflydescribetheotherstrategies.

Now,let’simporttheTypeScriptenum,whichcanbeusedtoconfigurethestrategyusedfortheindividualcomponents:

//ch4/ts/change_detection_strategy_broken/app.ts

import{ChangeDetectionStrategy}from'angular2/core';

Now,wecanconfiguretheTodoListcomponenttousetheCheckedstrategy:

@Component({

selector:'todo-list',

changeDetection:ChangeDetectionStrategy.Checked,

template:`...`,

styles:[…]

})

classTodoList{…}

Thisway,thechangedetectionwillbeskippeduntilitsmode(strategy)changestoCheckOnce.Butwhatdoesitmeantopreventthechangedetectionfromrunning?Youcangotohttp://localhost:5555/dist/dev/ch4/ts/change_detection_strategy_broken/andseetheinconsistentbehavioroftheTodoListcomponent.Whenyouaddanewto-doitemintheinputandyouclickonthebutton,itwon’timmediatelyappearinthelist.

Now,let’stryCheckOnce!Insidech4/ts/change_detection_strategy_broken/app.ts,changethechangedetectionstrategyoftheTodoListcomponenttoChangeDetectionStrategy.CheckOnce.Afterrefreshingthebrowser,trytoaddanewto-doitem.ThechangeshouldnotbeimmediatelyreflectedbecauseCheckOncewillinstructthechangedetectortoperformthecheckonlyonce(inthiscase,duringinitialization),andafterthat,nothingwillhappen.

Bydefault,itisusedintheCheckAlwaysmode,whichasitsnamestates,doesn’tpreventthechangedetectorfromrunning.

IfwedeclarethestrategyofagivencomponenttoDetached,thechangedetectorsubtreewillnotbeconsideredasapartofthemaintreeandwillbeskipped.

PerformanceboostingwithimmutabledataandOnPushThelastchangedetectionstrategythatwearegoingtodescribeisOnPush.Itisextremelyusefulwhentheresultthatthegivencomponentproducesdependsonlyonitsinputs.Insuchcases,wecanpassimmutabledatatotheinputsinordertomakesurethatitwillnotbemutatedbyanyothercomponent.Thisway,byhavingacomponentthatdependsonlyonitsimmutableinputs,wecanmakesurethatitproducesdifferentuserinterfacesonlyonceitreceivesdifferentinputs(thatis,differentreference).

Inthissection,wearegoingtoapplytheOnPushstrategyontheTodoListcomponent.Sinceitdependsonlyonitsinputs(thetodosinput),wewanttomakesurethatitschangedetectionwillbeperformedonlyonceitreceivesanewreferenceofthetodoscollection.

Theessenceofimmutabledataisthatitcannotchange.Thismeansthatonceweaddanewto-doitemtothetodoscollection,wecannotchangeit;instead,theadd(orinourcase,push)methodwillreturnanewcollection—acopyoftheinitialcollectionwiththenewitemincluded.

Thismayseemlikeahugeoverhead—tocopytheentirecollectiononeachchange.Inbigapplications,thismayhaveabigperformanceimpact.However,wedon’tneedtocopytheentirecollection.Therearelibrariesthatimplementimmutabledatastructureusingsmarteralgorithms:persistentdatastructures.Persistentdatastructuresareoutofthescopeofthecurrentcontent.Furtherinformationaboutthemcanbefoundinmostcomputersciencetextbooksforadvanceddatastructures.Thegoodthingisthatwedon’thavetounderstandtheirimplementationindepthinordertousethem!ThereisalibrarycalledImmutable.jsthatimplementsafewcommonlyusedimmutabledatastructures.Inourcase,wearegoingtousetheimmutablelist.Generally,theimmutablelistbehavesjustlikeanormallist,butoneachoperationthatissupposedtomutateit,itreturnsanewlist.

Thismeansthatifwehavealistcalledfoo,whichisimmutable,andweappendanewitemtothelist,wearegoingtogetanewreference:

letfoo=List.of(1,2,3);

letchanged=foo.push(4);

foo===changed//false

console.log(foo.toJS());//[1,2,3]

console.log(changed.toJS());//[1,2,3,4]

Inordertotakeadvantageofimmutability,weneedtoinstallImmutable.jsusingnpm.

We’vealreadydonethisinch4/ts/change_detection_strategy/app.ts.Immutable.jsisalreadypartofpackage.json,whichislocatedattherootdirectoryoftheproject.

Now,it’stimetorefactorourto-doapplicationandmakeituseimmutabledata!

UsingimmutabledatastructuresinAngularLet’stakealookathowwecurrentlykeeptheto-doitemsintheTodoAppcomponent:

classTodoApp{

todos:Todo[]=[...];

...

}

WeuseanarrayofTodoitems.TheJavaScriptarrayismutable,whichmeansthatifwepassittoacomponentthatusestheOnPushstrategy,itisnotsafetoskipthechangedetectionincasewegetthesameinputreference.Forinstance,wemayhavetwocomponentsthatusethesamelistofto-doitems.Bothcomponentscanmodifythelistsinceitismutable.Thiswillleadtoaninconsistentstatetoanyofthecomponentsincasetheirchangedetectionisnotperformed.That’swhyweneedtomakesurethatthelistthatholdstheitemsisimmutable.AllweneedtodointheTodoAppcomponentinordertomakesurethatitholdsitsdatainanimmutabledatastructureisthis:

//ch4/ts/change_detection_strategy/app.ts

classTodoApp{

todos:ImmutableList<Todo>=ImmutableList.of({

label:'Buymilk',

completed:false

},{

label:'Savetheworld',

completed:false

});

...

}

Inthisway,weconstructthetodospropertyasanimmutablelist.Sincethemutationoperationsoftheimmutablelistreturnanewlist,weneedtomakeaslightmodificationinaddTodoandtoggleTodoCompletion:

...

addTodo(label:string){

this.todos=this.todos.push({

label,

completed:false

});

}

toggleCompletion(index:number){

this.todos=this.todos.update(index,todo=>{

letnewTodo={

label:todo.label,

completed:!todo.completed

};

returnnewTodo;

});

}

TheaddTodofunctionlooksexactlythesameasbeforeexceptthatwesettheresultofthepushmethodasavaluetothetodosproperty.

IntoggleTodoCompletion,weusetheupdatemethodoftheimmutablelist.Asthefirstargument,wepasstheindexoftheto-doitemwewanttomodify,andthesecondargumentisacallbackthatdoestheactualmodification.Notethatsinceweareusingimmutabledatainthiscase,wecopythemodifiedto-doitem.Thisisrequiredbecauseittellstheupdatemethodthattheitemwiththegivenindexhasbeenchanged(sinceitisimmutable,itisconsideredaschangedonlywhenithasanewreference),whichmeansthattheentirelisthasbeenchanged.

Thatwasthecomplexpart!Nowlet’stakealookattheTodoListcomponent’sdefinition:

@Component({

selector:'todo-list',

changeDetection:ChangeDetectionStrategy.OnPush,

template:`...`,

styles:[...]

})

classTodoList{

@Input()todos:ImmutableList<Todo>;

@Output()toggle=newEventEmitter<number>();

toggleCompletion(index:number){

this.toggle.emit(index);

}

}

Insidethe@Componentdecorator,wesetthechangeDetectionpropertytothevalueoftheOnPushstrategy.Thismeansthatthecomponentwillrunitschangedetectoronlywhenanyofitsinputsgetsanewreference.ThetemplateofthecomponentstaysexactlythesamesincengForOfinternallyusesES2015iteratorstolooptheitemsintheprovidedcollection.TheyaresupportedbyImmutable.js,sothechangesinthetemplatearenotrequired.

Sinceweneedtheindexofthechangeditem(theoneweuseintheupdatemethodofthetodoscollectioninTodoApp),wechangethetypeoftheoutputofthecomponenttoEventEmitter<number>.IntoggleCompletion,weemittheindexofthechangedto-doitem.

Thisishowweoptimizedoursimpleto-doapplicationbypreventingthechangedetectionmechanismfromrunningintheentirerightsubtreeincasetheparentcomponenthasn’tpushedaninputwithanewreference.

SummaryInthischapter,wewentthroughthecorebuildingblocksofanAngular2application:directivesandcomponents.Webuiltacoupleofsamplecomponents,whichshowusthesyntaxtobeusedforthedefinitionofthesefundamentalconcepts.Wealsodescribedthelifecycleofeachdirectiveandthecoresetoffeaturesthegivendirectiveandcomponenthave.Asthenextstep,wesawhowwecanenhancetheperformanceofourapplicationbyusingtheOnPushchangedetectionstrategywithimmutabledata.

ThenextchapteriscompletelydedicatedtotheAngular2servicesandthedependencyinjectionmechanismoftheframework.Wearegoingtolookathowwecandefineandinstantiatecustominjectorsandhowwecantakeadvantageofthedependencyinjectionmechanisminourdirectivesandcomponents.

Chapter5.DependencyInjectioninAngular2Inthischapter,we’llexplainhowtotakeadvantageofthedependencyinjection(DI)mechanismoftheframeworkwithallitsvariousfeatures.

Wewillexplorethefollowingtopics:

Configuringandcreatinginjectors.Instantiatingobjectsusinginjectors.Injectingdependenciesintoourdirectivesandcomponents.Thisway,wewillbeabletoreusethebusinesslogicdefinedwithintheservicesandwireitupwiththeUIlogic.AnnotatingtheES5codewewillwriteinordertogettheexactsameresultwegetwhenweareusingtheTypeScriptsyntax.

WhydoIneedDependencyInjection?Let’ssupposethatwehaveaCarclassthatdependsontheEngineandTransmissionclasses.Howcanweimplementthissystem?Let’stakealook:

classEngine{…}

classTransmission{…}

classCar{

engine;

transmission;

constructor(){

this.engine=newEngine();

this.transmission=newTransmission();

}

}

Inthisexample,wecreatedthedependenciesoftheCarclassinsideofitsconstructor.Althoughitlookssimple,itisfarfrombeingflexible.EachtimewecreateaninstanceoftheCarclass,instancesofthesameEngineandTransmissionclasseswillbecreated.Thismaybeproblematicbecauseofthefollowingreasons:

TheCarclassgetslesstestablebecausewecan’ttestitindependentlyfromitsengineandtransmissiondependencies.WecoupletheCarclasswiththelogicusedfortheinstantiationofitsdependencies.

DependencyInjectioninAngular2AnotherwaywecanapproachthisisbytakingadvantageoftheDIpattern.We’realreadyfamiliarwithitfromAngularJS1.x.Let’sdemonstratehowwecanrefactortheprecedingcodeusingDIinthecontextofAngular2:

classEngine{…}

classTransmission{…}

@Injectable()

classCar{

engine;

transmission;

constructor(engine:Engine,transmission:Transmission){

this.engine=engine;

this.transmission=transmission;

}

}

Allwedidintheprecedingsnippetwasaddthe@InjectableclassdecoratorontopofthedefinitionoftheCarclassandprovidetypeannotationsfortheparametersofitsconstructor.

BenefitsofDIinAngular2Thereisonemorestepleft,whichwe’lltakealookatinthenextsection.Butlet’sseewhatthebenefitsofthementionedapproachare:

WecaneasilypassdifferentversionsofthedependenciesoftheCarclassforatestingenvironment.We’renotcoupledwiththelogicaroundthedependencies’instantiation.

TheCarclassisonlyresponsibleforimplementingitsowndomain-specificlogicinsteadofbeingcoupledwithadditionalfunctionalities,suchasthemanagementofitsdependencies.Ourcodealsogotmoredeclarativeandeasiertoread.

Now,afterwe’verealizedsomeofthebenefitsoftheDI,let’stakealookatthemissingpiecesinordertomakethiscodework!

ConfiguringaninjectorTheprimitiveusedfortheinstantiationoftheindividualdependenciesinourAngular2applicationsviatheDImechanismoftheframeworkiscalledtheinjector.Theinjectorcontainsasetofprovidersthatencapsulatethelogicfortheinstantiationofregistereddependenciesassociatedwithtokens.Wecanthinkoftokensasidentifiersofthedifferentprovidersregisteredwithintheinjector.

Let’stakealookatthefollowingsnippet,whichislocatedatch5/ts/injector-basics/injector.ts:

import'reflect-metadata';

import{

Injector,Inject,Injectable,

OpaqueToken,provide

}from'angular2/core';

constBUFFER_SIZE=newOpaqueToken('buffer-size');

classBuffer{

constructor(@Inject(BUFFER_SIZE)privatesize:Number){

console.log(this.size);

}

}

@Injectable()

classSocket{

constructor(privatebuffer:Buffer){}

}

letinjector=Injector.resolveAndCreate([

provide(BUFFER_SIZE,{useValue:42}),

Buffer,

Socket

]);

injector.get(Socket);

Youcanrunthefileusingthefollowingcommand:

cdapp

ts-nodech5/ts/injector-basics/injector.ts

Ifyouhaven’tinstalledts-nodeyet,takealookatChapter3,TypeScriptCrashCourse,whichexplainshowyoucanproceedinordertohaveitupandrunningonyourcomputer.

WeimportInjector,Injectable,Inject,OpaqueToken,andprovide.

Injectorrepresentsthecontainerusedfortheinstantiationofthedifferentdependencies.UsingtherulesdeclaredwiththeprovidefunctionandthemetadatageneratedbytheTypeScriptcompiler,itknowshowtocreatethem.

Intheprecedingsnippet,weinitiallydefinedtheBUFFER_SIZEconstantandsetittothenewOpaqueToken('buffer-size')value.WecanthinkofthevalueofBUFFER_SIZEasa

uniquevaluethatcannotbeduplicatedintheapplication(OpaqueTokenisanalternativeoftheSymbolclassfromES2015,sinceatthetimeofwritingthis,itisnotsupportedbyTypeScript).

Wedefinedtwoclasses:BufferandSocket.TheBufferclasshasaconstructorthatacceptsonlyasingledependencycalledsize,whichisofthetypeNumber.Inordertoaddadditionalmetadatafortheprocessofdependencyresolution,weusethe@Injectparameterdecorator.Thisdecoratoracceptsanidentifier(alsoknownastoken)ofthedependencywewanttoinject.Usually,itisthetypeofthedependency(thatis,areferenceofaclass),butinsomecases,itcanbeadifferenttypeofavalue.Forexample,inourcase,weusedtheinstanceoftheOpaqueTokenclass.

DependencyresolutionwithgeneratedmetadataNowlet’stakealookattheSocketclass.Wedecorateitwiththe@Injectabledecorator.ThisdecoratorissupposedtobeusedbyanyclassthatacceptsdependenciesthatshouldbeinjectedviathedependencyinjectionmechanismofAngular2.

The@InjectabledecoratorforcestheTypeScriptcompilertogenerateadditionalmetadataforthetypesofdependenciesthatagivenclassaccepts.Thismeansthatifweomitthe@Injectabledecorator,Angular’sDImechanismwillnotbeawareofthetokensassociatedwiththedependenciesitneedstoresolve.

TypeScriptdoesn’tgenerateanymetadataifnodecoratorisusedontopofaclassmostlyforperformanceconcerns.Imagineifsuchmetadatawasgeneratedforeachindividualclassthatacceptsdependencies—inthiscase,theoutputwouldbebloatedwithadditionaltypemetadatathatwouldbeunused.

Analternativetousing@Injectableistoexplicitlydeclarethetypesofdependenciesusingthe@Injectdecorator.Takealookatthefollowing:

classSocket{

constructor(@Inject(Buffer)privatebuffer:Buffer){}

}

Thismeansthattheprecedingcodehasequivalentsemanticstothecodethatuses@Injectable,asmentionedearlier.TheonlydifferenceisthatAngular2willgetthetypeofdependency(thatis,thetokenassociatedwithit)explicitly(directlyfromthemetadataaddedbythe@Injectordecorator)comparedtothecasewhere@Injectableisused,whenitwilllookatthemetadatageneratedbythecompiler.

InstantiatinganinjectorNow,let’screateaninstanceofaninjectorinordertouseitfortheinstantiationofregisteredtokens:

letinjector=Injector.resolveAndCreate([

provide(BUFFER_SIZE,{useValue:42}),

Buffer,

Socket

]);

WecreateaninstanceoftheInjectorusingitsstaticmethodcalledresolveAndCreate.ThisisafactorymethodthatacceptsanarrayofprovidersasargumentandreturnsanewInjector.

resolvemeansthattheproviderswillgothrougharesolutionprocess,whichincludessomeinternalprocessing(flatteningmultiplenestedarraysandconvertingindividualprovidersintoanarray).Later,theinjectorcaninstantiateanyofthedependenciesforwhichwehaveregisteredprovidersbasedontherulestheprovidersencapsulate.

Inourcase,weusedtheprovidemethodinordertoexplicitlytelltheAngular2DImechanismtousethevalue42whentheBUFFER_SIZEtokenisrequired.Theothertwoprovidersareimplicit.Angular2willinstantiatethembyinvokingtheprovidedclasswiththenewoperatoroncealloftheirdependenciesareresolved.

WerequesttheBUFFER_SIZEvalueintheconstructoroftheBufferclass:

classBuffer{

constructor(@Inject(BUFFER_SIZE)privatesize:Number){

console.log(this.size);

}

}

Intheprecedingexample,weusedthe@Injectparameterdecorator.IthintstheDImechanismthatthefirstargumentoftheconstructoroftheBufferclassshouldbeinstantiatedwiththeproviderassociatedwiththeBUFFER_SIZEtokenpassedtotheinjector.

IntroducingforwardreferencesAngular2introducedtheconceptofforwardreferences.Itisrequiredduetothefollowingreasons:

ES2015classesarenothoisted.Allowresolutionofthedependenciesthataredeclaredafterthedeclarationofthedependentproviders.

Inthissection,we’regoingtoexplaintheproblemthatforwardreferencessolveandthewaywecantakeadvantageofthem.

Now,let’ssupposethatwehavedefinedtheBufferandSocketclassesintheoppositeorder:

//ch5/ts/injector-basics/forward-ref.ts

@Injectable()

classSocket{

constructor(privatebuffer:Buffer){…}

}

//undefined

console.log(Buffer);

classBuffer{

constructor(@Inject(BUFFER_SIZE)privatesize:Number){…}

}

//[Function:Buffer]

console.log(Buffer);

Here,wehavetheexactsamedependenciesasintheonesinthepreviousexample,butinthiscase,theSocketclassdefinitionprecedesthedefinitionoftheBufferclass.NotethatthevalueoftheBufferidentifierwillequalundefineduntiltheJavaScriptvirtualmachineevaluatesthedeclarationoftheBufferclass.However,themetadataforthetypesofdependenciesthatSocketacceptswillbegeneratedandplacedrightaftertheSocketclassdefinition.ThismeansthatalongwiththeinterpretationofthegeneratedJavaScript,thevalueoftheBuffertokenwillequalundefined—thatis,asatypeofdependency(orinthecontextoftheDImechanismofAngular2,itstoken),theframeworkwillgetaninvalidvalue.

Runningtheprecedingsnippetwillresultinaruntimeerrorofthefollowingform:

Error:CannotresolveallparametersforSocket(undefined).Makesurethey

allhavevalidtypeorannotations.

Thebestwaytoresolvethisissueisbyswappingthedefinitionswiththeirproperorder.AnotherwaywecanproceedistotakeadvantageofasolutionthatAngular2provides:aforwardreference:

import{forwardRef}from'angular2/core';

@Injectable()

classSocket{

constructor(@Inject(forwardRef(()=>Buffer))

privatebuffer:Buffer){}

}

classBuffer{…}

Theprecedingsnippetdemonstrateshowwecantakeadvantageofforwardreferences.Allweneedtodoisusethe@InjectparameterdecoratorandpasstheresultoftheinvocationoftheforwardReffunctiontoit.TheforwardReffunctionisahigher-orderfunctionthatacceptsasingleargument—anotherfunctionthatisresponsibleforreturningthetokenassociatedwiththedependency(ormorepreciselyassociatedwithitsprovider)thatneedstobeinjected.Thisway,theframeworkprovidesawaytodefertheprocessofresolvingthetypes(tokens)ofdependencies.

ThetokenofthedependencywillberesolvedthefirsttimeSocketneedstobeinstantiated,unlikethedefaultbehaviorinwhichthetokenisrequiredatthetimeofthedeclarationofthegivenclass.

ConfiguringprovidersNow,let’stakealookatanexamplesimilartotheoneusedearlierbutwithadifferentconfigurationoftheinjector:

letinjector=Injector.resolveAndCreate([

provide(BUFFER_SIZE,{useValue:42}),

provide(Buffer,{useClass:Buffer}),

provide(Socket,{useClass:Socket})

]);

Inthiscase,insideoftheprovider,weexplicitlydeclaredthatwewanttheBufferclasstobeusedfortheconstructionofthedependencywithatokenequaltothereferenceoftheBufferclass.WedotheexactsamethingforthedependencyassociatedwiththeSockettoken;butthistime,weprovidetheSocketclassinstead.ThisishowAngular2willproceedwhenweomitthecalloftheprovidefunctionandpassonlyareferencetoaclassinstead.

Explicitlydeclaringtheclassusedfortheinstantiationofthesameclassmayseemquiteworthless,andgiventheexampleswelookedatsofar,this’llbecompletelycorrect.Insomecases,however,wemightwanttoprovideadifferentclassfortheinstantiationofadependencyassociatedwithgivenclasstoken.

Forinstance,let’ssupposewehavetheHttpservicethatisusedinaservicecalledUserService:

classHttp{…}

@Injectable()

classUserService{

constructor(privatehttp:Http){}

}

letinjector=Injector.resolveAndCreate([

UserService,

Http

]);

TheUserServiceserviceusesHttpforcommunicationwithaRESTfulservice.WecaninstantiateUserServiceusinginjector.get(UserService).Thisway,theconstructorofUserServiceinvokedbytheinjector’sgetmethodwillacceptaninstanceoftheHttpserviceasanargument.However,ifwewanttotestUserService,wedon’treallyneedtomakeHTTPcallstotheRESTfulservice.Incaseofunittesting,wecanprovideadummyimplementationthatwillonlyfaketheseHTTPcalls.InordertoinjectaninstanceofadifferentclasstotheUserServiceservice,wecanchangetheconfigurationoftheinjectortothefollowing:

classDummyHttp{…}

//...

letinjector=Injector.resolveAndCreate([

UserService,

provide(Http,{useClass:DummyHttp})

]);

Now,whenweinstantiateUserService,it’sconstructorwillreceiveareferencetoaninstanceoftheDummyHttpservice.Thiscodeisavailableatch5/ts/configuring-providers/dummy-http.ts.

UsingexistingprovidersAnotherwaytoproceedisusingtheuseExistingpropertyoftheprovider’sconfigurationobject:

//ch5/ts/configuring-providers/existing.ts

letinjector=Injector.resolveAndCreate([

DummyService,

provide(Http,{useExisting:DummyService}),

UserService

]);

Intheprecedingsnippet,weregisteredthreetokens:DummyService,UserService,andHttp.WedeclaredthatwewanttobindtheHttptokentotheexistingtoken,DummyService.ThismeansthatwhentheHttpserviceisrequested,theinjectorwillfindtheproviderforthetokenusedasthevalueoftheuseExistingpropertyandinstantiateitorgetthevalueassociatedwithit.WecanthinkofuseExistingascreatinganaliasofthegiventoken:

letdummyHttp={

get(){},

post(){}

};

letinjector=Injector.resolveAndCreate([

provide(DummyService,{useValue:dummyHttp}),

provide(Http,{useExisting:DummyService}),

UserService

]);

console.assert(injector.get(UserService).http===dummyHttp);

TheprecedingsnippetwillcreateanaliasoftheHttptokentotheDummyHttptoken.ThismeansthatoncetheHttptokenisrequested,thecallwillbeforwardedtotheproviderassociatedwiththeDummyHttptoken,whichwillberesolvedtothevaluedummyHttp.

DefiningfactoriesforinstantiatingservicesNow,let’ssupposewewanttocreateacomplexobject,forexample,onethatrepresentsaTransportLayerSecurity(TLS)connection.Afewofthepropertiesofsuchanobjectareasocket,asetofcryptoprotocols,andacertificate.Inthecontextofthisproblem,thefeaturesoftheDImechanismofAngular2wehavesofarlookedatmightseemabitlimited.

Forexample,wemightneedtoconfiguresomeofthepropertiesoftheTLSConnectionclasswithoutcouplingtheprocessofitsinstantiationwithalltheconfigurationdetails(chooseappropriatecryptoalgorithms,opentheTCPsocketoverwhichwewillestablishthesecureconnection,andsoon).

Inthiscase,wecantakeadvantageoftheuseFactorypropertyoftheprovider’sconfigurationobject:

letinjector=Injector.resolveAndCreate([

provide(TLSConnection,{

useFactory:(socket:Socket,certificate:Certificate,crypto:Crypto)

=>{

letconnection=newTLSConnection();

connection.certificate=certificate;

connection.socket=socket;

connection.crypto=crypto;

socket.open();

returnconnection;

},

deps:[Socket,Certificate,Crypto]

}),

provide(BUFFER_SIZE,{useValue:42}),

Buffer,

Socket,

Certificate,

Crypto

]);

Theprecedingcodeseemsabitcomplexatfirst,butlet’stakealookatitstepbystep.Wecanstartwiththepartswe’realreadyfamiliarwith:

letinjector=Injector.resolveAndCreate([

...

provide(BUFFER_SIZE,{useValue:42}),

Buffer,

Socket,

Certificate,

Crypto

]);

Initially,weregisteredanumberofproviders:Buffer,Socket,Certificate,andCrypto.Justlikeintheprecedingexample,wealsoregisteredtheBUFFER_SIZEtokenand

associateditwiththevalue42.ThismeansthatwecanalreadycreateobjectsoftheBuffer,Socket,Certificate,andCryptotypes:

//bufferwithsize42

console.log(injector.get(Buffer));

//socketwithbufferwithsize42

console.log(injector.get(Socket));

WecancreateandconfigureaninstanceoftheTLSConnectionobjectinthefollowingway:

letconnection=newTLSConnection();

connection.certificate=certificate;

connection.socket=socket;

connection.crypto=crypto;

socket.open();

returnconnection;

Now,ifweregisteraproviderthathastheTLSConnectiontokenasadependency,wewillpreventthedependencyinjectionmechanismofAngularfromtakingcareofthedependencyresolutionprocess.Inordertohandlethisproblem,wecanusetheuseFactorypropertyoftheprovider’sconfigurationobject.Thisway,wecanspecifyafunctioninwhichwecanmanuallycreatetheinstanceoftheobjectassociatedwiththeprovider’stoken.WecanusetheuseFactorypropertytogetherwiththedepspropertyinordertospecifythedependenciestobepassedtothefactory:

provide(TLSConnection,{

useFactory:(socket:Socket,certificate:Certificate,crypto:Crypto)=>

{

//...

},

deps:[Socket,Certificate,Crypto]

})

Intheprecedingsnippet,wedefinedthefactoryfunctionusedfortheinstantiationofTLSConnection.Asdependencies,wedeclaredSocket,Certificate,andCrypto.ThesedependenciesareresolvedbytheDImechanismofAngular2andinjectedtothefactoryfunction.Youcantakealookattheentireimplementationandplaywithitatch5/ts/configuring-providers/factory.ts.

ChildinjectorsandvisibilityInthissection,we’regoingtotakealookathowwecanbuildahierarchyofinjectors.ThisisacompletelynewconceptintroducedbyAngular2.Eachinjectorcanhavezerooroneparentinjectorsandeachparentinjectorcanhavezeroormorechildren,respectively.IncontrasttoAngularJS1.x,wherealltheregisteredprovidersarestoredinaflatstructureinAngular2,theyarestoredinatree.Theflatstructureismorelimited;forinstance,itdoesn’tsupportthenamespacingoftokens;thatis,wecannotdeclaredifferentprovidersforthesametoken,whichmightberequiredinsomecases.Sofar,welookedatanexampleofinjectorthatdoesn’thaveanychildrenoraparent.Nowlet’sbuildahierarchyofinjectors!

Inordertogainabetterunderstandingofthishierarchicalstructureofinjectors,let’stakealookatthefollowingfigure:

Here,weseeatreewhereeachnodeisaninjectorandeachoftheseinjectorskeepsareferencetoitsparent.InjectorHousehasthreechildinjectors:Bathroom,Kitchen,andGarage.

Garagehastwochildren:CarandStorage.Wecanthinkoftheseinjectorsascontainerswithregisteredprovidersinsideofthem.

Let’ssupposewewanttogetthevalueoftheproviderassociatedwiththetokenTire.IfweusetheinjectorCar,thismeansthatAngular2’sDImechanismwilltrytofindtheproviderassociatedwiththistokeninCarandallofitsparents,GarageandHouse,untilitfindsit.

BuildingahierarchyofinjectorsInordertogainabetterunderstandingofthepreviousparagraph,let’stakealookatthissimpleexample:

//ch5/ts/parent-child/simple-example.ts

classHttp{}

@Injectable()

classUserService{

constructor(publichttp:Http){}

}

letparentInjector=Injector.resolveAndCreate([

Http

]);

letchildInjector=parentInjector.resolveAndCreateChild([

UserService

]);

//UserService{http:Http{}}

console.log(childInjector.get(UserService));

//true

console.log(childInjector.get(Http)===parentInjector.get(Http));

Theimportsareomitted,sincetheyarenotessentialtoexplaintheprecedingsnippet.Wehavetwoservices,HttpandUserService,whereUserServicedependsontheHttpservice.

Initially,wecreatedaninjectorusingtheresolveAndCreatestaticmethodoftheInjectorclass.Wepassedanimplicitprovidertothisinjector,whichwilllaterberesolvedtoaproviderwithanHttptoken.UsingresolveAndCreateChild,weresolvedthepassedprovidersandinstantiatedaninjector,whichpointstoparentInjector(sowegetthesamerelationastheonebetweenGarageandHouseonthediagramabove).

Now,usingchildInjector.get(UserService),weareabletogetthevalueassociatedwiththeUserServicetoken.Similarly,usingchildInjector.get(Http)andparentInjector.get(Http),wegetthesamevalueassociatedwiththeHttptoken.ThismeansthatchildInjectorasksitsparentforthevalueassociatedwiththerequestedtoken.

However,ifwetrytouseparentInjector.get(UserService),wewon’tbeabletogetthevalueassociatedwiththetoken,sinceinthisinjector,wedon’thavearegisteredproviderwiththistoken.

ConfiguringdependenciesNowthatwe’refamiliarwiththeinjectors’hierarchy,let’sseehowwecangetthedependenciesfromtheappropriateinjectorsinit.

Usingthe@SelfdecoratorNowlet’ssupposewehavethefollowingconfiguration:

abstractclassChannel{}

classHttpextendsChannel{}

classWebSocketextendsChannel{}

@Injectable()

classUserService{

constructor(publicchannel:Channel){}

}

letparentInjector=Injector.resolveAndCreate([

provide(Channel,{useClass:Http})

]);

letchildInjector=parentInjector.resolveAndCreateChild([

provide(Channel,{useClass:WebSocket}),

UserService

]);

WecaninstantiatetheUserServicetokenusing:

childInjector.get(UserService);

InUserService,wecandeclarethatwewanttogettheChanneldependencyfromthecurrentinjector(thatis,childInjector)usingthe@Selfdecorator:

@Injectable()

classUserService{

constructor(@Self()publicchannel:Channel){}

}

AlthoughthisisgoingtobethedefaultbehaviorduringtheinstantiationoftheUserService,using@Self,wecanbemoreexplicit.Let’ssupposewechangetheconfigurationofchildInjectortothefollowing:

letparentInjector=Injector.resolveAndCreate([

provide(Channel,{useClass:Http})

]);

letchildInjector=parentInjector.resolveAndCreateChild([

UserService

]);

Ifwekeepthe@SelfdecoratorintheUserServiceconstructorandtrytoinstantiateUserServiceusingchildInjector,wewillgetaruntimeerrorbecauseofthemissingproviderforChannel.

Skippingtheselfinjector

Insomecases,especiallywhileinjectingthedependenciesofUIcomponents,wemaywanttousetheproviderregisteredintheparentinjectorinsteadoftheoneregisteredinthecurrentinjector.Wecanachievethisbehaviorbytakingadvantageofthe@SkipSelfdecorator.Forinstance,let’ssupposewehavethefollowingdefinitionoftheclassContext:

classContext{

constructor(publicparentContext:Context){}

}

EachinstanceoftheContextclasshasaparent.Nowlet’sbuildahierarchyoftwoinjectors,whichwillallowustocreateacontextwithaparentcontext:

letparentInjector=Injector.resolveAndCreate([

provide(Context,{useValue:newContext(null)})

]);

letchildInjector=parentInjector.resolveAndCreateChild([

Context

]);

Sincetherootcontextdoesn’thaveaparent,wewillsetthevalueofitsprovidertobenewContext(null).

Ifwewanttoinstantiatethechildcontext,wecanuse:

childInjector.get(Context);

Fortheinstantiationofthechild,ContextwillbeusedbytheproviderregisteredwithinthechildInjector.However,asadependencyitacceptsanobjectwhichisaninstanceoftheContextclass.Suchclassesexistinthesameinjector,whichmeansthatAngularwilltrytoinstantiateit,butithasadependencyoftheContexttype.Thisprocesswillleadtoaninfiniteloopthatwillcausearuntimeerror.

Inordertopreventitfromhappening,wecanchangethedefinitionofContextinthefollowingway:

classContext{

constructor(@SkipSelf()publicparentContext:Context){}

}

Theonlychangethatweintroducedistheadditionoftheparameterdecorator@SkipSelf.

HavingoptionaldependenciesAngular2introducesthe@Optionaldecorator,whichallowsustodealwithdependenciesthatdon’thavearegisteredproviderassociatedwiththem.Supposeadependencyofaproviderisnotavailableinanyofthetargetinjectorsresponsibleforitsinstantiation.Ifweusethe@Optionaldecorator,duringtheinstantiationofthedependentproviderforvalueofthemissingdependencywillbepassednull.

Nowlet’stakealookatthefollowingexample:

abstractclassSortingAlgorithm{

abstractsort(collection:BaseCollection):BaseCollection;

}

@Injectable()

classCollectionextendsBaseCollection{

privatesort:SortingAlgorithm;

constructor(sort:SortingAlgorithm){

super();

this.sort=sort||this.getDefaultSort();

}

}

letinjector=Injector.resolveAndCreate([

Collection

]);

Inthiscase,wedefinedanabstractclasscalledSortingAlgorithmandaclasscalledCollection,whichacceptsaninstanceofaconcreteclassasadependencythatextendsSortingAlgorithm.InsideoftheCollectionconstructor,wesetthesortinstancepropertytothepasseddependencyoftheSortingAlgorithmtypeoradefaultsortingalgorithmimplementation.

Wedidn’tdefineanyprovidersfortheSortingAlgorithmtokenintheinjectorweconfigured.So,ifwewanttogetaninstanceoftheCollectionclassusinginjector.get(Collection),we’llgetaruntimeerror.ThismeansthatifwewanttogetaninstanceoftheCollectionclassusingtheDImechanismoftheframework,wemustregisteraproviderfortheSortingAlgorithmtoken,althoughwecanfallbacktothedefaultsortingalgorithm’simplementation.

Angular2providesasolutiontothisproblemwiththe@Optionaldecorator.

Thisishowwecanapproachtheproblemusingthe@Optionaldecoratorprovidedbytheframework:

//ch5/ts/decorators/optional.ts

@Injectable()

classCollectionextendsBaseCollection{

privatesort:SortingAlgorithm;

constructor(@Optional()sort:SortingAlgorithm){

super();

this.sort=sort||this.getDefaultSort();

}

}

Intheprecedingsnippet,wedeclaredthesortdependencyasoptional,whichmeansthatifAngular2doesn’tfindanyproviderforitstoken,itwillpassthenullvalue.

UsingmultiprovidersMultiprovidersareanothernewconceptbroughttotheAngular2DImechanism.Theyallowustoassociatemultipleproviderswiththesametoken.Thiscanbequiteusefulifwe’redevelopingathird-partylibrarythatcomeswithsomedefaultimplementationsofdifferentservices,butyouwanttoallowtheuserstoextenditwithcustomones.TheyarealsoexclusivelyusedtodeclaremultiplevalidationsoverasinglecontrolintheAngular2

formmodule.WewillexplainthismoduleinChapter6,WorkingwiththeAngular2RouterandForms,andChapter7,ExplainingPipesandCommunicatingwithRESTfulServices.

AnothersampleofanapplicableusecaseofmultiprovidersiswhatAngular2usesforeventmanagementintheirWebWorkersimplementation.Theycreatemultiprovidersforeventmanagementplugins.Eachoftheprovidersreturnsadifferentstrategy,whichsupportsadifferentsetofevents(touchevents,keyboardevents,andsoon).Onceagiveneventoccurs,theycanchoosetheappropriatepluginthathandlesit.

Let’stakealookatanexamplethatillustratesthetypicalusageofmultiproviders:

//ch5/ts/configuring-providers/multi-providers.ts

constVALIDATOR=newOpaqueToken('validator');

interfaceEmployeeValidator{

(person:Employee):boolean;

}

classEmployee{...}

letinjector=Injector.resolveAndCreate([

provide(VALIDATOR,{multi:true,

useValue:(person:Employee)=>{

if(!person.name){

return'Thenameisrequired';

}

}

}),

provide(VALIDATOR,{multi:true,

useValue:(person:Employee)=>{

if(!person.name||person.name.length<1){

return'Thenameshouldbemorethan1symbollong';

}

}

}),

Employee

]);

Intheprecedingsnippet,wedeclaredaconstantcalledVALIDATORwithanewinstanceofOpaqueToken.Wealsocreatedaninjectorwhereweregisteredthreeproviders—twoofthemareusedasvaluefunctionsthat,basedondifferentcriteria,validateinstancesoftheclassEmployee.ThesefunctionsareofthetypeEmployeeValidator.

InordertodeclarethatwewanttheinjectortopassalltheregisteredvalidatorstotheconstructoroftheclassEmployee,weneedtousethefollowingconstructordefinition:

classEmployee{

name:string;

constructor(@Inject(VALIDATOR)privatevalidators:EmployeeValidator[])

{}

validate(){

returnthis.validators

.map(v=>v(this))

.filter(value=>!!value);

}

}

Intheprecedingexample,wedeclaredaclassEmployeethatacceptsasingledependency—anarrayofEmployeeValidators.Inthemethodvalidate,weappliedtheindividualvalidatorsoverthecurrentclassinstanceandfilteredtheresultsinordertogetonlytheonesthathavereturnedanerrormessage.

NoticethattheconstructorargumentvalidatorsisoftheEmployeeValidator[]type.Sincewecan’tusethetype“arrayofobjects”asatokenforaprovider,becauseitisnotavalidtypereference,weneedtousethe@Injectparameterdecorator.

UsingDIwithcomponentsanddirectivesInChapter4,GettingStartedwithAngular2ComponentsandDirectives,whenwedevelopedourfirstAngular2directive,wesawhowwecantakeadvantageoftheDImechanismtoinjectservicesintoourUI-relatedcomponents(thatis,directivesandcomponents).

Let’stakeaquicklookatwhatwedidearlier,butfromaDIperspective:

//ch4/ts/tooltip/app.ts

//...

@Directive(...)

exportclassTooltip{

@Input()

saTooltip:string;

constructor(privateel:ElementRef,privateoverlay:Overlay){

this.overlay.attach(el.nativeElement);

}

//...

}

@Component({

//...

providers:[Overlay],

directives:[Tooltip]

})

classApp{}

Mostofthecodefromtheearlierimplementationisomittedbecauseitisnotdirectlyrelatedtoourcurrentfocus.

NotethattheconstructorofTooltipacceptstwodependencies:

AninstanceoftheElementRefclass.AninstanceoftheOverlayclass.

Thetypesofdependenciesarethetokensassociatedwiththeirproviders,andthecorrespondingvaluesfromtheprovidersaregoingtobeinjectedwiththeDImechanismofAngular2.

AlthoughthedeclarationofthedependenciesoftheTooltipclasslooksexactlythesameaswhatwedidintheprevioussections,there’sneitheranyexplicitconfigurationnoranyinstantiationofaninjector.

IntroducingtheelementinjectorsUnderthehood,Angularwillcreateinjectorsforallthedirectivesandcomponents,andaddadefaultsetofproviderstoit.Thisistheso-calledelementinjectorandissomethingtheframeworktakescareofitself.Theinjectorsassociatedwiththecomponentsarecalledhostinjectors.OneoftheprovidersineachdirectiveandcomponentinjectorisassociatedwiththeElementReftoken;itwillreturnareferencetothehostelementofthedirective.ButwhereistheproviderfortheOverlayclassdeclared?Let’stakealookattheimplementationofthetop-levelcomponent:

@Component({

//...

providers:[Overlay],

directives:[Tooltip]})

classApp{}

WeconfiguredtheelementinjectorfortheAppcomponentbydeclaringtheproviderspropertyinsideofthe@Componentdecorator.Atthispoint,theregisteredprovidersaregoingtobevisiblebythedirectiveorthecomponentassociatedwiththecorrespondingelementinjectorandthecomponent’sentirecomponentsubtree,unlessitisoverriddensomewhereinthehierarchy.

DeclaringprovidersfortheelementinjectorsHavingthedeclarationofalltheprovidersinthesameplacemightbequiteinconvenient.Forexample,imaginewe’redevelopingalarge-scaleapplicationthathashundredsofcomponentsdependingonthousandsofservices.Inthiscase,configuringalltheprovidersintherootcomponentisnotapracticalsolution.Therewillbenamecollisionswhentwoormoreprovidersareassociatedtothesametoken.Theconfigurationwillbehugeanditwillbehardtotracewherethedifferenttokensneedtobeinjected.

Aswementioned,Angular2’s@Directive(andrespectively@Component)decoratorallowsustointroducedirective-specificprovidersusingtheprovidersproperty.Hereishowwecanapproachthis:

@Directive({

selector:'[saTooltip]',

providers:[OverlayMock]

})

exportclassTooltip{

@Input()

saTooltip:string;

constructor(privateel:ElementRef,privateoverlay:Overlay){

this.overlay.attach(el.nativeElement);

}

//...

}

//...

bootstrap(App);

TheprecedingexampleoverridestheproviderfortheOverlaytokenintheTooltipdirective’sdeclaration.Thisway,AngularwillinjectaninstanceofOverlayMockinsteadofOverlayduringtheinstantiationofthetooltip.

Abetterwaytooverridetheproviderisusingthebootstrapfunction.Wecandothefollowing:

bootstrap(AppMock,[provide(Overlay,{

useClass:OverlayMock

})]);

Intheprecedingbootstrapcall,weprovidedadifferenttop-levelcomponentandproviderfortheOverlayservicethatwillreturnaninstanceoftheOverlayMockclass.Thisway,wecantesttheTooltipdirectiveignoringtheimplementationofOverlay.

ExploringDIwithcomponentsSincecomponentsaregenerallydirectiveswithviews,everythingwe’veseensofarregardinghowtheDImechanismworkswithdirectivesisvalidforcomponentsaswell.However,becauseoftheextrafeaturesthatthecomponentsprovide,we’reallowedtohavefurthercontrolovertheirproviders.

Aswesaid,theinjectorassociatedwitheachcomponentwillbemarkedasahostinjector.There’saparameterdecoratorcalled@Host,whichallowsustoretrieveagivendependencyfromanyinjectoruntilitreachestheclosesthostinjector.Thismeansthatbyusingthe@Hostdecoratorinadirective,wecandeclarethatwewanttoretrievethegivendependencyfromthecurrentinjectororanyparentinjectoruntilwereachtheinjectoroftheclosestparentcomponent.

TheviewProviderspropertyaddedtothe@Componentdecoratorisinchargeofachievingevenmorecontrol.

viewProvidersversusprovidersLet’stakealookatanexampleofacomponentcalledMarkdownPanel.Thiscomponentwillbeusedinthefollowingway:

<markdown-panel>

<panel-title>#Title</pane-title>

<panel-content>

#Contentofthepanel

*Firstpoint

*Secondpoint

</panel-content>

</markdown-panel>

ThecontentofeachsectionofthepanelwillbetranslatedfromthemarkdowntotheHTML.WecandelegatethisfunctionalitytoaservicecalledMarkdown:

import*asmarkdownfrom'markdown';

classMarkdown{

toHTML(md){

returnmarkdown.toHTML(md);

}

}

TheMarkdownservicewrapsthemarkdownmoduleinordertomakeitinjectablethroughtheDImechanism.

Nowlet’simplementMarkdownPanel.

Inthefollowingsnippet,wecanfindalltheimportantdetailsfromthecomponent’simplementation:

//ch5/ts/directives/app.ts

@Component({

selector:'markdown-panel',

viewProviders:[Markdown],

styles:[...],

template:`

<divclass="panel">

<divclass="panel-title">

<ng-contentselect="panel-title"></ng-content>

</div>

<divclass="panel-content">

<ng-contentselect="panel-content"></ng-content>

</div>

</div>`

})

classMarkdownPanel{

constructor(privateel:ElementRef,privatemd:Markdown){}

ngAfterContentInit(){

letel=this.el.nativeElement;

lettitle=el.querySelector('panel-title');

letcontent=el.querySelector('panel-content');

title.innerHTML=this.md.toHTML(title.innerHTML);

content.innerHTML=this.md.toHTML(content.innerHTML);

}

}

Weusedthemarkdown-panelselectorandsettheviewProvidersproperty.Inthiscase,there’sonlyasingleviewprovider:theonefortheMarkdownservice.Bysettingthisproperty,wedeclaredthatalltheprovidersdeclaredinitwillbeaccessiblefromthecomponentitselfandallofitsviewchildren.

Now,let’ssupposewehaveacomponentcalledMarkdownButtonandwewanttoaddittoourtemplateinthefollowingway:

<markdown-panel>

<panel-title>###Smalltitle</panel-title>

<panel-content>

Somecode

</panel-content>

<markdown-button>*Clicktotoggle*</markdown-button>

</markdown-panel>

TheMarkdownservicewillnotbeaccessiblebytheMarkdownButtonusedbelowthepanel-contentelement;however,it’llbeaccessibleifweusethebuttoninthe

component’stemplate:

@Component({

selector:'markdown-panel',

viewProviders:[Markdown],

directives:[MarkdownButton],

styles:[…],

template:`

<divclass="panel">

<markdown-button>*Clicktotoggle*</markdown-button>

<divclass="panel-title">

<ng-contentselect="panel-title"></ng-content>

</div>

<divclass="panel-content">

<ng-contentselect="panel-content"></ng-content>

</div>

</div>`

})

Ifweneedtheprovidertobevisibleinallthecontentandviewchildren,allweshoulddoischangethepropertynameoftheviewProviderspropertytoproviders.

Youcanfindthisexampleinthefileintheexamplesdirectoryatch5/ts/directives/app.ts.

UsingAngular’sDIwithES5WearealreadyproficientinusingthedependencyinjectionofAngular2withTypeScript!Asweknow,wearenotlimitedtoTypeScriptforthedevelopmentofAngular2applications;wecanalsouseES5,ES2015,andES2016(aswellasDart,butthatisoutofthescopeofthisbook).

Sofar,wedeclaredthedependenciesofthedifferentclassesintheirconstructorusingstandardTypeScripttypeannotations.Allsuchclassesaresupposedtobedecoratedwiththe@Injectabledecorator.Unfortunately,someoftheotherlanguagessupportedbyAngular2missafewofthesefeatures.Inthefollowingtable,wecanseethatES5doesn’tsupporttypeannotations,classes,anddecorators:

ES5 ES2015 ES2016

Classes No Yes Yes

Decorators No No Yes(noparameterdecorators)

Typeannotations No No No

Inthiscase,howwecantakeadvantageoftheDImechanismintheselanguages?Angular2providesaninternalJavaScriptDomainSpecificLanguage(DSL),whichallowsustotakeadvantageoftheentirefunctionalityoftheframeworkusingES5.

Now,let’stranslatetheMarkdownPanelexamplewetookalookatintheprevioussectionfromTypeScripttoES5.First,let’sstartwiththeMarkdownservice:

//ch5/es5/simple-example/app.js

varMarkdown=ng.core.Class({

constructor:function(){},

toHTML:function(md){

returnmarkdown.toHTML(md);

}

});

WedefinedavariablecalledMarkdownandsetitsvaluetothereturnedresultfromtheinvocationofng.core.Class.ThisconstructallowsustoemulateES2015classesusingES5.Theargumentoftheng.core.Classmethodisanobjectliteral,whichmusthavethedefinitionofaconstructorfunction.Asaresult,ng.core.ClasswillreturnaJavaScriptconstructorfunctionwiththebodyofconstructorfromtheobjectliteral.Alltheothermethodsdefinedwithintheboundariesofthepassedparameterwillbeaddedtothefunction’sprototype.

Oneproblemissolved:wecannowemulateclassesinES5;therearetwomoreproblemsleft!

Now,let’stakealookathowwecandefinetheMarkdownPanelcomponent:

//ch5/es5/simple-example/app.js

varMarkdownPanel=ng.core.Component({

selector:'markdown-panel',

viewProviders:[Markdown],

styles:[...],

template:'...'

})

.Class({

constructor:[Markdown,ng.core.ElementRef,function(md,el){

this.md=md;

this.el=el;

}],

ngAfterContentInit:function(){

}

});

FromChapter4,GettingStartedwithAngular2ComponentsandDirectives,wearealreadyfamiliarwiththeES5syntaxusedtodefinecomponents.Now,let’stakealookattheconstructorfunctionofMarkdownPanelinordertomakesurehowwecandeclarethedependenciesofourcomponentsandevenclassesingeneral.

Fromtheprecedingsnippet,wecannotethatthevalueoftheconstructorisnotafunctionthistime,butanarrayinstead.ThismightseemfamiliartoyoufromAngularJS1.x,whereweareabletodeclarethedependenciesofthegivenservicebylistingtheirnames:

Module.service('UserMapper',

['User','$http',function(User,$http){

//…

}]);

AlthoughthesyntaxinAngular2issimilar,itbringsalotofimprovements.Forinstance,we’renolongerlimitedtousingstringsforthedependencies’tokens.

Now,let’ssupposewewanttomaketheMarkdownserviceanoptionaldependency.Inthiscase,wecanapproachthisbypassinganarrayofdecorators:

.Class({

constructor:[[ng.core.Optional(),Markdown],

ng.core.ElementRef,function(md,el){

this.md=md;

this.el=el;

}],

ngAfterContentInit:function(){

}

});

Thisway,bynestingarrays,wecanapplyasequenceofdecorators:[[ng.core.Optional(),ng.core.Self(),Markdown],...].Inthisexample,the@Optionaland@Selfdecoratorswilladdtheassociatedmetadatatotheclassinthespecifiedorder.

AlthoughusingES5makesourbuildsimplerandallowsustoskiptheintermediatestepof

transpilation,whichcanbetempting,Google’srecommendationistotakeadvantageofstatictypingusingTypeScript.Thisway,wehaveamuchclearersyntax,whichcarriesbettersemanticswithlesstypingandprovidesuswithgreattooling.

SummaryInthischapter,wecoveredtheDImechanismofAngular2.Webrieflydiscussedthepositivesofusingdependencyinjectioninourprojectsbyintroducingitinthecontextoftheframework.Thesecondstepinourjourneywashowtoinstantiateandconfigureinjectors;wealsoexplainedtheinjectors’hierarchyandthevisibilityoftheregisteredproviders.Inordertoenforceabetterseparationofconcerns,wementionedhowwecaninjectservicescarryingthebusinesslogicofourapplicationinourdirectivesandcomponents.ThelastpointwetookalookatwashowwecanusetheDImechanismwiththeES5syntax.

Inthenextchapter,we’llintroducethenewroutingmechanismoftheframework.We’llexplainhowwecanconfigurethecomponent-basedrouterandaddmultipleviewstoourapplication.Anotherimportanttopicwearegoingtocoveristhenewformmodule.Bybuildingasimpleapplication,wewilldemonstratehowwecancreateandmanageforms.

Chapter6.WorkingwiththeAngular2RouterandFormsBynow,we’realreadyfamiliarwiththecoreoftheframework.Weknowhowtodefinecomponentsanddirectivesinordertodeveloptheviewofourapplications.Wealsoknowhowtoencapsulatebusiness-relatedlogicintoservicesandwireeverythingtogetherwiththedependencyinjectionmechanismofAngular2.

Inthischapter,we’llexplainafewmoreconceptsthatwillhelpusbuildreal-lifeAngular2applications.Theyareasfollows:

Thecomponent-basedrouteroftheframework.UsingAngular2forms.Developingtemplate-drivenforms.Developingcustomformvalidators.

Let’sbegin!

Developingthe“Codersrepository”applicationIntheprocessofexplainingtheconceptsmentionedearlier,we’lldevelopasampleapplicationthatcontainsarepositoryofdevelopers.Beforewestartcoding,let’sexplainthestructureoftheapplication.

The“Codersrepository”willallowitsuserstoadddeveloperseitherbyfillingaformwithdetailsaboutthemorbyprovidingtheGitHubhandleforthedeveloperandimportinghisorherprofilefromGitHub.

NoteForthepurposeofthischapter,wewillstoreinformationonthedevelopersinmemory,whichmeansthatafterthepageisrefreshed,we’llloseallthestoredduringthesessiondata.

Theapplicationwillhavethefollowingviews:

Alistofallthedevelopers.Aviewthataddsorimportsnewdevelopers.Aviewthatshowsthegivendeveloper’sdetails.Thisviewhastwosubviews:

Basicdetails:ShowsthenameofthedeveloperandherorhisGitHubavatarifavailable.Advancedprofile:Showsallthedetailsknownforthedeveloper.

Theendresultofthehomepageoftheapplicationwilllookasfollows:

Fig.1

NoteInthischapter,wewillbuildonlyafewofthelistedviews.Therestoftheapplicationwill

beexplainedinChapter7,ExplainingPipesandCommunicatingwithRESTfulServices.

Eachdeveloperwillbeaninstanceofthefollowingclass:

//ch6/ts/multi-page-template-driven/developer.ts

exportclassDeveloper{

publicid:number;

publicgithubHandle:string;

publicavatarUrl:string;

publicrealName:string;

publicemail:string;

publictechnology:string;

publicpopular:boolean;

}

AllthedeveloperswillresidewithintheDeveloperCollectionclass:

//ch6/ts/multi-page-template-driven/developer_collection.ts

classDeveloperCollection{

privatedevelopers:Developer[]=[];

getUserByGitHubHandle(username:string){

returnthis.developers

.filter(u=>u.githubHandle===username)

.pop();

}

getUserById(id:number){

returnthis.developers

.filter(u=>u.id===id)

.pop();

}

addDeveloper(dev:Developer){

this.developers.push(dev);

}

getAll(){

returnthis.developers;

}

}

Theclassesmentionedhereencapsulatequiteasimplelogicanddon’thaveanythingAngular2-specific,sowewon’tgetintoanydetails.

Now,let’scontinuewiththeimplementationbyexploringthenewrouter.

ExploringtheAngular2routerAswealreadyknow,inordertobootstrapanyAngular2application,weneedtodeveloparootcomponent.The“Codersrepository”applicationisnotanydifferent;theonlyadditioninthisspecificcaseisthatwewillhavemultiplepagesthatneedtobeconnectedtogetherwiththeAngular2router.

Let’sstartwiththeimportsrequiredfortherouter’sconfigurationanddefinetherootcomponentrightafterthis:

//ch6/ts/step-0/app.ts

import{

ROUTER_DIRECTIVES,

ROUTER_PROVIDERS,

Route,

Redirect,

RouteConfig,

LocationStrategy,

HashLocationStrategy

}from'angular2/router';

Intheprecedingsnippet,weimportedacoupleofthingsdirectlyfromtheAngular2routermodule,whichisexternalizedoutsidetheframework’score.

WithROUTER_DIRECTIVES,therouterprovidesasetofcommonlyuseddirectivesthatwecanaddtothelistofusedonesbytherootcomponent.Thisway,wewillbeabletousetheminthetemplateslater.

TheimportROUTE_PROVIDERScontainsasetofrouter-relatedproviders,suchasoneforinjectingtheRouteParamstokenintothecomponents’constructors.

TheRouteParamstokenprovidesanaccesstoparametersfromtheroute’sURLinordertoparametrizethelogicassociatedwithagivenpage.We’lldemonstrateatypicalusecaseofthisproviderlater.

TheimportLocationStrategyclassisanabstractclassthatdefinesthecommonlogicbetweenHashLocationStrategy(usedforhash-basedrouting)andPathLocationStrategy(usedforHTML5-basedroutingbytakingadvantageofthehistoryAPI).

NoteHashLocationStrategydoesnotsupportserver-siderendering.Thisisduetothefactthatthehashofthepagedoesnotgetsenttotheserver,soitcannotfindoutthecomponentassociatedwiththegivenpage.AllmodernbrowsersexceptIE9supporttheHTML5historyAPI.Youcanfindmoreaboutserver-siderenderinginthelastchapterofthebook.

Thelastimportswedidn’ttakealookatareRouteConfig,whichisadecoratorthatallowsustodefinetheroutesassociatedwithagivencomponent;andRouteandRedirect,whichrespectivelyallowustodefinetheindividualroutesandredirects.WithRouteConfig,wecandefineahierarchyofroutes,whichmeansthattherouterofAngular

2supportsnestedroutingoutoftheboxunlikeitspredecessorinAngularJS1.x.

DefiningtherootcomponentandbootstrappingtheapplicationNow,let’sdefinearootcomponentandconfiguretheapplication’sinitialbootstrap:

//ch6/ts/step-0/app.ts

@Component({

selector:'app',

template:`…`,

providers:[DeveloperCollection],

directives:[ROUTER_DIRECTIVES]

})

@RouteConfig([…])

classApp{}

bootstrap(…);

Intheprecedingsnippet,youcannoticeasyntaxwe’realreadyfamiliarwithfromChapter4,GettingStartedwithAngular2ComponentsandDirectivesandChapter5,DependencyInjectioninAngular2.Wedefinedacomponentwithanappselector,templatethatwe’regoingtotakealookatlater,andsetsofprovidersanddirectives.

TheAppcomponentusesasingleprovidercalledDeveloperCollection.Thisistheclassthatcontainsallthedevelopersstoredbytheapplication.YoucannoticethatweaddedROUTER_DIRECTIVES;itcontainsanarrayofallthedirectivesdefinedwithintheAngular’srouter.Someofthedirectiveswithinthisarrayallowustolinktotheotherroutesdefinedwithinthe@RouteConfigdecorator(therouterLinkdirective)anddeclaretheplacewherethecomponentsassociatedwiththedifferentroutesshouldberendered(router-outlet).We’llexplainhowwecanusethemlaterinthissection.

Nowlet’stakealookatthecallofthebootstrapfunction:

bootstrap(App,[

ROUTER_PROVIDERS,

provide(LocationStrategy,{useClass:HashLocationStrategy})

)]);

Asthefirstargumentofbootstrap,wepasstherootcomponentoftheapplicationasusual.Thesecondargumentisalistofprovidersthatwillbeaccessiblebytheentireapplication.Tothesetofproviders,weaddROUTER_PROVIDERSandwealsoconfiguretheproviderfortheLocationStrategytoken.ThedefaultLocationStrategytoken,whichAngular2uses,isPathLocationStrategy(thatis,theHTML5-basedone).However,inthiscase,wearegoingtousethehash-basedone.

Thetwobiggestadvantagesofthedefaultlocationstrategyarethatitissupportedbytheserver-renderingmoduleofAngular2,andtheapplication’sURLlooksmorenaturaltotheenduser(there’sno#used).Ontheotherhand,incaseweusePathLocationStrategy,wemayneedtoconfigureourapplicationserver,inordertohandletheroutesproperly.

UsingPathLocationStrategyIfwewanttousePathLocationStrategy,wemayneedtoprovideAPP_BASE_HREF.Forinstance,inourcase,thebootstrapconfigurationshouldlookasfollows:

import{APP_BASE_HREF}from'angular2/router';

//...

bootstrap(App,[

ROUTER_PROVIDERS,

//Thefollowinglineisoptional,sinceit's

//thedefaultvaluefortheLocationStrategytoken

provide(LocationStrategy,{useClass:PathLocationStrategy}),

provide(APP_BASE_HREF,{

useValue:'/dist/dev/ch6/ts/multi-page-template-driven/'

}

)]);

Bydefault,thevalueassociatedwiththeAPP_BASE_HREFtokenis/;itrepresentsthebasepathnameinsideoftheapplication.Forinstance,inourcase,the“Codersrepository”willbelocatedunderthe/ch6/ts/multi-page-template-driven/directory(thatis,http://localhost:5555/dist/dev/ch6/ts/multi-page-template-driven/).

Configuringrouteswith@RouteConfigAsthenextstep,let’stakealookattheroute’sdeclarationplacedinthe@RouteConfigdecorator:

//ch6/ts/step-0/app.ts

@Component(…)

@RouteConfig([

newRoute({component:Home,name:'Home',path:'/'}),

newRoute({

component:AddDeveloper,

name:'AddDeveloper',

path:'/dev-add'

}),

//…

newRedirect({

path:'/add-dev',

redirectTo:['/dev-add']

})

])

classApp{}

Astheprecedingsnippetshows,the@RouteConfigdecoratoracceptsanarrayofroutesasanargument.Intheexample,wedefinedtwotypesofroutes:usingtheclassesRouteandRedirect.Theyareusedrespectivelytodefinetheroutesandredirectsintheapplication.

Eachroutemustdefinethefollowingproperties:

component:Thecomponentassociatedwiththegivenroute.name:Thenameoftherouteusedforreferencingitinthetemplates.path:Thepathtobeusedfortheroute.Itwillbevisibleinthebrowser’slocationbar.

NoteTheRouteclassalsosupportsadatapropertywhosevaluecanbeinjectedintheconstructorofitsassociatedcomponentbyusingtheRouteDatatoken.Asampleusecaseofthedatapropertycouldbeifwewanttoinjectdifferentconfigurationobjectsbasedonthetypeoftheparentcomponentthatcontainsthe@RouteConfigdeclaration.

Ontheotherhand,theredirectcontainsonlytwoproperties:

path:Thepathtobeusedfortheredirection.redirectTo:Thepaththeuserisredirectedto.

Inthepreviousexample,wedeclaredthatwewantthepageopenedbytheuserwiththepath/add-devtoberedirectedto['/dev-add'].

Now,inordertomakeeverythingwork,weneedtodefinetheAddDeveloperandHomecomponents,whicharereferencedin@RouteConfig.Initially,we’regoingtoprovideabasicimplementationthatwe’llincrementallyextendovertimealongthechapter.Inch6/ts/step-0,createafilecalledhome.tsandenterthefollowingcontent:

import{Component}from'angular2/core';

@Component({

selector:'home',

template:`Home`

})

exportclassHome{}

DonotforgettoimporttheHomecomponentinapp.ts.Now,openthefilecalledadd_developer.tsandenterthefollowingcontentinit:

import{Component}from'angular2/core';

@Component({

selector:'dev-add',

template:`Adddeveloper`

})

exportclassAddDeveloper{}

UsingrouterLinkandrouter-outletWehavetheroute’sdeclarationandallthecomponentsassociatedwiththeindividualroutes.TheonlythingleftistodefinethetemplateoftherootAppcomponentinordertolinkeverythingtogether.

Addthefollowingcontenttothetemplatepropertyinsidethe@Componentdecoratorinch6/ts/step-0/app.ts:

@Component({

//…

template:`

<navclass="navbarnavbar-default">

<ulclass="navnavbar-nav">

<li><a[routerLink]="['/Home']">Home</a></li>

<li><a[routerLink]="['/AddDeveloper']">Adddeveloper</a></li>

</ul>

</nav>

<router-outlet></router-outlet>

`,

//…

})

InthetemplateabovetherearetwoAngular2-specificdirectives:

routerLink:Thisallowsustoaddalinktoaspecificroute.router-outlet:Thisdefinesthecontainerwherethecomponentsassociatedwiththecurrentlyselectedrouteneedtoberendered.

Let’stakealookattherouterLinkdirective.Asvalueitacceptsanarrayofroutenamesandparameters.Inourcaseweprovideonlyasingleroutenameprefixedwithslash(sincethisrouteisonrootlevel).NoticethattheroutenameusedbyrouterLinkisdeclaredbythenamepropertyoftheroutedeclarationinside@RouteConfig.Laterinthischapterwe’llseehowwecanlinktonestedroutesandpassrouteparameters.

ThisdirectiveallowsustodeclarelinksindependentlyfromLocationStrategythatwehaveconfigured.Forinstance,imagineweareusingHashLocationStrategy;thismeansthatweneedtoprefixalltheroutesinourtemplateswith#.IncaseweswitchtoPathLocationStrategy,we’llneedtoremoveallthehashprefixes.AnotherhugebenefitofrouterLinkisthatitusestheHTML5historypushAPItransparentlytous,whichsavesusfromalotofboilerplates.

Thenextdirectivefromtheprevioustemplatethatisnewtousisrouter-outlet.Ithassimilarresponsibilitytotheng-viewdirectiveinAngularJS1.x.Basically,theybothhavethesamerole:topointoutwherethetargetcomponentshouldberendered.Thismeansthataccordingtothedefinition,whentheusernavigatesto/,theHomecomponentwillberenderedatthepositionpointedoutbyrouter-outlet,samefortheAddDevelopercomponentoncetheusernavigatesto/dev-add.

Nowwehavethesetworoutesupandrunning!Openhttp://localhost:5555/dist/dev/ch6/ts/step-0/andyoushouldseethefollowing

screenshot:

Fig.2

Ifyoudon’t,justtakealookatch6/ts/step-1thatcontainstheendresult.

Lazy-loadingwithAsyncRouteAngularJS1.xmodulesallowustogrouptogetherlogicallyrelatedunitsintheapplication.However,bydefault,theyneedtobeavailableduringtheinitialapplication’sbootstrapanddonotallowdeferredloading.Thisrequiresdownloadingtheentirecodebaseoftheapplicationduringtheinitialpageloadthat,incaseoflargesingle-pageapps,canbeanunacceptableperformancehit.

Inaperfectscenario,wewouldwanttoloadonlythecodeassociatedwiththepagetheuseriscurrentlyviewing,ortoprefetchbundledmodulesbasedonheuristicsrelatedtotheuser’sbehavior,whichisoutofthescopeofthisbook.Forinstance,opentheapplicationfromthefirststepofourexample:http://localhost:5555/dist/dev/ch6/ts/step-1/.Oncetheuserisat/,weonlyneedtheHomecomponenttobeavailable,andonceheorshenavigatesto/dev-add,wewanttoloadtheAddDevelopercomponent.

Let’sinspectwhatisactuallygoingoninChromeDevTools:

Fig.3

Wecannoticethatduringtheinitialpageload,wedownloadedthecomponentsassociatedwithalltheroutes,evenAddDeveloperthatisnotrequired.Thishappensbecauseinapp.ts,weexplicitlyrequireboththeHomeandtheAddDevelopercomponentsandusetheminthe@RouteConfigdeclaration.

Inthisspecificcase,loadingboththecomponentsmaynotseemlikeabigproblem,becauseatthisstep,theyareprettysimpleanddonothaveanydependencies.However,inreal-lifeapplications,theywillhaveimportsofotherdirectives,components,pipes,services,oreventhird-partylibraries.Onceanyofthecomponentsisrequired,itsentire

dependencygraphwillbedownloaded,evenifthecomponentisnotneededatthatpoint.

TherouterofAngular2comeswithasolutiontothisproblem.AllweneedtodoisimporttheAsyncRouteclassfromtheangular2/routermoduleanduseitinside@RouteConfiginsteadofusingRoute:

//ch6/ts/step-1-async/app.ts

import{AsyncRoute}from'angular2/router';

@Component(…)

@RouteConfig([

newAsyncRoute({

loader:()=>

System.import('./home')

.then(m=>m.Home),

name:'Home',

path:'/'

}),

newAsyncRoute({

loader:()=>

System.import('./add_developer')

.then(m=>m.AddDeveloper),

name:'AddDeveloper',

path:'/dev-add'

}),

newRedirect({path:'/add-dev',redirectTo:['/dev-add']})

])

classApp{}

TheconstructoroftheAsyncRouteclassacceptsasanargumentanobjectwiththefollowingproperties:

loader:Afunctionthatreturnsapromisethatneedstoberesolvedwiththecomponentassociatedwiththegivenroute.name:Thenameoftheroutethatcanbeusedtorefertoitinthetemplates(usually,insideoftherouterLinkdirective).path:Thepathoftheroute.

Oncetheusernavigatestoaroutethatmatchesanyoftheasyncroutes’definitionsinthe@RouteConfigdecorator,itsassociatedloaderwillbeinvoked.Whenthepromisereturnedbytheloaderisresolvedwithavalueofthetargetcomponent,thecomponentwillbecachedandrendered.Nexttimetheusernavigatestothesameroute,thecachedcomponentwillbeused,sotheroutingmodulewon’tdownloadthesamecomponenttwice.

NoteNoticethattheprecedingexampleusesSystem,however,Angular’sAsyncRouteimplementationisnotcoupledtoanyparticularmoduleloader.Thesameresultcouldbeachieved,forinstance,withrequire.js.

UsingAngular2formsNowlet’scontinuewiththeimplementationoftheapplication.Forthenextstep,we’llworkontheAddDeveloperandHomecomponents.Youcancontinueyourimplementationbyextendingwhatyoucurrentlyhaveinch6/ts/step-0,orifyouhaven’treachedstep1yet,youcankeepworkingonthefilesinch6/ts/step-1.

Angular2offerstwowaystodevelopformswithvalidation:

Atemplate-drivenapproach:ProvidesadeclarativeAPIwherewedeclarethevalidationsintothetemplateofthecomponent.Amodel-drivenapproach:ProvidesanimperativeAPIwithFormBuilder.

Inthenextchapter,we’llexploreboth.Let’sstartwiththetemplate-drivenapproach.

Developingtemplate-drivenformsFormsareessentialforeachCRUD(CreateRetrieveUpdateandDelete)application.Inourcase,wewanttobuildaformforenteringthedetailsofthedeveloperswewanttostore.

Bytheendofthissection,we’llhaveaformthatallowsustoentertherealnameofagivendeveloper,toaddhisorherpreferredtechnology,enterane-mail,anddeclarewhethersheorheispopularinthecommunityornotyet.Theendresultwilllookasfollows:

Fig.4

Addthefollowingimportstoadd_developer.ts:

import{

FORM_DIRECTIVES,

FORM_PROVIDERS

}from'angular2/common;

ThenextthingweneedtodoisaddFORM_DIRECTIVEStothelistofdirectivesusedbytheAddDevelopercomponent.TheFORM_DIRECTIVESdirectivescontainsasetofpredefineddirectivesformanagingAngular2forms,suchastheformandngModeldirectives.

TheFORM_PROVIDERSisanarraywithapredefinedsetofprovidersthatwecanuseforinjectingthevaluesassociatedwiththeirtokensintheclassesofourapplication.

NowupdatetheAddDeveloperimplementationtothefollowing:

@Component({

selector:'dev-add',

templateUrl:'./add_developer.html',

styles:[…],

directives:[FORM_DIRECTIVES],

providers:[FORM_PROVIDERS]

})

exportclassAddDeveloper{

developer=newDeveloper();

errorMessage:string;

successMessage:string;

submitted=false;

technologies:string[]=[

'JavaScript',

'C',

'C#',

'Clojure'

];

constructor(privatedevelopers:DeveloperCollection){}

addDeveloper(){}

}

Thedeveloperpropertycontainstheinformationassociatedwiththecurrentdeveloperthatwe’readdingwiththeform.Thelasttwoproperties,errorMessageandsuccessMessage,aregoingtobeusedrespectivelyfordisplayingthecurrentform’serrororsuccessmessagesoncethedeveloperhasbeensuccessfullyaddedtothedeveloperscollection,oranerrorhasoccurred.

Diggingintothetemplate-drivenform’smarkupAsthenextstep,let’screatethetemplateoftheAddDevelopercomponent(step-1/add_developer.html).Addthefollowingcontenttothefile:

<span*ngIf="errorMessage"

class="alertalert-danger">{{errorMessage}}</span>

<span*ngIf="successMessage"

class="alertalert-success">{{successMessage}}</span>

Thesetwoelementsareintendedtodisplaytheerrorandsuccessmessageswhenaddinganewdeveloper.TheyaregoingtobevisiblewhenerrorMessageandsuccessMessagerespectivelyhavenon-falsyvalues(thatis,somethingdifferentfromtheemptystring,false,undefined,0,NaN,ornull).

Nowlet’sdeveloptheactualform:

<form#f="ngForm"(ngSubmit)="addDeveloper()"

class="formcol-md-4"[hidden]="submitted">

<divclass="form-group">

<labelclass="control-label"

for="realNameInput">Realname</label>

<div>

<inputid="realNameInput"class="form-control"

type="text"ngControl="realName"required

[(ngModel)]="developer.realName">

</div>

</div>

<buttonclass="btnbtn-default"

type="submit"[disabled]="!f.form.valid">Add</button>

<!--MORECODETOBEADDED-->

</form>

WedeclareanewformusingtheHTMLformtag.OnceAngular2findssuchtagsinatemplatewithanincludedformdirectiveintheparentcomponent,itwillautomaticallyenhanceitsfunctionalityinordertobeusedasanAngularform.OncetheformisprocessedbyAngular,wecanapplyformvalidationanddata-bindings.Afterthis,using#f="ngForm",wewilldefinealocalvariableforthetemplatecalledf,whichallowsustoreferencetothecurrentform.Thelastthingleftfromtheformelementisthesubmiteventhandler.Weuseasyntaxthatwe’realreadyfamiliarwith(ngSubmit)="expr",whereinthiscase,thevalueoftheexpressionisthecalloftheaddDevelopermethodattachedtothecomponent’scontroller.

Now,let’stakealookatthedivelementwithclassnamecontrol-group.

NoteNotethatthisisnotanAngular-specificclass;itisaCSSclassdefinedbyBootstrapthatweuseinordertoprovideabetterlookandfeeltotheform.

Insideofit,wecanfindalabelelementthatdoesn’thaveanyAngular-specificmarkupandaninputelementthatallowsustosettherealnameofthecurrentdeveloper.WesetthecontroltobeofatypetextanddeclareitsidentifiertoequalrealNameInput.The

requiredattributeisdefinedbytheHTML5specificationandisusedforvalidation.Byusingitontheelement,wedeclarethatitisrequiredforthiselementtohaveavalue.AlthoughthisattributeisnotAngular-specificusingthengControlattribute,Angularwillextendthesemanticsoftherequiredattributebyincludingvalidationbehavior.ThisbehaviorincludessettingspecificCSSclassesonthecontrolwhenitsstatuschangesandmanagingitsstatethattheframeworkkeepsinternally.

ThengControldirectiveisaselectoroftheNgControlNamedirective.Itenhancesthebehavioroftheformcontrolsbyrunningvalidationoverthemforthechangeoftheirvalues,andapplyingspecificclassesduringthecontrols’lifecycle.YoumightbefamiliarwiththisfromAngularJS1.xwheretheformcontrolsaredecoratedwiththeng-pristine,ng-invalid,andng-validclasses,andsoon,inspecificphasesoftheirlifecycle.

ThefollowingtablesummarizestheCSSclassesthattheframeworkaddstotheformcontrolsduringtheirlifecycle:

Classes Description

ng-untouched Thecontrolhasn’tbeenvisited

ng-touched Thecontrolhasbeenvisited

ng-pristine Thecontrol’svaluehasn’tbeenchanged

ng-dirty Thecontrol’svaluehasbeenchanged

ng-valid Allthevalidatorsattachedtothecontrolhavereturnedtrue

ng-invalid Anyofthevalidatorsattachedtothecontrolhasafalsevalue

Accordingtothistable,wecandefinethatwewantalltheinputcontrolswithinvalidvaluetohavearedborderinthefollowingway:

input.ng-dirty.ng-invalid{

border:1pxsolidred;

}

TheexactsemanticsbehindtheprecedingCSSinthecontextofAngular2istousearedborderforalltheinputelementswhosevalueshavebeenchangedandareinvalidaccordingtothevalidatorsattachedtothem.

Now,let’sexplorehowwecanattachdifferentvalidationbehaviortoourcontrols.

Usingthebuilt-informvalidatorsWealreadysawthatwecanaltervalidationbehaviortoanycontrolbyusingtherequiredattribute.Angular2providestwomorebuilt-invalidators,asfollows:

minlength:Allowsustospecifytheminimumlengthofthevaluethatagivencontrolshouldhave.maxlength:Allowsustospecifythemaximumlengthofthevaluethatagivencontrolshouldhave.

ThesevalidatorsaredefinedwithAngular2directivesandcanbeusedinthefollowingway:

<inputid="realNameInput"class="form-control"

type="text"ngControl="realName"

minlength="2"

maxlength="30">

Thisway,wespecifythatwewantthevalueoftheinputtobebetween2and30characters.

DefiningcustomcontrolvalidatorsAnotherdatapropertydefinedintheDeveloperclassistheemailfield.Let’saddaninputfieldforthisproperty.Abovethebuttonintheprecedingform,addthefollowingmarkup:

<divclass="form-group">

<labelclass="control-label"for="emailInput">Email</label>

<div>

<inputid="emailInput"

class="form-control"

type="text"ngControl="email"

[(ngModel)]="developer.email"/>

</div>

</div>

Wecanthinkofthe[(ngModel)]attributeasanalternativetotheng-modeldirectivefromAngularJS1.x.WewillexplainitindetailintheTwo-waydatabindingwithAngular2section.

AlthoughAngular2providesasetofpredefinedvalidators,theyarenotenoughforallthevariousformatsourdatacanlivein.Sometimes,we’llneedcustomvalidationlogicforourapplication-specificdata.Forinstance,inthiscase,wewanttodefineane-mailvalidator.Atypicalregularexpression,whichworksingeneralcases(butdoesnotcovertheentirespecificationthatdefinestheformatofthee-mailaddresses),looksasfollows:/^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/.

Inch6/ts/step-1/add_developer.ts,defineafunctionthatacceptsaninstanceofAngular2controlasanargumentandreturnsnullifthecontrol’svalueisemptyormatchestheregularexpressionmentionedearlier,and{'invalidEmail':true}otherwise:

functionvalidateEmail(emailControl){

if(!emailControl.value||

/^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-

9-.]+$/.test(emailControl.value)){

returnnull;

}else{

return{'invalidEmail':true};

}

}

Now,fromthemodulesangular2/commonandangular2/coreimportNG_VALIDATORSandDirective,andwrapthisvalidationfunctionwithinthefollowingdirective:

@Directive({

selector:'[email-input]',

providers:[provide(NG_VALIDATORS,{

useValue:validateEmail,multi:true

})]

})

classEmailValidator{}

Intheprecedingcode,wedefinedasinglemultiproviderforthetokenNG_VALIDATORS.

Onceweinjectthevalueassociatedwiththistoken,we’llgetanarraywithallthevalidatorsattachedtothegivencontrol(forreference,takealookatthesectionformultiprovidersinChapter5,DependencyInjectioninAngular2).

Theonlytwostepsleftinordertomakeourcustomvalidationworkaretofirstaddtheemail-inputattributetothee-mailcontrol:

<inputid="emailInput"

class="form-control"

email-input

type="text"ngControl="email"

[(ngModel)]="developer.email"/>

Next,toaddthedirectivetothelistusedbythecomponentAddDeveloperdirectives:

@Component({

selector:'dev-add',

templateUrl:'./add_developer.html',

styles:[`

input.ng-touched.ng-invalid{

border:1pxsolidred;

}

`],

directives:[FORM_DIRECTIVES,EmailValidator],

providers:[FORM_PROVIDERS]

})

classAddDeveloper{…}

NoteWe’reusinganexternaltemplatefortheAddDevelopercontrol.There’snoultimateanswertowhetheragiventemplateshouldbeexternalizedorinlinedwithinthecomponentwithtemplateUrlortemplate,respectively.Thebestpracticestatesthatweshouldinlinetheshorttemplatesandexternalizethelongerones,butthere’snospecificdefinitionastowhichtemplatesareconsideredshortandwhicharelong.Thedecisiononwhetherthetemplateshouldbeusedinlineorputintoanexternalfiledependsonthedeveloper’spersonalpreferencesorcommonconventionswithintheorganization.

UsingselectinputswithAngularAsthenextstep,weshouldallowtheuseroftheapplicationtoenterthetechnologyintowhichtheinputdeveloperhasthemostproficiency.Wecandefinealistoftechnologiesandshowthemintheformasaselectinput.

IntheAddDeveloperclass,addthetechnologiesproperty:

classAddDeveloper{

technologies:string[]=[

'JavaScript',

'C',

'C#',

'Clojure'

];

}

Nowinthetemplate,justabovethesubmitbutton,addthefollowingmarkup:

<divclass="form-group">

<labelclass="control-label"

for="technologyInput">Technology</label>

<div>

<selectclass="form-control"

ngControl="technology"required

[(ngModel)]="developer.technology">

<option*ngFor="#toftechnologies"

[value]="t">{{t}}</option>

</select>

</div>

</div>

Justlikefortheinputelementswedeclaredearlier,Angular2willaddthesameclassesdependingonthestateoftheselectinput.Inordertoshowredborderaroundtheselectelementwhenitsvalueisinvalid,weneedtoaltertheCSSrules:

@Component({

styles:[

`input.ng-touched.ng-invalid,

select.ng-touched.ng-invalid{

border:1pxsolidred;

}`

],

})

classAddDeveloper{…}

NoteNoticethatinliningallthestylesinourcomponents’declarationcouldbeabadpractice,becausethisway,theywon’tbereusable.Whatwecandoisextractallthecommonstylesacrossourcomponentsintoseparatefiles.The@Componentdecoratorhasapropertycalled

styleUrlsoftypearraywherewecanaddareferencetotheextractedstylesusedbythegivencomponent.Thisway,wecaninlineonlythecomponent-specificstylesifrequired.

Rightafterthis,wewilldeclarethenameofthecontroltobeequalto“technology”usingngControl="technology".Byusingtherequiredattribute,wewilldeclarethattheuseroftheapplicationmustspecifythetechnologyintowhichthecurrentdeveloperisproficient.Let’sskipthe[(ngModel)]attributeforthelasttimeandseehowwecandefinetheselectelement’soptions.

Insidetheselectelement,wewilldefinethedifferentoptionsusing:

<option*ngFor="#toftechnologies"

[value]="t">{{t}}</option>

Thisisasyntaxwe’realreadyfamiliarwith.WewillsimplyiterateoverallthetechnologiesdefinedwithintheAddDeveloperclass,andforeachtechnology,wewillshowanoptionelementwithavalueofthetechnologyname.

UsingtheNgFormdirectiveWealreadymentionedthattheformdirectiveenhancestheHTML5form’sbehaviorbyaddingsomeadditionalAngular2-specificlogic.Now,let’stakeastepbackandtakealookattheformthatsurroundstheinputelements:

<form#f="ngForm"(ngSubmit)="addDeveloper()"

class="formcol-md-4"[hidden]="submitted">

</form>

Intheprecedingsnippet,wedefinedanewidentifiercalledf,whichreferencestotheform.Wecanthinkoftheformasacompositionofcontrols;wecanaccesstheindividualcontrolsthroughtheform’scontrolsproperty.Ontopofthis,theformhasthetouched,untouched,pristine,dirty,invalid,andvalidproperties,whichdependontheindividualcontrolsdefinedwithintheform.Forexample,ifnoneofthecontrolswithintheformhasbeentouched,thentheformitselfisgoingtobewiththestatusuntouched.However,ifanyofthecontrolsintheformhasbeentouchedatleastonce,theformwillbewiththestatustouchedaswell.Similarlytheformwillbevalidonlyifallitscontrolsarevalid.

Inordertoillustratetheusageoftheformelement,let’sdefineacomponentwiththeselectorcontrol-errors,whichshowsthecurrenterrorsforagivencontrol.Wecanuseitinthefollowingway:

<labelclass="control-label"for="realNameInput">Realname</label>

<div>

<inputid="realNameInput"class="form-control"type="text"

ngControl="realName"[(ngModel)]="developer.realName"

requiredmaxlength="50">

<control-errorscontrol="realName"

[errors]="{

'required':'Realnameisrequired',

'maxlength':'Themaximumlengthoftherealnameis50characters'

}"

/>

</div>

Noticethatwe’vealsoaddedthemaxlengthvalidatortotherealNamecontrol.

Thecontrol-errorselementhasthefollowingproperties:

control:Declaresthenameofthecontrolwewanttoshowerrorsfor.errors:Createsamappingbetweencontrolerrorandanerrormessage.

Nowaddthefollowingimportsinadd_developer.ts:

import{NgControl,NgForm}from'angular2/common';

import{Host}from'angular2/core';

Intheseimports,theNgControlclassistheabstractclassthatrepresentstheindividualformcomponents,NgFormrepresentstheAngularforms,andHostisaparameterdecoratorrelatedtothedependencyinjectionmechanism,whichwealreadycoveredinChapter5,DependencyInjectioninAngular2.

Hereisapartofthecomponent’sdefinition:

@Component({

template:'<div>{{currentError}}</div>',

selector:'control-errors',

inputs:['control','errors']

})

classControlErrors{

errors:Object;

control:string;

constructor(@Host()privateformDir:NgForm){}

getcurrentError(){…}

}

TheControlErrorscomponentdefinestwoinputs:control—thenameofthecontroldeclaredwiththengControldirective(thevalueofthengControlattribute)—anderrors—themappingbetweenanerrorandanerrormessage.Theycanbespecifiedrespectivelybythecontrolandtheerrorsattributesofthecontrol-errorselement.

Forinstance,ifwehavecontrol:

<inputtype="text"ngControl="foobar"required/>

Wecandeclareitsassociatedcontrol-errorscomponentbyusingthefollowing:

<control-errorscontrol="foobar"

[errors]="{

'required':'Thevalueoffoobarisrequired'

}"></control-errors>

InsideofthecurrentErrorgetter,intheprecedingsnippet,weneedtodothefollowingtwothings:

Findareferencetothecomponentdeclaredwiththecontrolattribute.Returntheerrormessageassociatedwithanyoftheerrorsthatmakethecurrentcontrolinvalid.

Hereisasnippetthatimplementsthisbehavior:

@Component(…)

classControlErrors{

getcurrentError(){

letcontrol=this.formDir.controls[this.control];

leterrorsMessages=[];

if(control&&control.touched){

errorsMessages=Object.keys(this.errors)

.map(k=>control.hasError(k)?this.errors[k]:null)

.filter(error=>!!error);

}

returnerrorsMessages.pop();

}

}

InthefirstlineoftheimplementationofcurrentError,wegetthetargetcontrolbyusingthecontrolspropertyoftheinjectedform.Itisofthetype{[key:string]:

AbstractControl},wherethekeyisthenameofthecontrolwe’vedeclaredwiththengControldirective.Oncewehaveareferencetotheinstanceofthetargetcontrol,wecancheckwhetheritsstatusistouched(thatis,whetherithasbeenfocused),andifitis,wecanloopoveralltheerrorswithintheerrorspropertyoftheinstanceofControlError.Themapfunctionwillreturnanarraywitheitheranerrormessageoranullvalue.Theonlythingleftistofilterallthenullvaluesandgetonlytheerrormessages.Oncewegettheerrormessagesforeacherror,wewillreturnthelastonebypoppingitfromtheerrorMessagesarray.

Theendresultshouldlookasfollows:

Fig.5

IfyouexperienceanyproblemsduringtheimplementationoftheControlErrorscomponent,youcantakealookatitsimplementationatch6/ts/multi-page-template-driven/add_developer.ts.

ThehasErrormethodofeverycontrolacceptsasanargumentanerrormessageidentifier,whichisdefinedbythevalidator.Forinstance,intheprecedingexamplewherewedefinedthecustome-mailvalidator,wewillreturnthefollowingobjectliteralwhentheinputcontrolhasaninvalidvalue:{'invalidEmail':true}.IfweapplytheControlErrorscomponenttothee-mailcontrol,itsdeclarationshouldlookasfollows:

<control-errorscontrol="email"

[errors]="{'invalidEmail':'Invalidemailaddress'}"/>

Two-waydata-bindingwithAngular2OneofthemostfamousrumorsaboutAngular2wasthatthetwo-waydata-bindingfunctionalitywasremovedbecauseoftheenforcedunidirectionaldataflow.Thisisnotexactlytrue;theAngular2’sformmoduleimplementsadirectivewiththeselector[(ngModel)],whichallowsustoeasilyachievedata-bindingintwodirections—fromtheviewtothemodelandfromthemodeltotheview.

Let’stakealookatthefollowingsimplecomponent:

//ch6/ts/simple-two-way-data-binding/app.ts

import{Component}from'angular2/core';

import{bootstrap}from'angular2/platform/browser';

import{NgModel}from'angular2/common';

@Component({

selector:'app',

directives:[NgModel],

template:`

<inputtype="text"[(ngModel)]="name"/>

<div>{{name}}</div>

`,

})

classApp{

name:string;

}

bootstrap(App,[]);

Intheprecedingexample,weimportedthedirectiveNgModelfromtheangular2/commonpackage.Later,inthetemplate,wesettheattribute[(ngModel)]tothevaluename.

Atfirst,thesyntax[(ngModel)]mightseemalittlebitunusual.FromChapter4,GettingStartedwithAngular2ComponentsandDirectives,weknowthatthesyntax(eventName)isusedforbindingtoevents(oroutputs)triggeredbyagivencomponent.Ontheotherhand,weusethesyntax[propertyName]="foobar"toachieveone-waydata-bindingbysettingthevalueoftheproperty(orintheterminologyoftheAngular2components,theinput)withthenamepropertyNametotheresultoftheevaluationoftheexpressionfoobar.ThesyntaxNgModeljustcombinesbothinordertoachievedata-bindingintwodirections.That’swhywecanthinkofitmorelikeasyntaxsugar,ratherthananewconcept.OneofthemainadvantagesofthissyntaxcomparedtoAngularJS1.xisthatwecantellwhichbindingsareone-wayandwhicharetwo-wayonlybylookingatthetemplate.

NoteJustlike(click)hasitscanonicalsyntaxon-clickand[propertyName]hasbind-propertyName,thealternativesyntaxof[(ngModel)]isbindon-ngModel.

Ifyouopenhttp://localhost:5555/dist/dev/ch6/ts/simple-two-way-data-

binding/,youwillseethefollowingresult:

Fig.6

Onceyouchangethevalueoftheinputbox,itwillautomaticallyupdatethefollowinglabel.

WealreadyusedtheNgModeldirectiveintheprecedingtemplates.Forexample,weboundtothedeveloper’se-mailusing:

<inputid="emailInput"

class="form-control"type="text"

ngControl="email"[(ngModel)]="developer.email"

email-input/>

Thisway,thevalueofthee-mailpropertyofthedeveloperobjectattachedtotheAddDevelopercomponent’sinstanceisgoingtobeupdatedoncewechangethevalueofthetextinput.

StoringtheformdataLet’speekattheinterfaceoftheAddDevelopercomponent’scontrolleragain:

exportclassAddDeveloper{

submitted:false;

successMessage:string;

developer=newDeveloper();

//…

constructor(privatedevelopers:DeveloperCollection){}

addDeveloper(form){…}

}

IthasafieldofthetypeDeveloper,andwebindtheformcontrolstoitspropertiesusingtheNgModeldirective.TheclassalsohasamethodcalledaddDeveloper,whichisbeinginvokedonthesubmissionoftheform.Wedeclarethisbybindingtothesubmiteventusing:

<!--ch6/ts/multi-page-template-driven/add_developer.html-->

<form#f="form"(ngSubmit)="addDeveloper()"

class="formcol-md-4"[hidden]="submitted">

<buttonclass="btnbtn-default"

type="submit"[disabled]="!f.form.valid">Add</button>

</form>

Intheprecedingsnippet,wecannoticetwomorethings.Wegotareferencetotheformusing#f="ngForm"andweboundthedisabledpropertyofthebuttontotheexpression:!f.form.valid.WealreadydescribedtheNgFormcontrolintheprevioussection;itsvalidpropertywillhaveavaluetrueonceallthecontrolswithintheformhavevalidvalues.

Now,let’ssupposewe’veenteredvalidvaluesforalltheinputcontrolsintheform.Thismeansthatitssubmitbuttonwillbeenabled.OncewepressEnterorweclickontheAddbutton,theaddDevelopermethodwillbeinvoked.Thefollowingisasampleimplementationofthismethod:

classAddDeveloper{

//…

addDeveloper(){

this.developer.id=this.developers.getAll().length+1;

this.developers.addDeveloper(this.developer);

this.successMessage=`Developer${this.developer.realName}was

successfullyadded`;

this.submitted=true;

}

Initially,wesettheidpropertyofthecurrentdevelopertoequalthetotalnumberofdevelopersinDeveloperCollection,plusone.Later,weaddedthedevelopertothecollectionandsetthevalueofthesuccessMessageproperty.Rightafterthis,wesetthepropertysubmittedtoequaltotrue,whichwillresultinhidingtheform.

ListingallthestoreddevelopersNowthatwecanaddanewentrytothedevelopers’collection,let’sshowalistofallthedevelopersonthefrontpageofthe“Codersrepository”.

Openthefilech6/ts/step-1/home.tsandenterthefollowingcontent:

import{Component}from'angular2/core';

import{DeveloperCollection}from'./developer_collection';

@Component({

selector:'home',

templateUrl:'./home.html'

})

exportclassHome{

constructor(privatedevelopers:DeveloperCollection){}

getDevelopers(){

returnthis.developers.getAll();

}

}

Thereisnothingnewtoushere.WeextendthefunctionalityoftheHomecomponentbyprovidinganexternaltemplateandimplementingthegetDevelopersmethod,whichdelegatesitscalltotheinstanceofDeveloperCollectionthatisinjectedintheconstructor.

Thetemplateitselfissomethingthatwe’realreadyfamiliarwithaswell:

<tableclass="table"*ngIf="getDevelopers().length>0">

<thead>

<th>Email</th>

<th>Realname</th>

<th>Technology</th>

<th>Popular</th>

</thead>

<tr*ngFor="#devofgetDevelopers()">

<td>{{dev.email}}</td>

<td>{{dev.realName}}</td>

<td>{{dev.technology}}</td>

<td[ngSwitch]="dev.popular">

<span*ngSwitchWhen="true">Yes</span>

<span*ngSwitchWhen="false">Notyet</span>

</td>

</tr>

</table>

<div*ngIf="getDevelopers().length==0">

Therearenoanydevelopersyet

</div>

WelistallthedevelopersasrowswithinanHTMLtable.Foreachdeveloper,wecheckthestatusofitspopularflag.Ifitsvalueistrue,thenforthePopularcolumn,weshowaspanwiththetextYes,otherwisewesetthetexttoNo.

WhenyouenterafewdevelopersintheAddDeveloperpageandyounavigatetothe

homepageafterthat,youshouldseearesultsimilartothefollowingscreenshot:

Fig.7

NoteYoucanfindthecompletefunctionalityoftheapplicationatch6/ts/multi-page-template-driven.

SummarySofar,wehaveexplainedthebasicsofroutinginAngular2.Wetookalookathowwecandefinedifferentroutesandimplementthecomponentsassociatedwiththemthataredisplayedonroutechange.Inordertolinktothedifferentroutes,weexplainedrouterLink,andwealsousedtherouter-outletdirectivesforpointingoutwherethecomponentsassociatedwiththeindividualroutesshouldberendered.

AnotherthingwetookalookatwastheAngular2formsfunctionalitywithbuilt-inandcustomvalidation.Afterthis,weexplainedtheNgModeldirective,whichprovidestoustwo-waydata-binding.

Inthenextchapter,wewillcoverhowwecandevelopmodel-drivenformsandchildandparametrizedroutes,usetheHttpmoduleformakingRESTfulcalls,andtransformdatawithcustompipes.

Chapter7.ExplainingPipesandCommunicatingwithRESTfulServicesInthelastchapter,wecoveredsomeverypowerfulfeaturesoftheframework.However,wecangoevendeeperintothefunctionalityofAngular’sformsmoduleandrouter.Inthenextsections,we’llexplainhowwecan:

Developmodel-drivenforms.Defineparameterizedroutes.Definechildroutes.UsetheHttpmoduleforcommunicationwithRESTfulAPIs.Transformdatawithcustompipes.

Wewillexplorealltheseconceptsintheprocessofextendingthefunctionalityofthe“Codersrepository”application.Atthebeginningofthepreviouschapter,wementionedthatwe’regoingtoallowimportofdevelopersfromGitHub.Butbeforeweimplementthisfeature,let’sextendthefunctionalityoftheform.

Developingmodel-drivenformsinAngular2Thesearegoingtobethelaststepsinfinishingthe“Codersrepository”.Youcanbuildontopofthecodeatch6/ts/step-1/(orch6/ts/step-2dependingonyourpreviouswork)inordertoextendtheapplication’sfunctionalitywiththenewconceptswe’regoingtocover.Thecompleteexampleislocatedatch7/ts/multi-page-model-driven.

Thisistheresultthatwearegoingtoachievebytheendofthissection:

Intheprecedingscreenshot,therearethefollowingtwoforms:

AformforimportingexistingusersfromGitHubthatcontains:

TheinputfortheGitHubhandle.AcheckboxthatpointsoutwhetherwewanttoimportthedeveloperfromGitHuborenteritmanually.

Aformforenteringnewusersmanually.

Thesecondformlooksexactlythewaywefinisheditinthelastsection.However,this

time,itsdefinitionlooksalittlebitdifferent:

<formclass="formcol-md-4"

[ngFormModel]="addDevForm"[hidden]="submitted">

<!--TODO-->

</form>

Noticethatthistime,wedon’thavethesubmithandlerorthe#f="ngForm"attribute.Instead,weusethe[ngFormModel]attributeinordertobindtoapropertydefinedinsidethecomponent’scontroller.Byusingthisattribute,wecanbindtosomethingcalledControlGroup.Asitsnamestates,theControlGroupclassconsistsofalistofcontrolsgroupedtogetherwiththesetsofvalidationrulesassociatedwiththem.

Weneedtouseasimilardeclarationtoimportadeveloperform.However,thistime,wewillprovideadifferentvalueofthe[ngFormModel]attribute,sincewearegoingtodefineadifferentcontrolgroupinthecomponent’scontroller.Placethefollowingsnippetabovetheformweintroducedearlier:

<formclass="formcol-md-4"

[ngFormModel]="importDevForm"[hidden]="submitted">

<!--TODO-->

</form>

Now,let’sdeclaretheimportDevFormandaddDevFormpropertiesinthecomponent’scontroller:

import{ControlGroup}from'angular2/common';

@Component(…)

exportclassAddDeveloper{

importDevForm:ControlGroup;

addDevForm:ControlGroup;

constructor(privatedevelopers:DeveloperCollection,

fb:FormBuilder){…}

addDeveloper(){…}

}

Initially,weimportedtheControlGroupclassfromtheangular2moduleand,later,declaredtherequiredpropertiesinthecontroller.Let’salsonoticethatwehaveoneadditionalparameteroftheconstructorofAddDevelopercalledfbofthetypeFormBuilder.

FormBuilderprovidesaprogrammableAPIforthedefinitionofControlGroupswherewecanattachvalidationbehaviortoeachcontrolinthegroup.Let’susetheFormBulderinstancefortheinitializationoftheimportDevFormandaddDevFormproperties:

constructor(privatedevelopers:DeveloperCollection,

fb:FormBuilder){

this.importDevForm=fb.group({

githubHandle:['',Validators.required],

fetchFromGitHub:[false]

});

this.addDevForm=fb.group({

realName:['',Validators.required],

email:['',validateEmail],

technology:['',Validators.required],

popular:[false]

});

}

TheFormBuilderinstancehasamethodcalledgroupthatallowsustodefineproperties,suchasthedefaultvaluesandthevalidatorsfortheindividualcontrolsinagivenform.

Accordingtotheprecedingsnippet,importDevFormhastwofieldsthatweintroducedearlier:githubHandleandfetchFromGitHub.WedeclaredthatthevalueofthegithubHandlecontrolisrequiredandsetthedefaultvalueofthecontrolfetchFromGitHubtofalse.

Inthesecondform,addDevForm,wedeclarefourcontrols.FortherealNamecontrolasthedefaultvalue,wesettheemptystringanduseValidators.requredinordertointroducevalidationbehavior(whichisexactlywhatwedidforthegithubHandlecontrol).Asavalidatorforthee-mailinput,wewillusethevalidateEmailfunctionandsetitsinitialvaluetoanemptystring.ThevalidateEmailfunctionusedforvalidationistheonewedefinedinthepreviouschapter:

functionvalidateEmail(emailControl){

if(!emailControl.value||

/^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-

9-.]+$/.test(emailControl.value)){

returnnull;

}else{

return{'invalidEmail':true};

}

}

Thelasttwocontrolswedefineherearethetechnologycontrol,whichvalueisrequiredandhasanemptystringasitsinitialvalue,andthepopularcontrolwithitsinitialvaluesettofalse.

UsingcompositionofcontrolvalidatorsWetookalookathowwecanapplyasinglevalidatortoformcontrols.However,insomeapplications,thedomainmayrequiremorecomplexvalidationlogic.Forexample,ifwewanttoapplyboththerequiredandthevalidateEmailvalidatorstothee-mailcontrol,weshoulddothefollowing:

this.addDevForm=fb.group({

email:['',Validators.compose([

Validators.required,

validateEmail]

)],

});

ThecomposemethodoftheValidatorsobjectacceptsasanargumentanarrayofvalidatorsandreturnsanewvalidator.Thenewvalidator’sbehaviorisgoingtobeacompositionofthelogicdefinedintheindividualvalidatorspassedasanargument,andtheyaregoingtobeappliedinthesameorderastheywereintroducedinthearray.

ThepropertynamesintheobjectliteralpassedtothegroupmethodshouldmatchwiththevaluesthatwesettothengControlattributesoftheinputsinthetemplate.

ThisisthecompletetemplateofimportDevForm:

<formclass="formcol-md-4"

[ngFormModel]="importDevForm"[hidden]="submitted">

<divclass="form-group">

<labelclass="control-label"

for="githubHandleInput">GitHubhandle</label>

<div>

<inputid="githubHandleInput"

class="form-control"type="text"

[disabled]="!fetchFromGitHub"

ngControl="githubHandle">

<control-errorscontrol="githubHandle"

[errors]="{

'required':'TheGitHubhandleisrequired'

}"></control-errors>

</div>

</div>

<divclass="form-group">

<labelclass="control-label"

for="fetchFromGitHubCheckbox">

FetchfromGitHub

</label>

<inputclass="checkbox-inline"id="fetchFromGitHubCheckbox"

type="checkbox"ngControl="fetchFromGitHub"

[(ngModel)]="fetchFromGitHub">

</div>

</form>

Intheprecedingtemplate,youcannoticethatoncetheflagsubmittedhasthevaluetrue,

sotheformwillbehiddenfromtheuser.Nexttothefirstinputelement,wesetthevalueofthengControlattributetogithubHandle.

NoteNotethatthevalueofthengControlattributeofthegiveninputelementmustmatchthenameweusedforitscorrespondingcontroldeclarationinthedefinitionofControlGroupwithinthecomponent’scontroller.

WithregardtothegithubHandlecontrol,wealsosetthedisabledattributetoequaltheresultoftheevaluationoftheexpression:!fetchFromGitHub.Thisway,whenthefetchFromGitHubcheckboxisunchecked,thegithubHandlecontrolwillbedisabled.Similarly,incaseoftheexampleintheprevioussections,weusedtheControlErrorscomponentwedefinedpreviously.Thistime,wesetasingleerrorwiththemessageTheGitHubhandleisrequired.

ThemarkupfortheformaddDevFormlooksquitesimilar,sowewon’tdescribeitindetailhere.Ifyou’renotcompletelysureofhowtoapproachdevelopingit,youcantakealookatthecompleteimplementationatch7/ts/multi-page-model-driven/add_developer.html.

Thelastpartofthetemplatewe’regoingtotakealookatistheSubmitbutton:

<buttonclass="btnbtn-default"

(click)="addDeveloper()"

[disabled]="(fetchFromGitHub&&!importDevForm.valid)||

(!fetchFromGitHub&&!addDevForm.valid)">

Add

</button>

ClickingonthebuttonwillinvoketheaddDevelopermethoddefinedinthecomponent’scontroller.Intheexpressionsetasvalueofthe[disabled]attribute,weinitiallycheckwhichformisselectedbyusingthevalueofthepropertyboundtothecheckbox,thatis,weverifywhethertheuserwantstoaddanewdeveloperorimportanexistingonefromGitHub.Ifthefirstoptionisselected(thatis,ifthecheckboxisnotchecked),weverifywhethertheControlGroupforaddinganewdeveloperisvalid.Ifitisvalid,thenthebuttonwillbeenabled,otherwiseitwillbedisabled.WewilldothesameincaseswhentheuserhascheckedthecheckboxforimportingadeveloperfromGitHub.

ExploringtheHTTPmoduleofAngularNow,afterwedeveloptheformsforbothimportingexistingandaddingnewdevelopers,itisthetimetoimplementthelogicbehinditinthecontrollerofthecomponent.

Forthispurpose,weneedtocommunicatewiththeGitHubAPI.Althoughwecandothisdirectlyfromthecomponent’scontroller,bydoingitthisway,wecancoupleitwiththeRESTfulAPIofGitHub.Inordertoenforcefurtherseparationofconcerns,wecanextractthelogicforcommunicationwithGitHubintoaseparateservicecalledGitHubGateway.Openafilecalledgithub_gateway.tsandenterthefollowingcontent:

import{Injectable}from'angular2/core';

import{Http}from'angular2/http';

@Injectable()

exportclassGitHubGateway{

constructor(privatehttp:Http){}

getUser(username:string){

returnthis.http

.get(`https://api.github.com/users/${username}`);

}

}

Initially,weimportedtheHttpclassfromtheangular2/httpmodule.AlltheHTTP-relatedfunctionalityisexternalizedandisoutsidetheAngular’score.SinceGitHubGatewayacceptsadependency,whichneedstobeinjectedthroughtheDImechanismoftheframework,wewilldecorateitwiththe@Injectabledecorator.

TheonlyfunctionalityfromtheGitHub’sAPIwe’regoingtouseistheoneforfetchingusers,sowewilldefineasinglemethodcalledgetUser.Asanargument,itacceptstheGitHubhandleofthedeveloper.

NoteNotethatifyoumakemorethan60requestsperdaytotheGitHub’sAPI,youmightgettheerrorGitHubAPIRatelimitexceeded.ThisisduetotheratelimitsforrequestswithoutaGitHubAPItoken.Forfurtherinformation,visithttps://github.com/blog/1509-personal-api-tokens.

InsidethegetUsermethod,weusetheinstanceoftheHttpservicethatwe’vereceivedintheconstructorfunction.TheHttpservice’sAPIstaysasclosetotheHTML5fetchAPIaspossible.However,thereareacoupleofdifferences.Themostsignificantoneofthemisthatatthemomentofwritingthiscontent,allthemethodsoftheHttpinstancesreturnObservablesinsteadofPromises.

TheHttpserviceinstanceshavethefollowingAPI:

request(url:string|Request,options:RequestOptionsArgs):MakesarequesttothespecifiedURL.TherequestcanbeconfiguredusingRequestOptionsArgs:

http.request('http://example.com/',{

method:'get',

search:'foo=bar',

headers:newHeaders({

'X-Custom-Header':'Hello'

})

});

get(url:string,options?:RequestOptionsArgs):MakesagetrequesttothespecifiedURL.Therequestheadersandotheroptionscanbeconfiguredusingthesecondargument.post(url:string,options?:RequestOptionsArgs):MakesapostrequesttothespecifiedURL.Therequestbody,headers,andotheroptionscanbeconfiguredusingthesecondargument.put(url:string,options?:RequestOptionsArgs):MakesaputrequesttothespecifiedURL.Therequestheadersandotheroptionscanbeconfiguredusingthesecondargument.patch(url:string,options?:RequestOptionsArgs):MakesapatchrequesttothespecifiedURL.Therequestheadersandotheroptionscanbeconfiguredusingthesecondargument.delete(url:string,options?:RequestOptionsArgs):MakesadeleterequesttothespecifiedURL.Therequestheadersandotheroptionscanbeconfiguredusingthesecondargument.head(url:string,options?:RequestOptionsArgs):MakesaheadrequesttothespecifiedURL.Therequestheadersandotheroptionscanbeconfiguredusingthesecondargument.

UsingAngular’sHTTPmoduleNow,let’simplementthelogicforimportingexistingusersfromGitHub!Openthefilech6/ts/step-2/add_developer.tsandenterthefollowingimports:

import{Response,HTTP_PROVIDERS}from'angular2/http';

import{GitHubGateway}from'./github_gateway';

AddHTTP_PROVIDERSandGitHubGatewaytothelistofprovidersoftheAddDevelopercomponent:

@Component({

providers:[GitHubGateway,FORM_PROVIDERS,HTTP_PROVIDERS]

})

classAddDeveloper{…}

Asthenextstep,wehavetoincludethefollowingparametersintheconstructoroftheclass:

constructor(privategithubAPI:GitHubGateway,

privatedevelopers:DeveloperCollection,

fb:FormBuilder){

//…

}

Thisway,theAddDeveloperclass’instanceswillhaveaprivatepropertycalledgithubAPI.

TheonlythingleftistoimplementtheaddDevelopermethodandallowtheusertoimportexistingdevelopersbyusingtheGitHubGatewayinstance.

OncetheuserpressestheAddbutton,weneedtocheckwhetherweneedtoimportanexistingGitHubuseroraddanewdeveloper.Forthispurpose,wecanusethevalueofthefetchFromGitHubcontrol:

if(this.importDevForm.controls['fetchFromGitHub'].value){

//Importdeveloper

}else{

//Addnewdeveloper

}

Ifithasatruthyvalue,thenwecaninvokethegetUsermethodofthegithubAPIpropertyandpassthevalueofthegithubHandlecontrolasanargument:

this.githubAPI.getUser(model.githubHandle)

InthegetUsermethod,wedelegatethecalltotheHttpservice’sgetmethod,whichreturnsanobservable.Inordertogettheresultthattheobservableisgoingtopush,weneedtopassacallbacktoitssubscribemethod:

this.githubAPI.getUser(model.githubHandle)

.map((r:Response)=>r.json())

.subscribe((res:any)=>{

//"res"containstheresponseoftheGitHub'sAPI

});

Intheprecedingsnippet,wefirstestablishtheHTTPgetrequest.Afterthis,we’llgettheobservablethat,ingeneralcases,willemitaseriesofvalues(inthiscase,onlyasingleone—theresponseoftherequest)andmapthemtotheJSONrepresentationoftheirbodies.IftheresponsefailsoritsbodyisnotavalidJSONstring,thenwewillgetanerror.

NoteNotethatinordertoreducethesizeofRxJS,Angular’scoreteamhasincludedonlyitscore.Inordertousethemethodsmapandcatch,youneedtoaddthefollowingimportsatadd_developer.ts:

import'rxjs/add/operator/map';

import'rxjs/add/operator/catch';

Nowlet’simplementthebodyofthesubscribecallback:

letdev=newDeveloper();

dev.githubHandle=res.login;

dev.email=res.email;

dev.popular=res.followers>=1000;

dev.realName=res.name;

dev.id=res.id;

dev.avatarUrl=res.avatar_url;

this.developers.addDeveloper(dev);

this.successMessage=`Developer${dev.githubHandle}successfullyimported

fromGitHub`;

Intheprecedingexample,wesetthepropertiesofanewDeveloperinstance.Here,weestablishedthemappingbetweentheobjectreturnedfromGitHub’sAPIandthedeveloper’srepresentationinourapplication.Wealsoconsideredadeveloperaspopularifsheorhehasabove1,000followers.

TheentireimplementationoftheaddDevelopermethodcanbefoundatch7/ts/multi-page-model-driven/add_developer.ts.

NoteInordertohandlefailedrequests,wecanusethecatchmethodoftheobservableinstances:

this.githubAPI.getUser(model.githubHandle)

.catch((error,source,caught)=>{

console.log(error)

returnerror;

})

DefiningparameterizedviewsAsthenextstep,let’sdedicateaspecialpageforeachdeveloper.Insideofit,we’llbeabletotakeadetailedlookathisorherprofile.Oncetheuserclicksonthenameofanyofthedevelopersonthehomepageoftheapplication,heorsheshouldberedirectedtoapagewithadetailedprofileoftheselecteddeveloper.Theendresultwilllookasfollows:

Inordertodothis,weneedtopassanidentifierofthedevelopertothecomponentthatshowsdeveloper’sdetailedprofile.Openapp.tsandaddthefollowingimport:

import{DeveloperDetails}from'./developer_details';

Wehaven’tdevelopedtheDeveloperDetailscomponentyet,soifyouruntheapplication,youwillgetanerror.Wewilldefinethecomponentinthenextparagraph,butbeforethis,let’salterthe@RouteConfigdefinitionoftheAppcomponent:

@RouteConfig([

//…

newRoute({

component:DeveloperDetails,

name:'DeveloperDetails',

path:'/dev-details/:id/...'

}),

//…

])

classApp{}

WeaddedasingleroutewiththeDeveloperDetailscomponentassociatedwithit,andasanalias,weusedthestring"DeveloperDetails".

Thevalueofthecomponentpropertyisareferencetotheconstructorofthecomponent,whichshouldhandlethegivenroute.Oncethesourcecodeoftheapplicationgetsminifiedforproduction,thecomponentnamemaydifferfromtheonewe’veentered.ThiswillcreateproblemswhenreferencingtheroutewithinthetemplatesusingtherouterLinkdirective.Inordertopreventthisfromhappening,thecoreteamintroducedthenamepropertythat,inthiscase,equalstothenameofthecontroller.

NoteAlthoughinalltheexamplessofar,wesetthealiasoftheroutetobethesameasthenameofthecomponent’scontroller,thisisnotrequired.Thisconventionisusedforsimplicity,sinceitcouldbeconfusingtointroducetwonames:oneforpointingtotherouteandanotheroneforthecontrollerassociatedwiththegivenroute.

Inthepathproperty,wedeclarethattheroutehasasingleparametercalledid,andwith"...",wehinttheframeworkthatthisroutewillhavenestedroutesinsideofit.

Now,let’spasstheidofthecurrentdeveloperasaparametertotherouterLinkdirective.Openhome.htmlinyourworkingdirectoryandreplacethetablecellwherewedisplaythedeveloper’srealNamepropertywiththefollowingcontent:

<td>

<a[routerLink]="['/DeveloperDetails',

{'id':dev.id},'DeveloperBasicInfo']">

{{dev.realName}}

</a>

</td>

ThevalueoftherouterLinkdirectiveisanarraywiththefollowingthreeelements:

'/DeveloperDetails':Astringthatshowstherootroute{'id':dev.id}:Anobjectliteralthatdeclarestherouteparameters'DeveloperBasicInfo':ThenameofaroutethatshowswhichcomponentwithinthenestedrouteinthecomponentwiththealiasDeveloperDetailsshouldberendered

DefiningnestedroutesNowlet’sjumptotheDeveloperDetailsdefinition.Inyourworkingdirectory,createafilecalleddeveloper_details.tsandenterthefollowingcontent:

import{Component}from'angular2/core';

import{

ROUTER_DIRECTIVES,

RouteConfig,

RouteParams

}from'angular2/router';

import{Developer}from'./developer';

import{DeveloperCollection}from'./developer_collection';

@Component({

selector:'dev-details',

template:`…`,

})

@RouteConfig(…)

exportclassDeveloperDetails{

publicdev:Developer;

constructor(routeParams:RouteParams,

developers:DeveloperCollection){

this.dev=developers.getUserById(

parseInt(routeParams.params['id'])

);

}

}

Intheprecedingsnippet,wedefinedacomponentwithcontrollercalledDeveloperDetails.Youcannoticethatwithinthecontroller’sconstructor,throughtheDImechanismofAngular2,weinjectedaparameterassociatedwiththeRouteParamstoken.Theinjectedparameterprovidesusaccesstotheparametersvisiblebythecurrentroute.Wecanaccessthemusingtheparamspropertyoftheinjectedobjectandaccessthetargetparameterusingitsnameasakey.

SincetheparameterwegotfromrouteParams.params['id']isastring,weneedtoparseittoanumberinordertogetthedeveloperassociatedwiththegivenroute.Nowlet’sdefinetheroutesassociatedwithDeveloperDetails:

@Component(…)

@RouteConfig([{

component:DeveloperBasicInfo,

name:'DeveloperBasicInfo',

path:'/'

},

{

component:DeveloperAdvancedInfo,

name:'DeveloperAdvancedInfo',

path:'/dev-details-advanced'

}])

exportclassDeveloperDetails{…}

Intheprecedingsnippet,thereisnothingnewforus.Theroutedefinitionfollowstheexactsameruleswe’realreadyfamiliarwith.

Now,tothetemplateofthecomponent,let’saddlinksassociatedwiththeindividualnestedroutes:

@Component({

selector:'dev-details',

directives:[ROUTER_DIRECTIVES],

template:`

<sectionclass="col-md-4">

<ulclass="navnav-tabs">

<li>

<a[routerLink]="['./DeveloperBasicInfo']">

Basicprofile

</a>

</li>

<li>

<a[routerLink]="['./DeveloperAdvancedInfo']">

Advanceddetails

</a>

</li>

</ul>

<router-outlet/>

</section>

`,

})

@RouteConfig(…)

exportclassDeveloperDetails{…}

Withinthetemplate,wedeclaretworelativetothecurrentpathlinks.ThefirstonepointstoDeveloperBaiscInfo,whichisthenameofthefirstroutedefinedwithin@RouteConfigoftheDeveloperDetailscomponent,andrespectively,thesecondonepointstoDeveloperAdvancedInfo.

Sincetheimplementationsofboththecomponentsarequitesimilar,let’stakealookonlyatDeveloperBasicInfo.Asanexercise,youcandevelopthesecondoneortakealookatitsimplementationatch7/ts/multi-page-model-driven/developer_advanced_info.ts:

import{

Component,

Inject,

forwardRef,

Host

}from'angular2/core';

import{DeveloperDetails}from'./developer_details';

import{Developer}from'./developer';

@Component({

selector:'dev-details-basic',

styles:[…],

template:`

<h2>{{dev.realName}}</h2>

<img*ngIf="dev.avatarUrl==null"

class="avatar"src="./gravatar-60-grey.jpg"width="150">

<img*ngIf="dev.avatarUrl!=null"

class="avatar"[src]="dev.avatarUrl"width="150">

`

})

exportclassDeveloperBasicInfo{

dev:Developer;

constructor(@Inject(forwardRef(()=>DeveloperDetails))

@Host()parent:DeveloperDetails){

this.dev=parent.dev;

}

}

Intheprecedingsnippet,weinjectedtheparentcomponentcombiningthe@Injectparameterdecoratorwith@Host.Insideof@Inject,weuseforwardRef,sincewehaveacirculardependencybetweenthepackagesdeveloper_basic_infoanddeveloper_details(insidedeveloper_basic_info,weimportdeveloper_details,andwithindeveloper_details,weimportdeveloper_basic_info).

Weneedareferencetotheinstanceoftheparentcomponentinordertogettheinstanceofthecurrentdevelopercorrespondingtotheselectedroute.

TransformingdatawithpipesItistimeforthelastbuildingblockthatAngular2providesforthedevelopmentofapplicationsthatwehaven’tcoveredindetailyet—thepipes.

JustlikethefiltersinAngularJS1.x,pipesareintendedtoencapsulateallthedata-transformationlogic.Let’stakealookatthetemplateofthehomepageoftheapplicationwejustdeveloped:

<td[ngSwitch]="dev.popular">

<span*ngSwitch-when="true">Yes</span>

<span*ngSwitch-when="false">Notyet</span>

</td>

Intheprecedingsnippet,dependingonthevalueofthepopularproperty,weshoweddifferentdatausingtheNgSwitchandNgSwitchThendirectives.Althoughthisworks,itisredundant.

DevelopingstatelesspipesLet’sdevelopapipethattransformsthevalueofthepopularpropertyandusesitintheplaceofNgSwitchandNgSwitchThen.Thepipewillacceptthreearguments:avaluethatshouldbetransformed,astringthatshouldbedisplayedwhenthevalueistruthy,andanotherstringthatshouldbedisplayedincaseofafalsyvalue.

WiththeuseofanAngular2custompipe,wewillbeabletosimplifythetemplateto:

<td>{{dev.popular|boolean:'Yes':'No'}}</td>

Wecouldevenuseemojis:

<td>{{dev.popular|boolean:'':''}}</td>

WeapplythepipetothevaluethesamewaywedoinAngularJS1.x.Theargumentswepasstothepipeshouldbeseparatedbythecolon(:)symbol.

InordertodevelopanAngular2pipe,weneedthefollowingimports:

import{Pipe,PipeTransform}from'angular2/core';

ThePipedecoratorcanbeusedforaddingmetadatatotheclassthatimplementsthedatatransformationlogic.ThePipeTransformisaninterfacewithasinglemethodcalledtransform:

import{Pipe,PipeTransform}from'angular2/core';

@Pipe({

name:'boolean'

})

exportclassBooleanPipeimplementsPipeTransform{

constructor(){}

transform(flag:boolean,args:string[]):string{

returnflag?args[0]:args[1];

}

}

TheprecedingsnippetistheentireimplementationofBooleanPipe.Thenameofthepipedeterminesthewayitshouldbeusedintemplates.

ThelastthingweneedtodobeforebeingabletousethepipeistoaddtheBooleanPipeclasstothelistofpipesusedbytheHomecomponent(BooleanPipealreadyholdsthemetadataattachedtoitbythe@Pipedecorator,soitsnameisattachedtoit):

@Component({

pipes:[BooleanPipe],

})

exportclassHome{

constructor(privatedevelopers:DeveloperCollection){}

getDevelopers(){…}

}

UsingAngular’sbuilt-inpipesAngular2providesthefollowingsetofbuilt-inpipes:

CurrencyPipe:Thispipeisusedforformattingcurrencydata.Asanargument,itacceptstheabbreviationofthecurrencytype(thatis,"EUR","USD",andsoon).Itcanbeusedinthefollowingway:

{{currencyValue|currency:'USD'}}<!--USD42-->

DatePipe:Thispipeisusedforthetransformationofdates.Itcanbeusedinthefollowingway:

{{dateValue|date:'shortTime'}}<!--12:00AM-->

DecimalPipe:Thispipeisusedfortransformationofdecimalnumbers.Theargumentitacceptsisofthefollowingform:"{minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}".Itcanbeusedinthefollowingway:

{{42.1618|number:'3.1-2'}}<!--042.16-->

JsonPipe:ThistransformsaJavaScriptobjectintoaJSONstring.Itcanbeusedinthefollowingway:

{{{foo:42}|json}}<!--{"foo":42}-->

LowerCasePipe:Thistransformsastringtolowercase.Itcanbeusedinthefollowingway:

{{FOO|lowercase}}<!--foo-->

UpperCasePipe:Thistransformsastringtouppercase.Itcanbeusedinthefollowingway:

{{'foo'|uppercase}}<!--FOO-->

PercentPipe:Thistransformsanumberintoapercentage.Itcanbeusedinthefollowingway:

{{42|percent:'2.1-2'}}<!--4,200.0%-->

SlicePipe:Thisreturnsasliceofanarray.Thepipeacceptsthestartandtheendindexesoftheslice.Itcanbeusedinthefollowingway:

{{[1,2,3]|slice:1:2}}<!--2-->

AsyncPipe:Thisisastatefulpipethatacceptsanobservableorapromise.We’regoingtotakealookatitattheendofthechapter.

DevelopingstatefulpipesTherewasonecommonthingbetweenallthepipesmentionedearlier—allofthemreturnexactlythesameresulteachtimeweapplythemtothesamevalueandpassthemthesamesetofarguments.Suchpipesthatholdthereferentiallytransparencypropertyarecalledpurepipes.

The@Pipedecoratoracceptsanobjectliteralofthefollowingtype:{name:string,pure?:boolean},wherethedefaultvalueforthepurepropertyistrue.Thismeansthatwhenwedecorateagivenclassusingthe@Pipedecorator,wecandeclarewhetherwewantthepipeitimplementsthelogicfortobeeitherstatefulorstateless.Thepurepropertyisimportant,becauseincasethepipeisstateless(thatis,itreturnsthesameresultincaseitisappliedoverthesamevaluewiththesamesetofarguments),thechangedetectioncanbeoptimized.

Nowlet’sbuildastatefulpipe!OurpipewillmakeanHTTPgetrequesttoaJSONAPI.Forthispurpose,wewillusetheangular2/httpmodule.

NoteNotethathavingbusinesslogicinapipeisnotconsideredasabestpractice.Thistypeoflogicshouldbeextractedintoaservice.Theexamplehereisforlearningpurposesonly.

Inthiscase,thepipeneedstoholdadifferentstatedependingonthestatusoftherequest(thatis,whetheritispendingorcompleted).Wewillusethepipeinthefollowingway:

{{"http://example.com/user.json"|fetchJson|json}}

Thisway,weapplythefetchJsonpipeovertheURL,andoncewehaveresponsefromtheremoteserviceandthepromisefortherequesthasbeenresolved,wecanapplythejsonpipeovertheobjectwegotfromtheresponse.TheexamplealsoshowshowwecanchainpipeswithAngular2.

Similarly,incaseofthepreviousexample,forthedevelopmentofastatelesspipe,wehavetoimportPipeandPipeTransform.However,thistime,becauseoftheHTTPrequestfunctionality,wealsoneedtoimporttheHttpandResponseclassesfromthe'angular2/http'module:

import{Pipe,PipeTransform}from'angular2/core';

import{Http,Response}from'angular2/http';

import'rxjs/add/operator/toPromise';

EachtimeithappenstoapplythefetchJsonpipetoadifferentargumentcomparedtotheonewegotinthepreviousinvocation,weneedtomakeanewHTTPgetrequest.Thismeansthatasthestateofthepipe,weneedtokeepatleastthevaluesoftheresponseoftheremoteserviceandthelastURL:

@Pipe({

name:'fetchJson',

pure:false

})

exportclassFetchJsonPipeimplementsPipeTransform{

privatedata:any;

privateprevUrl:string;

constructor(privatehttp:Http){}

transform(url:string):any{…}

}

Theonlypieceoflogicleftisthetransformmethod:

transform(url:string):any{

if(this.prevUrl!==url){

this.http.get(url).toPromise(Promise)

.then((data:Response)=>data.json())

.then(result=>this.data=result);

this.prevUrl=url;

}

returnthis.data||{};

}

Insideofit,weinitiallycomparedtheURLpassedasanargumentwiththeonewecurrentlykeepareferenceto.Iftheyaredifferent,weinitiateanewHTTPgetrequestusingthelocalinstanceoftheHttpclass,whichwaspassedtotheconstructorfunction.Oncetherequestiscompleted,weparsetheresponsetoJSONandsetthedatapropertytotheresult.

Now,let’ssupposethepipehasstartedanHttpgetrequest,andbeforeitiscompleted,thechangedetectionmechanisminvokesthepipeagain.Inthiscase,wewillcomparetheprevUrlpropertywiththeurlparameter.Incasetheyarethesame,wewon’tperformanewhttprequest,andwewillimmediatelyreturnthevalueofthedataproperty.IncaseprevUrlhasadifferentvaluefromurl,wewillstartanewrequest.

UsingstatefulpipesNowlet’susethepipethatwedeveloped!Theapplicationthatwearegoingtoimplementprovidestotheuseratextinputandabutton.Oncetheuserentersavalueinthetextinputandpressesthebutton,belowthetextinputwillappeartheavatarcorrespondingtotheGitHubuser,asshowninthefollowingscreenshot:

Now,let’sdevelopasamplecomponent,whichwillallowustoentertheGitHubuser’shandle:

//ch7/ts/statful_pipe/app.ts

@Component({

selector:'app',

providers:[HTTP_PROVIDERS],

pipes:[FetchJsonPipe,ObjectGetPipe],

template:`

<inputtype="text"#input>

<button(click)="setUsername(input.value)">GetAvatar</button>

`

})

classApp{

username:string;

setUsername(user:string){

this.username=user;

}

}

Intheprecedingexample,weaddedFetchJsonPipeusedbytheAppcomponent.TheonlythingleftistoshowtheGitHubavataroftheuser.Wecaneasilyachievethisbyalteringthetemplateoftheprecedingcomponentwiththefollowingimgdeclaration:

<imgwidth="160"[src]="(('https://api.github.com/users/'+username)|

fetchJson).avatar_url">

Initially,weappendedtheGitHubhandletothebaseURLusedforfetchingusersfromtheAPI.Later,weappliedthefetchJsonfilteroverit,andfromthereturnedresult,wegottheavatar_urlproperty.

NoteAlthoughthepreviousexampleworks,itisunnaturaltohavebusinesslogicinyourpipes.ItwillbefarbettertoimplementthelogicforcommunicationwiththeGitHub’sAPIintoaserviceor,atleast,invokethegetmethodoftheinstanceoftheHttpclassinacomponent.

UsingAngular’sAsyncPipeAngular’sAsyncPipetransformmethodacceptsasanargumentanobservableorapromise.Oncetheargumentpushesavalue(thatis,thepromisehasbeenresolvedorthesubscribecallbackoftheobservableisinvokedwithavalue),AsyncPipewillreturnitasaresult.Let’stakealookatthefollowingexample:

//ch7/ts/async-pipe/app.ts

@Component({

selector:'greeting',

template:'Hello{{greetingPromise|async}}'

})

classGreeting{

greetingPromise=newPromise<string>(resolve=>this.resolve=resolve);

resolve:Function;

constructor(){

setTimeout(_=>{

this.resolve('Foobar!');

},3000);

}

}

Here,wedefinedanAngular2component,whichhastwoproperties:greetingPromiseofthetypePromise<string>andresolveofthetypeFunction.WeinitializedthegreetingPromisepropertywithanewPromise<string>instance,andasvalueoftheresolveproperty,wesettheresolvecallbackofthepromise.

Intheconstructoroftheclass,westartatimeoutwiththedurationof3,000ms,andinsideofitscallback,weresolvethepromise.Oncethepromiseisresolved,thevalueoftheexpression{{greetingPromise|async}}willbeevaluatedtothestringFoobar!.TheendresultthattheuserwillseeonthescreenisthetextHelloFoobar!.

TheasyncpipeisextremelypowerfulwhenwecombineitwithanHttprequestortogetherwithanobservable,whichpushesasequenceofvalues.

UsingAsyncPipewithobservablesWe’realreadyfamiliarwiththeconceptofobservablesfromthepreviouschapters.Wecansaythatanobservableobjectallowsustosubscribetotheemissionofasequenceofvalues,forinstance:

letobserver=newObservable<number>(observer=>{

setInterval(()=>{

observer.next(newDate().getTime());

},1000);

});

observer.subscribe(date=>console.log(date));

Oncewesubscribetotheobservable,itwillstartemittingvalueseachsecond,whicharegoingtobeprintedintheconsole.Let’scombinethissnippetwiththecomponent’sdefinitionandimplementasimpletimer:

//ch7/ts/async-pipe/app.ts

@Component({

selector:'timer'

})

classTimer{

username:string;

timer:Observable<number>;

constructor(){

letcounter=0;

this.timer=newObservable<number>(observer=>{

setInterval(()=>{

observer.next(newDate().getTime());

},1000);

});

}

}

Theonlythingleftinordertobeabletousethetimercomponentistoadditstemplate.Wecansubscribetotheobservabledirectlyinourtemplatebyusingtheasyncpipe:

{{timer|async|date:"medium"}}

Thisway,eachsecondwewillgetthenewvalueemittedbytheobservable,andthedatepipewilltransformitintoareadableform.

SummaryInthischapter,wetookadeepdiveintotheAngular2formsbydevelopingamodel-drivenoneandcombiningitwiththehttpmoduleinordertobeabletoadddeveloperstoourrepository.Wetookalookatsomeadvancedfeaturesofthenewcomponent-basedrouterandsawhowwecanuseanddevelopourcustomizedstatefulandstatelesspipes.

ThenextchapterwillbededicatedtohowwecanmakeourAngular2applicationsSEO-friendlybytakingadvantageoftheserver-siderenderingthatthemoduleuniversalprovides.Wewillalsotakealookatangular-cliandtheothertoolsthatmakeourexperienceasdevelopersbetter.

Chapter8.DevelopmentExperienceandServer-SideRenderingWearealreadyfamiliarwithallthecoreconceptsofAngular2.Weknowhowtodevelopacomponent-baseduserinterface,takingadvantageofallthebuildingblocksthattheframeworkprovides—directives,components,dependencyinjections,pipes,forms,andthebrandnewcomponent-basedrouter.

Forthenextstep,we’lllookatwheretobeginwhenwewanttobuildasingle-pageapplication(SPA)fromscratch.Thischapterdescribeshowtodothefollowing:

UseWebWorkersforperformance-sensitiveapplications.BuildSEO-friendlyapplicationswithserver-siderendering.Bootstrapaprojectasquicklyaspossible.Enhanceourexperienceasdevelopers.

So,let’sbegin!

RunningapplicationsinWebWorkersWhentalkingaboutperformanceinthecontextoffrontendwebdevelopment,wecaneithermeannetwork,computational,orrenderingperformance.Inthissection,we’llconcentrateonrenderingandcomputationalperformance.

First,let’smakeaparallelbetweenawebapplicationandavideo,andbetweenabrowserandavideoplayer.Thebiggestdifferencebetweenthewebapplicationrunninginthebrowserandthevideofileplayinginthevideoplayeristhatthewebpageneedstobegenerateddynamically,incontrasttothevideowhichhasbeenrecorded,encoded,anddistributed.However,inboththecases,theuseroftheapplicationseesasequenceofframes;thecoredifferenceishowtheseframesarebeinggenerated.Intheworldofvideoprocessing,whenweplayavideo,wehaveitalreadyrecorded;itistheresponsibilityofthevideodecodertoextracttheindividualframesbasedonthecompressionalgorithm.Incontrasttothis,ontheWeb,JavaScript,andCSSareinchargeofproducingframes,renderedbythebrowser’srenderingengine.

Inthecontextofthebrowser,wecanthinkofeachframeasasnapshotofthewebpageatagivenmoment.Thedifferentframesarerenderedfastoneafteranother,sointheory,theenduseroftheapplicationshouldseethemsmoothlyincorporatedtogether,justlikeavideoplayedinavideoplayer.

OntheWeb,wearetryingtoreach60fps(framespersecond),whichmeansthateachframehasabout16mstobecomputedandrenderedonthescreen.Thisdurationincludesthetimerequiredbythebrowsertomakeallthenecessarycalculationsforthelayoutandtherenderingofthepage,andalsothetimethatourJavaScriptneedstoexecute.

Intheend,wehavelessthan16ms(becauseofthebrowserrenderingfunctionalitythattakestimedependingonthecalculationsitneedstoperform)forourJavaScripttofinishexecution.Ifitdoesn’tfitinthisduration,theframeratewilldropbyhalf.SinceJavaScriptisasingle-threadedlanguage,allthecalculationsneedtohappeninthemainUIthreadthat,inthecaseofcomputationally-intensiveapplications(suchasimageorvideoprocessing,marshalingandunmarshalingbigJSONstrings,andsoon),canleadtoverypooruserexperiencebecauseoftheframesbeingdropped.

HTML5introducedanAPIcalledWebWorkers,whichallowstheexecutionofclient-sidecodeinthebrowserenvironmentintomultiplethreads.Forsimplicity,thestandarddoesn’tallowsharedmemorybetweenindividualthreads,butinsteadallowscommunicationwithmessagepassing.ThemessagesexchangedbetweenWebWorkersandthemainUIthreadmustbestrings,whichoftenrequirestheserializationanddeserializationofJSONstrings.

Thelackofsharedmemorybetweentheindividualworkers,andtheworkersandthemainUIthreadbringsacoupleoflimitations,suchas:

DisabledaccesstotheDOMbytheworkerthreads.Globalvariablescannotbesharedamongtheindividualcomputationalunits(thatis,workerthreadsandmainUIthreadsandviceversa).

WebWorkersandAngular2BecauseoftheplatformagnosticdesignofAngular2,thecoreteamdecidedtotakeadvantageofthisAPI,andduringthesummerof2015,GoogleembeddedWebWorkerssupportintotheframework.ThisfeatureallowsmostoftheAngular2applicationstoberunonaseparatethread,makingthemainUIthreadresponsibleonlyforrendering.Thishelpsusachievethegoalof60fpsmucheasilythanrunningtheentireapplicationinasinglethread.

TheWebWorkerssupportisnotenabledbydefault.Whenenablingit,weneedtokeepsomethinginmind—inaWebWorkers-readyapplication,thecomponentsarenotgoingtoberuninthemainUIthread,whichdoesnotallowustodirectlymanipulatetheDOM.Inthiscase,weneedtousebindings,suchasinputs,outputs,andacombinationofbothwithNgModel.

BootstrappinganapplicationrunninginWebWorkerLet’smaketheto-doapplicationthatwedevelopedinChapter4,GettingStartedwithAngular2ComponentsandDirectivesworkinWebWorkers.Youcanfindtheexamplethatwe’llexploreatch8/ts/todo_webworkers/.

Firstofall,let’sdiscussthechangesthatweneedtomake.Takealookatch4/ts/inputs-outputs/app.ts.Noticethatinsideofapp.ts,weincludedthebootstrapfunctionfromtheangular2/platform/browsermodule.Thisisthefirstthingweneedtomodify!Thebootstrapprocessofanapplicationrunninginabackgroundprocessisdifferent.

Beforerefactoringourcode,let’stakealookatadiagramthatillustratesthebootstrapprocessofatypicalAngular2applicationrunninginWebWorkers:

JasonTeplitz,whoimplementedtheWebWorkersupportinAngular2,presentedthisdiagramduringhistalkonAngularConnect2015.

Thediagramhastwoparts:UIandWebWorker.UIshowstheactionsperformedduringinitializationinthemainUIthread;theWebWorkerpartofthediagramshowshowtheapplicationgetsbootstrappedinthebackgroundthread.Now,let’sexplainthebootstrapprocessstepbystep.

First,theuseropenstheindex.htmlpage,whichtriggersthedownloadofthefollowingtwofiles:

TheUIbundleofAngular2usedforapplicationsrunninginWebWorker.Thesystem.jsbundle(wetalkedabouttheglobalobjectSysteminChapter3,TypeScriptCrashCourse.Wecanthinkofthesystem.jsbundleasapolyfillforthemoduleloader).

Usingsystem.js,wedownloadthescriptusedfortheinitializationofthepartoftheapplicationrunninginthemainUIthread.Thisscriptstartsloader.jsinWebWorker.Thisisthefirstscriptthatisrunninginabackgroundthread.Oncetheworkerisstarted,loader.jswilldownloadsystem.jsandthebundleofAngular2,whichismeanttoberuninthebackgroundthread.Thefirstrequestwillusuallyhitthecachebecausesystem.jsisalreadyrequestedbythemainthread.Usingthemoduleloader,wedownloadthescriptthatisresponsibleforbootstrappingthebackgroundappbackground_bootstrap.js,whichwillfinallystartthefunctionalityofourapplicationinthebackground.

Fromnowon,theentireapplicationthatwebuiltwillberuninWebWorkerandwillexchangemessageswiththemainUIthreadforrespondingtousereventsandrenderinginstructions.

Nowthatweareawareofthebasicflowofeventsduringinitializationwhenusingworkers,let’srefactorourto-doapplicationtotakeadvantageofthem.

MigratinganapplicationtoWebWorkerInsideofindex.html,weneedtoaddthefollowingscripts:

<!--ch8/ts/todo_webworkers/index.html-->

<scriptsrc="/node_modules/systemjs/dist/system.src.js">

</script>

<scriptsrc="/node_modules/angular2/bundles/angular2-polyfills.js">

</script>

<scriptsrc="/node_modules/angular2/bundles/web_worker/ui.dev.js">

</script>

<script>

System.config({

baseURL:'/dist/dev/ch8/ts/todo_webworkers/'

});

System.import('./bootstrap.js')

.catch(function(){

console.log('Reportthiserrorto

https://github.com/mgechev/switching-to-angular2/issues',e);

});

</script>

Intheprecedingsnippet,we’veincludedreferencestosystem.js,angular2-polyfillsthatincludeszone.jsandtheothersusedbyAngularlibraries,andui.dev.jswhichisthebundlethatneedstoberuninthemainUIthread.

Rightafterthis,wewillconfiguresystem.jsbysettingthebaseURLpropertyofthemoduleloader.Forthenextstep,wewillexplicitlyimportthebootstrap.jsfile,whichcontainsthelogicusedforstartingtheloader.jsscriptinWebWorker.

Let’sexplorebootstrap.js,whichistheoriginalofthetranspiledbootstrap.js:

//ch8/ts/todo_webworkers/bootstrap.ts

import{platform,Provider}from'angular2/core';

import{

WORKER_RENDER_APPLICATION,

WORKER_RENDER_PLATFORM,

WORKER_SCRIPT

}from'angular2/platform/worker_render';

platform([WORKER_RENDER_PLATFORM])

.application([WORKER_RENDER_APPLICATION,

newProvider(WORKER_SCRIPT,{useValue:'loader.js'})]);

Inthisfile,wesettheplatformtothetypeWORKER_RENDER_PLATFORMandtheapplicationtypetoWORKER_RENDER_APPLICATION.WeconfiguredtheproviderusedforinjectingtheWORKER_SCRIPTtokentousethevalue'loader.js'.Aswesaid,loader.jsisgoingtoruninabackgroundthread.Thescriptislocatedintheapplication’sroot.

Now,wecanmovetotherightofthediagramgivenintheBootstrappinganapplicationrunninginaWebWorkersection.Thelogicinloader.jsisquitesimple:

//ch8/ts/todo_webworkers/loader.ts

importScripts("/node_modules/systemjs/dist/system.src.js",

"/node_modules/angular2/bundles/web_worker/worker.dev.js",

"/node_modules/angular2/bundles/angular2-polyfills.js");

System.config({

baseURL:'/dist/dev/ch8/ts/todo_webworkers/',

});

System.import('./background_app.js')

.then(()=>console.log('Theapplicationhasstartedsuccessfully'),

error=>console.error('Errorloadingbackground',error));

Asthefirststep,weimportsystem.js,theWebWorkersbundleofAngular2(worker.dev.js),andalltherequiredpolyfills.Then,weconfigurethebackgroundinstanceofthemoduleloaderandimportthebackground_appfile,whichcontainsthelogicofourapplicationaswellastheWebWorkersbootstrapcall.

Now,let’sexplorehowwebootstraptheapplicationinsideWebWorker:

import{platform}from'angular2/core';

import{

WORKER_APP_PLATFORM,

WORKER_APP_APPLICATION

}from'angular2/platform/worker_app';

//Logicfortheapplication…

platform([WORKER_APP_PLATFORM])

.application([WORKER_APP_APPLICATION])

.bootstrap(TodoApp);

JustlikeinthebootstrapinthemainUIthread,wespecifythetypeoftheplatformandthetypeoftheapplicationthatwewanttobootstrap.Inthefinalstep,wesettherootcomponentjustlikewedidinthestandardbootstrapprocess.TheTodoAppcomponentisdefinedbetweentheimportsandtheinitializationcallsinthebackground_appfile.

MakinganapplicationcompatiblewithWebWorkersAswesaid,thecodethatrunsinthecontextofWebWorkerdoesnothaveaccesstotheDOM.Let’sseewhatchangesweneedtomakeinordertoaddressthislimitation.

ThisistheoriginalimplementationoftheInputBoxcomponent:

//ch4/ts/inputs-outputs/app.ts

@Component({

selector:'input-box',

template:`

<input#todoInput[placeholder]="inputPlaceholder">

<button(click)="emitText(todoInput.value);

todoInput.value='';">

{{buttonLabel}}

</button>

`

})

classInputBox{

@Input()inputPlaceholder:string;

@Input()buttonLabel:string;

@Output()inputText=newEventEmitter<string>();

emitText(text:string){

this.inputText.emit(text);

}

}

Noticethatinsidethetemplate,wenamedtheinputelementtodoInputanduseditsreferencewithintheexpressionsetasthehandleroftheclickevent.ThiscodewillnotbeabletoruninWebWorker,sincewedirectlyaccessaDOMelementinsidethetemplate.Inordertotakecareofthis,weneedtorefactorthesnippet,soitusesAngular2bindingsinsteadofdirectlytouchinganyelements.WecaneitheruseinputswhenasingledirectionbindingmakessenseorNgModelforachievingtwo-waydata-binding,whichismorecomputationally-intensive.

Let’suseNgModel:

//ch8/ts/todo_webworkers/background_app.ts

import{NgModel}from'angular2/common';

@Component({

selector:'input-box',

template:`

<input[placeholder]="inputPlaceholder"[(ngModel)]="input">

<button(click)="emitText()">

{{buttonLabel}}

</button>

`

})

classInputBox{

@Input()inputPlaceholder:string;

@Input()buttonLabel:string;

@Output()inputText=newEventEmitter<string>();

input:string;

emitText(){

this.inputText.emit(this.input);

this.input='';

}

}

InthisversionoftheInputBoxcomponent,wewillcreateatwo-waydata-bindingbetweentheinputelementandtheinputpropertyoftheInputBoxcomponent.Oncetheuserclicksonthebutton,theemitTextmethodwillbeinvoked,whichwilltriggeraneweventemittedbyinputTextEventEmitter.Inordertoresetthevalueoftheinputelement,wetakeadvantageofthetwo-waydata-bindingthatwedeclaredandsetthevalueoftheinputpropertytotheemptystring.

NoteMovingtheentirelogicfromthetemplatesofthecomponentstotheircontrollersbringsalotofbenefits,suchasimprovedtestability,maintainability,codereuse,andclarity.

TheprecedingcodeiscompatiblewiththeWebWorkersenvironment,sincetheNgModeldirectiveisbasedonanabstractionthatdoesnotmanipulatetheDOMdirectly,butinstead,underthehood,exchangesmessagesasynchronouslywiththemainUIthread.

Torecap,wecansaythatwhilerunningapplicationsinthecontextofWebWorkers,weneedtokeepthefollowingtwothingsinmind:

Weneedtouseadifferentbootstrapprocess.WeshouldnotaccesstheDOMdirectly.

Typicalscenariosthatviolatethesecondpointareasfollows:

ChangingtheDOMofthepagebyselectinganelementandmanipulatingitdirectlywiththebrowser’snativeAPIsorathird-partylibrary.AccessingnativeelementsinjectedbyusingElementRef.Creatingareferencetoanelementinthetemplateandpassingitasanargumenttomethods.Directlymanipulatinganelementreferencedwithinthetemplate.

Inallthesescenarios,weneedtousetheAPIsprovidedbyAngular.Ifwebuildourapplicationsaccordingtothispractice,wewillbenefitnotonlyfrombeingabletoruntheminWebWorkers,butalsofromincreasingthecodereuseincasewewanttousethemacrossdifferentplatforms.

Keepingthisinmindwillallowustotakeadvantageofserver-siderendering.

Initialloadofasingle-pageapplicationInthissection,wewillexplorewhatserver-siderenderingis,whyweneeditinourapplications,andhowwecanuseitwithAngular2.

Forourpurposes,we’llexplainthetypicalflowofeventswhenauseropensaSPAimplementedinAngular2.First,we’lltracetheeventswiththeserver-siderenderingdisabled,andafterthat,we’llseehowwecanbenefitfromthisfeaturebyenablingit.OurexamplewillbeillustratedinthecontextofHTTP1.1.

Thisimageshowsthefirstrequestbythebrowserandthecorrespondingserver’sresponsewhenloadingatypicalSPA.TheresultthattheclientwillseeinitiallyistheinitialcontentoftheHTMLpagewithoutanyrenderedcomponents.

Let’ssupposethatwedeploytheto-doapplicationwebuiltinChapter4,GettingStartedwithAngular2ComponentsandDirectivestoawebserverthathasthehttps://example.comdomainassociatedwithit.

Oncetheusernavigatestohttps://example.com/,thebrowserwillopenanewHTTPGETrequest,fetchingtherootresource(/).Whentheserverreceivestherequest,itwillrespondwithanHTMLfilethat,inourcase,willlooksomethinglikethis:

<!DOCTYPEhtml>

<htmllang="en">

<head>

<title>SwitchingtoAngular2</title>

<linkrel="stylesheet"href="bootstrap.min.css">

</head>

<body>

<app>Loading…</app>

<scriptsrc="es6-shim.min.js"></script>

<scriptsrc="Reflect.js"></script>

<scriptsrc="system.src.js"></script>

<scriptsrc="angular2-polyfills.js"></script>

<scriptsrc="Rx.min.js"></script>

<scriptsrc="angular2.js"></script>

<scriptsrc="router.js"></script>

<scriptsrc="http.min.js"></script>

<script>…</script>

</body>

</html>

Thebrowserwillreceivethiscontentasthebodyoftheresponse.Whenthemarkupisrenderedontothescreen,allthattheuserwillseeisthelabel:Loading….

Inthenextstep,thebrowserwillfindallthereferencesintheHTMLfile’sexternalresources,suchasstylesandscripts,andstartdownloadingthem.Inourcase,someofthemarebootstrap.css,es6-shim.min.js,Reflect.js,system.src.js,andangular2-polyfills.js.

Onceallthereferencedresourcesareavailable,therestillwon’tbeanysignificantvisualprogressfortheuser(exceptifthestylesfromthedownloadedCSSfileareappliedtothepage).Thiswon’tchangeuntiltheJavaScriptvirtualmachineprocessesallthereferencedscriptsrelatedtotheapplication’simplementation.Atthispoint,AngularwillknowwhichcomponentneedstoberenderedbasedonthecurrentURLandbootstrap’sconfiguration.

Ifthecomponentassociatedwiththepageisdefinedinaseparatefileoutsideofourmainapplicationbundle,theframeworkwillneedtodownloadittogetherwithitsentiredependencygraph.Incasethetemplateandthestylesofthecomponentareexternalized,Angularwillneedtodownloadthemaswellbeforeitisabletorendertherequestedpage.

Rightafterthis,theframeworkwillbeabletocompilethetemplateassociatedwiththetargetcomponentandrenderthepage.

Inthepreviousscenario,therearethefollowingtwomainpitfalls:

SearchenginesarenotthatgoodatindexingdynamiccontentgeneratedbyJavaScript.ThismeansthattheSEO(SearchEngineOptimization)ofourSPAwillsuffer.Incaseoflargeapplicationsand/orpoorInternetconnection,theuserexperiencewillbepoor.

Inthepast,wesolvedtheSEOissueintheapplicationsbuiltwithAngularJS1.xwithdifferentworkarounds,suchasusingheadlessbrowserforrenderingtherequestedpage,cachingitontothedisk,andlaterprovidingittosearchengines.However,there’samoreelegantsolution.

InitialloadofaSPAwithserver-siderenderingAcoupleofyearsago,librariessuchasRendr,Derby,Meteor,andtheothersintroducedtheconceptofisomorphicJavaScriptapplications,whichwerelaterrenamedtouniversal.Inessence,universalapplicationscouldberunontheclientaswellasontheserver.SuchportabilityisonlypossibleinthecaseoflowcouplingbetweentheSPAandthebrowser’sAPIs.Thegreatestbenefitofthisparadigmisthattheapplicationcanbererenderedontheserverandlatersenttotheclient.

Universalapplicationsarenotframework-specific;wecantakeadvantageoftheminanyframeworkthatcanberunoutsideoftheenvironmentofthebrowser.Conceptually,thepracticeofserver-siderenderingisverysimilaracrossplatformsandlibraries;onlyitsimplementationdetailsmaydiffer.Forinstance,theAngular2Universalmodule,whichimplementsserver-siderendering,hassupportfornode.jsaswellasASP.NETthat,atthemomentofthiswriting,isstillworkinprogress.

TheprecedingimageshowstheresponsebytheservertotheinitialbrowserGETrequest.Thistime,incontrasttothetypicalscenarioofloadingaSPA,thebrowserwillgettherenderedcontentoftheHTMLpage.

Let’stracetheflowoftheeventsinthesameapplicationwiththeserver-siderenderingfeatureenabled.Inthiscase,oncetheserverreceivestheHTTPGETrequestbythebrowser,itwillruntheSPAontheserverinthenode.jsenvironment.AlltheDOMcallsaregoingtoberedirectedtoaserver-sideDOMimplementationandbeexecutedinthecontextoftheusedplatform.Similarly,alltheAJAXcallswiththehttpmodulewillbehandledbytheserver-sideimplementationofthemodule.Thisway,theapplicationwillnotmakeanydifference,whetheritisrunninginthecontextofthebrowserortheserver.

OncetherenderedversionoftheSPAisavailable,itcanbeserializedtoHTMLandsenttothebrowser.Thistime,duringtheapplication’sinitialization,insteadoftheLoading…label,theuserwillseethepagetheyrequestedrightaway.

Notethatatthispoint,theclientwillhavetherenderedversionoftheapplication,butall

thereferencedexternalresources,suchasscriptsandstyles,stillneedtobeavailable.Thismeansthat,initially,noneoftheCSSstylesdeclaredintheexternalfileswillbeappliedandtheapplicationwillnotberesponsivetoanyuser-relatedinteractions,suchasthemouseandkeyboardevents.

NoteNotethatincasethescriptsareinlinedintotheserver-siderenderedpage,theapplicationwillberesponsivetouserevents.However,inliningbigchunksofJavaScriptisgenerallyconsideredasabadpractice,sinceitwillincreasethepage’ssizedramaticallyandpreventthescriptsfromcaching.Bothwillinfluencethenetworkperformance.

WhentheJavaScriptvirtualmachineprocessestheJavaScriptassociatedwiththepage,ourSPAwillbereadytouse.

Server-siderenderingwithAngular2Inthefirsthalfof2015,PatrickStapletonandJeffWhelpleyannouncedthattheystartedthedevelopmentofthemodule,Universal.Universalisalibrarythatallowsustobuilduniversal(alsocalledisomorphic)JavaScriptapplicationswithAngular2;inotherwords,itprovidesserver-siderenderingsupport.

ApplicationsbuiltwithAngular2andUniversalwillnotberesponsiveuntilalltheJavaScriptbelongingtotherequestedpageisprocessed.Thisisadrawbackthatwealreadymentioned,whichisvalidforalltheserver-siderenderedapplications.However,PatrickandJeffintroducedpreboot.js,whichisalightweightlibrarythatwillbeinlinedonthepagerenderedbytheserverandavailableaftertheinitialclientrequest.

Preboot.jshasseveralstrategiesforthemanagementofthereceivedclienteventsbeforetheapplicationhasbeencompletelyinitialized.Theyareasfollows:

Recordandplaybackevents.Respondimmediatelytoevents.Maintainfocuswhenapageisrerendered.Bufferclient-sidere-renderingforsmoothertransition.Freezepageuntilthebootstrapiscompleteifauserclicksonabutton.

Atthemomentofthiswriting,theUniversalmoduleisstillbeingactivelydeveloped.However,youcangiveitatryusingtheAngular2universalstarterathttps://github.com/angular/universal-starter.

EnhancingourdevelopmentexperienceOurexperienceasdeveloperscanbeenhancedintermsofproductivityorbyallowingustohavemorefunwhileworkingonourprojects.Thiscanbeachievedwithallthetools,IDEs,texteditors,andmorethatweuseonadailybasis.Inthissection,we’llbrieflytakealookatpopularIDEsandtexteditorsthatwecanusefortakingadvantageofthestaticcodeanalysisfeaturesthatAngular2provides.

Inthesecondpartofthissection,we’llseewhathotreloadingisandhowwecantakeadvantageofitduringthedevelopmentofAngular2applications.

TexteditorsandIDEsAswealreadymentionedatthebeginningofthebook,thecoreteamputgreateffortintoenhancingthetoolingsupportinAngular2.Firstofall,theframeworkisbuiltwithTypeScript,whichnaturallyallowsustousestatictypingduringourdevelopmentprocess.SomeofthetexteditorsandIDEsthathavegreatTypeScriptsupportareasfollows:

IntelliJIdea:Ageneral-purposeIDEbyJetBrains.WebStorm:AnIDEspecializedforwebdevelopmentbyJetBrains.VSCode:Across-platformtexteditorwritteninTypeScriptanddevelopedbyMicrosoft.SublimeText:Across-platformtexteditor.Atom:Across-platformtexteditor.

Recently,JetBrainsannouncedadvancedAngular2supportinIntelliJIdeaandWebStorm,whichsupportsautocompletionforcomponentsandbindings.

AlthoughnotallthementionedIDEsandtexteditorshaveAngular2-specificsupportatthemomentofthiswriting,Angular2comeswithagreatdesign.Itallowsustoperformadvancedstaticcodeanalysisontheapplication’scodebaseforthedevelopmentofsophisticatedrefactoringandproductivitytoolsinthenearfuture.Untilthen,Angular2atleastprovidestoolingsupportasgoodanyotherJavaScriptframeworkinthemarket.

HotreloadingHotreloading(orhotloading)isapracticethatgotpopularintheworldofpurelyfunctionaluserinterfacesinlibrariessuchasOm(usedwithClojureScript)andReact.

WhendevelopingaSPA,itisquiteannoyingtorefreshyourbrowseraftereachsmallchangeofastyle,view,orevenacomponent.That’swhyacoupleofyearsago,atoolwasdevelopedcalledlivereload.Livereloadwatchesthefilesofourapplication,andwhenitdetectsachangeinanyofthem,itsendsamessagetothebrowsertorefreshthepage.Usually,theconnectionestablishedbetweenthelivereloadserverandtheclientisthroughWebSockets,sincetheserverneedstosendpushnotifications.Althoughthistoolworksgreatinsomecases,ithasonebigdisadvantage:oncethepageisrefreshed,allofthestatecollectedduringthedeveloper’sinteractionwillbelost.

Forinstance,imagineascenariowhereyou’reworkingonanapplicationwithacomplexview.Younavigatethroughafewpages,fillinforms,andsetthevaluestoinputfields,andthen,unexpectedly,youfindanissue.YougotoyourtexteditororIDEandfixtheissue;thelivereloadserverdetectsachangeinyourproject’srootandsendsanotificationtothebrowserinordertorefreshthepage.Now,you’rebacktotheinitialstateoftheapplicationandyouneedtogothroughallthesestepsinordertoreachthesamepointbeforetherefresh.

Incontrasttolivereloading,inmostcases,hotreloadingcaneliminatethestatelost.Let’stakeabrieflookathowitworks.

Atypicalimplementationofahotreloaderhastwomainmodules:aclientandaserver.Incontrasttotheserverinlivereloading,thehotreloaderservernotonlywatchesthefilesystemforchanges,butalsotakesthecontentofthechangedfileandsendsittothebrowser.Oncethebrowserreceivesthemessagesentbytheserver,itcanswapthepreviousimplementationofthechangedunitwiththenewone.Afterthis,theviewaffectedbythechangecanbererenderedinordertovisuallyreflectthechange.Sincetheapplicationdoesn’tloseitsstate,wecancontinuefromthepointwe’vereachedwiththenewversionofthechangedcodeunit.

Unfortunately,itisnotalwayspossibletodynamicallyswaptheimplementationsofallyourcomponentsusingthisstrategy.Ifyouupdateapieceofcodethatholdsthatholdsapplicationstate,youmayneedtorefreshthepagemanually.

HotreloadinginAngular2Atthetimeofwriting,thereisaworkingprototypeofAngular2hotreloaderthatcanbetestedwiththeangular2-seeddescribedintheAngular2quickstarterssection.Theprojectisinactivedevelopment,sotherearealotofimprovementsontheroadmap.Butitalreadyprovidesitscorefunctionality,whichcaneasethedevelopmentexperiencesignificantly.

Bootstrappingaprojectwithangular-cliDuringAngularConnect2015,BradGreenandIgorMinar,partoftheAngularteam,announcedangular-cli—aCLI(command-lineinterface)tooltoeasestartingandmanagingAngular2applications.ForthosewhohaveusedRubyonRails,theideabehindtheCLItoolmightbefamiliar.Thebasicpurposeofthetoolistoallowthequickbootstrappingofnewprojectsandscaffoldingofnewdirectives,components,pipes,andservices.

Atthetimeofwriting,thetoolisstillintheearlystageofdevelopment,sowe’lldemonstrateonlyitsbasicusage.

Usingangular-cliInordertoinstalltheCLItool,runthefollowingcommandinyourterminal:

npminstall-gangular-cli

Rightafterthis,theglobalngcommandwillappearinyour$PATH.ForcreatinganewAngular2project,usethefollowing:

#Maytakeawhile,dependingonyourInternetconnection

ngnewangular-cli-project

cdangular-cliproject

ngserve

Theprecedingcommandswilldothefollowing:

CreateanewAngular2projectandinstallallofitsnode.jsdependencies.Enteryourproject’sdirectory.Startadevelopmentwebserverthatwillletyouopentheapplicationyoujustcreatedinyourwebbrowser.

Forfurtherreading,takealookattheproject’srepositorylocatedathttps://github.com/angular/angular-cli.

Angular2quickstartersAlthoughAngular2CLIisgoingtobeamazing,atthemomentofthiswriting,itisstillataveryearlystageofdevelopment.It’sbuild-toolagnostic,whichmeansthatitdoesn’tprovideanybuildsystem.Luckily,therearealotofstarterprojectsdevelopedbythecommunitythatcanprovideagreatstartingpointforournextAngular2project.

Angular2seedIncaseyouenjoyGulpandstatictyping,youcangiveatrytotheangular2-seedproject.ItishostedonGitHubatthefollowingURL:https://github.com/mgechev/angular2-seed.

TheAngular2seedprovidesthefollowingkeyfeatures:

Advanced,ready-to-go,easy-to-extend,modular,andstaticallytypedbuildsystemusingGulp.Productionanddevelopmentbuilds.SampleunittestswithJasmineandKarma.End-to-endtestswithProtractor.AdevelopmentserverwithLivereload.Experimentalhotreloadingsupport.Followingthebestpracticesforyourapplications’andfiles’organization.ManagerfortheTypeScript-relatedtypedefinitions.

Thecodedistributedwiththebookisbasedonthisseedproject.

Forangular2-seed,youneedtohavenode.js,npm,andGitinstalled,andyouneedtorunthefollowinglistofcommands:

gitclone--depth1https://github.com/mgechev/angular2-seed.git

cdangular2-seed

npminstall

npmstart

Afteryourunthesecommands,yourbrowserwillbeautomaticallyopenedwiththehomepageoftheseed.OnthechangeofanyoftheTypeScriptfiles,thecodewillbeautomaticallytranspiledtoJavaScriptandyourbrowserwillberefreshed.

Theproductionbuildisconfigurable,butbydefault,itproducesasinglebundlethatcontainsaminifiedversionoftheapplicationandallthereferencedlibraries.

Angular2WebpackstarterIfyoupreferdeclarativeandminimalisticbuildswithWebpack,youcanuseangular2-webpack-starter.ItisastarterprojectdevelopedbyAngularClassandhostedonGitHub.YoucanfinditatthefollowingURL:https://github.com/AngularClass/angular2-webpack-starter.

Thisstarterprovidesthefollowingfeatures:

ThebestpracticesinfileandapplicationorganizationforAngular2.Ready-to-gobuildsystemusingWebpackforworkingwithTypeScript.TestingAngular2codewithJasmineandKarma.CoveragewithIstanbulandKarma.End-to-endAngular2codeusingProtractor.TypemanagerwithTypings.

Inordertogiveitatry,youneedtohavenode.js,npm,andgitinstalled,andyouneedtorunthefollowingcommands:

gitclone--depth1https://github.com/angularclass/angular2-webpack-

starter.git

cdangular2-webpack-starter

npminstall

./node_modules/.bin/typingsinstall

npmstart

SummaryWestartedthisbookbyintroducingthereasonsbehindthedevelopmentofAngular2,whichwasfollowedbyaconceptualoverviewthatgaveusageneralideaaboutthebuildingblocksthattheframeworkprovidesforapplicationdevelopment.Inthenextstep,wedidaTypeScriptcrashcoursethatpreparedusforChapter4,GettingStartedwithAngular2ComponentsandDirectiveswherewewentdeepintoAngular’sdirectives,components,andchangedetection.

InChapter5,DependencyInjectioninAngular2weexplainedthedependencyinjectionmechanismandsawhowwecanmanagetherelationsbetweenthedifferentcomponentsbyusingit.Thenextchaptersexplainedtoushowwecanbuildformsandpipes,andtakeadvantageofAngular2’scomponent-basedrouter.

Bycompletingthecurrentchapter,wefinishedourjourneyintotheframework.Atthemomentofthiswriting,thedesigndecisionsandtheideasbehindAngular2’scorearesolidandfinalized.Althoughtheframeworkisstillbrandnew,inthepastcoupleofmonthsitsecosystemreachedalevelthatwecandevelopproduction-ready,high-performance,SEO-friendlyapplications,andontopofthis,haveagreatdevelopmentexperienceexploitingstatictypingandhotreloading.

IndexA

accessmodifierspublic/Usingaccessmodifiersprivate/Usingaccessmodifiersprotected/Usingaccessmodifiers

ambienttypedefinitionsusing/Usingambienttypedefinitionspredefinedambienttypedefinitions,using/Usingambienttypedefinitions,Customambienttypedefinitionscustom/Customambienttypedefinitionsts.dfiles,defining/Definingts.dfiles

AMD(AsynchronousModuleDefinition)/WritingmodularcodewithES2015angular-cli

used,forbootstrappingproject/Bootstrappingaprojectwithangular-cliusing/Usingangular-cliURL/Usingangular-cli

Angular2conceptualoverview/AconceptualoverviewofAngular2components/ComponentsinAngular2route,definitionsyntax/Angular2routedefinitionsyntaxHelloworld!application,building/TheHelloworld!applicationinAngular2playingwith/PlayingwithAngular2andTypeScriptDependencyInjection(DI)/DependencyInjectioninAngular2model-drivenforms,developing/Developingmodel-drivenformsinAngular2HTTPmodule,exploring/ExploringtheHTTPmoduleofAngularHTTPmodule,using/UsingAngular’sHTTPmodulebuilt-inpipes,using/UsingAngular’sbuilt-inpipesAsyncPipe,using/UsingAngular’sAsyncPipeandWebWorkers/WebWorkersandAngular2hotreloading/HotreloadinginAngular2about/Angular2quickstartersGulpseed/Angular2seedWebpackstarter/Angular2Webpackstarter

Angular2andTypeScriptindex,defining/Diggingintotheindex

Angular2directivesusing/UsingAngular2directivesngFordirective/ThengFordirectivedefining/DefiningAngular2directivesinputs,setting/Settingthedirective’sinputsconstructor,defining/Understandingthedirective’sconstructor

encapsulation/BetterencapsulationofdirectivesAngular2forms

using/UsingAngular2formsmodel-drivenapproach/UsingAngular2formstemplate-drivenforms,developing/Developingtemplate-drivenformstemplate-drivenform’smarkup,exploring/Diggingintothetemplate-drivenform’smarkupbuilt-informvalidators,using/Usingthebuilt-informvalidatorscustomcontrolvalidators,defining/Definingcustomcontrolvalidatorsselectinputs,using/UsingselectinputswithAngularNgFormdirective,using/UsingtheNgFormdirectiveused,fortwo-waydata-binding/Two-waydata-bindingwithAngular2data,storing/Storingtheformdata

Angular2routerexploring/ExploringtheAngular2routerrootcomponent,defining/Definingtherootcomponentandbootstrappingtheapplicationapplication,bootstrapping/DefiningtherootcomponentandbootstrappingtheapplicationPathLocationStrategy,using/UsingPathLocationStrategyroutes,configuringwith@RouteConfig/Configuringrouteswith@RouteConfigrouterLink,using/UsingrouterLinkandrouter-outletrouter-outlet,using/UsingrouterLinkandrouter-outletlazy-loading,withAsyncRoute/Lazy-loadingwithAsyncRoute

angular2-seedprojectURL/Angular2seed

AngularJS1.xabout/LessonslearnedfromAngularJS1.xinthewildcontrollers/Controllersscopeobject/Scopedependencyinjection(DI)/DependencyInjectionserver-siderendering/Server-siderenderingsingle-pageapplications/Applicationsthatscaletemplates/Templateschangedetection/Changedetection,AngularJS1.xchangedetectionchangedetection,enhancing/EnhancingAngularJS1.x’schangedetection

applicationrunninginWebWorkers,bootstrapping/BootstrappinganapplicationrunninginWebWorkermigrating,toWebWorker/MigratinganapplicationtoWebWorker

AsyncPipeusing/UsingAngular’sAsyncPipeusing,withobservables/UsingAsyncPipewithobservables

AsyncRouteused,forlazy-loading/Lazy-loadingwithAsyncRoute

Bblockscope

used,fordefiningvariables/Definingvariableswithblockscopebuilt-inchangedetection

dynamicchangedetection/ChangedetectionJITchangedetection/Changedetection

built-indirectives,Angular2using/UsingAngular2’sbuilt-indirectives

built-informvalidatorsusing/Usingthebuilt-informvalidatorsminlength/Usingthebuilt-informvalidatorsmaxlength/Usingthebuilt-informvalidators

built-inpipesusing/UsingAngular’sbuilt-inpipesCurrencyPipe/UsingAngular’sbuilt-inpipesDatePipe/UsingAngular’sbuilt-inpipesDecimalPipe/UsingAngular’sbuilt-inpipesJsonPipe/UsingAngular’sbuilt-inpipesLowerCasePipe/UsingAngular’sbuilt-inpipesUpperCasePipe/UsingAngular’sbuilt-inpipesPercentPipe/UsingAngular’sbuilt-inpipesSlicePipe/UsingAngular’sbuilt-inpipesAsyncPipe/UsingAngular’sbuilt-inpipes

Cchangedetection

about/Changedetection,AconceptualoverviewofAngular2,Changedetectionexample/ClassicalchangedetectioninAngularJS1.x/AngularJS1.xchangedetectioninzone.js/Inthezone.jssimplifieddata-flow/Simplifieddataflowenhancing,inAngularJS1.x/EnhancingAngularJS1.x’schangedetectiondefining/Understandingandenhancingthechangedetectionenhancing/Understandingandenhancingthechangedetectionorderofexecution/Theorderofexecutionofthechangedetectorsstrategies/Changedetectionstrategiesperformanceboosting,withimmutabledataandOnPush/PerformanceboostingwithimmutabledataandOnPushimmutabledatastructures,usinginAngular/UsingimmutabledatastructuresinAngular

childinjectorsabout/Childinjectorsandvisibilitydependencies,configuring/Configuringdependencieselementinjectors/Introducingtheelementinjectors

Codersrepositoryapplicationdeveloping/Developingthe“Codersrepository”applicationviews/Developingthe“Codersrepository”applicationbootstrapping/Definingtherootcomponentandbootstrappingtheapplication

CommonJS/WritingmodularcodewithES2015component-basedrouter

about/Understandingthenewcomponent-basedrouterAngular2route,definitionsyntax/Angular2routedefinitionsyntax

Componentclassabout/GettingtoknowAngular2components

componentsabout/AconceptualoverviewofAngular2,GettingtoknowAngular2componentscomposing/ComponentsinactioninAngular2/ComponentsinAngular2DependencyInjection(DI),using/UsingDIwithcomponentsanddirectivesDependencyInjection(DI),exploringwith/ExploringDIwithcomponents

Compositeclassabout/GettingtoknowAngular2components

containerabout/Configuringaninjector

contentchildren

about/Nestingcomponentscontentprojection,Angular2

defining/ExplainingAngular2’scontentprojectionabout/BasiccontentprojectioninAngular2multiplecontentchunks,projecting/Projectingmultiplecontentchunkscomponents,nesting/NestingcomponentsViewChildren,using/UsingViewChildrenandContentChildrenContentChildren,using/UsingViewChildrenandContentChildrenViewChild,versusContentChild/ViewChildversusContentChild

controllerassyntaxabout/Scope

controllersabout/Controllers

controllers,componentimplementing/Implementingthecomponent’scontrollers

controlvalidatorscomposition,using/Usingcompositionofcontrolvalidators

CreateRetrieveUpdateandDelete(CRUD)/Developingtemplate-drivenformsCSP(Content-Security-Policy)

about/UnderstandingandenhancingthechangedetectionCSSclasses

about/Diggingintothetemplate-drivenform’smarkupng-untouched/Diggingintothetemplate-drivenform’smarkupng-touched/Diggingintothetemplate-drivenform’smarkupng-pristine/Diggingintothetemplate-drivenform’smarkupng-dirty/Diggingintothetemplate-drivenform’smarkupng-valid/Diggingintothetemplate-drivenform’smarkupng-invalid/Diggingintothetemplate-drivenform’smarkup

customcontrolvalidatorsdefining/Definingcustomcontrolvalidators

Ddata

transforming,pipesused/Transformingdatawithpipesdecorators

URL/Meta-programmingwithES2016decoratorsDefinitelyTyped

URL/Usingambienttypedefinitionsdependencies,childinjectors

configuring/Configuringdependenciesselfdecorator,using/Usingthe@Selfdecoratorselfinjector,skipping/Skippingtheselfinjectoroptionaldependencies,using/Havingoptionaldependenciesmultiproviders,using/Usingmultiproviders

dependencyinjection(DI)about/DependencyInjection

DependencyInjection(DI)needfor/WhydoIneedDependencyInjection?inAngular2/DependencyInjectioninAngular2benefits/BenefitsofDIinAngular2using,withcomponentsanddirectives/UsingDIwithcomponentsanddirectivesexploring,withcomponents/ExploringDIwithcomponentsusing,withES5/UsingAngular’sDIwithES5

differsabout/AconceptualoverviewofAngular2

DImechanismabout/AconceptualoverviewofAngular2

directivesabout/AconceptualoverviewofAngular2modifying/ChangingdirectivesDependencyInjection(DI),using/UsingDIwithcomponentsanddirectives

directivessyntaxsemantics,defining/Improvedsemanticsofthedirectivessyntaxvariables,declaringinsidetemplate/Declaringvariablesinsideatemplatesyntaxsugarused,intemplates/Usingsyntaxsugarintemplates

Domain-SpecificLanguage(DSL)about/UsingAngular’sDIwithES5

DomainSpecificLanguage(DSL)about/Templates,Changingdirectives

dynamicchangedetectionabout/Changedetection

EECMAScript

evolution/TheevolutionofECMAScriptWebComponents/WebComponentsWebWorkers/WebWorkers

ECMAScript5(ES5)about/Changingdirectives

ECMAScript2015(ES2015)about/TheevolutionoftheWeb–timeforanewframework

elementinjectorsabout/Introducingtheelementinjectorsproviders,declaring/DeclaringprovidersfortheelementinjectorsDependencyInjection(DI),exploringwithcomponents/ExploringDIwithcomponentsviewProviders,versusproviders/viewProvidersversusproviders

enumtypesabout/TheEnumtypes

environment,Angular2settingup/Settingupourenvironmentreferences/Settingupourenvironmentprojectrepository,installing/InstallingourprojectrepositoryURL,forissues/Installingourprojectrepository

ES5DependencyInjection(DI),usingwith/UsingAngular’sDIwithES5

ES2015TypeScriptsyntax/TypeScriptsyntaxandfeaturesintroducedbyES2015andES2016TypeScriptfeatures/TypeScriptsyntaxandfeaturesintroducedbyES2015andES2016arrowfunctions/ES2015arrowfunctionsandES2016classes,using/UsingtheES2015andES2016classesmodularcode,writingwith/WritingmodularcodewithES2015modulesyntax,using/UsingtheES2015modulesyntaximplicitasynchronousbehavior/Takingadvantageoftheimplicitasynchronousbehavioraliases,using/Usingaliasesmoduleexports,importing/Importingallthemoduleexportsdefaultexports/Defaultexportsmoduleloader/ES2015moduleloaderandES2016,recap/ES2015andES2016recap

ES2016decoratorsmeta-programmingwith/Meta-programmingwithES2016decoratorsconfigurabledecorators,using/Usingconfigurabledecorators

Ffactories

using/Usingexistingprovidersdefining,forinstantiatingservices/Definingfactoriesforinstantiatingservices

forwardreferencesabout/Introducingforwardreferences

Ggenericcode

writing,typeparametersused/Writinggenericcodebyusingtypeparametersgenericfunctions,using/Usinggenericfunctionsmultipletypeparameters,having/Havingmultipletypeparameters

genericviewsdefining,withTemplateRef/DefininggenericviewswithTemplateRef

GitHubAPItokenreferencelink/ExploringtheHTTPmoduleofAngular

GoogleClosureCompiler/BettersupportbytexteditorsandIDEs

HHelloworld!application

defining,inAngular2/TheHelloworld!applicationinAngular2hostinjectors

about/Introducingtheelementinjectors,ExploringDIwithcomponentshotreloading

about/HotreloadinginAngular2/HotreloadinginAngular2

HTTPmoduleexploring/ExploringtheHTTPmoduleofAngularusing/UsingAngular’sHTTPmodule

IIDEs/TexteditorsandIDEsimplicitasynchronousbehavior,ES2015/Takingadvantageoftheimplicitasynchronousbehaviorinjector

about/Configuringaninjectorconfiguring/Configuringaninjectordependencyresolution,withgeneratedmetadata/Dependencyresolutionwithgeneratedmetadatainstantiating/Instantiatinganinjectorforwardreferences/Introducingforwardreferencesproviders,configuring/Configuringprovidersfactories,using/Usingexistingprovidersfactories,definingforinstantiatingservices/Definingfactoriesforinstantiatingserviceschildinjectors/Childinjectorsandvisibilityhierarchy,building/Buildingahierarchyofinjectors

inlinecachingreferencelink/Changedetection

interfacesabout/Defininginterfacesinheritance/Interfaceinheritancemultipleinterfaces,implementing/Implementingmultipleinterfaces

inversionofcontrol(IoC)about/DependencyInjection

isomorphic/Server-siderenderingwithAngular2

JJITchangedetection

about/Changedetection

LLeafclass

about/GettingtoknowAngular2componentslessverbosecode

writing,withTypeScriptstypeinference/WritinglessverbosecodewithTypeScript’stypeinferencebestcommontype/Bestcommontypecontextualtypeinference/Contextualtypeinference

lifecycle,componenthookinginto/Hookingintothecomponent’slifecycle

livereload/Hotreloading

MMassiveViewController(MVC)

about/Controllersmodel-drivenforms,Angular2

developing/Developingmodel-drivenformsinAngular2controlvalidatorscomposition,using/Usingcompositionofcontrolvalidators

Model-View-Controller(MVC)about/GettingtoknowAngular2components

Model-View-ViewModel(MVVM)about/GettingtoknowAngular2components

Model-View-Whatever(MVW)about/Changedetection

ModelViewController(MVC)about/Controllers

ModelViewPresenter(MVP)about/Controllers

ModelViewViewModel(MVVM)about/Controllers

ModelViewWhatever(MVW)about/Controllers

multiprovidersusing/Usingmultiproviders

Nn

URL/Settingupourenvironmentnestedroutes

defining/DefiningnestedroutesNgFormdirective

using/UsingtheNgFormdirectivenode.js

URL/UsingTypeScriptNodePackageManager(npm)/UsingTypeScript

used,forinstallingTypeScript/InstallingTypeScriptwithnpmnvm

URL/Settingupourenvironment

Oobject-oriented(OO)paradigm/UsingtheES2015andES2016classesObjecttypes

Arraytypes/TheArraytypesFunctiontypes/TheFunctiontypes

operationabout/GettingtoknowAngular2components

orderofexecutiontracing/Theorderofexecution

Pparameterizedviews

defining/DefiningparameterizedviewsPathLocationStrategy

using/UsingPathLocationStrategypipes

about/AconceptualoverviewofAngular2,Pipesdefining/Definingpipesused,fordatatransformations/Transformingdatawithpipesstatelesspipes,developing/Developingstatelesspipesbuilt-inpipes,using/UsingAngular’sbuilt-inpipesstatefulpipes,developing/Developingstatefulpipes

preboot.js/Server-siderenderingwithAngular2primitivetypes

about/UnderstandingthePrimitivetypesproviders

about/Configuringaninjectorconfiguring/Configuringprovidersexistingproviders,using/Usingexistingprovidersdeclaring,forelementinjectors/DeclaringprovidersfortheelementinjectorsversusviewProviders/viewProvidersversusproviders

R@RouteConfig

used,forrouteconfiguration/Configuringrouteswith@RouteConfigrootcomponent

defining/Definingtherootcomponentandbootstrappingtheapplicationroute

definitionsyntax/Angular2routedefinitionsyntaxrouter-outlet

using/UsingrouterLinkandrouter-outletrouterLink

using/UsingrouterLinkandrouter-outletRxJSGitHubrepository

about/TheHelloworld!applicationinAngular2URL/TheHelloworld!applicationinAngular2

Sscopeobject

about/ScopeSearchEngineOptimization(SEO)

about/Server-siderenderingselectinputs

using,withAngular2forms/UsingselectinputswithAngularselfdecorator

using/Usingthe@Selfdecoratorselfinjector

skipping/SkippingtheselfinjectorSEO(SearchEngineOptimization)

andUI/Initialloadofasingle-pageapplicationserver-siderendering

about/Server-siderenderingservices

about/AconceptualoverviewofAngular2,Understandingservicesinstantiating,withfactories/Definingfactoriesforinstantiatingservices

single-pageapplicationinitialload/Initialloadofasingle-pageapplicationinitialload,withenabledserver-siderendering/InitialloadofaSPAwithserver-siderenderingserver-siderendering,withAngular2/Server-siderenderingwithAngular2

single-pageapplicationsabout/Applicationsthatscale

single-pageapplications(SPA)about/Understandingthenewcomponent-basedrouter

statefulpipesdeveloping/Developingstatefulpipesusing/Usingstatefulpipes

statelesspipesdeveloping/Developingstatelesspipes

statictypingabout/Takingadvantageofstatictypingexplicittypedefinitions,using/Usingexplicittypedefinitionstypeany/Thetypeanyprimitivetypes/UnderstandingthePrimitivetypesenumtypes/TheEnumtypesObjecttypes/UnderstandingtheObjecttypesArraytypes/TheArraytypesFunctiontypes/TheFunctiontypesclasses,defining/Definingclassesaccessmodifiers,using/Usingaccessmodifiers

interfaces,defining/Defininginterfacesinterfaceinheritance/Interfaceinheritancemultipleinterfaces,implementing/Implementingmultipleinterfaces

storeddeveloperslisting/Listingallthestoreddevelopers

subtyping/Defininginterfaces

Ttemplate-drivenforms

developing/Developingtemplate-drivenformsmarkup,exploring/Diggingintothetemplate-drivenform’smarkup

TemplateRefgenericviews,definingwith/DefininggenericviewswithTemplateRef

templatesabout/Templates

texteditorsandIDEs/TexteditorsandIDEs

timetolive(TTL)about/Applicationsthatscale

Todoapplicationcomponents/Findingoutdirectives’inputsandoutputs

tokenabout/Configuringaninjector

transpilationabout/TheevolutionofECMAScript

TransportLayerSecurity(TLS)about/Definingfactoriesforinstantiatingservices

two-waydata-bindingusing,withAngular2/Two-waydata-bindingwithAngular2

typeinferenceused,forwritinglessverbosecode/WritinglessverbosecodewithTypeScript’stypeinference

typeparametersused,forwritinggenericcode/Writinggenericcodebyusingtypeparametersgenericfunctions,using/Usinggenericfunctionsmultipletypeparameters,having/Havingmultipletypeparameters

types,TypeScriptabout/Thetypeanyprimitivetypes/Thetypeanyuniontypes/Thetypeanyobjecttypes/Thetypeanytypeparameters/Thetypeany

TypeScriptabout/IntroductiontoTypeScriptcompile-timetypechecking/Compile-timetypecheckingtexteditorsandIDEs,support/BettersupportbytexteditorsandIDEsbenefits/There’sevenmoretoTypeScriptusing/UsingTypeScript,TheHelloworld!applicationinAngular2installing,npmused/InstallingTypeScriptwithnpmprogram,running/RunningourfirstTypeScriptprogram

syntax,byES2015andES2016/TypeScriptsyntaxandfeaturesintroducedbyES2015andES2016features,byES2015andES2016/TypeScriptsyntaxandfeaturesintroducedbyES2015andES2016ES2015arrowfunctions/ES2015arrowfunctionsES2015andES2016classes,using/UsingtheES2015andES2016classesvariables,definingwithblockscope/Definingvariableswithblockscopedecorators/FurtherexpressivenesswithTypeScriptdecoratorsplayingwith/PlayingwithAngular2andTypeScript

UUniversal/Server-siderenderingwithAngular2universalstarter

URL/Server-siderenderingwithAngular2useractions

handling/Handlinguseractionsdirectives’inputandoutput,using/Usingadirectives’inputsandoutputsdirectives’inputandoutput,findingout/Findingoutdirectives’inputsandoutputscomponent’sinputandoutput,defining/Definingthecomponent’sinputsandoutputsinput,passing/Passinginputsandconsumingtheoutputsoutput,consuming/Passinginputsandconsumingtheoutputseventbubbling/Eventbubblinginputandoutputofdirective,renaming/Renamingtheinputsandoutputsofadirectivealternativesyntax,fordefininginputandoutput/Analternativesyntaxtodefineinputsandoutputs

Vviewchildren

about/Nestingcomponentsviewencapsulation,component

defining/Introducingthecomponent’sviewencapsulationviewProviders

versusproviders/viewProvidersversusprovidersviews,Codersrepositoryapplication

basicdetails/Developingthe“Codersrepository”application

WWeb

evolution/TheevolutionoftheWeb–timeforanewframeworkWebComponents

about/TheevolutionoftheWeb–timeforanewframeworkevolution/WebComponents

WebpackstarterURL/Angular2Webpackstarter

WebWorkersapplications,running/RunningapplicationsinWebWorkersabout/RunningapplicationsinWebWorkersandAngular2/WebWorkersandAngular2application,bootstrapping/BootstrappinganapplicationrunninginWebWorkerandUI/BootstrappinganapplicationrunninginWebWorkerapplication,migratingto/MigratinganapplicationtoWebWorkercompatible,applicationcreating/MakinganapplicationcompatiblewithWebWorkers

WebWorkersabout/TheevolutionoftheWeb–timeforanewframework,WebWorkers

Zzone.js

changedetection/Inthezone.js

Recommended