421
www.EBooksWorld.ir

dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

  • Upload
    others

  • View
    7

  • Download
    0

Embed Size (px)

Citation preview

Page 1: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

www.EBooksWorld.ir

Page 2: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

LearningAngular2

www.EBooksWorld.ir

Page 3: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

TableofContents

LearningAngular2CreditsAbouttheAuthorAcknowledgmentsAbouttheReviewerwww.PacktPub.com

eBooks,discountoffers,andmoreWhysubscribe?

PrefaceWhatthisbookcoversWhatyouneedforthisbookWhothisbookisforConventionsReaderfeedbackCustomersupport

DownloadingtheexamplecodeErrataPiracyQuestions

1.CreatingOurVeryFirstComponentinAngular2Afreshstart

WebcomponentsWhyTypeScriptoverothersyntaxes?

SettingupourworkspaceInstallingdependenciesInstallingTypeScriptInstallingTypeScripttypings

Hello,Angular2!TypeScriptclassesIntroducingmetadatadecoratorsCompilingTypeScriptintobrowser-friendlyJavaScriptTheHTMLcontainerServingtheexamplesofthisbookPuttingeverythingtogether

EnhancingourIDESublimeText3AtomVisualStudioCodeWebStormLeveragingGulpwithotherIDEs

DivingdeeperintoAngular2components

www.EBooksWorld.ir

Page 4: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

ImprovingproductivityComponentmethodsanddataupdatesAddinginteractivitytothecomponentImprovingthedataoutputintheviewandpolishingtheUI

Summary2.IntroducingTypeScript

UnderstandingthecaseforTypeScriptThebenefitsofTypeScriptIntroducingTypeScriptresourcesinthewild

TheTypeScriptofficialsiteTheTypeScriptWiki

TypesinTypeScriptString

DeclaringourvariablestheECMAScript6wayNumber

BooleanArray

DynamictypingwiththeanytypeEnum

VoidTypeinference

Functions,lambdas,andexecutionflowAnnotatingtypesinourfunctionsFunctionparametersinTypeScript

OptionalparametersDefaultparametersRestparametersOverloadingthefunctionsignature

BetterfunctionsyntaxandscopehandlingwithlambdasClasses,interfaces,andclassinheritance

Anatomyofaclass–constructors,properties,methods,getters,andsettersInterfacesinTypeScriptExtendingclasseswithclassinheritance

DecoratorsinTypeScriptClassdecorators

ExtendingtheclassdecoratorfunctionsignaturePropertydecoratorsMethoddecoratorsParameterdecorators

OrganizingourapplicationswithmodulesInternalmodulesExternalmodules

TheroadaheadSummary

www.EBooksWorld.ir

Page 5: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

3.ImplementingPropertiesandEventsinOurComponentsAbettertemplatesyntax

DatabindingswithinputpropertiesSomeextrasyntacticsugarwhenbindingexpressionsEventbindingwithoutputpropertiesInputandoutputpropertiesinaction

SettingupcustomvaluesdeclarativelyCommunicatingbetweencomponentsthroughcustomevents

EmittingdatathroughcustomeventsLocalreferencesintemplatesAlternativesyntaxforinputandoutputproperties

ConfiguringourtemplatefromourcomponentclassInternalandexternaltemplatesEncapsulatingCSSstyling

ThestylespropertyThestyleUrlspropertyInlinestylesheets

ManagingviewencapsulationSummary

4.EnhancingOurComponentswithPipesandDirectivesDirectivesinAngular2

CoredirectivesNgIfNgForNgStyleNgClassNgSwitch,NgSwitchWhen,andNgSwitchDefault

ManipulatingtemplatebindingswithPipesTheuppercase/lowercasepipeThenumber,percent,andcurrencypipes

ThenumberpipeThepercentpipeThecurrencypipe

TheslicepipeThedatepipeTheJSONpipeThereplacepipeThei18npipes

Thei18nPluralpipeThei18nSelectpipe

TheasyncpipePuttingitalltogetherinthePomodorotasklist

SettingupourmainHTMLcontainerBuildingourtasklisttablewithAngulardirectives

www.EBooksWorld.ir

Page 6: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

TogglingtasksinourtasklistDisplayingstatechangesinourtemplatesEmbeddingchildcomponents

BuildingourowncustompipesAnatomyofacustompipeAcustompipetobetterformattimeoutputFilteringoutdatawithcustomfilters

BuildingourowncustomdirectivesAnatomyofacustomdirectiveBuildingatasktooltipcustomdirective

AwordaboutnamingconventionsforcustomdirectivesandpipesSummary

5.BuildinganApplicationwithAngular2ComponentsIntroducingthecomponenttreeCommonconventionsforscalableapplications

FileandmodulenamingconventionsEnsuringseamlessscalabilitywithfacadesorbarrels

HowdependencyinjectionworksinAngular2Injectingdependenciesacrossthecomponenttree

RestrictingdependencyinjectiondownthecomponenttreeRestrictingproviderlookup

OverridingprovidersintheinjectorhierarchyExtendinginjectorsupporttocustomentitiesInitializingapplicationswithbootstrap()

SwitchingbetweendevelopmentandproductionmodesEnablingAngular2'sbuilt-inchangedetectionprofiler

IntroducingthePomodoroAppdirectorystructureRefactoringourapplicationtheAngular2way

ThesharedcontextServicesinthesharedcontextConfiguringapplicationsettingsfromacentralservice

CreatingafacademoduleincludingacustomprovidersbarrelCreatingourcomponents

ThetimercontextThetaskscontextDefiningthetoprootcomponent

BootstrappingtheapplicationSummary

6.AsynchronousDataServiceswithAngular2Strategiesforhandlingasynchronousinformation

ObservablesinanutshellReactivefunctionalprogramminginAngular2

TheRxJSlibraryIntroducingtheHTTPAPI

www.EBooksWorld.ir

Page 7: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

WhentousetheRequestandRequestOptionsArgsclassesTheResponseobjectHandlingerrorswhenperformingHttprequestsInjectingtheHttpclassandtheHTTP_PROVIDERSmodulessymbol

Arealcasestudy–servingObservabledatathroughHTTPAddingtaskstoourtasksservice

Summary7.RoutinginAngular2

AddingsupportfortheAngular2routerSettinguptherouterservice

BuildinganewcomponentfordemonstrationpurposesConfiguringtheRouteConfigdecoratorwiththeRouteDefinitioninstancesTherouterdirectives–RouterOutletandRouterLinkTriggeringroutesimperativelyCSShooksforactiveroutes

HandlingrouteparametersPassingdynamicparametersinourroutesParsingrouteparameterswiththeRouteParamsservice

DefiningchildroutersLinkingtochildroutes

TheRouterlifecyclehooksTheCanActivatehookTheOnActivateHookTheCanDeactivateandOnDeactivatehooksTheCanReuseandOnReusehooks

AdvancedtipsandtricksRedirectingtootherroutesTweakingthebasepathFinetuningourgeneratedURLswithlocationstrategiesLoadingcomponentsasynchronouslywithAsyncRoutes

Summary8.FormsandAuthenticationHandlinginAngular2

Two-waydatabindinginAngular2TheNgModeldirectiveBindingatypetoaformwithNgModel

BypassingtheCanDeactivaterouterhookuponsubmittingformsTrackingcontrolinteractionandvalidatinginput

TrackingchangeswithlocalreferencesControls,ControlGroups,andtheFormBuilderclass

IntroducingControlsandValidatorsControlsintheDOM–thengControldirectiveGroupingcontrolsintheDOMwithNgControlGroupDefiningcontrolgroupsimperativelywithControlGroupConnectingtheDOMandthecontrollerwithngFormModel

www.EBooksWorld.ir

Page 8: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Arealexample–ourlogincomponentTheloginfeaturecontextTheloginformtemplateThelogincomponentApplyingcustomvalidationtoourcontrolsWatchingstatechangesinourcontrols

MockingaclientauthenticationserviceExposingournewservicetoothercomponentsBlockingunauthorizedaccessMakingtheUIreactivetotheuserauthenticationstatus

RunningtheextramileonaccessmanagementBuildingourownsecureRouterOutletdirective

Summary9.AnimatingComponentswithAngular2

CreatinganimationswithplainvanillaCSSHandlinganimationwithCSSclasshooks

ClasshooksavailableAnimatingcomponentswiththeAnimationBuilder

TheCssAnimationBuilderAPITrackinganimationstatewiththeAnimationclass

DevelopingcustomanimationdirectivesInteractingwithourdirectivefromthetemplate

LookingintothefuturewithngAnimate2.0Summary

10.UnittestinginAngular2Whydoweneedtests?PartsofaunittestinAngular2

DependencyinjectioninunittestsSettingupourtestenvironment

ImplementingourtestrunnerSettingupNPMcommands

Angular2custommatcherfunctionsTestingpipesTestingcomponents

TestingcomponentswithdependenciesOverridingcomponentdependenciesforrefinedtesting

TestingroutesTestingroutesbyURLTestingredirections

TestingservicesTestingasynchronousservicesMockingHttpresponseswithMockBackend

TestingdirectivesTheroadahead

www.EBooksWorld.ir

Page 9: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

UsingJasmineincombinationwithKarmaIntroducingcodecoveragereportsinyourteststackImplementingE2Etests

SummaryIndex

www.EBooksWorld.ir

Page 10: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

LearningAngular2

www.EBooksWorld.ir

Page 11: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

LearningAngular2Copyright©2016PacktPublishing

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

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

PacktPublishinghasendeavoredtoprovidetrademarkinformationaboutallofthecompaniesandproductsmentionedinthisbookbytheappropriateuseofcapitals.However,PacktPublishingcannotguaranteetheaccuracyofthisinformation.

Firstpublished:May2016

Productionreference:2260516

PublishedbyPacktPublishingLtd.

LiveryPlace

35LiveryStreet

BirminghamB32PB,UK.

ISBN978-1-78588-207-4

www.packtpub.com

www.EBooksWorld.ir

Page 12: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

CreditsAuthor

PabloDeeleman

Reviewer

JohannesWeber

CommissioningEditor

SarahCrofton

AcquisitionEditor

ReshmaRaman

ContentDevelopmentEditor

SamanthaGonsalves

TechnicalEditor

MohitaVyas

CopyEditors

RoshniBanerjee

AkshataLobo

ProjectCoordinator

SanchitaMandal

Proofreader

SafisEditing

Indexer

PriyaSane

Graphics

www.EBooksWorld.ir

Page 13: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

KirkD'Penha

ProductionCoordinator

NileshR.Mohite

CoverWork

NileshR.Mohite

www.EBooksWorld.ir

Page 14: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

AbouttheAuthorPabloDeelemanisaformerUXdesignerandfrontendengineerwhodiscoveredtheWebbackinthe90s,whena14,400bpsmodemwasthekeytoanunparalleledworldofmarvelsandabuild-your-own-websitewasthenameofthegame.

AftergettinghisBA(Hons)degreeinmarketingandmovingthroughdifferentrolesintheadvertisingarena,hetookhischanceandevolvedintoaself-taught,passionateUXdesignerandfrontenddeveloperwithacrunchforbeautifullycraftedCSSlayoutsandJavaScriptthickclients,havingproducedcountlessinteractivedesignsandwebdesktopandmobileapplicationseversince.

Duringtheseyears,hehasfulfilledhiscareerasbothanUXdesignerandfrontenddeveloperbysuccessfullyleadingInternetprojectsforawiderangeofclientsandteams,encompassingEuropeanonlinetraveloperators,SiliconValley-basedstart-ups,internationalheavy-traffictubewebsites,globalbankingportals,orgamblingandmobilegamingcompanies,justtonameafew.Atsomepointalongthisjourney,theriseofNode.jsandsingle-page-applicationframeworksbecameaturningpointinhiscareer,beingcurrentlyfocusedonbuildingJavaScript-drivenwebexperiences.

Afterhavinglivedandworkedinseveralcountries,PabloDeelemancurrentlylivesinBarcelona,Spain,whereheleadsthefrontendendeavorintheBarcelonastudioofGameloft,theworldleaderinmobilegaming,andthehomeofinternationallyacclaimedgames,suchasDespicableMe:MinionsRushandAsphalt8.

Whennotwritingbooksortakingpartinindustryeventsandtalks,hespendsmostofhistimefulfillinghisotherpassion:playingpianoandguitar.

www.EBooksWorld.ir

Page 15: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

AcknowledgmentsThebookyouholdinyourhandsrightnowistheresultofalotoftime,effort,andsacrifice.Someonewiselysaidoncethatwritingabookaboutaframeworkinthealphastageislikeaimingatamovingtarget,andindeeditis.Duringthewriting,theauthorandtheteaminvolvedinthisprojectwounduplosingtrackofhowmanytimeswehadrewritteneverythingtoconformtothelatestincarnationoftheframework.Intheheatofthebattle,itisquiteeasytofallundertheweightoffrustrationandseriouslyconsiderwhethersuchaprojectisworththeeffortornot.Inthatsense,thisiswhyIonlyhavewordsofappreciationfortheteamatPacktandmostparticularlyforSamanthaGonsalves.HerkindwordsofsupportfueledtheenergyIneededtomovethisprojectahead.

IwouldalsoliketospeciallythankmyfriendandtechauthorJorgeFerrandoforhisguidanceandhintsduringtheproductionprocessforthisbook.HisexpertiseinAngular2becamepricelesswhenassessingthedifferentcoursesofactiontodeliverthebestlearningexperience.AmentionisrequiredaswellforourotherfellowdevelopersJavierGómez,AlfonsoFernández,FranIruela,andPedroNarciso.

I'dliketoalsothankthepeoplewhohavementoredmeandaccompaniedmealongthisprofessionaljourneyovertheseyears,withaspecialmentionforthepeopleatCasumoandGameloft,andmostspecificallyandinnoparticularorder,forRazmusSvenningson,KimLarsen,JosefGalea,SteveAttard,IdenAzzopardi,RenaldDalli,MatthewBorg,MarkBusuttil,GerardGiné,AntonioGonzález,AlbertPuértolas,RafaelMarfilandthealwaysinspiringStuartLangridge.

www.EBooksWorld.ir

Page 16: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

AbouttheReviewerJohannesWeberisapassionatedeveloperandadviserinthefieldofwebtechnologiesspotlightedonenterpriseJSapps.HeworksforMayflowerGmbH(Munich,Germany),wherehefocusesonthemigrationofSPAandMPA.Inhisfreetime,he(co)organizestheAngularJSMunichmeetups,AngularCampandJS-Kongress.de.JohannescofoundedESnextNews.com,whereyougetfivegreatECMAScript.nextlinkseveryweekinyourinbox.

www.EBooksWorld.ir

Page 17: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

www.PacktPub.com

www.EBooksWorld.ir

Page 18: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

eBooks,discountoffers,andmoreDidyouknowthatPacktofferseBookversionsofeverybookpublished,withPDFandePubfilesavailable?YoucanupgradetotheeBookversionatwww.PacktPub.comandasaprintbookcustomer,youareentitledtoadiscountontheeBookcopy.Getintouchwithusat<[email protected]>formoredetails.

Atwww.PacktPub.com,youcanalsoreadacollectionoffreetechnicalarticles,signupforarangeoffreenewslettersandreceiveexclusivediscountsandoffersonPacktbooksandeBooks.

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

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

www.EBooksWorld.ir

Page 19: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Whysubscribe?FullysearchableacrosseverybookpublishedbyPacktCopyandpaste,print,andbookmarkcontentOndemandandaccessibleviaawebbrowser

ThisbookisdedicatedtomyparentsPaulandPepa,andinthelovingmemoryofmybrotherJoséRaúl.

Youwillliveforeverinourhearts.

www.EBooksWorld.ir

Page 20: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

PrefaceOverthepastyears,Angular1.xhasbecameoneofthemostubiquitousJavaScriptframeworksforbuildingcuttingedgewebapplications,eitherbigorsmall.Atsomepoint,itsshortcomingswithregardtoperformanceandscalabilitybecametooprominentassoonasapplicationsgrewinsizeandcomplexity.Angular2wasthenconceivedasafullrewritefromscratchtofulfilltheexpectationsofmoderndevelopers,whodemandblazingfastperformanceandresponsivenessintheirwebapplications.

Angular2hasbeendesignedwithmodernwebstandardsinmindandallowsfullflexibilitywhenpickingupyourlanguageofchoice,providingfullsupportforES6andTypeScript,butworkingequallywellwithtoday'sES5,Dart,orCoffeeScript.Itsbuilt-independencyinjectionfunctionalitieslettheuserbuildhighlyscalableandmodularapplicationswithanexpressiveandself-explanatorycode,turningmaintainabilitytasksintoabreeze,whilesimplifyingtest-drivendevelopmenttothemax.However,whereAngular2standsoutiswhenitshowsoffitsunparalleledlevelofspeedandperformance,thankstoitsnewchangedetectionsystemthatisuptofivetimesfasterthanitspreviousincarnation.Cleanerviewsandanunsurpassedstandards-complianttemplatingsyntaxcompoundanendlesslistofpowerfulfeaturesforbuildingthenextgenerationofwebmobileanddesktopapps.

Angular2isheretostayandwillbecomeagamechangerinthewaymodernwebapplicationsareenvisionedanddevelopedintheyearstocome.However,andduetoitsdisruptivedesignandarchitecture,learningAngular2mightseemadauntingefforttonewcomers.

Thisiswherethisbookcomesin—itsgoalistoavoidbloatingthereaderwithAPIreferencesandframeworkdescriptions,buttoembraceahands-onapproach,helpingthereaderlearnhowtoleveragetheframeworktobuildstuffthatmattersrightfromdayone.Thisislearningbydoingrightfromthestart.

ThisbookaimstogivedevelopersacompletewalkthroughofthisnewplatformanditsTypeScript-flavoredsyntaxbybuildingawebprojectfrombacktoforth,startingfromthebasicconceptsandsamplecomponentsanditeratingonthemtobuildupmorecomplexfunctionalitiesineverychapteruntilwelaunchacomplete,tested,production-readysamplewebapplicationbytheendofthebook.

www.EBooksWorld.ir

Page 21: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

WhatthisbookcoversChapter1,CreatingOurVeryFirstComponentinAngular2,introducesthereadertowebcomponents,whicharethebuildingblocksofallAngular2applications.

Chapter2,IntroducingTypeScript,instructsthereaderaboutthesyntaxandparticularitiesofthistypedsupersetofECMAScript6,beinginfactthesyntaxofchoiceoftheAngularteamforbuildingAngular2.

Chapter3,ImplementingPropertiesandEventsinOurComponents,describeshowourcomponentsbehavelikestatemachinesthatcanchangetheirstatebyreceivingdatathroughtheirinputpropertiesandemitdataaseventsthroughtheiroutputproperties.

Chapter4,EnhancingourComponentswithPipesandDirectives,givesacompletewalkthroughoftheframework'sbuilt-inpipesusedtodigestdataoutputinourtemplates,andalsothebuilt-indirectivesthatprovideadvancedfunctionalitytoourcomponent.Thereaderwillalsolearnhowtocreatecustompipesordirectives

Chapter5,BuildinganApplicationwithAngular2Components,devotesanentirechaptertorecapwhatwehavelearnedsofarandorchestrateseverythingtoensureourAngular2projectsscalewellregardlesstheirsizeandconformtothecommunitycodingandnamingconventions.

Chapter6,AsynchronousDataServiceswithAngular2,teachesthereaderhowtoimplementanddeployHTTPconnectionswithotherdataservicesbymeansoftheHttpmodule,sowecancreateourowndataserviceclients.

Chapter7,RoutinginAngular2,introducesthereadertoAngular2'srouteranditsbuilt-indirectives,providingacompletewalkthroughthedifferentstrategieswehavetoloadcomponentsfromroutesandhandlethestatethroughtheHistoryAPI.

Chapter8,FormsandAuthenticationHandlinginAngular2,illustratesthedifferentstrategieswehaveatourdisposaltobuildwebformswithAngular2,managetwo-waydatabindingoninputcontrols,andcreatecomplexformsandvalidations.

Chapter9,AnimatingComponentswithAngular2,coversthecurrentlyavailabletoolsandclassesforimplementinganimationsonourcomponents,frompureCSSanimationshandledwithAngular2directivestomorecomplextransitionspurelymanagedthroughJavaScript,thankstoAngular2animationbuilders.

Chapter10,UnitTestinginAngular2,willguidethereaderthroughthestepsrequiredforimplementingasoundtestingfoundationinourapplication,andthegeneralpatternsfordeployingunittestsoncomponents,directives,pipes,routes,andservices.

www.EBooksWorld.ir

Page 22: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

WhatyouneedforthisbookInordertodeveloptheexamplescontainedinthisbook,youwillprimarilyneedawebbrowserupdatedtoitslatestversion.WerecommendGoogleChromeorMozillaFirefox,althoughAngular2ismeanttobesupportedinallevergreenbrowsers.

YouwillalsoneedterminalsoftwareinstalledonyourOS,sincemanyoperationsarehandledthroughnpmcommandstotheconsole.Inthissense,havingNode.jsandnpminstalledonyoursystemwillberequiredtorunmostoftheconsolecommandsmentionedinthebook.Therestofmodulesrequiredandtheirinstallationprocedurewillbedescribedaswego.

Last,butnotleast,youwillrequireatexteditortocodeyourAngular2modules,althoughChapter1,CreatingOurVeryFirstComponentinAngular2willprovideathoroughwalkthroughofallthebestIDEalternativesinstorenowadaysfordevelopingAngular2applications.

www.EBooksWorld.ir

Page 23: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

WhothisbookisforThisbookistargetedatwebdeveloperswhowanttobuildthenextgenerationofstate-of-the-artmobileanddesktopwebapplicationswithAngular2.ThisbookdoesnotrequireyoutohavepriorexposuretoeitherAngular1.xor2,althoughcomprehensiveknowledgeofJavaScriptisassumed.It'sgreatfornewcomerstoAngularwholearnbestthroughclearexplanationsanddefinitionofconcepts.

www.EBooksWorld.ir

Page 24: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

ConventionsInthisbook,youwillfindanumberoftextstylesthatdistinguishbetweendifferentkindsofinformation.Herearesomeexamplesofthesestylesandanexplanationoftheirmeaning.

Codewordsintext,databasetablenames,foldernames,filenames,fileextensions,pathnames,dummyURLs,userinput,andTwitterhandlesareshownasfollows:"Asaresultofthisaction,wewillfindanewtsconfig.jsonfileattherootofourproject,includingthesettingsrequiredbytheTypeScriptcompilertotranspilethecomponentcodeintoplainECMAScript5JavaScriptcodereadablebycurrentbrowsers."

Ablockofcodeissetasfollows:

<body>

<navclass="navbarnavbar-defaultnavbar-static-top">

<divclass="container">

<divclass="navbar-header">

<strongclass="navbar-brand">MyPomodoroTimer</strong>

</div>

</div>

</nav>

<pomodoro-timer></pomodoro-timer>

</body>

Whenwewishtodrawyourattentiontoaparticularpartofacodeblock,therelevantlinesoritemsaresetinbold:

<body>

<navclass="navbarnavbar-defaultnavbar-static-top">

<divclass="container">

<divclass="navbar-header">

<strongclass="navbar-brand">MyPomodoroTimer</strong>

</div>

</div>

</nav>

<pomodoro-timer></pomodoro-timer>

</body>

Anycommand-lineinputoroutputiswrittenasfollows:

$npminstallangular2es6-shimes6-promisereflect-metadatarxjszone.js--

save

Newtermsandimportantwordsareshowninbold.Wordsthatyouseeonthescreen,forexample,inmenusordialogboxes,appearinthetextlikethis:"Thelearnsectiongivesusaccesstoaquicktutorialtogetuptospeedwiththelanguageinnotime."

Note

Warningsorimportantnotesappearinaboxlikethis.

www.EBooksWorld.ir

Page 25: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Tip

Tipsandtricksappearlikethis.

www.EBooksWorld.ir

Page 26: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

ReaderfeedbackFeedbackfromourreadersisalwayswelcome.Letusknowwhatyouthinkaboutthisbook—whatyoulikedordisliked.Readerfeedbackisimportantforusasithelpsusdeveloptitlesthatyouwillreallygetthemostoutof.

Tosendusgeneralfeedback,simplye-mail<[email protected]>,andmentionthebook'stitleinthesubjectofyourmessage.

Ifthereisatopicthatyouhaveexpertiseinandyouareinterestedineitherwritingorcontributingtoabook,seeourauthorguideatwww.packtpub.com/authors.

www.EBooksWorld.ir

Page 27: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

CustomersupportNowthatyouaretheproudownerofaPacktbook,wehaveanumberofthingstohelpyoutogetthemostfromyourpurchase.

www.EBooksWorld.ir

Page 28: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

DownloadingtheexamplecodeYoucandownloadtheexamplecodefilesforthisbookfromGitHubathttps://github.com/deeleman/learning-angular2.

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

Youcandownloadthecodefilesbyfollowingthesesteps:

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

YoucanalsodownloadthecodefilesbyclickingontheCodeFilesbuttononthebook'swebpageatthePacktPublishingwebsite.Thispagecanbeaccessedbyenteringthebook'snameintheSearchbox.PleasenotethatyouneedtobeloggedintoyourPacktaccount.

Oncethefileisdownloaded,pleasemakesurethatyouunziporextractthefolderusingthelatestversionof:

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

www.EBooksWorld.ir

Page 29: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

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.

www.EBooksWorld.ir

Page 30: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

PiracyPiracyofcopyrightedmaterialontheInternetisanongoingproblemacrossallmedia.AtPackt,wetaketheprotectionofourcopyrightandlicensesveryseriously.IfyoucomeacrossanyillegalcopiesofourworksinanyformontheInternet,pleaseprovideuswiththelocationaddressorwebsitenameimmediatelysothatwecanpursuearemedy.

Pleasecontactusat<[email protected]>withalinktothesuspectedpiratedmaterial.

Weappreciateyourhelpinprotectingourauthorsandourabilitytobringyouvaluablecontent.

www.EBooksWorld.ir

Page 31: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

QuestionsIfyouhaveaproblemwithanyaspectofthisbook,youcancontactusat<[email protected]>,andwewilldoourbesttoaddresstheproblem.

www.EBooksWorld.ir

Page 32: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Chapter1.CreatingOurVeryFirstComponentinAngular2Unlessyouwerelostinspaceforthepastcoupleofyears,chancesareyouarewellawareofthemomentumthatmodernJavaScriptwebframeworksandlibrarieshavegotinthefrontendarenanowadays.Wehaveevenreachedastagewhereanewframeworkisborneveryday,forcingfrontenddeveloperstoassesscarefullywhetherthisnewcutting-edgecodetoolkitaddsenoughvaluetojustifythetimeandeffortrequiredtofaceitslearningcurveandputittogooduseinournextproject.

Eventually,ahandfulofnamesendedupgainingmorerelevancethantherest.Weareobviouslyreferringtoclient-sideframeworksthatwillprobablysoundprettyfamiliartoyoualready:Backbone,Ember,Knockout,Angular1,andsoon.

AsthebattleforsupremacyintheJavaScriptworldcarriedon,newframeworkssuchasReactorAureliaenteredthegame,favoringwebcomponentsandharnessingthepowerofShadowDOMasthecornerstoneofitsarchitecture.Applicationsbuiltthiswayprovedtobemoremodular,scalable,andmaintainable,letalonetheirunparalleledlevelofperformance.

Angular1hadcomealongwayalreadysinceitsinceptionanditsshortcomingshadbecometooprominenttobeoverlookedanylonger.Itwastimeforsomethingbetterandasimplerevampofcodebasedidnotsuffice.AmoreambitiousapproachwasrequiredandAngular2wasdeveloped—anewframeworkengineeredfromscratch,whichfullyembracesthenewesttrendsintheindustry.IthaswebcomponentsattheheartofitsdesignanditharnessesthepowerofShadowDOMtomaximizetheresponsivenessofourwebentitiesagainststatechanges.Ontopofthat,Angular2offersastate-of-the-artchangedetectionsystembakedintoeachcomponent,whichisresponsibleforpropagatingbindingsthroughoutthetreeofcomponentsthatcompriseourapplications.

ThedefiningtraitsofAngular2gobeyondtheconceptofjustbeingamerewebcomponentsframework,sinceitsfeaturesencompassprettymucheverythingyouneedinamodernwebapplication:componentinteroperability,universalsupportformultipleplatformsanddevices,atop-notchdependencyinjectionmachinery,aflexiblebutadvancedroutermechanismwithsupportfordecouplingandcomponentizationofroutedefinitions,advancedHTTPmessaging,andanimationorinternationalization,justtonameafew.

Inthischapter,wewill:

LearnwhyAngular2issouniqueincomparisontoitspreviousversionsLearnhowtosetupourcodeenvironmenttoworkwithAngular2andTypeScriptEnhanceourIDEofchoicetoprovideabetterexperiencecodingAngular2appsBuildourveryfirstAngular2webcomponentandlearnhowtoembeditonawebpageAddbasicinteractivityfeaturestoourwebcomponentDiscoversomebasichelperstobetterformatthedataoutput

www.EBooksWorld.ir

Page 33: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

AfreshstartAsmentionedbefore,Angular2representsafullrewriteoftheAngular1.xframework,introducingabrandnewapplicationarchitecturecompletelybuiltfromscratchinTypeScript,astrictsupersetofJavaScriptthataddsoptionalstatictypingandsupportforinterfacesanddecorators.

Inanutshell,Angular2applicationsarebasedonanarchitecturedesignthatcomprisestreesofwebcomponentsinterconnectedbetweenthembytheirownparticularI/Ointerface.Eachcomponenttakesadvantageunderthecoversofacompletelyrevampeddependencyinjectionmechanism.Tobefair,thisisasimplisticdescriptionofwhatAngular2reallyis.However,thesimplestprojectevermadeinAngulariscutoutbythesedefinitiontraits.Wewillfocusonlearninghowtobuildinteroperablecomponentsandmanagedependencyinjectioninthenextchapters,beforemovingontorouting,webforms,orHTTPcommunication.ThisalsoexplainswhywewillnotmakeexplicitreferencestoAngular1.xthroughoutthebook.Obviously,itmakesnosensetowastetimeandpagesreferringtosomethingthatwillnotprovideanyusefulinsightsonthetopic,besidesthefactweassumethatyoumightnotknowaboutAngular1.x,sosuchknowledgedoesnothaveanyvaluehere.

www.EBooksWorld.ir

Page 34: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

WebcomponentsWebcomponentsisaconceptthatencompassesfourtechnologiesdesignedtobeusedtogethertobuildfeatureelementswithahigherlevelofvisualexpressivityandreusability,therebyleadingtoamoremodular,consistent,andmaintainableweb.Thesefourtechnologiesareasfollows:

Templates:ThesearepiecesofHTMLthatstructurethecontentweaimtorenderCustomElements:ThesetemplatesnotonlycontaintraditionalHTMLelements,butalsothecustomwrapperitemsthatprovidefurtherpresentationelementsorAPIfunctionalitiesShadowDOM:ThisprovidesasandboxtoencapsulatetheCSSlayoutrulesandJavaScriptbehaviorsofeachcustomelementHTMLImports:HTMLisnolongerconstrainedtohostHTMLelements,buttootherHTMLdocumentsaswell

Intheory,anAngular2componentisindeedacustomelementthatcontainsatemplatetohosttheHTMLstructureofitslayout,thelatterbeinggovernedbyascopedCSSstylesheetencapsulatedwithinaShadowDOMcontainer.Let'strytorephrasethisinplainEnglish.ThinkoftherangeinputcontroltypeinHTML5.Itisahandywaytogiveourusersaconvenientinputcontrolforenteringavaluerangingbetweentwopredefinedboundaries.Ifyouhavenotuseditbefore,insertthefollowingpieceofmarkupinablankHTMLtemplateandloaditinyourbrowser:

<inputid="mySlider"type="range"min="0"max="100"step="10">

Youwillseeaniceinputcontrolfeaturingahorizontalsliderinyourbrowser.InspectingsuchcontrolwiththebrowserdevelopertoolswillunveilaconcealedsetofHTMLtagsthatwerenotpresentatthetimeyoueditedyourHTMLtemplate.ThereyouhaveanexampleofShadowDOMinaction,withanactualHTMLtemplategovernedbyitsownencapsulatedCSSwithadvanceddraggingfunctionality.Youwillprobablyagreethatitwouldbecooltodothatyourself.Well,goodnewsisthatAngular2givesyouthetoolsetrequiredfordeliveringthisverysamefunctionality,sowecanbuildourowncustomelements(inputcontrols,personalizedtags,andself-containedwidgets)featuringtheinnerHTMLmarkupofourchoiceandaveryownstylesheetthatdoesnotaffect(norisimpacted)bytheCSSofthepagehostingourcomponent.

www.EBooksWorld.ir

Page 35: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

WhyTypeScriptoverothersyntaxes?Angular2applicationscanbecodedinawidevarietyoflanguagesandsyntaxes:ECMAScript5,Dart,ECMAScript6,TypeScript,orECMAScript7.

TypeScriptisatypedsupersetofECMAScript6(alsoknownasECMAScript2015)thatcompilestoplainJavaScriptandiswidelysupportedbymodernOSes.Itfeaturesasoundobject-orienteddesignandsupportsannotations,decorators,andtypechecking.

Thereasonwhywepicked(andobviouslyrecommend)TypeScriptasthesyntaxofchoiceforinstructinghowtodevelopAngular2applicationsinthisbookisbasedonthefactthatAngular2itselfiswritteninthislanguage.BeingproficientinTypeScriptwillgivethedeveloperanenormousadvantagewhenitcomestounderstandingthegutsoftheframework.

Ontheotherhand,itisworthremarkingthatTypeScript'ssupportforannotationsandtypeintrospectionturnsouttobeparamountwhenitcomestomanagingdependencyinjectionandtypebindingbetweencomponentswithaminimumcodefootprint,aswewillseefurtherdownthelineinthisbook.

Ultimately,youcancarryoutyourAngular2projectsinplainECMAScript6syntaxifthatisyourpreference.EventheexamplesprovidedinthebookcanbeeasilyportedtoES6byremovingtypeannotationsandinterfaces,orreplacingthewaydependencyinjectionishandledinTypeScriptwiththemostverboseES6way.

Note

Forthesakeofbrevity,wewillonlycoverexampleswritteninTypeScriptandactuallyrecommenditsusebecauseofitshigherexpressivitythankstotypeannotations,anditsneatwayofapproachingdependencyinjectionbasedontypeintrospectionoutofsuchtypeannotations.

www.EBooksWorld.ir

Page 36: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

SettingupourworkspaceBeforejumpingintotheimplementationofourveryfirstandshinyAngular2component,weneedtobringinallthetoolswewillrequiretoimplementsoftwarebasedonTypeScript,letalonetheAngular2frameworkmodulesthemselves.

Firstandforemost,createafolderanddoublecheckthattheNPMCLIisavailableinyoursystemandisproperlyupdatedtothelateststableversion.Otherwise,pleasegotohttps://nodejs.organdinstallthelatestNode.jsruntime.

Note

Atthetimeofwriting,theAngular2frameworkisinReleaseCandidate1version,sotherequirementsforbuildinganddeployingtheexamplescontainedinthisbookmighthavechangedovernight.Theauthormaintainsacoderepositoryathttps://github.com/deeleman/learning-angular2,whereyoucancheckthemostup-to-dateversionofeachexamplecontainedinthisbook.Therepositoryisdividedintochapterfoldersandeachfoldercontainstheincrementalversionoftheprojectasitisattheendofeachchapter.Pleaserefertothecoderepositoryshouldanyproblemariseuponinstallingordeployingtheexamplesinthebook.

www.EBooksWorld.ir

Page 37: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

InstallingdependenciesOurfirstrequirementwillobviouslybetoinstallAngular2ontoourworkspace,includingitsownpeerdependencies.TheAngular2teamhasmadeagreatefforttoensuretheinstallationismodularenoughtoallowustobringonlywhatweneed,becomingourprojectsmoreorlessleandependingontherequirements.

Inthissense,Angular2doesnotcomeintheformofasingleinstallablepackage,butmany.Thisgivesthesmartdevelopertheopportunitytopickonlythosemodulesthatarerequiredforitsproject,minifyingtheoveralldependenciesfootprint.Someofthesepackages,suchascommonorcore,arerequiredregardlessthetypeofprojectwewanttoship.Someothers,suchasplatform-browser-dynamic,areboundtothetypeofprojectandtargetplatformaddressed.Anonthoroughlistofthemostcommonpackagesthatyouwillrequireinyourprojectsisgivenhere:

@angular/core:Thisisthemostrelevantpackage,encompassingthebackboneofAngularanditsmostcommonelements,suchasdirectivesandcomponents.YouwillneedtorelyonthismoduleonacommonbasistoimportthebasicelementsofAngular2intoyourproject.@angular/common:Youwillseldomneedtoexplicitlyimporttokensfromthismodule,butitisworthremarkingthatthispackagecontainsthedefinitionsofallthedirectives,services,andpipescontainedbyAngular2,amongotherrelevantclasses.@angular/compiler:Sameascommon,youwillrarelyimporttokensexplicitlyfromthismodule,althoughitistheoneresponsibleforcompilingtheHTMLtemplatesandturningthemintocodethatcanrendertheapplication'sUIoutput.@angular/platform-browser:ThismodulecontainsclassesandfunctionsrequiredforcomposingandinteractingwiththeDOMinawebbrowsercontext.Updatingthepagetitleorconfiguringthetouchgesturessetuparecommonactionsmadepossiblebythismodule.Thispackagealsocontainsthefunctionsrequiredtocompiletemplatesofflineinproductionenvironments.@angular/platform-browser-dynamic:Wewillrelythoroughlyonthismoduleduringthecourseofthebook,sinceitwillprovideuswiththebootstrappingfunctionwewillrequiretoinitializeourapplicationsondevelopment.@angular/http:ItistheAngular2HTTPclient,whichwewillcoverindetailinChapter6,AsynchronousDataServiceswithAngular2.@angular/router:ItistheAngular2built-inrouterstillunderBetaatthetimeofthiswriting.@angular/router-deprecated:AsnapshotofthepreviousincarnationoftheAngular2built-inrouter,madeavailabletoensurebackwardcompatibilitywithexistingapplications.Chapter7,RoutinginAngular2,willcoveritindetailandexplainsomeofitsmostremarkabledifferenceswiththenewrouterstillunderdevelopment.

Atthetimeofwriting,theseareallthedifferentthird-partylibrariesthatarerequiredaspeerdependenciesinanAngular2project,apartfromtheAngular2modules:

www.EBooksWorld.ir

Page 38: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

es6-shim:ThisintroducesECMAScript6compatibilitypolyfillsforlegacyJavaScriptengines(mostlyMicrosoftInternetExplorer).ThisdependencyisnowrequiredbecausemanymajorbrowsersstilldonotprovidewidesupportforECMAScript6features,buthopefully,thiswillchangesoon.Someotherimplementationsusethecore-jsstandardlibraryinstead.Ultimately,picktheoneyoulikethemostaslongasitproperlypolyfillsthecoreES2015APIsrequiredbyAngular2.zone.js:ThisisapolyfillfortheZonespecificationthatisusedtohandlechangedetectioninAngular2applications.reflect-metadata:ThisbringssupportfordecoratorsinourAngular2classesandmetadatareflectioninourcomponents.WewillseedecoratorsinactionlateroninthischapterandabroaderoverviewofitsdifferenttypesandimplementationsinChapter2,IntroducingTypeScript.DecoratorsareacorepartofAngular2.rxjs:ThislibrarywasdevelopedbyMicrosoftOpenTechnologies,Inc.AccordingtoMicrosoft,itisasetoflibrariestocomposeasynchronousandevent-basedprogramsusingobservablecollectionsandArray#extrasstylecompositioninJavaScript.Inshort,RxJSisalibraryformanagingObservables,whichallowustomakeourapplicationsfullyreactivetoasynchronousstatechanges.TheObservablesspecwillbestandardizedbymodernbrowsersinthefuture,sowewillbeabletoruleoutthisdependencybythen.

ThesedependenciesmayevolvewithoutpriornoticesopleaserefertotheGitHubrepositoryforthemostup-to-datelistofrequirements.

Note

YouwillbeprobablysurprisedbytheamountoflibrariesthatAngular2doesneedandthefactthatthesedependenciesarenotpartoftheAngularbundleitself.ThisisbecausetheserequisitesarenotspecifictoAngular2,butofavastmajorityofmodernJavaScriptapplicationsnowadays.

Withallthesedependenciesandthird-partylibrariesinmind,youcanrunthefollowingsetofbashcommandsinyourterminalconsole,onceyouhavecreatedafolderfortheprojectwewillcoverinthisbook:

$mkdirlearning-angular2

$cdlearning-angular2

$npminit

$npminstall@angular/common@angular/core@angular/compiler--save

$npminstall@angular/platform-browser@angular/platform-browser-dynamic--

save

$npminstall@angular/router@angular/router-deprecated--save

$npminstall@angular/http--save

$npminstalles6-shimreflect-metadatarxjszone.js--save

Apartfromthedependenciesenlistedpreviously,wewillalsoneedtoinstallthesystemjsuniversalmoduleloaderpackageinordertosupportmoduleloadingbetweencodeunitsoncetranspiledintoES5.ThesystemjspackageisnottheonlyoptionavailableformanagingmoduleloadinginAngular2.Infact,wecanswapitforothermoduleloaders,suchas

www.EBooksWorld.ir

Page 39: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

WebPack(https://webpack.github.io/),althoughalltheexamplesprovidedinthisbookmakeuseofSystemJSforhandlingcodeinjection.WewillinstallSystemJS,flaggingitasadevelopmentdependencybyexecutingthefollowingcommand:

$npminstallsystemjs–save

Last,butnotleast,wewillalsoinstallBootstrapinourapplicationsothatwecaneasilycraftaniceUIfortheexampleapplicationwewillbuildincrementallyineachchapter.ThisisnotanAngular2requirement,butaparticulardependencyoftheprojectwewillcarryoutthroughoutthisbook:

$npminstallbootstrap–save

TheinstallationcanthrowdifferentalertsandwarningsdependingontheversionsofeachpeerdependencyrequiredbyAngular2atthismomentintime,soincaseofissues,Istronglyrecommendtofetchthelatestversionofthepackage.jsonfileavailableinthisbook'scoderepositoryhttps://github.com/deeleman/learning-angular2/blob/master/chapter_01/package.json.

Downloadthefiletoyourdirectoryworkspaceandrunthenpminstallcommand.NPMwillfindandinstallallthedependenciesforyouautomatically.

Note

MacOSusers,whohavenotclaimedownershiprightsonthenpmdirectorylocatedat/usr/local/bin/npm(or/usr/local/npmforthoseusersonOSversionspriortoMacOSElCapitan),mightneedtoexecutethenpminstallcommandwithsudoprivileges.

www.EBooksWorld.ir

Page 40: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

InstallingTypeScriptWehavenowacompletesetofAngular2sourcesandtheirdependencies,plustheBootstrapmoduletobeautifyourprojectandSystemJStohandlemoduleloadingandbundlegeneration.

However,TypeScriptisprobablynotavailableonyoursystemyet.Let'sinstallTypeScriptandmakeitgloballyavailableonyourenvironmentsothatwecanleverageitsconvenientCLItocompileourfileslateron:

$npminstall-gtypescript

Great!We'realmostdone.OnelaststepentailsinformingTypeScriptabouthowwewanttousethecompilerwithinourproject.Todoso,justexecutethefollowingone-timecommand:

$tsc--init--experimentalDecorators--emitDecoratorMetadata--targetES5-

-modulesystem--moduleResolutionnode

Basically,wehavejustinitializedaTypeScriptproject(whichisourAngular2projectitself)withsupportforexperimentaldecorators(aswementionedalready,theseareanewfeatureinES7andTypeScriptthatAngular2usesextensively)andsetSystemJSasthedefaultmechanismforimportingmodulesanddependenciesbetweenfiles.

Asaresultofthisaction,wewillfindanewtsconfig.jsonfileattherootofourproject,includingthesettingsrequiredbytheTypeScriptcompilertotranspilethecomponentcodeintoplainECMAScript5JavaScriptcodereadablebycurrentbrowsers.

Note

PleaserememberthatourbrowsersdonotprovidesupportforTypeScriptorECMAScript6outofthebox,sowewilltranspileourcodetosomeflavorofJavaScriptthatiswidelysupportedbyourtargetbrowsers.

Asneakpeekonsuchfilewillyieldthefollowing:

{

"compilerOptions":{

"experimentalDecorators":true,

"emitDecoratorMetadata":true,

"target":"es5",

"module":"system",

"moduleResolution":"node",

"noImplicitAny":false,

"outDir":"built",

"rootDir":".",

"sourceMap":false

},

"exclude":[

"node_modules"

]

www.EBooksWorld.ir

Page 41: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

}

Simple,right?Thesetofpropertiesincludedinourconfigmanifestisself-descriptiveenough,butwecanhighlightthreeinterestingproperties.Theyareasfollows:

rootDir:ThispointstothefolderthecompilerwillusetoscanforTypeScriptfilestocompile(currentlythebasefolderinourexample).outDir:Thisdefineswherethecompiledfileswillbemovedunlesswedefineourownoutputpathbymeansofthe--outDirparameterinthecommandline,thecompilerwilldefaulttothebuiltfoldercreatedatruntimeinthesamelocationwherethetsconfig.jsonfilelives.sourceMap:Thissetsthesourcecodemappingpreferencestohelpdebugging.

Toggleitsvaluetotrueifyouwantsourcemapfilestobegeneratedatruntimetobacktracethecodetoitssourcethroughthebrowser'sdevtoolsincaseexceptionsarise.

Besidestheseproperties,wealsocanseethatwehavemarkedthenode_modulesfolderasexcluded,whichmeansthatthetsccommandwillskipthatfolderandallitscontentswhentranspilingTypeScriptfilestoES5throughouttheapplicationtree.

Tip

IwouldencourageyoutorefertotheTypeScriptcompilerwikiathttps://github.com/Microsoft/TypeScript/wiki/Compiler-OptionsforafullrundownofoptionsavailableinthecompilerAPI.

www.EBooksWorld.ir

Page 42: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

InstallingTypeScripttypingsBesidestheprojectdependencies,suchasBootstrapandAngular2'sowndependencies,TypeScriptdoesrequiresomeadditionallibrariessowecangetthebestoutofit.Specifically,ES6extendstheJavaScriptenvironmentwithmethodsandAPIsthatneedtobedescribedtotheTypeScriptcompiler.Otherwise,itwillnotrecognizethemaspartofthesyntaxandwillthrowerrorsuponcompiling.WheneverweneedtoinstructtheTypeScriptcompileraboutaJavaScriptAPI,eitheranativeoneoranyotherAPIbelongingtoathirdpartylibrary,wewillwanttouseaTypeScripttypedefinitionfile.

ATypeScripttypedefinitionfileisbasicallyafilewiththed.tsfileextensionthatcontainsTypeScriptinterfaces(moreonthisinChapter2,IntroducingTypeScript)sowecanbetterperformreal-timetypecheckingandpreventcompilererrors.Installingtypedefinitionfilesinourprojectsisnotabigdealandjustrequireshavingatypingstoolinstalledinourenvironment.Infact,weneedtoinstallatypedefinitionfiletoensurethattheTypeScriptcompilerisacquaintedwiththemostup-to-dateES6API.GoodnewsisthatwecaninstallaTypeScriptdefinitionsmanagertoolrightfromtheNPMregistry,sowecanautomatetheprocessofsearching,installinganddeployingtypedefinitionfiles.Therefore,returntotheconsoleandproceedwiththefollowingcommands:

$npminstall-gtypings

$typingsinstalles6-shim--ambient--save

First,weinstallthetypingstoolgloballyandthenweleveragethetypingsCLItoinstallthees6-shimtypesdefinitionfileintoourproject,creatingthetypings.jsonfilethatwillstorethereferencestothesourceoriginforalltypedefinitionfileswewillinstallnowandlateron.Anewfoldernamedtypingsiscreatedanditcontainsthedefinitionfileswerequire.Withoutthem,basicES6featureslikethenewfunctionalmethodsoftheArrayclasswouldnotbeavailable.

Beforemovingforward,weneedtotackleonemorestepregardingtheTypeScripttypings.Wheninstallingtypedefinitionfiles,twofaçadefilesaregeneratedbytheCLI:typings/main.d.tsandtypings/browser.d.ts.However,onlyoneshouldbeexposedtotheTypeScriptcompiler.Otherwise,itwillraiseanexceptionafterfindingduplicatedtypedefinitions.Sincewearebuildingfrontendapplications,wewillsticktobrowser.d.tsandexcludemain.d.tsanditslinkeddefinitionfilesbymarkingitasexcludedattsconfig.json:

{

"compilerOptions":{

"experimentalDecorators":true,

"emitDecoratorMetadata":true,

"target":"es5",

"module":"system",

"moduleResolution":"node",

"noImplicitAny":false,

"outDir":"built",

"rootDir":".",

"sourceMap":false

www.EBooksWorld.ir

Page 43: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

},

"exclude":[

"node_modules",

"typings/main.d.ts",

"typings/main"

]

}

Ontheotherhand,itisactuallyrecommendedtoexcludethetypingsfolderfromyourprojectdistributionbyincludingitinyour.gitignorefile,sameasweusuallydowiththenode_modulesfolder.Youonlywanttoincludethetypings.jsonmanifestwhendistributingyourappandthenhavealltheinstallationprocesseshandledbynpm,soitisveryconvenienttoincludethetypedefinitionfilesinstallationasanactionhandledbythepostinstallscriptinthepackage.jsonfile.Thisway,wecaninstallthenpmdependenciesandthedefinitionfilesinoneshot.Thecodeisasfollows:

"scripts":{

"typings":"typings",

"postinstall":"typingsinstall"

},

Whentakingthisapproach,thetypingspackageshouldbeincludedinthepackage.jsonaspartofthedevelopmentdependencies.Thus,reinstallitwiththe--save-devflag.Again,pleaserefertothebookcoderepositoryatGitHubtofetchthelatestversionofthepackage.jsonfileforthischapter.

www.EBooksWorld.ir

Page 44: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Hello,Angular2!WiththeAngular2librarybundleinplaceandfullsupportforTypeScriptnowavailable,thetimehascometoputeverythingtothetest.First,createandemptyfilenamedhello-angular.ts(.tsisthenaturalextensionforTypeScriptfiles)attherootofourworkingfolder.

Note

Here,westumbleuponthefirstofmanycodingconventionswewillcoverinthisbook:filenaming.Wenameourmodulefilesusinglowerkebabcase.InChapter5,BuildinganApplicationwithAngular2Components,wewilldelvedeeperintonamingconventionsandbestpracticesforcodingAngular2applications.Untilthen,wewillconcurintosomeanti-patternsforlearningpurposes,asthosemoreexperiencedreaderswillsoonnotice.

Now,openthatfileandwritethefollowingatthetop:

import{Component}from'@angular/core';

import{bootstrap}from'@angular/platform-browser-dynamic';

Wehavejustimportedthemostbasictypeandfunctionwewillneedtoscaffoldaverybasiccomponentinthenextsection.TheimportingsyntaxwillbefamiliartothosewhoarealreadyfamiliarwithECMAScript6.Forthosewhoarenotfamiliarwithitscodeparadigm,don'tworry.WewilldiscussmoreonthisinChapter2,IntroducingTypeScript.

www.EBooksWorld.ir

Page 45: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

TypeScriptclassesLet'snowdefineaclass:

classHelloAngularComponent{

greeting:string;

constructor(){

this.greeting='HelloAngular2!';

}

}

ECMAScript6(andTypeScriptaswell)introducedclassesasoneofthecorepartsofitsbuildingblocks.Ourexamplefeaturesaclassfieldpropertynamedgreetingtypedasstring,whichispopulatedwithintheconstructorwithatextstring,asyoucanseeintheprecedingcode.Theconstructorfunctioniscalledautomaticallywhenaninstanceoftheclassiscreated,andeachandeveryproperty(andfunctionsaswell)shouldbeannotatedwiththetypeitrepresents(orreturnsinthecaseoffunctions).

Donotworryaboutallthisnow.Chapter2,IntroducingTypeScript,willgiveyoutheinsightsyouneedtobetterunderstandthemechanicsofTypeScript.Now,let'sfocusontheactuallayoutofourcomponent.Youhaveprobablynoticedthenamestructure,whichconformstoanothercommoncodingconventioninAngular2.WedefineeachandeveryclassinPascalcasingbyappendingasuffixpointingoutitstype(willitbeacomponent,directive,pipe,andsoon),whichisComponentforthiscase.

www.EBooksWorld.ir

Page 46: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

IntroducingmetadatadecoratorsThecontrollerclasswehavejustcreatedgivesusthemachineryweneedtoinstanceanobjectexposingagreetingproperty,butwestillneedtoapplysomeAngular2sugartoturnitintoanactualcomponent.WealreadyimportedtheComponentmetadataclass,remember?Let'sputittoworkanddecorateourclasslikethis:

@Component({

selector:'hello-angular',

template:'<h1>{{greeting}}</h1>'

})

classHelloAngularComponent{

greeting:string;

constructor(){

this.greeting='HelloAngular2!';

}

}

AdecoratorisaveryinterestingexperimentalfeatureproposedbyECMAScript7thatwaslaterembracedandimplementedbyTypeScriptinordertodecorateclasseswithmetadata.Thereareseveraltypesofdecoratorsandallofthemareeasilyrecognizablebythe@symbolprefix.AlthoughChapter2,IntroducingTypeScript,willgiveyouanideaaboutdecorators,delvingdeeperintoitscorelogicisoutofthescopeofthisbook.However,wewillgetusedtothemasweadvancethroughthebook.

Inthisexample,wearetellingthecompilerthattheHelloAngularComponentclassis,infact,anAngular2component.Thecomponentismeanttobeencapsulatedbythe<hello-angular>customelementandthetemplatepropertydefinestheinternalHTMLstructureofourcomponent.Aswealreadymentionedpreviously,customelementsencapsulatingHTMLtemplatesarethefoundationofwebcomponents.

www.EBooksWorld.ir

Page 47: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

CompilingTypeScriptintobrowser-friendlyJavaScriptWearedonewithourprimeronTypeScript,butunfortunatelyitisquitelikelythatourbrowserofchoicewillnotsupportTypeScript.So,weneedtocompileoursourcecodeintogoodoldECMAScript5JavaScriptcode.

ThegoodnewsisthattheTypeScriptCLIcontainstoolstocompileTypeScriptintoJavaScriptcodeoutofthebox.Todothis,justopenaterminalwindowandtypethefollowingcommandatthelocationofthehello-angular.tsfile:

$tsc--watch

Anewhello-angular.jsfilewillshowupwithinthebuiltdirectory(orthepathyouhavedefinedintheoutDirpropertyattsconfig.json),anditwillcontainanECMAScript5versionoftheTypeScriptcodewejustbuilt.ThisfilealreadycontainssomefunctionalcodetoimplementsupportfortheMetadatadecoratorweconfigured.

Tip

Pleasenotethe--watchflaginourcommand.Thisparameterinformsthecompilerthatwewantthecompilationtobeautomaticallytriggeredagainuponchanginganyfile.Disregardtheflagwhenyoujustneedtocompileyourstuffonceanddonotneedtowatchthecodeforchanges.

Ourcomponentislookingbetterbytheminuteandnowweareinagoodstatetostartusingit,butwestillneedtoembeditsomewhereinordertoseeitlive.It'stimetodefinetheHTMLshellorwebcontainerwhereitwilllive.

www.EBooksWorld.ir

Page 48: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

TheHTMLcontainerCreateanHTMLfileattherootofourworkspaceandnameitindex.html.Then,populateitwiththefollowingcode:

<!DOCTYPEhtml>

<html>

<head>

<metacharset="utf-8">

<title>HelloAngular2!</title>

<scriptsrc="node_modules/es6-shim/es6-shim.min.js"></script>

<scriptsrc="node_modules/zone.js/dist/zone.js"></script>

<scriptsrc="node_modules/reflect-metadata/Reflect.js"></script>

<scriptsrc="node_modules/systemjs/dist/system.js"></script>

<scriptsrc="node_modules/rxjs/bundles/Rx.js"></script>

<scriptsrc="systemjs.config.js"></script>

</head>

<body>

</body>

</html>

Thisisthemostbasic,barebonesversionofanHTMLcontainerforanAngular2applicationwecancomeupwith.Thisisgreatbecausemostofthepresentationlogicanddependencymanagementwillbehandledbyourcomponentitself.

However,twothingscatchourattentioninthistemplate.Ononehand,wefindablockofscriptincludes.ThisblockcontainsallthepeerdependencieswerequiretopolyfillES2015(ES6)functionalities;theZoneandObservablesspecificationsensurefullsupportformetadatadecoratorsandlast,butnotleast,implementdynamicmoduleloadingfunctionalitiestoourapplication.

Note

Donottrytoreshufflethesortinglayoutofthesecodeblocksunlessyouwanttofaceunexpectedexceptions.

Then,weintroduceascriptincludepointingtoanewfilenamedsystemjs.config.js.Thisfileisyettobewritten,solet'screateitintherootofourprojectfolderwiththefollowingimplementation.Wewillbreakdownthissetupinthenextparagraphs:

(function(){

varpathMappings={

'@angular':'node_modules/@angular',

'rxjs':'node_modules/rxjs',

www.EBooksWorld.ir

Page 49: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

};

varpackages=[

'@angular/common',

'@angular/compiler',

'@angular/core',

'@angular/http',

'@angular/platform-browser',

'@angular/platform-browser-dynamic',

'@angular/router',

'@angular/router-deprecated',

'@angular/testing',

'rxjs',

'built',

];

varpackagesConfig={};

packages.forEach(function(packageName){

packagesConfig[packageName]={

main:'index.js',

defaultExtension:'js'

};

});

System.config({

map:pathMappings,

packages:packagesConfig,

});

})();

Aswecansee,thesystemjs.config.jsfilecontainsprimarilyanimmediatelyinvokedfunctionexpression.Thismeansthatthisblockofcodewillbeexecutedassoonasitisparsedbythebrowser.Let'sovervieweachandeverypieceofthisroutine:

varpathMappings={

'@angular':'node_modules/@angular',

'rxjs':'node_modules/rxjs',

};

Here,wedefinedanobjectliteralcontainingkey/valuepairsofindexescontainingfullpaths.WewillusethisobjectliteraltoconfigurethepathaliasesinSystemJSlateroninthissamefile.Therefore,executingtheimport'@angular/core'statementwillinstructSystemJStoactuallyimportthecorepackagefromnode_modules/@angular/core.However,packagesarenotimportedasawholeasis.Weneedanentrypointtosuchpackageand,therefore,weneedtoinformSystemJSwhatfaçadefileitshouldseekwhenimportinganentiremodule.Thiswewilldobydefininganarrayofmodulesandthencreatinganobjectliteral,whereeachmodulepathisthekeyofapropertycontainingthemainentrypointandthedefaultfileextensionconfigurationobjectforsuchpaths:

varpackages=[

'@angular/common',

www.EBooksWorld.ir

Page 50: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

'@angular/compiler',

'@angular/core',

'@angular/http',

'@angular/platform-browser',

'@angular/platform-browser-dynamic',

'@angular/router',

'@angular/router-deprecated',

'@angular/testing',

'rxjs',

'built',

];

varpackagesConfig={};

packages.forEach(function(packageName){

packagesConfig[packageName]={

main:'index.js',

defaultExtension:'js'

};

});

TheresultingpackagesConfigvariablerepresentstheaforementionedconfigurationobjectliteral.Italsoincludesmainentryfileanddefaultextensiondataforthebuiltfolder,whichisthefolderwheretheTypeScriptcompilerwillgosavingthetranspiledfileswhilewedevelopourapplication.

Withallthisconfigurationinplace,ourlaststepistofinallyconfigureSystemJSwiththesepathmappings,entrypoint,anddefaultfileextensionexpectedforeachpackagerepresentedbythepreviouspaths:

System.config({

map:pathMappings,

packages:packagesConfig,

});

ThisconcludesalltheconfigurationrequiredforSystemJS.Withthisinplace,wecanbothkickstartourapplicationandleveragetheES2015moduleimportsyntaxinourcode,aswewillseeinthenextsections.

www.EBooksWorld.ir

Page 51: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

ServingtheexamplesofthisbookBeforemovingonwithourexample,weneedalocalwebservertoexecutetheexamplescontainedinthisbook.Ifyoualreadyhaveaworkingwebserverthatyoucanconfiguretopointtoyourworkingdirectory,thenskiptothenextsection.Otherwise,setupawebserverinyourworkspace.Asaneasyworkaround,werecommendyouinstalltheextraordinarilypowerfulandlightweightlite-servernodemodulefromNPM:

$npminstall-glite-server

Then,youcanrunawebserverwithlive-reloadingfunctionalitybyrunningthefollowingcommandinaterminalshellaftermovingintoyourprojectfolder:

$lite-server

Afterexecutingtheprecedingcommand,abrowserwindowwillbefired,pointingtoyourworkingdirectory.PleaserefertotheNPMmoduleofficialrepositoryinordertocheckoutalltheoptionsavailable(https://github.com/johnpapa/lite-server).

Tip

Itisactuallyrecommendedtoinstallthelite-serverpackagepairedupwiththetypescriptandconcurrentlypackages,allofthemasdevelopmentdependenciesinstalledwiththe--save-devflag.Thisway,youcanruntheTypeScriptcompilerinwatchmodeandthelocalserveratthesametimewithasinglecommandthatcanbewrappedinthestartscriptofpackage.json.Then,youcanstartbuildingstuffrightawaybyaccessingyourworkingfolderandexecutingnpmstart.Thisbook'scoderepositoryinGitHubimplementsthisapproach,sofeelfreetoborrowthepackage.jsonexampleforyourconvenience.

www.EBooksWorld.ir

Page 52: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

PuttingeverythingtogetherOurHTMLfileisnowreadytohostourAngular2component.Todoso,let'seditthetemplateagainanddropacustomelementwiththesametagnamewedefinedintheselectorpropertyofourcomponent.Then,importtheactualfilethatcontainsthecomponentclassdeclaration,leveragingtheAPIofSystemJSforthat.Checkoutthesechangesinthefollowingexample:

<!DOCTYPEhtml>

<html>

<head>

<metacharset="utf-8">

<title>HelloAngular2!</title>

<scriptsrc="node_modules/es6-shim/es6-shim.min.js"></script>

<scriptsrc="node_modules/zone.js/dist/zone.js"></script>

<scriptsrc="node_modules/reflect-metadata/Reflect.js"></script>

<scriptsrc="node_modules/systemjs/dist/system.js"></script>

<scriptsrc="node_modules/rxjs/bundles/Rx.js"></script>

<scriptsrc="systemjs.config.js"></script>

//Hereweimportthecomponentmodule

//withnofileextension

System.import('built/hello-angular');

</script>

</head>

<body>

<!--Thisisourcustomelementtag-->

<hello-angular></hello-angular>

</body>

</html>

Howcoolisthat?Now,wecancreateourowncustomelementsthatrenderwhateverwedefineinthem.Let'sbringupthepageinourwebserverandseeitinactionbygoingtohttp://localhost:3000/(orwhateverhostandportyourlocalwebserveroperatesin).

Unfortunately,ifwereloadthebrowser,nothingwillhappenandwewillonlyseeablankpagewithnothinginthere.ThisisbecausewestillneedtobootstrapourcomponenttoinstantiateitontheHTMLpage.

Let'sreturntoourcomponentfilehello-angular.tsandaddafinallineofcode:

import{Component}from'@angular/core';

import{bootstrap}from'@angular/platform-browser-dynamic';

@Component({

selector:'hello-angular',

template:'<h1>{{greeting}}</h1>'

})

classHelloAngularComponent{

greeting:string;

constructor(){

this.greeting='HelloAngular2!';

www.EBooksWorld.ir

Page 53: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

}

}

bootstrap(HelloAngularComponent);//Componentisbootstrapped!

Thebootstrapcommandinstancesthecontrollerclasswepassasanargumentandusesittolayoutacompleteapplicationscaffold.Basically,thebootstrapmethodkickstartsthefollowingactions:

Analyzesthecomponentconfiguredasitsfirstargumentandchecksitstype.SearchestheDOMafteranelementwithatagmatchingthecomponentselector.Createsachildinjectorthatwillhandletheinjectionofdependenciesinthatcomponentandallthechilddirectives(includingcomponents,whicharedirectivestoo)thatsuchacomponentmighthost,inaverysimilarwayatreehasramifications.ItcreatesanewZone.WewillnotcoverZonesinthisbook,butlet'sjustsaythatZonesareinchargeofmanagingthechangedetectionmechanismofeachinstanceofabootstrappedcomponentinanisolatedfashion.ItcreatesaShadowDOMcontextinthecustomelementidentifiedbythecomponentselectorandrenderswithintheHTMLdefinedinthecomponenttemplate.Thecomponentcontrollerclassisinstantiatedstraightawayandthechangedetectionmachineryisfired.NowthatwehavetheShadowDOMplaceholdersinplace,dataprovidersareinitiatedanddataisinjectedwhererequired.

Laterinthisbook,wewillcoverhowwecanleveragethebootstrapcommandtodisplaydebugginginformationorhowapplicationproviderscanbegloballyoverriddenthroughoutthewholeapplicationsothedependencyinjectorbakedinAngular2pickstherightdependencywhererequired.

Hopefully,youarerunningtheTypeScriptcompilerinwatchmode.Otherwise,pleaseexecutethetsccommandtotranspileourcodetoES5andreloadthebrowser.WecandelightourselveswiththerenderedcontentofourveryfirstAngular2component.Yay!

www.EBooksWorld.ir

Page 54: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

www.EBooksWorld.ir

Page 55: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

EnhancingourIDEBeforemovingonwithourjourneythroughAngular2,it'stimetotakealookatIDEstoo.OurfavoritecodeeditorcanbecomeanunparalleledallywhenitcomestoundertakinganagileworkflowentailingTypeScriptcompilationatruntime,statictypecheckingandintrospection,andcodecompletionandvisualassistancefordebuggingandbuildingourapp.Thatbeingsaid,let'shighlightsomemajorcodeeditorsandtakeabird'seyeviewofhoweachoneofthemcanassistuswhendevelopingAngular2applications.Ifyou'rejusthappywithtriggeringthecompilationofyourTypeScriptfilesfromthecommandlineanddonotwanttohavevisualcodeassistance,feelfreetoskiptothenextsection.Otherwise,jumpstraighttothefollowingsectionthatcoverstheIDEofyourchoice.

www.EBooksWorld.ir

Page 56: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

SublimeText3Thisisprobablyoneofthemostwidespreadcodeeditorsnowadays,althoughithaslostsomemomentumlatelywithusersfavoringotherrisingcompetitorssuchasGitHub'sveryownAtom.Ifthisisyoureditorofchoice,wewillassumethatit'salreadyinstalledonyoursystemandyoualsohaveNode(whichisobvious,otherwise,youcouldhavenotinstalledTypeScriptinfirstplacethroughNPM).InordertoprovidesupportforTypeScriptcodeediting,youneedtoinstallMicrosoft'sTypeScriptplugin,availableathttps://github.com/Microsoft/TypeScript-Sublime-Plugin.Pleaserefertothispagetolearnhowtoinstallthepluginandalltheshortcutsandkeymappings.

Oncesuccessfullyinstalled,itonlytakesCtrl+spacebartodisplaycodehintsbasedontypeintrospection(seethefollowingscreenshot).Ontopofthat,wecantriggerthebuildprocessandcompilethefiletotheJavaScriptweareworkingonbyhittingtheF7functionkey.Realtimecodeerrorreportingisanotherfancyfunctionalityyoucanenablefromthecommandmenu.

www.EBooksWorld.ir

Page 57: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

AtomDevelopedbyGitHub,thehighlycustomizableenvironmentandeaseofinstallationofnewpackageshasturnedAtomintotheIDEofchoiceforalotofpeople.ItisworthmentioningthatthecodeexamplesprovidedinthisbookwereactuallycodedusingAtomonly.

InordertooptimizeyourexperiencewithTypeScriptwhencodingAngular2apps,youneedtoinstalltheAtomTypeScriptpackage.YoucaninstallitbymeansoftheAPMCLIorjustusethebuilt-inpackageinstaller.ThefunctionalitiesincludedareprettymuchthesameaswehaveinSublimeafterinstallingtheMicrosoftpackage:automaticcodehints,statictypechecking,codeintrospection,orautomaticbuilduponsavetonameafew.Ontopofthat,thispackagealsoincludesaconvenientbuilt-intsconfig.jsongenerator.

www.EBooksWorld.ir

Page 58: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

VisualStudioCodeVisualStudioCode,arelativelynewcodeeditorbackedbyMicrosoft,isgainingmomentumasaseriouscontenderintheAngular2medium,mostlybecauseofitsgreatsupportforTypeScriptoutofthebox.TypeScripthasbeen,toagreaterextent,aprojectdrivenbyMicrosoft,soitmakessensethatoneofitspopulareditorswasconceivedwithbuilt-insupportforthislanguage.Thismeansthatallthenicefeatureswemightwantarealreadybakedin,includingsyntaxanderrorhighlightingandautomaticbuilds.

www.EBooksWorld.ir

Page 59: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

WebStormThisexcellentcodeeditorsuppliedbyIntelliJisalsoagreatpickforcodingAngular2appsbasedonTypeScript.TheIDEcomeswithbuilt-insupportforTypeScriptoutoftheboxsothatwecanstartdevelopingAngular2componentsfromdayone.WebStormalsoimplementsabuilt-intranspilerwithsupportforfilewatching,sowecancompileourTypeScriptcodeintopurevanillaJavaScriptwithoutrelyingonanythird-partyplugins.

www.EBooksWorld.ir

Page 60: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

LeveragingGulpwithotherIDEsMaybeyourIDEisnotlistedhereandyoudonotwanttoswitchfromyourfavoritecodeeditornow,nothavingthechance,forwhateverreason,toautomateTypeScriptcompilationforyourproject.OrperhapsyoudonotfeelverycomfortablemessingaroundwiththeTypeScriptcommandsontheconsole.

Ifthisisnotthecase,feelfreetoskiptothenextsection.However,ifyourelatetothiscasescenario,don'tworry.ModernJavaScripttaskrunnershaveyourback.Let'spickGulp(http://gulpjs.com)andseehowwecancreateasupersimplescripttoautomateTypeScriptcompilationinourproject.

First,let'sproceedwiththedependenciesinstallation.Basically,wewillinstallGulpandthengulp-typescript,atypescriptcompilerforgulpwithincrementalcompilationsupport.Onyourconsolewindow,typethefollowingcommands:

$npminstall-ggulp

$npminstallgulpgulp-typescript--save-dev

Let'screateaJavaScriptfilenamedgulpfile.jsattherootofyourprojectwiththefollowingcontent:

vargulp=require('gulp');

varts=require('gulp-typescript');

vartsProject=ts.createProject('tsconfig.json');

gulp.task('build',function(){

vartsResult=tsProject.src().pipe(ts(tsProject));

returntsResult.js.pipe(gulp.dest('./built'));

});

Youwillneedatsconfig.jsonfileattherootofyourproject,soourGulptaskcanfetchourcompilationpreferencesfromit.Fromthismomentonwards,wecanlaunchthebuildprocessingoverthefileslistedatthefiles'arraypropertyinourtsconfig.jsonfilebyexecutingthefollowingcommand:

$gulpbuild

Unfortunately,thegulp-typescriptplugindoesnotsupportfilewatching,soifwewanttotriggerthebuildprocessingautomaticallyeverytimeaTypeScriptfilechange,weneedtorelyonGulp'snativewatchmethod.Todoso,justaddthefollowingchunkofcodeattheendofourgulpfile.jsfile:

gulp.task('watch',['build'],function(){

gulp.watch('./**/*.ts',['build']);

});

Now,youcanlaunchthebuildprocessandwatchthefilechangesbyexecutingthefollowingcommand:

www.EBooksWorld.ir

Page 61: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

$gulpwatch

www.EBooksWorld.ir

Page 62: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

DivingdeeperintoAngular2componentsWehavecomealongwaynow,fromtappingonTypeScriptforthefirsttimetolearninghowtocodethebasicscriptingschemaofanAngular2component.However,beforejumpingontomoreabstracttopics,let'sfleshoutourcurrentcomponentwithmorefunctionalitiesandtakeanoverviewofthemostcommontraitsofAngularappsandcomponents.

www.EBooksWorld.ir

Page 63: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

ImprovingproductivitySometimes,weneedsomehelperstoboostourfocus,especiallywhenwedealwithtooabstractstuffthatrequiresadditionalattention.AwidelyacceptedapproachisthePomodorotechnique,inwhichweputtogetheratasklistandthensplitthedeliverablesintoto-doitemsthatwon'trequireusmorethan25minutestoaccomplish.Whenwepickanyofthoseto-doitems,wefocusunderdistraction-freemodeonitsdeliveryfor25minuteswiththehelpofacountdowntimer.Youcangrabmoreinformationaboutthistechniqueathttp://pomodorotechnique.com.

Inthisbook,wearegoingtobuildamajorcomponentthatrepresentsthisfunctionalityandfillthecomponentwithadditionalfunctionalitiesandUIitemswrappedinsidetheirowncomponents.Todoso,wewillusethePomodorotechnique,solet'sstartbycreatingaPomodorotimer.

www.EBooksWorld.ir

Page 64: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

ComponentmethodsanddataupdatesCreateanewpomodoro-timer.tsfileinthesamefolderandpopulateitwiththefollowingbasicimplementationofaverysimplecomponent.Don'tworryabouttheaddedcomplexity,wewillrevieweachandeverychangemadeafterthecodeblock:

import{Component}from'@angular/core';

import{bootstrap}from'@angular/platform-browser-dynamic';

@Component({

selector:'pomodoro-timer',

template:'<h1>{{minutes}}:{{seconds}}</h1>'

})

classPomodoroTimerComponent{

minutes:number;

seconds:number;

constructor(){

this.minutes=24;

this.seconds=59;

}

}

bootstrap(PomodoroTimerComponent);

Ournewcomponentis,infairness,notthatmuchdifferentfromtheonewepreviouslyhad.Weupdatedthenamestomakethemmoreself-descriptiveandthendefinedtwopropertyfields,staticallytypedasnumbersinourPomodoroTimerComponentclass.Thesearerenderedinthecontainedview,wrappedinsidean<h1>element.Now,opentheindex.htmlfileandreplacethe<hello-angular></hello-angular>customelementwithournew<pomodoro-timer></pomodoro-timer>tag.Youcanduplicateindex.htmlandsaveitunderadifferentnameifyoudonotwanttoloosetheHTMLsideofourfancy"HelloWorld"component.

Note

Anoteaboutnamingcustomelements

SelectorsinAngular2arecasesensitive.Aswewillseelaterinthisbook,componentsareasubsetofdirectivesthatcansupportawiderangeofselectors.Whencreatingcomponents,wearesupposedtosetacustomtagnameintheselectorpropertybyenforcingadash-casingnamingconvention.Whenrenderingthattaginourview,weshouldalwaysclosethetagasanon-voidelement.So<custom-element></custom-element>iscorrect,while<custom-element/>willtriggeranexception.Lastbutnotleast,certain"common"camelcasenamesmightconflictwiththeAngular2implementation,soavoidnameslikeAppVieworAppElement.

YouwillwanttoupdatethereferenceinyourSystem.import(...)blockatindex.htmltopointtoournewcomponentaswell:

www.EBooksWorld.ir

Page 65: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

System.import('built/pomodoro-timer').then(null,console.error.bind(console));

Now,itisagoodtimetomentionthattheimportmethodofSystemJSisasynchronousandreturnsapromiseoncethemodulehasbeensuccessfullyloaded.Wecanleveragethispromisetothrowanyeventualerrormessagetotheconsole,whichwillbecomequitehandywheneverwehavetodebugourcode.Youwillseethispracticelaterinthisbook.

Ifyoubringupabrowserwindowandloadthisfile,youwillseearepresentationofthenumbersdefinedinthecomponentclass.Butwewanttodomorethanjustdisplayahandfulofnumbers,right?Weactuallywantthemtorepresentatimecountdown,andwecanachievethatbyintroducingthesechanges.Let'sfirstintroduceafunctionwecaniterateoninordertoupdatethecountdown.Addthisfunctionaftertheconstructorfunction:

tick():void{

if(--this.seconds<0){

this.seconds=59;

if(--this.minutes<0){

this.minutes=24;

this.seconds=59;

}

}

}

Asyoucanseehere,functionsinTypeScriptneedtobeannotatedwiththetypeofthevaluetheyreturn,orjustvoidifnone.Ourfunctionassessesthecurrentvalueofbothminutesandseconds,andtheneitherdecreasestheirvalueorjustresetsittotheinitialvalue.Thenthisfunctioniscalledeverysecondbytriggeringatimeintervalfromtheclassconstructor:

constructor(){

this.minutes=24;

this.seconds=59;

setInterval(()=>this.tick(),1000);

}

Here,wespotforthefirsttimeinourcodeanarrowfunction(alsoknownasalambdafunction,fatarrow,andsoon),anewsyntaxforfunctionsbroughtbyECMAScript6thatwewillcoverinmoredetailinChapter2,IntroducingTypeScript.Thetickfunctionisalsomarkedasprivate,soitcannotbeinspectedorexecutedoutsideaPomodoroTimerComponentobjectinstance.

Sofarsogood!WehaveaworkingPomodorotimerthatcountdownsfrom25minutesto0,andthenstartsalloveragain.Theproblemisthatwearereplicatingcodehereandthere.So,let'srefactoreverythingalittlebittopreventcodeduplication.

constructor(){

this.resetPomodoro();

setInterval(()=>this.tick(),1000);

}

resetPomodoro():void{

www.EBooksWorld.ir

Page 66: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

this.minutes=24;

this.seconds=59;

}

privatetick():void{

if(--this.seconds<0){

this.seconds=59;

if(--this.minutes<0){

this.resetPomodoro();

}

}

}

Wehavewrappedtheinitialization(andreset)ofminutesandsecondsinsideourfunctionresetPomodoro,whichiscalleduponinstantiatingthecomponentorreachingtheendofthecountdown.Waitamomentthough!AccordingtothePomodorotechnique,pomodoropractitionersareallowedtorestinbetweenpomodorosorevenpausethemshouldanunexpectedcircumstancegetsontheway.Weneedtoprovidesomesortofinteractivitysotheusercanstart,pause,andresumethecurrentpomodorotimer.

www.EBooksWorld.ir

Page 67: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

AddinginteractivitytothecomponentAngular2providesatop-notchsupportforeventsthroughadeclarativeinterfacethatremindstheoneinotherframeworkssuchasReact.Let'sfirstmodifyourtemplatedefinition:

@Component({

selector:'pomodoro-timer',

template:`

<h1>{{minutes}}:{{seconds}}</h1>

<p>

<button(click)="togglePause()">

{{buttonLabel}}

</button>

</p>

`

})

Weusedamultilinetextstring!ECMAScript6introducedtheconceptoftemplatestrings,whicharestringliteralswithsupportforembeddedexpressions,interpolatedtextbindings,andmultilinecontent.WewilllookintotheminmoredetailinChapter2,IntroducingTypeScript.

Inthemeantime,justfocusonthefactthatweintroducedanewchunkofHTMLthatcontainsabuttonwithaneventhandlerthatlistenstoclickeventsandexecutesthetogglePausemethoduponclicking.This(click)attributeissomethingyoumightnothaveseenbefore,eventhoughitisfullycompliantwiththeW3Cstandards.Again,wewillcoverthisinmoredetailinChapter3,ImplementingPropertiesandEventsinOurComponents.Let'sfocusonthetogglePausemethodandthenewbuttonLabelbinding.First,let'smodifyourclasspropertiessothattheylooklikethis:

classPomodoroTimerComponent{

minutes:number;

seconds:number;

isPaused:boolean;

buttonLabel:string;

//…Restofcodewillremainasitisbelowthispoint

}

Weintroducedtwonewfields.FirstisbuttonLabelthatcontainsthetextthatwillbelaterondisplayedonournewly-createdbutton.isPausedisanewly-createdvariablethatwillassumeatrue/falsevalue,dependingonthestateofourtimer.So,wemightneedaplacetotogglethevalueofsuchafield.Let'screatethetogglePausemethodwementionedearlier:

togglePause():void{

this.isPaused=!this.isPaused;

//ifcountdownhasstarted

if(this.minutes<24||this.seconds<59){

this.buttonLabel=this.isPaused?'Resume':'Pause';

}

}

www.EBooksWorld.ir

Page 68: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Inanutshell,thetogglePausemethodjustswitchesthevalueofisPausedtoitsoppositeandthen,dependingonsuchnewvalueandwhetherthetimerhasstarted(whichwouldentailthatanyofthetimevariableshasavaluelowerthantheinitializationvalue)ornot,weassignadifferentlabeltoourbutton.

Now,weneedtoinitializethesevalues,anditseemsthereisnobetterplaceforit.So,theresetPomodorofunctionistheplacewherevariablesaffectingthestateofourclassareinitialized:

resetPomodoro():void{

this.minutes=24;

this.seconds=59;

this.buttonLabel='Start';

this.togglePause();

}

ByexecutingtogglePauseeverytime,weresetthePomodorotomakesurethatwheneverthePomodororeachesastatewhereitrequirestobereset,thecountdownbehaviorwillswitchtotheoppositestateithadpreviously.Thereisonlyonetweakleftinthecontrollermethodthathandlesthecountdown:

privatetick():void{

if(!this.isPaused){

this.buttonLabel='Pause';

if(--this.seconds<0){

this.seconds=59;

if(--this.minutes<0){

this.resetPomodoro();

}

}

}

}

Obviously,wedonotwantthecountdowntocontinuewhenthetimerissupposedtobepaused,sowewrapthewholescriptinaconditional.Inadditiontothis,wewillwanttodisplayadifferenttextonourbuttonwheneverthecountdownisnotpausedandonceagainwhenthecountdownreachesitsend,stoppingandthenresettingthepomodorotoitsinitialvalueswillbetheexpectedbehavior.ThisreinforcestheneedofinvokingthetogglePausefunctionwithinresetPomodoro.

www.EBooksWorld.ir

Page 69: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

ImprovingthedataoutputintheviewandpolishingtheUISofar,wereloadedthebrowserandplayedaroundwiththenewlycreatedtogglefeature.However,thereisapparentlysomethingthatstillrequiressomepolishing:whenthesecondscounterislessthan10,itdisplaysasingle-digitnumberinsteadoftheusualtwo-digitnumbersweareusedtoseeindigitalclocksandwatches.Luckily,Angular2implementsasetofdeclarativehelpersthatformatthedataoutputinourtemplates.WecallthemPipes,andwewillcoverthemindetaillaterinChapter3,ImplementingPropertiesandEventsinOurComponents.Forthetimebeing,let'sjustintroducethenumberpipeinourcomponenttemplateandconfigureittoformatthesecondsoutputtodisplaytwodigitsallthetimes.Updateourtemplatesothatitlookslikethis:

@Component({

selector:'pomodoro-timer',

template:`

<h1>{{minutes}}:{{seconds|number:'2.0'}}</h1>

<p>

<button(click)="togglePause()">

{{buttonLabel}}

</button>

</p>

`

})

Basically,weappendedthepipenametotheinterpolatedbindinginourtemplateseparatedbyapipe(|)symbol,hencethename.Reloadthetemplateandyouwillseehowthesecondsfigurealwaysdisplaystwodigits,regardlessofthevalueitassumes.

WehavecreatedafullyfunctionalPomodoroTimerwidgetthatwecanreuseorembedinmorecomplexapplications.Chapter5,BuildinganApplicationwithAngular2Components,willguideusthroughtheprocessofembeddingandnestingourcomponentsinthecontextoflargercomponenttrees.

Inthemeantime,let'saddsomeUIbeautificationtomakeourcomponentmoreappealing.WealreadyintroducedaclassattributeinourbuttontagasananticipationoftheimplementationoftheBootstrapCSSframeworkinourproject.Let'simporttheactualstylesheetwedownloadedthroughNPMwheninstallingtheprojectdependencies.Openpomodoro-timer.htmlandaddthissnippetattheendofthe<HEAD>element:

<linkrel="stylesheet"href="node_modules/bootstrap/dist/css/bootstrap.min.css">

Now,let'sbeautifyourUIbyinsertinganicepageheaderrightbeforeourcomponent:

<body>

<navclass="navbarnavbar-defaultnavbar-static-top">

<divclass="container">

<divclass="navbar-header">

<strongclass="navbar-brand">MyPomodoroTimer</strong>

www.EBooksWorld.ir

Page 70: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

</div>

</div>

</nav>

<pomodoro-timer></pomodoro-timer>

</body>

TweakingthecomponentbuttonwithaBootstrapbuttonclasswillgiveitmorepersonalityandwrappingthewholetemplateinacenteringcontainerandappendinganiceiconatthetopwilldefinitelycompounduptheUI.Solet'supdatethetemplateinourtemplatetolooklikethis:

<divclass="text-center">

<imgsrc="assets/img/pomodoro.png"alt="Pomodoro">

<h1>{{minutes}}:{{seconds|number:'2.0'}}</h1>

<p>

<button(click)="togglePause()"

class="btnbtn-danger">

{{buttonLabel}}

</button>

</p>

</div>

Fortheicon,wepickedabitmapicondepictingapomodoro.Youcanuseanybitmapimageofyourchoiceoryoucanjustskiptheiconfornow,eventhoughwewillneedanactualpomodoroiconintheforthcomingchapters.ThisishowourPomodorotimerapplooksafterimplementingallthesevisualtweaks:

www.EBooksWorld.ir

Page 71: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Tip

Downloadingtheexamplecode

YoucandownloadtheexamplecodefilesforthisbookfromGitHubathttps://github.com/deeleman/learning-angular2.

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

Youcandownloadthecodefilesbyfollowingthesesteps:

Loginorregistertoourwebsiteusingyoure-mailaddressandpassword.HoverthemousepointerontheSUPPORT tabatthetop.ClickonCodeDownloads&Errata.EnterthenameofthebookintheSearchbox.

www.EBooksWorld.ir

Page 72: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Selectthebookforwhichyou'relookingtodownloadthecodefiles.Choosefromthedrop-downmenuwhereyoupurchasedthisbookfrom.ClickonCodeDownload.

YoucanalsodownloadthecodefilesbyclickingontheCodeFilesbuttononthebook'swebpageatthePacktPublishingwebsite.Thispagecanbeaccessedbyenteringthebook'snameintheSearchbox.PleasenotethatyouneedtobeloggedintoyourPacktaccount.

Oncethefileisdownloaded,pleasemakesurethatyouunziporextractthefolderusingthelatestversionof:

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

www.EBooksWorld.ir

Page 73: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

SummaryWelookedatwebcomponentsaccordingtomodernwebstandardsandhowAngular2componentsprovideaneasyandstraightforwardAPItobuildourowncomponents.WecoveredTypeScriptandsomebasictraitsofitssyntaxasapreparationforChapter2,IntroducingTypeScript.WesawhowtosetupourworkingspaceandwheretogotofindthedependenciesweneedtobringTypeScriptintothegameandusetheAngular2libraryinourprojects,goingthroughtheroleofeachdependencyinourapplication.

Ourfirstcomponentgaveustheopportunitytodiscusstheformofacontrollerclasscontainingpropertyfields,constructor,andutilityfunctions,andwhymetadataannotationsaresoimportantinthecontextofAngular2applicationstodefinehowourcomponentwillintegrateitselfintheHTMLenvironmentwhereitwilllive.Now,wealsoknowhowtodeploywebservertoolsandenhanceourcodeeditorstomakeourliveseasierwhendevelopingAngular2apps,leveragingtypeintrospectionandcheckingonthego.Ourfirstwebcomponentfeaturesitsowntemplateandsuchtemplateshostpropertybindingsdeclarativelyintheformofvariableinterpolations,convenientlyformattedbypipes.Bindingeventlistenersisnoweasierthaneveranditssyntaxisstandards-compliant.

Thenextchapterwillcover,indetail,alltheTypeScriptfeaturesweneedtoknowtogetuptospeedwithAngular2innotime.

www.EBooksWorld.ir

Page 74: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Chapter2.IntroducingTypeScriptInthepreviouschapter,webuiltourveryfirstcomponentandweusedTypeScripttoshapethecodescripts,whichgaveformtoit.Alltheexamplesincludedinthisbookuseitssyntax.Aswewillseelaterinthisbook,writingourscriptsinTypeScriptandleveragingitsstatictypingwillgiveusaremarkableadvantageovertheotherscriptinglanguages.

ThischapterisnotathoroughoverviewoftheTypeScriptlanguage.WewilljustfocusonthecoreelementsofthelanguageandstudythemindetailonourjourneythroughAngular2.ThegoodnewsisthatTypeScriptisnotallthatcomplex,andwewillmanagetocovermostofitsrelevantparts.

Inthischapter,wewill:

LookatthebackgroundandrationalebehindTypeScriptDiscoveronlineresourcestopracticewhilewelearnRecapontheconceptoftypedvaluesandhowtorepresentthemBuildourowntypes,basedonclassesandinterfacesLearntobetterorganizeourapplicationarchitecturewithmodules

www.EBooksWorld.ir

Page 75: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

UnderstandingthecaseforTypeScriptThenaturalevolutionoftheearlyJavaScript-drivensmallwebapplicationsintothickmonolithicclientsunveiledtheshortcomingsoftheECMAScript5JavaScriptspecification.Inanutshell,large-scaleJavaScriptapplicationssufferedfromseriousmaintainabilityandscalabilityproblemsassoonastheygrewupinsizeandcomplexity.

Thisissuebecamemorerelevantasnewlibrariesandmodulesrequiredseamlessintegrationontoourapplications.Thelackofgoodmechanismsforinteroperabilityledtoreallycumbersomesolutionsthatneverseemedtofitthebill.

Asaresponsetotheseconcerns,ECMAScript6(alsocalledasES6orES2015)promisedtosolvethesemaintainabilityandscalabilityissuesbyintroducingbettermoduleloadingfunctionalities,animprovedlanguagearchitectureforbetterhandlingofscope,andawidevarietyofsyntacticsugartobettermanagetypesandobjects.Theintroductionofclass-basedprogrammingturnedintoanopportunitytoembraceamoreOOPapproachwhenbuildinglarge-scaleapplications.

Microsofttookthechallengeandspentnearly2yearsbuildingasupersetofthelanguage,combiningtheconventionsofES6andborrowingsomeproposalsfromES7.Theideawastolaunchsomethingthathelpedoutwithbuildingenterpriseapplicationswithalowererrorfootprintbymeansofstatictypechecking,bettertooling,andcodeanalysis.

After2yearsofdevelopmentledbyAndersHejlsberg,leadarchitectofC#andcreatorofDelphiandTurboPascal,TypeScript0.8wasfinallyintroducedin2012anditreachedVersion1.0twoyearslater.TypeScriptwasnotonlyrunningaheadofECMAScript6,butitalsoimplementedthesamefeaturesandprovidedasolidenvironmentforbuildinglarge-scaleapplicationsbyintroducing,amongotherfeatures,optionalstatictypingthroughtypeannotations,therebyensuringtypecheckingatcompiletime.Thiscontributestocatchingerrorsinearlierstagesofthedevelopmentprocess.Thesupportfordeclarationfilesalsogivesdeveloperstheopportunitytodescribetheinterfaceoftheirmodules,sootherdeveloperscanbetterintegratethemintotheircodeworkflowandtooling.

www.EBooksWorld.ir

Page 76: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

ThebenefitsofTypeScriptThefollowinginfographicprovidesabird'seyeviewofthedifferentfeaturesthatdistinguishesECMAScript6fromECMAScript5,andthendifferentiatesTypeScriptfromthetwo.

AsasupersetofECMAScript6,oneofthemainadvantagesofembracingTypeScriptinyournextprojectisthelowentrybarrier.IfyouknowECMAScript6,youareprettymuchallset,sincealltheadditionalfeaturesinTypeScriptareoptional.Youcanpickandintroduceinyourpracticethefeaturesthathelpyoutoachieveyourgoal.Allinall,thereisalonglistofstrongargumentsforadvocatingforTypeScriptinyournextprojectandallofthemobviouslyapplytoAngular2aswell.Hereisashortrundownofarguments,justtonameafew:

Annotatingourcodewithtypesensuresaconsistentintegrationofourdifferentcodeunitsandimprovescodereadabilityandcomprehension.TheTypeScript'sbuilt-intype-checkerwillanalyzeyourcodeatruntimeandhelpyoupreventerrorsevenbeforeexecutingyourcode.Theuseoftypesensuresconsistencyacrossyourapplications.Incombinationwiththeprevioustwo,theoverallcodeerrorsfootprintgetsminimizedinthelongrun.TypeScriptextendsclasseswithlongtimedemandedfeaturessuchasclassfields,privatemembers,enums,andsoon.Theuseofdecoratorsopensthedoortoextendourclassesandimplementationsin

www.EBooksWorld.ir

Page 77: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

unparalleledways.Creatinginterfacesandtypedefinitionfiles(whichwewillnotcoverinthisbookthough)ensuresasmoothandseamlessintegrationofourlibrariesinothersystemsandcodebases.TypeScriptsupportacrossthedifferentIDEsonstoreisterrific,andwecanbenefitfromcodehighlighting,real-timetypechecking,andautomaticcompilationatnocost.TheTypeScriptsyntaxwilldefinitelypleasedeveloperscomingfromotherbackgroundssuchasJava,C#,C++,andsoon.

www.EBooksWorld.ir

Page 78: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

IntroducingTypeScriptresourcesinthewildInthepreviouschapter,wesawhowtoinstallTypeScriptinoursystemandusethecompilerfortranspilingourTypeScriptfilesintoES5scriptfiles.Now,wearegoingtotakealookatwherecanwegetfurthersupporttolearnandtest-driveournewknowledgeofTypeScript.

TheTypeScriptofficialsite

Obviously,ourfirststopistheofficialsiteforthelanguage:http://www.typescriptlang.org.There,wecanfindamoreextensiveintroductiontothelanguageandlinkstoIDEsandcorporatesupportersofthisproject.Nevertheless,themostimportantsectionsthatwewilldefinitelyrevisitmoreoftenarethelearnsectionandtheplaysandbox.

Thelearnsectiongivesusaccesstoaquicktutorialtogetuptospeedwiththelanguageinnotime.Itmightbeinterestingasarecaponwhatwediscussedinthepreviouschapter,butwewouldsuggestyoutoskipitinfavorofthesamplepagesandthelanguagespec,thelatterbeingadirectlinktothefullextensivedocumentationofthelanguageatGitHub.Thisisa

www.EBooksWorld.ir

Page 79: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

pricelessresourceforbothnewandexperiencedusers.

Theplaysectionoffersaconvenientsandbox,includingsomereadymadecodeexamples,coveringsomeofthemostcommontraitsofthelanguage.Weencourageyoutoleveragethistooltotestoutthecodeexampleswewillseethroughoutthischapter.

TheTypeScriptWiki

WemadeareferencetotheTypeScriptWikiinthepreviouschapterwhenspeakingaboutthemostbasicparametersweneedtoknowwhenexecutingcommandswiththeTypeScriptcompilerAPI.

ThecodeforTypeScriptisfullyopensourcedatGitHub,andtheMicrosoftteamhasmadeagoodeffortatdocumentingthedifferentfacetsofthecodeintheWikiavailableontherepositorysite.Weencourageyoutogotakealookatitanytimeyouhaveaquestionorwanttodelvedeeperintoanyofthelanguagefeaturesorformaspectsofitssyntax.

TheWikiislocatedathttps://github.com/Microsoft/TypeScript/wiki.

www.EBooksWorld.ir

Page 80: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

TypesinTypeScriptWorkingwithTypeScriptoranyothercodinglanguagemeansbasicallyworkingwithdata,andsuchdatacanrepresentdifferentsortsofcontent.Thisiswhatweknowastypes,anounusedtorepresentthefactthatsuchdatacanbeatextstring,anintegervalue,oranarrayofthesevaluetypes,amongothers.ThisisnothingnewtoJavaScript,sincewehavealwaysbeenworkingimplicitlywithtypes,butinaflexiblemanner.Thismeansthatanygivenvariablecouldassume(orreturninthecaseoffunctions)anytypeofvalue.Sometimes,thisledtoerrorsandexceptionsinourcodebecauseoftypecollisionsbetweenwhatourcodereturnedandwhatweexpectedittoreturntype-wise.Whilethisflexibilitycanstillbeenforcedbymeansofanytypethatwewillseelateroninthischapter,staticallytypingourvariablesgivesusandourIDEsagoodpictureofwhatkindofdatawearesupposedtofindoneachinstanceofcode.Thisbecomesaninvaluablewaytohelpdebugourapplicationsatcompiletimebeforeit'stoolate.

www.EBooksWorld.ir

Page 81: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

StringProbablyoneofthemostwidelyusedprimitivetypesinourcodewillbethestringtype,wherewepopulateavariablewithapieceoftext:

varbrand:string='Chevrolet';

Checkoutthetypeassignationnexttothevariablename,whichisseparatedbyacolonsymbol.ThisishowweannotatetypesinTypeScript,aswealreadysawinthepreviouschapter.

Backtothestringtype,wecanuseeithersingleordoublequotes,anditissameasECMAScript6.Wecandefinemultilinetextstringswithsupportfortextinterpolationwithplaceholdervariablesbyusingthesametype:

varbrand:string='Chevrolet';

varmessage:string=`Todayit'sahappyday!

Ijustboughtanew${brand}car`;

DeclaringourvariablestheECMAScript6way

TypeScript,asasupersetofECMAScript6,supportsexpressivedeclarationnounssuchaslet,whichinformsusthatthevariableisscopedtothenearestenclosingblock(eitherafunctionforlooporanyenclosingstatement).Ontheotherhand,constisanindicatorthatthevaluesdeclaredthiswayaremeanttofeaturealwaysthesametypeorvalueoncepopulated.Fortherestofthischapter,wewillenforcethetraditionalvarnotationfordeclaringvariables,butrememberthat

www.EBooksWorld.ir

Page 82: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

NumberNumberisprobablytheothermostwidespreadprimitivedatatypealongwithstringandboolean.ThesameasinJavaScript,numberdefinesafloatingpointnumber.Thenumbertypealsodefineshexadecimal,decimal,binary,andoctalliterals:

varage:number=7;

varheight:number=5.6;

Boolean

ThebooleantypedefinesdatathatcanbeTrueorFalse,representingthefulfillmentofacondition:

varisZeroGreaterThanOne:boolean=false;

Array

AssigningwrongmembertypestoarraysandhandlingexceptionsthatarisebythatcanbenoweasilyavoidedwiththeArraytype,wherewedescribeanarraycontainingcertaintypesonly.Thesyntaxjustrequiresthepostfix[]inthetypeannotation,asfollows:

varbrands:string[]=['Chevrolet','Ford','GeneralMotors'];

varchildrenAges:number[]=[8,5,12,3,1];

IfwetrytoaddanewmembertothechildrenAgesarraywithatypeotherthannumber,theruntimetypecheckerwillcomplain,makingsureourtypedmembersremainconsistentandourcodeiserror-free.

www.EBooksWorld.ir

Page 83: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

DynamictypingwiththeanytypeSometimes,itishardtoinferthedatatypeoutoftheinformationwehaveatsomepoint,especiallywhenweareportinglegacycodetoTypeScriptorintegratinglooselytypedthird-partylibrariesandmodules.Don'tworry,TypeScriptsuppliesuswithaconvenienttypeforthesecases.Theanytypeiscompatiblewithalltheotherexistingtypes,sowecantypeanydatavaluewithitandassignanyvaluetoitlateron.Thisgreatpowercomeswithagreatresponsibilitythough.Ifwebypasstheconvenienceofstatictypechecking,weareopeningthedoortotypeerrorswhenpipingdatathroughourmodules,anditwillbeuptoustoensuretypesafetythroughoutourapplication:

vardistance:any;

//Assigningdifferentvaluetypesisperfectlyfine

distance='1000km';

distance=1000;

//Allowsustoseamlesslycombinedifferenttypes

vardistances:any[]=['1000km',1000];

ThenullandundefinedJavaScriptliteralsrequirespecialmention.Inanutshell,theyaretypedundertheanytype.Thismakesitpossiblelaterontoassigntheseliteralstoanyothervariable,regardlessitsoriginaltype.

Enum

Enumisbasicallyasetofuniquenumericvaluesthatwecanrepresentbyassigningfriendlynamestoeachoneofthem.Theuseofenumsgoesbeyondassigninganaliastoanumber.Wecanusethemasawaytolist,inaconvenientandrecognizableway,thedifferentvariationsthataspecifictypecanassume.

Enumsaredeclaredusingtheenumkeyword,withoutvaroranyothervariabledeclarationnoun,andtheybeginnumberingmembersstartingat0unlessexplicitnumericvaluesareassignedtothem:

enumBrands{Chevrolet,Cadillac,Ford,Buick,Chrysler,Dodge};

varmyCar:Brands=Brands.Cadillac;

InspectingthevalueofmyCarwillreturn1(whichistheindexheldbyCadillacintheenum).Aswementionedalready,wecanassigncustomnumericvaluesintheenum:

enumBrandsReduced{Tesla=1,GMC,Jeep};

varmyTruck:BrandsReduced=BrandsReduced.GMC;

InspectingmyTruckwillyield2,sincethefirstenumeratedvaluewassetas1already.Wecanextendvalueassignationtoalltheenummembersaslongassuchvaluesareintegers:

enumStackingIndex{

None=0,

www.EBooksWorld.ir

Page 84: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Dropdown=1000,

Overlay=2000,

Modal=3000

};

varmySelectBoxStacking:StackingIndex=LayerStackingIndex.Dropdown;

Onelasttrickworthmentioningisthepossibilitytolookuptheenummembermappedtoagivennumericvalue:

enumBrands{Chevrolet,Cadillac,Ford,Buick,Chrysler,Dodge};

varmyCarBrandName:string=Brands[1];

www.EBooksWorld.ir

Page 85: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

VoidThevoidtypedefinitelyrepresentstheabsenceofanytypeanditsuseisconstrainedtoannotatingfunctionsthatdonotreturnanactualvalue.Therefore,thereisnoreturntypeeither.Wealreadyhadthechancetoseethiswithanactualexampleinthepreviouschapter:

resetPomodoro():void{

this.minutes=24;

this.seconds=59;

}

www.EBooksWorld.ir

Page 86: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

TypeinferenceTypingourdataisoptionalsinceTypeScriptissmartenoughtoinferthedatatypeofourvariablesandfunctionreturnvaluesoutofcontextwithacertainlevelofaccuracy.Whennotypeinferenceispossible,TypeScriptwillassignthedynamicanytypetothelooselytypeddataatthecostofreducingtypecheckingtoabareminimum.

However,itisalwaysagoodpracticetoensurethattheinformationwehandleisconvenientlydescribedbyexplicitlyannotatingitstype,sowecanharnessthebenefitofhavingthecompilerverifyingcorrectnessthroughoutourcode.

www.EBooksWorld.ir

Page 87: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Functions,lambdas,andexecutionflowThesameasinJavaScript,functionsaretheprocessingmachineswhereweanalyzeinput,digestinformation,andapplythenecessarytransformationstothedataprovidedtoeithertransformthestateofourapplicationorreturnanoutputthatwillbeusedtoshapeourapplication'sbusinesslogicoruserinteractivity.

FunctionsinTypeScriptarenotthatdifferentfromregularJavaScript,exceptforthefactthatfunctions,justaseverythingelseinTypeScript,canbeannotatedwithstatictypesandthus,theybetterinformthecompileroftheinformationtheyexpectintheirsignatureandthedatatypetheyaimtoreturn,ifany.

www.EBooksWorld.ir

Page 88: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

AnnotatingtypesinourfunctionsThefollowingexampleshowcaseshowaregularfunctionisannotatedinTypeScript:

functionsayHello(name:string):string{

return'Hello,'+name;

}

WecanclearlyseetwomaindifferencesfromtheusualfunctionsyntaxinregularJavaScript.First,weannotatewithtypeinformationtheparametersdeclaredinthefunctionsignature.Thismakessensesincethecompilerwillwanttocheckwhetherthedataprovidedwhenexecutingthefunctionholdsthecorrecttype.Inadditiontothis,wealsoannotatethetypeofthereturningvaluebyaddingthepostfixstringtothefunctiondeclaration.Inthesecases,wherethegivenfunctiondoesnotreturnanyvalue,thetypeannotationvoidwillgivethecompilertheinformationitrequirestoprovideapropertypechecking.

Aswementionedintheprevioussection,theTypeScriptcompilerissmartenoughtoinfertypeswhennoannotationisprovided.Inthiscase,thecompilerwilllookintotheargumentsprovidedandthereturnstatementstoinferareturningtypefromit.

FunctionsinTypeScriptcanalsoberepresentedasexpressionsofanonymousfunctions,wherewebindthefunctiondeclarationtoavariable:

varsayHello=function(name:string):string{

return'Hello,'+name;

};

However,thereisadownsideofthissyntax.Althoughtypingfunctionexpressionsthiswayisallowed,thankstotypeinference,thecompilerismissingthetypedefinitioninthedeclaredvariable.Wemightassumethattheinferredtypeofavariablethatpointstoafunctiontypedasstringisobviouslyastring.Well,it'snot.Avariablethatpointstoananonymousfunctionoughttobeannotatedwithafunctiontype.Basically,thefunctiontypeinformsaboutboththetypesexpectedinthefunctionpayloadandthetypereturnedbythefunctionexecution,ifany.Thiswholeblock,intheformof(arguments:type)=>returnedtype,becomesthetypeannotationourcompilerexpects:

varsayHello:(name:string)=>string=function(name:string):string{

return'Hello,'+name;

};

Whysuchacumbersomesyntax,youmightask?Sometimes,wewilldeclarevariablesthatmightdependonfactoriesorfunctionbindings.Then,itisalwaysagoodpracticetoprovideasmuchinformationtothecompileraswecan.Thissimpleexamplemighthelpyoutounderstandbetter:

//Twofunctionswiththesametypingbutdifferentlogic

functionsayHello(input:string):string{

return'Hello'+input;

www.EBooksWorld.ir

Page 89: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

}

functionsayHi(input:string):string{

return'Hi'+input;

}

//Herewedeclarethevariablewithitsfunctiontype

vargreetMe:(name:string)=>string;

//Last,weassignafunctiontothevariable

greetMe=sayHello;

Thisway,wealsoensurethatlaterfunctionassignationsconformtothetypeannotationssetwhendeclaringvariables.

www.EBooksWorld.ir

Page 90: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

FunctionparametersinTypeScriptDuetothetypecheckingperformedbythecompiler,functionparametersrequirespecialattentioninTypeScript.

Optionalparameters

ParametersareacorepartofthetypecheckingappliedbytheTypeScriptcompiler.Inotherwords,wecannotdeclareparametersandthendonotincludedinthepayloadwhenexecutingthefunctionafterwards.Thesameappliestoeventothoseargumentsinthefunctionpayload,whichwerenotoriginallydeclaredandannotatedwhendefiningthefunction.Weobviouslyneedawaytocopewiththiscasescenario,soTypeScriptoffersthisfunctionalitybyaddingthe?symbolasapostfixtotheparameternamewewanttomakeoptional:

functiongreetMe(name:string,greeting?:string):string{

if(!greeting){

greeting='Hello';

}

returngreeting+','+name;

}

Note

Whenaparameterismarkedasoptionalandnotprovidedwhenexecutingthefunction,TypeScriptwillassignthenullvaluetoit.Ontheotherhand,theruleofthumbistoputrequiredparametersfirstandthenoptionalparameterslast.

Defaultparameters

TypeScriptgivesusanotherfeaturetocopewiththescenariodepictedearlierintheformofdefaultparameters,wherewecansetadefaultvaluetheparameterwillassumewhennotexplicitlypopulateduponexecutingthefunction.Thesyntaxisprettystraightforwardaswecanseewhenwerefactorthepreviousexamplehere:

functiongreetMe(name:string,greeting:string='Hello'):string{

returngreeting+','+name;

}

Justaswithoptionalparameters,defaultparametersmustbeputrightafterthenon-defaultparametersinthefunctionsignature.

Restparameters

OneofthebigadvantagesoftheflexibilityofJavaScriptwhendefiningfunctionsisthefunctionalitytoacceptanunlimitednon-declaredarrayofparametersintheformoftheargumentsobject.InastaticallytypedcontextsuchasTypeScript,thismightbenotpossible,butitactuallyisbymeansoftheRestparameter'sobject.Here,wecandefine,attheendoftheargumentslist,anadditionalparameterprefixedbyellipsisandtypedasanarray:

www.EBooksWorld.ir

Page 91: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

functiongreetPeople(greeting:string,...names:string[]):string{

returngreeting+','+names.join('and')+'!';

}

alert(greetPeople('Hello','John','Ann','Fred'));

Note

It'simportanttonotethattheRestparametersmustbeputattheendoftheargumentslistandcanbeleftoffwhenevernotrequireduponexecutingthefunction.

Overloadingthefunctionsignature

MethodandfunctionoverloadingisacommonpatterninotherlanguagessuchasC#.However,implementingthisfunctionalityinTypeScriptclasheswiththefactthatJavaScript,whichTypeScriptismeanttocompileto,doesnotimplementanyelegantwaytointegratethisfunctionalityoutofthebox.So,theonlyworkaroundpossiblyrequireswritingfunctiondeclarationsforeachoftheoverloadsandthenwritingageneral-purposefunctionthatwillwraptheactualimplementationandwhoselistoftypedargumentsandreturningtypesarecompatiblewithalltheothers:

functionhello(name:string):string;

functionhello(names:string[]):string;

functionhello(names:any,greeting?:string):string{

varnamesArray:string[];

if(Array.isArray(names)){

namesArray=names;

}else{

namesArray=[names];

}

if(!greeting){

greeting='Hello';

}

returngreeting+','+namesArray.join('and')+'!';

}

Intheprecedingexample,weareexposingthreedifferentfunctionsignaturesandeachofthemfeaturesdifferenttypeannotations.Wecouldevendefinedifferentreturningtypesiftherewasacaseforthat.Fordoingso,weshouldhavejustannotatedthewrappingfunctionwithananyreturntype.

www.EBooksWorld.ir

Page 92: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

BetterfunctionsyntaxandscopehandlingwithlambdasECMAScript6introducedtheconceptoffatarrowfunctions(alsocalledlambdafunctionsinotherlanguagessuchasPython,C#,Java,orC++)asawaytobothsimplifythegeneralfunctionsyntaxandalsotoprovideabulletproofwaytohandlethescopeofthefunctionsthataretraditionallyhandledbytheinfamousscopeissuesoftacklingwiththethiskeyword.

Thefirstimpressionisitsminimalisticsyntax,where,mostofthetime,wewillseearrowfunctionsassingle-line,anonymousexpressions:

vardouble=x=>x*2;

Thefunctioncomputesthedoubleofagivennumber,x,andreturnstheresult,althoughwedonotseeanyfunctionorreturnstatementsintheexpression.Ifthefunctionsignaturecontainsmorethanoneargument,wejustneedtowrapthemallbetweenbraces:

varadd=(x,y)=>x+y;

Thismakesthissyntaxextremelyconvenientwhendevelopingfunctionaloperationssuchasmap,reduce,andothers:

varreducedArray=[23,5,62,16].reduce((a,b)=>a+b,0);

Arrowfunctionscanalsocontainstatements.Inthatcase,wewillwanttowrapthewholeimplementationincurlybraces:

varaddAndDouble=(x,y)=>{

varsum=x+y;

returnsum*2;

}

Still,whatdoesthishavetodowithscopehandling?Basically,thevalueofthiscanpointtoadifferentcontext,dependingonwhereweexecutethefunction.Thisisabigdealforalanguagethatpridesitselfonanexcellentflexibilityforfunctionalprogramming,wherepatternssuchascallbacksareparamount.Whenreferringtothisinsideacallback,welosetrackoftheuppercontextandthatusuallyforcesustouseconventionssuchasassigningthevalueofthistoavariablenamedselforthat,whichwillbeusedlateronwithinthecallback.Statementscontainingintervalortimeoutfunctionsmakeaperfectexampleofthis:

functiondelayedGreeting(name):void{

this.name=name;

this.greet=function(){

setTimeout(function(){

alert('Hello'+this.name);

},0);

}

}

vargreeting=newdelayedGreeting('Peter')

greeting.greet();//alerts'Helloundefined'

www.EBooksWorld.ir

Page 93: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Whenexecutingtheprecedingscript,wewon'tgettheexpectedHelloPeteralert,butanincompletestringhighlightingapeskygreetingtoMr.Undefined!Basically,thisconstructionscrewsthelexicalscopingofthiswhenevaluatingthefunctioninsidethetimeoutcall.Portingthisscripttoarrowfunctionswilldothetrickthough:

functiondelayedGreeting(name):void{

this.name=name;

this.greet=function(){

setTimeout(()=>alert('Hello'+this.name),0);

}

}

Evenifwebreakdownthestatementcontainedinthearrowfunctionintoseverallinesofcodewrappedbycurlybraces,thelexicalscopingofthiswillkeeppointingtothepropercontextoutsidethesetTimeoutcall,allowingamoreelegantandcleansyntax.

www.EBooksWorld.ir

Page 94: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Classes,interfaces,andclassinheritanceNowthatwehaveoverviewedthemostrelevantbitsandpiecesofTypeScript,it'stimetoseehoweverythingfallsintoplacetobuildTypeScriptclasses.TheseclassesarethebuildingblocksofTypeScriptandAngular2applications.

AlthoughthenounclasswasareservedwordinJavaScript,thelanguageitselfneverhadanactualimplementationfortraditionalPOO-orientedclassesasotherlanguagessuchasJavaorC#did.JavaScriptdevelopersusedtomimicthiskindoffunctionality,leveragingthefunctionobjectasaconstructortype,whichwouldbelateroninstancedwiththenewoperator.Othercommonpracticessuchasextendingourfunctionobjectswereimplementedbyapplyingprototypalinheritanceorbyusingcomposition.

Nowwehaveanactualclassfunctionality,whichisflexibleandpowerfulenoughtoimplementthefunctionalityourapplicationsrequire.Wealreadyhadthechancetotapintoclassesinthepreviouschapter.Let'slookattheminmoredetailnow.

www.EBooksWorld.ir

Page 95: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Anatomyofaclass–constructors,properties,methods,getters,andsettersThefollowingpieceofcodeillustrateshowaclasscouldbe.Pleasenotethattheclasspropertymemberscomefirstandthenweincludeaconstructorandseveralmethodsandpropertyaccessors.Noneofthemfeaturesthereservedwordfunctionandallthemembersandmethodsareproperlyannotatedwithatypeexceptconstructor:

classCar{

privatedistanceRun:number=0;

color:string;

constructor(publicisHybrid:boolean,color:string='red'){

this.color=color;

}

getGasConsumption():string{

returnthis.isHybrid?'Verylow':'Toohigh!';

}

drive(distance:number):void{

this.distanceRun+=distance;

}

statichonk():string{

return'HOOONK!';

}

getdistance():number{

returnthis.distanceRun;

}

}

ThisclasslayoutwillprobablyremindusofthecomponentclasswebuiltbackinChapter1,CreatingOurVeryFirstComponentinAngular2.Basically,theclassstatementwrapsseveralelementsthatwecanbreakdowninto:

Members:AnyinstanceoftheCarclasswillfeaturetwoproperties:colortypedasstring,anddistanceRuntypedasanumberandonlyaccessiblefromwithintheclassitself.Ifweinstancethisclass,distanceRunoranyothermemberormethodmarkedasprivatewon'tbepubliclyexposedaspartoftheobjectAPI.Constructor:Theconstructorfunctionisexecutedrightawaywhenaninstanceoftheclassiscreated.Usually,wewanttoinitializetheclassmembershere,withthedataprovidedintheconstructorsignature.Wecanalsoleveragetheconstructorsignatureitselftodeclareclassmembers,aswedidwiththeisHybridproperty.Todoso,wejustneedtoprefixtheconstructorparameterwithanaccessmodifiersuchasprivateorpublic.Sameaswesawwhenanalyzingfunctionsintheprevioussections,wecandefinerest,optional,ordefaultparametersasdepictedinthepreviousexamplewiththecolorargument,whichfallbacksto"red"whennotexplicitlydefined.Methods:Amethodisaspecialkindofmemberwhichrepresentsafunctionand

www.EBooksWorld.ir

Page 96: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

therefore,canreturn,ornot,atypedvalue.Basically,itisafunctionthatbecomespartoftheobjectAPI.Methodscanbeprivateaswell.Inthatcase,theyarebasicallyusedashelperfunctionswithintheinternalscopeoftheclasstoachievethefunctionalitiesrequiredbyotherclassmembers.Staticmembers:Membersmarkedasstaticareassociatedwiththeclassandnotwiththeobjectinstancesofthatclass.Thismeansthatwecanconsumestaticmembersdirectly,withouthavingtoinstantiateanobjectfirst.Infact,staticmembersarenotaccessiblefromtheobjectinstancesandthus,theycannotaccessotherclassmembersusingthis.Thesemembersareusuallyincludedintheclassdefinitionashelperorfactorymethodsinordertoprovideagenericfunctionalitynotrelatedtoanyspecificobjectinstance.Propertyaccessors:InES5,wecoulddefinecustomsetters/gettersinaveryverbosewaywithObject.defineProperty.Now,thingshavebecomequitesimpler.Inordertocreatepropertyaccessors(usuallypointingtointernalprivatefieldsasintheexampleprovided),wejustneedtoprefixatypedmethodnamedasthepropertywewanttoexposewithset(inordertomakeitwritable)andget(inordertomakeitreadable).

Asapersonalexercise,whydon'tyoucopytheprecedingpieceofcodeattheplaygroundpage(http://www.typescriptlang.org/Playground)andexecuteit?WecanevenseeaninstanceobjectoftheCarclassinactionbyappendingthissnippetrightaftertheclassdefinitionandrunningthecodeandinspectingtheoutputinthebrowser'sdevelopertoolsconsole:

varmyCar=newCar(false);

console.log(myCar.color);//'red'

//PublicaccessorreturnsdistanceRun:

console.log(myCar.distance);//0

myCar.drive(15);

console.log(myCar.distance);//15(0+15)

myCar.drive(21);

console.log(myCar.distance);//36(15+21)

//What'smycarbonfootprintaccordingtomycartype?

myCar.getGasConsumption();//'Toohigh!'

Car.honk();//'HOOONK!'noobjectinstancerequired

Wecanevenperformanadditionaltestandappendthefollowingillegalstatementstoourcode,whereweattempttoaccesstheprivatepropertydistanceRunorevenapplyavaluethroughthedistancemember,whichdoesnothaveagetter.

console.log(myCar.distanceRun);

myCar.distance=100;

Rightafterinsertingthesecodestatementsintheplaygroundtextfield,aredunderlinewillremarkthatweareattemptingtodosomethingthatisnotcorrect.Nevertheless,wecancarryonandtranspileandrunthecode,sinceES5willhonorthesepractices.Allinall,ifweattempttorunthetsccompileronthisfile,theruntimewillexitwiththefollowingerror

www.EBooksWorld.ir

Page 97: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

trace:

example_26.ts(21,7):errorTS1056:Accessorsareonlyavailablewhen

targetingECMAScript5andhigher.

example_26.ts(29,13):errorTS2341:Property'distanceRun'isprivateand

onlyaccessiblewithinclass'Car'.

www.EBooksWorld.ir

Page 98: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

InterfacesinTypeScriptAsapplicationsscaleandmoreclassesandconstructsarecreated,weneedtofindwaystoensureconsistencyandrulescomplianceinourcode.Oneofthebestwaystoaddresstheconsistencyandtypevalidationissueistocreateinterfaces.

Inanutshell,aninterfaceisacodeblueprintdefiningacertainfieldsschemaandanytypes(eitherclasses,functionsignatures)implementingtheseinterfacesaremeanttocomplywiththisschema.Thisbecomesquiteusefulwhenwewanttoenforcestricttypingonclassesgeneratedbyfactories,whenwedefinefunctionsignaturestoensurethatacertaintypedpropertyisfoundinthepayload,orothersituations.

Let'sgetdowntobusiness!Here,wedefinetheVehicleinterface.Vehicleisnotaclassbutacontractualschemathatanyclasswhichimplementsitmustcomplywith:

interfaceVehicle{

make:string;

}

AnyclassimplementingtheVehicleinterfacemustfeatureamembernamedmake,whichmustbetypedasastringaccordingtothisexample.Otherwise,theTypeScriptcompilerwillcomplain:

classCarimplementsVehicle{

//Compilerwillraiseawarningif'make'isnotdefined

make:string;

}

Interfacesarethereforeextremelyusefultodefinetheminimumsetofmembersanytypemustfulfill,becominganinvaluablemethodforensuringconsistencythroughoutourcodebase.

Note

Itisimportanttonotethatinterfacesarenotusedjusttodefineminimumclassschemas,butanytypeoutthere.Thisway,wecanharnessthepowerofinterfacesforenforcingtheexistenceofcertainfieldsandmethodsinclassesandpropertiesinobjectsusedlateronasfunctionparameters,functiontypes,typescontainedinspecificarrays,andevenvariables.Aninterfacemaycontainoptionalmembersaswellandevenmemberstypedasotherinterfaces.

Let'screateanexample.Todoso,wewillprefixallourinterfacetypeswithanI(uppercase).Thisway,itwillbeeasiertofinditstypewhenreferencingthemwithourIDEcodeautocompletionfunctionality.

First,wedefineanIExceptioninterfacethatmodelsatypewithamandatorymessagepropertymemberandanoptionalIDnumbermember:

interfaceIException{

www.EBooksWorld.ir

Page 99: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

message:string;

id?:number;

}

Wecandefineinterfacesforarrayelementsaswell.Todoso,wemustdefineaninterfacewithasolemember,definingindexaseitheranumberorstring(fordictionarycollections)andthenthetypewewantthatarraytocontain.Inthiscase,wewanttocreateaninterfaceforarrayscontainingIExceptiontypes.ThisisatypecomprisingastringmessagepropertyandanoptionalIDnumbermember,aswesaidinthepreviousexample:

interfaceIExceptionArrayItem{

[index:number]:IException;

}

Nowwedefinetheblueprintforourfutureclass,withatypedarrayandamethodwithitsreturningtypedefinedaswell:

interfaceIErrorHandler{

exceptions:IExceptionArrayItem[];

logException(message:string,id?:number):void;

}

Wecanalsodefineinterfacesforstandaloneobjecttypes.Thisisquiteusefulwhenitcomestodefiningatemplatedconstructorormethodsignatures,whichwewillseelaterinthisexample:

interfaceIExceptionHandlerSettings{

logAllExceptions:boolean;

}

Lastbutnotleast,inthefollowingclasswewillimplementalltheseinterfacetypes:

classErrorHandlerimplementsIErrorHandler{

exceptions:IExceptionArrayItem[];

logAllExceptions:boolean;

constructor(settings:IExceptionHandlerSettings){

this.logAllExceptions=settings.logAllExceptions;

}

logException(message:string,id?:number):void{

this.exceptions.push({message,id});

}

}

Basically,wearedefininganerrorhandlerclassherethatwillmanageaninternalarrayofexceptionsandexposeamethodtolognewexceptionsbysavingthemintotheaforementionedarray.ThesetwoelementsaredefinedbytheIErrorHandlerinterfaceandaremandatory.TheclassconstructorexpectstheparametersdefinedbytheIExceptionHandlerSettingsinterfaceandusesthemtopopulateexceptionmemberwithitemstypedasIException.InstancingtheErrorHandlerclasswithoutthelogAllExceptions

www.EBooksWorld.ir

Page 100: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

parameterinthepayloadwilltriggeranerror.

Let'swrapupthissectionaboutinterfacesbyhighlightingthatclassescanimplementmorethanoneinterface.

www.EBooksWorld.ir

Page 101: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

ExtendingclasseswithclassinheritanceJustlikeaclasscanbedefinedbyaninterface,itcanalsoextendthemembersandfunctionalityofotherclassasiftheywereitsown.Wecanmakeaclassinheritfromanotherbyappendingthekeywordextendstotheclassname,includingthenameoftheclasswewanttoinherititsmembersfrom:

classSedanextendsCar{

model:string;

constructor(make:string,model:string){

super(make);

this.model=model;

}

}

Here,weextendfromaparentclass,Car,whichalreadyexposedamakemember.Wecanpopulatethemembersalreadydefinedbytheparentclassandevenexecutetheirownconstructorbyexecutingthesuper()method,whichpointstotheparentconstructor.Wecanalsooverridemethodsfromtheparentclassbyappendingamethodwiththesamename.Nevertheless,wewillstillbeabletoexecutetheoriginalparent'sclassmethodsasitwillbestillaccessiblefromthesuperobject.Comingbacktotheinterface,theycanalsoinheritdefinitionfromotherinterfaces.Simplyput,aninterfacecaninheritfromanotherinterface.

Note

Asawordofcaution,ES6andTypeScriptdonotprovidesupportformultipleinheritance.So,youmaywanttousecompositionormiddlemanclassesinstead,incaseyouwanttoborrowfunctionalitiesfromdifferentsources.

www.EBooksWorld.ir

Page 102: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

DecoratorsinTypeScriptDecoratorsareaverycoolfunctionality,originallyproposedbyGoogleinAtScript(asupersetofTypeScriptthatfinallygotmergedintoTypeScriptbackinearly2015)andalsoapartofthecurrentstandardpropositionforECMAScript7.Inanutshell,decoratorsareawaytoaddmetadatatoclassdeclarationsforusebydependencyinjectionorcompilationdirectives(http://blogs.msdn.com/b/somasegar/archive/2015/03/05/typescript-lt-3-angular.aspx).Bycreatingdecorators,wearedefiningspecialannotationsthatmayhaveanimpactonthewayourclasses,methods,orfunctionsbehaveorjustsimplyalteringthedatawedefineinfieldsorparameters.Inthatsense,decoratorsareapowerfulwaytoaugmentourtype'snativefunctionalitieswithoutcreatingsubclassesorinheritingfromothertypes.

Thisis,byfar,oneofthemostinterestingfeaturesofTypeScript.Infact,itisextensivelyusedinAngular2whendesigningdirectivesandcomponentsormanagingdependencyinjection,aswewillseefromChapter4,EnhancingourComponentswithPipesandDirectives,onwards.

Note

Decoratorscanbeeasilyrecognizedbythe@prefixtotheirname,andtheyareusuallylocatedasstandalonestatementsabovetheelementtheydecorate,includingamethodpayloadornot.

Wecandefineuptofourdifferenttypesofdecorators,dependingonwhatelementeachtypeismeanttodecorate:

ClassdecoratorsPropertydecoratorsMethoddecoratorsParameterdecorators

Let'stakealookateachofthem!

www.EBooksWorld.ir

Page 103: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

ClassdecoratorsClassdecoratorsallowustoaugmentaclassorperformoperationsoveranyofitsmembers,andthedecoratorstatementisexecutedbeforetheclassgetsinstanced.

Creatingaclassdecoratorjustrequiresdefiningaplainfunction,whosesignatureisapointertotheconstructorbelongingtotheclasswewanttodecorate,typedasFunction(oranyothertypethatinheritsfromFunction).TheformaldeclarationdefinesaClassDecoratorasfollows:

declaretypeClassDecorator=<TFunctionextendsFunction>(Target:TFunction)=>

TFunction|void;

Yes,itisreallydifficulttograspwhatthisgibberishmeans,right?Let'sputeverythingincontextthroughasimpleexample,likethis:

functionGreeter(target:Function):void{

target.prototype.greet=function():void{

console.log('Hello!');

}

}

@Greeter

classGreeting{

constructor(){

//Implementationgoeshere...

}

}

varmyGreeting=newGreeting();

myGreeting.greet();//consolewilloutput'Hello!'

Aswecansee,wehavegainedagreet()methodthatwasnotoriginallydefinedintheGreetingclassjustbyproperlydecoratingitwiththeGreeterdecorator.

Extendingtheclassdecoratorfunctionsignature

Sometimes,wemightneedtocustomizethewayourdecoratoroperatesuponinstancingit.Noworries!Wecandesignourdecoratorswithcustomsignaturesandthenhavethemreturningafunctionwiththesamesignaturewedefinewhendesigningclassdecoratorswithnoparameters.Asaruleofthumb,decoratorstakingparametersjustrequireafunctionwhosesignaturematchestheparameterswewanttoconfigure.Suchafunctionmustreturnanotherfunction,whosesignaturematchesthatofthedecoratorwewanttodefine.

Thefollowingpieceofcodeillustratesthesamefunctionalityasthepreviousexample,butallowsdeveloperstocustomizethegreetingmessage:

functionGreeter(greeting:string){

returnfunction(target:Function){

target.prototype.greet=function():void{

www.EBooksWorld.ir

Page 104: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

console.log(greeting);

}

}

}

@Greeter('Howdy!')

classGreeting{

constructor(){

//Implementationgoeshere...

}

}

varmyGreeting=newGreeting();

myGreeting.greet();//consolewilloutput'Howdy!'

www.EBooksWorld.ir

Page 105: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

PropertydecoratorsPropertydecoratorsaremeanttobeappliedonclassfieldsandcanbeeasilydefinedbycreatingaPropertyDecoratorfunction,whosesignaturetakestwoparameters:

target:Thisistheprototypeofclasswewanttodecoratekey:Thisisthenameofthepropertywewanttodecorate

Possibleusecasesforthisspecifictypeofdecoratormayencompassfromloggingthevalueassignedtoclassfieldswheninstancingobjectsofsuchaclassandevenreactingtodatachangesonsuchfields.Let'sseeanactualexamplethatencompassesboththesebehaviors:

functionLogChanges(target:Object,key:string){

varpropertyValue:string=this[key];

if(deletethis[key]){

Object.defineProperty(target,key,{

get:function(){

returnpropertyValue;

},

set:function(newValue){

propertyValue=newValue;

console.log(`${key}isnow${propertyValue}`);

}

});

}

}

classFruit{

@LogChanges

name:string;

}

varfruit=newFruit();

fruit.name='banana'; //consoleoutputs'nameisnowbanana'

fruit.name='plantain';//consoleoutputs'nameisnowplantain'

Thesamelogicforparametrizedclassdecoratorsapplieshere,althoughthesignatureofthereturnedfunctionisslightlydifferentinordertomatchthatoftheparameter-lessdecoratordeclarationwealreadysaw.

Thefollowingexampledepictshowwecanlogchangesonagivenclasspropertyandtriggeracustomfunctionwhenthisoccurs:

functionLogChanges(callbackObject:any):Function{

returnfunction(target:Object,key:string):void{

varpropertyValue:string=this[key];

if(deletethis[key]){

Object.defineProperty(target,key,{

get:function(){

returnpropertyValue;

},

www.EBooksWorld.ir

Page 106: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

set:function(newValue){

propertyValue=newValue;

callbackObject.onchange.call(this,

propertyValue);

}

});

}

}

}

classFruit{

@LogChanges({

onchange:function(newValue:string):void{

console.log(`Thefruitis${newValue}now`);

}

})

name:string;

}

varfruit=newFruit();

fruit.name='banana';//console:'Thefruitisbanananow'

fruit.name='plantain';//console:'Thefruitisplantainnow'

www.EBooksWorld.ir

Page 107: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

MethoddecoratorsThesespecialdecoratorscandetect,log,andinterveneinhowmethodsareexecuted.Todoso,wejustneedtodefineaMethodDecoratorfunctionwhosepayloadtakesthefollowingparameters:

target:Thisistypedasanobjectandrepresentsthemethodbeingdecorated.key:Thisisastringthatgivestheactualnameofthemethodbeingdecorated.value:Thisisapropertydescriptorofthegivenmethod.Infact,it'sahashobjectcontaining,amongotherthings,apropertynamedvaluewithareferencetothemethoditself.

Let'sseehowwecanleveragetheMethodDecoratorfunctioninanactualexample.Supposewewanttobuildamultipurpose,signature-agnosticloggerthatwillkeeptrackoftheoutputreturnedbyeachmethodinourclassuponexecution,includingsomeadditionaldatasuchasthetimestampwhenthemethodwasexecuted:

functionLogOutput(target:Function,key:string,descriptor:any){

varoriginalMethod=descriptor.value;

varnewMethod=function(...args:any[]):any{

varresult:any=originalMethod.apply(this,args);

if(!this.loggedOutput){

this.loggedOutput=newArray<any>();

}

this.loggedOutput.push({

method:key,

parameters:args,

output:result,

timestamp:newDate()

});

returnresult;

};

descriptor.value=newMethod;

}

Aswementionedearlier,thedescriptorparametercontainsareferencetothemethodwewanttodecorate.Withthisinmind,nothingpreventsusfromreplacingsuchamethodbyourown.Wecantakeadvantageofthisnewlycreatedmethodtoexecutetheformerbypassingalongtoitthesameparameters.

Note

Rememberthatdecoratorfunctionsarescopedwithintheclassrepresentedinthetargetparameter,sowecantakeadvantageofthatforaugmentingtheclasswithourowncustommembers.Becarefulwhendoingthis,sincethismightoverridethealreadyexistingmembersthough.Forthesakeofthisexample,wewon'tapplyanyduediligenceoverthis,buthandlethiswithcareinyourcodeinthefuture.

www.EBooksWorld.ir

Page 108: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Aswecansee,ourcodeisprettysimpleandstraightforward.WearebasicallystoringareferencetotheoriginalmethodintheoriginalMethodvariable,borrowingitfromdescriptor.value.Then,wearebuildinganewmethodwitharestargumentthatensureswecoveranypossiblemethodsignatureoutthere.Internally,thisnewmethodexecutesthepreviousoneandstorestheresultalongsidesomeotherdata(suchasthenameofthemethodexecuted,theparametersused,andthetimeanddateitoccurredon)inanewlycreatedclassarrayfield,whichiscreatedonthespotincaseitdidn'texistyet.Wethenreturnthecomputedresult,ifany,aswewouldexpectfromtheoriginalmethodwehavejustoverridden.

Let'sputourShinydecoratortothetest!Let'screateaclasswithamethodperformingmethodcomputationsandloggingandseewhathappens:

classCalculator{

@LogOutput

double(num:number):number{

returnnum*2;

}

}

varcalc=newCalculator();

calc.double(11);

console.log(calc.loggedOutput);//Check[Object]arrayinconsole

Here,ourShinyCalculatorclassexposesamethodthatwilldoubleupanynumberwepickasaninput.But,isitproperlyloggingtheoperationsmade?Let'sinspectinthevalueofcalc.loggedOutputinthebrowserdevtoolsconsoleandseewhathappens.

Wecanevenruntheextramileandaddadifferentmethodwithacompletelydifferenttypeandsignatureandseewhethereverythingkeepsworkingfine.AddthistothebodyoftheCalculatorclass:

@LogOutput

doNothing(input:any):any{

returninput;

}

Now,executethefollowingintheDevToolsconsolecommandline:

calc.doNothing(['LearningAngular2',2016]);

Ifyoucheckthevalueofcalc.loggedOutput,youwillseethenewobjectalongwiththepreviouscomputationwemadeshowingupattheconsole.

Thefollowinglineinourexamplemusthavecaughtyourattention:

this.loggedOutput=newArray<any>();

Weneedtoproperlytypethememberweaimtocreateinourclass.Todoso,andsincewecannotannotatethetypethenormalwayhere,weleveragethegenericconstructorofthe

www.EBooksWorld.ir

Page 109: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Arrayobjectpassingtheanytypealongbetweentheanglebrackets.

ThischapterwillnotcoverGenericssincetheyarekindofoutofthescopeofanintroductorybooklikethis,butweencourageyoutorefertotheofficialsite(http://www.typescriptlang.org/Handbook#generics)anddelvedeeperintothetopic.Forthetimebeing,youjustneedtoknowthatgenericsallowustoenforceacertaintypewhenexecutingcertaincustommethodsorusingspecificclassessuchasarrays.Wewillseestrictlytypedarraysornewlycreatedemptyobjectsthataremeanttoenforceacertaininterfacethankstothefunctionalitiesprovidedbytheuseofgenerics.Again,pleaserefertotheofficialsiteoftheTypeScriptlanguageforfurtherinformation.

www.EBooksWorld.ir

Page 110: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

ParameterdecoratorsOurlastroundofdecoratorswillcovertheParameterDecoratorfunction,whichtapsintoparameterslocatedinfunctionsignatures.Thissortofdecoratorisnotintendedtoaltertheparameterinformationorthefunctionbehavior,buttolookintotheparametervalueandthenperformoperationselsewhere,suchas,forargument'ssake,loggingorreplicatingdata.TheParameterDecoratorfunctiontakesthefollowingparameters:

target:Thisistheobjectprototypewherethefunction,whoseparametersaredecorated,usuallybelongstoaclasskey:ThisisthenameofthefunctionwhosesignaturecontainsthedecoratedparameterparameterIndex:Thisistheindexintheparametersarraywherethisdecoratorhasbeenapplied

Thefollowingexampleshowsaworkingexampleofaparameterdecorator:

functionLog(target:Function,key:string,parameterIndex:number){

varfunctionLogged=key||target.prototype.constructor.name;

console.log(`

Theparameterinposition${parameterIndex}at${functionLogged}has

beendecorated

`);

}

classGreeter{

greeting:string;

constructor(@Logphrase:string){

this.greeting=phrase;

}

}

//Theconsolewilloutputrightaftertheclassaboveisdefined:

//'Theparameterinposition0atGreeterhasbeendecorated'

YouhaveprobablynoticedtheweirdassignationofthefunctionLoggedvariable.Thisisbecausethevalueofthetargetparameterwillvarydependingonthefunctionwhoseparametersarebeingdecorated.Therefore,itisdifferentifwedecorateaconstructorparameteroramethodparameter.Theformerwillreturnareferencetotheclassprototypeandthelatterwilljustreturntheconstructorfunction.Thesameappliesforthekeyparameter,whichwillbeundefinedwhendecoratingtheconstructorparameters.

Aswementionedinthebeginningofthissection,parameterdecoratorsarenotmeanttomodifythevalueoftheparametersdecoratedoralterthebehaviorofthemethodsorconstructorswheretheseparameterslive.Theirpurposeisusuallytologorpreparethecontainerobjectforimplementingadditionallayersofabstractionorfunctionalitythroughhigher-leveldecorators,suchasmethodorclassdecorators.Usualcasescenariosforthisencompassloggingcomponentbehaviorormanagingdependencyinjection,aswewillseeinChapter4,EnhancingOurComponentswithPipesandDirectives.

www.EBooksWorld.ir

Page 111: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

OrganizingourapplicationswithmodulesAsourapplicationsscaleandgrowinsize,therewillbeatimewhenwewillneedtobetterorganizeourcodetomakeitsustainableandmorereusable.Modulesaretheresponseforthisneed,solet'stakealookathowtheyworkandhowwecanimplementtheminourapplication.Modulescanbeeitherinternalorexternal.Inthisbook,wewillmostlyfocusonexternalmodules,butitisagoodideatooverviewthetwotypesnow.

www.EBooksWorld.ir

Page 112: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

InternalmodulesInanutshell,internalmodulesaresingletonwrapperscontainingarangeofclasses,functions,objects,orvariablesthatarescopedinternally,awayfromtheglobalorouterscope.Wecanpubliclyexposethecontentsofamodulebyprefixingthekeywordexporttotheelementwewanttobeaccessiblefromtheoutside,likethis:

moduleGreetings{

exportclassGreeting{

constructor(publicname:string){

console.log(`Hello${name}`);

}

}

exportclassXmasGreeting{

constructor(publicname:string){

console.log(`MerryXmas${name}`);

}

}

}

OurGreetingsmodulecontainstwoclassesthatwillbeaccessiblefromoutsidethemodulebyimportingthemoduleandaccessingtheclasswewanttousebyitsname:

importXmasGreeting=Greetings.XmasGreeting;

varxmasGreeting=newXmasGreeting('Joe');

//consoleoutputs'MerryXmasJoe'

Afterlookingattheprecedingcode,wecanconcludethatinternalmodulesareagoodwaytogroupandencapsulateelementsinanamespacecontext.Wecanevensplitourmodulesintoseveralfiles,aslongasthemoduledeclarationkeepsthesamenameacrossthesefiles.Inordertodoso,wewillwanttoreferencethedifferentfileswherewehavescatteredobjectsbelongingtothismodulewithreferencetags:

///<referencepath="greetings/XmasGreeting.ts"/>

ThemajordrawbackofinternalmodulesthoughisthatinordertoputthemtoworkoutsidethedomainofourIDE,weneedtohavealloftheminthesamefileorapplicationscope.WecanincludeallthegeneratedJavaScriptfilesasscriptinsertsinourwebpages,leveragetaskrunnerssuchasGruntorGulpforthat,orevenusethe--outFileflagintheTypeScriptcompilertohaveallthe.tsfilesfoundinyourworkspacecompiledintoasinglebundleusingabootstrapfilewithreferencetagstoalltheothermodulesasthestartingpointforourcompilation:

tsc--outFileapp.jsmodule.ts

ThiswillcompilealltheTypeScriptfilesfollowingthetrailofdependentfilesreferencedwithreferencetags.Ifweforgettoreferenceanyfilethisway,itwillnotbeincludedinthefinalbuildfile,soanotheroptionistoenlistallthefilescontainingstandalonemodulesinthe

www.EBooksWorld.ir

Page 113: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

compilingcommandorjustadda.txtfilecontainingacomprehensivelistofthemodulestobundle.Alternatively,wecanjustuseexternalmodulesinstead.

www.EBooksWorld.ir

Page 114: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

ExternalmodulesExternalmodulesareprettymuchthesolutionweneedwhenitcomestobuildingapplicationsdesignedtogrow.Basically,eachexternalmoduleworksatafilelevel,whereeachfileisthemoduleitselfandthemodulenamewillmatchthefilenamewithoutthe.jsextension.WedonotusethemodulekeywordanymoreandeachmembermarkedwiththeexportprefixwillbecomepartoftheexternalmoduleAPI.TheinternalmoduledepictedinthepreviousexamplewouldturnintothisonceconvenientlysavedintheGreetings.tsfile:

exportclassGreeting{

constructor(publicname:string){

console.log(`Hello${name}`);

}

}

exportclassXmasGreeting{

constructor(publicname:string){

console.log(`MerryXmas${name}`);

}

}

Importingthismoduleandusingitsexportedclasseswouldrequirethefollowingcode:

importgreetings=require('Greetings');

varXmasGreetings=greetings.XmasGreetings();

varxmasGreetings=newXmasGreetings('Pete');

//consoleoutputs'MerryXmasPete'

Obviously,therequirefunctionisnotsupportedbytraditionalJavaScript,soweneedtoinstructthecompilerabouthowwewantthatfunctionalitytobeimplementedinourtargetJavaScriptfiles.Fortunately,theTypeScriptcompilerincludesthe--moduleparameterinitsAPI,sowecanconfigurethedependencyloaderofchoiceforourproject:commonjsfornode-styleimports,amdforRequireJS-basedimports,umdforaloaderimplementingtheUniversalModuleDefinitionspecification,orsystemforSystemJS-basedimports.WewillfocusontheSystemJSmoduleloaderthroughoutthisbook:

tsc--outFileapp.js--modulecommonjs

Theresultingfilewillbeproperlyshimmed,somodulescanloaddependenciesacrossfilesusingourmoduleloaderofchoice.

www.EBooksWorld.ir

Page 115: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

TheroadaheadWereachednowtheendofthischapter.However,webarelyscratchedthesurfaceofTypeScript.Itshugepotentialgoeswaybeyondthescopeofthisshallowintroductionandthereisdefinitelymoreofthiswhichisworthbeingexploredandanalyzedindetail.Unfortunately,thatisoutofthescopeofthisbookandsuchinformationisnotevenrequiredtolearnandunderstandAngular2,althoughthisknowledgewillobviouslyexpandtheboundariesofyourcodepractice.Wewouldsuggestyoutocheckthefollowingaspectsofthelanguagespecificationasastartingpoint:

GenericsMixinsIntersectiontypesUniontypesTuplesCreatingdeclarationtypefiles

IfyouwanttodelvedeeperintoTypeScript,IwouldrecommendyoureadTypeScriptEssentialsbyChristopherNance,LearningTypeScriptbyRemoH.Jansen,orthemoreadvancedMasteringTypeScriptbyNathanRozentals,allbyPacktPublishing.

www.EBooksWorld.ir

Page 116: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

SummaryThiswasdefinitelyalongread,butthisintroductiontoTypeScriptwasabsolutelynecessaryinordertounderstandthelogicbehindmanyofthemostbrilliantpartsofAngular2.Itgaveusthechancetonotonlyintroducethelanguagesyntax,butalsoexplaintherationalebehinditssuccessasthesyntaxofchoiceforbuildingtheAngular2framework.Werevieweditstypearchitectureandhowwecancreateadvancedbusinesslogicdesigningfunctionswithawiderangeofalternativesforparametrizedsignaturesandevendiscoveredhowtobypasstheissuesrelatedwithscopebyusingthepowerfulnewarrowfunctions.Probablythemostrelevantpartofthischapterencompassedtheoverviewofclasses,methods,properties,andaccessorsandhowwecanhandleinheritanceandbetterapplicationdesignthroughinterfaces.Modulesanddecoratorsweresomeothermajorfeaturesexploredinthischapterand,aswewillseeverysoon,havingasoundknowledgeofthesemechanismsisparamounttounderstandhowdependencyinjectionworksinAngular2.

Withallthisknowledgeatourdisposal,wecannowresumeourinvestigationofAngular2andconfrontwithconfidencetherelevantpartsofcomponentcreationsuchasstyleencapsulation,outputformatting,andsoon.Chapter3,ImplementingPropertiesandEventsinOurComponents,willexposeustoadvancedtemplatecreationtechniques,data-bindingtechniques,directives,andpipes.AllthesefeatureswillallowustoputinpracticeallthisnewlygainedknowledgeofTypeScript.

www.EBooksWorld.ir

Page 117: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Chapter3.ImplementingPropertiesandEventsinOurComponentsSofar,wehavehadtheopportunitytotakeabird'seyeoverviewofwhatacomponentisinthenewAngularecosystem,whatisitsrole,howitbehaves,andwhattoolsarerequiredtostartbuildingourowncomponentstorepresentwidgetsandpiecesoffunctionality.Inaddition,TypeScriptturnsouttobetheperfectcompanionforthisendeavor,soweseemtohaveeverythingthatweneedtofurtherexplorethepossibilitiesthatAngular2bringstothegamewithregardstocreatinginteractivecomponentsthatexposepropertiesandemitevents.

Inthischapter,wewill:

DiscoverallthesyntacticpossibilitiesatourdisposaltobindcontentinourtemplatesCreatepublicAPIsforourcomponentssothatwecanbenefitfromtheirpropertiesandeventhandlersSeehowtoimplementdatabindinginAngular2ReducethecomplexityofCSSmanagementwithviewencapsulation

www.EBooksWorld.ir

Page 118: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

AbettertemplatesyntaxInChapter1,CreatingOurVeryFirstComponentinAngular2,wesawhowtoembedHTMLtemplatesinourcomponents,butwedidn'tevenscratchthesurfaceoftemplatedevelopmentforAngular2.Aswewillseelaterinthisbook,templateimplementationistightlycoupledwiththeprinciplesofShadowDOMdesignandbringsforthalotofsyntacticsugartoeasethetaskofbindingpropertiesandeventsinourviewsinadeclarativefashion.

Inanutshell,AngularcomponentsmayexposeapublicAPIthatallowsthemtocommunicatewithothercomponentsorcontainers.ThisAPImayencompassinputproperties,whichweusetofeedthecomponentwithdata.Italsomayexposeoutputpropertieswecanbindeventlistenersto,therebygettingpromptinformationaboutchangesinthestateofthecomponent.

Let'stakealookatthewayAngularsolvestheproblemofinjectingdatainandoutofourcomponentsthroughquickandeasyexamples.Pleasefocusonthephilosophybehindtheseproperties.Wewillhaveachancetoseetheminactionlateronwhenwefollowupwithourpomodoroproject.

www.EBooksWorld.ir

Page 119: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

DatabindingswithinputpropertiesLet'srevisitthepomodorofunctionalitythatwealreadysawinChapter1,CreatingOurVeryFirstComponentinAngular2,andlet'simaginethatwewantourcomponenttohaveaconfigurableattributesothatwecanincreaseordecreasethecountdowntime:

<pomodoro-timer[seconds]="25"></pomodoro-timer>

Pleasenotetheattributewrappedbetweenbrackets.ThisinformsAngularthatthisisaninputproperty.Theclassthatmodelsthepomodoro-timercomponentwillcontainasetterfunctionforthesecondsproperty,whichwillreacttochangesinthatvaluebyupdatingitsowncountdownduration.Wecaninjectadatavariableoranactualhardcodedvalue,inwhichcasewewillhavetowrapitaroundsinglequoteswithinthedoublequotesshouldsuchavaluebeatextstring.

Sometimes,wewillseethissyntaxwhileinjectingdataintoourcomponent'scustomproperties,whileatothertimes,wewillusethisverybracketsyntaxtomakenativeHTMLattributesreactivetocomponentfields,likethis:

<h1[hidden]="hideMe">

Thistextwillnotbevisibleif'hideMe'istrue

</h1>

www.EBooksWorld.ir

Page 120: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

SomeextrasyntacticsugarwhenbindingexpressionsTheAngularteamhasmadeavailablesomeshortcutsforperformingcommontransformationsinourcomponentdirectivesandDOMelements,suchastweakingattributesandclassnamesorapplyingstyles.Here,wehavesomeexamplesofgreattime-saverswhendeclarativelydefiningbindingsinourproperties:

<div[attr.hidden]="isHidden">...</div>

<input[class.is-valid]="isValid">

<div[style.width.px]="myWidth">...</div>

Inthefirstcase,divwillenablethehiddenattributeshouldtheisHiddenexpressionevaluatetotrue.BesidesBooleanvalues,wecanbindanyotherdatatype,suchasastringvalue.Inthesecondcase,theis-validclassnamewillbeinjectedintheclassattributeiftheisValidexpressionevaluatestotrue.Inourthirdexample,divwillfeatureastyleattributethatshowsoffawidthpropertymeanttobesetwiththevalueofthemyWidthexpressionsinpixels.YoucanfindmoreexamplesofthissyntacticsugarintheAngular2cheatsheet(https://angular.io/cheatsheet)availableattheofficialAngularsite.

www.EBooksWorld.ir

Page 121: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

EventbindingwithoutputpropertiesLet'simaginewewantourpomodorotimercomponenttonotifyuswhenthecountdownisfinishedsothatwecanperformsomeotheractionsoutsidetherealmofthecomponent.Wecanachievesuchfunctionalitywithanoutputpropertylikethis:

<pomodoro-timer(countdownComplete)="onCountownCompleted()"></pomodoro-timer>

Notetheattributewrappedbetweenbraces.ThisinformsAngularthatsuchanattributeis,infact,anoutputpropertythatwilltriggertheeventhandlerwebindtoit.Inthiscase,wewillwanttocreateanonCountownCompletedeventhandleronthecontainerobjectthatwrapsthiscomponent.

Tip

Bytheway,thecamelcaseisnotacoincidence.ItisanamingconventionappliedtoalloutputandinputpropertynamesinAngular2.

Wewillfindoutputpropertiesmappedtointeractioneventsthatwealreadyknow,suchasclick,mouseover,mouseout,focus,andmore.

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

www.EBooksWorld.ir

Page 122: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

InputandoutputpropertiesinactionThebestwaytograsptheconceptsdetailedintheearliersectionsisbypractice.Let'sstripdownthepomodorotimerexamplethatwesawinChapter1,CreatingOurVeryFirstComponentinAngular2,anddiscussasimplerexample.Openthepomodoro-timer.tsfileandreplaceitscontentswiththefollowingcomponentclass:

import{Component}from'@angular/core';

import{bootstrap}from'@angular/platform-browser-dynamic';

@Component({

selector:'countdown',

template:'<h1>Timeleft:{{seconds}}</h1>'

})

classCountdownComponent{

seconds:number=25;

intervalId:number;

constructor(){

this.intervalId=setInterval(()=>this.tick(),1000);

}

privatetick():void{

if(--this.seconds<1){

clearInterval(this.intervalId);

}

}

}

Great!Wehavejustdefinedasimplebuthighlyeffectivecountdowntimercomponentthatwillcountdownto0from25seconds.(Doyouseethesecondsfieldupthere?TypeScriptsupportstheinitializationofmembersupondeclaringthem).AsimplesetIntervalloopexecutesacustomprivatefunctionnamedtick()thatdecreasesthevalueofsecondsuntilitreacheszero,inwhichcasewejustcleartheinterval.

PleasenoticethatwedidnotincludeanycalltothebootstrapfunctiontoinstantiatethiscomponentinaHTMLdocument,eventhoughweareeffectivelyimportingthefunction.Thisisactuallyprettycommon,sinceweonlycallthebootstrapfunctiontoinstantiatethetopparentcomponent(alsoknownasrootcomponent)andalltheothercomponents,definedaschildcomponentsoftheformer,willbeautomaticallyinstantiatedincascade.

However,nowwejustneedtoembedthiscomponentsomewhere,solet'screateanothercomponentwithnofunctionalityotherthanactingasaHTMLwrapperhostforthepreviouscomponent.CreatethisnewcomponentrightaftertheCountdownComponentclassinitssamefile:

@Component({

selector:'pomodoro-timer',

directives:[CountdownComponent],

template:'<countdown></countdown>'

www.EBooksWorld.ir

Page 123: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

})

classPomodoroTimerComponent{}

bootstrap(PomodoroTimerComponent);

Aswecanseethere,weintroducedanewpropertynameddirectivesinthecomponentinitializationanddeclaredCountdownComponentthere.ComponentsinAngular2arebasicallydirectiveswithaviewtemplate.Wecanalsofinddirectiveswithnoview,whichbasicallyaddnewfunctionalitiestotheirhostelement;ortheyjustactascustomelementswithoutaUIthatwrapsotherelements.Alternatively,theysimplyprovidefurtherfunctionalitiestoothercomponentsbymeansoftheirAPI.

Wewillexploredirectivesindetailinthenextchapterandalsoalongthebook.Fornow,let'sjustpointoutthatwhenwedefineacomponentcontainingothercomponents,aswehavejustdone,wewillhavetoexplicitlydeclaretheimmediatechildrencomponents'classesinthedirectivesarrayparameter.YoumustbewonderingwhyhavewecreatedthishostorparentPomodoroTimerComponentcomponentwithnoimplementation.Soon,wewillfleshitoutwithsomemorefeatures,butfornowlet'suseitasaproofofconceptforhowtoinitiateacomponenttree.

Settingupcustomvaluesdeclaratively

Youwillprobablyagreeonthefactthathavingthefunctionalityofsettingupcustomcountdowntimerswouldbenice,right?Inputpropertiesturnouttobeanexcellentwaytoachievethis.Inordertoleveragethisfunctionality,wewillhavetotweaktheimportstatementatthetopofthefile:

import{Component,Input}from'@angular/core';

Let'supdateourpomodorotimeraccordingly:

@Component({

selector:'countdown',

template:'<h1>Timeleft:{{seconds}}</h1>'

})

classCountdownComponent{

@Input()seconds:number;

intervalId:number;

//Restofimplementationremainsthesame...

}

Youmighthavealreadynoticedthatwearenolongerinitializingthesecondsfield,anditisnowdecoratedwithapropertydecorator(aswesawinChapter2,IntroducingTypeScript).WehavejuststartedtodefinetheAPIofourcomponent.

Note

Propertynamingiscasesensitive,andtheconventionenforcedbyAngular2istoapply

www.EBooksWorld.ir

Page 124: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

camelcasetocomponentinputand,aswewillseeshortly,outputpropertiesalike.

Next,wejustneedtoaddthedesiredpropertyinourcontainercomponent'stemplate:

@Component({

selector:'pomodoro-timer',

directives:[CountdownComponent],

template:`<divclass="containertext-center">

<imgsrc="assets/img/pomodoro.png"/>

<countdown[seconds]="25"></countdown>

</div>`

})

PleasenotethatwehavenotupdatedthePomodorotimerComponentatall.WeonlyupdateditsCountdownComponentchildrencomponent.However,itsbrandnewAPIbecomesavailabletoanycomponentthateventuallyincludesitinitsowntemplateasachildcomponent,sowecansetupitspropertiesdeclarativelyrightfromthetemplate,orevenbindavalueimperativelyfromapropertylocatedatthePomodorotimerComponentcontrollerclassifwewish.

Tip

Whenflaggingaclasspropertywith@Input(),wecanconfigurethenamewewantthispropertytohaveuponinstantiatingthecomponentintheHTML.Todoso,wejustneedtointroduceournameofchoiceinthedecoratorsignature,likethis@Input('name_of_the_property').Inanyevent,thispracticeisdiscouragedsinceexposingpropertynamesinthecomponentAPIdistinctfromtheonesdefinedinitscontrollerclasscanonlyleadtoconfusion.

Communicatingbetweencomponentsthroughcustomevents

Nowthatourchildcomponentisbeingconfiguredbyitsparentcomponent,howcanweachievecommunicationfromthechildtotheparent?Thisiswherecustomeventscometotherescue!Inordertocreatepropereventbindings,wejustneedtoconfigureanoutputpropertyinourcomponentandattachaneventhandlerfunctiontoit.

Inordertotriggercustomevents,wewillneedtobringEventEmittertotheparty,alongwiththe@Outputdecorator,whosefunctionalityisexactlytheoppositetowhatwelearnedregardingthe@Inputdecorator:

import{Component,Input,Output,EventEmitter}from'@angular/core';

EventEmitteristhebuilt-ineventbusofAngular2.Inanutshell,theEventEmitterclassprovidessupportforemittingObservabledataandsubscribingObserverconsumerstodatachanges.Itssimpleinterface,whichbasicallyencompasstwomethods,emit()andsubscribe(),canthereforebeusedtotriggercustomeventsandlistentoeventsaswell,bothsynchronouslyorasynchronously.WewilldiscussObservablesinmoreindetailinChapter6,AsynchronousDataServiceswithAngular2.Forthetimebeing,wecangetawaywiththe

www.EBooksWorld.ir

Page 125: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

ideathatwewillbeusingtheEventEmitterAPItospawneventsthatlistenermethodsinthecomponentshostingourevent-emittingcomponentcanobserveandattacheventhandlersto.Theseeventsacquirevisibilityoutsidethescopeofthecomponentthroughanyofitspropertiesannotatedwiththe@Input()decorator.

Thefollowingcodeshowsanactualimplementationthatfollowsupfromthepreviousexample:

@Component({

selector:'countdown',

template:'<h1>Timeleft:{{seconds}}</h1>'

})

classCountdownComponent{

@Input()seconds:number;

intervalId:number;

@Output()complete:EventEmitter<any>=newEventEmitter();

constructor(){

this.intervalId=setInterval(()=>this.tick(),1000);

}

privatetick():void{

if(--this.seconds<1){

clearTimeout(this.intervalId);

//Aneventisemitteduponfinishingthecountdown

this.complete.emit(null);

}

}

}

AnewpropertynamedcompleteisconvenientlyannotatedwiththeEventEmittertypeandinitializedonthespot.Lateronwewillaccessitsemitmethodtospawnacustomeventassoonasthecountdownends.Theemit()methodneedsonemandatoryparameterofanytype,sowecansendadatavaluetotheeventsubscribers(ornullifnotrequired).

Now,wejustneedtosetupourhostcomponentsothatitwilllistentothiscompleteeventoroutputpropertyandsubscribeaneventhandlertoit:

@Component({

selector:'pomodoro-timer',

directives:[CountdownComponent],

template:`<divclass="containertext-center">

<imgsrc="assets/img/pomodoro.png"/>

<countdown[seconds]="25"

(complete)="onCountdownCompleted()">

</countdown>

</div>`

})

classPomodoroTimerComponent{

onCountdownCompleted():void{

alert('Timeup!');

}

}

www.EBooksWorld.ir

Page 126: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Tip

WhycompleteandnotonComplete?

Angular2providessupportforanalternativesyntaxnamedcanonicalformforbothinputandoutputproperties.Inthecaseofinputproperties,apropertyrepresentedas[seconds]couldberepresentedasbind-seconds,withouttheneedforbrackets.Withregardstooutputproperties,thesecanberepresentedason-completeinsteadof(complete).Thatiswhyweneverprefixoutputpropertynameswithanonprefix,sincethatwouldconcuronoutputpropertiessuchason-on-completeincaseweeventuallydecidetofavorthecanonicalsyntaxforminourprojects.

www.EBooksWorld.ir

Page 127: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

EmittingdatathroughcustomeventsNowthatweknowhowtoemitcustomeventsfromourcomponentAPI,whydon'twetakeastepfurtherandsenddatasignalsbeyondthescopeofthecomponent?Wealreadydiscussedthattheemit()eventoftheEventEmitter<T>classacceptsinitssignatureanygivendataofthetyperepresentedbytheTannotation.Let'sextendourexampletonotifytheprogressofthecountdown.Whywouldweeverwanttodothis?Basically,ourcomponentdisplaysonscreenavisualcountdown,butwemightwanttowatchthecountdownprogressprogrammaticallyinordertotakeactiononcethecountdownisfinishedorreachesacertainpoint.

Let'supdateourtimercomponentwithanotheroutputpropertythatmatchestheoriginalandemitsacustomeventoneachiterationofthesecondsproperty,asfollows:

classCountdownComponent{

@Input()seconds:number;

intervalId:number;

@Output()complete:EventEmitter<any>=newEventEmitter();

@Output()progress:EventEmitter<number>=newEventEmitter();

constructor(){

this.intervalId=setInterval(()=>this.tick(),1000);

}

privatetick():void{

if(--this.seconds<1){

clearTimeout(this.intervalId);

this.complete.emit(null);

}

this.progress.emit(this.seconds);

}

}

Now,let'srebuildourhostcomponent'stemplatetoreflecttheactualprogressofthecountdown.Wealreadydosobydisplayingthecountdown,butthatisafeaturehandledinternallybytheCountdownComponent.Now,wewillkeeptrackofthecountdownoutsidethiscomponent:

@Component({

selector:'pomodoro-timer',

directives:[CountdownComponent],

template:`<divclass="containertext-center">

<imgsrc="assets/img/pomodoro.png"/>

<countdown[seconds]="25"

(progress)="timeout=$event"

(complete)="onCountdownCompleted()">

</countdown>

<p*ngIf="timeout<10">

Beware!Only<strong>{{timeout}}seconds</strong>left.

</p>

</div>`

})

classPomodoroTimerComponent{

www.EBooksWorld.ir

Page 128: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

timeout:number;

onCountdownCompleted():void{

alert('Timeup!');

}

}

Wetookadvantageofthisroundofchangestoformalizethetimeoutvalueasapropertyofthehostcomponent.Thisallowsustobindnewvaluestothatpropertyinourcustomeventhandlers,aswedidintheprecedingexample.Ratherthanbindinganeventhandlermethodtothe(progress)handler,werefertothe$eventreservedvariable.Itisapointertothepayloadoftheprogressoutputpropertythatreflectsthevaluewepasstotheemit()functionwhenexecutingthis.progress.emit(this.seconds).Inshort,$eventisthevalueassumedbythis.secondsinsideCountdownComponent.Byassigningsuchvaluetothetimeoutclasspropertywithinthetemplate,wearealsoupdatingthebindingexpressedintheparagraphwejustinsertedinthetemplate.Thisparagraphwillonlybecomevisiblewhentimeoutislowerthan10.

<countdown[seconds]="25"

on-progress="timeout=$event"

on-complete="onCountdownCompleted()">

</countdown>

www.EBooksWorld.ir

Page 129: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

LocalreferencesintemplatesWehavepreviouslyseenhowwecanbinddatatoourtemplatesusingdatainterpolationwiththedoublecurlybracessyntax.Besidesthis,wewillquiteoftenspotnamedidentifiersprefixedbyahashsymbol(#)intheelementsbelongingtoourcomponentsorevenregularHTMLcontrols.Thesereferenceidentifiers,namelylocalnames,areusedtorefertothecomponentsflaggedwiththeminourtemplateviewsandthenaccessthemprogrammatically.TheycanalsobeusedbycomponentstorefertootherelementsinthevirtualDOMandaccessitsproperties.

Intheprevioussection,wesawhowwecouldsubscribetothecountdownprogressthroughtheprogressevent.But,whatifwecouldinspectthecomponentindepth,oratleastitspublicpropertiesandmethods,andreadthevaluethatthesecondspropertytakesoneachtickintervalwithouthavingtolistentotheprogressevent?Well,settingalocalreferenceonthecomponentitselfwillopenthedoortoitspublicfaçade.

Let'sflagtheinstanceofourCountdownComponentinthePomodoroTimerComponenttemplatewithalocalreferencenamed#counter.Fromthatverymoment,wewillbeabletodirectlyaccessthecomponent'spublicproperties,suchasseconds,andevenbinditinotherlocationsofthetemplate.Thisway,wedonotevenneedtorelyontheprogresseventemitterorthetimeoutclassfield,andwecanevenmanipulatethevalueofsuchproperties.Thisisshowninthefollowingcode:

@Component({

selector:'pomodoro-timer',

directives:[CountdownComponent],

encapsulation:ViewEncapsulation.None,

template:`<divclass="containertext-center">

<imgsrc="assets/img/pomodoro.png"/>

<countdown[seconds]="25"

(complete)="onCountdownCompleted()"

#counter>

</countdown>

<p>

<buttonclass="btnbtn-default"(click)="counter.seconds=

25">Resetcountdownto25seconds

</button>

</p>

<p*ngIf="counter.seconds<10">

Beware!Only

<strong>{{counter.seconds}}seconds</strong>

left.

</p>

</div>`

})

classPomodoroTimerComponent{

//timeout:number;/*Nolongerrequired*/

onCountdownCompleted():void{

alert('Timeup!');

}

www.EBooksWorld.ir

Page 130: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

}

www.EBooksWorld.ir

Page 131: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

AlternativesyntaxforinputandoutputpropertiesBesidesthe@Input()and@Output()decorators,thereisanalternativesyntaxwherewecandefineinputandoutputpropertiesinourcomponentsbymeansofthe@Componentdecorator.Itsmetadataimplementationprovidessupportforbothfeaturesthroughtheinputsandoutputspropertynames,respectively.

TheCountdownComponentAPIcouldthereforebeimplementedlikethis:

@Component({

selector:'countdown',

template:'<h1>Timeleft:{{seconds}}</h1>',

inputs:['seconds'],

outputs:['complete','progress']

})

classCountdownComponent{

seconds:number;

intervalId:number;

complete:EventEmitter<any>=newEventEmitter();

progress:EventEmitter<number>=newEventEmitter();

//Etc...

}

Allinall,thissyntaxisdiscouragedandhasbeenincludedhereforreferencepurposesonly.Inthefirstplace,weduplicatecodebydefiningthenamesofourAPIendpointsintwoplacesatthesametime,increasingtheriskoferrorswhenrefactoringcode.Itisalsoacommonconventiontokeepthedecoratorimplementationsasleanaspossibleinordertoimprovereadability.

Tip

Istronglysuggestthatyousticktothe@Inputand@Outputdecorators.

www.EBooksWorld.ir

Page 132: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

ConfiguringourtemplatefromourcomponentclassTheComponentmetadataalsosupportsseveralsettingsthatcontributetoeasytemplatemanagementandconfiguration.Ontheotherhand,Angular2takesadvantageoftheCSSencapsulationfunctionalitiesofWebComponents.

www.EBooksWorld.ir

Page 133: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

InternalandexternaltemplatesAsourapplicationsgrowinsizeandcomplexity,chancesarethatourtemplateswillgrowaswell,hostingothercomponentsandbiggerchunksofHTMLcode.Embeddingallthiscodeinourcomponentclassdefinitionswillbecomeacumbersomeandunpleasanttaskandquitepronetoerrorsbytheway.Inordertopreventthisfromhappening,wecanleveragethetemplateUrlproperty,pointingtoastandaloneHTMLfilethatcontainsourcomponentHTMLmarkup.

Backtoourpreviousexample,wecanrefactorthe@ComponentdecoratorofourPomodoroTimerComponentclasstopointtoanexternalhtmlfilecontainingourtemplate.Createanewfilenamedpomodoro-timer.htmlinthesameworkspacewhereourpomodoro-timer.tsfilelivesandpopulateitwiththesameHTMLweconfiguredinourPomodoroTimerComponentclass:

<divclass="containertext-center">

<imgsrc="assets/img/pomodoro.png"alt="Pomodoro"/>

<countdown

[seconds]="25"

(complete)="onCountdownCompleted()"

#counter>

</countdown>

<p>

<buttonclass="btnbtn-default"(click)="counter.seconds=25">

Resetcountdownto25seconds

</button>

</p>

<p*ngIf="counter.seconds<10">

Beware!Only

<strong>{{counter.seconds}}seconds</strong>

left.

</p>

</div>

Now,wecanpolishour@ComponentdecoratortopointtothatfileinsteadofdefiningtheHTMLinsidethedecoratormetadata:

@Component({

selector:'pomodoro-timer',

directives:[CountdownComponent],

templateUrl:'./pomodoro-tasks.html'

})

classPomodoroTimerComponent{

//Classfollowsbelow...

}

Note

ExternaltemplatesfollowacertainconventioninAngular2,enforcedbythemostpopularAngular2codingstyleguidesoutthere,whichistosharethesamefilenamethanthecomponenttheybelongto,includinganyfilenameprefixorsuffixwemightappendtothe

www.EBooksWorld.ir

Page 134: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

componentfilename.WewillseethiswhenexploringcomponentnamingconventionsinChapter5,BuildinganApplicationwithAngular2Components.Thisway,itiseasiertorecognize,orevensearchwithyourIDEsearchbuilt-infuzzyfindertool,whatHTMLfileisinfactthetemplateofaspecificcomponent.

Whatisthethresholdforcreatingstandalonetemplatesratherthankeepingthetemplatemarkupinsidethecomponent?Itdependsonthecomplexityandsizeofthetemplate.Commonsensewillbeyourbestadvisorinthiscase.

www.EBooksWorld.ir

Page 135: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

EncapsulatingCSSstylingInordertobetterencapsulateourcodeandmakeitmorereusable,wecandefineCSSstylingwithinourcomponents.Theseinternalstylesheetsareagoodwaytomakeourcomponentsmoreshareableandmaintainable.TherearethreedifferentwaysofdefiningCSSstylingforourcomponents.

Thestylesproperty

WecandefinestylesforourHTMLelementsandclassnamesthroughthestylepropertyinthecomponentdecorator,likethis:

@Component({

selector:'my-component',

styles:[`

p{

text-align:center;

}

table{

margin:auto;

}`]

})

ThispropertywilltakeanarrayofstringscontainingCSSruleseachandapplythemtothetemplatemarkupbyembeddingthoserulesattheheadofthedocumentassoonaswebootstrapourapplication.Wecaneitherinlinethestylingrulesinasingleline,ortakeadvantageofES2015templatestringstoindentthecodeandmakeitmorereadableasdepictedintheexampleabove.

ThestyleUrlsproperty

Justlikestyles,styleUrlswillacceptanarrayofstrings,althougheachonewillrepresentalinktoanexternalstylesheetthough.Thispropertycanbeusedalongsidethestylespropertyaswell,definingdifferentsetsofruleswhererequired:

@Component({

selector:'my-component',

styleUrls:['path/to/my-stylesheet.css'],

styles:[`

p{

text-align:center;

}

table{

margin:auto;

}`]

})

Inlinestylesheets

Wecanalsoattachthestylingrulestothetemplateitself,nomatterwhetherit'saninlinetemplateoratemplateservedthroughthetemplateUrlparameter:

www.EBooksWorld.ir

Page 136: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

@Component({

selector:'app',

template:`

<style>p{color:red;}</style>

<p>Iamaredparagraph</p>`

})

www.EBooksWorld.ir

Page 137: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

ManagingviewencapsulationAlltheprecedingsections(styles,styleUrls,andinlinestylesheets)willbegovernedbytheusualrulesofCSSspecificity(https://developer.mozilla.org/en/docs/Web/CSS/Specificity).CSSmanagementandspecificitybecomesabreezeonbrowsersthatsupportShadowDOM,thankstoscopedstyling.CSSstylesapplytotheelementscontainedinthecomponentbutdonotspreadbeyonditsboundaries.

Ontopofthat,Angularwillembedthesestylesheetsattheheadofthedocument,sotheymightaffectotherelementsofourapplication.Inordertopreventthisfromhappening,wecansetupdifferentlevelsofviewencapsulation.

Inanutshell,encapsulationisthewayAngularneedstomanageCSSscopingwithinthecomponentforbothShadowDOM-compliantbrowsersandthosethatdonotsupportit.Forallthis,weleveragetheViewEncapsulationenum,whichcantakeanyofthesevalues:

Emulated:Thisisthedefaultoption,anditbasicallyentailsanemulationofnativescopinginShadowDOMthroughsandboxingtheCSSrulesunderaspecificselectorthatpointstoourcomponent.Thisoptionispreferredtoensurethatourcomponentstyleswillnotbeaffectedbyotherexistinglibrariesonoursite.Native:UsethenativeShadowDOMencapsulationmechanismoftherenderer,anditonlyworksonbrowsersthatsupportShadowDOM.None:Templateorstyleencapsulationisnotprovided.Thestyleswillbeinjectedasisintothedocument'sheader.

Let'scheckoutanactualexample.First,importtheViewEncapsulationenumintothescript,andthencreateanencapsulationpropertywiththeEmulatedvalue.Then,let'screateastyleruleforourcountdowntextsoany<h1>(!)tagisrenderedindarkred:

import{

Component,

EventEmitter,

Input,

Output,

ViewEncapsulation

}from'@angular/core';

import{bootstrap}from'@angular/platform-browser-dynamic';

@Component({

selector:'countdown',

template:'<h1>Timeleft:{{seconds}}</h1>',

styles:['h1{color:#900}'],

encapsulation:ViewEncapsulation.Emulated

})

classCountdownComponent{

//Etc...

}

Now,clickonthebrowser'sdevtoolsinspectorandcheckthegeneratedHTMLtodiscover

www.EBooksWorld.ir

Page 138: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

howAngular2injectedtheCSSinsidethepage<HEAD>block.ThejustinjectedstylesheethasbeensandboxedtoensurethattheglobalCSSrulewedefinedatthecomponentsetupinaverynon-specificwayforall<h1>elementsonlyappliestomatchingelementsscopedbytheCountdownComponentcomponentexclusively.

WerecommendthatyoutryoutdifferentvaluesandseehowtheCSScodeisinjectedintothedocument.Youwillimmediatelynoticethedifferentgradesofisolationthateachvariationprovides.

www.EBooksWorld.ir

Page 139: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

SummaryThischapterguidedusthroughtheoptionsavailableinAngular2forcreatingpowerfulAPIsforourcomponents,sowecanprovidehighlevelsofinteroperabilitybetweencomponents,configuringitspropertiesbyassigningeitherstaticvaluesormanagedbindings.Wealsosawhowacomponentcanactasahostcomponentforanotherchildcomponent,instantiatingtheformer'scustomelementinitsowntemplate,settingthegroundupforlargercomponenttreesinourapplications.Outputparametersgivethelayerofinteractivityweneedbyturningourcomponentsintoeventemitterssotheycanproperlycommunicateinanagnosticfashionwithanyparentcomponentthatmighteventuallyhostthem.Templatereferencespavedthewaytocreatereferencesinourcustomelementsthatwecanuseasaccessorstotheirpropertiesandmethodsfromwithinthetemplateinadeclarativefashion.Wealsodiscussedhowwecouldisolatethecomponent'sHTMLtemplateinanexternalfileinordertoeaseitsfuturemaintainabilityandhowtodothesamewithanystylesheetwewantedtobindtothecomponent,incasewedonotwanttobundlethecomponentstylesinline.Anoverviewofthebuilt-infeaturesforhandlingviewencapsulationinAngular2gaveussomeadditionalinsightsonhowwecanbenefitfromShadowDOM'sCSSscopingonapercomponentbasisandhowwecanpolyfillitwhennotsupported.

WestillhavemuchmoretolearnregardingtemplatemanagementinAngular2,mostlywithregardstothetwoconceptsthatyouwilluseextensivelyalongyourjourneywithAngular.IamreferringtoDirectivesandPipes,whichwewillcoverextensivelyinChapter4,EnhancingourComponentswithPipesandDirectives.

www.EBooksWorld.ir

Page 140: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Chapter4.EnhancingOurComponentswithPipesandDirectivesInthepreviouschapters,webuiltseveralcomponentsthatrendereddataonscreenwiththehelpofinputandoutputproperties.Wewillleveragetheknowledgeinthischaptertotakeourcomponentstothenextlevelwiththeuseofdirectivesandpipes.Inanutshell,whilepipesgiveustheopportunitytodigestandtransformtheinformationwebindinourtemplates,directivesallowustoconductmoreambitiousfunctionalitieswherewecanaccessthehostelementpropertiesandalsobindourveryowncustomeventlistenersanddatabindings.

Inthischapter,wewill:

Haveacomprehensiveoverviewofthebuilt-indirectivesofAngular2DiscoverhowwecanrefineourdataoutputwithpipesSeehowwecandesignandbuildourowncustompipesanddirectivesLeveragebuilt-inobjectsformanipulatingourtemplatesPutalltheprecedingtopicsandmanymoreintopracticebyfollowinguponourpomodoroprojecttobuildafullyinteractiveto-doitemstable

www.EBooksWorld.ir

Page 141: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

DirectivesinAngular2Angular2definesdirectivesascomponentswithoutviews.Infact,acomponentisadirectivewithanassociatedtemplateview.ThisdistinctionisusedbecausedirectivesareaprominentpartoftheAngular2coreandeach(plaindirectivesandcomponentdirectives)needstheothertoexist.DirectivescanbasicallyaffectthewayHTMLelementsorcustomelementsbehaveanddisplaytheircontent.

www.EBooksWorld.ir

Page 142: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

CoredirectivesLet'stakeacloserlookattheframework'scoredirectives,andthenyouwilllearnhowtobuildyourowndirectiveslateroninthischapter.

NgIf

Astheofficialdocumentationstates,theNgIfdirectiveremovesorrecreatesaportionoftheDOMtreebasedonanexpression.IftheexpressionassignedtotheNgIfdirectiveevaluatestofalse,thentheelementisremovedfromtheDOM.Otherwise,acloneoftheelementisreinsertedintotheDOM.Wecouldenhanceourcountdowntimerbyleveragingthisdirective,likethis:

<pomodoro-timer[seconds]="timeout"></pomodoro-timer>

<p*ngIf="timeout===0">Timeup!</p>

Whenourpomodorotimerreaches0,theparagraphthatdisplaystheTimeup!textwillberenderedonthescreen.Youhaveprobablynoticedthatasteriskthatprependsthedirective.ThisisbecauseAngularembedstheHTMLcontrolmarkedwiththeNgIfdirective(andallitsHTMLsubtrees,ifany)ina<template>tag,whichwillbeusedlaterontorenderthecontentonthescreen.CoveringhowAngulartreatstemplatesisdefinitelyoutofthescopeofthisbook,butlet'sjustpointoutthatthisissyntacticsugarprovidedbyAngulartoactasashortcuttothatother,moreverbosesyntaxbasedontemplatetags.

PerhapsyouarewonderingwhatdifferencedoesitmaketorendersomechunkofHTMLonscreenwith*ngIf="conditional"ratherthanwith[hidden]="conditional".TheformerwillcloneandinjectpiecesoftemplatedHTMLsnippetsinthemarkup,removingitfromtheDOMwhentheconditionevaluatestofalse,whilethelatterdoesnotinjectorremoveanymarkupfromtheDOM.ItsimplysetsthevisibilityofthealreadyexistingchunkofHTMLannotatedwiththatDOMattribute.

NgFor

TheNgFordirectiveallowsustoiteratethroughacollection(oranyotheriterableobject)andbindeachofitsitemstoatemplateofourchoice,wherewecandefineconvenientplaceholderstointerpolatetheitemdata.Eachinstantiatedtemplateisscopedtotheoutercontext,wheretheloopdirectiveisplaced,sowecanaccessotherbindings.Let'simaginewehaveacomponentnamedStaff:itfeaturesafieldnamedemployees,whichrepresentsanarrayofEmployeeobjects.Wecanenlistthoseemployeesandjobtitlesinthisway:

<ul>

<li*ngFor="letemployeeofemployees;leti=index;letlast=last">

Employee#{{i}}:-{{employee.name}},{{employee.position}}

<span*ngIf="last"><br/>Endoflist</span>

</li>

</ul>

Aswecanseeintheexampleprovided,weturneachitemfetchedfromtheiterableobjecton

www.EBooksWorld.ir

Page 143: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

eachloopintoalocalreferencesothatwecaneasilybindthisiteminourtemplate.Here,wealsousethesyntaxsugarthatweusedintheprevioussection,andAngulargivesustheopportunitytoassignindextoascopedvariablethatwillbesettothecurrentloopiterationinthetemplatecontext.Wecanalsoassignlasttoascopedvariablethatwillinformwhethertheitemisthelastoneintheiteration.

Thisdirectiveobserveschangesintheunderlyingiterableobjectandwilladd,remove,orsorttherenderedtemplatesasitemsareadded,removed,orreorderedinthecollection.

NgStyle

Asyouprobablyhaveguessedalready,thisdirectiveallowsustobindCSSstylesbyevaluatingacustomobjectorexpression.WecanbindanobjectwhosekeysandvaluesmapCSSproperties,orjustdefinespecificpropertiesandbinddatatothem:

<p[ngStyle]="{'color':myColor,'font-weight':myFontWeight}">Iamredand

bold</p>

IfourcomponentdefinesthemyColorandmyFontWeightpropertieswiththeredandboldvalues,respectively,thecolorandweightofthetextwillchangeaccordingly.Thedirectivewillalwaysreflectthechangesmadewithinthecomponent,andwecanalsopassanobjectinsteadofbindingdataonaperpropertybasis:

<p[ngStyle]="myCssConfig">Iamredandbold</p>

NgClass

SimilartoNgStyle,NgClassallowsustodefineandtoggleclassnamesprogrammaticallyinaDOMelementusingaconvenientdeclarativesyntax.Thissyntaxhasitsownintricacies,however.Let'sseeeachoneofthethreecasescenariosavailableforthisexample:

<p[ng-class]="{{myClassNames}}">HelloAngular!</p>

Forinstance,wecanuseastringtypesothatifmyClassNamescontainsastringwithoneorseveralclassesdelimitedbyaspace,allofthemwillbeboundtotheparagraph.

Wecanuseanarrayaswellsothateachelementwillbeadded.

Lastbutnotleast,wecanuseanobjectinwhicheachkeycorrespondstoaCSSclassnamereferredtobyaBooleanvalue.Eachkeynamemarkedastruewillbecomeanactiveclass.Otherwise,itwillberemoved.Thisisusuallythepreferredwayofhandlingclassnames.

NgSwitch,NgSwitchWhen,andNgSwitchDefault

TheNgSwitchdirectiveisusedtoswitchtemplateswithinaspecificsetdependingontheconditionrequiredfordisplayingeachone.Theimplementationfollowsseveralsteps,thereforethreedifferentdirectivesareexplainedinthissection.

www.EBooksWorld.ir

Page 144: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

NgSwitchwillevaluateagivenexpressionandthentoggleanddisplaythosechildelementsmarkedwithanngSwitchWhenattributedirective,whosevaluematchesthevaluethrownbytheexpressiondefinedintheparentngSwitchelement.AspecialmentionisrequiredaboutthechildrenelementmarkedwiththengSwitchDefaultdirectiveattribute.ThisattributequalifiesthetemplatethatwillbedisplayedwhennoothervaluedefinedbyitsngSwitchWhensiblingsmatchestheparentconditionalexpression.

We'llseeallofthisinanexample:

<div[ngSwitch]="weatherForecastDay">

<templatengSwitchWhen="today">{{weatherToday}}</template>

<templatengSwitchWhen="tomorrow">

{{weatherTomorrow}}</template>

<templatengSwitchDefault>

Pickadaytoseetheweatherforecast

</template>

</div>

Theparent[ngSwitch]parameterevaluatestheweatherForecastDaycontextvariable,andeachnestedngSwitchWhendirectivewillbetestedagainstit.Wecanuseexpressionsinstead,butwewanttowrapngSwitchWheninbracketssothatAngularcanproperlyevaluateitscontentascontextvariablesinsteadoftakingitasatextstring.

Note

Atthetimeofclosingthewritingofthisbook,theAngularcoreteamisdiscussingtheconvenienceofrenamingngSwitchWhentongSwitchCaseinordertokeepconsistentwithJavaScriptandTypeScriptswitch/casekeywords,andalsowithotheri18ndirectivessuchasNgPluralandNgPluralCase.ItisquitelikelythatthisbreakingchangewillmakeittoAngular2finalsopleaserefertotheonlinedocumentationtodoublecheckthefinalsyntaxforNgSwitchtemplatecases.

CoveragefortheNgPluralandNgPluralCasesitsoutsideofthescopeofthisbook,butbasicallyprovideaconvenientwaytorenderorremovetemplatesDOMblocksthatmatchaswitchexpression,eitherstrictlynumericorjustastring,inasimilarfashiontohowtheNgSwitchandNgSwitchWhendirectivesdo.

www.EBooksWorld.ir

Page 145: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

ManipulatingtemplatebindingswithPipesSo,wesawhowwecanusedirectivestorendercontentdependingonthedatathatourcomponentclassesmanage,butthereisanotherpowerfulfeaturethatwewillbeusingthoroughlyinourdailypracticewithAngular.WearetalkingaboutPipes.

Pipesallowustofilterandfunneltheoutcomeofourexpressionsonaviewleveltotransformorjustbetterdisplaythedatawearebinding.Theirsyntaxisprettysimple,basicallyconsistingofthepipenamefollowingtheexpressionthatwewanttotransform,separatedbyapipesymbol(hencethename):

@Component({

selector:"greeting",

template:"HELLO{{name|uppercase}}"

})

classGreetingComponent{

name:string;

}

Intheprecedingexample,wearedisplayinganuppercasegreetingonthescreen.Sincewedonotknowwhetherthenamewillbeinuppercaseornot,weensureaconsistentoutputbytransformingthevalueofthenamewheneveritisnotanuppercaseversionattheviewlevel.Pipesarechainable,andAngularhasawiderangeofpipetypesalreadybakedin.Aswewillseefurtherinthischapter,wecanalsobuildourownpipestofine-graindataoutputincaseswherethebuilt-inpipesaresimplynotenough.

www.EBooksWorld.ir

Page 146: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Theuppercase/lowercasepipeThenameuppercase/lowercasepipesaysitall.Asintheexampleprovidedpreviously,thispipesetsthestringoutputinuppercaseorlowercase.Insertthefollowingcodeanywhereinyourviewandcheckouttheoutputforyourself:

<p>{{'helloworld'|uppercase}}</p>

<!--outputs'HELLOWORLD'-->

<p>{{'wEIrDhElLo'|lowercase}}</p>

<!--outputis'weirdhello'-->

www.EBooksWorld.ir

Page 147: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Thenumber,percent,andcurrencypipesNumericdatacancomeinawiderangeofflavors,andthispipeisespeciallyconvenientwhenitcomestobetterformattingandlocalizingtheoutput.ThesepipesusetheInternationalizationAPI,andthereforetheyarereliableinChromeandOperabrowsersonly.

Thenumberpipe

Thenumberpipewillhelpusdefinethegroupingandsizingofnumbersusingtheactivelocaleinourbrowser.Itsformatisasfollows:

expression|number[:digitInfo]

Here,expressionisanumberanddigitInfohasthefollowingformat:

{minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}

Eachbindingwouldcorrespondtothefollowing:

minIntegerDigits:Theminimumnumberofintegerdigitstouse.Itdefaultsto1.minFractionDigits:Theminimumnumberofdigitsafterthefraction.Itdefaultsto0.maxFractionDigits:Themaximumnumberofdigitsafterthefraction.Itdefaultsto3.

Note

Keepinmindthattheacceptablerangeforeachofthesenumbersandotherdetailswilldependonyournativeinternationalizationimplementation.

Thepercentpipe

Thepercentpipeformatsanumberaslocalpercent.Otherthanthis,itinheritsfromtheNumberpipesothatwecanfurtherformattheoutputtoprovideabetterintegeranddecimalsizingandgrouping.Itssyntaxisasfollows:

expression|percent[:digitInfo]

Thecurrencypipe

Formatsanumberasalocalcurrency,providingsupportforselectingthecurrencycodesuchasUSDfortheUSdollarorEURfortheeuroandsettinguphowwewantthecurrencyinfotobedisplayed.Itssyntaxisasfollows:

expression|currency[:currencyCode[:symbolDisplay[:digitInfo]]]

Intheprecedingstatement,currencyCodeisobviouslytheISO4217currencycode,whilesymbolDisplayisaBooleanthatindicateswhethertousethecurrencysymbol(forexample,$)orthecurrencycode(for,exampleUSD)intheoutput.Thedefaultforthisvalueisfalse.Similartothenumberandpercentpipes,wecanformattheoutputtoprovideabetterintegeranddecimalsizingandgroupingthroughthedigitInfovalue:

www.EBooksWorld.ir

Page 148: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

<p>{{11256.569|currency:"GBP":true:'4.1-2'}}</p>

<!--outputis'£11,256.57'-->

www.EBooksWorld.ir

Page 149: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

TheslicepipeThepurposeofthispipeisequivalenttotheroleplayedbyArray.prototype.slice()andString.prototype.slice()whenitcomestosubtractingasubset(slice)ofacollectionlist,array,orstring,respectively.Itssyntaxisprettystraightforwardandfollowsthesameconventionsasthoseoftheaforementionedslice()methods:

expression|slice:start[:end]

Basically,weconfigureastartingindexwherewewillbeginslicingeithertheitemsarrayorthestringonanoptionalendindex,whichwillfallbacktothelastindexontheinputwhenomitted.

Note

Bothstartandendargumentscantakepositiveandnegativevalues,astheJavaScriptslice()methodsdo.RefertotheJavaScriptAPIdocumentationforafullrundownonalltheavailablescenarios.

Lastbutnotleast,pleasenotethatwhenoperatingonacollection,thereturnedlistisalwaysacopy—evenwhenallelementsarebeingreturned.

www.EBooksWorld.ir

Page 150: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

ThedatepipeYoumusthavealreadyguessedthattheDatepipeformatsadatevalueasastringbasedontherequestedformat.Thetimezoneoftheformattedoutputwillbethelocalsystemtimezoneoftheenduser'smachine.Itssyntaxisprettysimple:

expression|date[:format]

Theexpressioninputmustbeadateobjectoranumber(millisecondssincetheUTCepoch).Theformatargumentishighlycustomizableandacceptsawiderangeofvariationsbasedondate-timesymbols.Forourconvenience,somealiaseshavebeenmadeavailableasshortcutstothemostcommondateformats:

'medium':Thisisequivalentto'yMMMdjms'(forexample,Sep3,2010,12:05:08PMforen-US)'short':Thisisequivalentto'yMdjm'(forexample,9/3/2010,12:05PMforen-US)'fullDate':Thisisequivalentto'yMMMMEEEEd'(forexample,Friday,September3,2010foren-US)'longDate':Thisisequivalentto'yMMMMd'(forexample,September3,2010)'mediumDate':Thisisequivalentto'yMMMd'(forexample,Sep3,2010foren-US)'shortDate':Thisisequivalentto'yMd'(forexample,9/3/2010foren-US)'mediumTime':Thisisequivalentto'jms'(forexample,12:05:08PMforen-US)'shortTime':Thisisequivalentto'jm'(forexample,12:05PMforen-US)

www.EBooksWorld.ir

Page 151: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

TheJSONpipeJSONisprobablythemoststraightforwardpipeinitsdefinition;itbasicallytakesanobjectasaninputandoutputsitinJSONformat:

{{{name:'Eve',age:43}|json}}

Hereistheoutput:

{"name":"Eve","age":43}

www.EBooksWorld.ir

Page 152: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

ThereplacepipeThereplacepipeoperatesprettymuchliketheString.prototype.replace()functionoftheJavaScriptAPI,anditwillevaluateastringexpression,oranumberthatwillbetreatedasastringeitherway,againstagivenpattern.Allmatchesfoundwillbethenreplacedbyagivenstringreplacement.Wecanalsointroduceafunction,orareferencetoafunction,thatwillreceivethematchstringfound.Theoverallsyntaxisasfollows:

expression|replace:pattern:replacement

Itisimportanttonotethatthepatterncanbeconfiguredasaregularexpression.Infact,Angular2usesregularexpressionsunderthehoodtofindstringmatchessomakesureyouescapeanyspecialcharacterlikeparentheses,brackets,andsoon.

www.EBooksWorld.ir

Page 153: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Thei18npipesAspartofAngular'sstrongcommitmenttoprovidingastronginternationalizationtoolset,areducedsetofpipestargetingcommoni18nusecaseshavebeenmadeavailable.Thisbookwillonlycoverthetwomajorones,butitisquitelikelythatmorepipeswillbereleasedinthefuture.Pleaserefertotheofficialdocumentationforfurtherinformationafterfinishingthischapter.

Thei18nPluralpipe

Thei18nPluralpipehasasimpleusage,wherewejustevaluateanumericvalueagainstanobjectmappingdifferentstringvaluestobereturneddependingontheresultoftheevaluation.Thisway,wecanrenderdifferentstringsonourtemplatedependingifthenumericvalueiszero,one,two,morethanN,andsoon.Talkingaboutpomodoros,wecouldslidethisinourtemplate:

<h1>{{pomodoros|i18nPlural:pomodorosWarningMapping}}</h1>

Then,wecanhavethismappingasafieldofourcomponentcontrollerclass:

classMyPomodorosComponent{

pomodoros:number;

pomodorosWarningMapping:any={

'=0':'Nopomodorosfortoday',

'=1':'Onepomodoropending',

'other':'#pomodorospending'

}

}

Weevenbindthenumericvalueevaluatedintheexpressionbyintroducingthe'#'placeholderinthestringmappings.Whennomatchingvalueisfound,thepipewillfallbacktothemappingsetwiththekey'other'.

Thei18nSelectpipe

Thei18nSelectpipeissimilartoi18nPluralpipe,butevaluatesastringvalueinstead.Thispipeisperfectforlocalizingtextinterpolationsorprovidingdistinctlabelsdependingonstatechanges,forinstance.Forexample,wecouldrecaponourpomodorotimerandservetheUIindifferentlanguages:

<button(click)="togglePause()">

{{languageCode|i18nSelect:localizedLabelsMap}}

</button>

Inourcontrollerclass,wecanpopulatelocalizedLabelsMapasfollows:

classPomodoroTimerComponent{

languageCode:string='fr';

localizedLabelsMap:any={

'en':'Starttimer',

www.EBooksWorld.ir

Page 154: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

'es':'Comenzartemporizador',

'fr':'Démarreruneséquence',

'other':'Starttimer'

}

...

}

Itisimportanttonotethatwecanputthisconvenientpipetouseinusecasesotherthanlocalisingcomponents,buttoprovidestringbindingsdependingonmapkeysandthelike.Sameasthei18nPluralpipe,whennomatchingvalueisfound,thepipewillfallbacktothemappingsetwiththekey'other'.

www.EBooksWorld.ir

Page 155: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

TheasyncpipeSometimes,wemanageobservabledataoronlydatathatishandledasynchronouslybyourcomponentclass,andweneedtoensurethatourviewspromptlyreflectthechangesintheinformationoncetheobservablefieldchangesorasynchronousloadinghasbeenaccomplishedaftertheviewhasbeenrendered.Theasyncpipesubscribestoanobservableorpromiseandreturnsthelatestvalueithasemitted.Whenanewvalueisemitted,theasyncpipemarksthecomponenttobecheckedforchanges.

www.EBooksWorld.ir

Page 156: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

PuttingitalltogetherinthePomodorotasklistNowthatyouhavelearnedalltheelementsthatallowyoutobuildfull-blowncomponents,it'stimetoputallofthisfreshknowledgeintopractice.Inthenextpageswearegoingtobuildasimpletasklistmanagerforourpomodoroapplication.Init,wewillseeataskstablecontainingtheto-doitemsweneedtoachieve:

Wewillalsoqueueuptasksstraightfromthebacklogoftasksavailable.Thiswillhelpshowingthetimerequiredtoaccomplishallthequeuedtasksandseehowmanypomodorosaredefinedinourworkingagenda.

www.EBooksWorld.ir

Page 157: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

SettingupourmainHTMLcontainerBeforebuildingtheactualcomponentweneedtosetupourworkenvironmentfirstandinordertodosowewillreusethesameHTMLboilerplatefileweusedinthepreviouscomponent.Pleasesetasidetheworkyou'vedonesofarandkeepthepackage.json,tsconfig.json,typings.jsonandindex.htmlfilesweusedinpreviousexamples.Feelfreetoreinstallthemodulesrequiredincaseyouneedto,andreplacethecontentsofthebodytaginourindex.htmltemplate:

<navclass="navbarnavbar-defaultnavbar-static-top">

<divclass="container">

<divclass="navbar-header">

<strongclass="navbar-brand">MyPomodoroTasks</strong>

</div>

</div>

</nav>

<pomodoro-tasks></pomodoro-tasks>

Inanutshell,wehavejustupdatedthetitleoftheheaderlayoutaboveournew<pomodoro-tasks>customelements,whichreplacestheprevious<pomodoro-timer>.YoumightwanttoupdatetheconfigurationoftheSystem.import()commandtopointtoournewcompiledcomponentclass:

System.import('built/pomodoro-tasks')

.then(null,console.error.bind(console));

www.EBooksWorld.ir

Page 158: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

BuildingourtasklisttablewithAngulardirectivesCreateanemptypomodoro-tasks.tsfile.Youmightwanttousethisnewlycreatedfiletobuildournewcomponentfromscratchandembedonitthedefinitionsofalltheaccompanyingpipes,directives,andcomponentswewillseelaterinthischapter.

Note

Real-lifeprojectsareneverimplementedthisway,sinceourcodemustconformtothe"oneclass,onefile"principle,takingadvantageofECMAScriptmodulesforgluingthingstogether.Chapter5,BuildinganApplicationwithAngular2ComponentswillintroduceyoutoacommonsetofgoodpracticesforbuildingAngular2applications,includingstrategiesfororganizingyourdirectorytreeandyourdifferentelements(components,directives,pipes,services,andsoon)inasustainableway.Thischapter,onthecontrary,willleveragepomodoro-tasks.tstoincludeallthecodeinacentrallocationandthenprovideabird'seyeviewofallthetopicswewillcovernowwithouthavingtogoswitchingacrossfiles.Bearinmindthatthisisinfactananti-pattern,butforinstructionalpurposeswewilltakethisapproachinthischapterforthelasttime.Theorderinwhichelementsaredeclaredwithinthefileisimportant.RefertothecoderepositoryinGitHubifexceptionsrise.

Beforemovingonwithourcomponent,weneedtoimportthedependenciesrequired,formalizethedatamodelwewillusetopopulatethetable,andthenscaffoldsomedatathatwillbeservedbyaconvenientserviceclass.

Let'sbeginbyaddingtoourpomodoro-tasks.tsfilethefollowingcodeblock,importingallthetokenswewillrequireinthischapter.PayspecialattentiontothetokensweareimportingfromtheAngular2library.WehavecoveredComponentandInputalready,butalltherestwillbeexplainedlaterinthischapter:

import{

Component,

Input,

Pipe,

PipeTransform,

Directive,

OnInit,

HostListener

}from'@angular/core';

import{bootstrap}from'@angular/platform-browser-dynamic';

Withthedependencytokensalreadyimported,let'sdefinethedatamodelforourtasks,nexttotheblockofimports:

///Modelinterface

interfaceTask{

name:string;

deadline:Date;

queued:boolean;

pomodorosRequired:number;

www.EBooksWorld.ir

Page 159: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

}

TheschemaofaTaskmodelinterfaceisprettyself-explanatory.Eachtaskhasaname,adeadline,afieldinforminghowmanypomodorosneedtobeshipped,andaBooleanfieldnamedqueuedthatdefinesifthattaskhasbeentaggedtobedoneinournextpomodorosession.

Tip

Youmightbesurprisedthatwedefineamodelentitywithaninterfaceratherthanaclass,butthisisperfectlyfinewhentheentitymodeldoesnotfeatureanybusinesslogicrequiringimplementationofmethodsordatatransformationinaconstructororsetter/getterfunction.Whenthelatterisnotrequired,aninterfacejustsufficessinceitprovidesthestatictypingwerequireinasimpleandmorelightweightfashion.

Now,weneedsomedataandaservicewrapperclasstodeliversuchdataintheformofacollectionofTaskobjects.TheTaskServiceclassdefinedherewilldothetrick,soappendittoyourcoderightaftertheTaskinterface:

///LocalDataService

classTaskService{

publictaskStore:Array<Task>=[];

constructor(){

consttasks=[

{

name:"CodeanHTMLTable",

deadline:"Jun232015",

pomodorosRequired:1

},{

name:"Sketchawireframeforthenewhomepage",

deadline:"Jun242016",

pomodorosRequired:2

},{

name:"StyletablewithBootstrapstyles",

deadline:"Jun252016",

pomodorosRequired:1

},{

name:"ReinforceSEOwithcustomsitemap.xml",

deadline:"Jun262016",

pomodorosRequired:3

}

];

this.taskStore=tasks.map(task=>{

return{

name:task.name,

deadline:newDate(task.deadline),

queued:false,

pomodorosRequired:task.pomodorosRequired

};

});

}

www.EBooksWorld.ir

Page 160: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

}

Thisdatastoreisprettyself-explanatory:itexposesataskStorepropertyreturninganarrayofobjectsconformingtotheTaskinterface(hencebenefitingfromstatictyping)withinformationaboutthename,deadline,andtimeestimateinpomodoros.

Nowthatwehaveadatastoreandamodelclass,wecanbeginbuildinganAngularcomponentwhichwillconsumethisdatasourcetorenderthetasksinourtemplateview.Insertthefollowingcomponentimplementationafterthecodeyouwrotebefore:

///Componentclasses

///-MainParentComponent

@Component({

selector:'pomodoro-tasks',

styleUrls:['pomodoro-tasks.css'],

templateUrl:'pomodoro-tasks.html'

})

classTasksComponent{

today:Date;

tasks:Task[];

constructor(){

constTasksService:TasksService=newTasksService();

this.tasks=taskService.taskStore;

this.today=newDate();

}

};

bootstrap(TasksComponent);

Asyoucansee,wehavedefinedandinstantiatedthroughthebootstrapfunctionanewcomponentnamedTasksComponentwiththeselector<pomodoro-tasks>(wealreadyincludeditwhenwewerepopulatingthemainindex.htmlfile,remember?).Thisclassexposestwoproperties:today'sdateandataskscollectionthatwillberenderedinatablecontainedinthecomponent'sview,aswewillseeshortly.Todoso,itinstantiatesinitsconstructorthedatasourcethatwecreatedpreviously,mappingittothearrayofmodelstypedasTaskobjectsrepresentedbythetasksfield.WealsoinitializethetodaypropertywithaninstanceoftheJavaScriptbuilt-inDateobject,whichcontainsthecurrentdate.

Tip

Asyouhaveseen,thecomponentselectordoesnotmatchitscontrollerclassnaming.Wewilldelvedeeperintonamingconventionsattheendofthischapter,asapreparationforChapter5,BuildinganApplicationwithAngular2Components.

Let'screatethestylesheetfilenow,whoseimplementationwillbereallysimpleandstraightforward.Createanewfilenamedpomodoro-tasks.cssatthesamelocationwhereourcomponentfilelives.Youcanthenpopulateitwiththefollowingstylesruleset:

www.EBooksWorld.ir

Page 161: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

h3,p{

text-align:center;

}

table{

margin:auto;

max-width:760px;

}

Thisnewlycreatedstylesheetissosimplethatitmightseemabittoomuchtohaveitasastandalonefile.However,thiscomesasagoodopportunitytoshowcaseinourexamplethefunctionalitiesofthestyleUrlspropertyofthecomponentmetadata.

ThingsarequitedifferentinregardsofourHTMLtemplate.ThistimewewillnothardcodeourHTMLtemplateinthecomponenteither,butwewillpointtoanexternalHTMLfiletobettermanageourpresentationcode.PleasecreateanHTMLfileandsaveitaspomodoro-tasks.htmlinthesamelocationwhereourmaincomponent'scontrollerclassexists.Onceitiscreated,fillitinwiththefollowingHTMLsnippet:

<divclass="containertext-center">

<imgsrc="assets/img/pomodoro.png"alt="Pomodoro"/>

<divclass="container">

<h4>Tasksbacklog</h4>

<tableclass="table">

<thead>

<tr>

<th>TaskID</th>

<th>Taskname</th>

<th>Deliverby</th>

<th>Pomodoros</th>

<th>Actions</th>

</tr>

</thead>

<tbody>

<tr*ngFor="lettaskoftasks;leti=index">

<thscope="row">{{i}}</th>

<td>{{task.name|slice:0:35}}

<span[hidden]="task.name.length<35">...</span>

</td>

<td>{{task.deadline|date:'fullDate'}}

<span*ngIf="task.deadline<today"class="labellabel-danger">

Due

</span>

</td>

<tdclass="text-center">{{task.pomodorosRequired}}</td>

<td>

[Futureoptions...]

</td>

</tr>

</tbody>

</table>

</div>

</div>

WearebasicallycreatingatablethatfeaturesaneatstylingbasedontheBootstrap

www.EBooksWorld.ir

Page 162: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

framework.Then,werenderallourtasksusingthealwaysconvenientNgFordirective,extractinganddisplayingtheindexofeachiteminourcollectionasweexplainedwhileoverviewingtheNgFordirectiveearlierinthischapter.

Pleaselookathowweformattedtheoutputofourtask'snameanddeadlineinterpolationsbymeansofpipes,andhowconvenientlywedisplay(ornot)anellipsistoindicatewhenthetextexceedsthemaximumnumberofcharactersweallocatedforthenamebyturningtheHTMLhiddenpropertyintoapropertyboundtoanAngularexpression.Allthispresentationlogicistoppedwitharedlabel,indicatingwhetherthegiventaskisduewheneveritsenddateispriortothisday.Ifyouexecutetheprecedingcode,thispagewillshowuponthescreen:

Youhaveprobablynoticedthatthoseactionbuttonsdonotexistinourcurrentimplementation.Wewillfixthisinthenextsection,playingaroundwithstateinourcomponents.BackinChapter1,CreatingOurVeryFirstComponentinAngular2,wetouchedupontheclickeventhandlerforstoppingandresumingthepomodorocountdown,andthendelveddeeperintothesubjectinChapter3,ImplementingPropertiesandEventsinOurComponents,wherewecoveredoutputproperties.Let'scontinueonourresearchandseehowwecanhookupDOMeventhandlerswithourcomponent'spublicmethods,addingarichlayerofinteractivitytoourcomponents.

www.EBooksWorld.ir

Page 163: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

TogglingtasksinourtasklistAddthefollowingmethodtoyourTasksComponentcontrollerclass.Itsfunctionalityisprettybasic;wejustliterallytogglethevalueofthequeuedpropertyforagivenTaskobjectinstance:

toggleTask(task:Task):void{

task.queued=!task.queued;

}

Now,wejustneedtohookitupwithourviewbuttons.Updateourviewtoincludeaclickattribute(wrappedinbracessothatitactsasanoutputproperty)inthebuttoncreatedwithintheNgForloop.NowthatwewillhavedifferentstatesinourTaskobjects,let'sreflectthisinthebuttonlabelsbyimplementingaNgSwitchstructurealltogether:

<tableclass="table">

<thead>

<tr>

<th>TaskID</th>

<th>Taskname</th>

<th>Deliverby</th>

<th>Pomodoros</th>

<th>Actions</th>

</tr>

</thead>

<tbody>

<tr*ngFor="#taskoftasks;#i=index">

<thscope="row">{{i}}

<span*ngIf="task.queued"class="labellabel-info">

Queued

</span>

</th>

<td>{{task.name|slice:0:35}}

<span[hidden]="task.name.length<35">...</span>

</td>

<td>{{task.deadline|date:'fullDate'}}

<span*ngIf="task.deadline<today"class="labellabel-danger">

Due

</span>

</td>

<tdclass="text-center">{{task.pomodorosRequired}}</td>

<td>

<button

type="button"

class="btnbtn-defaultbtn-xs"

(click)="toggleTask(task)"

[ngSwitch]="task.queued">

<template[ngSwitchWhen]="false">

<iclass="glyphiconglyphicon-plus-sign"></i>

Add

</template>

<template[ngSwitchWhen]="true">

<iclass="glyphiconglyphicon-minus-sign"></i>

Remove

www.EBooksWorld.ir

Page 164: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

</template>

<templatengSwitchDefault>

<iclass="glyphiconglyphicon-plus-sign"></i>

Add

</template>

</button>

</td>

</tr>

</tbody>

</table>

OurbrandnewbuttoncanexecutethetoggleTaskmethodinourcomponentclass,passingasanargumenttheTaskobjectthatcorrespondstothatiterationofNgFor.Ontheotherhand,theprecedingNgSwitchimplementationallowsustodisplaydifferentbuttonlabelsandiconsdependingonthestateoftheTaskobjectatanygiventime.

Tip

WearedecoratingthenewlycreatedbuttonswithfonticonsfetchedfromtheGlyphiconsfontfamily.TheiconsarepartoftheBootstrapCSSbundleweinstalledpreviouslyandareinnomeansrelatedtoAngular2.Feelfreetoskipitsuseortoreplaceitbyanothericonfontfamily.

Executethecodeasitisnowandcheckouttheresultsyourself.Neat,isn'tit?ButmaybewecangetmorejuicefromAngular2byaddingmorefunctionalitytothetasklist.

www.EBooksWorld.ir

Page 165: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

DisplayingstatechangesinourtemplatesNowthatwecanpickthetaskstobedonefromthetable,itwouldbegreattohavesomekindofvisualhintofhowmanypomodorosessionswearemeanttoachieve.Thelogicisasfollows:

Theuserreviewsthetasksonthetableandpickstheonestobedonebyclickingoneachone.Everytimearowisclicked,theunderlyingTaskobjectstatechangesanditsBooleanqueuedpropertyistoggled.Thestatechangeisreflectedimmediatelyonthesurfacebydisplayinga"queued"labelontherelatedtaskitem.Theusergetspromptinformationoftheamountofpomodorosessionsrequiredandatimeestimationtodeliverthemall.Weseehowarowofpomodoroiconsaredisplayedabovethetable,displayingthesumofpomodorosfromallthetaskssettobedone.

ThisfunctionalitywillhavetoreacttothestatechangesofthesetofTaskobjectswe'redealingwith.ThegoodnewsisthatthankstoAngular2'sveryownchangedetectionsystem,makingcomponentsfullyawareofstatechangesisextremelyeasy.

Thus,ourveryfirsttaskwillbetotweakourTasksComponentclasstoincludesomewaytocomputeanddisplayhowmanytasksarequeuedup.Wewillusethatinformationtorenderornotablockofmarkupinourcomponentwherewewillinformhowmanypomodoroswehavelinedupandhowmuchaggregatedtimeitwilltaketoaccomplishthemall.

ThenewqueuedPomodorosfieldofourclasswillprovidesuchinformation,andwewillwanttoinsertanewmethodnamedupdateQueuedPomodoros()inourclassthatwillupdateitsnumericvalueuponinstantiatingthecomponentorenqueueingtasks.Ontopofthat,wewillcreateakey/valuemappingwecanuselaterontorenderamoreexpressivetitleheaderdependingontheamountofqueuedpomodorosthankstotheI18nPluralpipe:

classTasksComponent{

today:Date;

tasks:Task[];

queuedPomodoros:number;

queueHeaderMapping:any={

'=0':'Nopomodoros',

'=1':'Onepomodoro',

'other':'#pomodoros'

};

constructor(){

constTasksService:TasksService=newTasksService();

this.tasks=taskService.taskStore;

this.today=newDate();

this.updateQueuedPomodoros();

}

www.EBooksWorld.ir

Page 166: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

toggleTask(task:Task):void{

task.queued=!task.queued;

this.updateQueuedPomodoros();

}

privateupdateQueuedPomodoros():void{

this.queuedPomodoros=this.tasks

.filter((task:Task)=>task.queued)

.reduce((pomodoros:number,queuedTask:Task)=>{

returnpomodoros+queuedTask.pomodorosRequired;

},0);

}

};

TheupdateQueuedPomodoros()methodmakesuseofJavaScript'snativeArray.filter()andArray.reduce()methodstobuildalistofqueuedtasksoutoftheoriginaltaskscollectionproperty.Thereducemethodappliedovertheresultingarraygivesusthetotalnumberofpomodorosrequired.Withastatefulcomputationofthenumberofqueuedpomodorosnowavailable,it'stimetoupdateourtemplateaccordingly.Gotopomodoro-tasks.htmlandinjectthefollowingchunkofHTMLrightbeforethe<h4>Tasksbacklog</h4>element.Thecodeisasfollows:

<div>

<h3>

{{queuedPomodoros|i18nPlural:queueHeaderMapping}}fortoday

<spanclass="small"*ngIf="queuedPomodoros>0">(Estimatedtime:{{

queuedPomodoros*25}})</span>

</h3>

</div>

<h4>Tasksbacklog</h4>

<!--restoftemplateremainsthesame-->

Theprecedingblockrendersaninformativeheadertitleatalltimes,evenwhennopomodoroshavebeenqueuedup.Wealsobindthatvalueinthetemplateanduseittoestimatethroughanexpressionbindingtheamountofminutesrequiredtogothrougheachandeverypomodorosessionrequired.

Tip

Wearehardcodingthedurationofeachpomodorointhetemplate.Ideally,suchconstantvalueshouldbeboundfromanapplicationvariableoracentralizedsetting.Don'tworry,wewillseehowwecanimprovethisimplementationinthenextchapters.

Saveyourchangesandreloadthepage,andthentrytotogglesometaskitemsonthetabletoseehowtheinformationchangesinrealtime.Exciting,isn'tit?

www.EBooksWorld.ir

Page 167: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

EmbeddingchildcomponentsNow,let'sstartbuildingatinypomodoroiconcomponentthatwillbenestedinsidetheTasksComponentcomponent.Thisnewcomponentwilldisplayasmallerversionofourbigpomodoroicon,whichwewillusetodisplayonthetemplatetheamountofpomodoroslineduptobedone,aswedescribedearlierinthischapter.Let'spavethewaytowardscomponenttrees,whichwewillanalyzeindetailinChapter5,BuildinganApplicationwithAngular2Components.Fornow,justincludethefollowingcomponentclassbeforetheTasksComponentclassyoubuiltearlier:

OurcomponentwillexposeapublicpropertynamedtaskinwhichwecaninjectaTaskobject.ThecomponentwillusethisTaskobjectbindingtoreplicatetheimagerenderedinthetemplateasmanytimesaspomodorosessionsarerequiredbythistaskinitspomodorosRequiredproperty,allthisbymeansofaNgFordirective.

Inourpomodoro-tasks.tsfile,injectthefollowingblockofcodebeforeourTasksComponent:

@Component({

selector:'pomodoro-task-icons',

template:`<img*ngFor="leticonoficons"

src="/assets/img/pomodoro.png"

width="50">`

})

classTaskIconsComponentimplementsOnInit{

@Input()task:Task;

icons:Object[]=[];

ngOnInit(){

this.icons.length=this.task.pomodorosRequired;

this.icons.fill({name:this.task.name});

}

}

OurnewTaskIconsComponentfeaturesaprettysimpleimplementation,withaveryintuitiveselectormatchingitscamel-casedclassnameandatemplatewhereweduplicatethegiven<img>tagasmanytimesasobjectsarepopulatedintheiconsarraypropertyofthecontrollerclass,whichispopulatedwiththenativefillmethodoftheArrayobjectintheJavaScriptAPI(thefillmethodfillsalltheelementsofanarraywithastaticvaluepassedasanargument),withinngOnInit().Wait,whatisthis?Shouldn'tweimplementthelooppopulatingtheiconsarraymemberintheconstructorinstead?

Thismethodisoneofthelifecyclehookswewilloverviewinthenextchapter,andprobablythemostimportantone.Thereasonwhywepopulatetheiconsarrayfieldhereandnotintheconstructormethodisbecauseweneedeachandeverydata-boundpropertiestobeproperlyinitializedbeforeproceedingtoruntheforloop.Otherwise,itwillbetoosoontoaccesstheinputvaluetasksinceitwillreturnanundefinedvalue.

Tip

www.EBooksWorld.ir

Page 168: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

TheOnInitinterfacedemandsanngOnInit()methodtobeintegratedinthecontrollerclassthatimplementssuch-interface,anditwillbeexecutedonceallinputpropertieswithabindingdefinedhavebeenchecked.Wewilltakeabird'seyeoverviewofcomponentlifecyclehooksinChapter5,BuildinganApplicationwithAngular2Components.

Still,ournewcomponentneedstofinditswaytoitsparentcomponent.So,let'sinsertareferencetothecomponentclassinthedirectivespropertyoftheTasksComponentdecoratorsettings:

@Component({

selector:'pomodoro-tasks',

directives:[TaskIconsComponent],

styleUrls:['pomodoro-tasks.css'],

templateUrl:'pomodoro-tasks.html'

})

Doyourememberthatwementionedthatcomponentsarebasicallydirectiveswithcustomviews?Ifso,thenwewillwanttousethedirectivespropertyofeachcomponenteverytimewewanttonestanothercomponentwithin.Thisexplainsthecaseforusingthedirectivespropertyhere.

Ournextstepwillbetoinjectthe<pomodoro-task-icons>elementintheTasksComponenttemplate.Gobacktopomodoro-tasks.htmlandupdatethecodelocatedinsidetheconditionalblockmeanttobedisplayedwhenqueuedPomodorosisgreaterthanzero.Thecodeisasfollows:

<div>

<h3>

{{queuedPomodoros|i18nPlural:queueHeaderMapping}}fortoday

<spanclass="small"*ngIf="queuedPomodoros>0">(Estimatedtime:{{

queuedPomodoros*25}})</span>

</h3>

<p>

<span*ngFor="letqueuedTaskoftasks">

<pomodoro-task-icons

[task]="queuedTask"

(mouseover)="tooltip.innerText=queuedTask.name"

(mouseout)="tooltip.innerText='Mouseoverfordetails'">

</pomodoro-task-icons>

</span>

</p>

<p#tooltip*ngIf="queuedPomodoros>0">Mouseoverfordetails</p>

</div>

<h4>Tasksbacklog</h4>

<!--restoftemplateremainsthesame-->

Thereisstillsomeroomforimprovementthough.Unfortunately,theiconsizeishardcodedintheTaskIconsComponenttemplateandthatmakesithardertoreusethatcomponentinothercontextswhereadifferentsizemightberequired.Obviously,wecouldrefactortheTaskIconsComponentclasstoexposeasizeinputpropertyandthenbindthevaluereceivedstraightintothecomponenttemplateinordertoresizetheimageaccordingly:

www.EBooksWorld.ir

Page 169: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

@Component({

selector:'pomodoro-task-icons',

template:`<img*ngFor="leticonoficons"

src="/assets/img/pomodoro.png"

width="{{size}}">`

})

classTaskIconsComponentimplementsOnInit{

@Input()task:Task;

icons:Object[]=[];

@Input()size:number;

ngOnInit(){

...

}

}

Then,wejustneedtoupdatetheimplementationofpomodoro-tasks.htmltodeclarethevalueweneedforthesize:

<span*ngFor="letqueuedTaskoftasks">

<pomodoro-task-icons

[task]="queuedTask"

size="50"

(mouseover)="tooltip.innerText=queuedTask.name"

(mouseout)="tooltip.innerText='Mouseoverfordetails'">

</pomodoro-task-icons>

</span>

Pleasenotethatthesizeattributeisnotwrappedbetweenbracketsbecausewearebindingahardcodedvalue.Ifwewantedtobindacomponentvariable,thatattributeshouldbeproperlydeclaredas[size]="{{mySizeVariable}}".

Let'ssummarizewhatwedid:

WeinsertedanewDOMelementthatwillshowuponlywhenwehavepomodorosqueuedup.Wedisplayedanactualheadertellingushowmanypomodoroswearemeanttoachieve,bybindingthequeuedPomodorospropertyinanH3DOMelement,plusatotalestimationinminutesforaccomplishingallofthiscontainedinthe{{queuedPomodoros*25}}expression.TheNgFordirectiveallowsustoiteratethroughthetasksarray.Ineachiteration,werenderanew<pomodoro-task-icons>element.WeboundtheTaskmodelobjectofeachiteration,representedbythequeuedTaskreference,inthetaskinputpropertyofthe<pomodoro-task-icons>inthelooptemplate.Wetookadvantageofthe<pomodoro-task-icons>elementtoincludeadditionalmouseeventhandlersthatpointtothefollowingparagraph,whichhasbeenflaggedwiththe#tooltiplocalreference.So,everytimetheuserhoversthemouseoverthepomodoroicon,thetextbeneaththeiconsrowwilldisplaytherespectivepomodoro'staskname.

Werantheextramile,turningthesizeoftheiconrenderedby<pomodoro-task-icons>intoa

www.EBooksWorld.ir

Page 170: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

configurablepropertyaspartofthecomponentAPI.Wenowhavepomodoroiconsthatgetupdatedinrealtimeaswetoggletheinformationonthetable.Newproblemshavearisen,however.Firstly,wearedisplayingpomodoroiconcomponentsmatchingtherequiredpomodorosofeachtask,withoutfilteringoutthosewhicharenotqueued.Ontheotherhand,theoverallestimationoftimerequiredtoachieveallourqueuedpomodorosdisplaysthegrossnumberofminutes,andthisinformationwillmakenosenseasweaddmoreandmorepomodorostotheworkingplan.

Perhaps,it'stimetoamendthis.It'sagoodthingthatcustompipeshavecometotherescue!

www.EBooksWorld.ir

Page 171: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

BuildingourowncustompipesWehavealreadyseenwhatpipesareandwhattheirpurposeisintheoverallAngularecosystem,butnowwearegoingtodivedeeperintohowwecanbuildourownsetofpipestoprovidecustomtransformationstodatabindings.

www.EBooksWorld.ir

Page 172: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

AnatomyofacustompipePipesareveryeasytodefine.Firstofall,weneedtoimportthePipedecoratorfromtheAngularcorelibraryandcreateanewclassdecoratedwiththisdecorator.ThisnewclasshastobenamedwithourselectorofchoiceinthedecoratorconfigurationandimplementthePipeTransforminterface.

Theclassimplementationisprettysimpleaswell.ItjustconsistsofamandatorymethodrequiredbythePipeTransforminterface,namedtransform,whichwillreturnatypeofourchoice(usuallythetypecorrespondingtotheinputthatwefeedthepipewith)andtwoparameters.Theinputitselfisthefirstparameter,followedbyanoptionalspreadargument(refertoChapter2,IntroducingTypeScripttolookintospreadargumentsinTypeScript)containingthesettingsthatconfigurethepipeinourview:

import{Pipe,PipeTransform}from'@angular/core';

@Pipe({

name:'myPipeName',

pure:false//optional,defaultistrue

})

classMyPipeimplementsPipeTransform{

transform(value:any,...args:any[]):any{

//Weapplytransformationstotheinputvaluehere

returnsomething;

}

}

@Component({

selector:'my-selector',

pipes:[MyPipe],

template:'<p>{{myVariable|myPipeName:"bar"}}</p>'

})

classmyComponent{

myVariable:string='Foo';

}

Tip

Intheprecedingexample,wecreatedanimpurepipenamedMyPipethatwouldapplysometransformationstoitsinputaccordingtotheparametersprovidedwhenapplyingitinthecomponentdefinednext.

Justaswithcustomdirectives,componentsmustexplicitlystateinthepipespropertywhatcustompipesareimplementing.

RegardingtheoptionalpurepropertyinthePipedecorator,wemustclarifythatpipesarestateless.Thismeansthataninstanceofapipewillbereusedandthepipewillbecalledonlywhenitsargumentschange.Inotherwords,whenthepipetransformstheinput,itdisregardstheoriginalinputandfocusesonthecopythatwasjustmade.Iftheoriginalinputchangeslateron,thechangeswillnotbereflectedintheview.Fortunately,wecanenablethestateon

www.EBooksWorld.ir

Page 173: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

pipesthroughthepureBooleanproperty.Whensettofalse,thepipewillkeepthestateofthevaluesittransforms,anditwillparseandtransformtheunderlyingexpressionagainassoonasAngular'schangedetectionsystemchecksthatthesourcedatahaschanged.

Let'sputthisconcepttoworkbycreatingacoupleofcustompipesforourcomponent.

www.EBooksWorld.ir

Page 174: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

AcustompipetobetterformattimeoutputWatchingthegrossnumberofminutessummedupwhenlininguptaskstobedoneisnotveryintuitive,soweneedawaytodeconstructthisvalueintohoursandminutes.OurpipewillhavethenamepomodoroFormattedTimeandwillbeimplementedbytheFormattedTimePipeclass,whoseuniquetransformmethodreceivesanumberrepresentingatotalnumberofminutesandreturnsastring(provingthatpipesdonotneedtoreturnthesametypeastheyreceiveinthepayload)inareadabletimeformat:

@Pipe({

name:'pomodoroFormattedTime'

})

classFormattedTimePipeimplementsPipeTransform{

transform(totalMinutes:number):string{

letminutes:number=totalMinutes%60;

lethours:number=Math.floor(totalMinutes/60);

return`${hours}h:${minutes}m`;

}

}

Tip

WeshouldnotskiptheopportunitytohighlightthatthenamingconventionforPipesis,sameaswesawwithComponents,thenameofthepipeclasswiththePipesuffixplusaselectormatchingthatnamewithoutthesuffix.Thedifferencehereisthatwerepresentthepipeselectorincamelcaseandprefixitwithpomodoroforourexample.Whythismismatchbetweenthepipecontroller'sclassnameandtheselector?Itiscommonpracticetoprefixtheselectorstringsofourcustompipesanddirectiveswithacustomprefixinordertopreventcollisionswithotherselectorsdefinedbythirdpartypipesanddirectives.

Pleaserememberthatcustompipesdonotbecomeavailableinourtemplatesautomatically;theyhavetobeexplicitlydeclaredinthepipespropertyofthedecoratorconfigurationofeachcomponentthatwantstousethem.Inourcase,itistheTasksComponent:

@Component({

selector:'pomodoro-tasks',

directives:[PomodoroIconComponent],

pipes:[FormattedTimePipe],

styleUrls:['pomodoro-tasks.css'],

templateUrl:'pomodoro-tasks.html'

})

classPomodoroTasksComponent{

//Classimplementationremainsthesame

}

Finally,wejustneedtotweaktheHTMLinthepomodoro-tasks.htmltemplatefiletoensurethatourEDTexpressionisproperlyformatted:

<spanclass="small">

(Estimatedtime:{{queuedPomodoros*25|pomodoroFormattedTime}})

www.EBooksWorld.ir

Page 175: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

</span>

Nowreloadthepageandtogglesometasks.Theestimatedtimewillbeproperlyrenderedinhoursandminutes.

www.EBooksWorld.ir

Page 176: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

FilteringoutdatawithcustomfiltersAswenoticedalready,wearedisplayingatthismomentapomodoroiconcomponentforeachandeverytaskinthecollectionservedfromthetasksservice,withoutfilteringoutwhattasksaremarkedasqueuedandwhicharen't.Pipesprovideaconvenientwaytomap,transformanddigestdatabindings,sowecanleverageitsfunctionalitiesforfilteringoutthetasksbindinginourNgForlooptoreturnonlythosetasksthataremarkedasqueued.

Thelogicwillbeprettysimple:sincethetasksbindingisanarrayofTaskobjects,wejustneedtomakeuseoftheArray.filter()methodtofetchonlythoseTaskobjectswhosequeuedpropertyissettotrue.WemightruntheextramileandconfigureourpipetotakeoneBooleanargumentindicatingwhetherwewanttofilteroutqueuedorunqueuedtasks.Theimplementationoftheserequirementsisasfollows,whereyoucanseeagaintheconventionsinplacefortheselectorandclassnames:

@Pipe({

name:'pomodoroQueuedOnly',

pure:false

})

classQueuedOnlyPipeimplementsPipeTransform{

transform(task:Task],...args:any[]):Task[]{

returntasks.filter((task:Task)=>{

returntask.queued===args[0];

});

}

}

Theimplementationisprettystraightforward,sowewillnotgetintodetailaboutithere.However,thereissomethingthatisworthhighlightingatthisstage:thisisanimpurepipe.Bearinmindthatthetasksbindingisacollectionofstatefulobjectsthatwillchangeinlengthandcontentastheusertogglestasksonthetable.Forthatreason,weneedtoinstructthepipetotakeadvantageofAngular'schangedetectionsystemsoitsoutputischeckedbythelatteroneverycycleregardlessofwhetheritsinputhaschangedornot.Configuringthepurepropertyofthepipedecoratorasfalsewilldothetrickthen.

Now,wejustneedtoupdatethepipespropertyofthecomponentusingthispipe:

@Component({

selector:'pomodoro-tasks',

directives:[PomodoroIconComponent],

pipes:[FormattedTimePipe,QueuedOnlyPipe],

styleUrls:['pomodoro-tasks.css'],

templateUrl:'pomodoro-tasks.html'

})

classPomodoroTasksComponent{

//Classimplementationremainsthesame

}

Then,updatetheNgForblockinpomodoro-tasks.htmltoproperlyfilterouttheunqueued

www.EBooksWorld.ir

Page 177: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

tasks:

<span*ngFor="#queuedTaskoftasks|pomodoroQueuedOnly:true">

<pomodoro-task-icons

[task]="queuedTask"

(mouseover)="tooltip.innerText=queuedTask.name"

(mouseout)="tooltip.innerText='Mouseoverfordetails'">

</pomodoro-task-icons>

</span>

PleasecheckhowweconfiguredthepipeaspomodoroQueuedOnly:true.ReplacingtheBooleanparametervaluebyfalsewillgiveusthechancetoenlistthepomodorospertainingtothequeueswehavenotpicked.

Saveallyourworkandreloadthepage,togglingsometasksthen.YouwillseehowouroverallUIreactstothelatestchangesaccordingly,andweonlyenlistthepomodoroiconspertainingtotheamountofpomodorosrequiredofqueuedtasksonly.

www.EBooksWorld.ir

Page 178: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

BuildingourowncustomdirectivesCustomdirectivesencompassavastworldofpossibilitiesandusecases,andwewouldneedanentirebookforshowcasingalltheintricaciesandpossibilitiestheyoffer.

Inanutshell,directivesallowyoutoattachadvancedbehaviorstoelementsintheDOM.Ifadirectivehasatemplateattached,thenitbecomesacomponent.Inotherwords,componentsareAngulardirectiveswithaview,butwecanbuilddirectiveswithnoattachedviewsthatwillbeappliedtoalreadyexistingDOMelements,makingitsHTMLcontentsandstandardbehaviorimmediatelyaccessibletothedirective.ThisappliestoAngularcomponentsaswell,wherethedirectivewilljustaccessitstemplateandcustomattributesandeventswhennecessary.

www.EBooksWorld.ir

Page 179: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

AnatomyofacustomdirectiveDeclaringandimplementingacustomdirectiveisprettyeasy.WejustneedtoimporttheDirectiveclasstoprovidedecoratorfunctionalitiestoitsaccompanyingcontrollerclass:

import{Directive}from'@angular/core';

ThenwedefineacontrollerclassannotatedbytheDirectivedecorator,wherewewilldefinethedirectiveselector,inputandoutputproperties(ifrequired),optionaleventsappliedtothehostelement,andinjectableprovidertokens,shouldourdirective'sconstructorrequirespecifictypestobeinstantiatedbytheAngular2injectorwheninstancingitself(wewillcoverthisindetailinChapter5,BuildinganApplicationwithAngular2Components):

@Directive({

selector:'[selector]',

inputs:['inputPropertyName'],

outputs:['outputPropertyName'],

host:{

'(event1)':'onMethod1($event)',

'(target:event2)':'onMethod2($event)',

'[prop]':'expression',

'attributeName':'attributeValue'

},

providers:[MyCustomType]

})

classmyDirective{

@Input()otherInputPropertyName:any;

@Output()otherOutputPropertyName:any;

constructor(myCustomType:MyCustomType){

//implementation...

}

}

Propertiesanddecorators'suchasselector,@Input(),or@Output()(samewithinputsandoutputs)willprobablyresonatetoyoufromthetimewhenweoverviewedthecomponentdecoratorspec.Althoughwehaven'tmentionedallthepossibilitiesindetailyet,theselectormaybedeclaredasoneofthefollowing:

element-name:Selectbyelementname.class:Selectbyclassname[attribute]:Selectbyattributename[attribute=value]:Selectbyattributenameandvaluenot(sub_selector):Selectonlyiftheelementdoesnotmatchthesub_selectorselector1,selector2:Selectifeitherselector1orselector2matches

Inadditiontothis,wewillfindthehostparameter,whichspecifiestheevents,actions,properties,andattributespertainingtothehostelement(thatis,theelementwhereourdirectivetakesaction)thatwewanttoaccessfromwithinthedirective.Wecanthereforetakeadvantageofthisparametertobindinteractionhandlersagainstthecontainercomponentor

www.EBooksWorld.ir

Page 180: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

anyothertargetelementofourchoice,suchaswindow,document,orbody.Inthisway,wecanrefertotwoveryconvenientlocalvariableswhenwritingadirectiveeventbinding:

$event:Thisisthecurrenteventobjectthattriggeredtheevent.$target:Thisisthesourceoftheevent.ThiswillbeeitheraDOMelementoranAngulardirective.

Besidesevents,wecanupdatespecificDOMpropertiesthatbelongtothehostcomponent.Wejustneedtolinkanyspecificpropertywrappedinbraceswithanexpressionhandledbythedirectiveasakey-valuepairinourdirective'shostdefinition.

Note

Theoptionalhostparametercanalsospecifystaticattributesthatshouldbepropagatedtoahostelement,ifnotpresentalready.ThisisaconvenientwayofinjectingHTMLpropertieswithcomputedvalues.

TheAngularteamhasalsomadeavailableacoupleofconvenientdecoratorssothatwecanmoreexpressivelydeclareourhostbindingsandlistenersstraightonthecode,likethis:

@HostBinding('[class.valid]')

isValid:boolean;//Thehostelementwillfeatureclass="valid"

//isthevalueof'isValid'istrue.

@HostListener('click',['$event'])

onClick(e){

//Thisfunctionwillbeexecutedwhenthehost//componenttriggersa

'click'event.

}

Inthenextchapters,wewillcovertheconfigurationinterfaceofdirectivesandcomponentsinmoredetail,payingspecialattentiontoitslifecyclemanagementandhowwecaneasilyinjectdependenciesintoourdirectives.Fornow,let'sjustbuildasimple,yetpowerful,directivethatwillmakeahugedifferencetohowourUIisdisplayedandmaintained.

www.EBooksWorld.ir

Page 181: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

BuildingatasktooltipcustomdirectiveLet'sputinpracticesomeofthesettingsdescribedaboveinacustomdirective.Sofar,wehavebeendisplayingatooltiptextuponhoveringoverourpomodoroicons.Todoso,weattachedapairofeventbindingstothe<pomodoro-task-icons>element.Whilethisapproachisnotwrong,theoutputisabitverboseandnotreusableatall.Atsomepointwemayevenneedtoapplythesame[task]bindingelsewhereaswellandtakingadvantageofthesametooltiponmouseoverwouldbequiteconvenient.Let'sautomatesuchfunctionalityinadirectivethatwillgetautomaticallyappliedtoanyelementfeaturinga[task]attribute,asour<pomodoro-task-icons>elementsdo.Thisdirectivewilldefineinputpropertiestorefertothatverysamepropertybindingandalsothetargetelementwewilluseasaplaceholder.Ifnotavailable,thedirectivewilljustdonothingandwillnotyieldanyexceptionwhatsoever.Whenavailable,thedirectivewillbindmouseoverandmouseouteventlistenerstothehostelement(<pomodoro-task-icons>inourexample).TheselistenerswilltogglethetextinsidetheDOMelementrepresentedbythelocalreferenceboundtotheplaceholderproperty.Beforedoingso,wewillcachetheoriginalvalueinordertoreuseituponmovingthemouseoutfromtheelement.

Theprecedingdescriptiontakesforminthefollowingdirectivethatyoushouldimplementbeforeourcomponentsinthepomodoro-tasks.tsfile:

@Directive({

selector:'[task]'

})

classTaskTooltipDirective{

privatedefaultTooltipText:string;

@Input()task:Task;

@Input()taskTooltip:any;

@HostListener('mouseover')

onMouseOver(){

if(!this.defaultTooltipText&&this.taskTooltip){

this.defaultTooltipText=this.taskTooltip.innerText;

}

this.taskTooltip.innerText=this.task.name;

}

@HostListener('mouseout')

onMouseOut(){

if(this.taskTooltip){

this.taskTooltip.innerText=this.defaultTooltipText;

}

}

}

Pleasenotetheselectorinuse:[task].Wehavenotconfiguredthemorelogical<pomodoro-task-icons>elementorcreatedanewselectorofourown.Weobviouslycouldhavedonethat,butourgoalinthisexerciseisdifferent.WewanttobindaspecialbehaviortoanyDOMelementandcomponentthatfeaturesa[task]attributewithadatabindingonit.Preciselybecausethisdirectivewilltakeactiononallelementsfeaturingsuchproperty,wecaninclude

www.EBooksWorld.ir

Page 182: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

itasaninputpropertyinthedirectiveimplementationitself.ThenwejustneedtoprovideawaytoconfigurewhatDOMelementwillbecomeourtooltipplaceholderwiththetaskTooltipinputpropertyandweareallset.

Aswesawintheprevioussection,thankstothe@HostListener()decorators,wecanbindalistenerfunctioninourdirectivetoaneventoccurredinthehostcomponent.Thistimeweboundthemouseoverandmouseouteventsotogglethetextofthetargettooltipplaceholder,cachingitscurrenttextbeforehand.

Inordertoseethisdirectiveinaction,weneedtoaddsupportforitfirstatthePomodoroTasksComponentdecorator:

@Component({

selector:'pomodoro-tasks',

directives:[PomodoroIconComponent,TaskTooltipDirective],

pipes:[FormattedTimePipe,pomodoroQueuedOnlyPipe],

styleUrls:['pomodoro-tasks.css'],

templateUrl:'pomodoro-tasks.html'

})

classPomodoroTasksComponent{

//Nomorechangesapply

}

Now,wecanupdateourpomodoro-tasks.htmltemplate:

<p>

<span*ngFor="#queuedTaskoftasks|pomodoroQueuedOnly:true">

<pomodoro-task-icons

[task]="queuedTask"

[taskTooltip]="tooltip"

size="50">

</pomodoro-task-icons>

</span>

</p>

<p#tooltip>Mouseoverfordetails</p>

Oneofthemostexcitingtakeawaysofthiscodeexampleisthelowcodefootprintrequiredforextendingelementswiththisnewfunctionality,anditshugereusability.

Afterallthelatestchanges,reloadyourbrowser,toggleanytask,moveyourmouseoverthenewlyrenderedpomodoroiconand...voilà!

www.EBooksWorld.ir

Page 183: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

AwordaboutnamingconventionsforcustomdirectivesandpipesTalkingaboutreusability,thecommonconventionistoprependacustomprefixtotheselector.Thispreventsconflictswithotherselectorsdefinedbyotherlibrarieswemightbeusinginourproject.SameappliestoPipesaswell,aswehighlightedalreadywhenintroducingourveryfirstcustompipe.

WehaveusedpomodoroasourcustomprefixandwillkeeponusingitthroughoutthebookbutIadvisetouseashorterbutrecognizableprefixinyourcustomdirectivesandpipes'selectors.

Ultimately,itisuptoyouandthenameconventionyouembracebutitisgenerallyagoodideatoestablishanamingconventionthatpreventsthisfromhappening.Acustomprefixisdefinitelytheeasierway.

www.EBooksWorld.ir

Page 184: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

SummaryNowthatwehavereachedthispoint,itisfairtosaythatyouknowalmosteverythingittakestobuildAngular2components,whichareindeedthewheelsandtheengineofallAngular2applications.Intheforthcomingchapters,wewillseehowwecandesignourapplicationarchitecturebetter,andthereforemanagedependencyinjectionthroughoutourcomponentstree,consumedataservices,leveragethenewAngularroutertoshowandhidecomponentswhenrequired,andmanageuserinputandauthentication.

Nevertheless,thischapteristhebackboneofAngular2development,andwehopethatyouenjoyeditasmuchaswedidwhenwritingabouttemplatesyntax,componentAPIsbasedonpropertiesandevents,viewencapsulation,pipes,anddirectives.Now,getreadytoassumenewchallenges—weareabouttomovefromlearninghowtowritecomponentstodiscoveringhowwecanusethemtobuildbiggerapplications,whileenforcinggoodpracticesandrationalarchitectures.Wewillseeallthisinthenextchapter.

www.EBooksWorld.ir

Page 185: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Chapter5.BuildinganApplicationwithAngular2ComponentsWehavereachedapointinourjourneywherewecansuccessfullydevelopmorecomplexapplicationsbynestingcomponentswithinothercomponents,inasortofcomponenttree.However,bundlingallourcomponentlogicinauniquefileisdefinitelynotthewaytogo.Ourapplicationmightbecomeunmaintainableverysoonand,aswewillseelaterinthechapter,wewouldbemissingtheadvantagesthatAngular'sdependencymanagementmechanismcanbringtothegame.

Inthischapter,wewillseehowtobuildapplicationarchitecturesbasedontreesofcomponents,andhowthenewAngular2dependencyinjectionmechanismwillhelpustodeclareandconsumeourdependenciesacrosstheapplicationwithminimumeffortandoptimalresults.

Inthischapter,wewillcoverthesetopics:

BestpracticesfordirectorystructuresandnamingconventionsDifferentapproachestodependencyinjectionInjectingdependenciesintoourcustomtypesOverridingglobaldependenciesthroughoutthecomponenttreeInteractingwiththehostcomponentOverviewingthedirectivelifecycle

www.EBooksWorld.ir

Page 186: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

IntroducingthecomponenttreeModernwebapplicationsbasedonwebcomponentarchitecturesoftenconformtoasortoftreehierarchy,whereinthetopmaincomponent(usuallydroppedsomewhereinthemainHTMLindexfile)actsasaglobalplaceholderwherechildcomponentsturnintohostsforothernestedchildcomponents,andsoonandsoforth.

Thereareobviousadvantagestothisapproach.Ononehand,reusabilitydoesnotgetcompromisedandwecanreusecomponentsthroughoutthecomponenttreewithlittleeffort.Secondly,theresultinggranularityreducestheburdenrequiredforenvisioning,designing,andmaintainingbiggerapplications.WecansimplyfocusonasinglepieceofUIandthenwrapitsfunctionalityaroundnewlayersofabstractionuntilwewrapupafull-blownapplicationfromthegroundup.

Alternatively,wecanapproachourwebapplicationtheotherwayaround,andstartfromamoregenericfunctionalityjusttoendupbreakingdowntheappintosmallerpiecesofUIandfunctionality,whichbecomeourwebcomponents.Thelatterhasbecomethemostcommonapproachwhenbuildingcomponent-basedarchitectures.Wewillsticktoitfortherestofthebook,undertakingarchitecturesastheonedepictedhere:

Applicationbootstrap

└──Applicationcomponent

├──ComponentA

├──ComponentB

│├──ComponentB-I

│└──ComponentB-II

├──ComponentC

└──ComponentD

Forthesakeofclarity,thischapterwilljustborrowthecodewewroteinthepreviouschapters,andwewilldeconstructitintoacomponenthierarchy.Wewillalsoallocatesomeroomintheresultingapplicationforallthesupportingclassesandmodelsrequiredtogiveshapetoourpomodorotool.ThiswillturnintoaperfectopportunitytolearntheintricaciesofthedependencyinjectionmachinerybakedintoAngular2,aswewillseelaterinthischapter.

www.EBooksWorld.ir

Page 187: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

CommonconventionsforscalableapplicationsInallfairness,wehavealreadytackledagoodnumberofthecommonconcernsthatmodernwebdevelopersconfrontwhenbuildingapplications,smallandlargealike,nowadays.Therefore,itmakessensetodefineanarchitecturethatwillseparatetheaforementionedconcernsintoseparatedomainfolders,cateringtomediaassetsandsharedcodeunits.

Atthetimeofwriting,acommonlyagreedpatternfordefiningprojectdirectoriesembracestheideaofstructuringfilesbyfeatures,orcontexts.Sometimes,twocontextsmayrequiresharingthesameentities,andthatisfine(aslongasitdoesnotbecomeacommonthinginourproject,whichwoulddenoteaseriousdesignissue).Thefollowingexample,appliedtoourpreviousworkonpomodorocomponents,depictsthisscheme:

.

├──tasksfeature

│├──Taskmodel

│├──Tasksservice

│├──Tasktablecomponent

│├──

Taskpomodoroscomponent

│└──Tasktooltipdirective

├──timerfeature

│└──Timercomponent

├──admin

│├──Authenticationservice

│├──Logincomponent

│└──Editorcomponent

└──shared

├──componentssharedacrossfeatures

├──pipessharedacrossfeatures

├──directivessharedacrossfeatures

├──globalmodelsandservices

└──sharedmediaassets

Aswecansee,thefirststepistodefinethedifferentfeaturesourapplicationneeds,keepinginmindthateachoneshouldmakesenseonitsowninisolationfromtheothers.Oncewedefinethesetoffeaturesrequired,wewillcreateafolderforeachone.Eachfolderwillbefilledthenwiththecomponents,directives,pipes,models,andservicesthatshapethefeatureitrepresents.Alwaysremembertheprinciplesofencapsulationandreusabilitywhendefiningyourfeaturesset.

Ifthenumberoffilesrequiredforanygivenfeatureexceedsalogicalthreshold,thenitisfinetoorganizethingsabitandsplitourfilesintodifferentfoldersbytype.Let'sfigureoutthatourtasksfeaturehasgrownoutofcontrolandwehaveupto30filesinthefolder,betweencomponents,directives,pipes,services,testspecs,andthelike.Identifyingcodeunitswould

www.EBooksWorld.ir

Page 188: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

becomeaburden,soapplyinganadditionallayerororganizationbytypewoulddefinitelyhelp:

.

├──tasksfeature

│├──components/

││└──componentfiles...

│├──directives/

││├──directiveX

││└──directiveY

│├──pipes/

││└──pipefiles...

│├──models/

││└──models...

│└──services/

│└──services...

├──timerfeature

│└──Timercomponent

├──admin

│├──Authenticationservice

│├──Logincomponent

│└──Editorcomponent

└──shared

├──...

└──Etc

Aswecansee,itisperfectlyfinetohaveinthesameprojectfeaturefolderswithallfilesatthesamelevelandfeaturefolderscontaininganadditionallevelofnestingbytype.Wheretosetthethresholdisuptoyoubutcommonsensesaysthat12or15codeunitsinthesamefeaturefoldermakeagoodcaseforanadditionalnestinglevelbasedontypes.Wecanalsocombinethembyintroducingadditionalnestinglevelsbasedonmultiplefeaturesthatfallundertheumbrellaofanuppercontextaswell,hostingitsimplementationinasub-treebytype:

.

├──tasksfeature

│├──taskscomponentandtemplate

│├──tasks-editorfeature/

││└──task-editorcomponentsandtemplates

│├──tasks-listfeature/

││├──components/

│││└──tasks-listcomponentsandtemplates

││└──pipes/

│││└──tasks-list-specificpipes

│└──task-reportsfeature/

│└──services...

...

www.EBooksWorld.ir

Page 189: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

FileandmodulenamingconventionsEachoneofourfeaturefolderswillhostawiderangeoffilessoweneedaconsistentnamingconventiontopreventfilenamecollisionswhileweensurethatthedifferentcodeunitsareeasytolocate.

Thefollowinglistsummarizesthecurrentconventionsenforcedbythecommunity:

Eachfileshouldcontainasinglecodeunit.Simplyput,eachcomponent,directive,service,pipe,andsoonshouldliveinitsownfile.Thisway,wecontributetoabetterorganizationofcode.Filesanddirectoriesarenamedinlower-kebab-case.Filesrepresentingcomponents,directives,pipes,andservicesshouldappendatypesuffixtotheirname:video-player.tswillbecomevideo-player.component.ts.Anycomponent'sexternalHTMLtemplateorCSSstylesheetfilenamewillmatchthecomponentfilename,includingthesuffix.Ourvideo-player.component.tsmightbeaccompaniedbyvideo-player.component.cssandvideo-player.component.html.DirectiveselectorsandpipenamesarecamelCased,whilecomponentselectorsarelower-kebab-cased.Plus,itisstronglyadvisedtoaddacustomprefixofourchoicetopreventnamecollisionswithothercomponentlibraries.Forexample,followingupourvideoplayercomponent,itmayberepresentedas<vp-video-player>,wherevp-(whichstandsforvideo-player)isourcustomprefix.ModulesarenamedbyfollowingtheruleoftakingaPascalCasedself-descriptivename,plusthetypeitrepresents.Forexample,ifweseeamodulenamedVideoPlayerComponent,wecaneasilytellitisacomponent.Thecustomprefixinuseforselectors(vp-inourexample)shouldnotbepartofthemodulename.

Modelsandinterfacesrequirespecialattentionthough.Dependingonyourapplicationarchitecture,modeltypeswillfeaturemoreorlessrelevance.ArchitecturessuchasMVC,MVVM,Flux,orReduxtacklemodelsfromdifferentstandpointsandgradesofimportance.Ultimately,itwillbeuptoyouandyourarchitecturaldesignpatternofchoicetoapproachmodelsandtheirnamingconventioninonewayoranother.Thisbookwillnotbeopinionatedinthatsense,althoughwedoenforceinterfacemodelsinourexampleapplicationandwillcreatemodulesforthem.

www.EBooksWorld.ir

Page 190: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

EnsuringseamlessscalabilitywithfacadesorbarrelsEachcomponentandsharedcontextofbusinesslogicinourapplicationisintendedtointegratewiththeotherpiecesofthepuzzleinasimpleandstraightforwardway.Clientsofeachsubdomainarenotconcernedabouttheinternalstructureofthesubdomainitself.Ifourtimerfeature,forexample,evolvestothepointofhavingtwodozencomponentsanddirectivesthatneedtobereorganizedintodifferentfolderlevels,externalconsumersofitsfunctionalitiesshouldremainunaffected.

Thiscanbedoneusingfacademodulesthatconcealtheinternalstructureofthecodebyexposingalwaysthesamelayerofendpoints,hidingtheimplementationdetailsoutsidetheboundariesofthefeature.

Inourpreviousexample,thetasksfeatureevolvesfrombeingahandfuloffilesinsidethesamefolderintoatype-drivensetoffolders.Whatifweareusingseveralofitscomponentselsewhereinourapplication?Noworries!Afacademodulelikethiswoulddefinitelyhelp:

app/tasks/tasks.ts

importTaskComponentfrom'./task.component';

importTaskDetailsComponentfrom'./task-details.component';

export{

TaskComponent,

TaskDetailsComponent

}

Then,wecanimportanyofthesecomponentsfromanyotherdistantcornerofourapplication,likethis:

app/example/example.ts

import{TaskDetailsComponent}from'../tasks/tasks'

Whatifthetasksfeaturekeepsgrowingandneedstoberefactoredintodifferentfolders?Wewouldjustupdatethepathreferencesinourapp/tasks/tasks.tsfacademodule,andourclientcodeatapp/example/example.tswouldremainthesame.

IntheAngularlingo,thiskindofdesignpatternalsoreceivesthenameofbarrel.InAngular'sownwords:

AbarrelisanAngularlibrarymoduleconsistingofalogicalgroupingofsingle-purposemodulessuchasComponentandDirective.

Takethatintoaccountinordertoavoidconfusionwhenbumpingintothisterminthefuture.Barrelsarealsousuallygroupedanddistributedinlargerpackagesnamedbundles.Anexampleofthisistheangular2/bundles/router.jsbundle,forinstance.Wewillnotcreate

www.EBooksWorld.ir

Page 191: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

packagedbundlesinthisbook,butwewillthoroughlyusetheonesthatcomewithAngular2whenimplementingHTTPconnection,routing,oranimationfunctionalities.

www.EBooksWorld.ir

Page 192: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

HowdependencyinjectionworksinAngular2Asourapplicationsgrowsandevolves,eachoneofourcodeentitieswillinternallyrequireinstancesofotherobjects,whicharebetterknownasdependenciesintheworldofsoftwareengineering.Theactionofpassingsuchdependenciestothedependentclientisknownasinjection,anditalsoentailstheparticipationofanothercodeentity,namedtheinjector.Theinjectorwilltakeresponsibilityforinstantiatingandbootstrappingtherequireddependenciessotheyarereadyforusefromtheverymomenttheyaresuccessfullyinjectedintheclient.Thisisveryimportantsincetheclientknowsnothingabouthowtoinstantiateitsowndependenciesandisonlyawareoftheinterfacetheyimplementinordertousethem.

Angular2featuresatop-notchdependencyinjectionmechanismtoeasethetaskofexposingrequireddependenciestoanyentitythatmightexistinanAngular2application,regardlessofwhetheritisacomponent,adirective,apipe,oranyothercustomserviceorproviderobject.Infact,aswewillseelaterinthischapter,anyentitycantakeadvantageofdependencyinjection(usuallyreferredtoasDI)inanAngular2application.Beforedelvingdeeperintothesubject,let'slookattheproblemthatAngular'sDIistryingtoaddress.

Let'sfigureoutwehaveamusicplayercomponentthatreliesonaplaylistobjecttobroadcastmusictoitsusers:

import{Component}from'@angular/core';

import{Playlist}from'./playlist';

@Component({

selector:'music-player',

templateUrl:'./music-player.component.html'

})

classMusicPlayerComponent{

playlist:Playlist;

constructor(){

this.playlist=newPlaylist();

}

}

ThePlaylisttypecouldbeagenericclassthatreturnsinitsAPIarandomlistofsongsorwhatever.Thatisnotrelevantnow,sincetheonlythingthatmattersisthatourMusicPlayerComponententitydoesneedittodeliveritsfunctionality.Unfortunately,theimplementationabovemeansthatbothtypesaretightlycoupled,sincethecomponentinstantiatestheplaylistwithinitsownconstructor.Thispreventsusfromaltering,overriding,ormockingupinaneatwaythePlaylistclassifrequired.ItalsoentailsthatanewPlaylistobjectiscreatedeverytimeweinstantiateaMusicPlayerComponent.Thismightbenotdesiredincertainscenarios,especiallyifweexpectasingletontobeusedacrosstheapplicationandthuskeeptrackoftheplaylist'sstate.

Dependencyinjectionsystemstrytosolvetheseissuesbyproposingseveralpatterns,andthe

www.EBooksWorld.ir

Page 193: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

constructorinjectionpatternistheoneenforcedbyAngular2.Thepreviouspieceofcodecouldberethoughtlikethis:

@Component({

selector:'music-player',

templateUrl:'./music-player.component.html'

})

classMusicPlayerComponent{

playlist:Playlist;

constructor(playlist:Playlist){

this.playlist=playlist;

}

}

Now,thePlaylistisinstantiatedoutsideourcomponent.Ontheotherhand,theMusicPlayerComponentexpectssuchanobjecttobealreadyavailablebeforethecomponentisinstantiatedsoitcanbeinjectedthroughitsconstructor.Thisapproachgivesustheopportunitytooverrideitormockitupifwewish.

Basically,thisishowdependencyinjection,andmorespecificallytheconstructorinjectionpattern,works.However,whathasthistodowithAngular2?DoesAngular'sdependencyinjectionmachineryworkbyinstantiatingtypesbyhandandinjectingthemthroughtheconstructor?Obviouslynot,mostlybecausewedonotinstantiatecomponentsbyhandeither(exceptwhenwritingunittests).Angularfeaturesitsowndependencyinjectionframework,whichcanbeusedasastandaloneframeworkbyotherapplications,bytheway.

Theframeworkoffersanactualinjectorthatcanintrospectthetokensusedtoannotatetheparametersintheconstructorandreturnasingletoninstanceofthetyperepresentedbyeachdependency,sowecanuseitstraightawayintheimplementationofourclass,asinthepreviousexample.Theinjectorignoreshowtocreateaninstanceofeachdependency,soitreliesonthelistofprovidersregistereduponbootstrappingtheapplication.Eachoneofthoseprovidersactuallyprovidesmappingsoverthetypesmarkedasapplicationdependencies.Wheneveranentity(let'ssayacomponent,adirective,oraservice)definesatokeninitsconstructor,theinjectorsearchesforatypematchingthattokeninthepoolofregisteredprovidersforthatcomponent.Ifnomatchisfound,itwillthendelegatethesearchontheparentcomponent'sprovider,andwillkeepconductingtheprovider'slookupupwardsuntilaproviderresolveswithamatchingtypeorthetopcomponentisreached.Shouldtheproviderlookupfinishwithnomatch,Angular2willthrowanexception.

Note

Thelatterisnotexactlytrue,sincewecanmarkdependenciesintheconstructorwiththe@Optionalparameterdecorator,inwhichcaseAngular2willnotthrowanyexceptionandthedependencyparameterwillbeinjectedasnullifnoproviderisfound.Thetopmostcomponentisnotthelastdead-endoftheproviderlookup,sincewecanalsodeclareglobaldependenciesinthebootstrap()function,aswewillseelaterinthischapter.

www.EBooksWorld.ir

Page 194: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Wheneveraproviderresolveswithatypematchingthattoken,itwillreturnsuchtypeasasingleton,whichwillbethereforeinjectedbytheinjectorasadependency.Infairness,theproviderisnotjustacollectionofkey/valuepairscouplingtokenswithpreviouslyregisteredtypes,butafactorythatinstantiatesthesetypesandalsoinstantiateseachdependency'sveryowndependenciesaswell,inasortofrecursivedependencyinstantiation.

So,insteadofinstantiatingthePlaylistobjectmanually,wecoulddothis:

import{Component}from'@angular/core';

import{Playlist}from'./playlist';

@Component({

selector:'music-player',

templateUrl:'./music-player.component.html',

providers:[Playlist]

})

classMusicPlayerComponent{

constructor(publicplaylist:Playlist){}

}

Theproviderspropertyofthe@Componentdecoratoristheplacewherewecanregisterdependenciesonacomponentlevel.Fromthatmomentonwards,thesetypeswillbeimmediatelyavailableforinjectionattheconstructorofthatcomponentand,aswewillseenext,atitsownchildcomponentsaswell.

www.EBooksWorld.ir

Page 195: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

InjectingdependenciesacrossthecomponenttreeWehaveseenthattheproviderlookupisperformedupwardsuntilamatchisfound.Amorevisualexamplemighthelp,solet'sfigureoutthatwehaveamusicappcomponentthathostsinitsdirectivesproperty(andhenceitstemplate)amusiclibrarycomponentwithacollectionofallourdownloadedtuneswhichalsohosts,initsowndirectivespropertyandtemplate,amusicplayercomponentsowecanplaybackanyofthetunesinourlibrary.

.

├──MusicAppComponent()

│└──MusicLibraryComponent()

│└──MusicPlayerComponent()

...

OurmusicplayercomponentrequiresaninstanceofthePlaylistobjectwementionedbefore,sowedeclareitasaconstructorparameter,convenientlyannotatedwiththePlaylisttoken.

.

├──MusicAppComponent()

│└──MusicLibraryComponent()

│└──MusicPlayerComponent(playlist:Playlist)

...

WhentheMusicPlayerComponententityisinstantiated,theAngularDImechanismwillgothroughtheparametersinthecomponentconstructorwithspecialattentiontotheirtypeannotations.Then,itwillcheckifthattypehasbeenregisteredinthecomponent'sproviderpropertyofthecomponentdecoratorconfiguration.Thecodeisasfollows:

@Component({

selector:'music-player',

providers:[Playlist]

})

classMusicPlayerComponent{

constructor(publicplaylist:Playlist){}

}

But,whatifwewanttoreusethePlaylisttypeinothercomponentsthroughoutthesamecomponenttree?MaybethePlaylisttypecontainsfunctionalitiesinitsAPIthatarerequiredbydifferentcomponentsatonceacrosstheapplication.Dowehavetodeclarethetokenintheprovider'spropertyforeachone?Fortunatelynot,sinceAngular2anticipatesthatnecessityandbringstransversaldependencyinjectionthroughthecomponenttree.

Note

Intheprevioussection,wementionedthatcomponentsconductaproviderlookupupwards.Thisisbecauseeachcomponenthasitsownbuilt-ininjector,whichisspecifictoit.Nevertheless,thatinjectorisinrealityachildinstanceoftheparent'scomponentinjector(andsoonsoforth),soitisfairtosaythatanAngular2applicationhasnotasingleinjector,but

www.EBooksWorld.ir

Page 196: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

manyinstancesofthesameinjector,sotosay.

WeneedtoextendtheinjectionofthePlaylistobjecttoothercomponentsinthecomponenttreeinaquickandreusablefashion.Knowingbeforehandthatcomponentsperformaproviderlookupstartingfromitselfandthenpassinguptherequesttoitsparentcomponent'sinjectors,wecanthenaddresstheissuebyregisteringtheproviderintheparentcomponent,oreventhetopparentcomponent,sothedependencywillbeavailableforinjectionforeachandeverychildcomponentfoundunderneathit.Inthissense,wecouldregisterthePlaylistobjectstraightatMusicAppComponent,regardlessitmightnotneeditforitsownimplementation:

@Component({

selector:'music-app',

providers:[Playlist],

directives:[MusicLibraryComponent],

template:'<music-library></music-library>'

})

classMusicAppComponent{}

Theimmediatechildcomponentmightnotrequirethedependencyforitsownimplementationeither.SinceithasbeenalreadyregisteredinitsparentMusicAppComponentcomponent,thereisnoneedtoregisteritthereagain.

@Component({

selector:'music-library',

directives:[MusicPlayerComponent],

template:'<music-player></music-player>'

})

classMusicLibraryComponent{}

Wefinallyreachourmusicplayercomponent,butnowitnolongerfeaturesthePlaylisttypeasaregisteredtokeninitsprovidersproperty.Infact,ourcomponentdoesnotfeatureaproviderspropertyatall.Itnolongerrequiresthis,sincethetypehasbeenalreadyregisteredsomewhereabovethecomponent'shierarchy,beingimmediatelyavailableforallchildcomponents,nomatterwheretheyare.

@Component({

selector:'music-player'

})

classMusicPlayerComponent{

constructor(publicplaylist:Playlist){}

}

Now,weseehowdependenciesareinjecteddownthecomponenthierarchyandhowtheproviderlookupisperformedbycomponentsjustbycheckingtheirownregisteredprovidersandbubblinguptherequestupwardsinthecomponenttree.However,whatifwewanttoconstrainsuchinjectionorlookupactions?

Restrictingdependencyinjectiondownthecomponenttree

www.EBooksWorld.ir

Page 197: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Inourpreviousexample,wesawhowthemusicappcomponentregisteredthePlaylisttokeninitsproviderscollection,makingitimmediatelyavailableforallchildcomponents.Sometimes,wemightneedtoconstraintheinjectionofdependenciestoreachonlythosedirectives(andcomponents)thatareimmediatelynexttoaspecificcomponentinthehierarchy.WecandothatbyregisteringthetypetokenintheviewProviderspropertyofthecomponentdecorator,insteadofusingtheproviderspropertywe'veseenalready.Inourpreviousexample,wecanrestrainthedownwardsinjectionofPlaylistonelevelonly:

@Component({

selector:'music-app',

viewProviders:[Playlist],

directives:[MusicLibraryComponent],

template:'<music-library></music-library>'

})

classMusicAppComponent{}

WeareinformingAngular2thatthePlaylistprovidershouldonlybeaccessiblebytheinjectorsofthedirectivesandcomponentslocatedintheMusicAppComponentview,butnotforthechildrenofsuchcomponents.Theuseofthistechniqueisexclusiveofcomponents,sinceonlytheyfeatureviews.

Restrictingproviderlookup

Justlikewecanrestrictdependencyinjection,wecanconstraindependencylookuptotheimmediateupperlevelonly.Todoso,wejustneedtoapplythe@Host()decoratortothosedependencyparameterswhoseproviderlookupwewanttorestrict:

Import{Component,Host}from'@angular/core';

@Component({

selector:'music-player'

})

classMusicPlayerComponent{

constructor(@Host()playlist:Playlist){}

}

Accordingtotheprecedingexample,theMusicPlayerComponentinjectorwilllookupforaPlaylisttypeatitsparentcomponent'sproviderscollection(MusicLibraryComponentinourexample)andwillstopthere,throwinganexceptionbecausePlaylisthasnotbeenreturnedbytheparent'sinjector(unlesswealsodecorateitwiththe@Optional()parameterdecorator).

www.EBooksWorld.ir

Page 198: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

OverridingprovidersintheinjectorhierarchyWe'veseensofarhowAngular'sDIframeworkusesthedependencytokentointrospectthetyperequiredandreturnitrightfromanyoftheprovidersetsavailablealongthecomponenthierarchy.However,wemightneedtooverridetheclassinstancecorrespondingtothattokenincertaincaseswhereamorespecializedtypeisrequiredtodothejob.Angularprovidesspecialtoolstooverridetheprovidersorevenimplementfactoriesthatwillreturnaclassinstanceforagiventoken,notnecessarilymatchingtheoriginaltype.

Wewillnotcoveralltheusecasesindetailhere,butlet'slookatasimpleexample.Inourexample,weassumedthatthePlaylistobjectwasmeanttobeavailableacrossthecomponenttreeforuseindifferententitiesoftheapplication.WhatifourMusicAppComponentdirectivehostsanothercomponentwhosechilddirectivesrequireamorespecializedversionofthePlaylistobject?Let'srethinkourexample:

.

├──MusicAppComponent()

│├──MusicChartsComponent()

││└──MusicPlayerComponent()

│└──MusicLibraryComponent()

│└──MusicPlayerComponent()

...

Thisisabitcontrivedexamplebutwilldefinitelyhelpustounderstandthepointofoverridingdependencies.ThePlaylistinstanceobjectisavailablerightfromthetopcomponentdownwards.TheMusicChartsComponentdirectiveisaspecializedcomponentthatcatersonlyformusicfeaturedinthetopseller'schartsandhenceitsplayermustplaybackbighitsonly,regardlessofthefactitusesthesamecomponentasMusicLibraryComponent.Weneedtoensurethateachplayercomponentgetstheproperplaylistobject,andthiscanbedoneattheMusicChartsComponentlevelbyoverridingtheobjectinstancecorrespondingtothePlaylisttoken.Thefollowingexampledepictsthisscenario,leveragingtheuseoftheprovidefunction:

import{Component,provide}from'@angular/core';

import{Playlist}from'./playlist';

import{TopHitsPlaylist}from'./top-hits-playlist';

@Component({

selector:'music-charts',

directives:[MusicPlayerComponent],

template:'<music-player></music-player>',

providers:[provide(Playlist,{useClass:TopHitsPlaylist})]

})

classMusicChartsComponent{}

Theprovide()functioncreatesaprovidermappedtothetokenspecifiedinthefirstargument(Playlistinthisexample)andtheimplementationconfiguredinthesecondargument,whichinthiscasepointstousingtheTopHitsPlaylisttypeasthereferenceclass.

www.EBooksWorld.ir

Page 199: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

WecouldrefactortheblockofcodetouseviewProvidersinstead,soweensurethat(ifrequired)thechildentitiesstillreceiveaninstanceofPlaylistinsteadofTopHitsPlaylist.Alternatively,wecangotheextramileanduseafactory,toreturnthespecificobjectinstanceweneeddependingonotherrequirements.ThefollowingexamplewillreturnadifferentobjectinstanceforthePlaylisttokendependingontheevaluationofaBooleanconditionvariable:

@Component({

selector:'music-charts',

directives:[MusicPlayerComponent],

template:'<music-player></music-player>',

providers:[

provide(Playlist,{useFactory:()=>{

if(condition){

returnnewTopHitsPlaylist();

}else{

returnnewPlaylist();

}

}

})

]

})

classMusicChartsComponent{}

Movingoutfromtheprecedingpseudo-codeexample,howcanweprovideabetterlogicflowwhenusingtheuseFactoryfunction?ItturnsoutthatthemethodsignaturecantakeargumentsthatoperateprettymuchthesameasdependenciesdowhenintheconstructorofanygivenAngularentity.WejustneedtopointAngulartothetypeeachargumenttokenoftheuseFactorylambdafunctionhasbydeclaringtheminthedepspropertyasfollows:

@Component({

selector:'music-charts',

directives:[MusicPlayerComponent],

template:'<music-player></music-player>',

providers:[

ConditionalService,

provide(Playlist,{useFactory:(conditionalService)=>{

if(conditionalService.isTopHits){

returnnewTopHitsPlaylist();

}else{

returnnewPlaylist();

}

},

deps:[ConditionalService]

})

]

})

classMusicChartsComponent{}

Intheprecedingexample,weareinjectinganobjectinstanceofanimaginaryConditionalServiceclass,whichexposesaBooleanpropertynamedisTopHitsthatwillinformabouttheplaylisttobeused.Keepinmindthatthesetypeswillhavetoberegisteredas

www.EBooksWorld.ir

Page 200: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

well,eitherintheproviderspropertyofthecurrentcomponentoratanyofitsparentcomponents.

www.EBooksWorld.ir

Page 201: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

ExtendinginjectorsupporttocustomentitiesDirectivesandcomponentsrequiredependenciestobeintrospected,resolved,andinjected.Otherentitiessuchasserviceclassesoftenrequirequitesuchfunctionalitytoo.Inourexample,ourPlaylistclassmightrelyonadependencyonaHTTPclienttocommunicatewithathirdpartytofetchthesongs.Theactionofinjectingsuchdependencyshouldbeaseasyasdeclaringtheannotateddependenciesintheclassconstructorandhaveaninjectorreadytofetchtheobjectinstancebyinspectingtheclassprovideroranyotherprovideravailablesomewhere.

Itisonlywhenwethinkhardaboutthelatterthatwerealizethereisagapinthisidea:customclassesandservicesdonotbelongtothecomponenttree.Hence,theydonotbenefitfromanythingsuchasabuilt-ininjectororaparentinjector.Wecannotevendeclareaprovidersproperty,sincewedonotdecoratethesetypesofclasswitha@[email protected]'stakealookatanexample:

classPlaylist{

songs:string[];

constructor(songsService:SongsService){

this.songs=songsService.fetch();

}

}

WemighttrytheaboveinthehopeofhavingAngular2'sDImechanismintrospectingthesongsServiceparameterofthePlaylistclassconstructorwheninstantiatingthisclassinordertoinjectitintoMusicPlayerComponent.Unfortunately,theonlythingwewilleventuallygetisanexceptionlikethis:

CannotresolveallparametersforPlaylist(?).Makesuretheyallhavevalid

typeorannotations.

Thisiskindofmisleading,sinceallconstructorparametersinPlaylisthavebeenproperlyannotated,right?Aswesaidbefore,theAngularDImachineryresolvesdependenciesbyintrospectingthetypesoftheconstructorparameters.Todoso,itneedssomemetadatatobecreatedbeforehand.EachandeveryAngularentityclassdecoratedwithadecoratorfeaturesthismetadataasaby-productofthewayTypeScriptcompilesthedecoratorconfigurationdetails.However,dependenciesthatalsorequireotherdependencieshavenodecoratorwhatsoeverandnometadataisthencreatedforthem.Thiscanbeeasilyfixedthankstothe@Injectable()decorator,whichwillgivevisibilitytotheseserviceclassesfortheDImechanism:

import{Injectable}from'@angular/core';

@Injectable()

classPlaylist{

songs:string[];

constructor(songsService:SongsService){

this.songs=songsService.fetch();

}

www.EBooksWorld.ir

Page 202: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

}

Youwillgetusedtointroducingthatdecoratorinyourserviceclasses,sincetheywillquiteoftenrelyonotherdependenciesnotrelatedtothecomponenttreeinordertodeliverthefunctionality.

Tip

Itisactuallyagoodpracticetodecorateallyourserviceclasseswiththe@Injectable()decorator,irrespectiveofwhetheritsconstructorfunctionshavedependenciesornot.Thisway,wepreventerrorsandexceptionsbecauseofskippingthisrequirementoncetheserviceclassgrowsandrequiresmoredependenciesinthefuture.

www.EBooksWorld.ir

Page 203: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Initializingapplicationswithbootstrap()Aswehaveseeninthischapter,thedependencylookupbubblesupuntilthefirstcomponentatthetop.Thisisnotexactlytrue,sincethereisanadditionalstepthattheDImechanismwillcheckon:thebootstrap()function.

Asfarasweknow,weusethebootstrap()functiontokickstartourapplicationbydeclaringinitsfirstargumenttherootcomponentthatinitiatestheapplication'scomponenttree.However,thebootstrapfunctiontakesasecondargumentintheformofaprovidersarray,wherewecanexplicitdependenciesaswell,thatwillbecomeavailablethroughouttheinjectortree.However,thisisabadpracticebecauseitcouplestheavailabilityofanyprovidertotheapplicationitself,constrainingtheencapsulationandreusabilityofourcomponents,moreoverwhenthebootstrapinitializationfunctionisplatform-specific.

Whereshallwedeclareourapplicationdependenciesthen?Alwaysuseourtoprootcomponentinstead.TheprovidersargumentofthebootstrapfunctionshouldonlybeusedwhenweneedtooverrideexistingAngular2providersonanapplicationlevel,leveragingtheprovide()function,forinstance.

Alwayskeepinmindthatwecanhavemultiplerootcomponents,eachoneofthemtheresultofmultipleexecutionsofthebootstrap()functiondeclaringadifferentrootcomponenteachtime.Eachoneoftheserootcomponentswillfeatureitsownsetofinjectorsandservicesingletons,withnorelationshipwhatsoeveramongstthem.

Switchingbetweendevelopmentandproductionmodes

Angular2applicationsarebootstrappedandinitializedbydefaultindevelopmentmode.Inthedevelopmentmode,theAngular2runtimewillthrowwarningmessagesandassertionstothebrowserconsole.Whilethisisquiteusefulfordebuggingourapplication,wedonotwantthosemessagestobedisplayedwhentheapplicationisinproduction.Thegoodnewsisthatthedevelopmentmodecanbedisabledinfavorofthemoresilentproductionmode.Thisactionisusuallyperformedbeforebootstrappingourapplication:

import{bootstrap}from'@angular/platform-browser-dynamic';

import{enableProdMode}from'angular/core';

importAppComponentfrom'./app.component';

enableProdMode();

bootstrap(AppComponent,[]);

EnablingAngular2'sbuilt-inchangedetectionprofiler

WecanalsoaccessadvancedtoolsfromthebrowserconsolebyenablingtheAngularDebugTools.Todoso,justimporttheenableDebugToolsfunction,whichisspecificintothebrowserplatform,andexecuteitassoonasyougetholdofaninstanceofthecomponentyouwanttoprofile.Thecodeisasfollows:

www.EBooksWorld.ir

Page 204: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

import{bootstrap}from'@angular/platform-browser-dynamic';

import{enableDebugTools}from'@angular/platform-browser';

import{ComponentRef}from'angular/core';

importAppComponentfrom'./app.component';

//Thebootstrap()functionreturnsapromisewith

//areferencetothebootstrappedcomponent

bootstrap(AppComponent,[]).then((ref:ComponentRef)=>{

enableDebugTools(ref);

});

WhentheenableDebugTools()functionistriggered,youjustneedtofollowthefollowingstepstoaccessthechangedetectionprofiler:

1. Openthebrowserdevtoolsandswitchtotheconsoleview.2. Typeng.profiler.timeChangeDetection({record:true})andthenpressEnter.3. TheAngular2runtimewillexercisechangedetectioninaloopandwillprintthe

averageamountoftimeasingleroundofchangedetectiontakesforthecurrentstateoftheUI.ACPUprofilerecordingwillbeconductedwhilethechangedetectorisexercised.

Hopefully,thedebugtoolswillbefleshedoutwithmorefunctionalitiesinthefuture.Staytunedandrefertotheofficialdocumentation.

www.EBooksWorld.ir

Page 205: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

IntroducingthePomodoroAppdirectorystructureInthepreviouschaptersandsectionsinthischapter,wehaveseendifferentapproachesandgoodpracticesforlayingoutAngular2applications.Theseguidelinesencompassedfromnamingconventionstopointersabouthowtoorganizefilesandfolders.Fromthispointonwards,wearegoingtoputallthisknowledgetopracticebyrefactoringallthedifferentinterfaces,components,directives,pipes,andservicesinanactualAngular2architecture,conformingtothemostcommonlyagreedcommunityconventions.

Bytheendofthischapter,wewillhaveafinalapplicationlayoutthatwrapseverythingwehaveseensofarinthefollowingsitearchitecture:

.

├──app/

│├──shared/

││├──assets/←GlobalCSSorimagefilesarestoredhere

││├──directives/

││├──interfaces/

││├──pipes/

││├──services/

││└──shared.ts←facadeforthe'shared'context

│├──tasks/

││├──(tasks-relatedcomponentsanddirectives)

││└──tasks.ts←facadeforthe'tasks'context

│├──timer/

││├──(timer-relatedcomponentsanddirectives)

││└──timer.ts←facadeforthe'timer'context

││

│├──app.component.ts←toprootapplicationcomponent

│└──main.ts←herewebootstrapthetoprootcomponent

├──index.html

├──package.json

├──tsconfig.json

└──typings.json

Itiseasytounderstandthewholerationaleoftheproject.Now,wewillputtogetheranapplicationthatfeaturestwomaincontexts:atimerfeatureandataskslistingfeature.Eachfeaturecanencompassadifferentrangeofcomponents,pipes,directives,orservices.Theinnerimplementationofeachfeatureisopaquetotheotherfeaturesorcontexts.Eachfeaturecontextexposesafacadethatexportsthepiecesoffunctionality(thatis,thecomponent,oneormany)thateachcontextdeliverstotheupper-levelcontextorapplication.Alltheotherpiecesoffunctionality(innerservicesordirectives)areconcealedfromtherestoftheapplication.

Itisfairtosaythatitisdifficulttodrawalineinthesanddifferentiatingwhatbelongstoaspecificcontextoranother.Sometimes,webuildpiecesoffunctionality,suchascertaindirectivesorpipes,whichcanbereusedthroughouttheapplication.So,lockingthemdownto

www.EBooksWorld.ir

Page 206: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

aspecificcontextdoesnotmakemuchsense.Forthosecases,wedohavethesharedcontext,wherewestoreanycodeunitwhichismeanttobereusableatanapplicationlevel,apartfrommediafilessuchasstylesheetsorbitmapimagesthatarecomponent-agnostic.

Themainapp.component.tsfilecontainsandexportstheapplicationrootcomponent,whichdeclaresandregistersinitsowninjectorthedependenciesrequiredbyitschildcomponents.Asyouknowalready,allAngular2applicationsmusthaveatleastonerootcomponent,initializedbythebootstrap()function.Thisoperationisactuallyperformedinthemain.tsfile,whichisfiredbytheindex.htmlfile.

Definingacomponentoragroupofrelatedcomponentswithinacontextlikethisimprovesreusabilityandencapsulation.Theonlycomponentthatistightlycoupledwiththeapplicationisthetoprootcomponent,whosefunctionalityisusuallyprettylimitedandentailsbasicallyrenderingtheotherchildcomponentsinitstemplatevieworactingasaroutercomponent,aswewillseeinthenextchapters.

ThelastbitofthepuzzleistheJSONfilesthatcontaintheTypeScriptcompiler,typings,andnpmconfiguration.SinceversioningontheAngular2frameworkkeepsevolving,wewillnotlookattheactualcontentofthesefileshere.Youaresupposedtoknowtheirpurpose,butsomespecificssuchasthepeerdependencyversionschangequiteoftensoyoubetterrefertothebook'sGitHubrepositoryforthelatestup-to-dateversionofeachone.Thepackage.jsonfilerequiresaspecialmentionthough.Thereareafewcommonindustryconventionsandpopularseedprojects,liketheoneprovidedbytheAngularofficialsiteitself.Wehaveprovidedseveralnpmcommandstoeasetheoverallinstallationprocessandthedevelopmentendeavor:

1. Gotohttps://github.com/deeleman/angular2-essentialsanddownloadpackage.json,typings.jsonandtsconfig.jsonintoafolderofyourchoice.Werecommendyoutodownloadindex.htmlaswell.

2. Openupyourconsoleinthefolderwhereyousavedtheprecedingfilesandrunnpminstall.Angular2andallitspeerdependenciesandrequiredtypingswillbedownloadedandinstalledintheprojectworkspace.

3. Now,youcanjustrunnpmstartintheconsole,enabletheTypeScriptcompilerinwatchmode,andfirealocalserverpointingtothisprojectfolder.Werecommendyoutocreatetheapplicationfilesbeforeexecutingthiscommandoranexceptionwillbetriggered.

www.EBooksWorld.ir

Page 207: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

RefactoringourapplicationtheAngular2wayInthissection,wewillsplitthecodewecreatedinChapters1,3,and4intocodeunits,followingthesingleresponsibilityprinciple.So,donotexpectmanychangesinthecode,apartfromallocatingeachmoduleinitsowndedicatedfile.Thisiswhywewillfocusmoreonhowtosplitthingsratherthanexplainingeachmodule,whosepurposeyoushouldknowalready.Inanyevent,wewilltakeaminutetodiscusschangesifrequired.

Let'sbeginbycreatinginyourworkfolderthesamedirectorystructurewesawintheprevioussection.Wewillpopulateeachfolderwithfilesonthego.

www.EBooksWorld.ir

Page 208: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

ThesharedcontextThesharedcontextiswherewestoreanymodulewhosefunctionalityismeanttobeusedbynotonebutmanycontextsatonce,asitisagnostictothosecontextsaswell.Agoodexampleisthepomodorobitmapwe'vebeenusingtodecorateourcomponents,whichshouldbestoredintheapp/shared/assets/imgpath(pleasedosaveitthere,bytheway).

Anothergoodexampleistheinterfacesthatmodeldata,mostlywhentheirschemacanbereusedacrossadifferentcontextoffunctionality.Forinstance,whenwedefinedtheQueuedOnlyPipeinChapter3,ImplementingPropertiesandEventsinOurComponents,weactionedonlyoverthequeuedpropertyofitemsintherecordset.WecanthenseriouslyconsiderimplementingaQueuedinterfacethatwecanuselaterontoprovidetype-checkingformodulesthatfeaturethatproperty.Thiswillmakeourpipesmorereusableandmodel-agnostic.Thecodeisasfollows:

app/shared/interfaces/queuable.ts

interfaceQueueable{

queued:boolean;

}

exportdefaultQueueable;

Tip

Payattentiontothisworkflow:firstwedefinethemodulecorrespondingtothiscodeunit,andthenweexportit,flaggingitasdefaultsowecanimportitbynamefromelsewhere.Interfacesneedtobeexportedthisway,butfortherestofthebookwewillusuallydeclarethemoduleandexportitinthesamestatement.

Withthisinterfaceinplace,wecannowsafelyrefactortheQueuedOnlyPipetomakeitfullyagnosticfromtheTaskinterfacesothatitisfullyreusableonanycontextwherearecordset,featuringitemsimplementingtheQueuedinterface,needstobefiltered,regardlessofwhattheyrepresent.Thecodeisasfollows:

app/shared/pipes/queued-only.pipe.ts

import{Pipe,PipeTransform}from'angular/core';

import{Queueable}from'../shared';

@Pipe({

name:'pomodoroQueuedOnly',

pure:false

})

exportdefaultclassQueuedOnlyPipeimplementsPipeTransform{

transform(

queueableItems:Queueable[],

...args):Queueable[]{

returnqueueableItems.filter((queueableItem:Queueable)=>{

www.EBooksWorld.ir

Page 209: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

returnqueueableItem.queued===args[0]

});

}

}

Asyoucansee,eachcodeunitcontainsasinglemodule.ThiscodeunitconformstothenamingconventionssetforAngular2filenames,clearlystatingthemodulenameincamelcase,plusthetypesuffix(.pipeinthiscase).Theimplementationdoesnotchangeeither,apartfromthefactthatwehaveannotatedallqueue-ableitemswiththeQueuabletype,insteadoftheTaskannotationwehadearlier.Now,ourpipecanbereusedwhereveramodelimplementingtheQueuedinterfaceispresent.

However,thereissomethingthatshoulddrawyourattention:we'renotimportingtheQueuableinterfacefromitssourcelocation,butfromafilenamedshared.tslocatedintheupperlevel.Thisisthefacadefileforthesharedcontext,andwewillexposeallpublicsharedmodulesfromthatfilenotonlytotheclientsconsumingthesharedcontextmodules,buttothoseinsidethesharedcontextaswell.Thereisacaseforthis:ifanymodulewithinthesharedcontextchangesitslocation,weneedtoupdatethefacadesothatanyotherelementreferringtothatmodulewithinthesamecontextremainsunaffected,sinceitconsumesitthroughthefacade.Thisisactuallyagoodmomenttostartbeefingupourveryfirstfacadethen:

app/shared/shared.ts

importQueueablefrom'./interfaces/queueable';

export{

Queueable

};

Asyoucansee,facadeshavenobusinesslogicimplementationand,intheirsimplestincarnation,arejustasummarizedblockofimportspubliclyexposedinasingleexport.NowthatwehaveaworkingQueuableinterfaceandafacade,wecancreatetheotherinterfacewewillrequirethroughoutthebook,correspondingtotheTaskentity,alongwiththeotherpipewerequired—bothexposedthroughthefacadeaswell:

app/shared/interfaces/task.ts

import{Queueable}from'../shared';

interfaceTaskextendsQueueable{

name:string;

deadline:Date;

pomodorosRequired:number;

}

exportdefaultTask;

WeimplementaninterfaceontoanotherinterfaceinTypeScriptbyusingextends(insteadof

www.EBooksWorld.ir

Page 210: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

implements).Now,fortheFormattedTimePipe:

app/shared/pipes/formatted-time.pipe.ts

import{Pipe,PipeTransform}from'@angular/core';

@Pipe({

name:'pomodoroFormattedTime'

})

exportdefaultclassFormattedTimePipeimplementsPipeTransform{

transform(totalMinutes:number):string{

letminutes:number=totalMinutes%60;

lethours:number=Math.floor(totalMinutes/60);

return`${hours}h:${minutes}m`;

}

}

Obviously,bothmoduleswillbemadeavailablepubliclyfromthefacade,aswedidpreviously.

Servicesinthesharedcontext

Thereisnoruleofthumbforserviceswithregardtowheretheyshouldgo.Someschoolsofthoughtassumethatservicesaremeredataandlogicprovidersand,assuch,shouldbeagnosticofwhatactuallyconsumesthem,irrespectiveofwhetheritisacomponent,directive,oranyotherservice.Servicesbecomethenfirst-classcitizensthatcaneasilybepromotedtotheirownprojectworkspaceforgreaterreusabilityacrossdifferentprojects.Someotherpractitionersprefertobindservicestothefeaturecontexttheybelongto,ifonlyasinglecontextapplies,favoringencapsulation.Ultimately,itwilldependonthelevelofreusabilityversusencapsulationyouaimtoachieveinyourapplication,andwhatentityactuallymakesuseofthedataandlogicofthoseservices.

Webuiltadataserviceinthepreviouschaptertoserveatasksdatasettopopulateourdatatablewith.Aswewillseelaterinthisbook,thedataservicewillbeconsumedbyothercontextsoftheapplication.So,wewillallocateitinthesharedcontext,exposingitthroughthefacadeasusual:

app/shared/services/task.service.ts

import{Injectable}from'@angular/core';

import{Task}from'../shared';

@Injectable()

exportdefaultclassTaskService{

publictaskStore:Task[]=[];

constructor(){

consttasks=[

{

name:"CodeanHTMLTable",

deadline:"Jun232015",

www.EBooksWorld.ir

Page 211: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

pomodorosRequired:1

},{

name:"Sketchawireframeforthenewhomepage",

deadline:"Jun242016",

pomodorosRequired:2

},{

name:"StyletablewithBootstrapstyles",

deadline:"Jun252016",

pomodorosRequired:1

},{

name:"ReinforceSEOwithcustomsitemap.xml",

deadline:"Jun262016",

pomodorosRequired:3

}

];

this.taskStore=tasks.map(task=>{

return{

name:task.name,

deadline:newDate(task.deadline),

queued:false,

pomodorosRequired:task.pomodorosRequired

};

});

}

}

PleasepayattentiontohowweimportedtheInjectable()decoratorandimplementeditonourservice.Itdoesnotrequireanydependencyinitsconstructor,soothermodulesdependingonthisservicewillnothaveanyissuesanywaywhendeclaringitinitsconstructors.Thereasonissimple:itisactuallyagoodpracticetoapplythe@Injectable()decoratorinourservicesbydefaulttoensuretheykeepbeinginjectedseamlesslyaslongastheybegindependingonotherproviders,justincaseweforgettodecoratethemthen.

Configuringapplicationsettingsfromacentralservice

Inthepreviouschapters,wehardcodedalotofstuffinourcomponents:labels,pomodorodurations,pluralmappings,andsoon.Sometimes,ourcontextsaremeanttohaveahighlevelofspecificityandandit'sfinetohavethatinformationthere.Atothertimes,wemightrequiremoreflexibilityandamoreconvenientwaytoupdatethesesettingsapplication-wide.Forthisexample,wewillmakeallthel18npipesmappingsandpomodorosettingsavailablefromacentralservicelocatedinthesharedcontextandexposed,asusual,fromtheshared.tsfacade.

app/shared/services/settings.service.ts

import{Injectable}from'@angular/core';

@Injectable()

exportdefaultclassSettingsService{

timerMinutes:number;

labelsMap:any;

pluralsMap:any;

www.EBooksWorld.ir

Page 212: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

constructor(){

this.timerMinutes=25;

this.labelsMap={

'timer':{

'start':'StartTimer',

'pause':'PauseTimer',

'resume':'ResumeCountdown',

'other':'Unknown'

}

};

this.pluralsMap={

'tasks':{

'=0':'Nopomodoros',

'=1':'Onepomodoro',

'other':'#pomodoros'

}

}

}

}

Pleasenotehowweexposecontext-agnosticmappingproperties,whichareactuallynamespaced,tobettergroupthedifferentmappingsbycontext.

Itwouldbeperfectlyfinetosplitthisserviceintotwospecificservices,onepercontext,andlocatetheminsidetheirrespectivecontextfolders,atleastwithregardtothel18nmappings.Keepinmindthatdatasuchasthetimedurationperpomodorowillbeusedacrossdifferentcontextsthough,aswewillseelaterinthischapter.

www.EBooksWorld.ir

Page 213: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

CreatingafacademoduleincludingacustomprovidersbarrelWithallthelatestchanges,ourshared.tsfacadeshouldlooklikethis:

app/shared/shared.ts

importQueueablefrom'./interfaces/queueable';

importTaskfrom'./interfaces/task';

importFormattedTimePipefrom'./pipes/formatted-time.pipe';

importQueuedOnlyPipefrom'./pipes/queued-only.pipe';

importSettingsServicefrom'./services/settings.service';

importTaskServicefrom'./services/task.service';

export{

Queueable,

Task,

FormattedTimePipe,

QueuedOnlyPipe,

SettingsService,

TaskService

};

Ourfacadeexposesinterfacetypings,pipes,andserviceproviders.Aswewillseewheninjectingourdependenciesgloballyfromtherootcomponent,itisactuallyquitecommonandconvenienttogroupservices(anddirectivesaswell,includingcomponentsinthesamegroup)intogroupingaliastokens,usuallynamedafterthecontextnamefollowedbythe_PROVIDERSsuffix,allinuppercase.Withregardtothefacade,wecouldintroducethisblockofcoderightabovetheexportstatement:

//importstatementsremainunchangedabove

constSHARED_PIPES:any[]=[

FormattedTimePipe,

QueuedOnlyPipe

];

constSHARED_PROVIDERS:any[]=[

SettingsService,

TaskService

];

export{

Queueable,

Task,

FormattedTimePipe,

QueuedOnlyPipe,

SHARED_PIPES,

SettingsService,

www.EBooksWorld.ir

Page 214: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

TaskService,

SHARED_PROVIDERS

};

Thisway,wecanregisterallourprovidersthroughasingletokenwhererequired.Thesameappliestodirectivesandcomponents,wheretheruleofthumbistoexportinthecontextfacadeatokenwiththename{CONTEXTNAME}_DIRECTIVES.Thisgivesustheopportunitytoinjectsupportforallrequiredcomponentsanddirectivesfromacontextinanothercomponentbymeansofasingletoken.Ifsuchcontextwindsupexposingmorecomponentsanddirectivesinthefutureorthenamesofitsexistinglogicalmoduleschange,wewillnotneedthentofollowthetrailofmoduletokensregisteredinthedirectivespropertyofourcomponentsthroughouttheapplication.Wewillseeallthisinactionfurtherupinthischapter.

www.EBooksWorld.ir

Page 215: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

CreatingourcomponentsWithoursharedcontextsortedout,timehascometocaterwithourothertwocontexts:timerandtasks.Theirnamesareself-descriptiveenoughofthescopeoftheirfunctionalities.Eachcontextfolderwillallocatethecomponent,HTMLviewtemplate,CSS,anddirectivefilesrequiredtodelivertheirfunctionality,plusafacadefilethatexportsthepubliccomponentsofthisfeature.

Thetimercontext

Ourfirstcontextistheonebelongingtothetimerfunctionality,whichhappenstobethesimpleroneaswell.Itcomprisesauniquecomponentwiththecountdowntimerwebuiltinthepreviouschapters:

app/timer/timer-widget.component.ts

import{Component,Input,OnInit}from'@angular/core';

import{SettingsService}from'../shared/shared';

@Component({

selector:'pomodoro-timer-widget',

template:`

<divclass="text-center">

<imgsrc="/app/shared/assets/img/pomodoro.png">

<h1>{{minutes}}:{{seconds|number:'2.0'}}</h1>

<p>

<button(click)="togglePause()"class="btnbtn-danger">

{{buttonLabelKey|i18nSelect:buttonLabelsMap}}

</button>

</p>

</div>`

})

exportdefaultclassTimerWidgetComponent{

minutes:number;

seconds:number;

isPaused:boolean;

buttonLabelKey:string;

buttonLabelsMap:any;

constructor(privatesettingsService:SettingsService){

this.buttonLabelsMap=settingsService.labelsMap.timer;

}

ngOnInit():void{

this.resetPomodoro();

setInterval(()=>this.tick(),1000);

}

resetPomodoro():void{

this.isPaused=true;

this.minutes=this.settingsService.timerMinutes-1;

this.seconds=59;

this.buttonLabelKey='start';

www.EBooksWorld.ir

Page 216: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

}

privatetick():void{

if(!this.isPaused){

this.buttonLabelKey='pause';

if(--this.seconds<0){

this.seconds=59;

if(--this.minutes<0){

this.resetPomodoro();

}

}

}

}

togglePause():void{

this.isPaused=!this.isPaused;

if(this.minutes<this.settingsService.timerMinutes||this.seconds<59){

this.buttonLabelKey=this.isPaused?'resume':'pause';

}

}

}

Asyoucansee,theimplementationisprettymuchthesamewesawalreadybackinChapter1,CreatingOurVeryFirstComponentinAngular2,withtheexceptionofinitializingthecomponentattheinitlifecyclestagethroughtheOnInitinterfacehook.Weleveragethel18nSelectpipetobetterhandlethedifferentlabelsrequiredforeachstateofthetimer,consumingthelabelinformationfromtheSettingsServiceprovider,whichisinjectedbythecomponentinjectorthroughanannotatedargumentintheconstructor.Lateroninthischapter,wewillseewheretoregisterthatprovider.Thepomodorodurationinminutesisalsoconsumedfromtheservice,oncethelatterisboundtoaclassfield.

Thecomponentisexportedpubliclythroughafacade,savingitsclientcomponentsfromhavingtoknowtheactualpathandfilenameofthecomponent.Thecodeisasfollows:

app/timer/timer.ts

importTimerWidgetComponentfrom'./timer-widget.component';

constTIMER_DIRECTIVES:any[]=[

TimerWidgetComponent

];

export{

TIMER_DIRECTIVES,

TimerWidgetComponent

};

PleasenotethattheTIMER_DIRECTIVESaliastokenisincludedforourconvenienceshouldthecomponentrangegrowinthefutureandwewishtotakeadvantageofasingleentrypointtoallcomponentsanddirectivespubliclyavailable.

www.EBooksWorld.ir

Page 217: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Thetaskscontext

Thetaskscontextencompassessomemorelogic,sinceitentailstwocomponentsandadirective.Let'sbeginbycreatingthecoreunitrequiredbyTaskTooltipDirective:

app/tasks/task-tooltip.directive.ts

import{Task}from'../shared/shared';

import{Input,Directive,HostListener}from'@angular/core';

@Directive({

selector:'[task]'

})

exportdefaultclassTaskTooltipDirective{

privatedefaultTooltipText:string;

@Input()task:Task;

@Input()taskTooltip:any;

@HostListener('mouseover')

onMouseOver(){

if(!this.defaultTooltipText&&this.taskTooltip){

this.defaultTooltipText=this.taskTooltip.innerText;

}

this.taskTooltip.innerText=this.task.name;

}

@HostListener('mouseout')

onMouseOut(){

if(this.taskTooltip){

this.taskTooltip.innerText=this.defaultTooltipText;

}

}

}

ThedirectivekeepsalltheoriginalfunctionalityinplaceandjustimportstheAngular2coretypesandtask-typingitrequires.Let'slookattheTaskIconsComponentnow:

app/tasks/task-icons.component.ts

import{Component,Input,OnInit}from'@angular/core';

import{Task}from'../shared/shared';

@Component({

selector:'pomodoro-task-icons',

template:`<img*ngFor="leticonoficons"

src="/app/shared/assets/img/pomodoro.png"

width="{{size}}">`

})

exportdefaultclassTaskIconsComponentimplementsOnInit{

@Input()task:Task;

@Input()size:number;

icons:Object[]=[];

ngOnInit(){

this.icons.length=this.task.pomodorosRequired;

www.EBooksWorld.ir

Page 218: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

this.icons.fill({name:this.task.name});

}

}

Sofarsogood.Now,let'sjumptoTasksComponent.Thiscomponentwillrequiresomemoreoverhead,sinceitfeaturesexternalHTMLtemplatesandstylesheets,whicharebasicallythesamewealreadybuiltbackinChapter4,EnhancingourComponentswithPipesandDirectives:

app/tasks/tasks.component.css

h3,p{

text-align:center;

}

.table{

margin:auto;

max-width:860px;

}

app/tasks/tasks.component.html

<divclass="containertext-center">

<h3>

{{queuedPomodoros|i18nPlural:queueHeaderMapping}}fortoday

<spanclass="small"*ngIf="queuedPomodoros>0">(Estimatedtime:

{{queuedPomodoros*timerMinutes|pomodoroFormattedTime}})

</span>

</h3>

<p>

<span*ngFor="letqueuedTaskoftasks|pomodoroQueuedOnly:true">

<pomodoro-task-icons

[task]="queuedTask"

[taskTooltip]="tooltip"

size="50">

</pomodoro-task-icons>

</span>

</p>

<p#tooltip[hidden]="queuedPomodoros===0">Mouseoverfordetails</p>

<h4>Tasksbacklog</h4>

<tableclass="table">

<thead>

<tr>

<th>TaskID</th>

<th>Taskname</th>

<th>Deliverby</th>

<th>Pomodoros</th>

<th>Actions</th>

</tr>

</thead>

<tbody>

<tr*ngFor="lettaskoftasks;leti=index">

<thscope="row">{{i}}

<span*ngIf="task.queued"class="labellabel-info">

Queued

www.EBooksWorld.ir

Page 219: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

</span>

</th>

<td>{{task.name|slice:0:35}}

<span[hidden]="task.name.length<35">...</span>

</td>

<td>{{task.deadline|date:'fullDate'}}

<span*ngIf="task.deadline<today"class="labellabel-danger">

Due

</span>

</td>

<tdclass="text-center">{{task.pomodorosRequired}}</td>

<td>

<buttontype="button"class="btnbtn-defaultbtn-xs"

[ngSwitch]="task.queued"

(click)="toggleTask(task)">

<template[ngSwitchWhen]="false">

<iclass="glyphiconglyphicon-plus-sign"></i>

Add

</template>

<template[ngSwitchWhen]="true">

<iclass="glyphiconglyphicon-minus-sign"></i>

Remove

</template>

<templatengSwitchDefault>

<iclass="glyphiconglyphicon-plus-sign"></i>

Add

</template>

</button>

</td>

</tr>

</tbody>

</table>

</div>

Pleasetakeamomenttocheckoutthenamingconventionappliedtotheexternalcomponentfiles,whosefilenamematchesthecomponent'sowntoidentifywhichfilebelongstowhatinflatstructuresinsideacontextfolder.Also,pleasenotehowweremovedthemainpomodorobitmapfromthetemplateandreplacedthehardcodedpomodorotimedurationswithavariablenamedtimerMinutesinthebindingexpressionthatcomputesthetimeestimationtoaccomplishallqueuedtasks.Wewillseehowthatvariableispopulatedinthefollowingcomponentclass:

app/tasks/tasks.component.ts

import{Component,OnInit}from'@angular/core';

importTaskIconsComponentfrom'./task-icons.component';

importTaskTooltipDirectivefrom'./task-tooltip.directive';

import{

TaskService,

SettingsService,

Task,

SHARED_PIPES

}from'../shared/shared';

www.EBooksWorld.ir

Page 220: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

@Component({

selector:'pomodoro-tasks',

directives:[TaskIconsComponent,TaskTooltipDirective],

pipes:[SHARED_PIPES],

styleUrls:['app/tasks/tasks.component.css'],

templateUrl:'app/tasks/tasks.component.html'

})

exportdefaultclassTasksComponentimplementsOnInit{

today:Date;

tasks:Task[];

queuedPomodoros:number;

queueHeaderMapping:any;

timerMinutes:number;

constructor(

privatetaskService:TaskService,

privatesettingsService:SettingsService){

this.tasks=this.taskService.taskStore;

this.today=newDate();

this.queueHeaderMapping=settingsService.pluralsMap.tasks;

this.timerMinutes=settingsService.timerMinutes;

}

ngOnInit():void{

this.updateQueuedPomodoros();

}

toggleTask(task:Task):void{

task.queued=!task.queued;

this.updateQueuedPomodoros();

}

privateupdateQueuedPomodoros():void{

this.queuedPomodoros=this.tasks

.filter((Task:Task)=>Task.queued)

.reduce((pomodoros:number,queuedTask:Task)=>{

returnpomodoros+queuedTask.pomodorosRequired;

},0);

}

};

SeveralaspectsoftheTasksComponentimplementationareworthhighlighting:

Weimportthecomponent'srequiredchildcomponentanddirectivesrelatively.Ifwetriedtobringthemfromthefacade,wewouldgetanundefinedvaluebecauseofacircularreference.Wedonotimportalltherequiredpipes,justitsSHARED_PIPESaliastoken,registeringitinthepipe'scomponentdecoratorproperty.WeinjecttheTaskServiceandSettingsServiceprovidersinthecomponent,leveragingAngular'sDIsystem.Thedependenciesareinjectedwithaccessorsrightfromtheconstructor,becomingprivateclassmembersonthespot.Thetasksdatasetandthepomodorotimedurationarethenpopulatedfromtheboundservices.

www.EBooksWorld.ir

Page 221: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Ourlaststepistoexposethefacaderequiredforthisfeaturecontext.Inallfairness,wearenotmeanttoexporteverythingoneverycontext.Wecansimplygetawaywithexportingonlythemaincontextcomponent,whileleavinganyothersubcomponentordirectiveoutsidethescopeofthecontextfacade.Thatisactuallywhatwewilldoinhere,sinceitisquiteunlikelythatanyhostcomponentwouldeverneedtoincludeinitsownviewtheTaskIconsComponent.WewillneverthelessexporttheTaskTooltipDirective,sinceitsimplementationmightbereusedinthefuturebysomeothercomponentdealingwiththe[task]inputproperties.

app/tasks/tasks.ts

importTasksComponentfrom'./tasks.component';

importTaskTooltipDirectivefrom'./task-tooltip.directive';

constTASKS_DIRECTIVES:any[]=[

TasksComponent,

TaskTooltipDirective

];

export{

TASKS_DIRECTIVES,

TasksComponent,

TaskTooltipDirective

};

Pleasecheckhowweconformtothenamingconventionforaliastokenswhengroupingcomponentsanddirectivesintheirownaliastoken.

Definingthetoprootcomponent

Withallourfeaturecontextsready,timehascometodefinethetoprootcomponent,whichwillkickstartthewholeapplicationasaclusterofcomponentslaidoutinatreehierarchy.Therootcomponentusuallyhasaminimumimplementation.Basically,itsgoalistoregisterthedependencyproviderstheapplicationwillrequireassingletonsatdifferentlevelsofthecomponenthierarchy,andinstantiateinitsviewtemplatethemainchildcomponentsthatwilleventuallyevolveintobranchesofchildcomponents.

app/app.component.ts

import{Component}from'@angular/core';

import{TIMER_DIRECTIVES}from'./timer/timer';

import{TASKS_DIRECTIVES}from'./tasks/tasks';

import{SHARED_PROVIDERS}from'./shared/shared';

@Component({

selector:'pomodoro-app',

directives:[TIMER_DIRECTIVES,TASKS_DIRECTIVES],

providers:[SHARED_PROVIDERS],

template:`

<navclass="navbarnavbar-defaultnavbar-static-top">

<divclass="container">

<divclass="navbar-header">

www.EBooksWorld.ir

Page 222: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

<strongclass="navbar-brand">MyPomodoroApp</strong>

</div>

</div>

</nav>

<pomodoro-timer-widget></pomodoro-timer-widget>

<pomodoro-tasks></pomodoro-tasks>

`

})

exportdefaultclassAppComponent{}

Pleasecheckhowweconvenientlyimportthealiastokensandregisterthemintheprovidersanddirectivespropertiesofthecomponentdecorator.TheSHARED_PROVIDERStokendeservesaspecialmention.Alltheprovidersgroupedbyitarenowavailabledownthetreeofcomponentsthathangsfromthistoprootcomponent,sothere'snoneedtoregisteritagainandthestateofeachproviderwillremainconsistentacrosstheapplicationdomain.

Theonlyexceptiontothiswouldbetohaveonecomponentatsomelevelregistering,forargument'ssake,theTaskServiceasaprovider.Thatwouldturnintoanewinstanceoftheserviceatthatcomponentlevelandforallitschildcomponentsaswell.

www.EBooksWorld.ir

Page 223: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

BootstrappingtheapplicationWenowhaveafull-blownapplicationfeaturingdifferentfunctionalitycontexts,wrappedbyatoprootcomponent.Thelaststepofourendeavorwillbetobootstraptheapplication,byimportingthemaintoprootcomponentandpassingitovertothebootstrap()function:

app/main.ts

import{bootstrap}from'@angular/platform-browser-dynamic';

importAppComponentfrom'./app.component';

bootstrap(AppComponent);

Thisfilemustbeimportedfromthemainindex.htmlfileinordertotriggerthewholeprocess,byusingastandardmoduleloadersuchasSystemJSorWebPack.Inthebookrepository,weuseSystemJSsopleaserefertothechaptercodethereforfurtherreference.Intheindex.htmlfile,wewillexpectacustomelementmatchingthetoprootcomponentselector,asshowninthefollowingindex.htmltranscription:

<!DOCTYPEhtml>

<html>

<head>

<metacharset="utf-8">

<title>MyAngular2PomodoroApplication</title>

<scriptsrc="node_modules/es6-shim/es6-shim.min.js"></script>

<scriptsrc="node_modules/zone.js/dist/zone.js"></script>

<scriptsrc="node_modules/reflect-metadata/Reflect.js"></script>

<scriptsrc="node_modules/systemjs/dist/system.js"></script>

<scriptsrc="node_modules/rxjs/bundles/Rx.js"></script>

<scriptsrc="systemjs.config.js"></script>

<script>

System.import('built/app/main')

.then(null,console.error.bind(console));

</script>

<linkrel="stylesheet"

href="node_modules/bootstrap/dist/css/bootstrap.min.css">

<basehref="/">

</head>

<body>

<pomodoro-app>Loading...</pomodoro-app>

</body>

</html>

Withallthefilesinplace,wecansafelycompiletheprojectandseetheresultsservedbyawebserverinabrowserwindow.Ifyouhavedownloadedpackage.json(andrelatedJSONfiles)fromthebookrepo,pleaserunnpmstartfromyourterminalwindowandenjoy.You

www.EBooksWorld.ir

Page 224: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

madeafantasticpomodoroapplicationbyyourself!

www.EBooksWorld.ir

Page 225: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

SummaryThischapterhasdefinitelysetthefoundationforallthegreatapplicationsthatyouwillbebuildingontopofAngular2fromnowon.TheAngular2dependencymanagementimplementationisinfactoneofthegemsofthisframeworkandatimesaver.Applicationarchitecturesbasedoncomponenttreesarenotrocketscienceanymore,andwehavefollowedthispatterntosomeextentwhilebuildingwebsoftwareinotherframeworkssuchasAngular1.xandReact.

ThischapterconcludesourtripthroughthecoreofAngular2anditsapplicationarchitecture,settingupthestandardsthatwewillfollowfromnowonwhilebuildingapplicationsontopofthisnewandexcitingframework.

Inthenextchapters,wewillfocusonveryspecifictoolsandmodulesthatwecanusetosolveeverydayproblemswhencraftingourwebprojects.WewillseehowtodevelopbetterHTTPnetworkingclientswithAngular2.

www.EBooksWorld.ir

Page 226: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Chapter6.AsynchronousDataServiceswithAngular2ConnectingtodataservicesandAPIsandhandlingasynchronousinformationisacommontaskinoureverydaylifeasdevelopers.Inthissense,Angular2providesanunparalleledtoolsettohelpitsenthusiasticdeveloperswhenitcomestoconsuming,digesting,andtransformingallkindsofdatafetchedfromdataservices.

TherearesomanypossibilitiesthatitwouldrequireanentirebooktodescribeallthatyoucandotoconnecttoAPIsorconsumeinformationfromthefilesystemasynchronouslythroughHTTP.Inthisbook,wewillonlyscratchthesurface,buttheinsightscoveredinthischapterabouttheHTTPAPIanditscompanionclassesandtoolswillgiveyouallthatyouneedtoconnectyourapplicationstoHTTPservicesinnotime,leavingtoyourcreativityallthatyoucandowiththem.

Inthischapter,wewill:

LookatthedifferentstrategiesforhandlingasynchronousdataIntroduceObservablesandObserversDiscussfunctionalreactiveprogrammingandRxJSReviewtheHTTPclassanditsAPI,aspartoftheCovertheHTTP_PROVIDERSmoduleSeealloftheprecedingpointsinactionthroughactualcodeexamples

www.EBooksWorld.ir

Page 227: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

StrategiesforhandlingasynchronousinformationConsuminginformationfromanAPIisacommonoperationinourdailypractice.WeconsumeinformationoverHTTPallthetime—whenauthenticatingusersbysendingoutcredentialstoanauthenticationservice,orwhenfetchingthelatesttweetsinourfavoriteTwitterwidget.Modernmobiledeviceshaveintroducedanunparalleledwayofconsumingremoteservices,deferringtherequestsandresponseconsumptionuntilmobileconnectivityisavailable.Responsivityandavailabilityhavebecomebigdeals.AndalthoughmodernInternetconnectionsareultra-fast,thereisalwaysaresponsetimeinvolvedwhenservingsuchinformationthatforcesustoputinplacemechanismstohandlestateinourapplicationsinatransparentwayfortheenduser.

Thisisnotspecifictoscenarioswhereweneedtoconsumeinformationfromanexternalresource.Sometimes,wemightneedtobuildfunctionalitiesthatdependontimeasaparameterofsomething,andweneedtointroducecodepatternsthathandlethisdeferredchangeintheapplicationstate.

Forallthesescenarios,wehavealwaysusedcodepatterns,suchasthecallbackpattern,wherethefunctionthattriggerstheasynchronousactionexpectsanotherfunctioninitssignature,whichwillemitasortofnotificationassoonastheasynchronousoperationiscompleted:

functionnotifyCompletion(){

console.log('Ourasynchronousoperationhasbeencompleted');

}

functionasynchronousOperation(callback){

setTimeout(callback,5000);

}

asynchronousOperation(notifyCompletion);

Theproblemwiththispatternisthatcodecanbecomequiteconfusingandcumbersomeastheapplicationgrowsandmoreandmorenestedcallbacksareintroduced.Inordertoavoidthisscenario,Promisesintroducedanewwayofenvisioningasynchronousdatamanagementbyconformingtoaneaterandmoresolidinterface,inwhichdifferentasynchronousoperationscanbechainedatthesamelevelandevenbesplitandreturnedfromotherfunctions:

functionnotifyCompletion(){

console.log('Ourasynchronousoperationhasbeencompleted');

}

functionasynchronousOperation(){

varpromise=newPromise((resolve,reject)=>{

setTimeout(resolve,5000);

});

returnpromise;

www.EBooksWorld.ir

Page 228: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

}

asynchronousOperation().then(notifyCompletion);

Theprecedingcodeexampleisperhapsabitmoreverbose,butitdefinitelyproducesamoreexpressiveandelegantinterfaceforourasynchronousOperationfunction.

So,Promisestookoverthecodingarenabystormandnodeveloperoutthereseemstoquestionthegreatvaluetheybringtothegame.Sowhydoweneedanotherparadigm?Well,becausesometimeswemightneedtoproducearesponseoutputthatfollowsamorecomplexdigestprocessasitisbeingreturned,orevencancelthewholeprocess.ThiscannotbedonewithPromises,becausetheyaretriggeredassoonasthey'reinstanced.Inotherwords,Promisesarenotlazy.Ontheotherhand,thepossibilityoftearingdownanasynchronousoperationafterithasbeenfiredbutnotcompletedyetcanbecomequitehandyincertainscenarios.Promisesonlyallowustoresolveorrejectanasynchronousoperation,butsometimeswemightwanttoaborteverythingbeforegettingtothatpoint.Ontopofthat,promisesbehaveasaone-timeoperation.Oncetheyareresolved,wecannotexpecttoreceiveanyfurtherinformationorstatechangenotificationunlesswereruneverythingfromscratch.Moreover,wesometimesneedamoreproactiveimplementationofasyncdatahandling.ThisiswhereObservablescomeintothegame.

www.EBooksWorld.ir

Page 229: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

ObservablesinanutshellAnObservableisbasicallyanasynceventemitterthatinformsanotherelement,calledtheObserver,thestatehaschanged.Inordertodoso,theObservableimplementsallofthemachinerythatitneedstoproduceandemitsuchasyncevents,anditcanbefiredandcanceledatanytimeregardlessofwhetherithasemittedtheexpecteddataeventsalreadyornot.

Thispatternallowsconcurrentoperationsandmoreadvancedlogic,sincetheObserversthatsubscribetotheObservableasynceventswillreacttoreflectthestatechangeoftheObservabletheysubscribeto.

Thesesubscribers,whicharetheObserverswementionedearlier,willkeeplisteningtowhateverhappensintheObservableuntiltheObservableisdisposed,ifthateverhappenseventually.Inthemeantime,informationwillbeupdatedthroughouttheapplicationwithnointerventionwhatsoeveroftriggeringroutines.

Wecanprobablyseeallthiswithmoretransparencyinanactualexample.Let'srefactortheexamplewecoveredwhenassessingpromise-basedasyncoperationsandreplacethesetTimeoutcommandwithsetInterval:

functionnotifyCompletion(){

console.log('Ourasynchronousoperationhasbeencompleted');

}

functionasynchronousOperation(){

varpromise=newPromise((resolve,reject)=>{

setInterval(resolve,2000);

});

returnpromise;

}

asynchronousOperation().then(notifyCompletion);

Copyandpastetheprecedingsnippetinyourbrowser'sconsolewindowandseewhathappens:thetextOurasynchronousoperationhasbeencompletedwillshowupatthedevtools'consoleonlyonceafter2secondsandwillneverberenderedagain.Thepromiseresolveditselfandtheentireasynceventwasterminatedatthatverymoment.

Now,pointyourbrowsertoanonlineJavaScriptcodeplaygroundsuchasJSBIN(https://jsbin.com/),andcreateanewcodesnippetenablingjusttheJavaScriptandtheConsoletabs.Then,makesureyouaddtheRxJSlibraryfromtheAddlibraryoptiondropdown(wewillneedthislibrarytocreateobservables,butdon'tpanic;wewillcoverthislaterinthechapter)andinsertthefollowingcodesnippet:

varobservable=Rx.Observable.create(observer=>{

setInterval(()=>{

observer.onNext('Myasyncoperation');

},2000);

});

www.EBooksWorld.ir

Page 230: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

observable.subscribe(response=>console.log(response));

Runitandexpectsomemessagetoappearontherightpane.2secondslater,wewillseethesamemessageshowingup,andthenagainandagain.Inthissimpleexample,wecreatedanobservableandthensubscribedtoitschanges,throwingtotheconsolewhateveritemits(asimplemessageinthisexample)asasortofpushnotification.

TheObservablereturnsastreamofeventsandoursubscribersreceivepromptnotificationofthosestreamedevents,actingaccordingly.ThisiswhatthemagicofObservablesrelieson—Observablesdonotperformanasyncoperationanddie(althoughwecanconfigurethemtodoso),butstartastreamofcontinuouseventswecansubscribeoursubscribersto.

Ifwecommentoutthelastline,nothingwillhappen.TheConsolepanewillremainsilentandallthemagicwillbeginonlywhenwesubscribeoursourceobject.

That'snotall,however.ThisstreamcanbethesubjectofmanyoperationsbeforehittingtheObserverssubscribedtothem.Justaswecangrabacollectionobject,suchasanarray,andapplyfunctionalmethodsoveritsuchasmap()orfilter()inordertotransformandplayaroundwiththearrayitems,wecandothesamewiththestreamofeventsthatareemittedbyourObservables.Thisiswhatisknownasreactivefunctionalprogramming,andAngular2makesthemostofthisparadigmtohandleasynchronousinformation.

www.EBooksWorld.ir

Page 231: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

ReactivefunctionalprogramminginAngular2TheObservablepatternstandsatthecoreofwhatweknowasreactivefunctionalprogramming.Basically,themostbasicimplementationofareactivefunctionalscriptencompassesseveralconceptsthatweneedtobecomefamiliarwith:

AnObservableAnObserverAtimelineAstreamofeventsfeaturingthesamebehaviorasanobjectscollectionAsetofcomposableoperators,alsoknownasReactiveExtensions

Soundsdaunting?It'snot.Believeuswhenwetellyouthatallofthecodeyouhavegonethroughsofarismuchmorecomplexthanthis.Thebigchallengehereistochangeyourmindsetandlearntothinkinareactivefashion,andthatisthemaingoalofthissection.

Ifwewanttoputitsimply,wecanjustsaythatreactiveprogrammingentailsapplyingasynchronoussubscriptionsandtransformationstoObservablestreamsofevents.Wecanimagineyourpokerfacenow,solet'sputtogetheramoredescriptiveexample.

Thinkaboutaninteractiondevicesuchasakeyboard.Akeyboardhaskeysthattheuserpresses.Eachoneofthosekeystrokestriggersakeypressevent.Thatkeypresseventfeaturesawiderangeofmetadata,including—butnotlimitedto—thenumericcodeofthespecifickeytheuserpressedatagivenmoment.Astheusercontinueshittingkeys,morekeyUpeventsaretriggeredandpipedthroughanimaginarytimeline.Congratulations!YouhaveanObservablesequenceintheworkshere:thekeyboardassumestheroleofanObservableandemitsasequenceofeventsthatinformofthekeyspressednomatterwhathappensnext.Eachkeystrokeeventisagnosticfromtherestandtheyfollowasequencealongtime.Nowwementionevents,oneoccurringafteranother.Ifwegrabagroupofelementsofagiventype(notnecessarilythesametypethough)andwraptheminanobject(whichiswhatthattimelineis),isn'titacollection?Moreover,thatcollectionisspannedalongtime.Soisn'tthatastream?Indeeditis,socongratulations!YounowhaveaneventstreamemittedbytheObservableforyourlisteningdelight:

www.EBooksWorld.ir

Page 232: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Ourstreamofkeypresseventsislookingreallygood,butwearegettingnotificationsforallthekeyspressed.Whatifwewanttosubscribetoasubsetofthestreamthatobservestheeventsrepresentedbycursorkeys?Wecouldbecodingasimplegameandthosearethekeysthatcontrolouravatarinthegame,whileotherkeyeventsarecompletelyuselessforthepurposeofthegame.Ouridealstreamshouldcontainonlycursorkeys'eventsthen.Thisiswherethefunctionalpartofthereactiveparadigmcomesintoplay.

Let'simaginewearegrabbingthateventstreamandfilteringittoincludeonlythekeypresseventsthatpertaintointeractionswiththecursorkeys,disregardingalltheothers.Ontopofthat,wearegoingtothrottlethestreamtoallowakeypresseventtopassthroughonlyevery500milliseconds,sowedonotoverflowwhateverislisteningattheotherendofthewire.Nowwehaveaneweventstream,whichistheby-productofthepreviousone,butrepresentsonlyasubsetoftheinformationwithaveryspecificgoal:

www.EBooksWorld.ir

Page 233: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Ifwesubscribetothislaststream,wecantakeactiononeachcursorkeypressandapplysomegamificationlogictowhatevergamewearedevelopingatthistime.Wecanevenhookupseveralsubscriberstothatstream,orapplyfurthertransformationsusingotherReactiveExtensionstocreatebrandnewstreamsthatothersubscriberscansubscribetoinordertoperformotherbusinessorpresentationlogicnotrelatedwhatsoevertothatperformedbytheoriginalsubscriber.

Thislogiccanbeportedtotherealmofcomponentstohandleinteractionevents,asynchronousbehavior,or(asthischapteraimstodescribe)theconsumptionanddigestionofinformationservedbyanAPIserviceordatastore.Inordertodoso,Angular2reliesonRxJS,whosecoremoduleisrequiredasapeerdependencywheninstallingAngular2.TheRxJSAPIprovidesallthatweneedtoputtheaforementionedthingstoworkonourasynchronousoperationswithAngular2.

www.EBooksWorld.ir

Page 234: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

TheRxJSlibraryAsmentionedpreviously,Angular2comeswithapeerdependencyonRxJS,theJavaScriptflavoroftheReactiveXlibrarythatallowsustocreateObservablesandObservablesequencesoutofalargevarietyofscenarios,suchasinteractionevents,promises,orcallbackfunctions,justtonameafew.Inthatsense,reactiveprogrammingdoesnotaimtoreplaceasynchronouspatternssuchaspromisesorcallbacks.Allthewayaround,itcanleveragethemaswelltocreateObservablesequences.

RxJScomeswithbuilt-insupportforawiderangeofcomposableoperatorstotransform,filter,andcombinetheresultingeventstreams.ItsAPIprovidesconvenientmethodstosubscribeObserverstothesestreamssothatourscriptsandcomponentscanrespondaccordinglytostatechangesorinteractioninputs.WhileitsAPIissomassivethatcoveringitindetailisoutofthescopeofthisbook,wewillhighlightsomebitsofitsmostbasicimplementationinorderforyoutobetterunderstandhowHTTPconnectionsarehandledbyAngular2.

BeforejumpingontotheHTTPAPIprovidedbyAngular2,let'screateasimpleexampleofanObservableeventstreamthatwecantransformwithReactiveExtensionsandsubscribeobserversto.Todoso,let'spickthescenariodescribedintheprevioussection.

Weenvisionedhowauserinteractingwithourapplicationthroughthekeyboardcan'tturnintoatimelineofkeystrokesand,therefore,aneventstream.GobacktoJSBIN,deletethecontentsoftheJavaScriptpane,andthenwritedownthefollowingsnippet:

varkeyboardStream=Rx.Observable

.fromEvent(document,'keyup')

.map(x=>x.which);

Theprecedingcodeisprettyself-descriptive.WeleveragetheRx.ObservableclassanditsfromEventmethodtocreateaneventemitterthatstreamsthekeyupeventsthattakeplaceinthescopeofthedocumentobject.Eachoftheeventobjectsemittedisacomplexobject,sowesimplifythestreamedobjectsbymappingtheeventstreamontoanewstreamthatcontainsonlythekeycodespertainingtoeachkeystroke.ThemapmethodisaReactiveExtensionthatfeaturesthesamebehaviorastheJavaScriptmapfunctionalmethod.Thisiswhyweusuallyrefertothiscodestyleasreactivefunctionalprogramming.

Allright,sonowwehaveaneventstreamofnumerickeystrokes,butweareonlyinterestedinobservingthoseeventsthatinformofhitsonthecursorkeys.WecanbuildanewstreamoutofanexistingstreambyapplyingmoreReactiveExtensions.So,let'sdoitwithkeyboardStreambyfilteringsuchastreamandreturningonlythoseeventsthatarerelatedtocursorkeys.Wewillalsomapthoseeventstotheirtextcorrespondenceforthesakeofclarity.Appendthefollowingchunkofcoderightbelowtheprevioussnippet:

varcursorMovesStream=keyboardStream.filter(x=>{

returnx>36&&x<41;

www.EBooksWorld.ir

Page 235: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

})

.map(x=>{

vardirection;

switch(x){

case37:

direction='left';

break;

case38:

direction='up';

break;

case39:

direction='right';

break;

default:

direction='down';

}

returndirection;

});

WecouldhavedoneallofthisinasingleactionbychainingthefilterandmapmethodstothekeyboardStreamObservableandthensubscribingtoitsoutput,butit'sgenerallyagoodideatoseparateconcerns.Byshapingourcodeinthisway,wehaveagenerickeyboardeventsstreamthatwecanreuselateronforsomethingcompletelydifferent.So,ourapplicationcanscaleupwhilekeepingthecodefootprinttoaminimum.

Nowthatwehavementionedsubscribers,let'ssubscribetoourcursormovesstreamandthrowthemovecommandsattheconsole.Wetypethefollowingstatementattheendofourscript,thencleartheConsolepane,andclickontheOutputtabsothatwecanhaveadocumentavailable:

cursorMovesStream.subscribe(e=>console.log(e));

ClickanywhereontheOutputpanetoputthefocusonitandstarttypingrandomkeyboardkeysandcursorkeys:

www.EBooksWorld.ir

Page 236: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

YouareprobablywonderinghowwecanapplythispatterntoanasynchronousscenariosuchasconsuminginformationfromaHTTPservice.Basically,youhavesofarbecomeusedtosubmittingasyncrequeststoAJAXservicesandthendelegatingtheresponsehandlingtoacallbackfunctionorjustpipingitthroughapromise.Now,wewillhandlethecallbyreturninganObservable.ThisObservablewillemittheserverresponseasaneventinthecontextofastream,whichwillbefunneledthroughReactiveExtensionstobetterdigesttheresponse.

www.EBooksWorld.ir

Page 237: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

IntroducingtheHTTPAPITheHttpclassprovidesapowerfulAPIthatabstractsalltheoperationsrequiredtohandleasynchronousconnectionsthroughavarietyofHTTPmethods,handlingtheresponsesinaneasyandcomfortableway.ItsimplementationhasbeenmadewithalotofcaretoensurethatprogrammerswillfeelateasewhiledevelopingsolutionsthattakeadvantageofthisclasstoconnecttoanAPIoradataresource.

Inanutshell,instancesoftheHttpclass(whichhasbeenimplementedasanInjectableresourceandcanthereforebeusedinourclassesconstructorsjustbyinjectingitasadependencyprovider)exposeaconnectionmethodnamedrequest()toperformanytypeofhttpconnection.TheAngular2teamhascreatedsomesyntaxshortcutsforthemostcommonrequestoperations,suchasGET,POST,PUT,andeveryexistingHTTPverb.So,creatinganasyncHttprequestisaseasyasthis:

varrequestOptions=newRequestOptions({

method:RequestMethod.Get,

url:'/my-api/my-data-store.json'

});

varrequest=newRequest(requestOptions);

varmyHttpRequest:Observable<Response>=http.request(request);

Also,allofthiscanbesimplifiedintoasinglelineofcode:

varmyHttpRequest:Observable<Response>=http.get('/my-api/my-data-store.json');

Aswecansee,theHttpclassconnectionmethodsoperatebyreturninganObservablestreamofResponseobjectinstances.ThisallowsustomapanddigesttheoutputoftheHttpcallassoonasitisavailableandsubscribeObserverstothestream,whichwillprocesstheinformationaccordinglyonceitisreturned,asmanytimesasrequired:

myHttpRequest.map(response:Response=>response.json())

.subscribe(data=>console.log(data));

Intheprecedingexample,wemaptheresponsesemittedbytheObservableeventstream(aswecansee,thosearebasicallyinstancesoftheResponseclassprovidedbyAngular2)toanewcollectionofeventsrepresentingJSONinstancesofeachresponse.Thisisdonebyleveragingthejson()methodoftheResponseclass,whichwewillcoverlateroninthischapter.ThenwesubscribetotheresultingstreamandthrowtheoutputoftheHTTPcalltotheConsoleonceourObserverreceivesthedatanotification.

Bydoingthis,wecanrespawntheHttprequestasmanytimesasweneed,andtherestofourmachinerywillreactaccordingly.WecanevenmergetheeventstreamrepresentedbytheHttpcallwithotherrelatedcalls,andcomposemorecomplexObservablestreamsanddatathreads.Thepossibilitiesareendless.

www.EBooksWorld.ir

Page 238: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

WhentousetheRequestandRequestOptionsArgsclassesWementionedtheRequestandRequestOptionsclasseswhileintroducingtheHttpclassatfirst.Onaregularbasis,youwillnotneedtomakeuseoftheselow-levelclasses,mostlybecausetheshortcutmethodsprovidedbytheHttpclassabstracttheneedtodeclaretheHTTPverbinuse(GET,POST,andsoon)andtheURLwewanttoconsume.Withthisbeingsaid,youwillsometimeswanttointroducespecialHTTPheadersinyourrequestsorappendquerystringparametersautomaticallytoeachrequest,forargument'ssake.Thatiswhytheseclassescanbecomequitehandyincertainscenarios.ThinkofausecasewhereyouwanttoaddanauthenticationtokentoeachrequestinordertopreventunauthorizedusersfromreadingdatafromoneofyourAPIendpoints.

Inthefollowingexample,wereadanauthenticationtokenandappenditasaheadertoourrequesttoadataservice.Contrarytoourexample,wewillinjecttheoptionshashobjectstraightintotheRequestconstructor,skippingthestepofcreatingaRequestOptionsobjectinstance.Angular2providesawrapperclassfordefiningcustomheadersaswell,andwewilltakeadvantageofitinthisscenario.Let'sfigureoutthatwedohaveanAPIthatexpectsallrequeststoincludeacustomheadernamedAuthorization,attachingtheauthTokenthatwereceivedwhenloggingintothesystem,whichwasthenpersistedinthebrowser'slocalstoragelayer,forinstance:

varauthToken=window.localStorage.getItem('auth_token');

varheaders=newHeaders();

headers.append('Authorization',`Token${authToken}`);

varrequest=newRequest({

method:RequestMethod.Get,

url:'/my-api/my-secured-data-store.json',

headers:headers

});

varauthRequest:Observable<Response>=http.request(request);

Again,wewouldliketonotethatapartfromthisscenario,youwillseldomneedtocreatecustomrequestconfigurations,unlessyouwanttodelegatethecreationofrequestconfigurationsinafactoryclassormethodandreusethesameHttpwrapperallthetime.Angular2givesyoualltheflexibilitytogoasfarasyouwishwhenabstractingyourapplications.

www.EBooksWorld.ir

Page 239: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

TheResponseobjectAswehavealreadyseen,theHTTPrequestsperformedbytheHttpclassreturnanobservablestreamofResponseclassinstances.SimilartotheRequestobject,youwillrarelyfindyourselfinneedofinstantiatingthisclass.However,understandingtheResponseclassinterfaceisquiteusefulinordertounderstandthestatusofourrequest,handleconnectionerrors,andproperlydigesttheinformationreturnedinthestream.

Inourfirstexample,wemappedthecontentofthestreamasastreamofJSONobjectsbyexecutingthejson()method.ThismethodparsestheresponsebodyasaJSONobject,orraisesanexceptionifsuchabodycannotbeparsed.Besidesthismethod,theResponseclassexposesthetext()method,whichwillparseandreturntheresponsebodyasaplainstring.

Let'sfigurethisout:wehaveacomponentthatexposesastringfieldnamedbio,whichisrenderedinthecomponent'stemplate.WemightwanttoservethisbiopropertyfromaRESTAPI.Hence,wecouldimplementsuchfunctionalityinafewlinesofcode,likethis:

http.get('/api/bio')

.map(res:Response=>res.txt)

.subscribe(bio=>this.bio=bio);

TheResponseobjectexposesotherminormethodsandsomeinterestingproperties,suchasthenumericstatusproperty,whichinformsofthestatuscodereturnedbytheserver.ThebytesLoadedandtotalBytesnumericpropertiesbecomequiteusefulwhenscaffoldingpreloadnotifiersinprogressevents.Specialmentiongoestotheheadersproperty,whichreturnsanobjectbasedontheHeadersclass(https://fetch.spec.whatwg.org/#headers-class)oftheFetchAPI.

CoverageforallthemethodsandpropertiesavailableintheResponseclassAPIisdefinitelybeyondthescopeofthisbook,asyouwillnotbeusingthoseonaregularbasis,butweencourageyoutoexpandyourknowledgeonthesubjectbyvisitingtheofficialdocumentation(https://angular.io/docs).

www.EBooksWorld.ir

Page 240: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

HandlingerrorswhenperformingHttprequestsHandlingerrorsraisedinourrequestsbyinspectingtheinformationreturnedintheResponseobjectisactuallyquitesimple.WejustneedtoinspectthevalueofitsBooleanproperty,whichwillreturnfalseiftheHTTPstatusoftheresponsefallssomewhereoutsideofthe2xxrange,clearlyindicatingthatourrequestcouldnotbeaccomplishedsuccessfully.Wecandouble-checkthatbyinspectingthestatuspropertytounderstandtheerrorcodeorthetypeproperty,whichcanassumethefollowingvalues:basic,cors,default,error,oropaque.InspectingtheresponseheadersandthestatusTextpropertyoftheResponseobjectwillprovideinsightfulinformationabouttheoriginoftheerror.

Allinall,wearenotmeanttoinspectthosepropertiesoneveryresponsemessageweget.Angular2providesanObservableoperatortocatcherrors,injectinginitssignaturetheResponseobjectwerequiretoinspectthepreviousproperties:

http.get('/api/bio')

.map(res:Response=>res.txt)

.subscribe(bio=>this.bio=bio)

.catch(error:Response=>console.error(error));

Inanormalscenario,youwouldwanttoinspectmoredataratherthantheerrorproperties,asidefromloggingthatinformationinamoresolidexceptiontrackingsystem.

www.EBooksWorld.ir

Page 241: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

InjectingtheHttpclassandtheHTTP_PROVIDERSmodulessymbolTheHttpclasscanbeinjectedinourowncomponentsandcustomclassesbyleveragingAngular'suniquedependencyinjectionsystem.So,ifweeverneedtoimplementHTTPcalls,weneedtoimporttheclassandbinditasadependencyinthelistofcomponentordirectiveproviders,likethis:

import{Component}from'@angular/core';

import{Http}from'@angular/http';

@Component({

selector:'bio',

providers:[Http],

template:'<div>{{bio}}</div>'

})

classBiography{

bio:string;

constructor(http:Http){

http.get('/api/bio')

.map((res:Response)=>res.text())

.subscribe((bio:string)=>this.bio=bio);

}

}

Inthecodeprovided,wejustfollowupwiththebioexamplethatwepointedoutintheprevioussection.NotehowweareimportingtheHttptypeandinjectingitasadependencythroughtheproviderscollectionpropertyoftheComponentdecorator.

Usually,weneedtoperformmultipleHTTPcallsindifferentpartsofourapplication,soit'susuallyrecommendedtoincludetheHttpclassintherootinjectorratherthanonapercomponentinjectorbasis.TodosoandkeepinginmindthattheHttpclassmightrequireotherproviderssuchastheRequestOptionsclass,Angular2providesasetofinjectablesymbolswrappedinsidetheHTTP_PROVIDERStoken.

WerecommendthatyouusethissetinsteadoftheHttpclasstoinjectthedependenciesrequiredtoperformHTTPrequeststhroughoutyourapplication.Foryourconvenience,itisadvisedtodosoattherootcomponentprovidersproperty,soitsinjectorwillmaketheprovideravailablethroughoutitschildcomponentstree.Takingourpomodoroappasanexample,wewouldneedtoimportitattheAppComponentcodeunitandinjectitintothecomponentprovidersrightaway:

app/app.component.ts

import{Component}from'@angular/http';

import{TIMER_DIRECTIVES}from'./timer/timer';

import{TASKS_DIRECTIVES}from'./tasks/tasks';

import{SHARED_PROVIDERS}from'./shared/shared';

import{HTTP_PROVIDERS}from'@angular/http';

www.EBooksWorld.ir

Page 242: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

@Component({

selector:'pomodoro-app',

directives:[TIMER_DIRECTIVES,TASKS_DIRECTIVES],

providers:[SHARED_PROVIDERS,HTTP_PROVIDERS],

...

})

exportdefaultclassAppComponent{}

www.EBooksWorld.ir

Page 243: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Arealcasestudy–servingObservabledatathroughHTTPInthepreviouschapter,werefactoredourentireappintomodels,services,pipes,directives,andcomponentfiles.OneofthoseserviceswaspreciselytheTaskServiceclass,whichisthebreadandbutterofourapp,sinceitdeliversthedatathatweneedtobuildourtasklistandotherrelatedcomponents.

Inourexample,theTaskServiceclasswascontainedwithintheinformationwewantedtodeliver.Inareal-worldscenario,youneedtofetchthatinformationfromaserverAPIorbackendservice.Let'supdateourexampletoemulatethisscenario.First,wewillremovethetaskinformationfromtheTaskServiceclassandwrapitintoanactualJSONfile.Let'screateanewJSONfileinsidethesharedfolderandpopulateitwiththetaskinformationthatwehadhardcodedintheoriginalTaskService.tsfile,nowinJSONformat,though:

app/shared/data/raw-tasks.json

[{

"name":"CodeanHTMLTable",

"deadline":"Jun232015",

"pomodorosRequired":1

},{

"name":"Sketchawireframeforthenewhomepage",

"deadline":"Jun242016",

"pomodorosRequired":2

},{

"name":"StyletablewithBootstrapstyles",

"deadline":"Jun252016",

"pomodorosRequired":1

},{

"name":"ReinforceSEOwithcustomsitemap.xml",

"deadline":"Jun262016",

"pomodorosRequired":3

}]

Withthedataproperlywrappedinitsownfile,wecanconsumeitasifitwereanactualbackendservicefromourTaskServiceclientclass.However,wewillneedtoconductrelevantchangesinourmain.tsfileforthat.ThereasonisthatdespiteinstallingtheRxJSbundlewheninstallingalltheAngular2peerdependencies,thereactivefunctionaloperators,likemap(),donotbecomeavailablestraightaway.Wecouldimportallofthematoncebyinsertingthefollowinglineofcodeatsomestepatthebeginningofourapplicationinitializationflow,suchasthebootstrappingstageinmain.ts:

import'rxjs/Rx';

However,thatwouldimportallthereactivefunctionaloperators,whichwillnotbeusedatallandwillconsumeanunnecessarilyhugeamountofbandwidthandresources.Instead,the

www.EBooksWorld.ir

Page 244: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

conventionmarkstoimportonlywhatisneeded,soappendthefollowingimportlineatthetopofthemain.tsfile:

app/main.ts

import'rxjs/add/operator/map';

import{bootstrap}from'@angular/platform-browser-dynamic';

importAppComponentfrom'./app.component';

bootstrap(AppComponent,[]);

Whenareactiveoperatorisimportedthisway,itgetsautomaticallyaddedtotheObservableprototype,beingthenavailableforusethroughouttheentireapplication.

Withallthedependenciesproperlyinplace,thetimehascometorefactorourTaskService.tsfile.Opentheservicefileandlet'supdatetheimportstatementsblock:

app/shared/services/task.service.ts

import{Injectable}from'@angular/code';

import{Task}from'../shared';

import{Http,Response}from'@angular/http';

import{Observable}from'rxjs/Observable';

First,weimportintheHttpandResponsesymbolssothatwecanannotateourobjectslateron.RememberanywaythattheHTTTP_PROVIDERStokenhasalreadybeeninjectedatthetoprootcomponent.TheObservablesymbolisimportedfromtheRxJSlibrarysothatwecanproperlyannotatethereturntypesofourasyncHttprequests.

Nowwewillreplacetheexistingimplementationwiththefollowingone.Basically,theTaskServicemodulesevolveintoaservicewithstatethatkeepsexposingataskStorepropertywherewecanfetchthetasksdataset.ItalsofeaturesanObservablepropertyrepresentingataskfeedwecansubscribetoinordertokeepuptodateofanynewtaskthatcouldbecreatedinthefuture.

TheconstructornowfeaturestheHttpdependencyinjectedandboundasaprivatemembertothehttpfield:

app/shared/services/task.service.ts

@Injectable()

exportdefaultclassTaskService{

taskStore:Task[]=[];

taskFeed:Observable<Task>;

privatetaskObserver:any;

privatedataUrl='/app/shared/data/raw-tasks.json';

constructor(privatehttp:Http){

this.taskFeed=newObservable(observer=>{

this.taskObserver=observer;

www.EBooksWorld.ir

Page 245: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

});

this.fetchTasks();

}

privatefetchTasks():void{

this.http.get(this.dataUrl)

.map(response=>response.json())

.map(stream=>stream.map(res=>{

return{

name:res.name,

deadline:newDate(res.deadline),

pomodorosRequired:res.pomodorosRequired,

queued:res.queued

}

}))

.subscribe(

tasks=>{

this.taskStore=tasks;

tasks.forEach(task=>this.taskObserver.next(task))

},

error=>console.log(error)

);

}

}

Let'stakeaminutetoexaminethenewserviceimplementation.WekeepthetaskStorepropertyasusual,andwepopulateitoncewefetchthewholegraphoftasksavailableuponinstantiatingtheservicebycallingthefetchTasks()privatemethodfromtheconstructor.ButwehaveintroducedanObservablememberandanObserverfieldtoo.

Let'sexplaintheirroleindetail.Intheconstructor,weinitializetheObservableinstanceandalsoassigntheObservable'sbuilt-inobservertoourObservermember.Thisway,everytimewewanttonotifyanewtaskinthetasksObservablesequencetotheservicesubscribers,wejustneedtoproceedtoexecutethenext()methodoftheObservermember.Therefore,asasingleton,thetasksserviceexposesadatastorethatispopulatedthemomentitisinstantiatedforthefirsttime,becomingsuchdatafullyavailableforallcomponentsthatwilleventuallyconsumeit.

Ontheotherhand,externalconsumersoftheservicecansubscribetothetaskFeedpropertyandreceivepromptnotificationseverytimeanewtaskisaddedtothesequence.WecouldspawnawebsocketsclientandleveragetheObserverAPItoemitnewtasksthroughourObservablesequenceeverytimeanewtaskiscreatedontheserverside.ThecomponentssubscribingtothatObservablesequencewouldreceivethechangesandreflectthosechangesintheirstateautomaticallywithnoadditionallogicrequired.

Aswecansee,thisdatahandlingpatterngoesastepbeyondthemereasynchronousdataconsumptionflowweareusedtowithpromisesandcallbacks,easingdataupdatesandallowingforabettermanagementofinformationoffline.

Let'sfinishourimplementationbyupdatingtheTaskComponentmodule.Sinceweareusing

www.EBooksWorld.ir

Page 246: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

thesameAPIwealreadyhadpreviously,thechangesareminimalandarelimitedtotweakingthengOnInithookmethod:

app/tasks/tasks.component.ts

ngOnInit():void{

this.updateQueuedPomodoros();

this.taskService.taskFeed.subscribe(newTask=>{

this.tasks.push(newTask);

this.updateQueuedPomodoros();

});

}

WeneedtosubscribetothetasksfeedsinceObservablesarecold.Theyarenotinitializeduntilsomeclientactuallysubscribestothem,andtheunderlyingObserverofthetaskFeedObservableatTaskServiceisactuallyrequiredwithintheinternalsubscriptiontothehttp.get()connection.Notsubscribingtoitwouldturnintoanexception.Withthischange,wealsoensuredthatourtaskstablewillremainuptodateshouldanewtaskaddedtotheoveralltasksdatastore,withouthavingtorepopulatethewholetasksproperty.ThecalltoupdateQueuedPomodoros()isintroducedinthecallbackaswellshouldanynewtaskbequeuedbydefault.

Now,executethecodeandyouwillseethetasksseamlesslyrenderedonthetable.

www.EBooksWorld.ir

Page 247: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

AddingtaskstoourtasksserviceUnfortunately,ourcodeworksinonewayonlynow:wecanconsumedatafromthetasksJSONfilebutwecannotappendnewtasksifrequired.Ideally,weshouldbeabletoappendourowntasksuponrequestandhavethewholesystemreactingtothesechanges,solet'supdateourimplementationfortheTaskServiceclasstoinsertandaddataskmethod.Appendthefollowingmethodattheendoftheserviceclass:

app/shared/services/task.service.ts

addTask(task:Task):void{

this.taskObserver.next(task);

}

Thisnewmethodalignswithwhatwepointedoutalreadyaboutreactiveserviceinterfaces.AddinganewtaskobjectwillturnintoaneweventpublishedintheObservableeventsstreamoftasks,soanyactivecomponentwhichisalreadyconsumingthedatagraphandissubscribedtoitschangeswillreceiveapromptnotificationofthenewtaskcreatedandthereforecanupdateitsstateaccordingly.

Tryitoutyourself!

Tip

Inallfairness,anynewitemrequestshouldbehandledbymeansofaPOSTrequestandallthepreviousoperationsshouldbeperformeduponresolvingtheHttp.post()request,likethis:

constbody=JSON.stringify(task);

constheaders=newHeaders();

headers.append('Content-Type','application/json');

addTask(task:Task):void{

this.http.post(this.dataUrl,body,headers)

.map(response=>response.json())

.subscribe((task:Task)=>

this.taskObserver.next(task);

}

);

}

Sinceserver-sideimplementationsareoutofthescopeofthisbook,wewillleaveituptoyoutoexperimentwiththeHttpmoduleagainstRESTfulAPIs.

www.EBooksWorld.ir

Page 248: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

SummaryAswepointedoutatthebeginningofthischapter,ittakesmuchmorethanasinglechaptertocoverindetailallthegreatthingsthatcanbedonewiththeAngular2HTTPconnectionfunctionalities,butthegoodnewsisthatwehavecoveredprettymuchallthetoolsandclassesweneedtodoso.

Therestisjustlefttoyourimagination,sofeelfreetogotheextramileandputallofthisknowledgeintopracticebycreatingbrandnewTwitterreaderclients,newsfeedwidgets,orblogengines,andassemblingallkindsofcomponentsofyourchoice.Thepossibilitiesareendless,andyouhaveassortedstrategiestochoosefrom,rangingfromPromisestoObservables.YoucanleveragetheincrediblefunctionalitiesoftheReactiveFunctionalextensionsandthetinybutpowerfulHttpclass.

Aswehavealreadyhighlighted,theskyisthelimit.Butwestillhavealongandexcitingwayahead.Nowthatweknowhowtoconsumeasynchronousdatainourcomponents,let'sdiscoverhowwecanprovideabroaderuserexperienceinourapplicationsbyroutingusersintodifferentcomponents.Wewillcoverthisinthenextchapter.

www.EBooksWorld.ir

Page 249: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Chapter7.RoutinginAngular2Inthepreviouschapters,wedidagreatjobseparatingconcernsinourapplicationsandaddingdifferentlayersofabstractiontoincreasethemaintainabilityinourPomodoroapp.However,wehaveneglectedthevisualsideofthingsandtheuserexperiencepart.

Atthismoment,ourUIisbloatedwithcomponentsandstuffscatteredacrossasinglescreen,andweneedtoprovideabetternavigationalexperienceandalogicalwaytochangetheapplication'sstateintuitively.

Thisisthemomentwhereroutingacquiresspecialrelevanceandgivesustheopportunitytobuildanavigationalnarrativeforourapplications,allowingustosplitthedifferentareasofinterestintodifferentpagesthatareinterconnectedbyagridoflinksandURLs.

However,ourapplicationisonlyasetofcomponents,sohowdowedeployanavigationschemebetweenthem?TheAngular2routerwasbuiltwithcomponentizationinmind.Wewillseehowcanwecreateourcustomlinksandmakecomponentsreacttotheminthefollowingpages.Inthischapter,wewill:

DiscoverhowtodefineroutestoswitchcomponentsonandoffandredirectthemtootherroutesTriggerroutesandloadcomponentsinourviewsdependingontherequestedroutePassparameterstoourcomponentsstraightfromourroutesLookatthedifferentcomponentlifecyclehooksbasedontheroutingstagesDefinedifferentURLrepresentationstrategies

www.EBooksWorld.ir

Page 250: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

AddingsupportfortheAngular2routerSameaswedidwhenoverviewingtheHttpdirectivesandproviders,allthetypesandtokensrequiredforimplementingroutingsupportinourapplicationscomefromitsownspecificbarrel.ThisbarrelwasalreadyinstalledandconfiguredbackinChapter1,CreatingOurVeryFirstComponentinAngular2,althoughwefoundtwobarrelsrelatedtoroutinginourinstallationandfurtherconfiguration:@angular/routerand@angular/router-deprecated.ThisisbecausetheAngularteamintroducedarevampedroutingmechanismwhenswitchingversionsfromBetatoReleaseCandidate.Thisnewroutingmachinery,whichaimstoreplacetheroutingAPIthatAngularhadbeenimplementingsinceitsAlphaversion,alsointroducedrelevantbreakingchangeswithitspreviousincarnation.InordertoensurethatapplicationsbuiltontopofthepreviousroutercouldupgradetoAngular2ReleaseCandidateseamlesslyandpreventmajorissues,theAngularteammadeavailableasnapshotoftheBetaRouter,availablefromthe@angular/router-deprecatedbarrel.Thatiswhyweinstalledandconfiguredtworoutingpackages.

Tip

Atthetimeofclosingthewritingofthisbook,thenewAngular2Routerisstillinaveryearlystageandlackssupportforseveralfunctionalitiesthatarecommonlyusedinourwebapplicationsonadailybasis.ThatiswaythischapterwillfocusondevelopingapplicationsontopofthedeprecatedrouteryetwewillhighlightthedifferencesbetweenitsAPIandthenewerAngular2Routerwhereaspossible.Allinallthedifferencesareminimalandlearninghowtousethedeprecatedrouterinterfacewillbecomepricelessforgettinguptospeedwiththenewrouteronceitbecomesfinal.Pleaserefertothebookcoderepositorytocheckthelatestversionofthecode.

WealsoneedtoinformAngularaboutthebasepathwewanttouse,soitcanproperlybuildandrecognizetheURLsastheuserbrowsesthewebsite,aswewillseeinthenextsection.Ourfirsttaskwillbetoinsertabasehrefstatementwithinour<HEAD>element.Appendthefollowinglineofcodeattheendofyourcodestatementinsidethe<head>tag:

index.html

<basehref="/">

Thebasetaginformsthebrowseraboutthepathitshouldfollowwhileattemptingtoloadexternalresources,suchasmediaorCSSfiles,onceitgoesdeeperintotheURLhierarchy.

Now,wecanstartplayingaroundwithallthegoodiesexistingintherouterlibrary.Priortothis,wewouldneedtoinformthedependencyinjectorabouthowitcaninstantiatethetokenswewillrequirelateronwhileimplementingtheroutingfeaturesinourcomponents.AlltheseprovidersareaccessiblefromtheROUTER_PROVIDERSsymbol.InasimilarfashionaswedidwithHTTP_PROVIDERS,weneedtodeclareitintheproviderspropertyofthetoprootcomponentsothatitisavailableforallitschildcomponents'injectors.

www.EBooksWorld.ir

Page 251: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Openyourtopcomponentmoduleandappendthefollowingimportstatementtotheexistingblockofimportedsymbols.Then,addittotheproviderspropertyofthecomponentdecorator:

app/app.component.ts

...

import{SHARED_PROVIDERS}from'./shared/shared';

import{HTTP_PROVIDERS}from'@angular/http';

import{ROUTER_PROVIDERS}from'@angular/router-deprecated';

@Component({

selector:'pomodoro-app',

directives:[ROUTER_DIRECTIVES],

providers:[SHARED_PROVIDERS,HTTP_PROVIDERS,ROUTER_PROVIDERS],

template:`

...

www.EBooksWorld.ir

Page 252: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

SettinguptherouterserviceWiththeprovidersanddirectivesinplace,ourfirststepwillbetoturnourmainhostcomponentintoaroutercomponent.Basically,anycomponentcanbecomearoutingcomponentjustbyconformingtothefollowingrequirements:

Justlikethecomponentclassisflaggedwitha@Componentdecorator,[email protected]@RouteConfigdecoratorisconfiguredwithanarrayofRouteDefinitions,whicharebasicallyobjectliteralsdefiningapathidentifiedwithanameandpointingtoacomponenttype.Thecomponentdecoratedwiththe@RouteConfigdecoratoristhensupposedtoincludeaRouterOutletdirectiveinitstemplate.Thiselementwillbecometheplaceholderwherethecomponentswillbeloadedandrendereduponloadingaroutepointingtoeachofthem,removinganypreviouscomponentexistingthere,ifany.

Inthissense,itisrighttosaythattherouterwatchesforstatechangesinthebrowserURLandthensearchesforaRouteDefinitionobjectwhosepathpropertymatchestheexistingURL.Then,itinstantiatesthecomponentdefinedinsuchroutedefinitioninsidetheplaceholderrepresentedbytherouteroutletdirective,whichismeanttoliveinthetemplatebelongingtothecomponentdecoratedwiththatrouterconfiguration.

Let'sseeallthisthrougharealexample.Aswementionedwhileintroducingthischapter,ourapplicationneedsabetternavigationarchitectureinordertobemoreusableandintuitive.AftersplittingallourlogicintodifferentcomponentsinChapter5,BuildinganApplicationwithAngular2Components,wewilldefinedifferentroutestouseeachone,implementingthefollowinglogic:

Theuserreachesourappandchecksthecurrentlistingofthetaskspendingtobedone.TheusercanschedulethetaskstobedoneinordertogettherequiredtimeestimationforthenextPomodorosession.Ifdesired,theusercanjumpontoanotherpageandseeacreatetaskform(wewillcreatetheform,butwillnotimplementitseditingfeaturesuntilthenextchapter).TheusercanchooseanytaskatanytimeandbeginthePomodorosessionrequiredtoaccomplishit.Theusercanmovebackandforthacrossthepagessheorhehasalreadyvisited.

www.EBooksWorld.ir

Page 253: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

BuildinganewcomponentfordemonstrationpurposesSofar,wehavebuilttwowell-differentiatedcomponentswecanleveragetodeliveramultipagenavigation.Butinordertoprovideabetteruserexperience,wemightneedathirdone.WewillnowintroducetheformcomponentwewillbeelaboratingmorethoroughlyinChapter8,FormsandAuthenticationhandlinginAngular2,asawaytohavemorenavigationoptionsinourexample.

Wewillcreateacomponentinourtasksfeaturefolder,anticipatingtheformwewilluseinthenextchaptertopublishnewtasks.Createthefollowingfilesinthelocationspointedoutforeachone:

app/tasks/task-editor.component.ts

import{Component}from'angular2/core';

import{ROUTER_DIRECTIVES}from'angular2/router';

@Component({

selector:'pomodoro-tasks-editor',

directives:[ROUTER_DIRECTIVES],

templateUrl:'app/tasks/task-editor.component.html'

})

exportdefaultclassTaskEditorComponent{

constructor(){}

}

app/tasks/task-editor.component.html

<formclass="container">

<h3>TaskEditor:</h3>

<divclass="form-group">

<inputtype="text"

class="form-control"

placeholder="Taskname"

required>

</div>

<divclass="form-group">

<inputtype="Date"

class="form-control"

required>

</div>

<divclass="form-group">

<inputtype="number"

class="form-control"

placeholder="Pomodorosrequired"

min="1"

max="4"

required>

</div>

<divclass="form-group"><inputtype="checkbox"name="queued">

www.EBooksWorld.ir

Page 254: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

<labelfor="queued">thistaskbydefault?</label>

</div>

<p>

<inputtype="submit"class="btnbtn-success"value="Save">

<ahref="/"class="btnbtn-danger">Cancel</a>

</p>

</form>

Thisisthemostbasicdefinitionofacomponent,butwewillalsobringtheROUTER_DIRECTIVESsymbolfromtherouterlibrary.Thiswillprovideussupport,aswewillseelateron,toincluderoutingdirectivesinourHTMLtemplate.Thiswillbeusedtointroducelinksinourtemplatetojumptoothercomponents,aswewillseeshortly.Lastbutnotleast,weneedtoexposethisnewcomponentfromourfeaturefolderfacade:

app/tasks/tasks.ts

importTasksComponentfrom'./tasks.component';

importTaskEditorComponentfrom'./task-editor.component';

importTaskTooltipDirectivefrom'./task-tooltip.directive';

constTASKS_DIRECTIVES:any[]=[

TasksComponent,

TaskEditorComponent,

TaskTooltipDirective

];

export{

TASKS_DIRECTIVES,

TasksComponent,

TaskEditorComponent,

TaskTooltipDirective

};

www.EBooksWorld.ir

Page 255: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

ConfiguringtheRouteConfigdecoratorwiththeRouteDefinitioninstancesInordertoachievethesegoals,weneedtostartbuildingourtoprouter,whichwillbeinchargeofkickingofftheroutes'scaffolding.Thelogicalpathbeginsinourtoprootcomponent.Openitsfilemoduleandimportthefollowingtokens,rightnexttotheROUTER_PROVIDERSsymbolweimportedatthebeginningofthischapter.Thecodeisasfollows:

app/app.component.ts

...

import{

ROUTER_PROVIDERS,

RouteConfig,

ROUTER_DIRECTIVES

}from'@angular/router-deprecated';

import{TimerComponent}from'./timer/timer';

import{

TasksComponent,

TaskEditorComponent}from'./tasks/tasks';

...

TheRouteConfigrepresentsthedecoratortypethatwillturnourcomponentintoaroutercomponent.TheROUTER_DIRECTIVESsymbolwrapstheviewdirectiveswewillneedshortlytolinktotheseroutes.Wealsoimportthetokensofallthethreecomponentswewillbedealingwith.Aswewillshortlysee,eachrouteneedstodeclarethetypeofthecomponentweareroutingthebrowserto.

Let'scontinuebyreplacingthedirectivesinourAppComponentmodulebytheROUTE_DIRECTIVESsymbol,sincewewillnotneedtodeclarethefacadetokensofthecomponentsthatlivedinitstemplateanymore.Therouterwillhandlethisforus:

app/app.component.ts

@Component({

selector:'pomodoro-app',

directives:[ROUTER_DIRECTIVES],

providers:[SHARED_PROVIDERS,HTTP_PROVIDERS,ROUTER_PROVIDERS],

template:`

...

})

Now,let'sexpandthecomponentclassdefinitionwiththeRouteConfigdecoratorbyappendingthefollowingdecoratorrightafterthe@Componentdecoratorblockandbeforetheclassstatement:

app/app.component.ts

www.EBooksWorld.ir

Page 256: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

@RouteConfig([

{path:'',

name:'TasksComponent',

component:TasksComponent

},

{

path:'tasks/editor',

name:'TaskEditorComponent',

component:TaskEditorComponent

},{

path:'timer',

name:'TimerComponent',

component:TimerComponent

}

])

exportdefaultclassAppComponent{}

Aswepointedoutatthebeginningofthischapter,theRouteConfigdecoratormustbepopulatedwithanarrayofRouteDefinitionobjects,eachonespecifyingapaththat,oncereachedbytheuser,willenablethecomponentwhosetypewehavedefinedinthecomponentproperty.

Tip

Note:ThenewRouterreplacedthe@RouteConfigdecoratorbythe@Routesdecorator.Thenamepropertyisremovedfromtheroutedefinitionsschemaandroutematchingisperformedjustbycheckingthepathvalue.

Inthepreviousexample,ourhostcomponentwillreacttothreedifferentroutesandthusservetheTasksComponentitem,theTimerComponentitem,ortheTaskEditorComponentitemdependingontheroute'spath.

Tip

HerewestumbleuponanothercommonconventioninthepreviousversionfotheAngular2router:namingrouteswiththesamenameasthecomponenttheyreferto.Aswewillseeshortly,wewilluseeachroutenameforpopulatingthelinkspointingtoeachresource,sonamingroutesafterthecomponenttheywillactivatebecomesprettyusefulandintuitivewhenitcomestoassessingthetargetofeachlinkfoundinthetemplate.Thisconventionisnolongerenforcedinthenewrouter,sinceonlypathsareusedtoperformroutematching.

Therearetwoquestionsatthispoint:wherewillthesecomponentsberenderedandhowwillwetriggereachroute?Inordertoanswerthesequestions,weneedtolookintotherouterdirectivesindetail.

www.EBooksWorld.ir

Page 257: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Therouterdirectives–RouterOutletandRouterLinkTheROUTER_DIRECTIVESsymbolgivesusaccesstotheonlytwodirectiveswewillneedinourapplications.

First,theRouterOutletdirectiveistheplaceholderdirectivewherethedifferentcomponentswhosepathshavebeennavigatedtobytheuserwillberendered.TheRouterLinkisusedasanattributedirectivetohelptheHTMLcontrolsbehaveasanchorsorlinkbuttonsleadingtothedifferentroutesbyspecifyingtheuniquenameeachrouteisconfiguredwith,aswewillseenext.

Inthepreviouschapters,weconfiguredourtoprootcomponenttemplatetodisplayacutenavbarheader,followedbythecustomelementsrepresentingthePomodorotimerandthetaskslist.Now,wewillstriptheHTMLtemplateoutfromthecomponentdecoratordefinitionandsaveitintoitsowntemplatefile,inordertoaccessitmoreconvenientlywheneditingtheHTMLisrequired.Also,wewillrefactoritintoarouter-friendlycomponenttemplatewithlinkspointingtothedifferentviewsorstatesourapplicationcanfeature,asfollows:

app/app.component.ts

...

@Component({

selector:'pomodoro-app',

directives:[ROUTER_DIRECTIVES],

providers:[SHARED_PROVIDERS,HTTP_PROVIDERS,ROUTER_PROVIDERS],

templateUrl:'app/app.component.html'

})

...

app/app.component.html

<navclass="navbarnavbar-defaultnavbar-static-top">

<divclass="container">

<divclass="navbar-header">

<strongclass="navbar-brand">MyPomodoroApp</strong>

</div>

<ulclass="navnavbar-navnavbar-right">

<li><a[routerLink]="['TasksComponent']">Tasks</a></li>

<li><a[routerLink]="['TimerComponent']">Timer</a></li>

<li><a[routerLink]="['TaskEditorComponent']">

PublishTask</a>

</li>

</ul>

</div>

</nav>

<router-outlet></router-outlet>

Asyoucanseeinthetemplate,thelocationwherethecomponentsusedtolivehasbeenreplacedbythe<router-outlet>directive,andthreenewlinkscompoundupourbeautiful

www.EBooksWorld.ir

Page 258: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

navbar.Reloadthepageandseehowourtaskslistisrenderedonthescreen,andthenclickontheTimerlink.Awesome!TheTimerComponentitemjustloadedonthescreen.Clickontheotherlinkorhitbackonyourbrowsertoseehowyoucanjumpacrossthedifferentcomponentsseamlessly.

Let'stakealookattheselinksmoreclosely,takingthelinkpointingtothetimerasanexample:

<a[routerLink]="['TimerComponent']">Timer</a>

ThemorphologyoftherouterLinkdirectiveisprettyself-explanatory.Ontheright-handsideoftheequalsymbol,itexpectsanarrayofroutenamescorrespondingtothenamedrouteand,optionally,thesubrouteswithintheformerthatwewanttonavigateto.Mostofthetime,wewilljustseeoneuniquestringvalue.However,thearraycanallocatemanyvaluesdependingonwhetherthecomponentwhosenamedpathwearepointingtohostsitsownrouterwithnamedroutesaswell.Inthiscase,thesubroutenameswewanttoloadwillbedeclarednextinthestringsarray.Thisiswhyitissoconvenientandrecommendedtonameourroutesafterthecomponentstheypointat.

Note

ThenewReleaseCandidateRouterdeprecatesnamedroutes,favoringURLpathsinstead.Therefore,the[routerLink]directivewillexpectafullpathasavalue.

TherouterLinkdefinitionalsoleavesroomtoaddparametersdeclaratively,sowecantriggerdynamicroutesatruntime.Wewilltapintoallthesefeaturesthroughoutthenextsections,butnowweneedtoansweroneimportantquestion.Whatifwewanttonavigatetoacomponentimperativelywithoutactuallyclickingonalink,butratherastheby-productofanactionperformedwithinthecomponent'scontrollerclass?

www.EBooksWorld.ir

Page 259: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

TriggeringroutesimperativelyPerhaps,youwouldliketojumponourtimerbyselectingthetaskwewanttoworkon.Wehavealreadysetupabehaviorthatdisplayedaqueuedlabelwhenevereachtaskwaspickedforbeingdone.Wewillleveragethesamebehaviortocreateaworkonbuttonthatwillredirecttheusertothetimercomponent.

First,let'sinjecttheRoutertypeasadependencyinourTasksComponentmodule,sowecangainaccessimperativelytoitsmethods.SincewealreadydeclaredtheROUTER_PROVIDERSsymbolwhilebootstrappingtheapplication,Angular2willtakecareofinjectingtheroutertypeifproperlydeclaredinourcomponent,solet'sdoit.Addthefollowingimportstatementatthetopofthecomponent,rightaftertheexistingones:

app/tasks/tasks.component.ts

import{Router}from'@angular/router-deprecated';

Now,let'supdatetheconstructortoinjecttheroutertype.Wewillmarktheconstructorasaprivateformsothatitbecomesprivatelyavailablefromthecomponentmembers:

app/tasks/tasks.component.ts

...

constructor(

privatetaskService:TaskService,

privatesettingsService:SettingsService,

privaterouter:Router){

this.tasks=this.taskService.taskStore;

this.today=newDate();

this.queueHeaderMapping=settingsService.pluralsMap.tasks;

this.timerMinutes=settingsService.timerMinutes;

}

...

Now,let'saddamethodrightbelowtheupdateQueuedPomodoros()method,whichwillleadtheuserimperativelytothetaskrouteuponexecutingit:

workOn():void{

this.router.navigate(['TimerComponent']);

}

Howdoweexecuteit?Let'sintroduceanewbuttoninourtable,nexttothetoggletaskbuttonatthelastcelloneachrow,withaclickhandlerpointingtotheprecedingmethod.Thecodeisasfollows:

app/tasks/tasks.component.html

<td>

<buttontype="button"class="btnbtn-defaultbtn-xs"

[ngSwitch]="task.queued"

www.EBooksWorld.ir

Page 260: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

(click)="toggleTask(task)">

<template[ngSwitchWhen]="false">

<iclass="glyphiconglyphicon-plus-sign"></i>

Add

</template>

<template[ngSwitchWhen]="true">

<iclass="glyphiconglyphicon-minus-sign"></i>

Remove

</template>

<templatengSwitchDefault>

<iclass="glyphiconglyphicon-plus-sign"></i>

Add

</template>

</button>

<buttontype="button"

class="btnbtn-defaultbtn-xs"

*ngIf="task.queued"

(click)="workOn()">

<iclass="glyphiconglyphicon-expand"></i>Start

</button>

</td>

AconvenientNgIfdirectivewilldisplaythebuttononlywhenrequired.Wehaveincludedaniconforcosmeticpurposes,butfeelfreetoremoveitorreplaceitbyanyotherglyphofyourchoice.

Nowreloadthetable,setoutanytasktobedone,andclickonthebuttonthatappears.Voila!Youwillberedirectedtothetimertobeginworkingonthattaskifdesired.

Note

Thenavigate()methodofthenewRouterwillexpectastringcontainingthefullpathinstead.

www.EBooksWorld.ir

Page 261: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

CSShooksforactiveroutesWehaveseenhowtoturnanylinkorDOMelementintoahyperlinkpointingtoanamedroutethatinstantiatesacomponent.However,itwouldbegreattoprovidesomekindofvisualcueabouttheactivelinkatanygiventime.ThatispreciselyoneofthesidefeaturesimplementedintherouterLinkdirective:whenevertheroutedefinedonarouterLinkdirectivebecomesactive(regardlessofwhethertheuserreachedthatroutedeclarativelyorimperatively),theelementwillbedecoratedwiththerouter-link-activeclass.WecanthereforeintroducespecificCSSdefinitionsinourcomponents'stylesheetstohighlightifaspecificlinkisactiveornot.

Wewillseeallthisfunctionalityinactionjustbytweakingourtopparentcomponentabit.Openthetoprootcomponentcontrollerclassfileandinsertthefollowingstylesheetinhtecomponentdecoratorconfiguration:

app/app.component.ts

@Component({

selector:'pomodoro-app',

directives:[ROUTER_DIRECTIVES],

providers:[SHARED_PROVIDERS,HTTP_PROVIDERS,ROUTER_PROVIDERS],

styles:[`

.router-link-active{

font-weight:bold;

border-bottom:2px#d9534fsolid;

}

`],

templateUrl:'app/app.component.html'

})

Nowreloadthebrowserandclickonanylinkornavigatetoatasktimerfromthetaskstable,keepinganeyeonthevisualstateofthetopnavbar.Theactivelinkwillbeproperlyenhancedwiththestylingwedefined.Ifyouinspectthecode,youwillseetherouter-link-activeclassrenderedontheactivelinkeachtime.

www.EBooksWorld.ir

Page 262: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

HandlingrouteparametersWehaveconfiguredprettybasicpathsinourroutessofar,butwhatifwewanttobuilddynamicpathswithsupportforparametersorvaluescreatedatruntime?Creating(andnavigatingto)URLsthatloadspecificitemsfromourdatastoresisacommonactionweneedtoconfrontonadailybasis.Forinstance,wemightneedtoprovideamaster-detailbrowsingfunctionality,soeachgeneratedURLlivinginthemasterpagecontainstheidentifiersrequiredtoloadeachitemoncetheuserreachesthedetailpage.

Wearebasicallytacklingadoubletroublehere:creatingURLswithdynamicparametersatruntimeandparsingthevalueofsuchparameters.Noproblem,theAngularrouterhasgotourbackandwewillseehowusingarealexample.

www.EBooksWorld.ir

Page 263: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

PassingdynamicparametersinourroutesWeupdatedthetaskslisttodisplayabuttonleadingtothetimercomponentpagewhenclicked.Butwejustloadthetimercomponentwithnocontextwhatsoeverofwhattaskwearesupposedtoworkononcewegetthere.Let'sextendthecomponenttodisplaythetaskwepickedpriortojumpingtothispage.

First,let'sgetbacktothetaskslistcomponenttemplateandupdatethesignatureofthebuttonthattriggersthenavigationtothetimercomponentinordertoincludetheindexofthetaskitemcorrespondingtothatloopiteration:

app/tasks/tasks.component.html

...

<buttontype="button"

class="btnbtn-defaultbtn-xs"

*ngIf="task.queued"

(click)="workOn(i)">

<iclass="glyphiconglyphicon-expand"></i>Start

</button>

...

RememberthatsuchanindexwasgeneratedateveryiterationoftheNgFordirectivethatrenderedthetablerows.Nowthatthecallincorporatestheindexinitssignature,wejustneedtomodifythepayloadofthenavigatemethod:

workOn(index:number):void{

this.router.navigate(['Timer',{id:index}]);

}

IfthishadbeenarouterLinkdirective,theparameterswouldhavebeendefinedinthesameway:ahashobjectfollowingthepathnamestring(orstrings,aswewillseewhiletappingintothechildrouters)insidethearray.Thisisthewayparametersareaddedtothegeneratedlink.However,ifweclickonanybuttonnow,wewillseethatthedynamicIDvaluesareappendedasquerystringparameters.Whilethismightsufficeinsomescenarios,weareafteramoreelegantworkaroundforthis.So,let'supdateourroutedefinitiontoincludetheparameterinthepath.GobacktoourtoprootcomponentandupdatetherouteinsidetheRouteConfigdecoratorasfollows:

app/app.component.ts

...

},{

path:'timer/:id',

name:'TimerComponent',

component:TimerComponent

}

...

www.EBooksWorld.ir

Page 264: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Refreshtheapplication,schedulethelasttaskonthetable,andclickontheStartbutton.YouwillseehowthebrowserloadstheTimercomponentunderaURLlike/timer/3.

Eachpathcancontainasmanytokensprefixedbyacolonasrequired.ThesetokenswillbetranslatedtotheactualvalueswhenweactonarouterLinkdirectiveorexecutethenavigatemethodoftheRouterclassbypassingahashofthekey/valuepairs,matchingeachtokenwithitscorrespondingkey.So,inanutshell,wecandefineroutepathsasfollows:

{

path:'/products/:category/:id',

name:'ProductsByCategoryComponent',

component:ProductsByCategoryComponent

}

Then,wecanexecuteanygivenroutesuchastheonedepictedearlierasfollows:

<a[routerLink]="['ProductsByCategoryComponent',{

category:'toys',

id:452

}]">SeeToy</a>

Thesameappliestotheroutescalledimperatively:

router.navigate(['ProductsByCategoryComponent',{

category:'toys',

id:452

}]);

www.EBooksWorld.ir

Page 265: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

ParsingrouteparameterswiththeRouteParamsserviceGreat!Now,wearepassingtheindexofthetaskitemwewanttoworkonloadingthetimer,buthowdoweparsethatparameterfromtheURL?TheAngularrouterprovidesaconvenientinjectabletype(alreadyincludedinROUTER_PROVIDERS)namedRouteParamsthatwecanusefromthecomponentshandledbytheroutertofetchtheparametersdefinedintheroutedefinitionpath.

Openourtimercomponentandimportitwiththefollowingimportstatement.Also,let'sinjecttheTaskServiceprovider,sowecanretrieveinformationfromthetaskitemrequested:

app/timer/timer-widget.component.ts

import{Component,OnInit}from'@angular/core';

import{SettingsService,TaskService}from'../shared/shared';

import{RouteParams}from'@angular/router-deprecated';

...

Weneedtoalterthecomponent'sdefinitioninordertoassigntheTaskServiceasanannotateddependencyforthiscomponent,sotheinjectorcanproperlyperformtheproviderlookup.

Note

ThenewReleaseCandidaterouterhasdeprecatedtheRouteParamsclass,favoringthenewRouteSegmentsclass,whichexposesmoreandmoreusefulmethodsandhelpers.PleaserefertotheofficialdocumentationforbroaderinsightsonitsAPI.

Wewillalsoleveragethisactiontoinserttheinterpolatedtitlecorrespondingtotherequestedtaskinthecomponenttemplate:

app/timer/timer-widget.component.ts

...

@Component({

selector:'pomodoro-timer-widget',

template:`

<divclass="text-center">

<imgsrc="/app/shared/assets/img/pomodoro.png">

<h3><small>{{taskName}}</small></h3>

<h1>{{minutes}}:{{seconds|number:'2.0'}}</h1>

<p>

<button(click)="togglePause()"class="btnbtn-danger">

{{buttonLabelKey|i18nSelect:buttonLabelsMap}}

</button>

</p>

</div>`

})

...

ThetaskNamevariableistheplaceholderwewillbeusingtointerpolatethenameofthetask.

www.EBooksWorld.ir

Page 266: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Withallthisinplace,let'supdateourconstructortobringboththeRouteParamstypeandtheTaskServiceclassestothegameasprivateclassmembersinjectedfromtheconstructor:

app/timer/timer-widget.component.ts

...

constructor(

privatesettingsService:SettingsService,

privaterouteParams:RouteParams,

privatetaskService:TaskService){

this.buttonLabelsMap=settingsService.labelsMap.timer;

}

...

Withthesetypesnowavailableinourclass,wecanleveragethengOnInithooktofetchthetaskdetailsoftheiteminthetasksarraycorrespondingtotheindexpassedasaparameter.WaitingfortheOnInitstageisnoteasy,sincewewillfindissueswhentryingtoaccessthepropertiescontainedinrouteParamsbeforethatstage:

app/timer/timer-widget.component.ts

ngOnInit():void{

this.resetPomodoro();

setInterval(()=>this.tick(),1000);

lettaskIndex=parseInt(this.routeParams.get('id'));

if(!isNaN(taskIndex)){

this.taskName=this.taskService.taskStore[taskIndex].name;

}

}

Howdowefetchthevaluefromthatidparameter?TheRouteParamsobjectexposesaget(param:string)methodwecanusetoaddressparametersbyname.Inourexample,weretrievedthevalueoftheidparameterbyexecutingtherouteParams.get('id')commandinthengOnInit()hookmethod.Basically,thisishowwegetparametervaluesfromourroutes.First,wegrabaninstanceoftheRouteParamsclassthroughthecomponentinjectorandthenweretrievevaluesbyexecutingitsgetterfunction,whichwillexpectastringparameterwiththenameofthetokencorrespondingtotheparameterweneed.

Note

ItisimportanttonotethatwearefetchingthedataalreadypersistedinthetaskStorepropertyofourTaskServiceprovider.Sinceitisasingleton,availablethroughouttheentireapplicationbymeansoftheAngularDImachinery,whichhadbeenalreadypopulatedatTaskComponent,alltheinformationwerequireisalreadythere.ThingswouldbecometrickierifweloaddirectlyeachtimerURL.Inthosecases,theinformationwouldhavenotbeenfetchedyet,sowewouldhavetosubscribetotheserviceinordertoforceittoloadthedatathroughitsunderlyingHttpclient.WesawthisinChapter6,AsynchronousDataServiceswithAngular2;applyingtheasyncpipetothetaskNameinterpolationinthetemplatewouldbe

www.EBooksWorld.ir

Page 267: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

required.Forthesakeofsimplicity,wewillskipthatrefactoringhere,butweencourageyoutotweakthecomponenttoextendsupportforthisscenarioaswell.

www.EBooksWorld.ir

Page 268: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

DefiningchildroutersAsourapplicationsscale,theideaofbundlingalltheroutedefinitionsinacentralizedlocation(forexample,therootcomponent)doesnotseemlikeagoodapproach.Themorerouteswedefinethere,theharderitwillbetomaintaintheapplication,letalonethetightcouplingwegeneratebetweenourcomponents(thataremeanttobeasmuchreusableandapplication-agnosticaspossible)andtheapplicationitself.

Thisiswhyitisgenerallyagoodpracticetosplitandwraptheroutedefinitionsthatapplytoaspecificfeaturearoundarouterconfigurationdefinedonaspecificcomponentperfeaturelevel,usuallytherootcomponentthatwrapsthatfeaturecontext.TheAngularteamhadthisideainmindwhentheRouterlibrarywasdesignedandthusimplementedsupporttoextendaroutewithchildrenrouteswhilekeepingtheparentroutefullyagnosticofwhatroutesaredefinedaboveitslayeroffunctionality.

Let'sseeallthisthroughanactualexample.Weupdatedourtimerrecentlytodisplaythenameofthetaskwewantedtoworkonafterselectingitfromthetable.However,whatifwewanttokeepprovidingastandalonetimernotboundtoanyspecifictask?Thisway,wecanleveragethecountdownfunctionalityforanyimpromptutaskwithouthavingtocreateitbeforehand.

So,wewillwanttogiveaccesstothetimerintwoflavors:

timer:Thiswillloadthetimerasitiswithoutpointingtoanyspecifictasktimer/task/{id}:Thiswillloadthetimerspecifyingataskname,where{id}istheindexofthetaskwewanttoloadfromtheoveralltasksarray.

Wewillbeginbyupdatingthemainrootcomponent,nowturnedintoaroutercomponent,toturnthe/timer/:idpathintoapathpointingtoachildroutercomponent.Openthecomponentandreplacetheroutedefinitionpointingtothetimercomponentusingthefollowingdefinition:

app/app.component.ts

{

path:'timer/...',

name:'TimerComponent',

component:TimerComponent

}

That'sit.TheellipsisrightnexttotheroutepathinformstheAngularRouterthatitshouldexpectroutedefinitionsnestedwithinthatcomponent.Theproblemhereisthatacomponentshouldnotroutetoitself,sinceitcannotinstantiateitselfinsideitsownRouterOutletdirective.Thisiswhyweneedtoproxythetimercomponentwitharoutercomponentforthisexample.So,let'screatearoutingcomponentforourtimerinsideitsownfolderforsimplicitysake.Aroutingcomponentis,bydefinition,acomponentwithnoimplementationotherthattoserveasacomponentdispatcherdependingonroutes.Theirimplementation,if

www.EBooksWorld.ir

Page 269: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

any,isgenerallyprettylimited,anditbasicallyentailstheRouteConfigdecoratorcontainingtheroutesdeliveredbythatfeaturecontextandtheRouterOutletpresentinitstemplate.Routingcomponentsareindeedagoodwaytodecoupleroutingfunctionalitiesfromthespecificimplementationofeachcomponentinthecontextofthatfeature,ensuringfullreusabilityofitsnon-routingcomponentsofthatfeature.

Openthetimerfeaturefolderandcreateafileforourtimerroutingcomponentwiththefollowingimplementation:

app/timer/timer.component.ts

import{Component}from'@angular/core';

import{RouteConfig,ROUTER_DIRECTIVES}from'@angular/router-deprecated';

importTimerWidgetComponentfrom'./timer-widget.component';

@Component({

selector:'pomodoro-timer',

directives:[ROUTER_DIRECTIVES],

template:'<router-outlet></router-outlet>'

})

@RouteConfig([

{path:'/',name:'GenericTimer',

component:TimerWidgetComponent,

useAsDefault:true},{

path:'/task/:id',

name:'TaskTimer',

component:TimerWidgetComponent

}

])

exportdefaultclassTimerComponent{}

Aswecansee,wehavecreatedacomponentclasswithnoimplementationotherthandispatchingroutestotheTimerWidgetComponentcomponentitself.TheRouteConfigtypeallowsustocreateaproperdecoratorcontainingroutedefinitionsandROUTER_DIRECTIVESwillallowustobindtheRouterOutletdirectiveinthecomponenttemplate.

PleasepayattentiontothenewuseAsDefaultBooleanpropertyinthefirstroutedefinition.Thisinformstherouterthatifnomatchingpathsarefound,theRouterclassshouldloadthisroutebydefault.

Note

Atthetimeofthiswriting,thenewReleaseCandidateRouterstilldoesnotimplementtheuseAsDefaultproperty,butitisplannedtobeimplementedbyitsfinalversion.Pleaserefertotheofficialdocumentationforfurtherdetails.

ItisimportanttonotethatanycomponentactingasaroutercomponentcanhaveitsownimplementationlivinginparalleltotheRouterOutletdirective.JustlikewedidwithAppComponent,wecanprovideadditionalfunctionalitiestoourcomponentotherthanthemereroutingfeatures.

www.EBooksWorld.ir

Page 270: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Allright,wehaveacomponentnowthatcanredirectuserstoourtimercomponentintwoflavors,buthowdowelinktoit?

www.EBooksWorld.ir

Page 271: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

LinkingtochildroutesThereisnodifferencewhatsoeverinlinkingtoachildrouterandlinkingtoanygivenroutemanagedbyatoprouter,exceptforthefactthatwewillpopulatetheroutenamesarraywiththenamesofthechildrouteswewanttolinkaswell.OpenthePomodoroTaskListcomponentandrefactortheworkOn()methodtolooklikethis:

workOn(index:number):void{

this.router.navigate(['TimerComponent','TaskTimer',{id:index}]);

}

Here,wearetellingAngulartolinktotheroutenamedTimerComponent(hencetheimportanceofnamingourroutesaftereachtargetcomponent'sname).Sincethisisaparentroute(remembertheellipsis)configuredatourtoprootrouter,weneedtoprovidethenameofthechildroutetoloadfromwithintheroutesconfiguredatthechildrouterlevel,TaskTimerinthiscase.Obviously,wewillcompounduptheroutewiththeIDinformationrequiredforloadingthetaskrequested.Clickonanytaskandseehowthetimerisloaded,displayingthetasknamewewishtoworkon.

ThisimplementationapproachgivesthecomponentthechancetobedisplayedwithorwithoutTaskIDinformation.Thisway,wecankeeponbrowsingtothetimerfunctionality,eitherfromthetopnavlinkorbyclickingtheStartbuttonatthetaskstable.

JustrememberweconfiguredthemainroutedefinitionwiththeuseAsDefaultpropertysetastrue,remember?Thismeansthatanythingpointingtothetimerroutewilldegradegracefullytothislastroutedefinitiononcewereachthechildroutedomain.

www.EBooksWorld.ir

Page 272: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

TheRouterlifecyclehooksJustlikecomponentsgothroughasetofdifferentphasesduringtheirlifetime,aroutingoperationgoesthroughdifferentlifecyclestages.Eachoneisaccessiblefromadifferentlifecyclehookwhich,justlikecomponents,canbehandledbyimplementingaspecificinterfaceinthecomponentsubjectoftheroutingaction.Theonlyexceptionforthisistheearliesthookintheroutinglifecycle,theCanActivatehook,whichtakestheshapeofadecoratorannotation,sinceitismeanttobecalledbeforethecomponentiseveninstantiated.

www.EBooksWorld.ir

Page 273: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

TheCanActivatehookTheCanActivatehook,presentedasadecoratorannotatingthecomponent,ischeckedbytheRouterrightbeforeitisinstantiated.ItwillneeditssetuptobeconfiguredwithafunctionthatisintendedtoreturnaBooleanvalue(oraPromise-typedBooleanvalue)indicatingwhetherthecomponentshouldbefinallyactivatedornot:

@CanActivate((next,prev)=>boolean|Promise<boolean>)

The@CanActivatedecoratoristhereforeafunctionthatexpectsanotherfunctionasanargument,expectingthelattertwoComponentInstructionobjectsasparametersinitssignature:thefirstargumentrepresentstheroutewewanttonavigatetoandthesecondargumentrepresentstheroutewearecomingfrom.Theseobjectsexposeusefulpropertiesabouttheroutewecomefromandthecomponentweaimtoinstantiate:path,parameters,componenttype,andsoon.

Note

Thishookrepresentsagoodpointintheoverallcomponent'slifecycletoimplementbehaviorssuchassessionvalidation,allowingustoprotectareasofourapplication.UnfortunatelytheCanActivatehookdoesnotnativelysupportdependencyinjection,whichmakeshardertointroduceadvancedbusinesslogicpriortoactivatearoute.Thenextchapterswilldescribeworkaroundsforscenariossuchasuserauthentication.

Inthefollowingexample,wepassword-protecttheformsothatitwon'tbeinstantiatedshouldtheuserentersthewrongpassphrase.First,opentheTaskEditorComponentmodulefileandimportallthatwewillneedforourfirstexperiment,alongwithallthesymbolsrequiredforimplementingtheinterfacesfortheroutinglifecyclehookswewillseethroughoutthischapter.Then,proceedtoapplytheCanActivatedecoratortothecomponentclass:

app/tasks/task-editor.component.ts

import{Component}from'@angular/core';

import{

ROUTER_DIRECTIVES,

CanActivate,

ComponentInstruction,

OnActivate,

CanDeactivate,

OnDeactivate}from'@angular/router-deprecated';

@Component({

selector:'pomodoro-tasks-editor',

directives:[ROUTER_DIRECTIVES],

templateUrl:'app/tasks/task-editor.component.html'

})

@CanActivate((

next:ComponentInstruction,

prev:ComponentInstruction):boolean=>{

www.EBooksWorld.ir

Page 274: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

letpassPhrase=prompt('Saythemagicwords');

return(passPhrase==='opensesame');

}

)

exportdefaultclassTaskEditorComponent{

...

Asyoucansee,wearepopulatingthe@CanActivatedecoratorwithanarrowfunctiondeclaringtwoComponentInstructiontypedarguments(whicharenotactuallyrequiredforourexample,althoughtheyhavebeenincludedhereforinstructionalpurposes).ThearrowfunctionreturnsaBooleanvaluedependingonwhethertheusertypesthecorrectcase-sensitivepassphrase.Wewouldadviseyoutoinspectthenextandpreviousparametersintheconsoletogetyourselfmoreacquaintedwiththeinformationthesetwousefulobjectsprovide.

Bytheway,didyounoticethatwedeclaredtheROUTER_DIRECTIVEStokeninthedirectivesproperty?Theroutingdirectivesarenotrequiredforouroverviewofthedifferentroutinglifecyclehooks,butnowwearetweakingthiscomponentandwillkeepupdatingittotestdrivethedifferentlifecyclehooks.Let'sintroduceaconvenientbackbutton,leveragingtheCancelbuttonalreadypresentinthecomponenttemplate:

app/tasks/task-editor.component.html

<formclass="container">

...

<p>

<inputtype="submit"class="btnbtn-success"value="Save">

<a[routerLink]="['TasksComponent']"class="btnbtn-danger">

Cancel

</a>

</p>

</form>

www.EBooksWorld.ir

Page 275: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

TheOnActivateHookTheOnActivatehookallowsustoperformcustomactionsoncetheroutenavigationtothecomponenthasbeensuccessfullyaccomplished.Wecaneasilyhandleitbyimplementingasimpleinterface.Thesecustomactionscanevenencompassasynchronousoperations,inwhichcase,wejustneedtoreturnaPromisefromtheinterfacefunction.Ifso,theroutewillonlychangeoncethepromisedhasbeenresolved.

Let'sseeanactualexamplewherewewillintroduceanewfunctionalitybychangingthetitleofourformpage.Todoso,wewillkeepworkingontheTaskEditorComponentmoduletobringsupportfortheOnActivatehookinterfaceandtheTitleclasswhoseAPIexposesutilitymethods(https://angular.io/docs/ts/latest/api/platform/browser/Title-class.html)tosetorgetthepagetitlewhileexecutingapplicationsinwebbrowsers.Let'simporttheTitlesymbolanddeclareitasaproviderinthecomponenttomakeitavailablefortheinjector(youcanalsoinjectitearlieratthetoprootcomponentshouldyouwishtointeractwiththisobjectinothercomponents):

app/tasks/task-editor.component.ts

import{Component}from'@angular/core';

import{

ROUTER_DIRECTIVES,

CanActivate,

ComponentInstruction,

OnActivate,

CanDeactivate,

OnDeactivate}from'@angular/router-deprecated';

import{Title}from'@angular/platform-browser';

@Component({

selector:'pomodoro-tasks-editor',

directives:[ROUTER_DIRECTIVES],

providers:[Title],

templateUrl:'app/tasks/task-editor.component.html'

})

Now,let'simplementtheinterfacewithitsrequiredrouterOnActivatemethod.Asaruleofthumb,allrouterlifecyclehooksarenamedafterthehooknameprefixedbyrouterinlowercase:

app/tasks/task-editor.component.ts

exportdefaultclassTaskEditorComponentimplementsOnActivate{

constructor(privatetitle:Title){}

routerOnActivate(

next:ComponentInstruction,

prev:ComponentInstruction):void{

this.title.setTitle('WelcometotheTaskForm!');

}

www.EBooksWorld.ir

Page 276: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

}

PleasenotehowweinjecttheTitletypeintheclassthroughitsconstructorandhowwelateronexecuteitwhentherouteractivatesthecomponentoncethenavigationhasfinished.Saveyourchangesandreloadtheapplication.YouwillnoticehowthebrowsertitlechangesoncewesuccessfullyaccessthecomponentafterpassingtheCanActivateandOnActivatestages.

www.EBooksWorld.ir

Page 277: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

TheCanDeactivateandOnDeactivatehooksJustlikewecanfilterifthecomponentwearenavigatingtocanbeactivated,wecanapplythesamelogicwhentheuserisabouttoleavethecurrentcomponenttowardsanotheronelocatedelsewhereinourapplication.AswesawincaseoftheCanActivatehook,wemustreturnaBooleanoraPromiseresolvingtoaBooleaninordertoallowthenavigationtoproceedornot.WhentheCanDeactivatehookreturnsorisresolvedtotrue,theOnDeactivatehookisexecutedjustliketheOnActivatehookafterthenavigationisaccomplished.

Inthefollowingexample,wewillinterceptthedeactivationstagesoftheroutinglifecycletofirstinterrogatetheuserwhetherhewantstoleavethecomponentpageornot,andthenwewillrestorethepagetitleifso.Forbothoperations,wewillneedtoimplementtheCanDeactivateandOnDeactivateinterfacesinourcomponent.Thecodeisasfollows:

exportdefaultclassTaskEditorComponentimplementsOnActivate,CanDeactivate,

OnDeactivate{

constructor(privatetitle:Title){}

routerOnActivate():void{

this.title.setTitle('WelcometotheTaskForm!');

}

routerCanDeactivate():Promise<boolean>|boolean{

returnconfirm('Areyousureyouwanttoleave?');

}

routerOnDeactivate():void{

this.title.setTitle('MyAngular2PomodoroTimer');

}

}

Pleasenotethatwehaveremovedthe(next:ComponentInstruction,prev:ComponentInstruction)argumentsfromourhookimplementationsbecausetheywereofnousefortheseexamples,butyoucanaccessalotofinterestinginformationthroughtheminyourowncustomimplementations.

SameastheCanActivatehook,theCanDeactivatehookmustreturnaBooleanvalueoraPromiseresolvedtoaBooleanvalueinordertoallowtheroutingflowtocontinue.

www.EBooksWorld.ir

Page 278: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

TheCanReuseandOnReusehooksLastbutnotleast,wecanreusethesameinstanceofacomponentwhilebrowsingfromonecomponenttoanothercomponentofthesametype.Thisway,wecanskiptheprocessofdestroyingandinstantiatinganewcomponent,savingresourcesonthego.

ThisrequiresustoensurethattheinformationcontainedintheparametersandstuffisproperlyhandledtorefreshthecomponentUIorlogicifrequiredinthenewincarnationofthesamecomponent.

TheCanReusehookisresponsibleforallthis,andittellstheRouterwhetherthecomponentshouldbefreshlyinstantiatedorwhetherweshouldreusethecomponentinthefuturecallsofthesameroute.TheCanReuseinterfacemethodshouldreturnaBooleanvalueoraPromiseresolvingtoaBooleanvalue(justliketheCanActivateorCanDeactivatehooksdo),whichinformstheRouterifitshouldreusethiscomponentinthenextcall.IftheCanReuseimplementationthrowsanerrororisrejectedfromwithinthePromise,thenavigationwillbecancelled.

Ontheotherhand,iftheCanReuseinterfacereturnsorresolvestotrue,theOnReusehookwillbeexecutedinsteadoftheOnActivatehookshouldthelatterexistalreadyinthecomponent.Therefore,useonlyoneofthesetwowheneveryouimplementthisfunctionality.

Let'sseealltheseinanactualexample.Whenwescheduleataskinthetasklisttableandproceedtoitstimer,wecanjumpatanytimetothegenerictimeraccessiblefromthetopnavbar,therebyloadinganothertimerthatisnotboundtoanytaskwhatsoever.Bydoingso,weareactuallyjumpingfromoneinstanceoftheTimerWidgetComponentcomponenttoanotherTimerWidgetComponentcomponentandtheAngularrouterwilldestroyandinstantiatethesamecomponentagain.WecansavetheRouterfromdoingsobyconfiguringthecomponenttobereused.OpentheTimerWidgetComponentmoduleandimporttheinterfaceswewillneedforthis,alongwiththesymbolswewereimportingalreadyfromtheRouterlibrary:

app/timer/timer-widget.component.ts

import{Component,OnInit}from'@angular/core';

import{SettingsService,TaskService}from'../shared/shared';

import{RouteParams,CanReuse,OnReuse}from'@angular/router-deprecated';

Now,implementtheCanReuseandOnReuseinterfacesintheclassbyaddingthemtotheimplementsdeclarationandthenproceedtoattachthefollowingrequiredinterfacemethodstotheclassbody:

routerCanReuse():boolean{

returntrue;

}

routerOnReuse(next:ComponentInstruction):void{

//Noimplementationyet

www.EBooksWorld.ir

Page 279: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

}

Nowgotothetaskstable,scheduleanytask,andgotoitstimer.ClickontheTimerlinkinthetopnavbar.YouwillseehowtheURLchangesinthebrowserbutnothinghappens.Wearereusingthesamecomponentasitis.Whilethissavesmemoryresources,weneedafreshtimerwhenperformingthisaction.So,let'supdatetheOnReusemethodaccordingly,resettingthetaskNamevalueandthePomodoroitself:

routerOnReuse():void{

this.taskName=null;

this.isPaused=false;

this.resetPomodoro();

}

Reproducenowthesamenavigationjourneyandseewhathappens.Voila!Newbehaviorbutsameoldcomponent.

Advancedtipsandtricks

Althoughwehavediscussedallthatyouneedtostartbuildingcomplexapplicationswithroutingfunctionalities,thereisstillabigcollectionofadvancedtechniquesyoucanusetotakeourapplicationtothenextlevel.Intheupcomingsections,wewillhighlightjustafew.

www.EBooksWorld.ir

Page 280: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

RedirectingtootherroutesBesidestheroutedefinitiontypeswehaveseenalready,thereisanotherRouteDefinitiontypenamedRedirectthatisnotboundtoanynamedRouteorcomponent,butwillratherredirecttoanotherexistingRoute.

Sofar,wewereservingthetasklisttablefromtherootpath,butwhatifwewanttodeliverthistablefromapathnamed/taskswhileensuringthatallthelinkspointingtotherootareproperlyhandled?Let'screatearedirectroutethen.WewillupdatethetoprootrouterconfigurationwithanewpathfortheexistinghomepathandaredirectpathtoitfromthenewhomeURL.Thecodeisasfollows:

app/app.component.ts

...

@RouteConfig([{

path:'',

name:'Home',

redirectTo:['TasksComponent']

},{

path:'tasks',

name:'TasksComponent',

component:TasksComponent,

useAsDefault:true

},{

path:'tasks/editor',

name:'TaskEditorComponent',

component:TaskEditorComponent

},{

path:'timer/...',

name:'TimerComponent',

component:TimerComponent

}])

exportdefaultclassAppComponent{}

ThenewredirectingroutejustneedsastringpathpropertyandaredirectTopropertydeclaringthearrayofnamedrouteswewanttoredirectalltherequeststo.

Note

Atthetimeofthiswriting,therotuedefinitionsinthenewRouterstilldonotimplementsupportfortheredirectToproperty.Pleasechecktheonlinedocumentationforamoreup-to-datestatusonthesubject.

www.EBooksWorld.ir

Page 281: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

TweakingthebasepathWhenwebeganworkingonourapplicationrouting,wedefinedthebasehrefofourapplicationatindex.html,sotheAngularrouterisabletolocateanyresourcetoloadapartfromthecomponentsthemselves.Weobviouslyconfiguredtheroot/path,butwhatif,forsomereason,weneedtodeployourapplicationwithanotherbaseURLpathwhileensuringtheviewsarestillabletolocateanyrequiredresourceregardlessoftheURLthey'rebeingservedunder?OrperhapswedonothaveaccesstotheHEADtaginordertodropa<basehref="/">tag,becausewearejustbuildingaredistributablecomponentanddonotknowwherethiscomponentwillwinduplater.Whateverthereasonis,wecaneasilycircumventthisissuebyoverridingthevalueoftheAPP_BASE_HREFtoken,whichrepresentsthebasehreftobeusedwithourLocationStrategyofchoice.

Tryitforyourself.Openthemain.tsfilewherewebootstraptheapplication,importtherequiredtokens,andoverridethevalueoftheaforementionedbasehrefapplicationvariablebyacustomvalue:

app/main.ts

import'rxjs/add/operator/map';

import{bootstrap}from'@angular/platform-browser-dynamic';

importAppComponentfrom'./app.component';

import{provide}from'@angular/core';

import{APP_BASE_HREF}from'@angular/common';

bootstrap(AppComponent,[provide(APP_BASE_HREF,{

useValue:'/my-apps/pomodoro-app'

})]);

ReloadtheappandseetheresultingURLinyourbrowsers.

www.EBooksWorld.ir

Page 282: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

FinetuningourgeneratedURLswithlocationstrategiesAsyouhaveseen,wheneverthebrowsernavigatestoapathbycommandofarouterLinkorasaresultoftheexecutionofthenavigatemethodoftheRouterobject,theURLshowingupinthebrowser'slocationbarconformstothestandardizedURLsweareusedtoseeing,butitisinfactalocalURL.Nocalltotheserverisevermade.ThefactthattheURLshowsoffanaturalstructureisbecauseofthepushStatemethodoftheHTML5historyAPIthatisexecutedunderthefoldsandallowsthenavigationtoaddandmodifythebrowserhistoryinatransparentfashion.

Therearetwomainproviders,bothinheritedfromtheLocationStrategytype,forrepresentingandparsingstatefromthebrowser'sURL:

PathLocationStrategy:Thisisthestrategyusedbydefaultbythelocationservice,honoringtheHTML5pushStatemode,yieldingcleanURLswithnohash-bangedfragments(example.com/foo/bar/baz).HashLocationStrategy:ThisstrategymakesuseofhashfragmentstorepresentstateinthebrowserURL(example.com/#foo/bar/baz).

RegardlessofthestrategychosenbydefaultbytheLocationservice,youcanfallbacktotheoldhashbang-basednavigationbypickingtheHashLocationStrategyastheLocationStrategytypeofchoice.

Inordertodoso,gotomain.tsandtelltheAngularglobalinjectorthat,fromnowon,anytimetheinjectorrequiresbindingtheLocationStrategytypeforrepresentingorparsingstate(whichinternallypicksPathLocationStrategy),itshouldusenotthedefaulttype,butuseHashLocationStrategyinstead.

Itjusttakestooverrideadefaultproviderinjection:

app/main.ts

import'rxjs/add/operator/map';

import{bootstrap}from'@angular/platform-browser-dynamic';

importAppComponentfrom'./app.component';

import{provide}from'@angular/core';

import{

LocationStrategy,

HashLocationStrategy

}from'@angular/common';

bootstrap(AppComponent,[provide(LocationStrategy,{

useClass:HashLocationStrategy

})]);

Saveyourchangesandreloadtheapplication,requestinganewroute.You'llseetheresultingURLinthebrowser.

www.EBooksWorld.ir

Page 283: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Note

Pleasenotethatanylocation-relatedtokenintheexampleisnotimportedfrom'@angular/router-deprecated'butfrom'@angular/common'instead.

www.EBooksWorld.ir

Page 284: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

LoadingcomponentsasynchronouslywithAsyncRoutesAsyouhaveseeninthischapter,eachroutedefinitionneedstobeconfiguredwithacomponentpropertythatwillinformtherouteraboutwhattoloadintotherouteroutletwhenthebrowsersreachthatURL.However,wemightsometimesfindourselvesinascenariowherethiscomponentneedstobefetchedatruntimeorisjusttheby-productofanasynchronousoperation.Inthesecases,weneedtoapplyadifferentstrategytopickupthecomponentweneed.Here'swhereanewtypeofrouterdefinitionnamedAsyncRoutecomestotherescue.ThisspecifickindofrouteexposesthesamepropertiesofthealreadyfamiliarRouteDefinitionclasswehavebeenusingalongthischapter.ItreplacesthecomponentpropertywithaloaderpropertythatwillbelinkedtoaPromisethatresolvesasynchronouslytoacomponentloadedondemand.

Let'sseethiswithanactualexample.Inordertokeepthingssimple,wewillnotbeimportingthecomponentwewanttoloadatruntime,ratherwewillreturnitfromanasynchronousoperation.OpenthetoprootcomponentmoduleandreplacetheroutepointingtoTimerComponentwiththisasyncroutedefinition:

app/app.component.ts

...

@RouteConfig([{

path:'',

name:'Home',

redirectTo:['TasksComponent']

},{

path:'tasks',

name:'TasksComponent',

component:TasksComponent,

useAsDefault:true

},{

path:'tasks/editor',

name:'TaskEditorComponent',

component:TaskEditorComponent

},{

path:'/timer/...',

name:'TimerComponent',

loader:()=>{

returnnewPromise(resolve=>{

setTimeout(()=>resolve(TimerComponent),1000);

});

}

}

])

exportdefaultclassAppComponent{}

Thenexttimeweattempttoloadanyroutebelongingtothetimerbranch(eitherthegenerictimeraccessiblefromthenavbaroranytask-specifictimer),wewillhavetowaituntilthePromiseresolvestothecomponentweneed.Obviously,thegoalofthisexampleisnotto

www.EBooksWorld.ir

Page 285: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

teachhowtomakethingsloadslower,buttoprovideasimpleexampleofloadingacomponentasynchronously.

www.EBooksWorld.ir

Page 286: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

SummaryWehavenowuncoveredthepoweroftheAngularrouterandwehopeyouhaveenjoyedthisjourneyintotheintricaciesofthislibrary.OneofthethingsthatdefinitelyshinesintheRoutermoduleisthevastnumberofoptionsandscenarioswecancoverwithsuchasimplebutpowerfulimplementation.

Inthischapter,wediscussedhowtoinstallandprovidesupportforroutinginourapplicationsandhowtoturnanygivencomponentintoaroutingcomponentbydecoratingitwiththerouterconfigurationdecoratorandplacingarouteroutletinitstemplate,evenspreadingroutersdownwardinthecomponentstree.Wealsosawhowtodefineregularroutesandsomeotheradvancedtypessuchasredirectorasyncroutes.Theroutinglifecyclehasnosecretsforusanymoreandharnessingitspowerwillopenthedoortodeliveradvancedfunctionalitiesinourapplicationswithnoeffort.Thepossibilitiesareendlessand,mostimportantly,routingcontributestodeliveringabetterbrowsingexperience.

Inthenextchapter,wewillbeefupourtaskeditingcomponenttoshowcasethemechanismsunderlyingwebformsinAngular2andwhatarethebestsstrategiestograbuser'sinputwithformcontrols.

www.EBooksWorld.ir

Page 287: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Chapter8.FormsandAuthenticationHandlinginAngular2Inthepreviouschapter,wecoveredroutingandthisledtosecurityconcernswhenitcametoprovidingdifferenttiersofcontentinourapplication.Enablinguserauthenticationisthefirststepforintroducingrelevantfeaturessuchaspublishingformsinourapplication.However,ifwewanttobuildthesebrandnewfunctionalities,wewillneedtodiscoverhowtosetafoundationfirst.Inthischapter,wewillseehowtobuildformsandthenmoveontocoverhowtoleveragethoseformstoallowuserstologinandcreatenewcontent.

Asawordofcautionaboutthischapter,wewilloverviewdifferentwaysofbuildingforms.Allofthemarevalid,anditsusewilldependonthegoalsyou'reaimingoneverymomenttofulfilleachprojectrequirement.

Inthischapter,wewill:

LearnhowtocreateresponsiveinputcontrolsinourformswithdirectivesDiscusstwo-waydatabindingsupportinAngular2BinddatamodelsandinterfacetypesforformsandinputcontrolsDesigncontrolsetsbothdeclarativelyandimperativelyDiveintothealternativesforinputvalidationBuildourowncustomvalidatorsDevelopourownloginformsImplementgeneral-purposeauthorizationprovidersSecureareasofoursitebyrequestinguserloginupfront

www.EBooksWorld.ir

Page 288: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Two-waydatabindinginAngular2WementionedinpreviouschaptersthatoneofthemaindifferencesbetweenAngular2andthepreviousincarnationsoftheframeworkisthatitdoesnotfavortwo-waydatabindingasthecorepatternofdatamanagement.Well,thisisnotexactlytrue.WhilemostofthedatamanagementprocessesinAngular2areonewayonly,formmanagementprovidesroomfortwo-waydatabindingbymeansoftheNgModeldirective.

Let'sseeallthisthroughanactualexample.Inthepreviouschapter,weintroducedanewcomponentsowecouldexpandtherangeofcomponentsavailableinourappinordertohavemoreoptionsfornavigatingthesite,andthuswecouldbettertestourrouter'simplementation.Thisnewcomponent,namedTaskEditorComponent,hadnoimplementationyetanditstemplatefeaturedthislayout:

app/tasks/task-editor.component.html

<formclass="container">

<h3>TaskEditor:</h3>

<divclass="form-group">

<inputtype="text"

class="form-control"

placeholder="Taskname"

required>

</div>

<divclass="form-group">

<inputtype="Date"

class="form-control"

required>

</div>

<divclass="form-group">

<inputtype="number"

class="form-control"

placeholder="Pomodorosrequired"

min="1"

max="4"

required>

</div>

<divclass="form-group">

<inputtype="checkbox"name="queued">

<labelfor="queued">thistaskbydefault?</label>

</div>

<p>

<inputtype="submit"class="btnbtn-success"value="Save">

<a[routerLink]="['TasksComponent']"class="btnbtn-danger">Cancel</a>

</p>

</form>

www.EBooksWorld.ir

Page 289: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Thisisatinybutniftywebformindeed.Thecomponentincludedsupportforsomeroutinglifecyclehooksinordertoserveasaproof-of-conceptforthedifferentstagesacomponentgoesthroughinitsjourneythroughthenavigationpipeline.Apartfromthat,theformhadnolifewhatsoever—itwasjustanunanimatedcreatureinthemiddleofnowhere.

Note

Youwillseeseveralclassnamesdecoratingourformsthroughthedifferentexamplesincludedinthischapter.Unlesspointedotherwise,allclassnamescontainedinthischapterareborrowedfromtheBootstrapstylesheetforstylingupourform,forexample,container,form-group,form-controlandsoon.Angularhasnorelationshipwiththeseandtheyareindeednotrequiredwhencodingagainsttheframework.

www.EBooksWorld.ir

Page 290: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

TheNgModeldirectiveLet'sinfusesomelifeintoitthen!Oneofthegoodthingsaboutimplementingtwo-waydatabindingsupportinourformelementsisthatwedonotneedtoimportanythingupfront.Angular2issmartenoughtodetectwhatitneedsandtheonlydirectivewewillneedisalreadysuppliedout-of-the-box.WeareobviouslyreferringtotheNgModeldirective.AccordingtotheAngular2officialdocumentation:

"ngModelbindsanexistingdomainmodeltoaformcontrol.Foratwo-waybinding,use[(ngModel)]toensurethemodelupdatesinbothdirections."

Inanutshell,intheverymomentwebindanngModelattributetoaformcontrol,thecontrolwillwatchthevaluestoredatthecomponentclasspropertyitisboundtoandwillupdateitselfassoonasthevaluechangesinthemodel.Youmightthink:thisisalreadydonebyAngularwithoutanyrealfanfare.Yes,butthemaindifferencehereisthatsuchsurveillanceisperformedinbothways.Thismeansthattheclassmodelwillupdateitsstateassoonastheformcontrolvalueisupdated.

Enoughsaid!It'stimeforsomeaction.Let'supdateourtaskeditingcomponenttotrythisout.Bringupthecodeofourtaskeditingcomponentand,firstofall,pleasenotethatitfeaturesaCanActivatedecoratorthatposedapassthroughquestiontotheuser.Let'sremoveit,sincewewillencountermoresecureandelegantwaystoprovidesuchfunctionalitylaterinthischapter.Now,let'saddanewmembernamedtaskName,whichwillobviouslyrepresentataskname!

app/tasks/task-editor.component.ts

exportdefaultclassTaskEditorComponentimplementsOnActivate,CanDeactivate,

OnDeactivate{

taskName:string;

constructor(privatetitle:Title){}

//Restofthecomponentremainsunchanged

}

Opentheassociatedtemplateandupdatethefirstinputblocktolooklikethis.Wewillexplainallthisinaminute:

app/tasks/task-editor.component.html

<p>Yourtasknameis{{taskName}}</p>

<divclass="form-group">

<inputtype="text"

class="form-control"

placeholder="Taskname"

[(ngModel)]="taskName">

</div>

www.EBooksWorld.ir

Page 291: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Asyoucansee,wehaveattacheda[(ngModel)]attributedirectiveintoourinputcontrolpointingtothestringpropertywejustcreatedinthecomponentclass,whichisalsoshownonthescreenrightabovetheinput.Executethecodeandchangethetextfieldvalues.Youwillseehowthetextenteredisupdatedinreal-timeonscreen.

ThesyntaxofthengModelgivesaverygoodhinttowhatisitallabout.Weareblendinginasingleattributeaneventhandlerandapropertybinding(hencethecombinationofbracketsplusbraces),sowecaninjectavalueintothetargetcontrolwhilelisteningtochangesmadeonthevalueatthesametime.Inotherwords,itistwo-waydatabinding.

Obviously,thisisaverysimplisticexampleandweaimtobuildsomethingmoreambitious,solet'sleveragethisrecentlygainedexperiencetobuildsomethingmoreuseful.Inthefollowingsection,wearegoingto:

LinktheformtoanewlyinstantiatedtaskobjectactingasamodelPopulatethemodelwiththevaluesenteredintheformPersistthechangesaftervalidatingthedataenteredRedirecttheusertothePomodorostabletoseethetaskjustcreatedthere

www.EBooksWorld.ir

Page 292: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

BindingatypetoaformwithNgModelRemovethecodejustaddedandimporttheTaskinterfacetypeintoourformalongwiththeTaskServicemanager,byappendingthefollowingimportstatementtothetop:

app/tasks/task-editor.component.ts

import{Component}from'@angular/core';

import{Title}from'@angular/platform-browser';

import{

Router,

ROUTER_DIRECTIVES,

ComponentInstruction,

CanActivate,

OnActivate,

CanDeactivate,

OnDeactivate}from'@angular/router-deprecated';

import{

Task,

TaskService}from'../shared/shared';

YoumighthavenoticedthatwealsoimportedtheRoutertypefromangular2/router.WewillneedittoredirecttheuserbacktothePomodorolistpageoncethenewtaskhasbeensuccessfullycreated.

Now,weneedtoappendanewTask-annotatedmembertoourclassanddeclareTaskServiceasadependencyintheconstructor,sowecanpersistthenewlycreatedtasklater.RemovethetaskNamestringfieldwecreatedearlierandupdatetheclasswiththesechanges:

app/tasks/task-editor.component.ts

exportdefaultclassTaskEditorComponentimplementsOnActivate,CanDeactivate,

OnDeactivate{

task:Task;

constructor(

privatetitle:Title,

privaterouter:Router,

privatetaskService:TaskService){

this.task=<Task>{};

}

//Restofthecomponentremainsunchanged

}

WehaveaddedanewfieldtotheclassrepresentingtheTaskmodelourformwillbeboundto.SinceTaskisaninterfacetype,wecannotinstantiateitbyusingthenewkeyword,sinceinterfaceshavenoconstructor.However,wecantakeadvantageofgenericsandtypecastanemptyobjecttoenforcetheTaskinterface,aswedidintheprecedingcode.

Ontheotherhand,thetypesdeclaredintheconstructorensurethattheAngularinjectorwill

www.EBooksWorld.ir

Page 293: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

makethemavailableasclassfieldsfortherestofthecomponentmembersonceitisinstantiated.

Ideally,wewouldjustneedtolinktheformdatatotheobjectrepresentedbythetaskmemberofourcomponentclass,persistitthroughouttheapplicationbyusingthemethodsalreadycreatedintheTaskServiceclass,andthenproceedtothetasklistrightafterthat.Let'sbeginbyupdatingourHTMLtemplatewiththerequiredngModelattributes,includingaSubmitbuttonandasubmithandlerintheformwrappertag:

app/tasks/task-editor.component.html

<formclass="container"(submit)="saveTask()">

<h3>TaskEditor:</h3>

<divclass="form-group">

<inputtype="text"

class="form-control"

placeholder="Taskname"

[(ngModel)]="task.name">

</div>

<divclass="form-group">

<inputtype="date"

class="form-control"

[(ngModel)]="task.deadline">

</div>

<divclass="form-group">

<inputtype="number"

class="form-control"

placeholder="Pomodorosrequired"

min="1"

max="4"

[(ngModel)]="task.pomodorosRequired">

</div>

<divclass="form-group">

<inputtype="checkbox"

name="queued"

[(ngModel)]="task.queued">

<labelfor="queued">thistaskbydefault?</label>

</div>

<p>

<inputtype="submit"class="btnbtn-success"value="Save">

<a[routerLink]="['TaskList']"class="btnbtn-danger">

Cancel

</a>

</p>

</form>

Therearetworemarkableelementsinthispieceofcode:

NoweachinputcontrolfeaturesanngModeldirectiveattribute,mappedtooneofthepropertiesoftheTasktyperepresentedbythetaskmemberofthecontrollerclass.

www.EBooksWorld.ir

Page 294: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

WehaveincludedaSubmitbuttoninourform,althoughtheformtagdoesnotfeatureanyactionattribute,sowherearewesubmittingourformto?The(submit)eventlistenertakescareofhandlingtheeventbybindinganeventhandlertoit.

Ourthreeinputfieldsnowbenefitfromtwo-waydatabindingfunctionality,beingeachinputcontrolpointingtoapropertyexposedbythemodelobject.Whensubmitted,theformwillexecutethesaveTask()methodlocatedinthebodyofourcomponent.Let'stakealookintothismethodthen.Ithasnotbeenaddedalreadytotheclassthoughsopleaseextendourcomponentwithamethodfeaturingsuchanameandappenditanywhereintheclassrightafteritsconstructor:

app/tasks/task-editor.component.ts

saveTask(){

this.task.deadline=newDate(this.task.deadline.toString());

this.taskService.addTask(this.task);

this.router.navigate(['TaskList']);

}

Note

YouhaveprobablyraisedaneyebrowafterwatchingthefirstlineofcodeinthesaveTask()method.Yes,thatisweird.Wegrabthevalueofthedeadlinepropertyjusttoconvertittoastring(itwasaDateobjectalready)andthenweturnitintoaDateobjectagain.Thereisareasonforthis.TheDatePipe(liketheoneweuseintheTasksComponenttemplate)willonlytaketheDateobjectsandtheseneedtobeproperlyformattedsincenolocalizationtransformationisprovidedatthetimeofthiswriting.Thedateinputfielddoesnotsupplysuchlocalizationfunctionalitysoweneedtoensuredataconsistencyacrosstheboardbyrepurposingthedataformatbeforesavingit.Therearebetterworkaroundsforthisbutallofthemarebasicallymoreverboseanddefinitelysitoutsidethescopeofourtopichere,sowewillsticktothisquickfixfortherestofthechapter.

BypassingtheCanDeactivaterouterhookuponsubmittingforms

Nowourcomponenthaseverythingweneedinordertoreflectthechangesmadeonourmodelbytheform.However,ifweattempttofillouttheformwiththedetailsofournexttaskandproceedtosaveit,wewillbeconfrontedwiththatpeskyalertpopupwesetupinthepreviouschapterforinvitingtheuserstofillouttheform,andthat'swhatwejustdidnow!It'stimeforalast-minutechangethen.Let'sinsertabeaconvariableinformingwhethertheformhasbeenupdatedandsuccessfullysavedornot,anduseittoskipthepopuplaterwhererequired.Thecodeisasfollows:

app/tasks/task-editor.component.ts

exportdefaultclassTaskEditorComponentimplementsOnActivate,CanDeactivate,

OnDeactivate{

task:Task;

changesSaved:boolean;

www.EBooksWorld.ir

Page 295: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

constructor(

privatetitle:Title,

privaterouter:Router,

privatetaskService:TaskService){

this.task=<Task>{};

}

saveTask(){

this.task.deadline=newDate(this.task.deadline.toString());

this.taskService.addTask(this.task);

this.changesSaved=true;

this.router.navigate(['TaskList']);

}

routerOnActivate(){

this.title.setTitle('WelcometotheTaskForm!');

}

routerCanDeactivate(){

returnthis.changesSaved||confirm('Areyousureyouwanttoleave?');

}

routerOnDeactivate(){

this.title.setTitle('MyAngular2PomodoroTimer');

}

}

Basically,thechangesSavedfieldrepresentsabooleanflagthatwilltakeatruthvaluerightafterpersistingtheTasktypeddatathroughtheTaskServiceAPI.ThisallowstherouterCanDeactivate()methodtoeitherreturntrueassoonasitseeswhetherthechangeshavebeensavedorjustthrowtheconfirmpopup.

Sofarsogood,butnowit'stimetogetfancyandbeautifyourformlogicalittlebit.ValidatingourinputfieldsisdefinitelyagoodstartingpointandthatwillleadustothenextstageinourjourneythroughtheexcitingworldofAngular2forms.

www.EBooksWorld.ir

Page 296: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

TrackingcontrolinteractionandvalidatinginputAlthoughwearealreadytrackingchangesinourinputformsthroughthetwo-waydatabinding,weneedabetterwaytowatchtheoverallstateofourform.Inordertodoso,wecantakeadvantageoftheNgFormdirective.TheNgFormdirectivekeepstrackofthestateofallinputcontrolsfoundwithinit.Thegoodnewsisthatsuchadirectivehasbeenpresentinourexamplerightfromthebeginning.How?Basically,theNgFormdirectiveisconfiguredinitsselectortobeattachedtoany<form>tagpresentinourtemplate,providingadditionalfeaturestoourformasreal-timetrackingofthestateoftheinputfieldsinrespectofvalidityanduserinteraction.Inotherwords,ifyouhaveaforminyourtemplate,youhaveangFormdirectivealready.

Tip

YoucancancelthisautomaticbindingbyappendingthengNoFormattributetoany<form>tagyoudonotwanttobeintervenedbyAngular.

Let'sseeallthisthroughasimpleexample.First,markallourfieldswiththeHTML5requiredattribute:

app/tasks/task-editor.component.html

<formclass="container"(submit)="saveTask()">

<h3>TaskEditor:</h3>

<divclass="form-group">

<inputtype="text"

class="form-control"

placeholder="Taskname"

[(ngModel)]="task.name"

required>

</div>

<divclass="form-group">

<inputtype="date"

class="form-control"

[(ngModel)]="task.deadline"

required>

</div>

<divclass="form-group">

<inputtype="number"

class="form-control"

placeholder="Pomodorosrequired"

min="1"

max="4"

[(ngModel)]="task.pomodorosRequired"

required>

</div>

www.EBooksWorld.ir

Page 297: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

<divclass="form-group">

<inputtype="checkbox"

name="queued"

[(ngModel)]="task.queued">

<labelfor="queued">thistaskbydefault?</label>

</div>

<p>

<inputtype="submit"class="btnbtn-success"value="Save">

<a[routerLink]="['TaskList']"class="btnbtn-danger">

Cancel

</a>

</p>

</form>

Ifweattempttosubmittheform,automaticalertswillbetriggeredbythebrowserapplyingthevalidationpoliciesenforcedbytheHTML5formvalidationAPI.Butthereisalotmorehappeningunderthehood.Ifweinspectthecodeofourformwiththebrowser'sdevelopertools,wewillseeamyriadofclassnamesdecoratingtheinputcontrolsnow.Wheredidallthesecomefrom?Theanswerissimple!TheNgFormdirective,actioningour<form>tag,putallthesethere.Let'sinspectthefirstinputfieldthroughourbrowser'sdevtoolsasanexample:

<inputtype="text"

class="form-controlng-untouchedng-pristineng-invalid"

placeholder="Taskname"

required="">

Theseclassnames(easilyidentifiablebytheng-prefix)giveusaverygoodhinttothedifferentstatesanygiveninputcontrolcantakewhenwrappedinsidetheNgFormdirective.Theseclassbindingsarefullyreactivetostatechangesinourinputfields.Withyourdevtoolspaneopen,selectanyinputfield,updateitsvalueandthenemptyitagainandseehowtheclassnameschange,assuminganyofthefollowingstates:

Untouched:Whentrue,thecontrolhasnotbeeninteractedwiththeuserTouched:Whentrue,thecontrolhasbeeninteractedwiththeuserPristine:ThecontrolanditsunderlyingmodelhasnotbeenchangedDirty:ThecontrolanditsunderlyingmodelhasbeenchangedValid:TheinnermodelisvalidInvalid:Theinnermodelisnotvalid

Wheninteractingwithourformcontrols,wewillseegeneratedclassnamesmatchingthesestatesrepresentedwiththeng-prefixhereandthere:ng-untouched,ng-pristine,ng-invalid,andsoon.

www.EBooksWorld.ir

Page 298: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

TrackingchangeswithlocalreferencesNowthatweknowthatourinputcontrolscanreacttouserinteractionsandmodelvalidation,wecantakeastepfurtherandrendermoreinformationonscreen.Forinstance,wecanstyleourforminareactivefashion:

app/tasks/task-editor.component.ts

@Component({

selector:'pomodoro-tasks-editor',

directives:[ROUTER_DIRECTIVES],

providers:[Title],

templateUrl:'app/tasks/task-editor.component.html',

styles:[`

.ng-valid{border-color:#3c763d;}

.ng-invalid{border-color:#a94442;}

.ng-untouched{border-color:#999999;}

`]

})

...

Ourformwillprovidenowvisualhintsoftheoverallstateofeachinputthroughitsvisuallayout,butperhapsrenderingsomemessagesonscreenwillcompounduptheuserexperiencewewanttodeliverinourapp.Inordertodoso,weneedtogointoeachinputcontrolstate,andtemplatelocalreferencesbecomequitehandyforthis.TheywillprovideavaluableaccessortothegeneralstatehandlerofourformwhichisourngFormdirective.Let'sinsertastatemessageinourformandturnitintoawatcherofthestateofthefirstinputfield,usingngFormasaproxy:

app/tasks/task-editor.component.html

<formclass="container"(submit)="saveTask()">

<h3>TaskEditor:</h3>

<divclass="form-group">

<pclass="text-muted"*ngIf="name.untouched">

Startherebyenteringthetaskname.

</p>

<pclass="text-success"*ngIf="name.valid&&name.touched">

Welldone!That'sagoodnameforatask!

</p>

<pclass="text-danger"*ngIf="!name.valid&&name.touched">

Oops!Youcannotleavethenameblank...

</p>

<inputtype="text"

class="form-control"

placeholder="Taskname"

[(ngModel)]="task.name"

#name="ngForm"

required>

</div>

...

www.EBooksWorld.ir

Page 299: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Let'stakeacloserlookattheprecedingcode.Thefirstblockisprettystraightforward:wewillrenderdifferentmessages(usingthestylingprovidedbyBootstrap,aswedidalreadywiththerestoftheform)dependingoftheoverallstateofthecontrol.Inordertorefertotheinputcontrol,weneedtocreatealocaltemplatevariablesowecanaddressitfromadifferentelement,andsowedobyappendingthe#namelocaltemplatereferenceinourcontrol.Surprisingly,itispopulatedwithavaluethough.LocaltemplatevariablesinAngular2canbepopulatedwithothervalues,orpointerstootherobjectsandthat'sexactlywhatwearedoingbypointing#nametothengFormstring.TheNgFormdirectiveexportsitselfunderthengFormname,soifwerefertoitsnamefromanylocaltemplatereference,wewillgainaccesstoitsAPIand,thus,itsstate.

Let'swrapupourjourneyintoformbuildingbasedonclassicaltwo-waydata-binding.Nevertheless,thereisanotherpowerfulwaytobuildformsinAngular2thathasnothingtodowithourbeloved[(ngModel)]directive,althoughitalsoprovidessupportforthesamefunctionalitiesandevenextendssupportforsomemorefeatures,becomingthepreferredwayforbuildingformsinAngular2.

www.EBooksWorld.ir

Page 300: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Controls,ControlGroups,andtheFormBuilderclassInthischapter,wehaveseenhowtoimplementtwo-waydatabindinginourformstohookupdataentitieswithinputfields.Whilethisapproachisperfectlyfine,Angular2providesamoreefficientformmodelwhereeverythingflowsinonedirectiononly.Therearealotofupsidesforthis,butprobablythemostrelevantreasonistheremarkableimpactonperformancethattraditionaltwo-waydata-bindinghasinourapplications,incomparisontootherpatternswhereinformationflowsinonedirectiononly.

www.EBooksWorld.ir

Page 301: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

IntroducingControlsandValidatorsInanutshell,wemightsummarizeControlsinthefollowingstatement:AControlistheminimumrepresentationofanelementbeingpartofaform.Thinkofatextinputoracheckboxasperfectexamplesofacontrol.Infact,wehavebeenusingAngular2controlsthroughoutthischaptereverytimewetappedintoanyoftheinputfieldsofthePomodorotaskcreationform.Angular2creates(bydefault)acontrolforeveryformcomponentexistingwithinaNgFormdirective.Sincetheformelementisattachedtothisdirective,wehavebeendealingwithControlobjectsrightfromthebeginning.

Now,whatdoesittaketocreateaControlobject?Itisprettysimplereally.OnceweimporttheControltypefromtheangular2/commonbarrel,wecancreatecontrolsinourclassesjustbyinstantiatingthemlikethis:

varfirstName=newControl('',Validators.required);

TheControlconstructorisprettysimple:thefirstparameteristhedefaultvalueourcontrolwillassumebydefaultandcanallocateanyobjecttype.Itisusuallypopulatedwithanemptystringwhennodefaultvalueisrequired.ThesecondparameterexpectsafunctionwhichtheControlobjectwillusetovalidatethedatainput.Wecancreateourowncustomvalidationfunction,aswewillseelaterinthischapter,orwecantakeadvantageofanyofthestaticfunctionsalreadycreatedintheValidatorsclass,alsoavailableattheangular2/commonbarrel.Thisclassexposesthefollowingstaticvalidatormethods:

required:Thisrequiresthecontroltohaveanactualnon-emptyvalue.ItisequivalenttotheHTML5requiredattribute.minLength(minLength:number):Thevalidatorwillrequirethecontroltobepopulatedwithavalueofagivenminimumlength.maxLength(maxLength:number):Thevalidatorwillrequirethecontroltobepopulatedwithavalueofagivenmaximumlength.pattern(pattern:string):Theargumentofthepatternvalidatorexpectsastringcontainingaregularexpression.compose(validators:Function[]):Thisisnotanactualvalidatorthough,butamixintocombinevalidatorsinthearraypassedasaparameter.composeAsync():Thisisthesameascompose,butitwillexpecttoconverteachfunctionintoapromiseunderthehoodandexecuteallthematonce,returningtheresultofthevalidationcheckonceallpromiseshaveresolved.

Eachoneofthesevalidators,wheninterrogatedbyAngular,willeitherreturnanullvalueindicatingthattheinputisvalidoranerrormapobjectwithfurtherdetailsoftheerrorscausingtheinvalidationofourcontrol.Don'tpanic!Youwillgetthefullpicturelateronwhenweimplementsomeofthesevalidatorsinarealexample.

www.EBooksWorld.ir

Page 302: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

ControlsintheDOM–thengControldirectiveWecancreateControlsinthebodyofourcomponentclassorwecanhaveAngular2createandbindthemdirectlyintoourtemplatesthankstothengControlattributedirective,formalizedintheNgControlNameclass.

Note

DonotconfusetheNgControlNameclasswiththeNgControlclass,whichisthebaseclassthatNgControlNameactuallyextendsfrom.

ThisdirectivecanonlybeusedasachildofaNgFormdirective(whichwecoveredalready)oranelementdecoratedwiththeNgFormModelattributedirective,whichwewillcovershortly.TheofficialAngulardocumentationdescribesthengControlusingthefollowingstatement:

"CreatesandbindsacontrolwithaspecifiednametoaDOMelement.ThisdirectivecanonlybeusedasachildofNgFormorNgFormModel."

Accordingtothisquote,wewillusuallyfindthengControldirectivedecoratinganinputcontrollikethis:

<inputtype="email"ngControl="email">

FromthemomentweintroduceaNgControlNamedirectiveasanamedngControlattributeinourinputcontrols,wecanaccessitsvalue,checkitsvalidity,andwatchitforstatechanges.ThemostconvenientwayistoassignalocaltemplatereferencepointingtotheexportedNgFormdirectiveandevaluateitsproperties:

<inputtype="password"ngControl="password"#pwd="ngForm">

<div*ngIf="!pwd.valid">Passwordisinvalid</div>

Intheprecedingexample,whichismeanttobeexecutedwithinthecontextofaformelement,welabeledtheinputcontrolextendedalreadybythengControldirectivewithalocaltemplatereferencenamed#pwdpointingtongForm.Angulardetectsthisreferenceandresetsthepointertothecontrolitself.Thisway,wecanconvenientlyaccessandinspectthepropertiesoftheControlobjectboundtothengControldirective.YouwillactuallyseethispatternquiteoftenwhenworkingwithformsinAngular2,aswealreadydidintheprevioussections.

Note

ItisworthnotingthatthestaticmethodsoftheValidatorclasshaveacounterpartintheformofattributedirectivesthataresensitivetoelementsdecoratedwiththengControldirective,sowecanapplyvalidationrightoverthem:

<inputngControl="taskName"requiredpattern="[a-zA-Z]*">

<inputngControl="pomodoros"minlength="1"maxlength="5">

www.EBooksWorld.ir

Page 303: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

GroupingcontrolsintheDOMwithNgControlGroupUsually,formsincludemorethanoneinputcontrolandthereforethedatamodeltheyrepresentfeaturesseveralfieldsorproperties.Whendefiningasetofinputs,wecantakeadvantageoftheNgControlGroupdirective.ThisdirectivebehavesasareferencewrapperforseveralinputcontrolsdecoratedwiththengControldirective,sowecanbenefitfromasingleentrypointtoinspectthevaluesofeachdifferentinputcontrolcontainedintheNgControlGroup.ThefollowingexampledefinesasimpleformcomprisingtwoinputfieldswrappedwithinaNgControlGroupwhosereferenceispassedtoacomponentmethoduponsubmittingtheform:

import{Component}from'@angular/core';

import{NgControlGroup}from'@angular/common';

@Component({

selector:'hello-form',

template:`

<form(submit)="sayHello(fullName)">

<divngControlGroup="nameControlsGroup"#fullName="ngForm">

<divclass="form-group">

<inputtype="text"

placeholder="Firstname"

ngControl="firstName"

required>

<inputtype="text"

placeholder="Lastname"

ngControl="lastName"

required>

</div>

</div>

<inputtype="submit"value="Saymyname">

</form>

`

})

exportdefaultclassSayHello{

sayHello(controlGroup:NgControlGroup):void{

if(controlGroup.control){

letfirstName=controlGroup.control.value.firstName;

letlastName=controlGroup.control.value.lastName;

alert(`Hello${firstName}${lastName}!`);

}

}

}

WeusedlocaltemplatereferencestointrospecttheNgControlGroupwithitselfusingngFormasaproxy.TheobjectpassedtothesayHello()method,whichisthereferencetotheNgControlGroupitselfandexposes(throughthecontrolproperty)alltheinformationpertainingtotheglobalstateofthegroup:pristine,valid,touched,andsoon.WecanalsoinspecttheNgControlobjectscontainedbythecontrolgroupbyinspectingthecontrolspropertyinsidecontrol.Thevaluepropertyofcontrolreturnsahashobjectrepresentationoftheactualvalueofalltheinputfieldscontained.

www.EBooksWorld.ir

Page 304: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Inthisexample,wearepopulatingtheNgControlGroupwiththenameControlsGroupvalue.ThisstringwillbecomethenameofthecontrolgroupinthecontextofthewrappingNgFormdirective.Ifweslidealocaltemplatereferenceintothe<form>elementandinspectitsowncontrolsproperty,wewillseeanobjectcontainingapropertywiththenamegiventoourgroup(orgroups)pointingtoitsrespectiveNgControlGroupobject.Inspectingthevalueproperty,ontheotherhand,willreturnahashobjectwithasmanypropertiesascontrolgroupsfoundintheformcontainingeachoneanobjectrepresentationofthevaluesoftheinputcontrolstherein.

www.EBooksWorld.ir

Page 305: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

DefiningcontrolgroupsimperativelywithControlGroupInthebeginningofthischapter,wesawhowtodeclarecontrolsimperativelyinourcomponentcontrollerclass.Then,wejumpedstraighttotheDOMandcoveredhowtocreatecontrolsrightinthetemplateandalsohowtocreatesubsetsofcontrolsgroupedbytheNgControlGroupdirective.

GroupingcontrolsisacommonoperationandonethatwecandoaswellfromwithinthecomponentcontrollerclassbyinstantiatingControlGroupobjectswiththehelpofanothertypenamedFormBuilder,whichcreatesformobjects(inotherwords,controlgroups)byparsingtheconfigurationoftheinputfieldsofourchoice.Wewillseeanactualexampleinacomponentclassinwhich,afterinjectingtheFormBuilderthroughtheconstructor,weaccessitsmethodstoinstantiateaControlGroupwithotherControlGroupsandControlsnested:

import{Component}from'@angular/core';

import{

Control,

ControlGroup,

FormBuilder,

Validators}from'@angular/common';

@Component({

selector:'my-login',

providers:[FormBuilder],

templateUrl:'my-login.component.html'

})

exportclassLoginComponent{

name:Control;

username:Control;

password:Control;

signupForm:ControlGroup;

constructor(formBuilder:FormBuilder){

this.name=newControl('',Validators.required);

this.username=newControl('',Validators.required);

this.password=newControl('',Validators.required);

this.signupForm=formBuilder.group({

name:this.name,

credentials:formBuilder.group({

username:this.username,

password:this.password

})

});

}

}

Intheprecedingexample,wejustcreatedacomponentfeaturingaControlGrouprepresentingatypicalsign-upform,containedinthesignupFormfieldoftheLoginComponentclass.ThisfieldispopulatedintheconstructorbytheformBuilder.group()methodwithanamecontrolandacredentialcontrolthatareacontrolgroupitself(containingtwoothercontrols),with

www.EBooksWorld.ir

Page 306: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

differentflavorsofvalidation.EachoneofthosecontrolsisanactualinstanceoftheControlclass.WhenitcomestoinstantiatingControlobjects,wecanbenefitfromthecontrol()methodoftheFormBuilderclass,savingusfromimportingtheControltokenintotheclassifwedonotneeditelsewhere.Wecandramaticallysimplifytheimplementationofthepreviousexamplebyrefactoringitlikethis:

exportclassLoginComponent{

signupForm:ControlGroup;

constructor(formBuilder:FormBuilder){

this.signupForm=formBuilder.group({

name:this.formBuilder.control('',Validators.required]),

credentials:formBuilder.group({

username:this.formBuilder.control('',Validators.required),

password:this.formBuilder.control('',Validators.required)

})

});

}

}

WecanevengoastepfurtherandtakeadvantageofthesyntaxsugarprovidedbyAngularandthusinstantiatethecontrolsdefinedintheControlGroupthroughamoresimplisticsyntax:

exportclassLoginComponent{

signupForm:ControlGroup;

constructor(formBuilder:FormBuilder){

this.signupForm=formBuilder.group({

name:['',Validators.required],

credentials:formBuilder.group({

username:['',Validators.required],

password:['',Validators.required]

})

});

}

}

Thisisthemostsimplisticwayofinstantiatingcontrols,whereeachcontrolisapropertynameofahashobjectwhosevalueisanarraywherethefirstelementisthedefaultvaluewewantforourcontrolsfollowedbytherangeofvalidatorswewanttoapplytoeachcontrol.

Thesethreewaysofrepresentingagroupofcontrolsastheblueprintforanimaginarysign-upformgiveusalotofflexibilitytocreatecomplexformsfromourcomponentcontroller.Whatsyntaxshouldyouchooseforyournextproject?Itdepends.WhilethislasttakeonhowtoinstantiateControlGroupandControlobjectsisprobablythemostpopularonebecauseofitssimplicity,theformergivesustheopportunitytorefertothecontrolsfromotherendsofourcontrollerclass,giventhefactthatsuchcontrolsareactuallypartofitsmembersAPI.Ultimately,itwilldependofwhereyouwantyourcontrolstobeandfromwheretheyareaccessible.

www.EBooksWorld.ir

Page 307: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

ConnectingtheDOMandthecontrollerwithngFormModelSowecancreateformsimperativelyfromwithinourcomponentcontroller.Nowwhat?WeneedtolinkallthislogictoourHTMLtemplatesomehowandthisiswhereanewdirectivecomesintoplay:theNgFormModeldirective.ThisdirectivemustbeboundtoaDOMelement(usuallytheformoranyelementintervenedbyNgForm)andpopulatedwiththenameofaControlGroupobjectbinding.Fromthatmomentonwards,theControlobjectsattachedtotheControlGroupobjectarebondedtotheinputelementsflaggedwithangControldirectivematchingtheirnames.Thecodeisasfollows:

<form[ngFormModel]="signupForm"(submit)="doSomething($event)">

<div>

<inputtype="text"

placeholder="Yourname"

ngControl="name">

</div>

<divngControlGroup="credentials">

<inputtype="text"

placeholder="Yourusername"

ngControl="username">

<inputtype="password"

placeholder="Yourpassword"

ngControl="password">

</div>

<p>

<inputtype="submit"value="Signup">

</p>

</form>

Thereitis:ourbelovedsignupFormControlGroupisnowlinkedtoanactualformwhoseinputcontrols,handledbyngControldirectives,areautomaticallymappedtotheControlGroupcontrols.

NowitistimetotranslateallthistoourPomodoroproject,solet'spullupoursleevesasthere'ssomeworktodo.

www.EBooksWorld.ir

Page 308: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Arealexample–ourlogincomponentEarlierinthischapter,weimplementedabasicformtopublishourowntasks.Inanormalscenario,wewillnotbewillingtoleavethatfunctionalityopentoeveryone,sowewillwanttoprotectitwithapassword.Buildingaloginformisthefirststepandthatwewilldoalongthissectionbydevelopingournextfeature:login.

www.EBooksWorld.ir

Page 309: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

TheloginfeaturecontextTheloginfunctionalitycanliveinparalleltotherestofapplicationfunctionalitiesandthusdeservesitsownfolderandfacade.ThewholeimplementationwilldependonacomponentnamedLoginComponent,solet'sstartbycreatingthebasicfileswerequireinsidethenewloginfeaturefolder,locatedatthesamelevelasshared,tasks,ortimer.First,wewillcreateacomponentwithnoimplementationanditsassociatedtemplate,plusthefacade.Donotworryabouttheimplementationdetailsnow.

Thecodeisasfollows:

app/login/login.component.ts–componentcontrollerclass

import{Component}from'@angular/core';

import{

FormBuilder,

ControlGroup,

Validators,

Control}from'@angular/common';

import{Router}from'@angular/router-deprecated';

@Component({

selector:'pomodoro-login',

templateUrl:'app/login/login.component.html'

})

exportdefaultclassLoginComponent{

loginForm:ControlGroup;

notValidCredentials:boolean=false;

constructor(

formBuilder:FormBuilder,

privaterouter:Router){}

authenticate(){}

}

app/login/login.component.html–componenttemplate

<form[ngFormModel]="loginForm"

class="container"

(ngSubmit)="authenticate()">

<h3>Thiscontentispassword-protected</h3>

<p>Pleaseenteryourcredentialsbelow</p>

<divclass="alertalert-danger"*ngIf="notValidCredentials">

Yourcredentialsarenotvalid!

</div>

<divclass="form-group">

<inputtype="text"

class="form-control"

placeholder="Yourusername"

www.EBooksWorld.ir

Page 310: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

ngControl="username">

</div>

<divclass="form-group">

<inputtype="password"

class="form-control"

placeholder="Yourpassword"

ngControl="password">

</div>

<p>

<inputtype="submit"

class="btnbtn-success"

value="Authenticate"

[disabled]="!loginForm.valid">

</p>

</form>

app/login/login.ts–featurefacade

importLoginComponentfrom'./login.component';

export{

LoginComponent

};

Updatingtherouterconfigurationinourtoprootcomponentwillbeagreathelpintestingthechangeswewillconductoverthenextpages.Inordertodoso,let'seditdifferentpartsofthetoprootcomponent,asfollows:

app/app.component.ts–importstatementsblock

import{Component}from'@angular/core';

import{SHARED_PROVIDERS}from'./shared/shared';

import{HTTP_PROVIDERS}from'@angular/http';

import{ROUTER_PROVIDERS,RouteConfig,ROUTER_DIRECTIVES,Router}from

'@angular/router-deprecated';

import{TimerComponent}from'./timer/timer';

import{TasksComponent,TaskEditorComponent}from'./tasks/tasks';

import{FORM_PROVIDERS}from'@angular/common';

import{LoginComponent}from'./login/login';

...

app/app.component.ts–RouteConfigparams

...

@RouteConfig([

{path:'',

name:'Home',

redirectTo:['TasksComponent']

},{

path:'tasks',

name:'TasksComponent',

component:TasksComponent,

useAsDefault:true

www.EBooksWorld.ir

Page 311: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

},{

path:'tasks/editor',

name:'TaskEditorComponent',

component:TaskEditorComponent

},{

path:'timer/...',

name:'TimerComponent',

component:TimerComponent

},{

path:'login',

name:'LoginComponent',

component:LoginComponent

}

])

exportdefaultclassAppComponent{}

www.EBooksWorld.ir

Page 312: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

TheloginformtemplateItjusttakesaquickglanceattheformtounderstandthemachinerythatwillbebuiltinthecomponentcontrollerclasstobringthislittleguytolife.TheNgformModeldirectiveispointingtoaControlGroupnamedloginFormthatisyettobecreated.TheControlGroupwillwraptwoControlobjects(usernameandpassword),anditsstatewilldisabletheSubmitbuttonlocatedatthebottomoftheformwhennotvalid.Thecomponentwillfeatureamethodnamedauthenticate()thatwillhandletheformsubmitevent.Lastbutnotleast,wehavethislittlechunkofcoderightbeforeourinputcontrols:

<divclass="alertalert-danger"*ngIf="notValidCredentials">

Yourcredentialsarenotvalid!

</div>

Asyoumusthaveguessed,thismessagewillbedisplayedincasethecredentialsenteredarenotcorrect.Wewillperformthatvalidationintheimplementationoftheauthenticate()method,asyouwillseenext.Thereisanewdirectiveinourexample:thengSubmiteventdirective.InAngular'sownwords,itwillsignalwhentheusertriggersaformsubmission.Theuseofthiseventdirectivereplacesthe(submit)eventbindingwe'vebeenusingsofar.

www.EBooksWorld.ir

Page 313: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

ThelogincomponentLet'sconducttheimplementationofthelogincomponentclassnow.Wecurrentlyhavethecontrollerclassskeleton,butweneedtobeefuptheconstructorandtheauthenticate()method:

app/login/login.component.ts

...

@Component({

selector:'pomodoro-login',

templateUrl:'app/login/login.component.html'

})

exportdefaultclassLoginComponent{

loginForm:ControlGroup;

notValidCredentials:boolean=false;

constructor(

formBuilder:FormBuilder,

privaterouter:Router){

this.loginForm=formBuilder.group({

username:['',Validators.required],

password:['',Validators.required]

});

}

authenticate(){

letcredentials:any=this.loginForm.value;

this.notValidCredentials=!this.loginForm.valid&&

this.loginForm.dirty;

if(credentials.username==='[email protected]'&&

credentials.password==='letmein')

{

this.router.navigateByUrl('/');

}else{

this.notValidCredentials=true;

}

}

}

Thispieceofcodeconformsprettymuchtowhatwealreadysawintheprevioussections.Wehaveskippedtheimportstatementblockinthecodesnippetforbrevitysake,butwecanclearlyseehowweinjecttheRouterandFormBuildertypeddependenciesintoourclass(alsoconfiguringtherouterobjectasaprivateclassmember)usingtheconstructorinjectionpatternfavoredbyAngular2.WithaninstanceoftheFormBuilderclassinplace,wecancreatetheControlGroupwerequire,assignittotheloginFormmember,andbinditlaterontotheNgFormModeldirectivedecoratingtheNgForm(thisis,theformelement,remember?)directiveawaitinginourtemplate.

FillingoutbothinputcontrolsisrequiredbythegraceoftheValidators.requiredstatic

www.EBooksWorld.ir

Page 314: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

methodsfoundintheControlinstances,butthepieceofcodethatrequiresmoreattentionisprobablytheimplementationoftheauthenticate()method.Let'sdeconstructitbitbybit:

letcredentials:any=this.loginForm.value;

this.notValidCredentials=!this.loginForm.valid&&

this.loginForm.dirty;

Ourauthenticate()methodfirstbindsthevalueoftheloginFormcontrolgrouptothecredentialsvariable.RememberthatloginForm.valuewilltaketheshapeofahashobjectlikethis:

{username:"",password:""}

Ontheotherhand,thenotValidCredentialsclasspropertyevaluatestoabooleanvalueasaresultofthevalidityandstateoftheloginFormcontrolgroupstate.Thelastpieceofcodeismorestraightforwardandbasicallyentailscheckingifthecredentialsarevalidinwhichcasetheuserwillberedirectedtotheindexpage(feelfreetoreplacethedestinationpathtowhateveryoufeelismoreappropriate)orthewrongcredentialswarningwillbedisplayedonscreen.

Note

Weareevaluatingthecredentialsagainsthardcodedvaluesforthesakeofsimplicitybutyoushouldnever,andwewouldliketoremarkthewordneverinthisstatement,takethispathforcheckinglogininformationandgrantaccesstosensitivepartsofyourapplications.Thisiswaytooinsecureandcanbeeasilytamperedbymalicioushackerswithnoeffort.Wewillelaboratealittlebitonhandlingauthenticationinthefollowingpages,butwewillsticktohardcodedcredentialstominimizecomplexityinourexamples.InarealscenarioyoushouldalwaysrelyonaremotesecureauthenticationAPIandencryptedtokenstohandlesessionpersistence.Theseconcernsaremoregearedtowardsbackendprogrammingandareobviouslybeyondthescopeofthisbook.

www.EBooksWorld.ir

Page 315: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

ApplyingcustomvalidationtoourcontrolsWehaveseenalreadyhowtoapplyvalidatorstoourcontrolsbyaddingvalidatormethodswheninstantiatingthem,butwestillneedtogiveanswertotworelevantconcerns:

Howtocreateourcustomvalidators?HowtocombinemorethanonevalidatorinasingleControlobject?

Thissectionwillcoverthesetwoissues.AcustomvalidatorisbasicallyafunctionthatexpectsaControlobjectinitssignatureandwillreturneitheranullvalue(iftheControlisvalid)orahashobjectcomprisedbykey/valuepairs.Wewantourloginformtocheckiftheusernameiscorrect.Sincetheusernameissupposedtobeanactuale-mailaddress,maybeitisagoodideatocheckiftheusernameinputcontainsavalide-mailaddressbeforelettingtheusermoveonwithsubmittingtheform.

Tip

Inanormalscenario,wewouldbeusingthepatternvalidatorinstead,butthepurposeofthisexampleistoshowcasethemechanicsofacustomvalidator.

So,let'smoveonandcreateane-mailvalidationfunctionandappendittothebodyofourLoginComponentclass.Thecodeisasfollows:

app/login/login.component.ts

privateemailValidator(control:Control):

{[key:string]:boolean}{

if(!/(.+)@(.+){2,}\.(.+){2,}/.test(control.value)){

return{

'emailNotValid':true

};

}

returnnull;

}

Theformofthereturningobjectwhenthecontrolvalueisnotvalidisnotsimple.ThatobjectwillbeappendedtotheerrorspropertyofthecontrolannotatedwithaValidatorfunction.Thisisquiteconvenientsincewecanthenprovidefurtherinsightsintothesourceoferrorwhenevaluatingvalidityoneachinput,mostlywhentheinputcontrolsfeaturemorethanonevalidationadapter.

Speakingofthedevil,nowweneedtoincludetwovalidatorsinourControlsotheusernameControlvalidatesitsvalueagainsttherequiredValidatorandournewcustomValidatorfunction.TheValidatorhasastaticmethodthatwilldothetrick:

this.loginForm=formBuilder.group({

username:['',

Validators.compose([

www.EBooksWorld.ir

Page 316: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Validators.required,

this.emailValidator

])

],

password:['',Validators.required]

});

www.EBooksWorld.ir

Page 317: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

WatchingstatechangesinourcontrolsSofar,wesawhowwecanconductoperationsdependingoninputchangesinourcontrols,butitwouldbedefinitelynicetotakeamorereactiveapproachdependingoncertainuse-cases.ThegoodnewsisthatboththeControlGroupandtheControltypesexposetwoEventEmittermemberseach,whichwecansubscribeourownObserversto.Then,wewillgetpromptnotificationseverytimeanyControloritswrappingControlGroupobjectupdateseitheritsstatus(pristine,touchedandthelike)orvalue.WearetalkingaboutthestatusChangesandvalueChangesObservables,whicharepartofanyControlorControlGroupobject,andoperateexposingthesameinterfacewesawwhensubscribingtoHTTPobservablesinChapter6,AsynchronousDataServiceswithAngular2.

Let'sseeanactualexample.Itwouldbenicetodisplayareal-timevisualhintastheuserentershis/herusernameinformingifthedataenteredisanactualusernameornot.First,updateourcomponentcontrollertoincludeaBooleanfieldthatwillbeusedlaterontotoggleonandoffavisualnotificationinourtemplate.Thecodeisasfollows:

app/login/login.component.ts

exportdefaultclassLoginComponent{

loginForm:ControlGroup;

notValidCredentials:boolean=false;

showUsernameHint:boolean=false;

constructor(

formBuilder:FormBuilder,

privaterouter:Router){

this.loginForm=formBuilder.group({

username:['',Validators.compose([

Validators.required,

this.emailValidator])],

password:['',Validators.required]

});

constusername=this.loginForm.controls['username'];

username.valueChanges.subscribe(value=>{

this.showUsernameHint=(username.dirty&&

value.indexOf('@')<0);

});

}

//Restofcomponentclassremainsthesame

...

}

app/login/login.component.html

<divclass="form-group">

<inputtype="text"

class="form-control"

placeholder="Yourusername"

www.EBooksWorld.ir

Page 318: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

ngControl="username">

<p*ngIf="showUsernameHint"class="help-block">

Thatdoesnotlooklikeaproperusername

</p>

</div>

PleasenoticehowwearereferringtotheusernameControlbytraversingthecontrolspropertyoftheloginFormobject.Withapointertotheusernameinplace,wecansubscribeobserverstoanyreal-timechangesinitsvalue,andthereforeactaccordingly.Inthiscase,weupdatethevalueoftheshowUsernameHintfield,ifthevalueenteredfulfilstheminimumrequirementsofausername.Whatevervalueittakes,avisualhintwillbedisplayedornotonscreen.

www.EBooksWorld.ir

Page 319: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

MockingaclientauthenticationservicePerhapsthewordmocking,whichisprettycommoninthecontextofunittesting,isabitmisleadingherebutatleastservesasaheads-upforwhatwearegoingtobuildnow.Intheprevioussection,weimplementedaprettysimpleuserauthenticationcheckingbut,inarealscenario,weusuallydelegatealltheheavyliftingonanauthenticationservicethatwrapsallthenecessarytoolsforhandlinguserlogin,logout,andsometimesauthenticationforgrantingaccesstoprotectedareasofourapplication.

Next,wewillcreateasimplifiedversionofsuchserviceandwillputitinchargeofhandlinguserloginalongwiththecomponentwejustcreatedintheprevioussection.Thisservicewillalsomanageauthtokenpersistenceandprovidemethodstocheckiftheuserhasaccessgrantedtosecurepages.

Beforejumpingintothecode,let'ssummarizetheminimumrequirementsthisservicemustfulfil:

WeneeditsAPItoexposeamethodtohandleuserloginUserlogoutmustbehandledaswellbyapublicmethodinthisAPIAthirdmethodorpropertyshouldinformiftheuserisloggedinornotsotheycanproceedtosecuredpagesHavinganobservablepropertyinformingofthecurrentstateoftheactiveuserforauthenticationwillbecomehandytomaketheoverallUImorereactive

Withthesespecificationsinmind,let'sbuildouridealauthenticationservice.Sincethisserviceiscomponent-agnosticandwillhaveanimpactonthewholeapplication,wewillstoreitintheservicesfolderofoursharedcontext,applyingthenamingconventionswealreadyknowandexposingitthroughthesharedfacade:

app/shared/services/authentication.service.ts

import{Injectable,EventEmitter}from'@angular/core';

@Injectable()

exportdefaultclassAuthenticationService{

constructor(){}

login({username,password}):Promise<boolean>{}

logout():Promise<boolean>{}

staticisAuthorized():boolean{}

}

app/shared/shared.ts

importQueueablefrom'./interfaces/queueable';

www.EBooksWorld.ir

Page 320: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

importTaskfrom'./interfaces/task';

importFormattedTimePipefrom'./pipes/formatted-time.pipe';

importQueuedOnlyPipefrom'./pipes/queued-only.pipe';

importAuthenticationServicefrom'./services/authentication.service';

importSettingsServicefrom'./services/settings.service';

importTaskServicefrom'./services/task.service';

constSHARED_PIPES:any[]=[

FormattedTimePipe,

QueuedOnlyPipe

];

constSHARED_PROVIDERS:any[]=[

AuthenticationService,

SettingsService,

TaskService

];

export{

Queueable,

Task,

FormattedTimePipe,

QueuedOnlyPipe,

SHARED_PIPES,

AuthenticationService,

SettingsService,

TaskService,

SHARED_PROVIDERS

};

Asyoucanseeintheresultingfacade,thenewservicewillbecomepartoftheSHARED_PROVIDERSgrouptoken.Then,itwillbeavailableforourapplicationinjector,sincethissymbolisbeingdeclaredintheprovidersarrayofourrootcomponent.

Backtotheserviceclass,weimportedtheInjectabledecorator.Asyouknow,wewillneeditifwewantourAuthServiceclasstobeautomaticallyinstantiatedandinjectedasasingletoninourcomponentsbyAngular(incaseourclassrequiresitsowndependenciesinthefuture).WealsoimporttheEventEmitterclass,whichwewillcoverlaterinthissection.

InthebodyoftheAuthenticationServiceclass,wehavedefinedanemptyconstructorandthreemethodswithnoimplementation(oneofthembeingstatic).Whilethenamesgiveaverygoodhintofthepurposeofeachmethod,perhapsthelastonerequiressomemoreelaboration:TheisAuthorized()methodwillinformiftheuserhaspermissionstoaccesssecuredpages.ThereasonwhyitisstaticisbecausewewillneedtouseitinsomeareaswhereAngular'sdependencyinjectionmachinerycannotreachsonoautomaticproviderinjectionisavailable.

Ourfirstrequirementwastoprovideapublicmethodtohandleuserlogin.Let'sgoforit.Get

www.EBooksWorld.ir

Page 321: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

backtotheAuthenticationServicemoduleandextendtheloginmethodwiththefollowingimplementation:

app/shared/services/authentication.service.ts

login({username,password}):Promise<boolean>{

returnnewPromise(resolve=>{

letvalidCredentials:boolean=false;

//@NOTE:Inarealscenariothischeck

//shouldbeperformedagainstawebservice:

if(username==='[email protected]'&&

password==='letmein'){

validCredentials=true;

window.sessionStorage.setItem('token','eyJhbGciOi');

}

resolve(validCredentials);

});

}

Asyoucanseefromthecommentsinlineinthecode,wearenotsubmittingdataforvalidationtoaremotewebservicealthoughweshoulddefinitelydo.Pleaserecallthewarningweraisedinpreviouschapters:youshouldneverimplementuservalidationthisway.Havingsaidthat,let'sreviewthisimplementation.Inthefirstplace,thereissomethingthatdrawsourattention:thereturningtype.ThismethodissupposedtoreturnaPromiseandthereisagoodreasonforthat.Usually,youwouldalsowanttoimplementanasyncHTTPconnectiontoaremoteservicesoyoucansendtheusercredentialsandwaitforaresponse.Hence,weusetheasynchronousinterfaceintheformofareturningPromise,whichresolvestoaBooleanvalueinformingifthecredentialsprovidedaregoodtoaccessthesystemornot.Ontheotherhand,themethodsignatureisnotanannotatedargument,butadeconstructedobjectinformingthatthismethodwillexpectanytypeorobjectinitspayloadcontainingbothusernameandpasswordproperties.Lastbutnotleast,rightafterconductingourfakeuservalidation,westorearandomtokenontotheuser'sbrowserusingthebrowser'sownsessionstoragelayer.Thisisacommonwayofhandlingauthenticationandusersessionpersistencenowadays,withthesoledifferencethatthetokenisusuallysentinthebodyoftheserverresponseandthereafterissentbackintherequestheadersoneveryinformationrequestmadetotheserver.

Conductingaserver-sideimplementationisbeyondthescopeofthisbook,sowewillnotexplorethattopicingreaterdepth.YoucanrefertothePacktlibraryforfurtherreference.

Nowthatweknowhowtohandleuserlogin,implementingauserlogout()methodthatliterallyreverseswhatthepreviouslogin()methoddidisprettyeasy:

app/shared/services/authentication.service.ts

logout():Promise<boolean>{

returnnewPromise(resolve=>{

window.sessionStorage.removeItem('token');

www.EBooksWorld.ir

Page 322: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

resolve(true);

});

}

Ourthirdrequirementwastoprovideapropertyormethodthatwouldtellusiftheuserisauthenticated,sotheycanproceedtosecuredpages.Keepinginmindthatuserpermissionistiedtotheexistenceorabsenceofasecuritytokenstoredinthebrowserstoragelayer,themethodlogicisreallysimple:

staticisAuthorized():boolean{

return!!window.sessionStorage.getItem('token');

}

Wewilldiscussthismethodandtherationalebehinditsstaticannotationlateron.Now,let'smoveintothelastbitofourservice:providinganObservablethatallowsUIelementsandotherapplicationclientstosubscribetoupdatesintheuserstatus.First,wewillcreateapublicEventEmittermember,whichwecanusetosendnotificationseverytimetheuserlogsinandoutsothatotherclassesandcomponentscansubscribetoitasmereobserversandreacttothoseevents.Obviously,theloginandlogoutmethodswillbeupdatedtoalsosendthecorrespondingnotificationstotheobserversdependingontheactionstakenandtheuserstateatalltimes.

Withallthesechanges,thisisthefinallayoutofourinjectableauthenticationservice:

import{Injectable,EventEmitter}from'@angular/core';

@Injectable()

exportdefaultclassAuthenticationService{

userIsloggedIn:EventEmitter<boolean>;

constructor(){

this.userIsloggedIn=newEventEmitter();

}

login({username,password}):Promise<boolean>{

returnnewPromise(resolve=>{

letvalidCredentials:boolean=false;

//@NOTE:Inanormalscenariothischeck

//shouldbeperformedagainstawebservice:

if(username==='[email protected]'&&

password==='letmein'){

validCredentials=true;

window.sessionStorage.setItem('token','eyJhbGciOi');

}

this.userIsloggedIn.emit(validCredentials);

resolve(validCredentials);

});

}

logout():Promise<boolean>{

returnnewPromise(resolve=>{

www.EBooksWorld.ir

Page 323: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

window.sessionStorage.removeItem('token');

this.userIsloggedIn.emit(false);

resolve(true);

});

}

staticisAuthorized():boolean{

return!!window.sessionStorage.getItem('token');

}

}

www.EBooksWorld.ir

Page 324: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

ExposingournewservicetoothercomponentsWiththeauthenticationserviceprovidernowavailablefromourapplicationinjector,wecanbeginhookingitupinothercomponents:theloginfeatureisthemostlogicalstartingpoint.First,opentheLoginComponentcodeunitandimportthenewAuthenticationServicetokensothatwecanproperlyuseitstypetoinjectitintothecomponent:

app/login/login.component.ts

import{Component}from'@angular/core';

import{

FormBuilder,

ControlGroup,

Validators,

Control}from'@angular/common';

import{Router}from'@angular/router-deprecated';

import{AuthenticationService}from'../shared/shared';

...

Inthesamecodeunit,let'snowupdatetheconstructorpayloadwithanewargumentannotatedwiththeAuthenticationServicetoken,sotheAngular2DImachinerybecomesawarethatthismodulerequiresthattypetobeinjected:

constructor(

formBuilder:FormBuilder,

privaterouter:Router,

privateauthService:AuthService){

//Restofconstructorimplementationremainsunchanged

...

}

Withallthecodeinplace,nowwecanreplaceourauthenticate()methodtoremovethebusinesslogic:

authenticate(){

letcredentials:any=this.loginForm.value;

this.notValidCredentials=!this.loginForm.valid&&

this.loginForm.dirty;

this.authenticationService.login(credentials).then(success=>{

if(success){

this.router.navigateByUrl('/');

}else{

this.notValidCredentials=true;

}

});

}

www.EBooksWorld.ir

Page 325: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

BlockingunauthorizedaccessWithallthisinplace,it'stimetoactuallypreventunloggedusersfromaccessingprotectedcontent.Inourcase,itjustentailsprotectingthetaskeditingformcomponentfromunauthorizedrequests.Inthepreviouschapter,wesawhowtoalloworpreventcomponentinstantiationbymeansofRouterhooks.

Withthatknowledgetohand,protectingthetaskeditorcomponentfromundesiredvisitsbecomesquitesimple.OpentheTaskEditorComponentfile,importournewAuthenticationServiceprovider,andcheckwhethertheuserisauthorizedbybindingtheexecutionofthestaticisAuthorized()methodtotheCanActivatedecorator:

app/tasks/task-editor.component.ts

...

//Otherimportstatementsremainastheyarealready

import{

Task,

TaskService,

AuthenticationService}from'../shared/shared';

@Component({

selector:'pomodoro-tasks-editor',

directives:[ROUTER_DIRECTIVES],

providers:[Title],

templateUrl:'app/tasks/task-editor.component.html',

styles:[`

.ng-valid{border-color:#3c763d;}

.ng-invalid{border-color:#a94442;}

.ng-untouched{border-color:#999999;}

`]

})

@CanActivate(AuthService.isAuthorized)

exportdefaultclassTaskEditorComponentimplementsOnActivate,CanDeactivate,

OnDeactivate{

//Theclassimplementationremainsthesame

...

}

Andthat'sit!Now,anyunloggeduserattemptingtoaccesstheprotectedtaskeditorcomponentwillgetnothing!Ifyoulookcarefullyatthe@CanActivate()decorator,youwillunderstandwhywedefinedtheisAuthorized()methodoftheAuthenticationServiceasstatic.Thereasonreliesonthefactthatwecanonlyinjectdependencysingletonsinourcomponentsbutnotindecorators.Whilethisisnotexactlytrue(wecanleveragetheprovide()injectortobringthedesiredsingletonalthoughthecoderequiredisnowherenearassimpleorneat),thetruthisthatthisimplementationissimple:providingthesamelevelofeffectivenessinaneatandclearfashion.

Note

www.EBooksWorld.ir

Page 326: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

TheidealscenariowouldbetoinjectboththeAuthenticationServiceandtheRouterprovidersintheCanActivateimplementation,andthenredirecttheusertotheloginpageshouldtheuserisnotloggedin.

Unfortunately,atthetimeofwrappingupthewritingofthisbook,thereisstillnoformalsupportfordependencyinjectioninthecontextoftheCanActivaterouterhook.However,thisissueispartofthefeaturesthatwillbecomepartofAngular2Final.Itisquitelikelythatthe@CanActivatedecoratorwillbereplacedbyananalogueinstancemethodofaparentroutingcomponentonceAngular2becomesfinaleventually.Pleaserefertotheofficialdocumentation.

www.EBooksWorld.ir

Page 327: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

MakingtheUIreactivetotheuserauthenticationstatusAllright,sounauthorizeduserscannotaccessthetaskeditorformcomponent.However,havinganunresponsivelinkinourmaintoolbarisdefinitelynotgood,soweshouldleveragetheObservablefeaturesoftheAuthenticationServicetofliptheUIwheneverthereisachangeintheuserloginstatus.

Rightnow,thenavbarfeaturestheLoginlinkthatleadstheuserloginformpage.WhatwewanttodoistohidethePublishTasklinkandmakesureweonlydisplayitwhentheuserisloggedin,nomatterwhereandhowthisloginprocedurewasundertaken.Ontheotherhand,wealsowanttooffertheenduseraLogoutlinkwhenloggedin,sotheycanshutdownhissessioninconfidence.Thislogoutlinkshouldbemadeavailableforloggedinusersonly.AccesstotheprotectedcomponentbyhardcodingURLsisnotaconcern,sincethe@CanActivatedecoratorwilldoitsjobtokeepundesiredusersaway.

Nowthatwehavedescribedtherequirements,let'sputthemintopractice.Openthetoprootcomponentfileandupdateitsimplementation(itremainedemptyuntilnow).WewillneedtheAuthenticationServiceandtheRouterdependenciestodoso,somakesuretoimportthematthetopofthefile:

app/app.component.ts

import{Component}from'@angular/core';

import{

SHARED_PROVIDERS,

AuthenticationService}from'./shared/shared';

import{HTTP_PROVIDERS}from'@angular/http';

import{

ROUTER_PROVIDERS,

RouteConfig,

ROUTER_DIRECTIVES,

Router}from'@angular/router-deprecated';

//Restofimportstatementsremainthesame

Withthetokensproperlydeclaredintheimportstatements,wecanmoveonandprovideanimplementationfortheAppComponentclass:

app/app.component.ts

...

exportdefaultclassAppComponent{

userIsLoggedIn:boolean;

constructor(

privateauthenticationService:AuthenticationService,

privaterouter:Router){

authenticationService.userIsloggedIn.subscribe(isLoggedIn=>{

this.userIsLoggedIn=isLoggedIn;

});

www.EBooksWorld.ir

Page 328: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

}

logout($event):void{

$event.preventDefault();

this.authenticationService.logout().then(success=>{

if(success){

this.router.navigateByUrl('/');

}

});

}

}

WehavedeclaredauserIsLoggedInBooleanfield,whichwillchangeitsvalueeverytimetheobservableuserIsloggedInmemberoftheinjectedAuthenticationServicetypechangesitsvalue.WealsoinjectedtheRoutertypeandcreatedanewcomponentmethodnamedlogout()thatwillwipeouttheusersessionandredirecttheusertotherootpageuponsigningoutfromtheapplication.

ThisgivesusthechancetowrapuptheapplicationUIbyupdatingtherootcomponenttemplatetomakethesensiblelinksfullyreactivetothesechanges:

app/app.component.html

<navclass="navbarnavbar-defaultnavbar-static-top">

<divclass="container">

<divclass="navbar-header">

<strongclass="navbar-brand">MyPomodoroApp</strong>

</div>

<ulclass="navnavbar-navnavbar-right">

<li><a[routerLink]="['TasksComponent']">Tasks</a></li>

<li><a[routerLink]="['TimerComponent']">Timer</a></li>

<li*ngIf="userIsLoggedIn">

<a[routerLink]="['TaskEditorComponent']">PublishTask</a>

</li>

<li*ngIf="!userIsLoggedIn"><a[routerLink]="

['LoginComponent']">Login</a>

</li>

<li*ngIf="userIsLoggedIn">

<ahref="#"(click)="logout($event)">Logout</a>

</li>

</ul>

</div>

</nav>

<router-outlet></router-outlet>

Giveitatry!Reloadtheapplication,checkthelinksavailableatthenavbar,headovertotheloginpage,proceedtologinwiththecredentials,andcheckthenavbaragain...Magic!

www.EBooksWorld.ir

Page 329: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

RunningtheextramileonaccessmanagementApparently,wehaveeverythingthatittakestomoveonwithourapplication.However,asourapplicationgrowsandmoreareasneedtobeprotected,wewillfindourselvesfacingtheburdenoftogglingvisibilityonmoreandmorelinksandhavingtoenableaccessandactivationofcomponentsonebyonebyimplementingthe@CanActivatedecoratoroneach.

Obviously,thisscalesupjustbadly,soitwouldbegreattorelyonaone-size-fits-allsolutioninstead.Unfortunately,atthetimeofwriting,theAngular2frameworkstilldoesnotprovideafeasiblesolutiontotacklewiththisconcern.Ontheotherhand,andaccordingtomodernUX,mostofthetimetheexpectedbehavioristoprovidetheuserwithasmanybrowsingalternativesaspossibleandredirecttheusertotheloginpageonlywhererequired.

Inthislastsection,wewillintroduceagenericworkaroundforthis,basedonthefollowingcriterion:protectingareasofcontentasawholebywrappingtheminsidechildroutesthatwillredirecttheusertotheloginpagewheneverunauthorizedaccessisdetected,whereparameterssuchasthelocationoftheloginpatharefullyconfigurablefromoursolution.

www.EBooksWorld.ir

Page 330: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

BuildingourownsecureRouterOutletdirectiveOurworkaroundisbasedondevelopingourveryownRouterOutletdirectivebyextendingtheRouterOutletclassbakedinAngular2,whichwillbeslightlyrewrittentooverridethebasedirective'sdefaultbehaviorwhenitcomestoproceedingtoactivate(ornot)therequestedcomponent.Allofthisbasedonthecurrentuserloginstatus.

Todoso,wewillcreateanewdirectiveinoursharedcontext,whichwillbeusedacrosstheapplication.Soweneedtoexposeitinthesharedfacadeaswell.

app/shared/directives/router-outlet.directive.ts

import{

Directive,

ViewContainerRef,

DynamicComponentLoader,

Attribute,

Input}from'@angular/core';

import{

Router,

RouterOutlet,

ComponentInstruction}from'@angular/router-deprecated';

import{AuthenticationService}from'../shared';

@Directive({

selector:'pomodoro-router-outlet'

})

exportdefaultclassRouterOutletDirectiveextendsRouterOutlet{

parentRouter:Router;

@Input()protectedPath:string;

@Input()loginUrl:string;

constructor(

_viewContainerRef:ViewContainerRef,

_loader:DynamicComponentLoader,

_parentRouter:Router,

@Attribute('name')nameAttr:string){

super(_viewContainerRef,_loader,_parentRouter,nameAttr);

this.parentRouter=_parentRouter;

}

activate(nextInstruction:ComponentInstruction):Promise<any>{

letrequiresAuthentication=

this.protectedPath===nextInstruction.urlPath;

if(requiresAuthentication&&

!AuthenticationService.isAuthorized()){

this.parentRouter.navigateByUrl(this.loginUrl);

}

returnsuper.activate(nextInstruction);

}

www.EBooksWorld.ir

Page 331: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

}

Here,wearecreatinganewdirectivethatextendsfromtheRouterOutletdirectivewehavebeenusingallthistimeinourapplication.Thisdirectiveneedstobemadeavailableforusefromtheotherfeaturecontextsofourapplication:

app/shared/shared.ts

importQueueablefrom'./interfaces/queueable';

importTaskfrom'./interfaces/task';

importFormattedTimePipefrom'./pipes/formatted-time.pipe';

importQueuedOnlyPipefrom'./pipes/queued-only.pipe';

importAuthenticationServicefrom'./services/authentication.service';

importSettingsServicefrom'./services/settings.service';

importTaskServicefrom'./services/task.service';

importRouterOutletDirectivefrom'./directives/router-outlet.directive';

constSHARED_PIPES:any[]=[

FormattedTimePipe,

QueuedOnlyPipe

];

constSHARED_PROVIDERS:any[]=[

AuthenticationService,

SettingsService,

TaskService

];

constSHARED_DIRECTIVES:any[]=[

RouterOutletDirective

];

export{

Queueable,

Task,

FormattedTimePipe,

QueuedOnlyPipe,

SHARED_PIPES,

AuthenticationService,

SettingsService,

TaskService,

SHARED_PROVIDERS,

RouterOutletDirective,

SHARED_DIRECTIVES

};

Backtothedirectivefile,wecanseeitinheritsthesameconstructoroftheinheritedRouterOutletconstructor.Thus,wewillimportthesamerequiredtokenssothatwecan

www.EBooksWorld.ir

Page 332: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

properlydeclaretheconstructordependenciesandcallthesuperclassconstructorwithsuper().Thecodeisasfollows:

app/shared/directives/router-outlet.directive.ts

constructor(

_elementRef:ElementRef,

_loader:DynamicComponentLoader,

_parentRouter:Router,

@Attribute('name')nameAttr:string){

super(_elementRef,_loader,_parentRouter,nameAttr);

this.parentRouter=_parentRouter;

}

Intheconstructorbody,wedonotonlyinjectthedependencies,wealsoassigntheRouterinstanceinaclassmemberforfutureuse.Thecoreofthesolutionreliesonoverridingtheimplementationofthenativeactivate()method,wherewebasicallyintroduceanauthenticationcheck(thatiswhywealsoimporttheAuthenticationServiceatthetopofthescript)toseeiftherecentlyrequiredcomponentlivesinthedomainoftheprotectedpath.Inthatcase,wewillredirecttheusertotheloginpagelocationshouldtheauthenticationtokenisnotavailablethankstothestaticmethodisAuthorized()exposedbytheAuthenticationService.Inanyevent,themethodwillfinallyreturntheresultofthesuperclass'activatemethod,representedbyaPromiseobject.Thecodeisasfollows:

activate(nextInstruction:ComponentInstruction):Promise<any>{

letrequiresAuthentication=this.protectedPath===nextInstruction.urlPath;

if(requiresAuthentication&&

!AuthenticationService.isAuthorized()){

this.parentRouter.navigateByUrl(this.loginUrl);

}

returnsuper.activate(nextInstruction);

}

WheredowefetchtheseprotectedPathandloginUrlparameters?Asyousawalreadyatthebeginningofthissection,weareexposingtwoinputparametersonthisdirective,whichwillmakeitsinstanceslooklikethis:

<pomodoro-router-outletprotectedPath="edit"loginUrl="/login">

</pomodoro-router-outlet>

So,openupthetoproutercomponentfileandreplacethecurrentRouterOutletdirectiveinstanceinthetemplatewithafewlinesofcode,rightafterimportingtheSHARED_DIRECTIVESsymbolinthedirectivespropertyofthecomponentdecorator:

app/app.component.ts

import{Component}from'angular2/core';

import{

www.EBooksWorld.ir

Page 333: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

SHARED_PROVIDERS,

AuthenticationService,

SHARED_DIRECTIVES}from'./shared/shared';

...

@Component({

selector:'pomodoro-app',

directives:[ROUTER_DIRECTIVES,SHARED_DIRECTIVES],

...

})

app/app.component.html

<navclass="navbarnavbar-defaultnavbar-static-top">

<divclass="container">

<divclass="navbar-header">

<strongclass="navbar-brand">MyPomodoroApp</strong>

</div>

<ulclass="navnavbar-navnavbar-right">

<li><a[routerLink]="['TasksComponent']">Tasks</a></li>

<li><a[routerLink]="['TimerComponent']">Timer</a></li>

<li*ngIf="userIsLoggedIn">

<a[routerLink]="['TaskEditorComponent']">

PublishTask

</a>

</li>

<li*ngIf="!userIsLoggedIn">

<a[routerLink]="['LoginComponent']">Login</a>

</li>

<li*ngIf="userIsLoggedIn">

<ahref="#"(click)="logout($event)">Logout</a>

</li>

</ul>

</div>

</nav>

<pomodoro-router-outlet

protectedPath="tasks/editor"

loginUrl="login">

</pomodoro-router-outlet>

Lastbutnotleast,inordertotryoutthissolution,westillneedtodotwothings:remove(orcommentout)the@CanActivate()decoratorinthetaskeditorcomponentandthentweaktherouterCanDeactivate()routerhooktolooklikethis:

app/tasks/task-editor.component.ts

routerCanDeactivate(

next:ComponentInstruction,

prev:ComponentInstruction){

return!AuthenticationService.isAuthorized()||

this.changesSaved||

confirm('Areyousureyouwanttoleave?');

}

Thisway,wecansmoothlydeactivatethecomponentiftheuserisnotloggedin.Whyshould

www.EBooksWorld.ir

Page 334: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

wedothis?Basically,thisistheflowtheuserwillfollow:

TheuserasksforaprotectedcomponentTherouterdirectivechecksiftheuserisauthenticatedIfloggedinalready,thedirectivedoesnothingandthecomponentisactivatedIfnot,althoughthecomponentwillbeactivated,aredirectionwillbeperformedatthesametimeandtheuserwilllandsafelyonthecomfortoftheloginpage

Inordertoproperlytryoutthissolution,removetheconditionalfromtheroutesetting.Wewanttoactuallydisplaythelinkbutredirecttheuserifnotloggedin.Thecodeisasfollows:

app/app.component.html

...

<ulclass="navnavbar-navnavbar-right">

<li><a[routerLink]="['TasksComponent']">Tasks</a></li>

<li><a[routerLink]="['TimerComponent']">Timer</a></li>

<li>

<a[routerLink]="['TaskEditorComponent']">

PublishTask

</a>

</li>

<li*ngIf="!userIsLoggedIn">

<a[routerLink]="['LoginComponent']">Login</a>

</li>

<li*ngIf="userIsLoggedIn">

<ahref="#"(click)="logout($event)">Logout</a>

</li>

</ul>

...

Note

Thereisanimportantcaveatinthissolution:theprotectedcomponentwillbeactuallyrenderedonscreenbeforebouncingtheusertotheloginpage(wheneverloginisrequired).Thisisdefinitelynotgood,sincethereisachancethattheprotectedcontentswillflickeronscreenifthesecurecomponentinstantiationtakeslongerthanexpectedoreitherthecanDeactivate()methodposessomeconditionsinordertomoveon(hencethechangeweintroduced.

Otherwise,thetransitionwillbesofastthatthechancesarethattheenduserwillnotevennoticethesecomponentshavebeeninstantiated.Inanyevent,usethiswithcareandwatchoutfortheCanDeactivatefunctioninallyourimplementations.

www.EBooksWorld.ir

Page 335: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

SummaryThiswasquitealonganddensecomplicatedchapter,withoutanydoubt.ThemostimportanttakeawayfromthischapterwasthatthereisnotonebutmanyalternativeswhenitcomestodesigningandimplementingformsinAngular2.Someofthemaremoredeclarativeandsomeothersaremodeimperative.Whenshouldyouuseoneorfavoranother?Aswesaidalready,itdependsonwhereandhowyouwanttoaccessandtackletheissueofaccessingtheControlandControlGroupstateandvalidityproperties.

Userauthenticationwasalsocoveredinthischapterandweintroduceddifferentalternativesforcateringwithprotectedareasofoursite.StaytunedandkeepaneyeonthelatestaccomplishmentsmadeintheAngular2arena,sinceitisquitelikelythatnewworkaroundsforthemostcommonusecaseswillspringoutdownthetrack.

Now,getreadytoconfrontthelastlegofourjourneyintoAngular2,wherewewillprovidesomecoverageontheframeworksupportforanimationsbeforewrappingupeverythingbylearningsomeunittestingtechniques—allinthelastchapterofthebook.

www.EBooksWorld.ir

Page 336: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Chapter9.AnimatingComponentswithAngular2Nowadays,animationsareoneofthecornerstonesofmodernuserexperiencedesign.FarfromjustrepresentingavisualeyecandyforbeautifyingtheUI,theyhavebecomeanimportantpartofthevisualnarrative.Animationspavetheroadtoconveymessagesinanon-intrusiveway,becomingacheapbutpowerfultoolforinformingtheuserabouttheunderlyingprocessesandeventsthathappenwhileweinteractwithourapplication.Themomentananimationpatternbecomeswidespreadandtheaudienceembracesitasamodernstandard,wegainaccesstoapricelesstoolforenhancingourapplication'suserexperience.Animationsarelanguage-agnostic,arenotnecessarilyboundtoasingledeviceorenvironment(web,desktopormobile)andarepleasanttotheeyeofthebeholderwhenusedwisely.Inotherwords,animationsareheretostayandAngular2hasastrongcommitmenttothisaspectofmodernvisualdevelopment.

WithallmodernbrowsersembracingthenewerfeaturesofCSS3foranimationhandling,Angular2offerssupportforimplementingimperativeanimationscriptingthroughanincrediblyeasybutpowerfulAPI.Thischapterwillcoverseveralapproachestoimplementinganimationeffects,movingfromleveragingplainvanillaCSSforapplyingclass-basedanimations,toimplementingscriptroutineswhereAngular2takesfullresponsibilityforhandlingDOMtransitions.

Inthischapterwewill:

CreateanimationswithplainvanillaCSSLeverageclass-namedanimationwiththengClassdirectivetobetterhandletransitionsLookatAngular'sbuilt-inCSShooksfordefiningstylesforeachtransitionstateAnimatecomponentswiththeCssAnimationBuilderAPIDesigndirectivesthathandleanimationIntroducengAnimate2.0

Note

Asawordofcaution,bearinmindthatthecurrentimplementationofanimationinAngular2atthetimeofwritingis,toacertainextent,temporary.Inthatsense,allthefunctionalitiesdescribedinthischaptercouldprobablybedeprecatedinthelongrunastheAngular2codebasematures.However,fornow,thereisnoreasontoholdourselvesbackandleveragetheanimationmodulesalreadyavailableintheframework.Thiswillgiveusthepowerandfunctionalityrequiredtoenhancetheoveralluserexperienceofourapplications.

www.EBooksWorld.ir

Page 337: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

CreatinganimationswithplainvanillaCSSTheinceptionofCSS-basedanimationsetanimportantmilestoneinmodernwebdesign.Beforethat,weusedtorelyonJavaScripttoaccommodateanimationsinourwebapplicationsbymanipulatingDOMelementsthroughcomplexandcumbersomescriptsbasedonintervals,timeouts,andloopsofallsorts.Unfortunately,thiswasneithermaintainablenorscalable.

ThenmodernbrowsersembracedthefunctionalitiesbroughtbytherecentCSStransform,transition,keyframes,andanimationproperties.Thisbecameagamechangerinthecontextofwebinteractiondesigninrecenttimes.WhilesupportforthesetechniquesinbrowserssuchasMicrosoftInternetExplorerisfarfromoptimal,therestofthebrowsersinstore(includingMicrosoft'sveryownEdge)providefullsupportfortheseCSSAPIs.

Note

MSIEprovidessupportfortheseanimationtechniquesonlyasofVersion10.

WeassumethatyouhaveabroadunderstandingofhowCSSanimationworksinregardsofbuildingkeyframe-drivenortransition-basedanimations,sinceprovidingcoveragetothesetechniquesisobviouslyoutofthescopeofthisbook.Asarecap,wecanhighlightthefactthatCSS-basedanimationisusuallyimplementedbyanyoftheseapproaches,orevenacombinationofboth:

Transitionproperties,thatwillactasObserversofeitherallorjustasubsetoftheCSSpropertiesappliedtotheDOMelementsimpactedbytheselector.WheneveranyoftheseCSSpropertiesischanged,theDOMelementwillnottakethenewvaluerightaway,butwillexperienceasteadytransitionintoitsnewstate.Namedkeyframe,animations,wherewedefinedifferentstepsoftheevolutionofoneorseveralCSSpropertiesunderauniquename,whichwillpopulatelateronananimationpropertyofagivenselector,beingoneabletosetadditionalparametersasthedelay,durationoftheanimationtweeningorthenumberofiterationsthatsuchanimationismeanttofeature.

Aswecanseeinthetwoaforementionedscenarios,theuseofaCSSselectorpopulatedwithanimationsettingsisthestartingpointforallthingsrelatedtoanimation,andthatiswhatwewilldonow:let'sbuildafancypulseanimationtoemulateaheartbeat-styleeffectinthebitmapthatdecoratesourPomodorotimer.

Wewilluseakeyframe-basedanimationthistime,sowewillbeginbybuildingtheactualCSSroutineinaseparatestylesheet.Theentireanimationisbasedonasimpleinterpolationwherewetakeanobject,scaleitupby10percentandscaleitbackdownagaintoitsinitialstate.Thiskeyframe-basedtweeningisthennamedandwrappedinaCSSclassnamedpulse,whichwillexecutesuchanimationinaninfiniteloopwhereeachiterationtakes1secondtocomplete.

www.EBooksWorld.ir

Page 338: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

AlltheCSSrulesforimplementingthisanimationwillliveinanexternalstylesheetpartofthetimerwidgetcomponent,withinthetimerfeaturefolder:

app/timer/timer-widget.component.css

@keyframespulse{

0%{

transform:scale3d(1,1,1);

}

50%{

transform:scale3d(1.1,1.1,1.1);

}

100%{

transform:scale3d(1,1,1);

}

}

.pulse{

animation:pulse1sinfinite;

}

Asforthispointon,anyDOMelement(intheTimerWidgetComponenttemplate)annotatedwiththisclassnamewillvisuallybeatlikeaheart.Thisvisualeffectisactuallyagoodhintthattheelementisundertakingsomekindofaction,soapplyingittothemainpomodoroiconbitmapinourpomodorotimerwidgetwhenthecountdownisonwillhelpconveythefeelingthatanactivityiscurrentlytakingplaceinalivelyfashion.

Thankfully,wehaveagoodwaytoapplysucheffectonlywhenthecountdownisactive.WeusetheisPausedbindingintheTimerWidgetComponenttemplate.BindingitsvaluetotheNgClassdirectiveinordertorendertheclassnameonlywhenthecomponentisnotpausedwilldothetrick,sojustopenthetimerwidgetcodeunitfileandaddareferencetothestylesheetwejustcreatedandapplythedirectiveasdescribedpreviously:

app/timer/timer-widget.component.ts

...

@Component({

selector:'pomodoro-timer-widget',

styleUrls:['app/timer/timer-widget.component.css'],

template:`

<divclass="text-center">

<imgsrc="/app/shared/assets/img/pomodoro.png"

[ngClass]="{pulse:!isPaused}">

<h3><small>{{taskName}}</small></h3>

<h1>{{minutes}}:{{seconds|number:'2.0'}}</h1>

<p>

<button(click)="togglePause()"class="btnbtn-danger">

{{buttonLabelKey|i18nSelect:buttonLabelsMap}}

</button>

</p>

www.EBooksWorld.ir

Page 339: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

</div>`

})

...

Andthat'sit!RunourpomodoroappandclickontheTimerlinkatthetoptoreachthetimercomponentpageandcheckthevisualeffectliveafterstartingthecountdown.Stopitandresumeitagaintoseetheeffectappliedonlywhenthecountdownisactive.

www.EBooksWorld.ir

Page 340: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

HandlinganimationwithCSSclasshooksAswehavejustseenintheprevioussection,applyingvisualeffectsbasedonCSSclassesisabreezethanksmostlytotheflexibilitywehaveforaddingcustomclassnamesinAngular2.

Ontopofthat,Angularprovidessupportforanimationclasshooks,afunctionalitythatwasalreadyavailableinAngular1.x,underadifferentincarnationthough.Basically,themechanicsisasfollows:DOMelementsmanagedbytemplate-drivendirectives(NgSwitch,NgFor,orNgIf)canbedecoratedwiththeng-animateclassname.Fromthatverymoment,suchelementswillfeatureadditionalclassnamesdependingonthestageoftheanimationsappliedtothatDOMelementinthecontextofthewrappingcomponentlifecycle.

Thislaststatementmightsoundabitoddanddaunting,solet'sseeallthisinactionthroughanactualexample.OpenourTasksComponenttemplateandupdatetherowtagofthetaskslistbyaddingaclassnamedng-animatetothemarkup.Then,dothesamewiththequeuedlabeldisplayedwhenthetaskmodelislinedupinourtasksqueue.Thecodeisasfollows:

app/tasks/tasks.component.html

<tr*ngFor="lettaskoftasks;leti=index"class="ng-animate">

<thscope="row">{{i}}

<span*ngIf="task.queued"class="labellabel-infong-animate">

Queued

</span>

</th>

<!--therestofthetemplateremainsuntouched-->

</tr>

Saveeverythingandthenrunagaintheapplicationwhileinspectingthecodeinthebrowserdevtools.Well,apparentlynothinghappens.Butwearetalkingaboutanimationshere,andasamatteroffacttheunderlyingprocessisexpectingexactlythat,solet'sdecoratetheng-animateclasswithsomeanimation-relatedstylingdefinedinthecomponent'sassociatedstylesheet:

app/tasks/tasks.component.css

h3,p{

text-align:center;

}

.table{

margin:auto;

max-width:860px;

}

.ng-animate{

transition:all0.3sease-in;

}

TheCSSruledefinedpreviouslywillforceanystylingchangeappliedtotheDOMelementto

www.EBooksWorld.ir

Page 341: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

takeplacein10secondsfollowinganease-inalgorithmcurvewhenapplied.Runthecodeagainandinspectthecode:apparentlywe'renowintosomething:allelementsflaggedwiththeng-animateclassnowfeaturesomeclassesthattemporarilydecoratetheelement.Theseclassesareng-enterandng-enter-active,wherethefirstoneisenabledbydefaultwhenrenderingtheelementandthelatterappliedstraightaway.After10seconds,whichisthescopeofthetransitionwedefinedintheCSSrule,bothclassnameswilldisappear,leavingtheng-animateclassastheonlytrackofitsnowextinctexistence.

Basically,wehavethesamebehaviorweimplementedbyhandintheprevioussection,withthesoleexceptionthattheclassname'sbindingisoperatedthistimebyAngular2itself.Withallthisinmind,let'srepurposethestylesheetalittlebittoleveragethebriefexistenceoftheseclassnamesinourDOMandthetransitionpropertytomakeanicefade-ineffectoccuruponloading.Replacethe10secondstransitionperiodbyashorter0.3secondvalue(300milliseconds)andlet'sdefineopacityvaluesfortheinitialng-enterclassnameandthefinalng-enter-activeclassnames.Thecodeisasfollows:

app/tasks/tasks.component.css

...

.ng-animate{

transition:all0.3sease-in;

}

.ng-enter{

opacity:0;

}

.ng-enter-active{

opacity:1;

}

Saveandrerunthecodeexamples.Now,youwillseehowthetasklistsmoothlyfadesinonscreen.Thesameappliestothebluelabelinformingifanygiventaskhasbeenqueuedupornot.

Classhooksavailable

TheclassnameswehavejustseenareknowninAngular-landasclasshooks,whichresonatesfromthedirectiveorroutinglifecycleeventswealreadycovered,andeachoneofthemwillonlyexistwhiletheanimationtakesplace.Wehavefourclasshooks:

ng-enter:ThiswillbeappliedbyAngular'sDOMrenderertoanyelementflaggedwiththeng-animateclassnameuponbeingattachedtotheview.Itusuallywrapsthestylingwerequireourcomponenttofeaturebydefault.ng-enter-active:ThisclassnameistemporarilyattachedtotheelementonruntimerightbeforestartingtheCSSanimationandisremovedautomatically,alongwiththeng-enterclassname,whentheanimationiscompleted.Itusuallydefinesthestylingwestriveourcomponenttoassumebytheendoftheanimationwhichwaspreviouslyresetbyng-enter.ng-leave:Thinkofthisclasshookasthecounterpartofng-enter,butittakesplace

www.EBooksWorld.ir

Page 342: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

whentheelementisabouttobedetachedfromtheview.ng-leave-active:Thisissameasng-enter-active,butittakesplacewhentheelementisabouttobedetachedfromtheview.Theclassnameisappliedtotheelementandwillberemoved,alongwithng-leave,oncethetransitioniscompletedandbeforetheelementisremovedfromtheDOM.

Tip

DonoteverforgetthatthistechniqueisonlyavailableforDOMelementsthatarehandledbytheDomRenderertype,whichisalow-levelclassinchargeofcreating,updating,orremovingnodesandviewsamongothertasks.Inourcase,elementsdecoratedwiththeNgIf,NgFor,orNgSwitchdirectivesaretheonlyfeasiblecandidatesforit.

www.EBooksWorld.ir

Page 343: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

AnimatingcomponentswiththeAnimationBuilderIfyoueverdecidetoinvestigatehowtheCSSclasshookstriggeredbytheng-animateclassbindingworkunderthehood,youwillbepositivelysurprisedbythefactthatalltheheavyliftingiscarriedoutbyaninstanceoftheCssAnimationBuilderclassinstantiatedthroughtheAnimationBuilderAPI.

TheAnimationBuilderclass(whichisaninjectabletypeandthereforesubjecttobeimportedthroughtheconstructorofourcomponents)isafactorytypewhoseAPIprovidesaccesstoinstantiatemorespecializedanimationbuilderssuchastheCssAnimationBuilderclass.ThistypehasaverybroadandpowerfulAPIwhosemethodsallowustoaddorremoveCSSclassnamesinordertotriggertransitionsoranimations,orevenconfigurebyhandanimationparameterssuchasstyles,duration,ordelayonourDOMelementsofchoice.Inthissense,wecandefinegeneralpurposeanimationhandlersandthenusethemasanimationadaptersforanyDOMelement.Inthatsense,animationhandlerscreatedbytheCssAnimationBuilderareagnosticoftheDOMelements.Therefore,asingleanimationadaptercanbeappliedtooneormanyHTMLelements.

So,inordertocreateourownanimationsprogrammaticallyusingonlyJavaScript,wejustneedaninstanceoftheAnimationBuildertype,whichwewillusetoinstantiateaCssAnimationBuilderforcreatingaspecific(HTMLnode-agnostic)animatedtransition.Lastbutnotleast,anaccessortypetotheDOMelementswewanttoanimate.Soundsdaunting?Aquickandeasyexamplewillclarifyallthis.

AgoodwaytoputallthesetothetestistointroduceanewvisualeffectonourPomodorotimer:arenderinganimationeffectwhenthecomponentisloaded,soeverytimeweactivatethePomodoroTimerroute,thecomponentgetsloadedgracefullybyfadinginonscreen.

Let'sbeginbyimportingnewtokensintheblockofimportstatementsofTimerWidgetComponent.WewillneedtofetchElementReffromangular/core,sinceitwillgiveusaccesstotheDOMelementwewanttoanimate.ThenwewillhavetobringtheAnimationBuildersymbol,whichwillbeusedtoinstantiateaCssAnimationBuilderobject.Thelatterisalsoimportedsowecanproperlyannotateourclassmembers.Thecodeisasfollows:

app/timer/timer-widget.component.ts

import{Component,OnInit,ElementRef}from'@angular/core';

import{RouteParams,CanReuse,OnReuse}from'@angular/router-deprecated';

import{SettingsService,TaskService}from'../shared/shared';

import{AnimationBuilder}from'@angular/platform-

browser/src/animate/animation_builder';

import{CssAnimationBuilder}from'@angular/platform-

browser/src/animate/css_animation_builder';

www.EBooksWorld.ir

Page 344: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

...

Note

PleasenotethesourcelocationsforAnimationBuilderandCssAnimationBuilder.Atthetimeofwriting,the@angular/animatebarrelhasnotbeenregisteredinanyspecificbundlewithintheAngular2framework,soweneedtousethefullpathforimportingeachsymbol.ThismaychangeinthefuturesopleaserefertotheofficialAngular2documentationincaseyougeta404errorwhenimportingthetypes.

Withallthesetoolsinplace,wecanbeginsettingupthefoundationforouranimation.Firstlet'screateanewmemberinourcomponentcontrollerclassunderthenameoffadeInAnimationBuilder.ThisnewclasspropertywillrepresenttheCssAnimationBuilderinstanceobjectthatwilldefinetheanimatedtransitionweareabouttobuildnow.First,let'sinjectthedependenciesweneedthroughtheclassconstructor,properlyprefixedwithaccessmodifierssotheybecomeclassmembersinstantly.Thecodeisasfollows:

app/timer/timer-widget.component.ts

...

exportdefaultclassTimerWidgetComponentimplementsOnInit,CanReuse,OnReuse{

minutes:number;

seconds:number;

isPaused:boolean;

buttonLabelKey:string;

buttonLabelsMap:any;

taskName:string;

fadeInAnimationBuilder:CssAnimationBuilder;

constructor(

privatesettingsService:SettingsService,

privaterouteParams:RouteParams,

privatetaskService:TaskService,

privateanimationBuilder:AnimationBuilder,

privateelementRef:ElementRef){

this.buttonLabelsMap=settingsService.labelsMap.timer;

this.fadeInAnimationBuilder=animationBuilder.css();

this.fadeInAnimationBuilder.setDuration(1000)

.setDelay(300)

.setFromStyles({opacity:0})

.setToStyles({opacity:1});

}

//Restoftheclassremainsthesame

}

TheAnimationBuilder.css()methodisusedintheconstructorimplementationtoinstantiateaCssAnimationBuilderobject,anditisdirectlyassignedtothefadeInAnimationBuildermember.Onceassigned,wecanusetheCssAnimationBuilderAPItodefineananimationthatwillkickoffafter300milliseconds,afterwhichwillendurealong1000milliseconds(thatis,asecond)astyletransitiononanygivenDOMelementfromfulltransparencytosolidcolor

www.EBooksWorld.ir

Page 345: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

state.ItisworthremarkingthattheCssAnimationBuilderfeaturesachainableAPI,sowecanconvenientlychainsettingsoneafteranother.

Butaswepointedoutinthebeginningofthissection,thisanimationhandleriscompletelyagnosticoftheelementsitcanbeappliedon.Let'sseehowwecanmakeallthistransitionhappenonanactualDOMelement.Todoso,wecantriggertheanimationusingthestart()methodexposedbytheCssAnimationBuilder.ThismethodexpectsanHTMLelementinitssignature,onwhichtheconfiguredanimationswillbeapplied.GotothengOnInit()hookandaddthefollowingblockofcodeattheend:

app/timer/timer-widget.component.ts

...

ngOnInit():void{

this.resetPomodoro();

setInterval(()=>this.tick(),1000);

lettaskIndex=parseInt(this.routeParams.get('id'));

if(!isNaN(taskIndex)){

this.taskName=this.taskService.taskStore[taskIndex].name;

}

this.fadeInAnimationBuilder.start(

this.elementRef.nativeElement.firstElementChild);

}

...

Aswementioned,thestart()methodwillexpectanHTMLelementonwhichtoapplytheanimationsetupandthatwedobyfeedingthefunctionwiththefirstchildnodeofthenativeElementpropertyoftheelementRefclassmember.TheElementReftypeweimportedintheconstructorgivesusareferencepointertothecomponentdirectiveitself(thisisthePomodoroTimercomponent)inthecontextoftheparentviewortemplateitcurrentlyexists.ItsnativeElementpropertygivesusaccesstotheunderlyingnativeelementofthecomponent,whichisusuallythetemplateHTMLnodestree.Fromthatpointonward,wejustneedtofetchitsfirstElementChildpropertyvalue,whichwillpointtotherootnodeofthecomponenttemplate,andapplytheanimationtweeningonit.Itisimportanttoremarkthatthecomponentwillnotreacttoanyanimationapplieddirectlytoit.Therefore,weneedtotraversethenativeElementpropertyaftertheactualDOMelementwewanttoanimate.WecanintroduceotherDOMselectorshere,leveragingthewebelementAPImethodssuchasdocument.getElementById()ordocument.querySelector(),butthisisdiscouragedsinceitcreatesatightcouplingbetweenthecontrollerandtherenderinglayersandcancompromisefuturemaintainabilityofourcomponents.

Now,saveallyourworkandre-runthePomodoroapplication,accessingthePomodorotimer.Voila!Ourbelovedtimernowgracefullyshowsuponscreenwithasmoothtransition.

www.EBooksWorld.ir

Page 346: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

TheCssAnimationBuilderAPIWehavejustseenhowwecancreateagnosticanimationhandlerswiththeCssAnimationBuilder,andtodosowehaveleveragedsomepowerfulmethodsofitsAPI(suchassetDelay,setDuration,setFromStyles,orsetToStyles).ThisisjustasubsetofallmethodsavailableinitsAPI,whichencompasssomemoremethodsthatarereallyusefulforbuildingcomplexanimations.Thesemethods,includingthesignatures,areasfollows:

setDelay(delay:number):Aswesawinourexample,thissetstheanimationdelayandoverridesanyotheranimationdelaypreviouslydefinedthroughCSS.setDuration(duration:number):Thissetstheanimationdurationand,similartosetDelay(),overridesanyanimationdurationpreviouslydefinedthroughCSS.setFromStyles(from:{[key:string]:any}):Aswesawinourexample,itsetstheinitialstylesfortheanimation,intheformofahashobjectofkey/valuepairs.BecarefulwhenstylingCSSpropertiesnamedwithcamelcase.AllCSSpropertynamesmustbeconvertedtocamelcase.Inthatsense,propertiessuchasmargin-toporbackground-colorwouldturnintomarginToporbackgroundColor.setToStyles(to:{[key:string]:any}):Thissetsthedestinationstylesfortheanimation.setStyles(from:{[key:string]:any},to:{[key:string]:any}):ThisissyntacticsugartodirectlyaccessthefunctionalityprovidedbysetFromStylesandsetToStylesinasinglemethod,whichobviouslysetsstylesforboththeinitialstateandthedestinationstate.addClass(className:string):Thisaddsaclassthatwillremainontheelementaftertheanimationhasfinished.ThismethodisespeciallyusefulforoverridingCSSpropertiesonDOMelementsalreadymanagedbyCSStransitions.removeClass(className:string):Asthecounterpartofthepreviousmethod,thisremovesaclassfromtheelement.addAnimationClass(className:string):Thisaddsatemporaryclassthatwillberemovedattheendoftheanimation.Angular2leveragesthismethodunderthecoversforhandlingtheCSShookstriggeredbytheng-animateclassbindingweoverviewedatthebeginningofthischapter.start(element:HTMLElement):ThisstartstheanimationontheHTMLelementdefinedinthepayloadwhenexecutingthemethodandreturnsanAnimationobject.ThisAnimationobjectexposesveryusefulmethodswecanleveragetoimplementadditionalfunctionalitiesascallbackstobeexecutedwhentheanimationiscomplete,amongotherfunctionalities.

Allthesechainablemethodsallowustobuildreallycomplexanimationhandlersandreusethemthroughoutourapplicationswithnoeffort.

www.EBooksWorld.ir

Page 347: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

TrackinganimationstatewiththeAnimationclassOurapplications'interactivitydoesnotendinthemomentananimationcompletesitsinterpolation.Infact,thiscanbecomethestartingpointofmanyotheranimationsorinteractiveeventsoccurringinouruserinterface.Forthatreason,itisimportantforourapplicationstobeabletodetectwhenananimationcompletesitsinterpolation.

Fortunately,wehavetheAnimationclassforthis,andtheCssAnimationBuilder.start()methodpreciselyreturnsaninstanceofthistype,aswecanseeinthefollowingexample:

app/timer/timer-widget.component.ts

...

ngOnInit():void{

this.resetPomodoro();

setInterval(()=>this.tick(),1000);

lettaskIndex=parseInt(this.routeParams.get('id'));

if(!isNaN(taskIndex)){

this.taskName=this.taskService.taskStore[taskIndex].name;

}

constanimation=this.fadeInAnimationBuilder.start(

this.elementRef.nativeElement.firstElementChild);

animation.onComplete(()=>console.log('Animationcompleted!'));

}

TheAnimationclassexposesinitsAPItheonCompleteeventhandler,whichisfiredassoonastheanimationtriggeredbytheCssAnimationBuilder.start()methodfinishes.So,wecanleverageittotriggeranyotheractioninourapp,suchasloggingoperations(asdepictedintheprecedingexample)orfurtheranimations.

RegardingtheAnimationclass,itisinfacttheonethatcarriesoutallthehardworkofmanagingthetransition.Inthatsense,theCssAnimationBuilderisjustafacadeprovidingafriendlyinterfaceforsettinguptheanimationflow.Whenitcomesthentoperformingfurtheroperationsoncetheanimationisongoingorhasjustfinished,theAnimationclassisouronlyresource.

Allinall,wewillrarelyinteractwiththeAnimationclassbeyondusingitscallbackfunctionsorleveragingitsbuilt-inmethodstohandleCSSclassesorswappingstyleswhentheanimationisover.Ontheotherhand,itisnotanInjectableclasssowecannotinstantiateitusingAngular'sdependencyinjectionsystem.Forthesereasons,wewillnotcoveritsAPIindetailhere.

www.EBooksWorld.ir

Page 348: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

DevelopingcustomanimationdirectivesWehavesaidseveraltimesthatoneoftheadvantagesoftheCssAnimationBuilderAPIisitsreusability.PuttingtogetheranygivenanimationsetupandapplyingitonnotonebutmanyHTMLelementslateronbecomesabreeze.However,directivesaretheperfectsolutionwhenitcomestomanagingreusabilityintheAngulararena.Sowhynotgetthebestofbothworlds?Aswewillseeinthenextexample,wrappinganimationwithindirectivesbecomesthego-tosolutionformanycasescenarios.

Ourlastanimationexampleinthischapterwillintroduceabrandnewcustomdirectiveintoourapplication.TheHighlightdirectiveleveragestheCssAnimationBuilderAPItochangethebackgroundcolorofanygivenDOMelementonthefly,resettingthebackgroundcolortoitsoriginalstateattheendoftheanimation.ThiskindofflashingeffecthasbecomequitewidespreadinmodernwebdesignformakingtheuserawarethatsomethinghasjusthappenedonsomepartoftheUI.

Let'sstartbycreatingthedirectivecontrollerclassfileinsidethedirectivessubfolderofoursharedfeaturesfolder,andpopulateitwiththefollowingscript.PleasenotehowwekeepapplyingthefilenamingconventionsweembraceandhowourdirectivewillbemappedtoaCSSclassselectorthistime:

app/shared/directives/highlight.directive.ts

import{Directive,ElementRef,OnInit}from'@angular/core';

import{AnimationBuilder}from'@angular/platform-

browser/src/animate/animation_builder';

import{CssAnimationBuilder}from'@angular/platform-

browser/src/animate/css_animation_builder';

@Directive({

selector:'.pomodoro-highlight',

providers:[AnimationBuilder]

})

exportdefaultclassHighlightDirective{

cssAnimationBuilder:CssAnimationBuilder;

constructor(

privateanimationBuilder:AnimationBuilder,

privateelementRef:ElementRef){

this.cssAnimationBuilder=animationBuilder.css()

.setDuration(300)

.setToStyles({backgroundColor:'#fff5a0'});

}

ngOnInit(){

letanimation=this.cssAnimationBuilder.start(

this.elementRef.nativeElement

);

www.EBooksWorld.ir

Page 349: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

animation.onComplete(()=>{

animation.applyStyles({backgroundColor:'inherit'});

});

}

}

Thecodeisprettysimpleinitsimplementation.WebasicallybuildadirectivemappedtoaCSSclassselectorwhoseconstructorinstantiatesaCSSanimationconsistingofabackground-colorinterpolationtoacertaintoneofyellow(definedbythe#fff5a0hexvalue)along300milliseconds.Thisisourdesiredflashyeffect.ThengOnInithookmethod,whichisexecutedintheverymomentthatthecomponentaffectedbythisdirectiveisrenderedintheview,firestheanimationandresetsthebackgroundcoloroftheaffectedDOMelementbacktoitsoriginalvalue.Aswecansee,wearetakingadvantageoftheapplyStyles()methodoftheAnimationclass.Thismethod,alongwithothermethodsexposedinitsAPIsuchasaddClasses()orremoveClasses()(bothexpectinganstringarraywiththeclassnamestoaddorremove,respectively),allowsustointeractwiththeCSSbindingsoftheanimatedDOMelement.

Beforemovingon,weneedtoensurethisnewdirectiveisavailablefortherestoffeaturescoexistinginourapplication,soweneedtoexposethisnewdirectivefromthesharedfacademoduleaswell.Thecodeisasfollows:

app/shared/shared.ts

importQueueablefrom'./interfaces/queueable';

importTaskfrom'./interfaces/task';

importFormattedTimePipefrom'./pipes/formatted-time.pipe';

importQueuedOnlyPipefrom'./pipes/queued-only.pipe';

importAuthenticationServicefrom'./services/authentication.service';

importSettingsServicefrom'./services/settings.service';

importTaskServicefrom'./services/task.service';

importRouterOutletDirectivefrom'./directives/router-outlet.directive';

importHighlightDirectivefrom'./directives/highlight.directive';

constSHARED_PIPES:any[]=[

FormattedTimePipe,

QueuedOnlyPipe

];

constSHARED_PROVIDERS:any[]=[

AuthenticationService,

SettingsService,

TaskService

];

constSHARED_DIRECTIVES:any[]=[

RouterOutletDirective,

HighlightDirective

];

www.EBooksWorld.ir

Page 350: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

export{

Queueable,

Task,

FormattedTimePipe,

QueuedOnlyPipe,

SHARED_PIPES,

AuthenticationService,

SettingsService,

TaskService,

SHARED_PROVIDERS,

RouterOutletDirective,

HighlightDirective,

SHARED_DIRECTIVES

};

Asyoucanseeinthenewrefactoredfacade,theHighlightdirectiveispartoftheSHARED_DIRECTIVESsymbol.Therefore,itisavailableforuseonanycomponentalreadydeclaringthattokeninitsdirectivesproperty,suchasTasksComponent,whosetemplateweareabouttotweaknow.

Openthecomponent'stemplateanddecoratethengForelementwithanadditionalclass,asfollows:

app/tasks/tasks.component.html

<tr*ngFor="lettaskoftasks;leti=index"

class="ng-animatehighlight">

Reloadtheapplicationandrejoicebywatchinghowourtasklistflashesuponloadingonscreen,justtoreturnbacktoitsnormalstate.RememberthatwecanapplythesamebehaviortoanyotherpieceofDOMinourapplicationjustbyimportingthedirectiveandbindingtheclassnameintheDOMelementofourchoice.However,youareprobablywonderingwhywebuiltallthisboilerplatefordeliveringjustaflashyeffect.Wouldn'titbeeasiertowrapeverythingaroundaCSSclassperhaps?Well,thatiscorrect...unlessyouwanttointeractwiththeanimation,andthatiswhatwearegoingtodonext.

www.EBooksWorld.ir

Page 351: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

InteractingwithourdirectivefromthetemplateInfairness,havingadirectivetriggeringananimationlikethismakesnosense,butitwouldbegreatifwecouldinteractwiththeanimation.Moreover,ifwecouldactuallyinteractrightfromthetemplate.Todoso,wecanassignanexportabletokennametoourdirectivesowecanrefertoitfromthesameelementintervenedbythedirective.DoyourememberhowweusedtorefertothengFormdirectivewhenhandlingforms?Here,wetakeadvantageofthesametechnique,aswewillseelater.First,proceedtoupdatetheHighlightdirectivebyaddinganewpropertytothedirectivesetupnamedexportAs,withthevaluehighlight.ThevaluedefinedtherewillbecomethenameweshouldrefertowhentryingtoaccessthedirectiveAPIfromwithinoutside.Howshallwedothis?Alittlebitofpatience,firstlet'sditchthengOnInitmethodbychangingitsnametocolorize,therebyremovingthetypefromthefirstlineofimportsandtheinterfaceimplementationfromtheclass.Thecodeisasfollows:

app/shared/directives/highlight.directive.ts

import{Directive,ElementRef}from'@angular/core';

import{AnimationBuilder}from'@angular/platform-

browser/src/animate/animation_builder';

import{CssAnimationBuilder}from'@angular/platform-

browser/src/animate/css_animation_builder';

@Directive({

selector:'.pomodoro-highlight',

providers:[AnimationBuilder],

exportAs:'pomodoroHighlight'

})

exportdefaultclassHighlightDirective{

cssAnimationBuilder:CssAnimationBuilder;

constructor(

privateanimationBuilder:AnimationBuilder,

privateelementRef:ElementRef){

this.cssAnimationBuilder=animationBuilder.css()

.setDuration(300)

.setToStyles({backgroundColor:'#fff5a0'});

}

colorize(){

letanimation=this.cssAnimationBuilder.start(

this.elementRef.nativeElement

);

animation.onComplete(()=>{

animation.applyStyles({backgroundColor:'inherit'});

});

}

}

Reruntheexample.Nowthetabledoesnotflashuponloading,whichisfine.Withallthisin

www.EBooksWorld.ir

Page 352: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

place,it'stimetoupdateourtemplate.First,wewilladdalocalreferencenamedrow(orwhatevernameyoufancy)inthesameHTMLnodeimpactedbythedirective,pointingtohighlightwhichis,aswenowknow,thepublicnameofourdirective.SameasweusedtodowhenreferencingthestateandvalidityofourformswithngForm,nowwecanaccessthedirectivepublicAPI,whichexposesthecolorize()methodwejustcreatedoutoftheformerngOnInitinterfacemethod.Wecansafelyexecutethatmethodnowjustbypointingtothelocaltemplatereference,likethis:

app/tasks/tasks.component.html

<tr*ngFor="lettaskoftasks;leti=index"

class="ng-animatepomodoro-highlight"

#row="pomodoroHighlight"

(click)="row.colorize()">

Reloadtheapplicationandclickonanytask,queuingitupandoff.Itsrowwillflashmomentarily.

Tip

IntheimplementationoftheHighlightdirective,wehardcodedtheCSSvalueinbodyofthedirective.Inordertomakethisdirectivemoresustainableandscalable,weshouldpreventthisapproachinlargerapplications.Asapersonalexercise,wewouldsuggestyoutorefactorthedirectivetouseanattributeselector,suchas[pomodoroHighlight],whosevalueisparsedbyaclassmemberannotatedwiththe@Inputdecorator,soyoucanconfigurecustomflashingcolorswhenbindingthedirectiveinyourcomponenttemplates.

www.EBooksWorld.ir

Page 353: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

LookingintothefuturewithngAnimate2.0MostoftheprocedureswehaveseenalongthischapterinherittheanimationlogicalreadypresentinAngular1.x,whichwasbasedonattachingCSSclassesandrelyingonkeyframeanimationspreviouslycreatedinourstylesheets.Unfortunately,thisapproachfallsshortwhenitcomestoperformanceoptimization,implementingconcurrentanimationsorperformingadvancedinteractions,liketheonesproposedinMaterialDesign,justtonameafewshortcomings.

ThisiswhytheAngularteamisnowworkingonanewsetofmodulesthatwilladdanunparalleledlayerofperformanceandabstractionthroughtheuseofadomainspecificlanguage(DSL)withfullprogrammaticAPI.ThisprojectwilltakeforminwhatisknownasngAnimate2.0,anditwillbereleasedatsomepointlaterin2016.TheapproachproposesaprogrammaticsystemthatreturnstoJavaScript,tappingintoCSSandallowingforprogrammaticcontrolallaround.

Inanutshell,someofthegreatimprovementsthatngAnimate2.0willfeatureareasfollows:

UseananimationfactorytocreateanimationsthroughaprogrammaticAPIwithsupportforconcurrentandsequentialanimationsStaggeringanimationshandled100percentthroughJavaScript,bringingthesamelogicofCSSKeyframesanimationtoJavaScript,compoundedbyuniversalsequencingandanimationchainingPerformancetuningwithdramaticallyfewerreflowsMultiDOM-levelanimationsandasoundcontroloverCSSBettereventintegrationandfullcontrolovertheanimationflow,withtheabilitytofast-forwardorreverseananimationinterpolationprogrammaticallySolidsupportforMaterialDesignandimprovedhandlingofuserinteractionevents,mouseclicks,andsoonAdaptivestylingFulltestabilityandsupportforanimationassertsinourtestspecs

TheseandmanymorefeatureswillbecomeavailableassoonasngAnimate2.0isreleased.Atthetimeofwriting,theAPIisnotpublicyetasngAnimate2.0isaproofofconcept,soprovidingadeepercoverageofitsmechanismisoutofthescopeofthisbook.However,keepaneyeonfutureannouncements,sincengAnimate2.0willbecomeanimportantmilestoneinthewayanimationsarehandledinJavaScript.

www.EBooksWorld.ir

Page 354: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

SummaryInthischapter,wediscussedsomeofthedifferenttechniquescurrentlyavailableforhandlinganimationsinAngular2.First,welookedatthebasicclassname-basedanimationwithCSS3transitionswiththehelpoftheAngularbuilt-indirectives.Then,wesawhowAngularprovidesclass-basedanimationhelpersoutofthebox,sowecaneasilyimplementourowntransitionsbyfollowingsomebasicconventionsinourstylesheets.Then,wediscussedprogrammatically-managedanimationdevelopmentwiththeamazinganimationbuilderscurrentlyexistinginAngular2,withsomeexamplesofhowtotakeadvantageoftheAnimationclasscallbackstotriggeractionsalongtheanimationlifecycle.

ThelastlegofthischapterwasdevotedtogettingsomeinsightsaboutngAnimate2.0,whichissetouttotakeovertheprevioustechniquesinthenearfuture.

Ournextandfinalchapterinthebookwillsumupallwehavelearnedsofarbyintroducingoneofthemostrelevantrequirementsinmodernwebapplicationdevelopment:unittesting.Angular2embracescommonindustrystandardsandlibraries,whileintroducingsomeparticulartoolstoturnunittestingintoanenjoyabletask.

www.EBooksWorld.ir

Page 355: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Chapter10.UnittestinginAngular2Thehardworkofthepreviouschaptershasmaterializedintoaworkingapplicationwecanbeproudof.Buthowcanweensureapainlessmaintainabilityinthefuture?Acomprehensiveautomatedtestinglayerwillbecomeourlifelineonceourapplicationbeginstoscaleupandwehavetomitigatetheimpactofbugscausedbynewfunctionalitiescollidingwiththealreadyexistingones.

Testing(andmorespecificallyunittesting)ismeanttobecarriedoutbythedeveloperastheprojectisbeingdeveloped.However,wewillcoveralltheintricaciesoftestingAngular2modulesinbriefinthischapter,nowthattheprojectisinamaturestage.

Inthischapter,youwillseehowtoimplementtestingtoolstoperformproperunittestingofyourapplicationclassesandcomponents.

Inthischapterwewill:

Lookattheimportanceoftestingand,morespecifically,unittestingReviewthedifferentpartsofaJavaScriptunittestDiscoverJasmine,ourtestingframeworkofchoiceLearnhowtosetupaunittestingenvironmentwithJasmineandSystemJSBuildatestspectestingapipeDesignunittestsforcomponents,withorwithoutdependenciesPutourroutestothetestImplementtestsforservices,mockingdependencies,andstubsInterceptXHRrequestsandprovidemockedresponsesforrefinedcontrolDiscoverhowtotestdirectivesascomponentswithnoviewIntroduceotherconceptsandtoolssuchasKarma,codecoveragetools,orE2Etesting

www.EBooksWorld.ir

Page 356: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Whydoweneedtests?Whatisaunittest?Ifyou'refamiliaralreadywithunittestingandtest-drivendevelopment,youcansafelyskiptothenextsection.Ifnot,let'ssaythatunittestsarepartofanengineeringphilosophythattakesastandforefficientandagiledevelopmentprocessesbyaddinganadditionallayerofautomatedtestingonthecodebeforeitisdeveloped.Thisisthecoreconceptisthateachpieceofcodeisdeliveredwithitsowntest,andbothpiecesofcodearebuiltbythedeveloperwhoisworkingonthatcode.Firstwedesignthetestagainstthemodulewewanttodeliver,checkingtheaccuracyofitsoutputandbehavior.Sincethemoduleisstillnotimplemented,thetestwillfail.Hence,ourjobistobuildthemoduleinsuchawaythatitpassesitsowntest.

Unittestingisquitecontroversial.Whilethereisacommonagreementabouthowbeneficialtest-drivendevelopmentforensuringcodequalityandmaintenanceupontimeis,noteverybodyundertakesunittestingintheirdailypractice.Whyisthat?Well,buildingtestswhilewedevelopourcodecanfeellikeaburdensometimes,particularlywhenthetestwindsupbeingbiggerinsizethanthepieceoffunctionalityitaimstotest.

However,theargumentsfavoringtestingoutnumbertheargumentsagainstit:

Buildingtestscontributestobettercodedesign.Ourcodemustconformtothetestrequirementsandnottheotherwayaround.Inthatsense,ifwetrytotestanexistingpieceofcodeandwefindourselvesblockedatsomepoint,chancesarethatthepieceofcodeweaimtotestisnotwelldesignedandshowsoffaconvolutedinterfacethatrequiressomerethinking.Ontheotherhand,buildingtestablemodulescanhelptoearlydetectsideeffectsonothermodules.Refactoringtestedcodeisthelifelineagainstintroducingbugsinlaterstages.Anydevelopmentismeanttoevolvewithtime,andoneveryrefactortheriskofintroducingabugthatwillonlypopupinanotherpartofourapplicationishigh.Unittestsareagoodwaytoensurethatwecatchbugsinanearlystage,eitherwhenintroducingnewfeaturesorwhenupdatingexistingones.BuildingtestsisagoodwaytodocumentourcodeAPIsandfunctionalities.Andthisbecomesapricelessresourcewhensomeonenotacquaintedwiththecodebasetakesoverthedevelopmentendeavor.

Theseareonlyafewarguments,butyoucanfindcountlessresourcesonthewebaboutthebenefitsoftestingyourcode.Ifyoudonotfeelconvincedyet,giveitatry.Otherwise,let'scontinuewithourjourneyandseetheoverallformofatest.

www.EBooksWorld.ir

Page 357: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

PartsofaunittestinAngular2Therearemanydifferentwaystotestapieceofcode,butwewillfocusonhowAngular2aimstotestmodules.Thefirstthingweneedisatestframework,providingutilityfunctionsforbuildingtestsuitescontainingoneorseveraltestspecseach.

describe('Thesubmitcomponent',()=>{//Testsuite

it('shouldberendereddisabledbydefault',()=>{//Testspec

//...Testspecimplementationgoeshere

});

});

Eachtestspecchecksoutaspecificfunctionalityofthefeaturedescribedinthesuitedescriptionargument,anddeclaresoneorseveralexpectationsinitsbody.Eachexpectationtakesavalue,whichwecalltheexpectedvalue,andiscomparedagainstanactualvaluebymeansofamatcherfunction,whichcheckswhetherexpectedandactualvaluesmatchaccordingly.Thisiswhatwecallanassertion,andthetestframeworkwillpassorfailthespecdependingontheresultofsuchassertion.Thecodeisasfollows:

describe('Thesubmitcomponent',()=>{//Testsuite

it('shouldberendereddisabledbydefault',()=>{//Testspec

//Testassertionbasedonaninstanceofthesubmitcomponent

expect(submitComponent.disabled).toBe(true);

});

});

Inthepreviousexample,submitComponentInstance.disabledwillreturntheactualvaluethatissupposedtomatchtheexpectedvaluedeclaredinthetoBe()matcherfunction.

www.EBooksWorld.ir

Page 358: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

DependencyinjectioninunittestsItisquitecommontoperformseveraloperationsbeforeexecutingeachspec:instantiatingthecomponentorserviceclasswewanttotest,fetchingadependency,declaringamockargument,andsoon.ThemostcommonoperationwhentestingAngular2modulesistooverridethedefaultprovidersoftheinjectorwiththebeforeEachProviders()function.Thisfunctionmustbeexecutedbeforeexecutinganythingelseandtakesthisshape:

describe('Thesubmitcomponent',()=>{

beforeEachProviders(()=>[

TestComponentBuilder,

MyCustomService,

provide(Router,{useClass:RootRouter})

]);

it('shouldberendereddisabledbydefault',()=>{

expect(submitComponent.disabled).toBe(true);

});

});

Inthisexampleandpriortoexecutinganyspec,wearesettingupalistofDIprovidersweneedourtestinjectortobeawareof.Aswecansee,wecanjustdeclareclasstokensorevenoverridedependenciesbybindingatokentoaclass,value,orfactoryfunctionofourchoicewiththeprovide()function.Infact,replacingmoduledependenciesbymocktypeswithfakedataorfunctionalityisindeedaquitecommonpracticewhentesting.

We'restillnotdoneinregardsofDI:whataboutinstantiatingtheobjectswerequirefortestingorresettingthefixtureinformationweneedforeachtest?ThebeforeEach()functionisthenaturalplaceforfetchingthedependenciespreviouslydeclaredinbeforeEachProviders(),instantiatingtheclassesweneedtotest,orperforminganyoperationrequiredforpreparingtheobjectsordatafixturesourtestspecsneed.ItmustbeexecutedafterthebeforeEachProviders()andbeforethetestspecs.Inordertodoso,itleveragestheinject()function.

Inthefollowingpieceofcode,westripdownupabittheexampleprovidedandintroduceallthenecessarysteps.Pleasepayattentiontotheinlinecodecomments,sincetheydescribetheintentofeachblockofcode:

describe('Thesubmitcomponent',()=>{

//Wedeclarethedependenciesweneedtheprovidertomanage

beforeEachProviders(()=>[

TestComponentBuilder

]);

//Avariablewillallocatethetestcomponentbuilder

lettestComponentBuilder:TestComponentBuilder;

www.EBooksWorld.ir

Page 359: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

//Beforeeachspecwefetchaninstanceof

//TestComponentBuilderrightfromtheinjector

beforeEach(inject([TestComponentBuilder],

(tcb:TestComponentBuilder)=>{

testComponentBuilder=tcb;

})

);

//Thedonefunctionargumentconfiguresourspecasasynchronous

it('shouldberendereddisabledbydefault',done=>{

//Thetestcomponentbuilderasynchronouslyresolvestoa

//fixtureobjectwrappinganactualinstanceofSubmitComponent

testComponentBuilder

.createAsync(SubmitComponent)

.then(testComponent=>{

//Wefetchthecomponentinstance

//outfromthefixturewrapper

constsubmitComponent=testComponent.componentInstance;

//Weevaluatetheassertionwithagivenmatcherfunction

expect(submitComponent.disabled).toBe(true);

//finally,thedone()functionresolves

//theasynchronousspec

done();

});

});

});

DonotworryabouttheTestComponentBuilder,wewillcoveritshortly.PayspecialattentiontothewaywegetaninstanceoftheTestComponentBuildertypebypassingtheclasstokentotheinject()function,whichwillreturninitscallbackargumentthedesiredinstancethatwewillbindtoavariableintheouterscopeforlateruseinourspecs.AmoreexpressivewaytofetchobjectinstancesthroughtheinjectorwouldbetofetchanactualInjectorobjectinstanceandthenleverageitsget()helpermethod:

letmyCustomService;//Acustomserviceofours

beforeEach(inject([Injector],(injector:Injector)=>{

myCustomService=injector.get(MyCustomService);

})

);

Ultimately,choosethesyntaxyoufindmoreconvenient.Therestoftheexamplebasicallyentailsthecommonoperationsrequiredwhentestingcomponents:creatingatestingcomponentfixture,fetchinganactualcomponentinstancefromthefixturewrapper,conductingassertionsonthecomponentinstance,andfinallyresolvingtheasynchronousspecwiththedone()command.Wewillseealltheseindetailonceweoverviewcomponenttestinglateroninthischapter.

Therearemoreelementsinaunittest(mocks,spies,andsoon),butwewillseethemost

www.EBooksWorld.ir

Page 360: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

commonsuspectsalongthefollowingpages.

Therearetwoimportantquestions:howdoallthesetestsareexecutedandwheredowecheckthepass/failresults?Forthefirstquestion,let'sjustsaywewillneedatestingframeworkforJavaScript,andatthetimeofthiswriting,Jasmine(http://jasmine.github.io)seemstobethepreferredtestingframeworkintheAngularteam.Regardingthesecondquestion,wewillneedaspecrunner,andJasminepreciselyprovidesanHTML-basedrunnerout-of-the-box,solet'sseehowwecanconfigureJasminetodothis.

www.EBooksWorld.ir

Page 361: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

SettingupourtestenvironmentOurfirststepwillbetodownloadandintegrateJasmineinourproject.Todoso,gototheconsoleinourprojectandexecutethefollowingnpmcommandthatwillinstallthejasmine-corepackage(includingtheexactversionsofthedependenciesrequiredratherthanusingnpm'sdefaultsemverrangeoperator,hencethe--save-exactflag)requiredforourtests:

$npminstalljasmine-core--save-dev--save-exact

Withthepackageinstalled,weneedtoensurethatourprojectcontainsthepropertypingsfortheJasmineglobalobjectsandfunctionmatchers,sothecompilercanrecognizethetypesandthusbuildourtests:

$typingsinstalljasmine--save--ambient

Withallthelibrariesinplace,let'sseehowwewillputtogetheraspecrunnertoprocessourtestsandoutputresults.

www.EBooksWorld.ir

Page 362: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

ImplementingourtestrunnerWearegoingtorunourtestsinabrowser,andinordertodosowewillneedtosetsomebasetestingprovidersthatarespecifictothebrowserplatform.Createafoldernamedtestattherootofourprojectandsavethefollowingfilethere:

test/setup.ts

import{resetBaseTestProviders,setBaseTestProviders}from

'@angular/core/testing';

import{BROWSER_APP_DYNAMIC_PROVIDERS}from"@angular/platform-browser-

dynamic";

import{

TEST_BROWSER_STATIC_PLATFORM_PROVIDERS,

ADDITIONAL_TEST_BROWSER_PROVIDERS

}from'@angular/platform-browser/testing';

resetBaseTestProviders();

setBaseTestProviders(

TEST_BROWSER_STATIC_PLATFORM_PROVIDERS,

[

BROWSER_APP_DYNAMIC_PROVIDERS,

ADDITIONAL_TEST_BROWSER_PROVIDERS

]

);

Wewillimportthisfileintoourtestingimplementationshortlyandthereisnoneedtocoveritindetailatthispoint.Let'sjustsaytheseproviderscontainDOMadaptersrequiredtoperformcertainoperationsinShadowDOMimplementations.

Beforemovingon,itisimportanttohighlightoneoftheconventionsusedinthisbookforAngular2unittests:filenamingandspeclocation.Thereisageneralagreementonsavingourtestspecfilesinthesamelocationwherethetestedmodulelives.Inordertomakethingsevenclearer,wewillnamethespecfilesafterthenameofthecodeunittheytest,andwillappendthe.specsuffixtothefilename.Thisway,itbecomeseasiertolocatethetestscorrespondingtoeachmodule,checkwhatistestedandwhatmoduleslackatest,andgetbetteracquaintedwiththecodebase.Keepinmindthatagoodtestbecomesavaluablepieceofdocumentationbyitself.

Let'sseethisnamingconventioninactionbycreatingatemporarytestspecattherootofyourproject,sowecancheckwhetherourrunnerisworkingfine:

test.spec.ts

describe('Ourtestrunner',()=>{

it('isalive!',()=>{

expect(true).toBe(true);

});

});

www.EBooksWorld.ir

Page 363: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Let'screateourspecrunnerfilenowattherootofourproject.ThespecrunnerisprettysimilartothemainHTMLpageinregardstothescriptresourcesrequiredbutalsoaddsontopofthatalltherequiredincludescriptsfromJasmineandthetestingbundlefromAngular2.Thefollowingimplementationalsodeclaresanarraywithstringpointerstothelocationsofthebrowsertestingsetupfileandtheproofofconcepttestwejustbuilt,besidesfeaturingavariablestoringthepathtothecompiledTypeScriptfiles:

spec-runner.html

<!DOCTYPEhtml>

<html>

<head>

<metahttp-equiv="content-type"

content="text/html;charset=utf-8">

<title>PomodoroAppUnitTests</title>

<linkrel="stylesheet"

href="node_modules/jasmine-core/lib/jasmine-core/jasmine.css">

<scriptsrc="node_modules/jasmine-core/lib/jasmine-core/jasmine.js"></script>

<scriptsrc="node_modules/jasmine-core/lib/jasmine-core/jasmine-html.js">

</script>

<scriptsrc="node_modules/jasmine-core/lib/jasmine-core/boot.js">

</script>

<scriptsrc="node_modules/es6-shim/es6-shim.min.js"></script>

<scriptsrc="node_modules/zone.js/dist/zone.js"></script>

<scriptsrc="node_modules/reflect-metadata/Reflect.js"></script>

<scriptsrc="node_modules/systemjs/dist/system.js"></script>

<scriptsrc="node_modules/rxjs/bundles/Rx.js"></script>

<scriptsrc="systemjs.config.js"></script>

</head>

<body>

<script>

//Yourtypescriptcompiler'outDir'parametervalue

varoutDir='built';

//Enlistyourspecsinthefollowingspeccollectionarray,

//nexttothesetupimportfile,allwithnofileextension

varspecCollection=[

'test/setup',

'test.spec'//Thetestspecwejustbuilt

];

//Weloadallspecsasynchronouslyfromthebuiltfolder

//andevaluatetheiroutputatonce

Promise.all(specCollection.map(specPath=>{

returnSystem.import(`${outDir}/${specPath}`);

}))

.then(window.onload)

.catch(console.error.bind(console));

</script>

</body>

www.EBooksWorld.ir

Page 364: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

</html>

Checktheimportscriptsandfocusonthelastblockofcode.Aswesaid,wedefinethepathtothecompiledfiles(accordingtoourtsconfig.jsonsetup)andanarrayofstringpathstothemainbrowsertestingsetupandourspecs,whichisonlyoneatthismoment.TheSystem.configimplementationisprettystraightforwardandremindsoftheonewealreadyhaveforlaunchingourprojectatindex.html.Ourlastblockisabitmorecomplexandrequiressomemoreattention.Basically,wecreateanarrayofSystem.importcommandsbymappingthesetupandspecpathsarrayintoanotherarraythatcombinesthosepathswiththeoutDirfolderlocation.EachSystem.import()executionreturnsapromise,sotheresultingarraycanbeexecutedaltogetherthroughaPromise.all()command,triggeringawindowonloadevenuponresolving.ItispreciselythiseventthatJasmineiswaitingfortorenderthepass/failreport.

Timetoseeallthisinaction!Savethechanges,runthecompilertohavetranspiledversionsofthethetestsetupfileandourjustcreatedspec,andthenrunthelocalwebserverbybrowsingtothespec-runnerfileURLinthebrowserlocationbar.Youshouldseeawebreportlikethis:

www.EBooksWorld.ir

Page 365: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

SettingupNPMcommandsTestingourmodulesisaniterativeprocess,sowecaneasethingsabitinordertomakethewholeflowsmoother.Todoso,wecansetupsomewrappingcommandsaroundthecommontasksoferasingthecontentsofthebuildfolder,recompilingtheprojectandtriggeringawebserverpointingtoourtestrunner.Asamatteroffact,thepackage.jsonfilecanallocateatestcommand,whichwillalsotriggerpretestandposttestscriptswhenexecuted.Withthisknowledgeinhand,let'supdatethescriptsblockinourpackage.jsonfiletoincludecommandsthatperformalltheaforementionedoperations:

package.json

...

"scripts":{

"postinstall":"npmruntypingsinstall",

"tsc":"tsc",

"tsc:w":"tsc-w",

"lite":"lite-server",

"prestart":"tsc",

"start":"concurrently\"npmruntsc:w\"\"npmrunlite\"",

"typings":"typings",

"pretest":"rm-rf./built&&npmruntsc",

"test":"npmrunlite--open=/spec-runner.html"

},

...

Wheneveryouwanttorunthewholebatchoftestsonourapplication,justgototheconsoleandrunnpmtestattheprojectlocationprompt.Rememberthatthecompilerisnotexecutedinwatchmode,soduringyourdevelopmentsessionsitissafetorunnpmstartinsteadandloadthespecrunnerinaseparatebrowserwindowtochecktheevolutionofourtests,ifrequired.

We'redonewithoursetup!Inthefollowingpages,wewillgothroughdifferentexamplesoftestsstructuresforcomponents,directives,pipes,servicesandroutes,takingsomemodulesofthepomodoroappprojectasanexample.Thereisnoone-size-fits-allpatternwhenunittestingAngular2modules,butdifferentapproachesdependingonthesubjectoftest.Allinall,wewillgofindingrecurringpatternsinourtestsandtherestoftheimplementationwillexplainbyitself,sowewillnotgetintomuchdetaildescribingwhatthecodedoesineachexample.Ultimately,thatiswhatTDDisallabout:explaininghowcodeworksbyputtingittothetest.

www.EBooksWorld.ir

Page 366: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Angular2custommatcherfunctionsWhatkindofcheckscanweperformwithmatcherfunctionsinAngular2?Well,theansweris:quitealot.BesidesthemostcommonmatcherssuchastotoBe()ortoEqual(),wecanuseanybuilt-inJasminematcher.PleaserefertotheJasmineofficialsiteforacompleterundownonthematchersavailable.Ontopofthat,Angular2implementsasetofcustommatcherstoperformcommonoperationswhentestingAngular2specificmodules:

toBePromise():ThisexpectsthevaluetobeaPromisetoBeAnInstanceOf(expected:any):ThisexpectstheactualvaluetobeaninstanceofaclassdefinedintheexpectedargumenttoHaveText(expected:any):ThisexpectstheelementtohaveexactlythetextdefinedintheexpectedargumenttoHaveCssClass(expected:any):ThisexpectstheelementtohavetheCSSclassdefinedintheexpectedargumenttoHaveCssStyle(expected:any):ThisexpectstheelementtobestyledwiththegivenCSSstylesdefinedinthepayloadtoImplement(expected:any):ThisexpectsaclasstoimplementtheinterfaceofthegivenclasstoContainError(expected:any):ThisexpectsanexceptiontocontainanexpectederrortexttoThrowErrorWith(expectedMessage:any):ThisexpectsafunctiontothrowanerrorwiththegivenerrortextwhenexecutedtoMatchPattern(expectedMessage:any):Thisexpectsastringtomatchthegivenregularexpression

AlltheprecedingmatcherfunctionsresolvetoaBooleanvalue.Sometimes,wewillwanttoevaluateinourassertiontheoppositeofthecomparisonactuallyimplementedinthematcherfunction.Forthosecaseswejustneedtoprependa.notmodifierbeforethematcher:

expect(true).not.toBe(false);

www.EBooksWorld.ir

Page 367: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

TestingpipesApipeisbasicallyaclassthatimplementsthePipeTransforminterface,henceexposingatransform()methodthatisusuallysynchronous.Inthatsense,pipesaretheperfectcandidatesfortakingourfirststepsintheworldofunittestingAngular2modules.WewillbeginthenbytestingFormattedTimePipe,creatingaswementionedatestspecrightnexttoitscodeunitfile.Thecodeisasfollows:

app/shared/pipes/formatted-time.pipe.spec.ts

importFormattedTimePipefrom'./formatted-time.pipe';

import{

describe,

expect,

it,

beforeEach}from'@angular/core/testing';

describe('shared:FormattedTimePipe',()=>{

letformattedTimePipe:FormattedTimePipe;

beforeEach(()=>formattedTimePipe=newFormattedTimePipe());

//Specswithassertions

it('shouldexposeatransform()method',()=>{

expect(typeofformattedTimePipe.transform).toEqual('function');

});

it('shouldtransform50into"0h:50m"',()=>{

expect(formattedTimePipe.transform(50)).toEqual('0h:50m');

});

it('shouldtransform75into"1h:15m"',()=>{

expect(formattedTimePipe.transform(75)).toEqual('1h:15m');

});

it('shouldtransform100into"1h:40m"',()=>{

expect(formattedTimePipe.transform(100)).toEqual('1h:40m');

});

});

Weimportthepipetokeninordertoinstantiateitandbindittoavariablebeforerunningeachtest,whichwillgrabthisvariableandintrospectitstypeorwillpassdifferentvaluestoitstransformmethodtocheckwhetherweobtaintheexpectedvalueornot.

Whataboutthetestingcyclewementionedearlier?Inanormalscenario,wewouldaddourtestfirst,leveragingthetestitselftodesignourpipeinterface.Ourtestwouldfailatfirstandthenwewoulddevelopitsimplementation.Whendefiningourassertions,weshouldruntheextramileanddefineassertionswhereourpipehastoconfrontwronginputsorunexpectedscenarios.Asourcodeevolvesandisrefactored,ourtestswillhavetoberefactoredand

www.EBooksWorld.ir

Page 368: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

simplifiedaswell.

Note

ItisworthremarkingthatweareimportingtheJasmineglobalfunctionsfromtheAngular2testingbundle,andnotfromJasmineitself.ThisisbecauseAngular2overridesJasmine'sbuilt-infunctions,althoughthefunctionalityandinterfaceremainsthesame.

SavethefileanddeclareitinourspecCollectionarrayatourspecrunner(youcansafelyremovethereferencetothetestspecsincewewillnolongeruseit):

spec-runner.html

...

varspecCollection=[

'test/setup',

'app/shared/pipes/formatted-time.pipe.spec'

];

...

Inthenextsections,wewillseehowtocreatedifferenttests.

Note

Everytimewecreateanewtestspec,youwillhavetoappenditspath(withoutthefileextension)tothespecCollectionarrayvariableatspec-runner.html.Donotforgetthis,sincewewillnotmakeexplicitreferencetothisrequirementfromnowon.Failingtoincludethereferencewillturnintonon-executionofthetest.Therefore,thespecwillnotshowupinthespecrunnerreport.

www.EBooksWorld.ir

Page 369: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

TestingcomponentsTestingpipesisprettystraightforwardbuttestingcomponentscanbecomeamoredauntingexperiencewhenapproachedforthefirsttime.Therearetoomanyquestions:howcanwetestacomponentthatneedstobebootstrappedsomewhere?GoodnewsisthatAngular2,andmorespecificallyitstestingbundle,containsaclassnamedTestComponentBuilderthatcanbeusedtoinstantiatefullyfunctionalcomponentsofanygiventype,wrappedbyafixtureobjectthatgivesusaccesstothecomponentinstanceobjectoritscompiledHTMLview.Insummary,anyinstanceoftheTestComponentBuilderexposesthefollowingpropertiesandmethods:

debugElement:ThisistheDebugElementassociatedwiththerootelementofthiscomponent.ComponentInstance:Thisreturnstheinstanceobjectoftherootcomponentclass,withfullaccesstoallitspropertiesandmethods.NativeElement:Thisreturnsthenativeelementattherootofthecomponent.DetectChanges():Thistriggersachangedetectioncycleforthecomponent.Wewanttorunthismethodinordertocheckthatthechangesoccurredonthecomponentstateshouldweupdateanyofitspropertiesorexecuteitsmethods.destroy():Thistriggerscomponentdestruction.

Withallthesepointsinmind,let'screateourfirstcomponenttest.TaskIconsComponentisaperfectcandidatetostartwith:

app/tasks/task-icons.component.spec.ts

importTaskIconsComponentfrom'./task-icons.component';

import{

describe,

expect,

it,

inject,

beforeEach,

beforeEachProviders}from'@angular/core/testing';

import{TestComponentBuilder}from'@angular/compiler/testing';

describe('tasks:TaskIconsComponent',()=>{

lettestComponentBuilder:TestComponentBuilder;

//Firstwesetuptheinjectorwithprovidersforourcomponent

//andforafixturecomponentbuilder

beforeEachProviders(()=>[TestComponentBuilder]);

//Wereinstantiatethefixturecomponentbuilder

//beforeeachtest

beforeEach(

inject([TestComponentBuilder],

(_testComponentBuilder:TestComponentBuilder)=>{

testComponentBuilder=_testComponentBuilder;

}

www.EBooksWorld.ir

Page 370: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

));

//Specswithassertions

it('renders1imageforeachpomodorosessionrequired',done=>{

//Wecreateatestcomponentfixtureonruntime

//outfromtheTaskIconsComponentsymbol

testComponentBuilder

.createAsync(TaskIconsComponent)

.then(componentFixture=>{

//WefetchinstancesofthecomponentandtherenderedDOM

lettaskIconsComponent=componentFixture.componentInstance;

letnativeElement=componentFixture.nativeElement;

//Wesetatestvaluetothe@Inputproperty

//andtriggerchangedetection

taskIconsComponent.task={pomodorosRequired:3};

componentFixture.detectChanges();

//theseassertionsevaluatethecomponent'ssurfaceDOM

expect(nativeElement.querySelectorAll('img').length).toBe(3);

//Wefinallydestroythecomponentfixtureand

//resolvetheasynctest

componentFixture.destroy();

done();

})

.catch(e=>done.fail(e));

});

});

Takeaminutetolookattheimportstatementblockatthetopofthefile.Besidesthebasictestingfunctions,weareimportingthesymbolspertainingtotheDI-relatedfunctionsofthetestingbundle,apartfromthesetupmethodsbeforeEachandbeforeEachProviders.First,wewillexecutebeforeEachProviders,passingasanargumentalambdafunctionreturningthearrayofproviderswewillneed.Then,thebeforeEachfunctionusestheinjectortofetchanobjectinstanceoftypeTestComponentBuilder,whichwepreviouslydeclaredasaprovider,andbindsittothetestComponentBuildervariable.Insidethetestspec,weuseobjectvariabletoexecutetheasynchronouspromisifiedcreateAsync()method,whichwillreturnafixturearoundourcomponentofchoice(definedasanargument).Aswesawalreadyatthebeginningofthissection,wecaninspectthefixturetograbanactualinstanceofTaskIconsComponentanditsunderlyingnativeelement.

Then,webegininteractingwiththecomponentbyconfiguringitsproperties.Everytimeweupdateanyinputpropertyorexecuteamethodthatmightchangethecomponentstate,weneedtoexecutethefixture'sdetectChanges()methodtotriggerchangedetectionandhencereflectthatstatechangeinthenativeElement.ThisallowsustotestassertionsusingamatcherfunctiontocomparetheamountofDOMnodesgeneratedagainsttheexpectedamountofnodesconfiguredinthematcher.Finally,wedestroythecomponentandresolvethe

www.EBooksWorld.ir

Page 371: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

asynchronousfunctionspecwe'rein.

Usually,atesthasmorethanonespec,onepereachfunctionalitydescribed,forinstance.Thus,let'saddanotheritstatementrightaftertheonewealreadyhaveinsidethedescribe()suite:

app/tasks/task-icons.component.spec.ts(continued)

...

it('shouldrendereachimagewiththeproperwidth',done=>{

testComponentBuilder.createAsync(TaskIconsComponent)

.then(componentFixture=>{

lettaskIconsComponent=componentFixture.componentInstance;

letnativeElement=componentFixture.nativeElement;

letactualWidth;

taskIconsComponent.task={pomodorosRequired:2};

taskIconsComponent.size=60;

componentFixture.detectChanges();

actualWidth=nativeElement

.querySelector('img')

.getAttribute('width');

expect(actualWidth).toBe('60');

done();

})

.catch(e=>done.fail(e));

});

...

Wecanaddasmanyspecsaswefeelnecessarytoprovideabroadcoverageofallscenarios.

Tip

Debuggingourowntests

Aswecreatemoreandmorespecs,chancesarewewillintroducesomebugsinourowntestimplementations,turningthisintoageneralfailureinourtestreport.Trackingdowntheseissuescanbeabittricky,sothebestwaytoaddressthisscenarioisbyisolatingtestsuitesorspecsexecutionor,allthewayaround,disablingtheexecutionofbrokenteststemporarily.Todoso,wecanusevariationsofthedescribe()andit()functions,byprependingalettertothefunctionname,asfollows:

fdescribe():Thisinstructsthetestrunnertoonlyrunthetestcasesinthisgroup.Itcanbeusedasddescribe()aswell.fit():Thetestrunnerwillonlyexecutethistest,disregardingalltheothers.Itcanbeusedasiit()aswell.xdescribe():Thisinstructstherunnertoexcludethistestsuitefromexecution.xit():Thisinstructstherunnertoexcludethistestspecfromexecution.

www.EBooksWorld.ir

Page 372: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

TestingcomponentswithdependenciesIntheprevioussection,weundertookourveryfirstunittest,takingasimplecomponentwithnodependenciesasanexample.However,componentsandotherAngular2modulesusuallyhavedependenciesinjected.Theunittestneedstoreflectthiscircumstance.Let'slookatTimerWidgetComponentasanexample.Thistinycomponentrequiresallthesedependenciesinjectedthroughitsconstructor:

app/timer/timer-widget.component.ts

...

constructor(

privatesettingsService:SettingsService,

privaterouteParams:RouteParams,

privatetaskService:TaskService,

privateanimationBuilder:AnimationBuilder,

privateelementRef:ElementRef){...

Thus,inordertoinstantiatethecomponentwithinthefixturereturnedbytheTestComponentBuilderfactory,weneedtodeclaretheprovidersforthesedependenciesatthetestinjectorandhavetheminjectedsomehow.Ontopofthat,thecomponenthadsomeimportantnuancesinitsexecution:itissensitivetoURLparamsandoneofitsdependencies(TaskService)performsunderlyingXHRoperationsbymeansoftheHttpmodule.Itneedstobeinterceptedandproperlymocked.

Thismightsoundquitedaunting,butthetruthisthatyoualreadyknowallthecodeproceduresrequiredtoputtogetherthistest.Let'sseeitwithinlinecommentsinthecode:

app/timer/timer-widget.component.test.ts

importTimerWidgetComponentfrom'./timer-widget.component';

import{provide}from'@angular/core';

import{RouteParams}from'@angular/router-deprecated';

import{SettingsService,TaskService}from'../shared/shared';

import{

describe,

expect,

it,

inject,

beforeEach,

beforeEachProviders,

setBaseTestProviders}from'@angular/core/testing';

import{TestComponentBuilder}from'@angular/compiler/testing';

import{Http,BaseRequestOptions}from'@angular/http';

import{MockBackend}from'@angular/http/testing';

import'rxjs/add/operator/map';

describe('timer:TimerWidgetComponent',()=>{

lettestComponentBuilder:TestComponentBuilder;

letcomponentFixture:any;

www.EBooksWorld.ir

Page 373: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

//Firstwesetuptheinjectorwithprovidersforourcomponent

//dependenciesandforafixturecomponentbuilder

//Note:Animationprovidersarenotnecessary

beforeEachProviders(()=>[

TestComponentBuilder,

SettingsService,

TaskService,

//RouteParamsisinstantiatedwithcustomvaluesuponinjecting

provide(RouteParams,{useValue:newRouteParams({id:null})}),

//WereplacetheHttpproviderinjectedlaterinTaskService

MockBackend,

BaseRequestOptions,

provide(Http,{useFactory:

(backend:MockBackend,options:BaseRequestOptions)=>{

returnnewHttp(backend,options);

},

deps:[MockBackend,BaseRequestOptions]

}),

TimerWidgetComponent

]);

//Wereinstantiatethefixturecomponentbuilderbeforeeachtest

beforeEach(inject([TestComponentBuilder],

(_testComponentBuilder:TestComponentBuilder)=>{

testComponentBuilder=_testComponentBuilder;

}

));

});

Youhaveprobablynoticedthereisnotasingletestassertioninthesuite.Wewillgetthereinaminute,butnowlet'sovervieweachpieceofcodeinthescript.Thetestsuiteimplementationcontainseverythingyoualreadyknowaboutinjectingprovidersintests.FirstweusebeforeEachProviders()todeclarealltheprovidersweneedtheinjectortobeawareof.Asyouwillremember,theTimerWidgetComponenthadadependencyonRouteParams,whichallowedustofetchthevalueoftheidquerystringparameter,ifany.Obviously,wenotonlyneedtoinjectthatprovider,butourinjectoroughttoreturnaninstanceofitwiththeparameterproperlypopulatedforourtestingpurposes:

provide(RouteParams,{useValue:newRouteParams({id:null})}),

AbitmoreattentionisrequiredtounderstandhowweaccomplishtheHTTPrequestsperformedbyTaskService.ThedefaultconstructoroftheHttpmodulerequiresabackendconnectionobjectimplementingtheConnectionBackendinterface.Forourtest,weneedtoprovidetheinjectorwithaworkingversionofHttpandthusweleverageMockBackend,whichimplementstheinterfacerequired.Laterinthischapter,wewillseehowwecanleveragethisclasstointerceptXHRrequestsandreturncannedresponsesforourtests.

BaseRequestOptions,

provide(Http,{useFactory:

(backend:MockBackend,options:BaseRequestOptions)=>{

returnnewHttp(backend,options);

www.EBooksWorld.ir

Page 374: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

},

deps:[MockBackend,BaseRequestOptions]

}),

Withalltheprovidersproperlyavailablefromtheinjector,wecanleverageittoinstantiateanewTestComponentBuilderfactoryobjectbeforeexecutinganytest.

beforeEach(inject([TestComponentBuilder],

(_testComponentBuilder:TestComponentBuilder)=>{

testComponentBuilder=_testComponentBuilder;

}

));

Withallthetestingscaffoldedandready,let'sintroduceourfirsttestspec.AppendthefollowingpieceofcoderightafterthebeforeEach(...)blockwithinthebodyofthedescribe(...)suite:

it('shouldinitialisewiththepomodorocounterat24:59',done=>{

//Wecreateatestcomponentfixtureon

//runtimeoutfromthecomponentsymbol

testComponentBuilder

.createAsync(TimerWidgetComponent)

.then(componentFixture=>{

//WefetchinstancesofthecomponentandtherenderedDOM

lettimerWidgetComponent=componentFixture.componentInstance;

letnativeElement=componentFixture.nativeElement;

//WeexecutetheOnInithookandtriggerchangedetection

timerWidgetComponent.ngOnInit();

componentFixture.detectChanges();

//Theseassertionsevaluatethecomponentproperties

expect(timerWidgetComponent.isPaused).toBeTruthy();

expect(timerWidgetComponent.minutes).toEqual(24);

expect(timerWidgetComponent.seconds).toEqual(59);

componentFixture.destroy();

done();//Resolveasynctext

})

.catch(e=>done.fail(e));

});

Asyoucansee,ournewlycreatedspecexecutesseamlesslybyinstantiatingacomponentfixturewrappingthecomponentinstanceandnativeelementwerequire.Weexecutethecomponent'sngOnInit()hookmethodtoforceitsinitializationasifhadbeenrenderedonaview.Then,wetriggerthefixture'sdetectChanges()methodthatwilltriggerachangedetectioncycleonourcomponentinstance,applyinganystatechangeasaresultoftheoperationstakenplacewithinngOnInit().

Thefollowingspec,whichyoucanappendtothedescribe()bodyrightaftertheprevioustestspec,reinforcestheseconcepts:

www.EBooksWorld.ir

Page 375: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

it('shouldinitialisedisplayingthedefaultlabels',done=>{

testComponentBuilder

.createAsync(TimerWidgetComponent)

.then(componentFixture=>{

componentFixture.componentInstance.ngOnInit();

componentFixture.detectChanges();

expect(componentFixture.componentInstance.buttonLabelKey)

.toEqual('start');

expect(componentFixture.nativeElement

.querySelector('button')

.innerHTML.trim())

.toEqual('StartTimer');

componentFixture.destroy();

done();

})

.catch(e=>done.fail(e));

});

www.EBooksWorld.ir

Page 376: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

OverridingcomponentdependenciesforrefinedtestingInthepreviousexamples,wesawhowwecoulddeclareandinjecttheprovidersthatoursubjectsoftestingrequired.Wealsosawhowwecouldleveragetheprovide()functiontopasstheinjectoraninstanceofanygivenprovideralreadypopulatedwiththevalueswerequire.Inthatsense,provide()isnotjustusedtoreplacedependencytypesuponinjectingproviders,buttocustomizethewaywewantaparticularproviderofthatspecifictypetobeinjected.

However,canweoverrideprovidersatatestspeclevel?Theanswerisyes,anditisquiteusefulwhenitcomestomockdependencyvaluesforcertaintests.Inournexttestspec,wewillcontinuetestingthetimerwidgetcomponent.However,wewilloverridetheTaskServiceproviderthistime,replacingitbyanobjectliteralwithmockdata.WewillalsooverridethedefaultRouteParamsinjectionwithanotherinstanceobjectofRouteParams,featuringanactualvaluefortheidparameter.

Addthistestspecrightaftertheprevioustwospecswithinthebodyofthedescribe(...)function:

it('shouldinitialisedisplayingaspecifictask',done=>{

//WemocktheTaskServiceproviderwithsomefakedata

letmockTaskService={

taskStore:[{

name:'TaskA'

},{

name:'TaskB'

},{

name:'TaskC'

}

]

};

testComponentBuilder

.overrideProviders(TimerWidgetComponent,[

provide(RouteParams,{useValue:newRouteParams({id:'1'})}),

provide(TaskService,{useValue:mockTaskService})

])

.createAsync(TimerWidgetComponent)

.then(componentFixture=>{

componentFixture.componentInstance.ngOnInit();

componentFixture.detectChanges();

expect(componentFixture.componentInstance.taskName)

.toEqual('TaskB');

expect(componentFixture.nativeElement.querySelector('small'))

.toHaveText('TaskB');

componentFixture.destroy();

done();

})

www.EBooksWorld.ir

Page 377: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

.catch(e=>done.fail(e));

});

Thecodeisprettyself-explanatory,butlet'stakesomeminutestoanalyzethisblock:

testComponentBuilder.overrideProviders(TimerWidgetComponent,[

provide(RouteParams,{useValue:newRouteParams({id:'1'})}),

provide(TaskService,{useValue:mockTaskService})

])

Basically,weleveragetheoverrideProviders()methodoftheTestComponentBuilderfactory,whichwillexpectinitsfirstargumentthetypeofthecomponentwhoseproviderswewanttooverrideandanarrayofprovidersasasecondargument.Wecaninsertinsuchanarrayanykindoftypeoverrideorreplacementbymeansoftheprovide()function.

InordertogettheoverrideProviders()towork,itparsesthecurrentproviderspropertyofthecomponentdecoratorwhoseproviderswewanttooverride.Ifthecomponentdoesnotfeaturethepropertyinitsdecorator(mostlybecauseallitsdependenciesareinheritedfromtherootinjector),Angularwillthrowanexception.So,forourexample,pleaseincludeanemptyproviderspropertyintheTimerWidgetComponentdecoratorconfiguration:

app/timer/timer-widget.component.ts

@Component({

selector:'pomodoro-timer-widget',

styleUrls:['app/timer/timer-widget.component.css'],

providers:[],

template:`…`

})

ThisissuemightbeaddressedbytheAngular2teaminthefuture,butinthemeantimeweneedtoproceedthisway.

Tip

Needtooverrideatestcomponent'sdirectivesortemplate?

YoucanoverrideotherelementsofthetestcomponentinstancewiththemethodsoverrideTemplate(),overrideView()(whichgivesyouaccesstooverridetheliteraldefiningthingssuchasstyles),oroverrideDirectives().Theirsignaturefollowsprettymuchthesameconvention,wherewedefinefirstthecomponenttypeandthen,asasecondargument,thereplacementweneedforthecomponentoriginalvalue.

PleaserefertotheofficialAPIdocumentationforfurtherdetailsifrequired.

www.EBooksWorld.ir

Page 378: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

TestingroutesJustlikecomponents,routesplayanimportantroleinthewayourapplicationsdeliveranefficientuserexperience.Assuch,testingroutesbecomesparamounttoensureaflawlessperformance.TryingtodeclaretheRoutertokenasaproviderwouldturnintoanexception,sowebasicallyneedtosomehowinformourtestinjectoraboutwhatshouldituseasrootrouterandrootcomponent,bringinginallthedependenciesrequiredbythesetwotypes,asidefrommockingtheLocationservicewithamorespecializedservicewhichistheSpyLocationservice.Thefollowingexampleclarifiesallthis(disregardtheLoginComponentimportfornow,aswewilluseitinthenextsection):

app/app.component.spec.ts

importAppComponentfrom'./app.component';

import{LoginComponent}from'./login/login';

import{provide}from'@angular/core';

import{

describe,

expect,

it,

inject,

beforeEach,

beforeEachProviders}from'@angular/core/testing';

import{

Router,

RouteRegistry,

ROUTER_PRIMARY_COMPONENT}from'@angular/router-deprecated';

import{Location}from'@angular/common';

import{SpyLocation}from'@angular/common/testing';

import{RootRouter}from'@angular/router-deprecated';

describe('AppComponent',()=>{

letlocation:Location,router:Router;

//WeoverridetheRouterandLocationprovidersanditsown

//dependenciesinordertoinstantiateafixturerouterto

//triggerroutingactionsandalocationspy

beforeEachProviders(()=>[

RouteRegistry,

provide(Location,{useClass:SpyLocation}),

provide(Router,{useClass:RootRouter}),

provide(ROUTER_PRIMARY_COMPONENT,{useValue:AppComponent})

]);

//WeinstantiateRouterandLocationobjectsbeforeeachtest

beforeEach(inject([Router,Location],(_router,_location)=>{

router=_router;

location=_location;

}));

//Specswithassertions

it('cannavigatetothemaintaskscomponent',done=>{

www.EBooksWorld.ir

Page 379: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

//Wenavigatetoacomponentandchecktheresulting

//stateintheURL

router.navigate(['TasksComponent'])

.then(()=>{

expect(location.path()).toBe('/tasks');

done();

})

.catch(e=>done.fail(e));

});

});

www.EBooksWorld.ir

Page 380: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

TestingroutesbyURLInourpreviousexample,wetestedhowwecouldnavigatetoanamedrouteandcheckiftherouterresultingURLmatchedtheexpectedstate.Butsometimeswewanttocheckwhetherwehaveactuallyloadedthecomponentweaimedtoreach.Thefollowingexampletakestheoppositeapproach:wenavigatetoaURLandchecktheresultingcomponenttype.DoyourememberweimportedtheLoginComponenttokenpreviously?Now,we'llputittogooduseinournexttestassertion.

Pleaseappendthefollowingspectoapp/app.component.spec.ts:

...

it('shouldbeabletonavigatetothelogincomponent',done=>{

//WenavigatetoanURLandchecktheresultingstateintheURL

router.navigateByUrl('/login').then(()=>{

expect(router.currentInstruction.component.componentType)

.toBe(LoginComponent);

done();

}).catch(e=>done.fail(e));

});

www.EBooksWorld.ir

Page 381: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

TestingredirectionsWhatifwewanttocheckwhetheraredirectionactuallyworks?Noworries,wejustneedtonavigatebyURLtothepathtriggeringtheredirectionandthencheckeitherthetypeoftheroutercurrentinstructionortheresultinglocationpath.

Pleaseappendthefollowingspectoapp/app.component.spec.ts:

it('shouldredirect"/"requeststothetaskscomponent',done=>{

//WenavigatetoanURLandchecktheresultingstateintheURL

router.navigateByUrl('/').then(()=>{

expect(location.path()).toBe('/tasks');

done();

}).catch(e=>done.fail(e));

});

www.EBooksWorld.ir

Page 382: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

TestingservicesServicesarealsoasubjectoftestinginourapplications.OneofthetraitsthatmakeservicessouniqueintheAngular-landisthattheydonotnecessarilyneedtorelyonAngular2itself.UnlessaserviceneedstotakeadvantageofAngular2'sDImachinerytoleverageotherAngularmodulessuchasHTTP,itwillbeprettymucharegular,framework-agnosticJavaScriptclass.

Thismakestestingserviceswithnodependenciesabreeze,where,justlikewedidwhentestingpipes,weneedtoinstantiatetheclassoneverytestspecandtestitspropertiesandthefunctionalityofitsmethods.

Let'sseeallthisthroughanactualexample,wherewewilltestthesimplestservicewehaveonstore:

app/shared/services/settings.service.spec.ts

importSettingsServicefrom'./settings.service';

import{

describe,

expect,

it,

inject,

beforeEach,

beforeEachProviders}from'@angular/core/testing';

describe('shared:SettingsService',()=>{

letsettingsService:SettingsService;

beforeEach(()=>{

settingsService=newSettingsService();

});

it('shouldprovidethedurationforeachpomodoro',()=>{

expect(settingsService.timerMinutes).toBeDefined();

expect(settingsService.timerMinutes).toBeGreaterThan(0);

expect(settingsService.timerMinutes).not.toBeNaN();

});

it('shouldprovidepluralmappingsfortasks',()=>{

consttasksPluralMappings=settingsService.pluralsMap['tasks'];

constactualProperties=Object.keys(tasksPluralMappings).sort()

constexpectedProperties=['=0','=1','other'].sort();

expect(tasksPluralMappings).toBeDefined();

expect(actualProperties).toEqual(expectedProperties);

});

});

Here,wearetestingwhetherthereisanactualnumericvalueconfiguredinthetimerMinutespropertyandwhetherthetasksPluralMappingscontainsallthemappingsweexpectitto

www.EBooksWorld.ir

Page 383: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

have.Wecould(anddefinitelyshould)conductmoretests,butforthetimebeingitisfinetoleavethetestlikethisandthenfocusonanimportantdetail.IfwehadwantedtoleveragetheAngular2testingmachineryforthistest,wecouldhaveinstantiatedtheSettingServiceobjectlikethis:

beforeEachProviders(()=>[SettingsService]);

beforeEach(inject([SettingsService],

(_settingsService:SettingsService)=>{

settingsService=_settingsService;

}

));

Thissyntaxwillremindyouofwhatyousawintheprevioussections.Ultimately,taketheapproachthatsuitsyourcodingstyle.Obviously,thelatterwillrequirelessrefactoringasourserviceclassevolvesandbeginstodemandinjecteddependencies,inwhichcasemakinguseofthebeforeEachProviders()functionwillbecomeparamount.

www.EBooksWorld.ir

Page 384: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

TestingasynchronousservicesThepreviousexampleshowcasedhowwecantestthemostbasicbare-bonesservicewecancomeupwith,butinrealitytwoofthemostcommontraitsofcustomservicesarethattheyusuallyrelyonotherdependenciestoprovidetheirfunctionality.Inaddition,thesefunctionalitiesaremostofthetimebasedonasynchronousmethodsthatconnecttothirdpartyservices.Regardlessoftheinterfaceexposedbytheseasynchronousmembers(callbacks,emittedevents,promises,orobservables),testingservicesliketheseisnothardatall,aswecanseeinthefollowingexamplewherewetestthedifferentAPIendpointsofAuthenticationService.Thecodeisasfollows:

app/shared/services/authentication.service.spec.ts

importAuthenticationServicefrom'./authentication.service';

import{

describe,

expect,

it,

inject,

beforeEach,

beforeEachProviders}from'@angular/core/testing';

describe('shared:AuthenticationService',()=>{

letauthenticationService:AuthenticationService;

beforeEachProviders(()=>[

AuthenticationService

]);

beforeEach(inject(

[AuthenticationService],(_authenticationService)=>{

authenticationService=_authenticationService;

}

));

it('shouldrejectinvalidcredentials',done=>{

authenticationService.login({

username:'foo',

password:'bar'})

.then(success=>{

expect(success).toBeFalsy();

done();

});

});

describe('emitsaneventuponuserauthstatuschanges',()=>{

it('thatshouldbetruthyforsuccessfullogins',done=>{

authenticationService

.userIsloggedIn

.subscribe((authStatus:boolean)=>{

expect(authStatus).toBeTruthy();

done();

www.EBooksWorld.ir

Page 385: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

});

authenticationService.login({

username:'[email protected]',

password:'letmein'

});

});

it('thatshouldbefalsyforfailedlogins',done=>{

authenticationService

.userIsloggedIn

.subscribe((authStatus:boolean)=>{

expect(authStatus).toBeFalsy();

done();

});

authenticationService.login({

username:'foo',

password:'bar'

});

});

});

});

Thetestmustseemlengthy,butitisactuallyquitesimple,sincewearejustputtingintopracticeallthatweknowbynowaboutunittesting.Theonlypartworthremarkingishowwewraptheexpectationassertioninthesubscribe()methodoftheuserIsLoggedIneventemittermember,sotheassertionwillonlybeevaluatedonceaneventisemittedandshinesthroughthesubscriptionfunction.Thecodeisasfollows:

authenticationService

.userIsloggedIn

.subscribe((authStatus:boolean)=>{

expect(authStatus).toBeTruthy();

done();

});

Wethenconductanauthenticationrequestinthefollowingblock,sothesubscribe()functionemitstheexpectedevent:

authenticationService.login({

username:'[email protected]',

password:'letmein'

});

Lastbutnotleast,pleasenoticehowwehavenestedadescribe()suitewithinanotherdescribe()suite.Thisisquitecommonwheneveritmakessensetogrouptestspecsbyareaoffunctionality,easingthetaskofdisablingtestsifrequired.

www.EBooksWorld.ir

Page 386: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

MockingHttpresponseswithMockBackendThepreviousexampleisabitcontrivedsinceourAuthenticationServicemodulewasinfactamockbyitself,withnorealimplementationwhatsoever.Inarealscenario,theAuthenticationServiceshouldimplementanasynchronousmethodsendingaPOSTrequesttoanauthenticationservice.Let'supdateourcurrentserviceimplementationtoincludethisfeatureunderadifferentmethodname,soitdoesnotcollidewiththecurrentlogin()implementationanditstests.Thecodeisasfollows:

app/shared/services/authentication.service.ts

...

httpLogin(credentials):Promise<boolean>{

returnnewPromise(resolve=>{

consturl='/api/authentication';//OryourownAPIAuthurl

constbody=JSON.stringify(credentials);

constheaders=newHeaders({'Content-Type':'application/json'});

constoptions=newRequestOptions({headers:headers});

this.http.post(url,body,options)

.map(response=>response.json())

.subscribe(authResponse=>{

letvalidCredentials:boolean=false;

if(authResponse&&authResponse.token){

validCredentials=true;

window.sessionStorage.setItem(

'token',

authResponse.token

);

}

this.userIsloggedIn.emit(validCredentials);

resolve(validCredentials);

},

error=>console.log(error)

);

});

}

...

InournewimplementationofAuthenticationService,anewasynchronousmethodnamedhttpLogin()performsanactualHTTPPOSTrequesttoanauthserviceofourchoiceandsubmitsthecredentialsinJSONformat,resolvingapromisewithaBooleanvalueafterpersistingthetokeninthelocalsessionstorage.

Tip

Forthesakeofreusability,themethodshouldjustresolvetotheHTTPresponseoncefetchedanditwillbeuptothemethod'sclientstodecidehowtopersisttheinformationcontainedin

www.EBooksWorld.ir

Page 387: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

theresponseandwhattodonext.Forthesakeofbrevity,let'sleaveourmethodlikethis.

Withournewshinyasynchronousmethod,therearesomenewchallenges:

First,weneedtodeclarethedependenciesrequiredforallowingHTTPconnectionsSecond,ournewmethodperformsanactualHTTPrequesttoaremoteservicewhichisoutofthescopeofourtestingcapabilities,soweneedtobeabletointerceptsuchrequestandreturnacustomizedmockedresponsetofulfilourtests

Regardingtheformer,wealreadysawinprevioussectionshowtodeclareprovidersandinstantiateanHttpdependencythroughtheinjectorusingMockBackendinthedependencyconstructor.

However,itistimetoharnessallthefunctionalitythatMockBackendcanprovideforinterceptingHTTPconnectionsandmockresponsesofourown.Let'sgetbacktoauthentication.service.spec.tsandreplacethecurrentimplementationofbeforeEachProviders()andbeforeEach()afterimportingsomemoretokensyou'realreadyfamiliarwith:

app/shared/services/authentication.service.spec.ts(updated)

importAuthenticationServicefrom'./authentication.service';

import{provide}from'@angular/core';

import{

describe,

expect,

it,

inject,

beforeEach,

beforeEachProviders}from'@angular/core/testing';

import{

Http,

BaseRequestOptions,

Response,

ResponseOptions}from'@angular/http';

import{MockBackend,MockConnection}from'@angular/http/testing';

import'rxjs/add/operator/map';

describe('shared:AuthenticationService',()=>{

letauthenticationService:AuthenticationService;

letmockBackend:MockBackend;

beforeEachProviders(()=>[

MockBackend,

BaseRequestOptions,

provide(Http,{

useFactory:(

backend:MockBackend,

options:BaseRequestOptions

)=>{

returnnewHttp(backend,options);

},

www.EBooksWorld.ir

Page 388: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

deps:[MockBackend,BaseRequestOptions]

}),

AuthenticationService

]);

beforeEach(inject(

[MockBackend,AuthenticationService],

(_mockBackend,_authenticationService)=>{

authenticationService=_authenticationService;

mockBackend=_mockBackend;

}

));

it('canfetchavalidtokenwhenqueryingtheAuthAPI',done=>{

constmockedResponse=newResponseOptions({

body:'{"token":"eyJhbGciOi"}'

});

mockBackend.connections.subscribe(

(connection:MockConnection)=>{

if(connection.request.url==='/api/authentication'){

connection.mockRespond(newResponse(mockedResponse));

}

}

);

authenticationService.httpLogin({

username:'foo',

password:'bar'

}).then(success=>{

expect(success).toBeTruthy();

done();

},

error=>done.fail(error)

);

});

//Restoftestspecsremainthesamebelow

//...

First,weimporteverythingthatisrequiredtointeractwithHTTP-basedservices.ThebeforeEachProviders()andthebeforeEach()implementationshavenodifferencewithwhatwealreadysawwhenoverviewingtimer-widget.component.test.ts.PerhapstheonlynuanceworthremarkingisthefactthatwebindanewinstanceofMockBackendtothemockBackendvariableoneverytestexecution.Itisrequiredbecausewewillbeusingitinthenewlyintroducedtestspec.Let'sreviewitinmoredetail.First,wedefineamockedresponsewithsomefakedata.WewillbeusingthismockresponselateronwhenperformingactualHTTPrequests:

constmockedResponse=newResponseOptions({

body:'{"token":"eyJhbGciOi"}'

});

TheMockBackendobjectsexposeaconnectionspropertyoftypeEventEmitter,whichemitsa

www.EBooksWorld.ir

Page 389: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

MockConnectioneventobjecteverytimeitdetectsanattempttoperformaXHRconnectionthroughHttp(which,asweknownow,isusingmockBackendtoperformbackendconnections).ThisMockConnectionobjectcontainsrelevantinformationabouttherequestattemptthatwecanusetorefineourtestandreturnaspecificresponsetailoredtothenecessitiesofourtestingscenario.Todoso,wewillusethemockRespond()oftheconnectionobjectitself.Thecodeisasfollows:

mockBackend.connections.subscribe(

(connection:MockConnection)=>{

if(connection.request.url==='/api/authentication'){

connection.mockRespond(newResponse(mockedResponse));

}

}

);

Withalltheseelementsinplace,performinganactualrequestwithourservicemethodsandevaluatingtheresponsesbecomesaneasytask:

authenticationService.httpLogin({

username:'foo',

password:'bar'

}).then(success=>{

expect(success).toBeTruthy();

done();

},

error=>done.fail(error)

);

www.EBooksWorld.ir

Page 390: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

TestingdirectivesThelastlegofourjourneyintotheworldofunittestingAngular2elementswillcoverdirectives.Directiveswillbeusuallyquitestraightforwardintheiroverallshape,beingprettymuchcomponentswithnoviewattached.Thefactthatdirectivesusuallyworkwithcomponentsgivesusaverygoodideaofhowtoproceedwhentestingthem.

Wecouldcreateastubcomponentforthepurposeofthetestandthenbindthedirectiveonit,eitherdirectlyupondefiningitorbyleveragingtheoverrideDirectives()oftheTestComponentBuilderinstanceobjectwewillcomposeforthetest.Inthatsense,thecomponent,asthehostelementforthedirective,willproxyourtestoperations,sowewillnotdelvedeeperintothisapproachafterreviewingcomponenttestingintheprevioussections.

Anotherapproachistoleveragethehostbindingsandlistenersourdirectivetakesactionon,andtesttheboundmethodstothesedecoratorstoseeiftheyprovidethefunctionalityrequired.Let'slookatanactualexampleofthisapproachbytestingtheTaskTooltipDirectivemodule:

app/tasks/task-tooltip.directive.spec.ts

import{Task}from'../shared/shared';

importTaskTooltipDirectivefrom'./task-tooltip.directive';

import{

describe,

expect,

it,

beforeEach}from'@angular/core/testing';

describe('shared:TaskTooltipDirective',()=>{

lettaskTooltipDirective:TaskTooltipDirective;

beforeEach(()=>{

taskTooltipDirective=newTaskTooltipDirective();

});

it('shouldupdateagiventooltipuponmouseover',done=>{

letmockTooltip={innerText:''};

taskTooltipDirective.task=<Task>{name:'Foo'};

taskTooltipDirective.taskTooltip=mockTooltip;

taskTooltipDirective.onMouseOver();

expect(mockTooltip.innerText).toBe('Foo');

done();

});

it('shouldrestoreagiventooltipuponmouseout',done=>{

letmockTooltip={innerText:'Foo'};

taskTooltipDirective.task=<Task>{name:'Bar'};

taskTooltipDirective.taskTooltip=mockTooltip;

www.EBooksWorld.ir

Page 391: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

taskTooltipDirective.onMouseOver();

expect(mockTooltip.innerText).toBe('Bar');

taskTooltipDirective.onMouseOut();

expect(mockTooltip.innerText).toBe('Foo');

done();

});

});

Here,weinstantiatethedirectiveobjectdirectly,sinceithasnodependenciesthatrequireustousetheinjectorinstead.Sincealltheoperationsperformedbythisdirectivearegovernedbytheclassmethodsdecoratedwith@HostListener()decorators,wejustneedtofeedthedirectiveclassinputmemberswithmockdataandseeifweobtainthedesiredbehavior.

www.EBooksWorld.ir

Page 392: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

TheroadaheadThislasttestexamplewrapsupourjourneyintounittestingwithAngular2,butkeepinmindthatwehavebarelyscratchedthesurface.TestingwebapplicationsingeneralandAngular2applicationsinparticularposesamyriadofscenariosthatneedaspecificapproachmostofthetimes.Rememberthatifaspecifictestrequiresacumbersomeandconvolutedsolution,weareprobablyfacingagoodcaseforamoduleredesigninstead.

Whereshouldwegofromhere?ThereareseveralpathstocompoundourknowledgeofwebapplicationtestinginAngular2andbecomegreattestingninjas.

www.EBooksWorld.ir

Page 393: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

UsingJasmineincombinationwithKarmaSofar,wehaveusedtheJasmineHTMLspecrunnertoexecuteourtestsandgetaresultsreport.Whilethisisperfectlyfineforsmallerprojects,theHTMLspecrunnermightnotbethebestsolutionforbiggerprojects,especiallyifwewantourteststobere-executedautomaticallywhencodechangesorweneedtohookupourtestslayerwithacontinuousintegrationserver.

Atthispointyouwillwanttouseamorepowerfulandfasterspecrunnerwithoutcompromisingyourproject.Forthatreason,yourbestbetmightbepickingupKarmaasaspecrunner.UsedbytheAngularteamitself,itplayswellwithJasmineandothertestingframeworkssuchasMochaorQUnit.Italsofeaturesasimplebutpowerfulconfigurationsetupwithsupportforautomaticspecscanning,filewatching,multiplereportoutputs,andadvancedextensibilitywithplugins.

Forthoseinneedtohookuptheirapplicationwithcontinuousintegrationservers,KarmaalsoprovidesadaptersforJenkins,Travis,orSemaphore.

Youcanfindfurtherinformationathttps://karma-runner.github.io.

www.EBooksWorld.ir

Page 394: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

IntroducingcodecoveragereportsinyourteststackHowcanweknowhowfardoourtestsgoontestingtheapplication?Arewesurewearenotleavinganypieceofcodeuntestedandifso,isitrelevant?Howcanwedetectthepiecesofcodethatfalloutsidethescopeofourcurrenttestssowecanbetterassessiftheyareworthtestingornot?

Theseconcernscanbeeasilyaddressedbyintroducingcodecoveragereportinginourapplicationtestsstack.Acodecoveragetoolaimstotrackdownthescopeofourunittestinglayerandproduceaneducatedreportinformingoftheoverallreachofyourtestspecsandwhatpiecesofcodestillremainuncovered.

Thereareseveraltoolsforimplementingcodecoverageanalysisinourapplications,Blanket(http://blanketjs.org),andIstanbul(https://gotwarlost.github.io/istanbul)themostpopularonesatthistime.Inbothcases,theinstallationprocessisprettyquickandeasy.

www.EBooksWorld.ir

Page 395: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

ImplementingE2EtestsInthischapter,wesawhowwecouldtestcertainpartsoftheUIbyevaluatingthestateoftheDOM.Thisgivesusagoodideaofhowthingswouldlooklikefromtheenduser'spointofview,butultimatelythisisjustanuneducatedguess.

End-to-end(E2E)testingisamethodologyfortestingwebapplicationsusinganautomatedagentthatwillprogrammaticallyfollowtheenduser'sflowfromstarttofinish.Contrarytowhatunittestingposes,thenuancesofthecodeimplementationarenotrelevanthere,sinceE2Etestingentailstestingourapplicationfromstarttofinishfromtheuser'sendpoint.Thisapproachallowsustotesttheapplicationinanintegratedway.Whileunittestingfocusesonthereliabilityofeachparticularpieceofthepuzzle,E2Etestingdoesassesstheintegrityofthepuzzleasawhole,findingintegrationissuesbetweencomponentsthatarefrequentlyoverlookedbyunittests.

TheAngularteambuiltforthepreviousincarnationoftheAngularframeworkapowerfultoolnamedProtractor(https://docs.angularjs.org/guide/e2e-testing),whichisdefinedasfollows:

"..anendtoendtestrunnerwhichsimulatesuserinteractionsthatwillhelpyouverifythehealthofyourAngularapplication."

ThetestssyntaxwillbecomeprettyfamiliarsinceitalsousesJasmineforputtingtogethertestspecs.Unfortunately,E2Esitsoutsidethescopeofthisbook,butthereareseveralresourcesyoucanrelyontoexpandyourknowledgeonthesubject.Inthatsense,werecommendthebookAngular2Test-drivendevelopment,PacktPublishing,whichprovidesbroadinsightsontheuseofProtractortocreateE2EtestsuitesforourAngular2applications.

www.EBooksWorld.ir

Page 396: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

SummaryWeareattheendofourjourney,andit'sbeenalongbutexcitingonewithoutanyshadeofdoubt.Inthischapter,yousawtheimportanceofintroducingunittestinginourAngular2applications,thebasicshapeofaunittest,andtheprocessofsettingupJasmineforourtests.Youalsosawhowtocodepowerfultestsforourcomponents,directives,pipes,routes,andservices.WealsodiscussednewchallengesinyourpathformasteringAngular2.Itisfairtosaythatthereisstillalongroadahead,anditisdefinitelyanexitingone.

Theendofthischapterisalsotheendofthisbook,buttheexperiencecontinuesbeyonditsboundaries.Angular2isstillaprettyyoungframeworkandassuchallthegreatthingsthatitwillbringtothecommunityareyettobecreated.Hopefully,youwillbeoneofthosecreators.Ifso,pleaselettheauthorknow.

Thanksfortakingthetimeforreadingthisbook.

www.EBooksWorld.ir

Page 397: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

IndexA

Angular2defining/Afreshstart,Hello,Angular2!URL/SettingupourworkspaceTypeScriptclasses/TypeScriptclassesmetadatadecorators,defining/IntroducingmetadatadecoratorsTypeScript,compilingintobrowser-friendlyJavaScript/CompilingTypeScriptintobrowser-friendlyJavaScriptHTMLcontainer/TheHTMLcontainerexamples/Servingtheexamplesofthisbooktemplate,editing/Puttingeverythingtogetherdirectives/DirectivesinAngular2dependencyinjection,defining/HowdependencyinjectionworksinAngular2dependencies,injectingacrosscomponenttree/Injectingdependenciesacrossthecomponenttreeproviders,overridingininjectorshierarchy/Overridingprovidersintheinjectorhierarchyinjectorsupport,extendingtocustomentities/Extendinginjectorsupporttocustomentitiesapplications,initializingwithbootstrap()/Initializingapplicationswithbootstrap()references/IntroducingthePomodoroAppdirectorystructurematcherfunctions,defining/Angular2custommatcherfunctions

Angular2cheatsheetURL/Someextrasyntacticsugarwhenbindingexpressions

Angular2componentsdefining/DivingdeeperintoAngular2componentsproductivity,improving/Improvingproductivitycomponentmethods/Componentmethodsanddataupdatesdataupdates/Componentmethodsanddataupdatesinteractivity,adding/Addinginteractivitytothecomponentdataoutput,improvinginview/ImprovingthedataoutputintheviewandpolishingtheUI

Angular2routerbundleimplementing/AddingsupportfortheAngular2router

AnimationBuildercomponents,animatingwith/AnimatingcomponentswiththeAnimationBuilderCssAnimationBuilderAPI/TheCssAnimationBuilderAPIanimationstate,trackingwithAnimationclass/TrackinganimationstatewiththeAnimationclass

animationscreating,withplainvanillaCSS/CreatinganimationswithplainvanillaCSS

www.EBooksWorld.ir

Page 398: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

handling,withCSSclasshooks/HandlinganimationwithCSSclasshooksapplication

bootstrapping/Bootstrappingtheapplicationapplication,Angular2

refactoring/RefactoringourapplicationtheAngular2wayapplications,withbootstrap()

defining/Initializingapplicationswithbootstrap()switching,betweendevelopmentandproductionmodes/SwitchingbetweendevelopmentandproductionmodesAngular2built-inchangedetectionprofiler,enabling/EnablingAngular2'sbuilt-inchangedetectionprofiler

applications,withmodulesorganizing/Organizingourapplicationswithmodulesinternalmodules/Internalmodulesexternalmodules/Externalmodules

Arrayabout/Array

asynchronousinformationhandling,strategiesused/Strategiesforhandlingasynchronousinformation

Asyncpipe/TheasyncpipeAtScript

about/DecoratorsinTypeScript

www.EBooksWorld.ir

Page 399: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

BBlanket

URL/IntroducingcodecoveragereportsinyourteststackBootstrap/Installingdependenciesbootstrapmethod

actions,defining/Puttingeverythingtogether

www.EBooksWorld.ir

Page 400: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

CCanDeactivaterouterhook

bypassing,uponformsubmission/BypassingtheCanDeactivaterouterhookuponsubmittingforms

childroutersdefining/Definingchildrouterslinking,tochildroutes/Linkingtochildroutes

classanatomydefining/Anatomyofaclass–constructors,properties,methods,getters,andsetters

classdecoratorfunctionsignatureextending/Extendingtheclassdecoratorfunctionsignature

classdecoratorsabout/Classdecorators

classesabout/Classes,interfaces,andclassinheritance

classhooksabout/Classhooksavailabledefining/Classhooksavailable

classinheritanceabout/Classes,interfaces,andclassinheritanceclasses,extendingwith/Extendingclasseswithclassinheritance

classstatementelements/Anatomyofaclass–constructors,properties,methods,getters,andsetters

clientauthenticationservicemocking/Mockingaclientauthenticationserviceexposing,toothercomponents/Exposingournewservicetoothercomponentsunauthorisedaccess,blocking/Blockingunauthorizedaccessuserauthenticationstatus,reflectingonUI/MakingtheUIreactivetotheuserauthenticationstatus

codecoveragereportsdefining,inteststack/Introducingcodecoveragereportsinyourteststack

componentscreating/Creatingourcomponentstimercontext/Thetimercontexttaskscontext/Thetaskscontexttoprootcomponent,defining/Definingthetoprootcomponenttesting/Testingcomponentspropertiesandmethods/Testingcomponentstesting,withdependencies/Testingcomponentswithdependenciescomponentdependencies,overridingforrefinedtesting/Overridingcomponentdependenciesforrefinedtesting

www.EBooksWorld.ir

Page 401: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

componenttreedefining/Introducingthecomponenttree

ControlGroupsabout/Controls,ControlGroups,andtheFormBuilderclasscontrolgroups,definingwith/DefiningcontrolgroupsimperativelywithControlGroup

controlinteractiontracking/Trackingcontrolinteractionandvalidatinginputchanges,trackingwithlocalreferences/Trackingchangeswithlocalreferences

Controlsabout/Controls,ControlGroups,andtheFormBuilderclass,IntroducingControlsandValidatorscreating,inDOMwithngControldirective/ControlsintheDOM–thengControldirectivegrouping,inDOMwithNgControlGroupdirective/GroupingcontrolsintheDOMwithNgControlGroupDOMandcontroller,connectingwithngFormModel/ConnectingtheDOMandthecontrollerwithngFormModel

CSSclasshooksanimation,handlingwith/HandlinganimationwithCSSclasshooks

CSSspecificityURL/Managingviewencapsulation

CSSstylingencapsulating/EncapsulatingCSSstylingstylesproperty/ThestylespropertystyleUrlsproperty/ThestyleUrlspropertyinlinestylesheets/Inlinestylesheetsviewencapsulation,managing/Managingviewencapsulation

currencypipe/Thecurrencypipecustomanimationdirectives

developing/Developingcustomanimationdirectivesinteracting,fromtemplate/Interactingwithourdirectivefromthetemplate

customdirectivesbuilding/Buildingourowncustomdirectivesanatomy/Anatomyofacustomdirectivetasktooltipcustomdirective,building/Buildingatasktooltipcustomdirectivenamingconventions/Awordaboutnamingconventionsforcustomdirectivesandpipes

customelementsnaming/Componentmethodsanddataupdates

customeventsused,forcommunicatingbetweencomponents/Communicatingbetweencomponentsthroughcustomevents

custompipes

www.EBooksWorld.ir

Page 402: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

building/Buildingourowncustompipesanatomy/Anatomyofacustompipeformattimeoutput,improving/Acustompipetobetterformattimeoutputdata,filtering/Filteringoutdatawithcustomfilters

customvaluessettingup/Settingupcustomvaluesdeclaratively

www.EBooksWorld.ir

Page 403: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Ddatepipe/Thedatepipedecorators,TypeScript

classdecorators/Classdecoratorspropertydecorators/Propertydecoratorsmethoddecorators/Methoddecoratorsparameterdecorators/Parameterdecorators

dependenciesabout/HowdependencyinjectionworksinAngular2

dependencyinjectionrestricting/Restrictingdependencyinjectiondownthecomponenttree

directivesabout/DirectivesinAngular2coredirectives/CoredirectivesNgIf/NgIfNgFor/NgForNgStyle/NgStyleNgClass/NgClassNgSwitch/NgSwitch,NgSwitchWhen,andNgSwitchDefaultNgSwitchWhen/NgSwitch,NgSwitchWhen,andNgSwitchDefaultNgSwitchDefault/NgSwitch,NgSwitchWhen,andNgSwitchDefaulttesting/Testingdirectives

documentation,AngularURL/TheResponseobject

domainspecificlanguage(DSL)about/LookingintothefuturewithngAnimate2.0

www.EBooksWorld.ir

Page 404: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

EE2Etests

implementing/ImplementingE2Etestsabout/ImplementingE2Etests

ECMAScript6(ES6orES2015)about/UnderstandingthecaseforTypeScript

Enumabout/Enum

executionflowabout/Functions,lambdas,andexecutionflow

www.EBooksWorld.ir

Page 405: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Ffacademodule

creating/Creatingafacademoduleincludingacustomprovidersbarrelform

type,bindingwithNgModeldirective/BindingatypetoaformwithNgModelFormBuilderclass

about/Controls,ControlGroups,andtheFormBuilderclassfunctionparameters,TypeScript

optionalparameters/Optionalparametersdefaultparameters/Defaultparametersrestparameters/Restparametersfunctionsignature,overloading/Overloadingthefunctionsignature

functionsabout/Functions,lambdas,andexecutionflowtypes,annotating/Annotatingtypesinourfunctions

www.EBooksWorld.ir

Page 406: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

GGenerics

references/MethoddecoratorsGulp

URL/LeveragingGulpwithotherIDEs

www.EBooksWorld.ir

Page 407: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

HHeadersclass

URL/TheResponseobjectHTMLcontainer

tasklisttablebuilding,Angulardirectivesused/BuildingourtasklisttablewithAngulardirectives

HttpAPIdefining/IntroducingtheHTTPAPIRequestclass,using/WhentousetheRequestandRequestOptionsArgsclassesRequestOptionsArgsclass,using/WhentousetheRequestandRequestOptionsArgsclassesResponseobject/TheResponseobjecterrors,handlingwhenperformingHttprequests/HandlingerrorswhenperformingHttprequestsHttpclass,injecting/InjectingtheHttpclassandtheHTTP_PROVIDERSmodulessymbolHTTP_PROVIDERSmodulesymbol/InjectingtheHttpclassandtheHTTP_PROVIDERSmodulessymbol

www.EBooksWorld.ir

Page 408: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

II18npipes/Thei18npipesI18nPluralpipe/Thei18nPluralpipeI18nSelectpipe/Thei18nSelectpipeIDE

enhancing/EnhancingourIDESublimeText3/SublimeText3Atom/AtomVisualStudioCode/VisualStudioCodeWebStorm/WebStormGulp,leveragingwithotherIDEs/LeveragingGulpwithotherIDEs

injectionabout/HowdependencyinjectionworksinAngular2

inputvalidating/Trackingcontrolinteractionandvalidatinginput

interfacesabout/Classes,interfaces,andclassinheritance

IstambulURL/Introducingcodecoveragereportsinyourteststack

www.EBooksWorld.ir

Page 409: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

JJasmine

URL/Dependencyinjectioninunittestsusing,withKarma/UsingJasmineincombinationwithKarma

JohnPapaURL/IntroducingthePomodoroAppdirectorystructure

JSBINURL/Observablesinanutshell

Jsonpipe/TheJSONpipe

www.EBooksWorld.ir

Page 410: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

KKarma

URL/UsingJasmineincombinationwithKarma

www.EBooksWorld.ir

Page 411: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Llambdas

about/Functions,lambdas,andexecutionflowlocalreferences

used,fortrackingcontrolchanges/Trackingchangeswithlocalreferenceslogincomponent

building/Arealexample–ourlogincomponentloginfeaturecontext/Theloginfeaturecontextloginformtemplate/Theloginformtemplateimplementation/Thelogincomponentcustomvalidation,applyingtocontrols/Applyingcustomvalidationtoourcontrolsstatechanges,monitoringincontrols/Watchingstatechangesinourcontrolsaccessmanagement,handling/RunningtheextramileonaccessmanagementcustomsecureRouterOutletdirective,building/BuildingourownsecureRouterOutletdirective

lowercasepipe/Theuppercase/lowercasepipe

www.EBooksWorld.ir

Page 412: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Nnamedkeyframe

about/CreatinganimationswithplainvanillaCSSngAnimate2.0

defining/LookingintothefuturewithngAnimate2.0about/LookingintothefuturewithngAnimate2.0

NgClassdirective/NgClassngControldirective

Controls,creatinginDOM/ControlsintheDOM–thengControldirectiveNgControlGroupdirective

Controls,groupinginDOM/GroupingcontrolsintheDOMwithNgControlGroupNgFordirective/NgForNgIfdirective/NgIfNgModeldirective

about/TheNgModeldirectivetype,bindingtoform/BindingatypetoaformwithNgModelCanDeactivaterouterhook,bypassinguponformsubmission/BypassingtheCanDeactivaterouterhookuponsubmittingforms

NgStyledirective/NgStyleNgSwitchDefaultdirective

about/NgSwitch,NgSwitchWhen,andNgSwitchDefaultNgSwitchdirective

about/NgSwitch,NgSwitchWhen,andNgSwitchDefaultNgSwitchWhendirective

about/NgSwitch,NgSwitchWhen,andNgSwitchDefaultNode.js

URL/SettingupourworkspaceNPMmoduleofficialrepository

references/Servingtheexamplesofthisbooknumberpipe/Thenumberpipe

www.EBooksWorld.ir

Page 413: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

OObservabledata

serving,throughHTTP/Arealcasestudy–servingObservabledatathroughHTTPtasks,addingtotasksservice/Addingtaskstoourtasksservice

observablesinnutshell/Observablesinanutshell

www.EBooksWorld.ir

Page 414: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Ppercentpipe/Thepercentpipepipes

templatebindings,manipulatingwith/ManipulatingtemplatebindingswithPipesUppercase/lowercasepipe/Theuppercase/lowercasepipeabout/Thenumber,percent,andcurrencypipesnumberpipe/Thenumberpipepercentpipe/Thepercentpipecurrencypipe/Thecurrencypipeslicepipe/Theslicepipedatepipe/ThedatepipeJsonpipe/TheJSONpipereplacepipe/ThereplacepipeI18npipes/Thei18npipesI18nPluralpipe/Thei18nPluralpipeI18nSelectpipe/Thei18nSelectpipeAsyncpipe/Theasyncpipenamingconventions/Awordaboutnamingconventionsforcustomdirectivesandpipestesting/Testingpipes

playgroundpageURL/Anatomyofaclass–constructors,properties,methods,getters,andsetters

PomodoroAppdirectorystructuredefining/IntroducingthePomodoroAppdirectorystructure

PomodorotasklistHTMLcontainer,setting/SettingupourmainHTMLcontainer

Pomodorotasklistabout/PuttingitalltogetherinthePomodorotasklisttasks,toggling/Togglingtasksinourtaskliststatechangesintemplates,displaying/Displayingstatechangesinourtemplateschildcomponents,embedding/Embeddingchildcomponents

PomodorotechniqueURL/Improvingproductivity

ProtractorURL/ImplementingE2Etests

providerslookuprestricting/Restrictingproviderlookup

www.EBooksWorld.ir

Page 415: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

RReactivefunctionalprogramming

inAngular2/ReactivefunctionalprogramminginAngular2RxJSlibrary/TheRxJSlibrary

replacepipe/Thereplacepiperouteparameters

handling/Handlingrouteparametersdynamicparameters,passing/Passingdynamicparametersinourroutesparsing,withRouteParamsservice/ParsingrouteparameterswiththeRouteParamsservice

Routerlifecyclehooksabout/TheRouterlifecyclehooksCanActivatehook/TheCanActivatehookOnActivatehook/TheOnActivateHookCanDeactivatehook/TheCanDeactivateandOnDeactivatehooksOnDeactivatehook/TheCanDeactivateandOnDeactivatehooksCanReusehook/TheCanReuseandOnReusehooksOnReusehook/TheCanReuseandOnReusehookstipsandtricks/Advancedtipsandtricksredirecting,tootherroutes/Redirectingtootherroutesbasepath,tweaking/TweakingthebasepathgeneratedURLs,finetuningwithlocationstrategies/FinetuningourgeneratedURLswithlocationstrategiescomponents,loadingasynchronouslywithAsyncRoutes/LoadingcomponentsasynchronouslywithAsyncRoutes

routerservicenewcomponent,buildingfordemonstration/BuildinganewcomponentfordemonstrationpurposesRouteConfigdecorator,configuringwithRouteDefinitioninstances/ConfiguringtheRouteConfigdecoratorwiththeRouteDefinitioninstancesrouterdirectives,defining/Therouterdirectives–RouterOutletandRouterLinkroutes,triggering/TriggeringroutesimperativelyCSShooks,foractiveroutes/CSShooksforactiveroutes

routestesting/Testingroutestesting,byURL/TestingroutesbyURLredirections,testing/Testingredirections

RxJSlibraryabout/TheRxJSlibrary

www.EBooksWorld.ir

Page 416: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Sscalableapplications

conventions,defining/Commonconventionsforscalableapplicationsfileandmodulenamingconventions/Fileandmodulenamingconventionsseamlessscalability,ensuringwithfacadesorbarrels/Ensuringseamlessscalabilitywithfacadesorbarrels

servicestesting/Testingservicesasynchronousservices,testing/TestingasynchronousservicesHttpresponses,mockingwithMockBackend/MockingHttpresponseswithMockBackend

sharedcontextdefining/Thesharedcontextservices/Servicesinthesharedcontextapplicationsettings,configuringfromcentralservice/Configuringapplicationsettingsfromacentralservice

singleresponsibilityprincipleabout/RefactoringourapplicationtheAngular2way

slicepipe/Theslicepipestaticvalidatormethods

about/IntroducingControlsandValidatorsrequired/IntroducingControlsandValidatorsminLength(minLength-number)/IntroducingControlsandValidatorsmaxLength(maxLength-number)/IntroducingControlsandValidatorspattern(pattern-string)/IntroducingControlsandValidatorscompose(validators-Function[])/IntroducingControlsandValidatorscomposeAsync()/IntroducingControlsandValidators

strategiesused,forhandlingasynchronousinformation/Strategiesforhandlingasynchronousinformation

stringtypeabout/Stringvariables,declaring/DeclaringourvariablestheECMAScript6way

www.EBooksWorld.ir

Page 417: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Ttemplate

configuring,fromcomponentclass/Configuringourtemplatefromourcomponentclassinternaltemplate/Internalandexternaltemplatesexternaltemplate/Internalandexternaltemplates

templatebindingsmanipulating,withpipes/ManipulatingtemplatebindingswithPipes

templatesyntaxabout/Abettertemplatesyntaxdatabindings,withinputproperties/Databindingswithinputpropertiesexpressions,binding/Someextrasyntacticsugarwhenbindingexpressionseventbinding,withoutputproperties/Eventbindingwithoutputpropertiesinputandoutputproperties/Inputandoutputpropertiesinactiondata,emittingthroughcustomevents/Emittingdatathroughcustomeventslocalreferences,intemplates/Localreferencesintemplatesalternativesyntax,forinputandoutputproperties/Alternativesyntaxforinputandoutputproperties

testenvironmentsettingup/Settingupourtestenvironmenttestrunner,implementing/ImplementingourtestrunnerNPMcommands,settingup/SettingupNPMcommands

testsdebugging/Testingcomponents

TitleclassURL/TheOnActivateHook

transitionpropertiesabout/CreatinganimationswithplainvanillaCSS

two-waydatabindingabout/Two-waydatabindinginAngular2NgModeldirective/TheNgModeldirective

TypeScriptusing,overothersyntaxes/WhyTypeScriptoverothersyntaxes?about/WhyTypeScriptoverothersyntaxes?case,definingfor/UnderstandingthecaseforTypeScriptbenefits/ThebenefitsofTypeScripttypes/TypesinTypeScriptstring/Stringnumber/Numberboolean/Booleandynamictyping,withanytype/Dynamictypingwiththeanytypevoid/Voidtypeinference/Typeinference

www.EBooksWorld.ir

Page 418: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

functionparameters/FunctionparametersinTypeScriptscopehandling,withlambdas/Betterfunctionsyntaxandscopehandlingwithlambdasinterfaces/InterfacesinTypeScriptdecorators/DecoratorsinTypeScriptreferences/DecoratorsinTypeScriptdefining/Theroadahead

TypeScriptcompilerwikiURL/InstallingTypeScript

TypeScriptofficialsiteURL/TheTypeScriptofficialsite

TypeScriptpluginURL/SublimeText3

TypeScriptresourcesdefining/IntroducingTypeScriptresourcesinthewildTypeScriptofficialsite/TheTypeScriptofficialsiteTypeScriptWiki/TheTypeScriptWiki

TypeScriptWikiURL/TheTypeScriptWiki

www.EBooksWorld.ir

Page 419: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

Uunittest

about/Whydoweneedtests?defining,inAngular2/PartsofaunittestinAngular2dependencyinjection,defining/Dependencyinjectioninunittests

uppercasepipe/Theuppercase/lowercasepipe

www.EBooksWorld.ir

Page 420: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

VValidatorsclass

about/IntroducingControlsandValidatorsViewEncapsulationenum

values/Managingviewencapsulation

www.EBooksWorld.ir

Page 421: dl.ebooksworld.irdl.ebooksworld.ir/motoman/Packt.Learning.Angular.2.pdf · Improving productivity Component methods and data updates Adding interactivity to the component Improving

WWebcomponents

defining/Webcomponentstemplates/WebcomponentsCustomElements/WebcomponentsShadowDOM/WebcomponentsHTMLImports/Webcomponents

WebPackreference/Installingdependencies

workspacesetting/Settingupourworkspacedependencies,installing/InstallingdependenciesTypeScript,installing/InstallingTypeScriptproperties/InstallingTypeScriptTypeScripttypings,installing/InstallingTypeScripttypings

www.EBooksWorld.ir