422
www.EBooksWorld.ir

Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

  • Upload
    others

  • View
    0

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

www.EBooksWorld.ir

Page 2: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

MasteringAngular2Components

www.EBooksWorld.ir

Page 3: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

TableofContents

MasteringAngular2ComponentsCreditsAbouttheAuthorAbouttheReviewerwww.PacktPub.com

eBooks,discountoffers,andmoreWhysubscribe?

PrefaceWhatthisbookcoversWhatyouneedforthisbookWhothisbookisforConventionsReaderfeedbackCustomersupport

DownloadingtheexamplecodeDownloadingthecolorimagesofthisbookErrataPiracyQuestions

1.Component-BasedUserInterfacesThinkingoforganismsComponents–Theorgansofuserinterfaces

EncapsulationComposabilityComponents,inventedbynature

MyUIframeworkwishlistTimefornewstandards

TemplateelementsShadowDOM

Angular'scomponentarchitectureEverythingisacomponent

YourfirstcomponentJavaScriptofthefuture

IspeakJavaScript,translate,please!ClassesModulesTemplatestringsECMAScriptorTypeScript?Decorators

ToolsNode.jsandNPM

www.EBooksWorld.ir

Page 4: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

SystemJSandJSPMJSPMGettingstartedwithJSPM

Summary2.Ready,Set,Go!

ManagingtasksVision

StartingfromscratchBootstrappingRunningtheapplicationRecap

CreatingatasklistRecap

TherightlevelofencapsulationRecap

InputgeneratesoutputRecap

CustomUIelementsRecap

FilteringtasksSummary

3.ComposingwithComponentsData–FaketorealReactiveprogrammingwithobservabledatastructuresImmutabilityPurecomponents

PurifyingourtasklistRecap

CompositionusingcontentprojectionCreatingatabbedinterfacecomponent

RecapMixingprojectedwithgeneratedcontentSummary

4.NoComments,Please!Oneeditortorulethemall

CreatinganeditorcomponentRecap

BuildingacommentingsystemBuildingthecommentcomponentBuildingthecommentscomponentRecap

Summary5.Component-BasedRouting

AnintroductiontotheAngularrouter

www.EBooksWorld.ir

Page 5: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

CompositionbyroutingRouterversustemplatecompositionUnderstandingtheroutetree

BacktotheroutesRoutabletabs

RefactoringnavigationSummary

6.KeepingUpwithActivitiesCreatingaserviceforloggingactivities

LoggingactivitiesLeveragingthepowerofSVG

StylingSVGBuildingSVGcomponents

BuildinganinteractiveactivityslidercomponentProjectionoftimeRenderingactivityindicatorsBringingittolifeRecap

BuildingtheactivitytimelineSummary

7.ComponentsforUserExperienceTagmanagement

TagdataentityGeneratingtagsCreatingatagsserviceRenderingtagsIntegratingthetaskserviceCompletionofthetagsservice

SupportingtaginputCreatingataginputmanagerCreatingatagsselectcomponentIntegratingtaginputwithintheeditorcomponentFinishingupourtaggingsystem

DraganddropImplementingthedraggabledirectiveImplementingadroptargetdirectiveIntegratingdraganddropintasklistcomponentRecapitulateondraganddrop

Toinfinityandbeyond!TheasterisksyntaxandtemplatesCreatinganinfinitescrolldirectiveDetectingchangewithinourtemplatedirectiveAddingandremovingembeddedviewsFinishingourinfinitescrolldirective

www.EBooksWorld.ir

Page 6: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Summary8.TimeWillTell

TaskdetailsEnablingtagsfortasksManagingefforts

ThetimedurationinputComponentstomanageeffortsThevisualeffortstimelineRecapitulatingoneffortsmanagement

SettingmilestonesCreatinganautocompletecomponent

Summary9.SpaceshipDashboard

IntroductiontoChartistProjectsdashboard

CreatingtheprojectsdashboardcomponentProjectsummarycomponent

CreatingyourfirstchartVisualizingopentasks

CreatinganopentaskschartCreatingachartlegendMakingtaskschartinteractive

Summary10.MakingThingsPluggable

PluginarchitecturePluggableUIcomponentsImplementingthepluginAPI

InstantiatingplugincomponentsFinalizingourpluginarchitecture

BuildinganAgilepluginAgiletaskinfocomponentAgiletaskdetailscomponentRecapitulatingonourfirstplugin

ManagingpluginsLoadingnewpluginsatruntime

Summary11.PuttingThingstotheTest

AnintroductiontoJasmineWritingourfirsttestSpyingoncomponentoutputsUtilitiestotestcomponents

InjectingintestsTestcomponentbuilder

Testingcomponentsinaction

www.EBooksWorld.ir

Page 7: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

TestingcomponentinteractionTestingourpluginsystemSummary

A.TaskManagementApplicationSourceCodeDownloadPrerequisitesUsageTroubleshooting

CleaningIndexDBtoresetdataEnablingwebcomponentsinFirefox

Index

www.EBooksWorld.ir

Page 8: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

MasteringAngular2Components

www.EBooksWorld.ir

Page 9: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

MasteringAngular2ComponentsCopyright©2016PacktPublishing

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

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

PacktPublishinghasendeavoredtoprovidetrademarkinformationaboutallofthecompaniesandproductsmentionedinthisbookbytheappropriateuseofcapitals.However,PacktPublishingcannotguaranteetheaccuracyofthisinformation.

Firstpublished:June2016

Productionreference:1280616

PublishedbyPacktPublishingLtd.

LiveryPlace

35LiveryStreet

BirminghamB32PB,UK.

ISBN978-1-78588-464-1

www.packtpub.com

www.EBooksWorld.ir

Page 10: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

CreditsAuthor

GionKunz

Reviewer

CarlosMorales

CommissioningEditor

SarahCrofton

AcquisitionEditors

AaronLazar

LarissaPinto

ContentDevelopmentEditor

SamanthaGonsalves

TechnicalEditor

MadhunikitaSunilChindarkar

CopyEditor

PriyankaRavi

ProjectCoordinator

SanchitaMandal

Proofreader

SafisEditing

Indexer

MonicaAjmeraMehta

Graphics

www.EBooksWorld.ir

Page 11: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

DishaHaria

ProductionCoordinator

MelwynDsa

CoverWork

MelwynDsa

www.EBooksWorld.ir

Page 12: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

AbouttheAuthorGionKunzhasyearsofexperiencewithwebtechnologiesandistotallyinlovewithwebstandards.Withover10yearsofexperienceofwritinginteractiveuserinterfacesusingJavaScript,heconstantlyevaluatesnewapproachesandframeworks.He'sworkedwithAngularJSforover3yearsnowandisoneoftheearliestadoptersofAngular2.GionspeaksaboutAngular2atconferences,andhehelpswiththeorganizationoftheZurichAngularMeetupgroupinSwitzerland.

Hecurrentlyworksforthestart-upcompanyoddEVENinZurich,wheretheyhelpcustomersbuildwebsitesandapplications.BesidesworkingforoddEVEN,GionisaheadinstructorattheSAEInstituteinZurichandlovestogethisstudentsenthusiasticabouttheWeb.

HeisalsothecreatoroftheresponsivechartinglibraryChartist,andhelovestocontributetotheopensourcecommunitywheneverhefindstime.

WhenGionisnotbusywithwebtechnologies,youcanprobablyfindhimathishomemusicstudio,outdoors,fishing,orspendingqualitytimewithhisgirlfriendandtheircutelittledog.

Iwouldliketothankmygirlfriend,Nathalie,forherongoingsupportandherpatiencewithallmyeffortsspentonthisbook.IreallyappreciateallthatyoudidforbothofusduringthistimeandthatyoucompensatedtheenergylossI'vebroughtintoourrelation.

www.EBooksWorld.ir

Page 13: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

AbouttheReviewerCarlosMoralesstartedprogramminginBASICwhenhewas6yearsoldwithaSinclairZXSpectrum+thathestillowns.He'slovedthetechnologyeversince.Professionally,hehasworkedformorethan15yearsindifferentroles,alwaysaroundwebapplications.HefellinlovewithAngular,andhefoundedtheAngularMeetupgroupinZürich,whichisoneofthemostattendedMeetupsinthecity.

Soyingenieroinformáticograciasamidedicación,perosobretodoamispadres.

www.EBooksWorld.ir

Page 14: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

www.PacktPub.com

www.EBooksWorld.ir

Page 15: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

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 16: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Whysubscribe?FullysearchableacrosseverybookpublishedbyPacktCopyandpaste,print,andbookmarkcontentOndemandandaccessibleviaawebbrowser

www.EBooksWorld.ir

Page 17: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

PrefaceWebcomponentshavelongbeentoutedasthenextgreatleapforwardinwebdevelopment.WithAngular2,we'recloserthanever.Overthepastcoupleofyears,there'sbeenalotofbuzzaroundwebcomponentsforquitesometimeinthewebdevelopmentcommunity.Newcomponent-styledirectivesinAngular2willchangedevelopers'workflowsandtheirthinkingaboutsharedandreusableblocksofcustomHTMLintheshadowDOM.Oursisthefirstbookthatguidesdevelopersalongthispath.It'salsoapracticalwaytolearn,givingreadersthechancetobuildcomponentsoftheirown.WithMasteringAngular2Components,learnerswillgetaheadofthecurveinanewwaveofwebdevelopmentbytightlyfocusingonanareathat'sthekeytounlockingthepowersofAngulardevelopment.

MasteringAngular2Componentsteachesreaderstothinkcomponentially.Thisrichguidetothenewcomponent-centricwayofdoingthingsinAngularteachesreadershowtoinvent,build,andmanagesharedandreusablecomponentsfortheirwebprojects.ThisbookwillchangehowdevelopersthinkabouthowtoaccomplishthingsinAngular2,andthereaderwillbeworkingonusefulandfunexamplecomponentsthroughout.

www.EBooksWorld.ir

Page 18: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

WhatthisbookcoversChapter1,Component-BasedUserInterfaces,looksatabitattheUIdevelopmenthistoryandprovidesabriefintroductiontocomponent-baseduserinterfacesingeneral.WewillseehowAngular2handlesthisconcept.

Chapter2,Ready,Set,Go!,willgetthereaderstartedontheirjourneytowardbuildinganAngular2component-basedapplication.Itcoversthebasicelementsofstructuringanapplicationwithcomponents.

Chapter3,ComposingwithComponents,iswherethereaderwillstarttostructuretheuserinterfaceintoitsbasicpieces.Thereaderwillthengoontocomposeanapplicationusingcomponentsbyorganizinganapplicationlayoutintocomponents,establishingthecompositionofcomponentsusingQueryList,andcreatingareusabletabcomponenttostructuretheapplicationinterface.

Chapter4,NoComments,Please!,iswherethereaderwilllearnhowtobuildacommentingsystemusingcomponents.Theywilllearntocreateacomponenttolistcommentsandalsotocreatenewcomments.

Chapter5,Component-BasedRouting,explainshowcomponentsreacttoroutingandwillenablethereadertoaddsimpleroutingtotheexistingcomponentsinthetaskmanagementapplication.Thereaderwillalsoworkontheloginprocessandbuildanunderstandingtoprotectcomponentsusingtherouter.

Chapter6,KeepingUpwithActivities,coversthecreationofcomponentsthatwillvisualizeactivitystreamsonprojectandtasklevel.

Chapter7,ComponentsforUserExperience,iswherethereaderwillcreatemanysmallreusablecomponentsthatwillhaveagreateffectontheoveralluserexperienceofthetaskmanagementapplication.Thisincludesin-placeeditingoftextfields,infinitescroll,popupnotification,anddraganddropsupport.

Chapter8,TimeWillTell,focusesoncreatingtime-trackingcomponentsthathelpsestimatetimeonaprojectandtasklevelbutalsoforuserstologthetimespentontasks.

Chapter9,SpaceshipDashboard,focusesoncreatingcomponentstovisualizesomedatainthetaskmanagementapplicationusingthethird-partylibraryChartist.

Chapter10,MakingThingsPluggable,iswherethereaderwilllearnaboutanapproachtomakecomponentspluggableusingasimplebutpowerfulpattern.WithaDIYpluginarchitectureforAngular2components,wemakeourtaskmanagementsystemextensible.

Chapter11,PuttingThingstotheTest,coverssomebasicapproachestotestingAngular2components.Wewillseetheoptionsformocking/overridingspecificpartsofacomponent

www.EBooksWorld.ir

Page 19: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

fortesting.

Appendix,TaskManagementApplicationSourceCode,containsalltheinformationyou'llneedtodownloadandinstallthesourcecodethatcomeswiththisbook.You'llalsofindinstructionstouseandtroubleshootthecodeinthere.

www.EBooksWorld.ir

Page 20: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

WhatyouneedforthisbookThisbookwillneedabasicinstallationofNode.jsonyourWindows,Mac,orLinuxmachine.

www.EBooksWorld.ir

Page 21: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

WhothisbookisforThisbookisforAngulardeveloperswhoalreadyhaveagoodunderstandingofbasicfrontendwebtechnologies,suchasJavaScript,HTML,andCSS.Youwilllearnaboutthenewcomponent-basedarchitectureinAngular2andhowtouseittobuildmodernandcleanuserinterfaces.

www.EBooksWorld.ir

Page 22: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

ConventionsInthisbook,youwillfindanumberoftextstylesthatdistinguishbetweendifferentkindsofinformation.Herearesomeexamplesofthesestylesandanexplanationoftheirmeaning.

Codewordsintext,databasetablenames,foldernames,filenames,fileextensions,pathnames,dummyURLs,userinput,andTwitterhandlesareshownasfollows:"YouhaveaFisherclassandaDeveloperclass,bothofwhichholdspecificbehaviors."

Ablockofcodeissetasfollows:

classFruit{

constructor(name){this.name=name;}

}

constapple=newFruit('Apple');

Whenwewishtodrawyourattentiontoaparticularpartofacodeblock,therelevantlinesoritemsaresetinbold:

<body>

<templateid="template">

<h1>Thisisatemplate!</h1>

</template>

</body>

Anycommand-lineinputoroutputiswrittenasfollows:

npminstalljspm--save-dev

jspminit

Newtermsandimportantwordsareshowninbold.Wordsthatyouseeonthescreen,forexample,inmenusordialogboxes,appearinthetextlikethis:"Ifeverythinggoeswell,youwillhaveanopenwebbrowserthatshowsHelloWorld!."

Note

Warningsorimportantnotesappearinaboxlikethis.

Tip

Tipsandtricksappearlikethis.

www.EBooksWorld.ir

Page 23: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

ReaderfeedbackFeedbackfromourreadersisalwayswelcome.Letusknowwhatyouthinkaboutthisbook—whatyoulikedordisliked.Readerfeedbackisimportantforusasithelpsusdeveloptitlesthatyouwillreallygetthemostoutof.

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

Ifthereisatopicthatyouhaveexpertiseinandyouareinterestedineitherwritingorcontributingtoabook,seeourauthorguideatwww.packtpub.com/authors.

www.EBooksWorld.ir

Page 24: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

CustomersupportNowthatyouaretheproudownerofaPacktbook,wehaveanumberofthingstohelpyoutogetthemostfromyourpurchase.

www.EBooksWorld.ir

Page 25: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

DownloadingtheexamplecodeYoucandownloadtheexamplecodefilesforthisbookfromyouraccountathttp://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

ThecodebundleforthebookisalsohostedonGitHubathttps://github.com/PacktPublishing/Mastering-Angular-2-Components.Wealsohaveothercodebundlesfromourrichcatalogofbooksandvideosavailableathttps://github.com/PacktPublishing/.Checkthemout!

www.EBooksWorld.ir

Page 26: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

DownloadingthecolorimagesofthisbookWealsoprovideyouwithaPDFfilethathascolorimagesofthescreenshots/diagramsusedinthisbook.Thecolorimageswillhelpyoubetterunderstandthechangesintheoutput.Youcandownloadthisfilefromhttps://www.packtpub.com/sites/default/files/downloads/MasteringAngular2Components_ColorImages.pdf

www.EBooksWorld.ir

Page 27: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

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 28: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

PiracyPiracyofcopyrightedmaterialontheInternetisanongoingproblemacrossallmedia.AtPackt,wetaketheprotectionofourcopyrightandlicensesveryseriously.IfyoucomeacrossanyillegalcopiesofourworksinanyformontheInternet,pleaseprovideuswiththelocationaddressorwebsitenameimmediatelysothatwecanpursuearemedy.

Pleasecontactusat<[email protected]>withalinktothesuspectedpiratedmaterial.

Weappreciateyourhelpinprotectingourauthorsandourabilitytobringyouvaluablecontent.

www.EBooksWorld.ir

Page 29: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

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

www.EBooksWorld.ir

Page 30: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Chapter1.Component-BasedUserInterfacesAlthoughwe'llcoveralotofAngular-relatedtopicsinthisbook,thefocuswillbemainlyoncreatingcomponent-baseduserinterfaces.It'sonethingtounderstandaframework,suchasAngular2,butit'sawholedifferentthingtoestablishaneffectiveworkflowusingacomponent-basedarchitecture.Inthisbook,I'lltrytoexplainthecoreconceptsbehindAngular2componentsandhowwecanleveragethisarchitecturetocreatemodern,efficient,andmaintainableuserinterfaces.

BesideslearningallthenecessaryconceptsbehindAngular2,wewilltogethercreateatask-managementapplicationfromscratch.ThiswillallowustoexploredifferentapproachestosolvecommonUIproblemsusingthecomponentsystemthatisprovidedbyAngular2.

Apreviewofthetaskmanagementapplicationthatwearegoingtobuild

Inthischapter,wewilltakealookathowcomponent-baseduserinterfaceshelpusbuildgreaterapplications.Overthecourseofthisbook,wewillbuildanAngular2applicationtogether,wherewewillusethecomponent-basedapproachtoitsfullpotential.Thischapterwillalsointroduceyoutothetechnologiesthatareusedinthisbook.Thetopicsthatwewillcoverinthischapterareasfollows:

Anintroductiontocomponent-baseduserinterfacesEncapsulationandcompositionusingcomponent-baseduserinterfacesEvolutionofUIframeworksThestandardandWebcomponents

www.EBooksWorld.ir

Page 31: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

AnintroductiontotheAngular2componentsystemWritingyourfirstAngular2componentAnoverviewandhistoryofECMAScriptandTypeScriptECMAScript7decoratorsasmetaannotationsAnintroductiontoNode.js-basedtoolingusingJSPMandSystemJS

www.EBooksWorld.ir

Page 32: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

ThinkingoforganismsToday'suserinterfacesdonotconsistofjustabunchofformelementsthatarecobbledtogetherontoascreen.Modernusersexperiencedesignandinnovativevisualpresentationsofinteractivecontentchallengestechnologymorethanever.

Sadly,wealmostalwaystendtothinkinpageswhenwefleshoutconceptsforwebapplications,suchasthepageswithinaprintedbook.Well,thisisprobablythemostefficientwaytoconveyinformationforthiskindofcontentandmedium.Youcanskimthroughthepagesonebyonewithoutanyrealphysicaleffort,readparagraphbyparagraph,andjustscanthroughthechaptersthatyoudon'tfindinteresting.

Theproblemwiththinkinginpagestoomuchisthatthisconcept,whichisborrowedfrombooks,doesnotreallytranslatewelltohowthingsworkintherealworld.Theworldiscreatedfromorganismsthatformasystemoforganismstogether.Thissystemitselfformsanorganismagain,justonahigherlevel.

Takeourbodiesasanexample.Wemostlyconsistofindependentorgansthatinteractwitheachotherusingelectricalandchemicalsignals.Organsthemselvesconsistofproteinsthatontheirownworklikeamachinetoformasystem.Downtothemolecules,atoms,protons,andquarks,wecan'treallytellwhereonestartsandwhereitends.Whatwecantellforsureisthatit'sallaboutsystemsoforganismswithinterdependencies,anditisnotaboutpages.

Iliketoviewuserinterfacesassystemsoforganisms.Whetherif,where,andhowtheyaredistributedtopagesissubordinatewhiledesigningthem.Also,theyshouldworkindependently,andtheyshouldinteractwitheachotheronaninterdependentlevel.

www.EBooksWorld.ir

Page 33: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Components–Theorgansofuserinterfaces "We'renotdesigningpages,we'redesigningsystemsofcomponents."

--StephenHay

ThisquotebyStephenHayfromBDConfinOrlando2012bringsittothepoint.Interfacedesignisreallynotaboutpages.Tocreateefficientuserinterfacesfornotonlytheusersbutalsothedeveloperswhomaintainthem,weneedtothinkinsystemsofcomponents.Componentsareindependent,buttheycaninteractwitheachotherandcreatelargercomponentswhentheyarearrangedtogether.Weneedtolookatuserinterfacesholisticallyandusingcomponentsenablesustodothis.

Inthefollowingtopics,we'regoingtoexploreafewfundamentalaspectsofcomponents.Someofthesearealreadyknownfromotherconcepts,suchasobject-orientedprogramming(OOP),buttheyappearinaslightlydifferentlightwhenthinkingaboutcomponents.

www.EBooksWorld.ir

Page 34: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

EncapsulationEncapsulationisaveryimportantfactorwhenthinkingaboutmaintenanceinasystem.HavingaclassicalOOPbackground,I'velearnedthatencapsulationmeansbundlinglogicanddatatogetherintoanisolatedcontainer.Thisway,wecanoperateonthecontainerfromtheoutsideandtreatitlikeaclosedsystem.

Therearemanypositiveaspectsofthisapproachwhenitcomestomaintainabilityandaccessibility.Dealingwithclosedsystemsisimportantfortheorganizationofourcode.However,thisisevenmoreimportantlybecausewecanorganizeourselveswhileworkingwithcode.

Ihaveaprettybadmemory,andit'sveryimportantformetofindtherightfocuslevelwhenworkingoncode.Immediatememoryresearchtoldusthatthehumanbraincanrememberaboutsevenitemsatonceonanaverage.Therefore,it'scrucialforustowritecodeinsuchawaythatitallowsustofocusonfewerandsmallerpiecesatonce.

Aclearencapsulationhelpsusinorganizingourcode.Wecanmaybeforgetalltheinternalsoftheclosedsystemandaboutthekindoflogicanddatathatwe'veputintoit.Wecanfocusonlyonitssurface,whichallowsustoworkonahigher-abstractionlevel.Similartothepreviousfigure,withoutusingahierarchyofencapsulatedcomponents,we'dhaveallourcodecobbledtogetheronthesamelevel.

Encapsulationencouragesustoisolatesmallandconcisecomponentsandbuildasystemofcomponents.Duringdevelopment,wecanfocusontheinternalsofonecomponentandonlydealwiththeinterfaceofothercomponents.

Sometimes,weforgetthatalltheorganizationofthecodingweactuallyperformisforourselvesandnotforthecomputerthatrunsthiscode.Ifthiswasforthecomputer,thenwe

www.EBooksWorld.ir

Page 35: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

wouldprobablyallstartwritinginmachinelanguageagain.Astrongencapsulationhelpsusaccessspecificcodeeasily,focusononelayerofthecode,andtrusttheunderlyingimplementationswithincapsules.

ThefollowingJavaScriptexampleshowsyouhowtouseencapsulationtowritemaintainableapplications.Let'sassumethatweareinaT-shirtfactory,andweneedsomecodetoproduceT-shirtswithabackgroundandforegroundcolor.ThisexampleusessomenewlanguagefeaturesofECMAScript6.Ifyou'renotfamiliarwiththelanguagefeaturesofECMAScript6,don'tworrytoomuchatthispoint.Wewilllearnabouttheselaterinthischapter:

//Thisclassimplementsdataandlogictorepresentacolour

//whichestablishescleanencapsulation.

classColour{

constructor(red,green,blue){

Object.assign(this,{red,green,blue});

}

//Usingthisfunctionwecanconverttheinternalcolourvalues

//toahexcolourstringlike#ff0000(red).

getHex(){

return'#'+Colour.getHexValue(this.red)+Colour.getHexValue(this.green)+

Colour.getHexValue(this.blue);

}

//StaticfunctiononColourclasstoconvertanumberfrom

//0to255toahexadecimalrepresentation00toff

staticgetHexValue(number){

consthex=number.toString(16);

returnhex.length===2?hex:'0'+hex;

}

}

//OurTShirtclassexpectstwocolourstobepassedduring

//constructionthatwillbeusedtorendersomeHTML

classTShirt{

constructor(backgroundColour,foregroundColour){

Object.assign(this,{backgroundColour,foregroundColour});

}

//Functionthatreturnssomemarkupwhichrepresentsour

//T-Shirts

getHtml(){

return`

<t-shirtstyle="background-color:${this.backgroundColour.getHex()}">

<t-shirt-textstyle="color:${this.foregroundColour.getHex()}">

AwesomeShirt!

</t-shirt-text>

</t-shirt>

`;

}

}

//Instantiateabluecolour

constblue=newColour(0,0,255);

www.EBooksWorld.ir

Page 36: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

//Instantiatearedcolour

constred=newColour(255,0,0);

//Createanewshirtusingtheabovecolours

constawesomeShirt=newTShirt(blue,red);

//Addingthegeneratedmarkupofourshirttoourdocument

document.body.innerHTML=awesomeShirt.getHtml();

Usingacleanencapsulation,wecannowworkwiththeabstractionofcolorinourT-shirt.Wedon'tneedtoworryabouthowtocalculatethehexadecimalrepresentationofcolorsattheT-shirtlevelbecausethisisalreadydonebytheColourclass.Thismakesyourapplicationmaintainableandkeepsitveryopenforchange.

IreallyrecommendthatyoureadabouttheSOLIDprinciplesifyouhaven'tdonesoalready.Asthenamealreadysuggests,thisassemblyofprinciplesisasolidpowertoolthatcanchangethewayyouorganizecodetremendously.YoucanlearnmoreabouttheSOLIDprinciplesinthebook,AgilePrinciples,Patterns,andPractices,byRobertC.Martin.

www.EBooksWorld.ir

Page 37: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

ComposabilityCompositionisaspecialkindofreusability.Youdon'textendanexistingcomponent,butyoucreateanewlargercomponentbycomposingmanysmallercomponentstogetherintoasystemofcomponents.

InOOPlanguages,compositionisoftenusedtogetaroundthemultipleinheritanceissuesthatmostOOPlanguageshave.Subclasspolymorphismisalwaysgreatuntilyoureachthepointwhereyourdesigndoesnotmatchthelatestrequirementsinyourproject.Let'slookatasimpleexamplethatillustratesthisproblem.

YouhaveaFisherclassandaDeveloperclass,bothofwhichholdspecificbehaviors.Now,you'dwanttocreateaFishingDeveloperclassthatinheritsbothfromFisherandDeveloper.Unlessyou'reusingalanguagethatsupportsmultipleinheritance(suchasC++doestoacertainextent),youwillnotbeabletoreusethisfunctionalityusinginheritance.Thereisnowaytotellthelanguagethatyournewclassshouldinheritfrombothsuperclasses.Usingcomposition,youcaneasilysolvethisproblem.Insteadofusinginheritance,you'recomposinganewFishingDeveloperclassthatdelegatesallbehaviortoaninternalDeveloperandFisherinstance:

classDeveloper{

code(){

console.log(`${this.name}writessomecode!`);

}

}

classFisher{

fish(){

console.log(`${this.name}catchesabigfish!`);

}

}

classFishingDeveloper{

constructor(name){

this.name=name;

this.developerStuff=newDeveloper();

this.fisherStuff=newFisher();

}

code(){

this.developerStuff.code.bind(this)();

}

fish(){

this.fisherStuff.fish.bind(this)();

}

}

varbob=newFishingDeveloper('Bob');

bob.code();

bob.fish();

www.EBooksWorld.ir

Page 38: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Experiencehastaughtusthatcompositionisprobablythemostefficientwaytoreusecode.Incontrasttoinheritance,decoration,andotherapproachestogainreusability,compositionisprobablytheleastintrusiveandthemostflexible.

Recentversionsofsomelanguagesalsosupportapatterncalledtraits,thatis,mixins.Traitsallowyoutoreusecertainfunctionalityandattributesfromotherclassesinawaythatissimilartomultipleinheritance.

Ifwethinkabouttheconceptofcomposition,it'snothingmorethandesigningorganisms.WehavethetwoDeveloperandFisherorganisms,andweunifytheirbehaviorsintoasingleFishingDeveloperorganism.

www.EBooksWorld.ir

Page 39: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Components,inventedbynatureComponents,embracingencapsulation,andcompositionareaneffectivewaytobuildmaintainableapplications.Composedfromcomponents,applicationsareveryresistanttothenegativeimplicationsofchange,andchangeisanecessarythingthatwillhappentoeveryapplication.It'sonlyamatteroftimeuntilyourdesignwillbechallengedbytheeffectsofchange;therefore,it'sveryimportanttowritecodethatcanhandlechangeassmoothlyaspossible.

Natureisthebestteacher.Almostalltheachievementsintechnologicaldevelopmentshavetheirorigininobservationsofhownaturesolvesproblems.Ifwelookatevolution,it'sanongoingredesignofmatterbyadaptingtoouterforcesandconstraints.Naturesolvesthisbyconstantchangeusingmutationandnaturalselection.

Ifweprojecttheconceptofevolutionontodevelopinganapplication,wecansaythatnaturedoesactuallyrefactoritscodeineverysinglemoment.Thisisactuallythedreamofeveryproductmanager—anapplicationthatcanundergoconstantchangebutdoesnotloseanyofitsefficiency.

Ibelievethattherearetwokeyconceptsthatplayamajorroleinnaturethatallowsittoapplyconstantchangeinitsdesignwithoutlosingmuchefficiency.Thisusesencapsulationandcomposition.Comingbacktotheexampleofourbodies,wecanactuallytellthatourorgansuseaveryclearencapsulation.Theyusemembranestocreateisolation,veinstotransportnutrition,andsynapsestosendmessages.Also,theyhaveinterdependencies,andtheycommunicatewithelectricalandchemicalmessages.Mostobviously,theyformlargersystems,whichisthecoreconceptofcomposition.

Ofcourse,therearemanyotherfactors,andI'mnotaprofessorinbiology.However,Ithinkit'safascinatingthingtoseethatwehavelearnedtoorganizeourcodeverysimilarlytohownatureorganizesmatter.

TheideaofcreatingreusableUIcomponentsisquiteold,anditwasimplementedinvariouslanguagesandframeworks.OneoftheearliestsystemsthatusedUIcomponentswasprobablytheXeroxAltosystembackin1970s.ItusedreusableUIcomponentsthatalloweddeveloperstocreateanapplicationbycomposingthemonascreenwhereuserscouldinteractwiththem.

www.EBooksWorld.ir

Page 40: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

TheuserinterfaceoffilemanagerontheXeroxAltosystemfromthe1970s.

EarlyfrontendUIframeworks,suchasDHTMLX,ExtJS,orjQueryUIimplementedcomponentsinamorelimitedfashionthatdidn'tprovidegreatflexibilityorextensibility.Mostoftheseframeworksjustprovidedwidgetlibraries.TheproblemwithUIwidgetsisthat

www.EBooksWorld.ir

Page 41: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

theymostlydon'tembracethepatternofcompositionenough.Youcanarrangewidgetsonapageandtheyprovideencapsulation,butwithmosttoolkits,youcan'tcreatelargercomponentsbynestingtheminsideeachother.Sometoolkitssolvethisbyprovidingaspecialkindofwidgetwhichwasmostlycalledacontainer.However,thisisnotthesameasafull-fledgedcomponenttreethatallowsyoutocreatesystemswithinsystems.Containerswereactuallymeanttoprovideavisuallayoutcontainerratherthanacompositecontainertoformalargersystem.

Usuallywhenworkingwithwidgetsonapageofourapplication,we'dhavealargecontrollerthatcontrolsallthesewidgets,userinput,andstates.However,weareleftwithtwolevelsofcomposition,andthere'snowaythatwecanstructureourcodemoregranularly.Thereisthepageandtherearethewidgets.HavingabunchofUIwidgetsissimplynotenough,andwearealmostbacktothestatewherewecreatepagesplasteredwithformelements.

I'vebeenauserofJavaServerFacesforyears,andbesidesallitsproblems,theconceptofhavingreusablecustomelementswasgroundbreaking.UsingXHTML,onecouldwriteso-calledcompositecomponentsthatconsistedofothercompositecomponentsornativeHTMLelements.Adevelopercouldgainafantasticlevelofreusabilityusingcomposition.Thebigissueinmyviewwiththistechnologywasthatitdidnotaddresstheconcernsinthefrontendenoughtobecomereallyusableforcomplexuserinteractions.Infact,aframeworklikethisshouldlivecompletelywithinthefrontend.

MyUIframeworkwishlist

UsuallywhenUIframeworksgetcompared,theygetmeasuredagainsteachotherbasedonmetrics,suchaswidgetcount,themingcapabilities,andasynchronousdataretrievalfeatures.Everyframeworkhasitsstrengthsandweaknesses,butleavingalltheextrafeaturesasideandreducingittothecoreconcernsofaUIframework,IonlyhaveafewmetricsleftthatI'dliketobeassessed.Thesemetricsare,ofcourse,nottheonlyonesthatareimportantintoday'sUIdevelopment,buttheyalsoarethemainfactorstowardbuildingacleanarchitecturethatsupportstheprincipleofchange:

IcancreateencapsulatedcomponentswithclearinterfacesIcancreatelargercomponentsbycompositionIcanmakecomponentsinteractwitheachotherwithintheirhierarchy

Ifyou'relookingforaframeworkwhichenablesyoutotakefulladvantageofcomponent-basedUIdevelopment,youshouldlookforthesethreekeymeasures.

Firstofall,Ithinkit'sveryimportanttounderstandthemainpurposeofthewebandhowitevolved.Ifwethinkofthewebinitsearlydaysinthe1990s,itwasprobablyonlyabouthypertext.Therewereverybasicsemanticsthatcouldbeusedtostructureinformationanddisplaythemtoauser.HTMLwascreatedtoholdstructureandinformation.TheneedforcustomvisualpresentationofinformationledtothedevelopmentofCSSrightafterHTMLstartedbeingwidelyused.

www.EBooksWorld.ir

Page 42: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Itwasinthemid1990swhenBrendanEichinventedJavaScript,anditwasfirstimplementedinNetscapeNavigator.Byprovidingawaytoimplementbehaviorandstate,JavaScriptwasthelastmissingpieceforafullwebcustomization:

Technology Concern

HTML Structureandinformation

CSS Presentation

JavaScript Behaviorandstate

Wehavelearnedtokeeptheseconcernsasseparateaspossibleinordertomaintainacleanarchitecture.Althoughtherearedifferentopinionsonthisandsomerecenttechnologiesalsomoveawayfromthisprinciple,Ibelievethatacleanseparationoftheseconcernsisveryimportanttocreateamaintainableapplication.

Leavingthisviewaside,thestandarddefinitionofencapsulationfromOOPisjustconcernedaboutcouplingandisolationoflogicanddata.Thisprobablyapplieswelltoclassicsoftwarecomponents.However,assoonasweconsiderauserinterfaceaspartofanarchitecture,thereisanewdimensionthatisadded.

ClassicalMVCframeworksareviewcentric,anddevelopersorganizetheircodebasedonpages.You'llprobablygoaheadandcreateanewviewthatrepresentsapage.Ofcourse,yourviewneedsacontrollerandmodel,soyou'llalsocreatethem.Theproblemwithorganizationbypagesisthatthere'slittletonogainofreusability.Onceyou'vecreatedapageandyou'dliketoreuseonlysomepartsofthepage,youwillneedawaytoencapsulateonlyaspecificpartofthismodel—theviewandthecontroller.

UIcomponentssolvethisproblemnicely.IliketoseethemasamodularapproachtoMVC.AlthoughtheystillembracetheMVCpattern,theyalsoestablishencapsulationandcomposability.Thiswayaviewisacomponentitself,butitalsoconsistsofcomponents.Bycomposingviewsofcomponents,onecangainamaximumamountofreusability:

www.EBooksWorld.ir

Page 43: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

UIcomponentsembraceMVC,buttheyalsosupportencapsulationandcompositiononamuchlowerlevel

Technically,therearesomechallengeswhenimplementingcomponentswithwebtechnologies.JavaScriptwasalwaysflexibleenoughtoimplementdifferentpatternsandparadigms.Workingwithencapsulationandcompositionisn'tanissueatall,andthecontrollingpartandthemodelofcomponentscaneasilybeimplemented.Approaches,suchastherevealingmodulepattern,namespaces,prototypes,ortherecentECMAScript6modules,provideallthetoolsthatareneededfromtheJavaScriptside.

However,fortheviewpartofourcomponents,wefacesomelimitations.AlthoughHTMLsupportsgreatflexibilityintermsofcomposabilitybecausetheDOMtreeisnothingelsethanabigcomposition,wehavenowaytoreusethesecompositions.Wecanonlycreateonelargecomposition,whichisthepageitself.HTMLbeingonlythefinalviewthatwasdeliveredfromtheserver,thiswasneverreallyarealconcern.Today'sapplicationsaremuchmoredemanding,andweneedtohaveafully-encapsulatedcomponentrunninginthebrowser,whichalsoconsistsofapartialview.

WefacethesameproblemwithCSS.ThereisnorealmodularizationandencapsulationwhilewritingCSS,andweneedtousenamespacesandprefixesinordertosegregateourCSSstyles.Still,thewholecascadingnatureofCSScaneasilydestroyallencapsulationthatwetrytobringinplaceusingCSS-structuringpatterns.

www.EBooksWorld.ir

Page 44: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

TimefornewstandardsWebstandardshavebeenevolvingimmenselyinthelastcoupleofyears.Therearesomanynewstandards,andthebrowserbecamesuchabigmultimediaframework,thatit'shardforotherplatformstocompetewiththis.

I'devengoasfarastosaythatwebtechnologywillactuallyreplaceotherframeworksinthefuture,anditprobablywillberenamedtomultimediatechnologyorsomethingsimilar.There'snoreasonwhyweneedtousedifferentnativeframeworkstocreateuserinterfacesandpresentations.Webtechnologiesembedsomanyfeaturesthatit'shardtofindareasonnottousethemforanykindofapplication.JustlookattheFirefoxOSortheChromeOS,whicharedesignedtorunwithwebtechnologies.Ithinkit'sonlyamatteroftimeuntilmoreoperatingsystemsandembeddeddevicesmakeuseofwebtechnologiestoimplementtheirsoftware.ThisiswhyIbelievethatatsomepointitwillbequestionablewhetherthetermwebtechnologiesisstillappropriateorwhetherweshouldreplaceitwithamoregeneralterm.

Althoughweusuallyjustseenewfeaturesappearinbrowsers,thereisaveryopenandlong-windedstandardizationprocessbehindthem.It'sveryimportanttostandardizefeatures,butthistakesalotoftime,especiallywhenpeopledisagreeaboutdifferentapproachestosolvingproblems.

Comingbacktotheconceptofcomponents,thisissomethingwherewereallyneedsupportfromwebstandardstobreakthecurrentlimitations.Fortunately,theW3Cthoughtthesame,andagroupofdevelopersstartedtoworkonspecificationsunderthehoodofanumbrellaspecificationcalledwebcomponents.

ThefollowingtopicswillgiveyouabriefoverviewovertwospecificationsthatalsoplayaroleinAngular2components.OneofAngular2'scorestrengthsisthatitactsmorelikeasupersetofwebstandardsratherthanbeingacompleteisolatedframework.

Templateelements

TemplateelementsallowyoutodefineregionswithinyourHTML,whichwillnotberenderedbythebrowser.YoucantheninstantiatethesedocumentfragmentswithJavaScriptandthenplacetheresultingDOMwithinyourdocument.

Whilethebrowserisactuallyparsingthetemplatecontent,itonlydoessoinordertovalidatetheHTML.Anyimmediateactionsthattheparserwouldusuallyexecutewillnotbetaken.Withinthecontentoftemplateelements,imageswillnotbeloadedandscriptswon'tbeexecuted.Onlyafteratemplateisinstantiated,theparserwilltakethenecessaryactions,asfollows:

<body>

<templateid="template">

<h1>Thisisatemplate!</h1>

</template>

www.EBooksWorld.ir

Page 45: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

</body>

ThissimpleHTMLexampleofatemplateelementwon'tdisplaytheheadingonyourpage.Astheheadingisinsideatemplateelement,wefirstneedtoinstantiatethetemplateandaddtheresultingDOMintoourdocument:

vartemplate=document.querySelector('#template');

varinstance=document.importNode(template.content,true);

document.body.appendChild(instance);

UsingthesethreelinesofJavaScript,wecaninstantiatethetemplateandappenditintoourdocument.

ShadowDOM

ThispartofthewebcomponentsspecificationwasthemissingpiecetocreateproperDOMencapsulationandcomposition.WithshadowDOM,wecancreateisolatedpartsoftheDOMthatareprotectedagainstregularDOMoperationsfromtheoutside.Also,CSSwillnotreachintoshadowDOMautomatically,andwecancreatelocalCSSwithinourcomponent.

Tip

IfyouaddastyletaginsideshadowDOM,thestylesarescopedtotherootwithintheshadowDOM,andtheywillnotleakoutside.ThisenablesaverystrongencapsulationforCSS.

ContentinsertionpointsmakeiteasytocontrolcontentfromtheoutsideofashadowDOMcomponent,anditprovidessomekindofaninterfacetopassincontent.

Atthetimeofwritingthisbook,shadowDOMissupportedbymostbrowsersalthoughitstillneedstobeenabledinFirefox.

www.EBooksWorld.ir

Page 46: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Angular'scomponentarchitectureForme,theconceptofdirectivesfromthefirstversionofAngularchangedthegameinfrontendUIframeworks.ThiswasthefirsttimethatIfeltthattherewasasimpleyetpowerfulconceptthatallowedthecreationofreusableUIcomponents.DirectivescouldcommunicatewithDOMeventsormessagingservices.Theyallowedyoutofollowtheprincipleofcomposition,andyoucouldnestdirectivesandcreatelargerdirectivesthatsolelyconsistedofsmallerdirectivesarrangedtogether.Actually,directiveswereaveryniceimplementationofcomponentsforthebrowser.

Inthissection,we'lllookintothecomponent-basedarchitectureofAngular2andhowthethingswe'velearnedaboutcomponentswillfitintoAngular.

www.EBooksWorld.ir

Page 47: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

EverythingisacomponentAsanearlyadopterofAngular2andwhiletalkingtootherpeopleaboutit,Igotfrequentlyaskedwhatthebiggestdifferenceistothefirstversion.Myanswertothisquestionwasalwaysthesame.Everythingisacomponent.

Forme,thisparadigmshiftwasthemostrelevantchangethatbothsimplifiedandenrichedtheframework.Ofcourse,therearealotofotherchangeswithAngular2.However,asanadvocateofcomponent-baseduserinterfaces,I'vefoundthatthischangeisthemostinterestingone.Ofcourse,thischangealsocamewithalotofarchitecturalchanges.

Angular2supportstheideaoflookingattheuserinterfaceholisticallyandsupportingcompositionwithcomponents.However,thebiggestdifferencetoitsfirstversionisthatnowyourpagesarenolongerglobalviews,buttheyaresimplycomponentsthatareassembledfromothercomponents.Ifyou'vebeenfollowingthischapter,you'llnoticethatthisisexactlywhataholisticapproachtouserinterfacesdemands.Nomorepagesbutsystemsofcomponents.

Tip

Angular2stillusestheconceptofdirectives,althoughdirectivesarenowreallywhatthenamesuggests.Theyareordersforthebrowsertoattachagivenbehaviortoanelement.

www.EBooksWorld.ir

Page 48: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Componentsareaspecialkindofdirectivesthatcomewithaview.

www.EBooksWorld.ir

Page 49: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

YourfirstcomponentKeepingupthetradition,beforewestartbuildingarealapplicationtogether,weshouldwriteourfirsthelloworldcomponentwithAngular:

//Decoratorsallowustoseparatedeclarativelogicfromour

//componentimplementationlogic.

@Component({

selector:'hello-world',

template:'<div>Hello{{name}}</div>'

})

classHelloWorld{

constructor(){

this.name='World';

}

}

Thisisalreadyafully-workingAngular2application.WeusedECMAScript6classestocreatethenecessaryencapsulationrequiredforacomponent.Youcanalsoseeameta-annotationthatisusedtodeclarativelyconfigureourcomponent.Thisstatement,whichlookslikeafunctioncallthatisprefixedwithanatsymbolactuallycomesfromtheECMAScript7decoratorproposal.

Note

ECMAScript7decoratorsarestillveryexperimentalatthetimeofwritingthisbook.Forthecodeinthisbook,weactuallyusetheTypeScripttranspilerversion1.5thatalreadyimplementsdecoratorswithaslighttwisttotheoriginalspecification.TypeScript1.5isalsousedbytheAngular2teamtodevelopthecoreofAngular.

It'simportanttounderstandthatanelementcanonlybeboundtoonesinglecomponent.Asacomponentalwayscomeswithaview,thereisnowaythatwecanbindmorethanonecomponenttoanelement.Ontheotherhand,anelementcanbeboundtomanydirectives,asdirectivesdon'tcomewithaviewbuttheyonlyattachbehavior.

IntheComponentdecorator,weneedtoconfigureeverythingthatisrelevanttodescribeourcomponentforAngular.This,ofcourse,alsoincludesourtemplatefortheview.Intheprecedingexample,wearespecifyingourtemplatedirectlywithinJavaScriptasastring.WecanalsousethetemplateUrlpropertytospecifyaURLwherethetemplateshouldbeloadedfrom.

Now,let'senhanceourexamplealittlebitsothatwecanseehowwecancomposeourapplicationfromsmallercomponents:

//Usingdecoratorswecandeclarativelydefineourcomponentused

//towriteboldtext

@Component({

selector:'shout-out',

www.EBooksWorld.ir

Page 50: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

template:'<strong>{{words}}</strong>'

})

classShoutOut{

@Input()words;

}

//Thiscomponentwillbeourmainapplicationcomponentthat

//makesuseoftheaboveshout-outcomponent(composition)

@Component({

selector:'hello-world'

template:'<shout-outwords="Hello,{{name}}!"></shout-out>',

directives:[ShoutOut]

})

classHelloWorld{

constructor(){

this.name='World';

}

}

Youcanseethatwehavenowcreatedasmallcomponentthatallowsustoshoutoutwordsaswelike.InourHelloWorldapplication,wemakeuseofthiscomponenttoshoutoutHello,World!

Tip

Everydirectiveorcomponentthatisusedinsideacomponentsviewtemplateneedstobeexplicitlydeclaredinthedirectivespropertyoftheviewannotation.Otherwise,thecompilerwillnotrecognizethedirectivewhenitencounterstheelementinthetemplate.

Overthecourseofthisbookandwhilewritingourtaskmanagementapplication,wewilllearnalotmoreabouttheconfigurationandimplementationofcomponents.However,beforewestartwiththisinthesecondchapter,weshouldtakealookatsometoolsandlanguagefeaturesthatwe'lluseduringthisbook.

www.EBooksWorld.ir

Page 51: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

JavaScriptofthefutureItwasnotsolongagothatsomebodyaskedmewhetherweshouldreallyusethebindfunctionofECMAScript5.1,asthenwe'dprobablyrunintobrowsercompatibilityissues.Thewebmovesveryfast,andweneedtokeepupthepace.Wecan'twritecodethatdoesnotusethelatestfeaturesevenifthiswouldcauseissuesinoldbrowsers.

ThefantasticpeoplefromTC39,thetechnicalcommitteethatisresponsibleforwritingtheECMAScriptspecification,havedoneagreatjobprogressivelyenhancingtheJavaScriptlanguage.This,andthefactthatJavaScriptissoflexible,allowsustouseso-calledpolyfillsandshimstomakeourcoderuninolderbrowsers.

ECMAScript6(alsoreferredtoasECMAScript2015)waspublishedinJune2015,exactlyfouryearsafteritspredecessor.ThereisamassiveamountofnewAPIadditionsaswellasawholebunchofnewlanguagefeatures.Thelanguagefeaturesaresyntacticsugar,andECMAScript6canbetranspiledtoitspreviousversionwhereitrunsperfectlyinolderbrowsers.Atthetimeofwritingthisbook,noneofthecurrentbrowserversionshavefullyimplementedECMAScript6,butthere'sabsolutelynoreasonnottouseitforproductionapplications.

Tip

Syntacticsugarisadesignapproachwhereweevolveaprogramminglanguagewhilenotbreakingbackwardscompatibility.Thisallowslanguagedesignerstocomeupwithnewsyntax,whichenrichesdeveloperexperiencebutdoesnotbreaktheweb.Everynewfeatureneedstobetranslatabletotheoldsyntax.Thisway,so-calledtranspilerscanbeusedtoconvertcodetoolderversions.

IspeakJavaScript,translate,please!

Whilecompilerscompilefromahigher-levellanguagetoalower-levellanguage,atranspilerortranscompileractsmorelikeaconverter.Itisasource-to-sourcecompilerthattranslatescodetoruninadifferentinterpreter.

Recently,there'sarealbattleamongnewlanguagesthataretranspiledtoJavaScriptandcanruninthebrowser.IusedGoogleDartforquitesometime,andImustadmit,Ireallylovedthelanguagefeatures.Theproblemwithnonstandardizedlanguagesisthattheydependheavilyoncommunityadoptionandthehype.Also,it'salmostcertainthattheywillneverrunnativelywithinthebrowser.ThisisalsothereasonwhyIpreferstandardJavaScript,andtheJavaScriptofthefutureusestranspilersthatallowmetodothis.

Somepeoplearguethattranspilersintroducecodethatdoesnotrunveryperformantand,therefore,recommendthatyoudonotuseECMAScript6andtranspilersatall.Idon'tagreewiththisbecauseofmanyreasons.Usually,thisisaboutperformanceinmicroorevennanosecondareaswherethisoftenreallydoesnotmatterformostapplications.

www.EBooksWorld.ir

Page 52: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Idon'tsayperformancedoesn'tmatter,butperformanceneedstoalwaysbediscussedwithinacontext.Ifyou'retryingtooptimizealoopwithinyourapplicationbyreducingprocessingtimefrom10microsecondstofivemicrosecondswhereyou'dneveriterateovermorethan100items,thenyou'reprobablyspendingyourtimeonthewrongthings.

Also,averyimportantfactisthattranspiledcodeisdesignedbypeoplewhounderstandmicroperformanceoptimizationmuchbetterthanIdo,andI'msuretheircoderunsfasterthanmine.Ontopofthis,atranspilerisprobablyalsotherightplacewhereyou'dwanttodoperformanceoptimizationbecausethiscodeisautomaticallygeneratedandyoudon'tlosemaintainabilityofyourcodethroughperformancequirks.

I'dliketoquoteDonaldKnuthhereandsaythatprematureoptimizationistherootofallevil.Ireallyrecommendthatyoureadhispaperonthistopic(DonaldKnuth,December1974,StructuredProgrammingwithgotoStatements).Justbecausethegotostatementsgotbanishedfromallmodernprogramminglanguages,itdoesn'tmeanthisislessofagoodread.

Lateroninthischapter,you'lllearnabouttoolsthathelpyouusetranspilerseasilywithinyourproject,andwe'lltakealookatthedecisionsanddirectionsAngularwentwiththeirsourcecode.

Let'slookatafewlanguagefeaturesthatcomewithECMAScript6andmakeourlifemucheasier.

Classes

ClasseswereamongonethemostrequestedfeaturesinJavaScript,andIwasoneofthepeoplevotingforit.Well,comingfromanOOPbackgroundandbeingusedtoorganizingeverythingwithinclasses,itwashardformetoletgo.Although,afterworkingwithmodernJavaScriptforsometime,you'llreducetheirusetothebareminimumandtoexactlywhattheyaremadefor—inheritance.

ClassesinECMAScript6provideyouwithsyntacticsugartodealwithprototypes,constructorfunctions,supercalls,andobjectpropertydefinitionsinawaythatyouhavetheillusionthatJavaScriptcouldbeaclass-basedOOPlanguage:

classFruit{

constructor(name){this.name=name;}

}

constapple=newFruit('Apple');

Aswelearnedintheprevioustopicabouttranspilers,ECMAScript6canbede-sugaredtoECMAScript5.Let'stakealookatwhatatranspilerwouldproducefromthissimpleexample:

functionFruit(name){this.name=name;}

varapple=newFruit('Apple');

ThissimpleexamplecaneasilybebuiltusingECMAScript5.However,onceweusethemore

www.EBooksWorld.ir

Page 53: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

complexfeaturesofclass-basedobject-orientedlanguages,thede-sugaringgetsquitecomplicated.

ECMAScript6classesintroducesimplifiedsyntaxtowriteclassmemberfunctions(staticfunctions),theuseofthesuperkeyword,andinheritanceusingtheextendskeyword.

IfyouwouldliketoreadmoreaboutthefeaturesinclassesandECMAScript6,IhighlyrecommendthatyoureadthearticlesofDr.AxelRauschmayer(http://www.2ality.com/).

Modules

Modulesprovideawaytoencapsulateyourcodeandcreateprivacy.Inobject-orientedlanguages,weusuallyuseclassesforthis.However,Iactuallybelievethisisanantipatternratherthanagoodpractice.Classesshouldbeusedwhereinheritanceisdesiredandnotjusttostructureyourcode.

I'msurethatyou'veencounteredalotofdifferentmodulepatternsinJavaScriptalready.Oneofthemostpopularonesthatcreatesprivacyusingafunctionclosureofanimmediatelyinvokedfunctionexpression(IIFE)isprobablytherevealingmodulepattern.Ifyou'dliketoreadmoreaboutthisandmaybeothergreatpatterns,Irecommendthebook,LearningJavaScriptDesignPatterns,byAddyOsmani.

WithinECMAScript6,wecannowusemodulestoservethispurpose.Wesimplycreateonefilepermodule,andthenweusetheimportandexportkeywordstoconnectourmodulestogether.

WithintheECMAScript6modulespecification,wecanactuallyexportasmanythingsaswelikefromeachmodule.Wecanthenimportthesenamedexportsfromanyothermodule.Wecanhaveonedefaultexportpermodule,whichisespeciallyeasytoimport.Defaultexportsdon'tneedtobenamed,andwedon'tneedtoknowtheirnamewhenimportingthem:

importSomeModulefrom'./some-module.js';

varsomething=SomeModule.doSomething();

exportdefaultsomething;

Therearemanycombinationsonhowtousemodules.Wewilldiscoversomeofthesetogetherwhileworkingonourtaskmanagementapplicationduringtheupcomingchapters.Ifyou'dliketoseemoreexamplesonhowtousemodules,IcanrecommendtheMozillaDeveloperNetworkdocumentation(https://developer.mozilla.org)ontheimportandexportkeywords.

Templatestrings

Templatestringsareaverysimple,buttheyareanextremelyusefuladditiontotheJavaScriptsyntax.Theyservethreemainpurposes:

Writingmultilinestrings

www.EBooksWorld.ir

Page 54: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

StringinterpolationTaggedtemplatestrings

Beforetemplatestrings,itwasquiteverbosetowritemultilinestrings.Youneededtoconcatenatepiecesofstringsandappendanew-linecharacteryourselftothelineendings:

constheader='<header>\n'+

'<h1>'+title+'</h1>\n'+

'</header>';

Usingtemplatestrings,wecansimplifythisexamplealot.Wecanwritemultilinestrings,andwecanalsousethestringinterpolationfunctionalityforourtitlevariablethatweusedtoconcatenateearlier:

constheader='

<header>

<h1>${title}</h1>

</header>

`;

Notethebackticksinsteadoftheprevioussinglequotes.Templatestringsarealwayswrittenbetweenbackticks,andtheparserwillinterpretallcharactersinbetweenthemaspartoftheresultingstring.Thisway,thenew-linecharacterspresentinoursourcefilewillalsobepartofthestringautomatically.

Youcanalsoseethatwehaveusedthedollarsign,followedbycurlybracketstointerpolateourstrings.ThisallowsustowritearbitraryJavaScriptwithinstringsandhelpsalotwhileconstructingHTMLtemplatestrings.

YoucanreadmoreabouttemplatestringsontheMozillaDeveloperNetwork.

ECMAScriptorTypeScript?

TypeScriptwascreatedin2012byAndersHejlsbergwiththeintentiontoimplementthefuturestandardofECMAScript6butalsotoprovideasupersetofsyntaxandfeaturesthatwasnotpartofthespecification.

TherearemanyfeaturesinTypeScriptasasupersettotheECMAScript6standard,including,butnotlimitedtothefollowing:

OptionalstatictypingwithtypeannotationsInterfacesEnumtypesGenerics

It'simportanttounderstandthatallofthefeaturesthatTypeScriptprovidesasasupersetareoptional.YoucanwritepureECMAScript6andnottakeadvantageoftheadditionalfeaturesthatTypeScriptprovides.TheTypeScriptcompilerwillstilltranscompilepureECMAScript6

www.EBooksWorld.ir

Page 55: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

codetoECMAScript5withoutanyerrors.

Note

MostofthefeaturesthatareseeninTypeScriptareactuallyalreadypresentinotherlanguages,suchasJavaandC#.OnegoalofTypeScriptwastoprovidelanguagefeaturesthatsupportworkflowsandbettermaintainabilityforlarge-scaleapplications.

Theproblemwithanynonstandardlanguageisthatnobodycantellhowlongthelanguagewillbemaintainedandhowfastthemomentumofthelanguagewillbeinthefuture.Intermsofsupport,thechancesarehighthatTypeScript,withitssponsorMicrosoft,willactuallyhavealonglife.However,there'sstillnoguaranteethatthemomentumandtrendofthelanguagewillkeepmovingatareasonablepace.ThisproblemdoesobviouslynotexistforstandardECMAScript6becauseit'swhatthewebofthefutureismadeofandwhatbrowserswillspeaknatively.

Still,therearevalidreasonstousetheextendedfeaturesofTypeScriptifyou'dwanttoaddressthefollowingconcernsthatclearlyoutweighthenegativeimplicationsofanuncertainfutureinyourproject:

LargeapplicationsthatundergoahugeamountofchangesandrefactoringLargeteamsthatrequireastrictgovernancewhileworkingoncode

Inthisbook,we'lluseaTypeScriptcompiler,butwewillworkwithstandardECMAScript6codewithoneexceptionthatiscoveredinthenexttopicaboutdecorators.

Decorators

DecoratorsarenotpartoftheECMAScript6specification,buttheywereproposedtotheECMAScript7standardfor2016.Theyprovideuswithawaytodecorateclassesandpropertiesduringdesigntime.Thisallowsadevelopertousemeta-annotationswhilewritingclasses,anddeclarativelyattachfunctionalitytotheclassanditsproperties.

DecoratorsarenamedafterthedecoratorpatternthatwasinitiallydescribedinthebookDesignPatterns:ElementsofReusableObject-OrientedSoftwareofErichGammaandhiscolleagues,alsoknownastheGangofFour(GoF).

Theprincipleofdecorationisthatanexistingprocedureisinterceptedandthedecoratorhasthechancetoeitherdelegate,provideanalternativeprocedure,ordoamixfromboth.

www.EBooksWorld.ir

Page 56: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Visualizationofdecorationinadynamicenvironmentwiththeexampleofasimpleaccessprocedure

DecoratorsinECMAScript7canbeusedtoannotateclassesandclassproperties.Notethatthisalsoincludesclassmethods,asclassmethodsarealsopropertiesoftheclassprototypeobject.Decoratorsgetdefinedasregularfunctions,andtheycanbeattachedtoclassesorclasspropertieswiththeatsymbol.Ourdecoratorfunctionwillthenbecalledwithcontextualinformationaboutthelocationofinclusioneverytimethatthedecoratorisplaced.

Let'stakealookatasimpleexamplethatillustratestheuseofadecorator:

functionlogAccess(obj,prop,descriptor){

constdelegate=descriptor.value;

descriptor.value=function(){

console.log(`${prop}wascalled!`);

returndelegate.apply(this,arguments);

};

}

classMoneySafe{

@logAccess

openSafe(){

this.open=true;

}

}

www.EBooksWorld.ir

Page 57: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

constsafe=newMoneySafe();

safe.openSafe();//openSafewascalled!

WehavecreatedalogAccessdecoratorthatwilllogallfunctioncallsthataretaggedwiththedecorator.IfwelookattheMoneySafeclass,youcanseethatwehavedecoratedtheopenSafemethodwithourlogAccessdecorator.

ThelogAccessdecoratorfunctionwillbeexecutedforeachannotatedpropertywithinourcode.Thisenablesustointerceptthepropertydefinitionofthegivenproperty.Let'stakealookatthesignatureofourdecoratorfunction.Decoratorfunctionsthatareplacedonclasspropertieswillbecalledwiththetargetobjectofthepropertydefinitionasafirstparameter.Thesecondparameteristheactualpropertynamethatisdefined,followedbythelastparameter,whichisthedescriptorobjectthatissupposedtobeappliedtotheobject.

Thedecoratorgivesustheopportunitytointerceptthepropertydefinition.Inourcase,weusethisabilitytoexchangethedescriptorvalue(whichistheannotatedfunction)withaproxyfunctionthatwilllogthefunctioncallbeforecallingtheoriginfunction(delegation).Forsimplificationpurposes,we'veimplementedaverysimpleyetincompletefunctionproxy.Forreal-worldscenarios,itwouldbeadvisabletouseabetterproxyimplementation,suchastheECMAScript6proxyobject.

Decoratorsareagreatfeaturetoleverageaspect-orientedconceptsanddeclarativelyaddbehaviortoourcodeatdesigntime.

Let'slookatasecondexamplewhereweuseanalternativewaytodeclareandusedecorators.Wecantreatdecoratorslikefunctionexpressionswhereourdecoratorfunctionisrewrittenasafactoryfunction.Thisformofusageisespeciallyusefulwhenyouneedtopassalongconfigurationtothedecorator,whichismadeavailableinthedecoratorfactoryfunction:

functiondelay(time){

returnfunction(obj,prop,descriptor){

constdelegate=descriptor.value;

descriptor.value=function(){

constcontext=this;

constargs=arguments;

returnnewPromise(function(success){

setTimeout(function(){

success(delegate.apply(context,arguments));

},time);

});

};

};

}

classDoer{

@delay(1000)

doItLater(){

console.log('Ididit!');

}

www.EBooksWorld.ir

Page 58: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

}

constdoer=newDoer();

doer.doItLater();//Ididit!(after1second)

WehavenowlearnedhowECMAScript7decoratorscanhelpyoutowritedeclarativecodethathasanaspect-orientedtwisttoit.Thissimplifiesdevelopmentalotbecausewecannowthinkofbehaviorthatweaddtoourclassesduringdesigntimewhenweactuallythinkabouttheclassasawholeandwritetheinitialstuboftheclass.

DecoratorsinTypeScriptareslightlydifferentthanthedecoratorsfromECMAScript7.Theyarenotlimitedtoclassesandclassproperties,buttheycanalsobeplacedonparameterswithintheclassmethods.Thisallowsyoutoannotatefunctionparameters,whichcanbeusefulinsomecases:

classTypeScriptClass{

constructor(@ParameterDecorator()param){}

}

Angularusesthisfeaturetosimplifydependencyinjectiononclassconstructors.Asalldirective,component,andserviceclassesgetinstantiatedfromAngulardependencyinjectionandnotbyusdirectly,theseannotationshelpAngularfindthecorrectdependencies.Forthisuse-case,functionparameterdecoratorsactuallymakealotofsense.

Note

Currently,therearestillissueswiththeimplementationofdecoratorsonclassmethodparameters,whichisalsowhyECMAScript7doesnotsupportit.AsthisfeatureiscrucialtobuildanAngular2application,we'llusetheTypeScriptcompilertotranspilethecodeofourapplication.ThisistheonlyTypeScript-specificfeaturethatwe'lluseinthisbook.

www.EBooksWorld.ir

Page 59: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

ToolsInordertomakeuseofallthesefuturetechnologies,weneedsometoolstosupportus.WewerealreadytalkingaboutECMAScript6anddecorators,whereweactuallypreferTypeScriptdecorators,astheysupportthefunctionparameterdecoratorsthatareusedbyAngular2.AlthoughtheECMAScript6syntaxsupportsmodules,westillneedsomesortofamoduleloaderthatwillactuallyloadtherequiredmodulesinthebrowserorhelpusgenerateanexecutablebundle.

www.EBooksWorld.ir

Page 60: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Node.jsandNPMNode.jsisJavaScriptonsteroids.Initially,aforkoftheV8JavaScriptenginefromtheGoogleChromebrowser,Node.jswasextendedwithmorefunctionality,specificallytomakeJavaScriptusefulontheserver-side.Filehandling,streams,systemAPIs,andahugeecosystemofuser-generatedpackagesarejustsomeofthefactsthatmakethistechnologyanoutstandingpartnerforyourwebdevelopment.

Thenodepackagemanager,NPM,isadoortoover200,000packagesandlibrariesthathelpyoubuildyourownapplicationorlibrary.TheNode.jsphilosophyisverysimilartotheUNIXphilosophy,wherepackagesshouldstaysmallandsharp,buttheyshouldusecompositiontoachievegreatergoals.

Tobuildourapplication,wewillrelyonNode.jsasthehostforthetoolsthatwe'regoingtouse.Weshould,therefore,makesurethatweinstallNode.jsonourmachinesothatwearepreparedforthenextchapter,wherewestarttocraftourtaskmanagementapplication.

Note

YoucangetNode.jsfromtheirwebsiteathttps://nodejs.org,anditshouldbeabreezetoinstallthisonanykindofoperatingsystembyfollowingtheinstructionsontheirwebsite.

Onceyou'veinstalledNode.js,wecanperformasimpletesttocheckwhethereverythingisupandrunning.Openaterminalconsoleandexecutethefollowingcommand:

node-e"console.log('HelloWorld');"

www.EBooksWorld.ir

Page 61: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

SystemJSandJSPMTherearemanymoduleformatsandmoduleloadersoutthere,butthere'sonethatrulesthemallinmyopinion.SystemJSisbuiltontopofanES6moduleloaderpolyfilland,therefore,movesveryclosetoanupcomingstandard.Istronglybelieveinstandardizationand,therefore,preferSystemJSoverothermoduleloaders,suchasRequireJS,Browserify,orwebpack.Weshouldstopusinglibrarieswherepossibleandrelyonpolyfillsthatmakeourbrowsercapableofrunningthecodeofthefuture.

SystemJSisauniversalmoduleloaderthatiscapableofloadingmanydifferentmoduleformats,suchasAMD,CommonJS,andECMAScript6,anditalsosupportsaveryflexibleshimingmechanismtomodularizeglobalJavaScript.

SystemJSalsosupportsthemostpopulartranspilers,includingECMAScript6andTypeScript.ThismeansthatyoucanactuallyloadECMAScript6codedirectlyinyourbrowser,whereit'stranspiledbySystemJSinruntime.Thisisgreatduringdevelopment,especiallybecauseyou'reallowedtoloadmodulesfromanylocation,includingremoteHTTPlocations,suchasGitHubortheNPMrepository.

JSPM

TheJavaScriptpackagemanagerisnotjustanotherpackagemanagerforJavaScript.ThisisbasicallyamediatorandmanagerforSystemJSthathelpsyoulookuppackagesfrompackagerepositories,suchasBowerorNPM,anditcreatesthenecessaryconfigurationforSystemJS.JSPMiswritteninNode.jsanddoesnotcomewithitsownremotepackagerepository.AsSystemJSneedsURLsandmodulemappingstoknowwheretoloadmodulesfrom,JSPMisyourtooltocreatethisnecessaryconfigurationandsimplifypackageinstallation.

GettingstartedwithJSPM

Let'screateasimpleapplicationtogetherusingJSPM.Firstofall,weneedtoinstalltwoglobalmoduleswithNPM.BesidesJSPM,we'llalsoinstallatoolcalledlive-server,whichwillhelpusduringdevelopmentbyprovidinganHTTPserverthatservesstaticfiles.Italsohasfilechangedetectionbuilt-in,anditwillreloadyourbrowserautomaticallyonceafilechangehasbeendetected.Thisprovidesaveryshortfeedbackloopandmakesdevelopmentaveryfastprocess:

1. Runthefollowingcommandonyourcommandline:

npminstall-gjspmlive-server

Tip

NotethatonUNIX-likesystems,suchasLinuxorMacOSX,it'ssometimesrequiredtorunNPMasasuperuser.It'salsorecommendedthatyouusetheNodeVersionManager(NVM)togetaroundthoseissues(https://github.com/creationix/nvm).

www.EBooksWorld.ir

Page 62: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

2. AfterinstallingJSPMandthelive-serverpackage,wecangoaheadandcreateourfirstapplicationusingJSPM.

3. Createanewdirectoryfortheapplication,andopenaterminalconsolewithinthisdirectory.

4. YoucannowexecutethefollowingcommandonyourterminalconsoletoinstallJSPMlocallyandinitializeanewJSPMproject:

npminstalljspm--save-dev

jspminit

5. JSPMwillstartawizardthatguidesyouthoughtheinitializationsteps.Youcananswerallquestionswiththedefaultanswer(justhitEnter)exceptforthequestionaboutwhichtranspileryou'dliketousethatyoushouldanswerwithTypeScript.

6. AfterJSPMinstallsallthenecessarypackages,wecangoaheadandcreateourindex.htmlfile.Navigatetoyourprojectfolderandcreateanewfile,index.html,inyourfavoriteeditor:

<!doctypehtml>

<scriptsrc="jspm_packages/system.js"></script>

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

<script>

System.import('main.js');

</script>

7. ThisveryminimalisticHTMLisalreadythefoundationforourJSPMHelloWorldapplication.AfterincludingtheSystemJSlibraryandtheconfig.jsfilethatwasgeneratedbyJSPM,weonlyneedtobootstrapourapplicationbytellingSystemJSwhichfiletoimport.

8. Beforewecreateourmainapplicationfile,wewillquicklyinstalljQueryasapackage,justtodemonstratehoweasilythird-partylibrariescanbeinstalledandusedwithSystemJSandJSPM:

jspminstalljquery

9. AfterinstallingjQuery,wecangoaheadandcreateourmain.jsfileinsideoftheapplicationfolder:

import$from'jquery';

classHelloWorld{

constructor(){

$(document.body).append('<h1>HelloWorld!</h1>');

}

}

consthelloWorld=newHelloWorld();

10. Inordertorunthisexampleinthebrowser,wecannowstartourliveserverwiththefollowingcommandexecutedinsideoftheapplicationfolder:

live-server

www.EBooksWorld.ir

Page 63: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Afterfollowingtheprecedingsteps,youshouldhaveaworkingexamplewithECMAScript6andSystemJSusingtheTypeScripttranspiler.UsingLiveReload,yourbrowsershouldautomaticallyopenanddisplayourHelloWorldapplication.YoucanalsotrynowtomodifythecodeabitandchangethesentencethatiswrittentotheDOM.You'llnoticethatonceyousaveyourchanges,thebrowserwillimmediatelyreloadthepage.

www.EBooksWorld.ir

Page 64: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

SummaryInthischapter,welookedatacomponent-basedapproachtostructureuserinterfaces,andwetalkedaboutthenecessaryaspectsofitsbackgroundtounderstandwhywearemovinginthisdirectionwiththewebstandardandframeworks,suchasAngular.Wealsoensuredthatwearepreparedwithallthetechnologythatwewilluseintheupcomingchaptersinthisbook.YoucreatedyourfirstsimpleexampleusingJSPM,SystemJS,ECMAScript6,andtheTypeScripttranspiler.Now,wearereadytostartbuildingourtask-managementsystemusingacomponent-basedarchitecturetoitsfullpotential.

Inthenextchapter,we'regoingtostartbuildingourtaskmanagementapplicationusingAngular2components.We'lllookattheinitialstepsthatarerequiredtocreateanAngular2applicationfromscratchandfleshoutthefirstfewcomponentsinordertobuildatasklist.

www.EBooksWorld.ir

Page 65: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Chapter2.Ready,Set,Go!Inthischapter,wewillstartbuildingourtaskmanagementapplication.We'lljumprightintothecoreoftheapplicationandcreatetheinitialcomponentsrequiredtomanageasimpletasklist.Intheprocessofgoingthroughthischapter,you'lllearnaboutthefollowingtopics:

BootstrappinganAngularapplicationusingamaincomponentComponentinputandoutputHostpropertybindingStylingandviewencapsulationImportingHTMLtemplatesusingtheSystemJStextloaderUsingEventEmittertoemitcustomeventsTwo-waydatabindingComponentlifecycle

www.EBooksWorld.ir

Page 66: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

ManagingtasksAfterpickingupthebasicsfromthepreviouschapter,wewillnowgoonandcreateataskmanagementapplicationtogetherintheupcomingchapters.You'lllearnaboutsomeconceptsduringthechaptersandthenusethemwithpracticalexamples.You'llalsolearnhowtostructureanapplicationusingcomponents.Thisbeginswiththefolderstructureandendswithsettinguptheinteractionbetweencomponents.

www.EBooksWorld.ir

Page 67: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

VisionThetaskmanagementapplicationshouldenableuserstomanagetaskseasilyandhelpthemorganizesmallprojects.Usabilityisthecentralaspectofanyapplication;therefore,you'llneedtodesignamodernandflexibleuserinterfacethatwillsupporttheuser.

Apreviewofthetaskmanagementapplicationwearegoingtobuild

Ourtaskmanagementapplicationwillconsistofcomponentsthatwillallowustodesignaplatformprovidingagreatuserexperienceforthepurposeofmanagingtasks.Let'sdefinethecorefeaturesofourapplication:

ManagingtaskswithinmultipleprojectsandprovidingaprojectoverviewSimpleschedulingaswellasatime-and-effort-trackingmechanismOverviewingthedashboardusinggraphicalchartsTrackingactivitiesandprovidingavisualauditlogAsimplecommentingsystemthatwouldworkacrossdifferentcomponents

Thetaskmanagementapplicationisthemainexampleinthisbook.Therefore,thebuildingblockswithinthebookshouldonlycontainthecodethatisrelevanttothethemeofthebook.Ofcourse,otherthancomponents,anapplicationneedsotherfunctionalities,suchasvisualdesign,data,sessionmanagement,andotherimportantparts,towork.Whiletherequiredcodeforeachchaptercanbedownloadedonline,we'llonlydiscussthecoderelevanttothetopicslearnedwithinthebook.

www.EBooksWorld.ir

Page 68: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

StartingfromscratchLet'sstartoutbycreatinganewfoldercalledangular-2-componentsinordertocreateourapplication:

1. OpenaconsolewindowinsideournewlycreatedfolderandrunthefollowingcommandtoinitializeanewNode.jsproject:

npminit

2. FinishtheinitializationwizardbyconfirmingallthestepswiththeEnterkey(defaultsettings).

3. Sincewe'reusingJSPMtomanageourdependencies,weneedtoinstallitasaprojectNode.jspackage:

npminstalljspm--save-dev

4. Let'salsoinitializeanewJSPMprojectwithinourprojectfolder.Besuretousethedefaultsettings(justhittheEnterkey)forallsettings,exceptforthestepwhereyouareaskedwhichtranspileryou'dliketouse.EnterTypeScriptatthisstage:

jspminit

5. We'llnowuseJSPMtoinstalltherelevantAngular2packagesintoourprojectasdependencies.We'llalsoinstallaSystemJSloaderplugintoloadtextfilesasmodules.We'llprovidesomedetailsaroundthislateron:

jspminstallnpm:@angular/corenpm:@angular/commonnpm:@angular/compiler

npm:@angular/platform-browser-dynamicnpm:rxjstext

Let'sexaminewhatwe'vebeencreatingsofarbyusingtheNPMandJSPMcommand-linetools.

Thepackage.jsonfileisourNode.jsconfigurationfilethatwe'reusingasthebasetoworkwithJSPM(thepackagemanager)andSystemJS(themoduleloaderwithtranspiler).Ifyoucheckoutthepackage.jsonfile,youwillseeanadditionalsectionforJSPMdependencies:

"jspm":{

"dependencies":{

"@angular/common":"npm:@angular/[email protected]",

"@angular/compiler":"npm:@angular/[email protected]",

"@angular/core":"npm:@angular/[email protected]",

"@angular/platform-browser-dynamic":"npm:@angular/platform-browser-

[email protected]",

"text":"github:SystemJS/[email protected]"

},

"devDependencies":{

"typescript":"npm:[email protected]",

}

}

www.EBooksWorld.ir

Page 69: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Let'stakeaquicklookatthedependencieswehaveinstalledusingJSPMandtheirpurpose:

Package Description

@angular/core

ThisisthecorepackageofAngular2,hostedonNPM.IfyourememberfromChapter1,Component-BasedUserInterfaces,JSPMisonlyabroker,anditdelegatestootherpackagerepositories.ThecorepackagecontainsallAngular-coremodules,suchasthe@Componentdecorator,changedetection,dependencyinjection,andmore.

@angular/common

TheAngularcommonpackageprovidesuswithbasedirectives,suchasNgIfandNgFor.Italsocontainsallthebasepipesandthedirectivesthatareusedtocontrolforms.

@angular/compiler

Thecompilerpackagecontainsalltheartifactsrequiredtocompileviewtemplates.Angularnotonlyprovidestheabilitytoprecompiletemplatestogainfasterbootingtime,butitalsousesthecompileratruntimetoconverttexttemplatesintocompiledtemplates.Thispackageisrequiredifwe'recompilingtemplatesatruntime.

@angular/platform-

browser-dynamic

Thispackageincludesthebootstrappingfunctionalitythatwillhelpusstartourapplication.Thebootstrapinitiatedbytheplatform-browser-dynamicpackageisdynamicinthesenseofcompilingtemplatesatruntime.

typescript

ThisdevelopmentdependencyistheTypeScripttranspilerforSystemJS.IttranspilesourECMAScript6andTypeScriptcodetoECMAScript5,fromwhereitcanruninthebrowser.

text

ThisSystemJSloadersupportstheloadingoftextfilesintheformofJavaScriptstrings.ThisisespeciallyusefulifyouliketoloadHTMLtemplatesandavoidasynchronousrequests.

Ourmainentrypointfordisplayingourapplicationwithinthebrowserisourindexsite.Theindex.htmlfilecompletesthefollowingfiveactions:

LoadingECMAScript6polyfilles6-shimfromaCDN.ThisscriptisrequiredtomakesurethebrowserunderstandsthelatestECMAScript6APIs.LoadingtheAngular2polyfillsrequiredbytheframework.ThisincludesvariouspatchesforthebrowserthatarerequiredtorunanAngular2application.It'simportant

www.EBooksWorld.ir

Page 70: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

toloadthesepolyfillsbeforeweloadanyothercodewithinourapplication.LoadingSystemJSandtheSystemJSconfig.jsfilethatcontainsthemappinginformationgeneratedbyJSPM.UsingtheSystem.importfunctiontoloadandexecutethemainentrypoint,whichisourboostrap.jsfile.

Let'screateanewindex.htmlfilewithintherootfolderofourproject:

<!doctypehtml>

<html>

<headlang="en">

<title>Angular2Components</title>

</head>

<body>

<scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/es6-shim/0.35.0/es6-

shim.min.js"></script>

<scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/angular.js/2.0.0-

beta.15/angular2-polyfills.js"></script>

<scriptsrc="jspm_packages/system.js"></script>

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

<script>

System.import('lib/bootstrap.js');

</script>

</body>

</html>

Let'smoveontoourapplicationcomponent.Youcanthinkofitastheoutermostcomponentofyourapplication.It'sthemaincomponentinthatitrepresentsyourwholeapplication.Everyapplicationneedsoneandjustonemaincomponent.Thisiswhereyourcomponenttreehasitsroots.

We'llnameourmaincomponentAppbecauseitrepresentsourwholeapplication.Let'sgoaheadandcreatethecomponentwithinanewlibfolderinourprojectfolder.Createafile,app.js,withthefollowingcontent:

//WeneedtheComponentannotationaswellasthe

//ViewEncapsulationenumeration

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

//Usingthetextloaderwecanimportourtemplate

importtemplatefrom'./app.html!text';

//Thiscreatesourmainapplicationcomponent

@Component({

//TellsAngulartolookforanelement<ngc-app>tocreatethis

//component

selector:'ngc-app',

//Let'susetheimportedHTMLtemplatestring

template,

//TellAngulartoignoreviewencapsulation

encapsulation:ViewEncapsulation.None

})

www.EBooksWorld.ir

Page 71: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

exportclassApp{}

There'snothingdifferentherefromwhatwealreadyknowaboutstructuringacomponent,somethingthatwelearnedinthepreviouschapter.However,therearetwomaindifferencesherecomparedtohowwecreatedthecomponentsbefore.Ifyoulookathowweconfiguredthetemplateproperty,youcouldtellthatwedidn'twritetheHTMLtemplatedirectlywithintheJavaScriptfileinsidetheECMAScript6templatestrings.Instead,we'regoingtoloadthetemplateintoaJavaScriptstringusingthetextloaderplugininSystemJS.Wecanjustloadanytextfilefromthefilesystembyappending!texttoourregularECMAScript6imports:

importtemplatefrom'./app.html!text';

Thiswillloadthefile,app.html,fromthecurrentdirectoryandmakeadefaultexportwithitscontentasastring.

Theseconddifferenceisthatwe'reusingViewEncapsulationtospecifyhowAngularshouldhandleviewencapsulation.Angularhasthreeways,tohandleviewencapsulation,whichprovidesdifferentlevelsofgranularityandhastheirownprosandcons.Theyareasfollows:

Encapsulationtype Description

ViewEncapsulation.Emulated

Ifacomponentissettoemulatedviewencapsulation,itwillemulatestyleencapsulationbyattachingthegeneratedattributestothecomponentelementandmodifyingCSSselectorstoincludetheseattributeselectors.Thiswillenablecertainformsofencapsulation,althoughtheouterstylescanstillleakintothecomponentifthereareotherglobalstyles.

Thisviewencapsulationmodeisthedefaultmode,ifnotspecifiedotherwise.

ViewEncapsulation.Native

NativeviewencapsulationissupposedtobetheultimategoaloftheviewencapsulationconceptwithinAngular.ItmakesuseofShadowDOM,asdescribedinthepreviouschapter,tocreateanisolatedDOMforthewholecomponent.ThismodedependsonthebrowsertosupportShadowDOMnatively,andtherefore,can'talwaysbeused.It'salsoimportanttonotethatglobalstyleswillnolongerberespectedandlocalstylesneedtobeplacedwithinthecomponentininlinestyletags(orusethestylespropertyonthecomponentannotation).

ThismodetellsAngularnottoprovideanytemplateorstyleencapsulation.Withinourapplication,wemainlyrelyon

www.EBooksWorld.ir

Page 72: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

ViewEncapsulation.None stylescomingfromaglobalCSS;therefore,weusethismodeformostofthecomponents.NeitherShadowDOM,norattributeswillbeusedtocreatestyleencapsulation;wecansimplyusetheclassesspecifiedwithinourglobalCSSfile.

Asthiscomponentisnowrelyingonatemplatetobeloadedfromthefilesystem,weneedtocreatetheapp.htmlfileinthelibfolderwithsomeinitialcontent:

<div>HelloWorld!</div>

Forthetimebeing,that'severythingweputinourtemplate.Ourdirectoryshouldlooksimilartothis:

angular-2-components

├──node_modules/

├──jspm_packages/

├──config.js

├──index.html

├──lib

│├──app.html

│└──app.js

└──package.json

Nowthatwehavecreatedourmainapplicationcomponent,wecanaddthecomponent'shostelementtoourindex.htmlfile:

<!DOCTYPEhtml>

<html>

<headlang="en">

<title>Angular2Components</title>

</head>

<body>

<ngc-app></ngc-app>

...

www.EBooksWorld.ir

Page 73: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

BootstrappingTheindex.htmlfilewillloadthebootstrap.jsmoduleusingSystemJSinaninlinescripttag.It'sabestpracticetohaveamainentrypointforyourscriptswhenworkingwithSystemJS.Ourbootstrap.jsfileisresponsibleforloadingallthenecessaryJavaScriptdependenciesforourapplicationaswellasbootstrappingtheAngularframework.

WecangoaheadandbootstrapourAngularapplicationbyprovidingourmainapplicationcomponent,App.Weneedtoimportthebootstrapfunctionfromtheangular2module.WecanthenimportourAppcomponentandcallthebootstrapfunction,passingitasparameter:

//ImportAngularbootstrapfunction

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

//Importourmainappcomponent

import{App}from'./app';

//WearebootstrappingAngularusingourmainapplication

//component

bootstrap(App);

www.EBooksWorld.ir

Page 74: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

RunningtheapplicationThecodewe'veproducedsofarshouldnowbeinastatewherewecanrunit.Beforewerunourcodeusingthelive-servermodule,let'sensurewehaveallthefilesready.Atthisstage,ourdirectoryshouldlooksomethinglikethis:

angular-2-components

├──jspm_packages/

├──node_modules/

├──config.js

├──index.html

├──lib

│├──app.html

│├──app.js

│└──bootstrap.js

└──package.json

Nowlet'sstartliveservertostartaserverandabrowserwithlivereload.Forthis,weneedtosimplyexecutethefollowingcommandonthecommandlinewithinourprojectfolder:

live-server

Ifeverythinggoeswell,youwillhaveanopenwebbrowserthatshowsHelloWorld!.

www.EBooksWorld.ir

Page 75: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

RecapLet'srecapwhatwehavedonesofar:

1. WeinitializedanewprojectusingNPMandJSPMandinstalledtheAngulardependenciesusingJSPM.

2. Wecreatedourmainapplicationcomponentinapp.js.3. Wealsocreatedabootstrap.jsscripttoincludetheAngularframeworkbootofour

application.4. Weaddedourcomponenttotheindex.htmlfilebyincludinganelementthatmatches

ourcomponentselectorproperty.5. Finally,weusedliveservertostartabasicwebserverandlaunchawebbrowser.

www.EBooksWorld.ir

Page 76: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

CreatingatasklistNowthatwehaveourmainapplicationcomponentsetup,wecangoonandfleshoutourtaskapplication.Thesecondcomponentthatwe'regoingtocreatewillberesponsibleforlistingtasks.Followingtheconceptofcomposition,we'llcreateatask-listcomponentasasubcomponentofourmainapplicationcomponent.

Let'screateafolderwithinthelibfoldercalledtask-listandanewJavaScriptfilecalledtask-list.js,wherewewillwriteourcomponentcode:

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

importtemplatefrom'./task-list.html!text';

@Component({

selector:'ngc-task-list',

//Thehostpropertyallowsustosetsomepropertiesonthe

//HTMLelementwhereourcomponentisinitialized

host:{

class:'task-list'

},

template,

encapsulation:ViewEncapsulation.None

})

exportclassTaskList{

constructor(){

this.tasks=[

{title:'Task1',done:false},

{title:'Task2',done:true}

];

}

}

We'vecreatedaverysimpletask-listcomponentthathasalistoftasksstoredinternally.ThiscomponentwillbeattachedtoHTMLelementsthatmatchtheCSSelementselectorngc-task-list.

Nowlet'screateaviewforthiscomponenttodisplaythetasks.AsyoucanseefromtheimportwithinthecomponentJavaScriptfile,wearelookingforafilecalledtask-list.html:

<div*ngFor="lettaskoftasks"class="task">

<inputtype="checkbox"[checked]="task.done">

<divclass="task__title">{{task.title}}</div>

</div>

WeusetheNgFordirectivetorepeatthe<div>elementwiththeclasstaskforasmanytasksaswehaveinthetasklistofourcomponent.TheNgFordirectiveinAngularwillcreateatemplateelementfromitsunderlyingcontentandinstantiateasmanyelementsfromthetemplateastheexpressionevaluatesto.Wecurrentlyhavetwotasksinourtask-listcomponent,sothiswillcreatetwoinstancesofourtemplate.

www.EBooksWorld.ir

Page 77: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Yourfolderstructureinsidethelibfoldershouldnowlooksimilartothis:

angular-2-components

└──lib

├──app.html

├──app.js

├──bootstrap.js

└──task-list

├──task-list.html

└──task-list.js

Allthat'slefttodoinordertomakeourtasklistworkistheinclusionofthetask-listcomponentwithinthemainapplicationcomponent.Wecangoaheadandmodifyourapp.jsfileandaddthefollowinglineontopofit:

import{TaskList}from'./task-list/task-list';

Aswewanttoaddthetask-listcomponenttoourmainapplicationviewtemplate,wealsoneedtomakesurethatAngularknowsaboutthecomponentwhencompilingtheview.Forthis,weneedtoaddthedirectivespropertytoourmainapplicationcomponentwithintheapp.jsfileandincludeourimportedTaskListcomponentclasswithinthelistofdirectives:

...

//TellAngulartoignoreviewencapsulation

encapsulation:ViewEncapsulation.None,

directives:[TaskList]

})

...

Finally,weneedtoincludethehostelementofourtask-listcomponentinthetemplateofthemainapplication,whichislocatedwithintheapp.htmlfile:

<ngc-task-list></ngc-task-list>

Thesewerethelastchangesweneededtomakeinordertomakeourtask-listcomponentwork.Toviewyourchanges,youcanstarttheliveserverbyexecutingthelive-servercommandwithinyourangular-2-componentsdirectory.

www.EBooksWorld.ir

Page 78: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

RecapLet'slookatwhatwehavedoneinthepreviousbuildingblock.Weachievedasimplelistingoftaskswithinanencapsulatedcomponentbyfollowingthesesteps:

1. WecreatedthecomponentJavaScriptfilethatcontainsthelogicofourcomponent.2. Wecreatedthecomponent'sviewwithinaseparateHTMLfile.3. Weincludedthecomponentclasswithintheconfigurationofourmainapplication

component.4. WeincludedthecomponentHTMLelementwithinourmainapplicationviewtemplate.

www.EBooksWorld.ir

Page 79: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

TherightlevelofencapsulationOurtasklistisdisplayedcorrectlyandthecodeweusedtoachievethislooksquiteokay.However,ifwewanttofollowabetterapproachforcomposition,weshouldrethinkthedesignofourtask-listcomponent.Ifwedrawalineatenlistingthetasklistresponsibilities,wewouldcomeupwiththingssuchaslistingtasks,addingnewtaskstothelist,andsortingandfilteringthetasklist;however,operationsarenotperformedonanindividualtaskitself.Also,renderingthetaskitselffallsoutsideoftheresponsibilitiesofthetasklist.Thetask-listcomponentshouldonlyserveasacontainerfortasks.

Ifwelookatourcodeagain,wewillseethatwe'reviolatingthesingleresponsibilityprincipleandrenderingthewholetaskbodywithinourtask-listcomponent.Let'stakealookathowwecanfixthisbyincreasingthegranularityoftheencapsulation.

Thegoalnowistodoacoderefactoringexercise,alsoknownasextraction.Wearepullingourtask'srelevanttemplateoutofthetasklisttemplateandcreatinganewcomponentthatencapsulatesthetasks.

Forthis,weneedtocreateanewsubfolderwithinthetask-listfoldercalledtask.Withinthisfolder,wewillcreateatemplatefilewiththenametask.html:

<inputtype="checkbox"[checked]="task.done">

<divclass="task__title">{{task.title}}</div>

Thecontentofournewtask.htmlfileisprettymuchthesameaswhatwealreadyhavewithinourtask-list.htmltemplate.Theonlydifferenceisthatwewillnowrefertoanewmodelcalledtask.

Now,withinthetaskfolder,let'screatetheJavaScriptfile,task.js,whichwillcontainthecontrollerclassofourcomponent:

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

importtemplatefrom'./task.html!text';

@Component({

selector:'ngc-task',

host:{

class:'task'

},

template,

encapsulation:ViewEncapsulation.None

})

exportclassTask{

//Ourtaskmodelcanbeattachedonthehostwithintheview

@Input()task;

}

Inthepreviouschapterofthisbook,wespokeaboutencapsulationandthepreconditionsto

www.EBooksWorld.ir

Page 80: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

establishacleanencapsulationforUIcomponents.Oneofthesepreconditionsisthepossibilitytodesignproperinterfacesinandoutofthecomponent.Suchinputandoutputmethodsarenecessarytomakethecomponentworkwithincompositions.That'showacomponentwillreceiveandpublishinformation.

Asyoucanseefromourtaskcomponentimplementation,wearenowbuildingsuchaninterfaceusingthe@Inputannotationonaclassinstancefield.Inordertousethisannotation,wewillfirstneedtoimportitfromtheangularcoremodule.

InputpropertiesinAngularallowustobindtheexpressionsinourtemplatestoclassinstancefieldsonourcomponents.Thisway,wecanpassdatafromtheoutsideofthecomponenttothecomponentinside,usingthecomponentstemplate.Thiscanbethoughtofasanexampleofone-waybinding,fromtheviewtothecomponent.

Ifwe'reusingpropertybindingonaregularDOMproperty,Angularwillcreateabindingoftheexpressiondirectlytotheelement'sDOMproperty.We'reusingsuchatypeofbindingtobindthetaskcompletedflagtothecheckedpropertyofthecheckbox'sinputelement:

Usage Description

@Input()

inputProp;

ThisallowsustobindtheinputPropattributetothecomponentelementwithintheparentcomponent.

Angularassumesthattheattributeoftheelementhasthesamenameasthatoftheinputproperty.

@Input('inp')

inputProp;

Youcanalsooverridethenameoftheattributethatshouldbemappedtothisinput.Here,theinpattributeofthecomponent'sHTMLelementismappedtothecomponent'sinputproperty,inputProp.

Thelastmissingpiecetouseournewlycreatedtaskcomponentisthemodificationoftheexistingtemplateofthetasklist.

Weincludethetaskcomponentwithinourtasklisttemplatebyusingan<ngc-task>element,asspecifiedintheselectorwithinourtaskcomponent.Also,wecreateapropertybindingonthetaskelement.There,wepassthetaskobjectfromthecurrentNgForiterationtothetaskinputofthetaskcomponent.Weneedtoreplacealltheexistingcontentinthetask.htmlfilewiththefollowinglinesofcode:

<ngc-task*ngFor="lettaskoftasks"

[task]="task"></ngc-task>

Inordertomakeourtask-listcomponentrecognizethetaskcomponentelement,weneedto

www.EBooksWorld.ir

Page 81: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

addittothetask-listcomponent'sdirectivespropertywithinthetask-list.jsfile:

...

import{Task}from'./task/task';

@Component({

...

directives:[Task]

})

...

Congratulations!You'vesuccessfullyrefactoredyourtasklistbyextractingthetaskintoitsowncomponentandhaveestablishedacleanencapsulation.Also,wecannowsaythatourtasklistisacompositionoftasks.

Ifyouthinkaboutmaintainabilityandreusability,thiswasactuallyaveryimportantstepintheprocessofbuildingourapplication.Youshouldconstantlylookoutforsuchencapsulationopportunities,andifyoufeelsomethingcouldbearrangedintomultiplesubcomponents,youshouldprobablygoforit.Ofcourse,youcanalsooverdothis.There'ssimplynogoldenruletodeterminewhatgranularityofencapsulationistherightone.

Tip

Therightgranularityofencapsulationforacomponentarchitecturealwaysdependsonthecontext.MypersonaltiphereistouseknownprinciplesfromOOP,suchassingleresponsibility,tolaythegroundworkforagooddesignofyourcomponenttree.Alwaysmakesureyourcomponentsareonlydoingthingsthattheyaresupposedtodoastheirnamessuggest.Atasklisthastheresponsibilityoflistingtasksandprovidingsomefiltersorothercontrolsforthelist.Theresponsibilityofoperatingonindividualtaskdataandrenderingthenecessaryviewclearlybelongstoataskcomponentandnotthetasklist.

www.EBooksWorld.ir

Page 82: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

RecapInthisbuildingblock,wecleanedupourcomponenttreeandestablishedcleanencapsulationusingsubcomponents.Then,wesetuptheinterfacesprovidedbyAngularusinginputbindings.Weperformedtheseactionsbyfollowingtheensuingsteps:

1. Wecreatedatasksubcomponent.2. Weusedthetasksubcomponentwiththetask-listcomponent.3. WeusedinputbindingsandDOMelementpropertybindingstoestablishone-waydata

bindinginthetaskcomponent.

www.EBooksWorld.ir

Page 83: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

InputgeneratesoutputOurtasklistlooksnicealready,butitwouldbequiteuselessiftheuserisunabletoaddnewtaskstothelist.Let'screateacomponentforenteringnewtaskstogether.Asthiscomponentbelongstothetask-listcomponent,we'regoingtocreateanewfoldercalledenter-taskwithinthetask-listfolder.TheresponsibilitiesofthiscomponentwillbetohandlealltheUIlogicnecessaryforenteringanewtask.

Usingthesamenamingconventionaswiththerestofourcomponents,let'screateafilecalledenter-task.htmltostorethetemplateofourcomponent:

<inputtype="text"class="enter-task__title-input"

placeholder="Enternewtasktitle..."

#titleInput>

<buttonclass="button"(click)="enterTask(titleInput)">

AddTask

</button>

Thistemplateconsistsofaninputfieldaswellasabuttontoenteranewtask.Here,we'remakinguseoftheso-calledlocalviewvariablesbyspecifyingthatourinputfieldshouldhavethereferencename#titleInput.WecanreferencethisvariablewithinthecurrentcomponentviewbythenametitleInput.

Inthiscase,weareactuallyusingthevariabletopasstheinputfieldDOMelementtotheenterTaskfunctionthatwecallonaclickeventontheAddTaskbutton.

Let'stakealookattheimplementationofourComponentclassforenteringanewtaskbyusingthefollowingcodeinanewly-createdenter-task.jsfile:

import{Component,Output,ViewEncapsulation,EventEmitter}from

'@angular/core';

importtemplatefrom'./enter-task.html!text';

@Component({

selector:'ngc-enter-task',

host:{class:'enter-task'},

template,

encapsulation:ViewEncapsulation.None

})

exportclassEnterTask{

//Eventemitterthatgetsfiredonceataskisentered.

@Output()taskEntered=newEventEmitter();

//ThisfunctionwillfirethetaskEnteredeventemitter

//andresetthetasktitleinputfield.

enterTask(titleInput){

this.taskEntered.next(titleInput.value);

titleInput.value='';

titleInput.focus();

}

}

www.EBooksWorld.ir

Page 84: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Forthiscomponent,we'vechosenadesignapproachwhereweusealooserelationtoourtasklistwheretheactualtaskwillbecreated.Althoughthiscomponentiscloselyrelatedtothetasklist,it'sbettertokeepthecomponentsaslooselycoupledaspossible.

Oneofthesimplestformsofinversionofcontrol,acallbackfunctionoreventlistenerisagreatprincipletoestablishloosecoupling.Inthiscomponent,weareusingthe@Outputannotationtocreateaneventemitter.Theoutputpropertiesneedtobeinstancefieldsthatholdaneventemitterwithinthecomponent.Onthecomponent'sHTMLelement,wecanthenuseeventbindingstocaptureanyeventsemitted.Thisgivesusgreatflexibilitythatwecanusetocreateacleanapplicationdesign,wherewegluecomponentstogetherthroughthebindingwithintheview:

Usage Description

@Output()

outputProp=

new

EventEmitter();

WhenoutputProp.next()iscalled,acustomeventwiththenameoutputPropwillbeemittedonthecomponent.Angularwilllookforeventbindingsonthecomponent'sHTMLelement(wherethecomponentisused)andexecutethem:

<my-comp(output-prop)="doSomething()">

Withintheexpressionsineventbindings,youwillalwayshaveaccesstoasyntheticvariablecalled$event.ThisvariableisareferencetothedataemittedbyEventEmitter.

@Output('out')

outputProp=

new

EventEmitter();

Usethiswayofdeclaringyouroutputpropertiesifyou'dwanttonameyoureventsdifferentlyfromwhatyourpropertynameis.Inthisexample,acustomeventwiththenameoutwillbefiredwhenoutputProp.next()iscalled:

<my-comp(out)="doSomething()">

Okay,let'susethisnewlycreatedcomponenttoaddnewtaskswithinourtask-listcomponent.First,let'smodifytheexistingtemplateofthetask-listcomponent.Openthefile,task-list.html,inthetask-listcomponentfolder.WeneedtoaddtheEnterTaskcomponenttothetemplateandalsohandlethecustomeventthatwe'regoingtoemit,onceanewtaskisenteredwithinthecomponent:

<ngc-enter-task(taskEntered)="addTask($event)">

</ngc-enter-task>

<ngc-task*ngFor="lettaskoftasks"

[task]="task"></ngc-task>

Sincetheoutputpropertywithintheenter-taskcomponentiscalledtaskEntered,wecan

www.EBooksWorld.ir

Page 85: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

binditwiththeeventbindingattribute,(taskEntered)="",onthehostelement.

Withintheeventbindingexpression,wethencallafunctiononourtask-listcomponentcalledaddTask.Also,weusethesyntheticvariable$event,whichcontainsthetasktitleemittedfromtheenter-taskcomponent.Now,wheneverwepushthebuttoninourenter-taskcomponentandaneventgetsemittedfromthecomponent,wecatchtheeventinoureventbindingandhandleitwithinthetask-listcomponent.

Wealsoneedtomakesomeminorchangestothetask-listcomponent'sJavaScriptfile.Let'sopentask-list.jsandmodifyitwiththefollowingchanges:

...

//Thecomponentforenteringnewtasks

import{EnterTask}from'./enter-task/enter-task';

@Component({

...

directives:[Task,EnterTask]

})

exportclassTaskList{

...

//Functiontoaddataskfromtheview

addTask(title){

this.tasks.push({

title,done:false

});

}

}

Theonlythingwechangedwithinthetask-listcomponentmoduleisitsabilitytodeclaretheEnterTaskcomponentinthedirectivespropertysothatthecompilerrecognizesourenter-taskcomponentcorrectly.

Wehavealsoaddedafunction,addTask,whichwilladdanewtasktoourtasklistwithatitlethatispassedtothefunction.Nowthecircleisclosedandoureventfromtheenter-taskcomponentisroutedtothisfunctionwithintheviewofthetask-listcomponent.

Youcannowstartliveserverfromyourprojectdirectoryinordertotestthenewlyaddedfunctionalityusingthelive-servercommand.

www.EBooksWorld.ir

Page 86: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

RecapWehaveaddedanewsubcomponentofthetasklistthatisresponsibleforprovidingtheUIlogictoaddnewtasks.Inotherwords,wehavecoveredthefollowingtopics:

1. Wecreatedasubcomponentthatislooselycoupledusingoutputpropertiesandeventemitters.

2. Welearnedaboutthe@Outputannotationandhowtouseittocreateoutputproperties.3. Weusedeventbindingstolinkthebehaviortogether,fromtheviewofacomponent.

www.EBooksWorld.ir

Page 87: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

CustomUIelementsThestandardUIelementsinthebrowseraregreat,butsometimes,modernwebapplicationsrequiremorecomplexandintelligentinputelementsthantheonesavailablewithinthebrowser.

We'llnowcreatetwospecificcustomUIelementsthatwe'llusewithinourapplicationgoingforwardinordertoprovideaniceuserexperience:

Checkbox:There'salreadyanativecheckboxinputinthebrowser,butsometimes,it'shardtofititintothevisualdesignofanapplication.Nativecheckboxesarelimitedintheirstylingpossibilities,andtherefore,it'shardtomakethemlookgreat.Sometimes,it'sthoseminordetailsthatmakeanapplicationlookappealing.Togglebuttons:Thisisalistoftogglebuttons,whereonlyonebuttoncanbetoggledwithinthelist.Theycouldalsoberepresentedwithanativeradiobuttonlist.However,likewithnativecheckboxes,radiobuttonsaresometimesnotreallythenicestvisualsolutiontotheproblem.Alistoftogglebuttonsthatalsorepresentsaselect-one-userinputelementismuchmoremodernandprovidesthevisualaspectthatwearelookingfor.Besides,whodoesnotliketopushbuttons?

Let'screateourcustomcheckboxUIelementfirst.Aswe'llprobablycomeupwithafewcustomUIelements,firstlet'screateanewsubfoldercalleduiwithinthelibfolder.

Withintheuifolder,wenowcreateafolderwiththenamecheckboxforourcheckboxcomponent.Startingwiththetemplateofournewcomponent,wenowcreateafilewiththenamecheckbox.htmlwithinthecheckboxfolder:

<inputtype="checkbox"

[checked]="checked"

(change)="onChecke

dChange($event.target.checked)">

{{label}}

Onthecheckboxinput,wehavetwobindings.First,wehaveapropertybindingforthecheckedpropertyontheDOMelement.WearebindingtheDOMpropertytothecheckedmemberfieldonourcomponent,whichwearegoingtocreateinamoment.

Also,wehaveaneventbindingontheinputelementwherewelistenforthecheckboxchangeDOMeventandcallthemethodonCheckedChangeonourcomponentclass.Weusethesyntheticvariable$eventtopassthecheckedpropertyonthecheckboxDOMelementwherethechangeeventisoriginated.

Movingontoourcomponentclassimplementation,weneedtocreateafilewiththenamecheckbox.jswithinthecheckboxfolder:

import{Component,Input,Output,ViewEncapsulation,EventEmitter}from

'@angular/core';

www.EBooksWorld.ir

Page 88: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

importtemplatefrom'./checkbox.html!text';

@Component({

selector:'ngc-checkbox',

host:{class:'checkbox'},

template,

encapsulation:ViewEncapsulation.None

})

exportclassCheckbox{

//Anoptionallabelcanbesetforthecheckbox

@Input()label;

//Ifthecheckboxischeckedorunchecked

@Input()checked;

//Eventemitterwhencheckedischangedusingtheconvention

//fortwowaybindingwith[(checked)]syntax.

@Output()checkedChange=newEventEmitter();

//Thisfunctionwilltriggerthecheckedeventemitter

onCheckedChange(checked){

this.checkedChange.next(checked);

}

}

There'snothingspecialaboutthiscomponentclassifwefirstlookatit.Itusesaninputpropertytosetthecheckedstatefromtheoutside,anditalsohasanoutputpropertywithaneventemitterthatallowsustonotifytheoutercomponentaboutthechangesofthecheckedstateusingacustomevent.However,there'sanamingconventionthatmakesthiscomponentabitspecial.Theconventionofusinganinputpropertynamealsoasanoutputpropertynamebutappendingthewordchangeisactuallyenablingadeveloperwhousesthecomponenttomakeuseofthetwo-waydatabindingtemplateshorthand.

Angulardoesnotcomewithtwo-waydatabindingoutofthebox.However,creatingtwo-waybindingisquiteeasy.Actually,two-waydatabindingisnodifferentthancombiningapropertybindingwithaneventbinding.

Thefollowingexamplecreatesaverysimpletwo-waydatabindingprocessonaninputfield:

<inputtype="text"(input)="value=$event.target.value"

[value]="value">

ThesimplicityofAngularandthegeneralapproachofextendingthenativefunctionalityofthebrowsermakesimplementingthismechanismabreeze.

Implementingtwo-waydatabindingbetweenacomponentanditssubcomponentisn'treallytoodifficult.Theonlythingweneedtotakecareaboutisthatthereareinputandoutputpropertiesofthesubcomponentinvolved.

Pleasehavealookatthefollowingscreenshot:

www.EBooksWorld.ir

Page 89: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Atwo-waydatabindingbetweenmembervariablesofacomponentandasubcomponent

Sincetwo-waydatabindingwasahighlyrequestedfeatureinAngular,there'sahandyshorthandtowriteit.Let'slookatsomeexamplesonhowtoimplementdatabindingsbetweenatemplateofacomponentanditssubcomponent:

Subcomponentproperties Bindingsincomponenttemplate

@Input()text;

@Output()

textOut=new

EventEmitter();

<sc[text]="myText"

(textOut)="myText=$event">

Webindthecomponent'smyTextpropertytothesubcomponent'stextinput.Also,wecapturethetextOuteventemittedfromthesubcomponentandupdateourmyTextproperty.

@Input()text;

@Output()

textChange=

new

<sc[(text)]="myText">

Wecansimplifythistwo-waydatabindingbyusingthenamingconventiontoappendtheword"change"tooureventemitteridentifier.Thisway,wecanusethetwo-waydatabindingshorthandwithinour

www.EBooksWorld.ir

Page 90: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

EventEmitter(); templateusingthe[(property)]notation.

Ifwelookatourcheckboxcomponentimplementationagain,wewillseethatweareusingthetwo-waydatabindingnamingconventionforthecheckedpropertyofourcomponent.Thisway,weenabletheuseofthetemplateshorthandfortwo-waydatabindingwhereverweuseourcustomcheckboxUIcomponent.

Let'sintegrateourcheckboxinthetaskcomponenttoreplacethenativecheckboxinputwe'recurrentlyusingthere.Forthis,weneedtomodifythetask.htmlfilewithinthetask-list/taskfolder,byreplacingthenativeinputcheckboxthatwehaveinthetask.htmlfilewiththefollowinglineofcode:

<ngc-checkbox[(checked)]="task.done"></ngc-checkbox>

Asalways,wealsoneedtotellthetaskcomponentthatwe'dliketousethecomponentwithinthetemplate.Let'schangethecodewithinthetask.jsfileaccordingly:

...

import{...,HostBinding}from'@angular/core';

//Eachtaskhasacheckboxcomponentformarkingtasksasdone.

import{Checkbox}from'../../ui/checkbox/checkbox';

@Component({

...

//WeneedtospecifythatthiscomponentreliesontheCheckbox

//componentwithintheview.

directives:[Checkbox]

})

exportclassTask{

//Ourtaskmodelcanbeattachedonthehostwithintheview

@Input()task;

@HostBinding('class.task--done')

getdone(){

returnthis.task&&this.task.done;

}

}

We'vealreadylearnedaboutthehostpropertyoncomponents.Itallowsustosetpropertyandeventbindingsonourcomponenthostelement.ThehostelementistheDOMelementwhereourcomponentisinitializedwithintheparentcomponent.

There'sanotherwaythroughwhichwecansetpropertiesonourcomponenthostelement,whichbecomeshandywhenwewanttosetapropertybasedonsomedatawithinourcomponent.

Usingthe@HostBindingannotation,wecancreatepropertybindingsonthecomponenthostelementbasedonthememberswithinourcomponent.Let'susethisannotationinordertocreateabindingthatwillconditionallysetthetask--doneclassonthecomponent'sHTML

www.EBooksWorld.ir

Page 91: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

element.Thisisusedtomakesomevisualdistinctionsoffinishedtaskswithinourstyles.

ThiswasjustthelaststeptointegrateourcustomcheckboxUIcomponentwithinthetaskcomponent.Youcannowstartlive-serverinordertoviewyourchangesandplayaroundwiththeselargenewcheckboxesinthetasklist.Isn'tthatmuchmorefuntodothanactivatingregularcheckboxes?Don'tunderestimatetheeffectofauserinterfacethatispleasingtouse.Thiscanhaveaverypositiveimpactontheusageofyourproduct.

Ourtasklistafteraddingourcustomcheckboxcomponent

Nowthatwe'vecreatedourcheckboxcomponent,let'sgoaheadandcreateanotherUIcomponentfortogglebuttonsthatwe'lluseinthenexttopic.Weneedtocreateafoldernamedtogglewithintheuifolderandcreateatemplatecalledtoggle.htmlwithinthetogglefolder:

<buttonclass="buttonbutton--toggle"

*ngFor="letbuttonofbuttonList"

[class.button--active]="button===selectedButton"

(click)="onButtonActivate(button)">{{button}}</button>

Nothingspecialhere,really!Werepeatabuttonbyiteratingoveraninstancefieldcalled

www.EBooksWorld.ir

Page 92: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

buttonListusingtheNgFordirective.Thisbuttonlistwillcontainthelabelsofourtogglebuttons.Conditionally,wesetaclasscalledbutton--activeusingapropertybindingandcheckingitagainstourcurrentbuttonwithintheiterationagainstaninstancefieldcalledselectedButton.Whenthebuttonisclicked,wecallamethod,onButtonActivate,onourcomponentclassandpassthecurrentbuttonlabelfromtheiteration.

Let'screatetoggle.jsinsidethetogglefolderandimplementthecomponentclass:

import{Component,Input,Output,ViewEncapsulation,EventEmitter}from

'@angular/core';

importtemplatefrom'./toggle.html!text';

@Component({

selector:'ngc-toggle',

host:{

class:'toggle'

},

template,

encapsulation:ViewEncapsulation.None

})

exportclassToggle{

//Alistofobjectsthatwillbeusedasbuttonvalues.

@Input()buttonList;

//Inputandstateofwhichbuttonisselectedneedstoreferto

//anobjectwithinbuttonList

@Input()selectedButton;

//EventemitterwhenselectedButtonischangedusingthe

//conventionfortwowaybindingwith[(selected-button)]

//syntax.

@Output()selectedButtonChange=newEventEmitter();

//Callbackwithinthecomponentlifecyclethatwillbecalled

//aftertheconstructorandinputshavebeenset.

ngOnInit(){

if(this.selectedButton===undefined){

this.selectedButton=this.buttonList[0];

}

}

//Methodtosetselectedbuttonandtriggereventemitter.

onButtonActivate(button){

this.selectedButton=button;

this.selectedButtonChange.next(button);

}

}

Withinourtogglecomponent,werelyonthebuttonListmembertobeanarrayofobjects,asweareusingthisarraywithinourtemplateonanNgFordirective.ThebuttonListmemberisannotatedtobeaninputproperty;thisway,wecanpassthearrayintothecomponent.

FortheselectedButtonmember,whichholdstheobjectofthebuttonListarraythatiscurrentlyselected,weuseatwo-waydatabindingapproach.Thisway,wecannotonlysetthetoggledbuttonfromtheoutsideofthecomponent,butalsogetnotifiedviathetoggle

www.EBooksWorld.ir

Page 93: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

component,whenabuttonistoggledintheUI.

WithintheonButtonActivatefunction,wearesettingtheselectedButtonmemberaswellastriggeringtheeventemitter.

ThengOnInitmethodisactuallycalledbyAngularwithinthelifecycleofdirectivesandcomponents.InthecasewheretheselectedButtoninputpropertywasnotspecified,we'lladdacheckandselectthefirstbuttonfromtheavailablebuttonlist.SinceselectedButtonaswellasbuttonListareinstancefieldsthatarealsoinputpropertiesatthesametime,weneedtowaitforthemtobeinitializedinordertoexecutethislogic.It'simportantnottoperformthisinitializationwithinthecomponentconstructor.Thelifecyclehook,OnInit,willbecalledafterthedirectiveinputandoutputpropertieshavebeencheckedforthefirsttime.Itisinvokedonlyoncewhenthedirectiveisconstructed.

Tip

Angularwillcallanylifecyclehooksthathavebeenimplementedonyourcomponentautomatically.

ThenextdiagramillustratesthelifecycleofanAngularcomponent.Uponcomponentconstruction,allthelifecyclehookswillbecalledaspertheordershowninthediagram,excepttheOnDestroyhook,whichwillbecalleduponcomponentdestruction.

Changedetectionwillalsostartasubsetoflifecyclehooks,wheretherewillbeatleasttwocyclesinthefollowingorder:

doCheck

afterContentChecked

afterViewChecked

onChanges(ifanychangesaredetected)

AdetaileddescriptionofthelifecyclehooksandtheirpurposeisavailableontheAngulardocumentationwebsiteathttps://angular.io/docs/ts/latest/guide/lifecycle-hooks.html.

www.EBooksWorld.ir

Page 94: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

AnillustrationofthelifecycleofanAngularcomponent

www.EBooksWorld.ir

Page 95: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

www.EBooksWorld.ir

Page 96: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

RecapInthisblock,youlearnedhowtobuildcustomUIcomponentsthataregenericandlooselycoupledsothattheycanbeusedinothercomponentsassubcomponents.Wealsocompletedthefollowingtasks:

1. Wecreatedasubcomponentthatislooselycoupledusingoutputpropertiesandeventemitters.

2. Welearnedwhatthe@Outputannotationisandhowtouseittocreateoutputproperties.3. Weusedthe@HostBindingannotationtocreatepropertybindingsdeclarativelyfrom

withinourcomponentclass.4. Weusedeventbindingstolinkthebehaviortogetherfromtheviewofacomponent.5. Webuilttwo-waydatabindingusingabindingshorthand.6. WelearnedabouttheworkingoftheAngularcomponentlifecycleandhowwecanuse

theOnInitlifecyclehooktoinitializethecomponentaftertheinputandoutputhavebeenprocessedforthefirsttime.

www.EBooksWorld.ir

Page 97: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

FilteringtasksThisisthelastbuildingblockofthischapter.Wehavealreadylearnedalotaboutbuildingbasiccomponentsandhowtocomposethemtogetherinordertoformlargercomponents.Inthepreviousbuildingblock,wecreatedgenericUIcomponentsthatcouldbeusedinothercomponents.Inthistopic,wewillusethetogglebuttoncomponentnotonlytocreateafilterforourtasklist,butalsotoimprovethewaywereceiveandstoretasksbyusingdataservices.

Let'scontinuewithanotherrefactoringexercise.Sofar,wehavestoredourtasklistdatadirectlywithinthetask-listcomponent,butlet'schangethathereanduseaservicethatwillprovidetasksforus.

Ourservicewillstillnotuseadatabase,butwe'llgetthetaskdataoutofourcomponent.Inordertousetheservice,we'remakinguseofAngular'sdependencyinjectionforthefirsttime.

Let'screateanewfilecalledtask-list-service.jswithinthelib/task-listfolderofourapplication:

//Classeswhichwe'dliketoprovidefordependencyinjection

//needtobeannotatedusingthisdecorator

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

@Injectable()

exportclassTaskListService{

constructor(){

this.tasks=[

{title:'Task1',done:false},

{title:'Task2',done:false},

{title:'Task3',done:true},

{title:'Task4',done:false}

];

}

}

We'vemovedallourtaskdataintothenewlycreatedservice.Inordertomakeourserviceclassinjectable,weneedtodecorateitwiththe@Injectableannotation.

Let'sapplysomechangestoourtask-listcomponentandmodifythetask-list.jsfilewithinthetask-listfolder.Themodifiedcodeinthefileishighlightedinthefollowingcodeexcerpt:

import{...,Inject}from'@angular/core';

//Thedummytaskservicewherewegetourtasksfrom

import{TaskListService}from'./task-list-service';

...

//WealsoneedaToggleUIcomponenttoprovideafilter

import{Toggle}from'../ui/toggle/toggle';

www.EBooksWorld.ir

Page 98: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

@Component({

...

//SettheTaskListServiceashostprovider

providers:[TaskListService],

//Specifyalldirectives/componentsthatareusedintheview

directives:[Task,EnterTask,Toggle]

})

exportclassTaskList{

//InjecttheTaskListServiceandsetourfilterdata

constructor(@Inject(TaskListService)taskListService){

this.taskListService=taskListService;

this.taskFilterList=['all','open','done'];

this.selectedTaskFilter='all';

}

//Methodthatreturnsafilteredlistoftasksbasedonthe

//selectedtaskfilterstring.

getFilteredTasks(){

returnthis.taskListService.tasks?

this.taskListService.tasks.filter((task)=>{

if(this.selectedTaskFilter==='all'){

returntrue;

}elseif(this.selectedTaskFilter==='open'){

return!task.done;

}else{

returntask.done;

}

}):[];

}

//Methodtoaddataskfromtheview

addTask(title){

this.taskListService.tasks.push({

title,

done:false

});

}

}

Intheimportsectionofourmodule,we'regoingtoimportthetasklistservice.WewillusedependencyinjectiontoreceiveaninstanceoftheTaskListServiceclasswithinourcomponentconstructor.Forthis,we'lluseanewannotation,whichletsusspecifythetypewe'dliketoinject.TheInjectdecoratorneedstobeimportedfromtheAngularcoremoduleinordertousethe@Injectannotation.Ifyoutakealookatourconstructor,you'llfindthatwe'reusingthe@Injectannotationtheretospecifywhatinstancetypewe'dliketoinject.

Inadditiontothe@Injectannotationontheconstructor,weneedonelastthingtomaketheinjectionwork.WeneedtoregisterTaskListServiceasaproviderwithintheproviderspropertyofour@Componentannotation.

NowwegettheTaskListServiceinjectedwhenthedirectiveisconstructed,andwecanstoreareferencetoitinsideaninstancefield.

Withintheconstructorofthecomponent,wealsowanttostorealistofstatesthetaskstatus

www.EBooksWorld.ir

Page 99: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

filtercanhave.Thislistwillalsoserveasinputforourtogglebuttonlist.Ifyourecalltheinputpropertiesonourtogglebutton,wehaveabuttonListinputthatacceptsalistofbuttonlabels.Tostorethecurrentlyselectedfiltertype,weuseaninstancefieldcalledselectedTaskFilter.

Thelastpiecethatweneedtoaddtoourtask-listcomponentisthemethod,getFilteredTasks.Wenolongerneedtostorethetasklistdirectlywithinaninstancefield,andtasksshouldonlybereceivedwithinthecomponentusingthismethod.ThelogicinsidethemethodcheckstheselectedTaskFilterpropertyandreturnsafilteredlistthatmeetsthiscondition.

Sincewewanttousethetogglebuttoncomponentthatwe'vecreatedwithintheprevioustopictocreateafilterbuttonlist,wewillneedtoimportthetogglecomponentwithintheimportsectionandalsoaddtheToggleclasstoourdirectivesproperty.Nowwecanusethetogglecomponentwithinthetemplateofourtask-listcomponent.

Okay,that'sallwearegoingtochangeinourcomponentimplementation.Wewanttochangeourviewtemplatethoughtousethefilteredtasklistcomingfromthedataserviceandshowatogglebuttonlisttoactivatethedifferentfiltertypes.Let'sopenthetemplatefile,task-list.html,insidethetask-listfolderandmodifyitwiththefollowingcontent:

<ngc-toggle[buttonList]="taskFilterList"

[(selectedButton)]="selectedTaskFilter">

</ngc-toggle>

<ngc-enter-task(taskEntered)="addTask($event)">

</ngc-enter-task>

<ngc-task*ngFor="lettaskofgetFilteredTasks()"

[task]="task"></ngc-task>

Sincewe'veaddedthetogglecomponentwithinthedirectivespropertyofourtask-listcomponent,wecanuseitnowwithinourviewtemplate.WebindtheinputpropertybuttonListtotaskFilterListthatwestorewithinourtask-listcomponent.Also,we'reusingtwo-waydatabindingtobindtheselectedButtoninputpropertyofthetogglebuttonlisttotheselectedTaskFilterinstancefieldofthetasklist.Thisway,wecannotonlyupdatetheselectedtaskfilerfromourtask-listcomponentprogrammatically,butalsoallowausertochangethevalueusingthetogglebuttonlist.

NowweonlyneedtomakeasmallchangetotheNgFordirectivethatrepeatsourtaskelementswithinthetasklist.Sinceweneedtoaccessthetasksofthetask-listcomponentwiththegetFilteredTasksmethodnow,wealsoneedtousethatmethodwithinourrepeaterexpression.

That'sitalready,congratulations!You'vesuccessfullyaddedafilteringmechanismtoyourtasklistbyreusingthetogglecomponentthatwecreatedintheprevioustopic.Youcannowstartyourliveserver(usingthelive-servercommand)andshouldseeafullyfunctionaltasklistwhereyoucanenternewtasksandalsofilterthetasklist:

www.EBooksWorld.ir

Page 100: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Screenshotofthetasklistwiththenewlyaddedtogglebuttoncomponentforfilteringthetaskstate

www.EBooksWorld.ir

Page 101: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

SummaryInthischapter,youlearnedalotofnewconceptsonbuildingUI-component-basedapplicationswithAngular.Also,webuiltthecorecomponentofourtaskmanagementapplication,whichisthetasklistitself.Youlearnedabouttheconceptofinputandoutputpropertiesandhowtousethemtobuildtwo-waydatabinding.

WealsocoveredthebasicsoftheAngularcomponentlifecycleandhowtouselifecyclehookstoexecutepostinitializationsteps.

Asthelaststep,weintegratedatogglebuttonlistcomponentwithinourtasklisttofilterthetaskstates.Werefactoredourtask-listcomponenttouseaserviceinordertoobtaintaskdata.Forthis,weusedAngular'sdependencyinjection.

www.EBooksWorld.ir

Page 102: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Chapter3.ComposingwithComponentsInthischapter,wewillgoonestepfurtherinstructuringourapplicationandworkingonthelayoutandarchitecturethatservesasthebaseforourtaskmanagementsystem.Besidesintroducingnewcomponentsandcreatinglargercompositionswiththeexistingcomponents,we'llalsolookatthewaythatwedealwithdata.Sofar,we'veobtainedtaskdatasynchronouslyfromtheTaskListServicethatwecreatedinthepreviouschapter.However,inreal-worldscenarios,thiswouldrarelybethecase.Inarealapplication,dataismostlyretrievedinanasynchronousform.Usually,weacquiredatathroughaRESTfulwebservice,andweuseXMLHttpRequestortherecentlystandardizedfetchAPI.However,aswe'retryingtobuildacutting-edgeapplication,wewillgoonestepfurther.Inthischapter,we'lllookathowwecanrestructureourapplicationtodealwithobservabledatastructuresusingRxJS—afunctionalandreactiveprogramminglibrarythatisusedinAngular.

Inthischapter,wewilllookatthefollowingtopics:

RestructuringourapplicationtodealwithobservabledatastructuresThebasicsofRxJSanditsoperatorsinordertobuildareactivedatamodelUsingpurecomponentsinAngularUsingChangeDetectionStrategy.OnPushforpurecomponentsUsingcontentprojectionpointsand@ContentChildrentocreateaTabcomponentCreatingasimplenavigationcomponentInjectingparentcomponentsandestablishingdirectcomponentcommunicationCombininginternalandexternalcontenttocreateaflexiblecomponentAPI

www.EBooksWorld.ir

Page 103: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Data–FaketorealStartingwiththischapter,weareswitchingtoadocument-baseddatabasetostoreourtasksandprojectdata.Asadatastore,weusethePouchDBproject,whichisanin-browserdatabasethatisdesignedtorunwithIndexedDBandvariousfallbackstrategies.PouchDBisdesignedsimilarlytoApacheCouchDB,anditcanevenbesynchronizedwithit.

Inordertoprovideaqualityexperienceforyouwhileyoubuildyourapplication,it'simportantthatweworkinreal-lifeconditions.ThismeansthatweshoulduseasynchronousdatainourcomponentsandnotrelyonasimpleJavaScriptarrayofdata.Inordertomakethisassmoothaspossible,thewholedatalayerisalreadysetupforyou,andyoudon'tneedtoworryabouttheinternalstoomuch.Ofcourseifyou'restillinterested,I'mnotholdingyoubackfromexploringthesourcecodethatisinthedata-accessfolder.

www.EBooksWorld.ir

Page 104: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

ReactiveprogrammingwithobservabledatastructuresSofar,weusedsimplearraydatastructuresinthetasklistthatwecreated.Thisisnotreallywhatwe'llfindinreal-worldscenarios.Inrealapplications,wehavetodealwithasynchronousdataandthechangesofthedatathatneedstobesynchronizedbetweenusers.Therequirementsformodernapplicationssometimesevengofurtherandalsoprovideviewupdatesonthechangeddatainrealtime.Aswe'rebuildingamoderntaskmanagementsystemhere,weshouldtrytokeepupwiththeserequirements.

Bothofthese,handlingasynchronousdataandhandlingreal-timedataupdates,requireamajorredesignofthedataflowinourapplication.Usingobservabledatastructures,weenableourapplicationtomasterthechallengesofasynchronousdatawhereweneedtoreacttochange.

Handlingdatainapplicationsbehavesverysimilarlytostreams.Youtakeinput,transformit,combineit,mergeit,andfinally,writeitintooutput.Insystemssuchasthis,it'salsoverylikelythatinputisinacontinuousformandsometimesevenofinfiniteduration.Justtakealivefeedasanexample;thistypeofdataflowscontinuously,andthedataalsoflowsinfinitely.Functionalandreactiveprogrammingareparadigmstohelpusdealwiththiskindofdatainacleanerway.

Asimpleobservablesubscriptionwithvalueemissionandatransformation

www.EBooksWorld.ir

Page 105: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Angular2isreactiveatitsverycoreandthewholeofthechangedetectionandbindingsarebuiltusingareactivearchitecture.Theinputandoutputofcomponents,whichwe'velearnedaboutinthepreviouschapter,isnothingbutadataflowthatisestablishedusingareactiveevent-drivenapproach.AngularusesRxJS,afunctionalandreactiveprogramminglibraryforJavaScript,toimplementthisdataflow.Infact,theEventEmitter,whichwe'veusedtosendcustomeventsfromwithinourcomponents,isjustawrapperaroundanRxJSobservable.

Reactiveandfunctionalprogrammingisexactlywhatwearelookingfortoredesignourapplicationinordertohandleasynchronousdataanddatachanges.AswealreadyhaveRxJSathandfromtheproductiondependencyofAngular,let'suseittoestablishacontinuousdataflowfromourdatasourceintoourapplication.TheDataProviderservicethatispresentinthedata-accessfolderofourprojectprovidesanicewrapperaroundourdatastoreusingRxJS.Aswewillusethisserviceinourwholeapplication,wecandirectlyprovideittothebootstrapinthebootstrap.jsfile,asfollows:

//ImportAngularbootstrapfunction

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

import{DataProvider}from'../data-access/data-provider';

//Importourmainappcomponent

import{App}from'./app';

bootstrap(App,[

DataProvider

]);

AsasecondargumenttothebootstrapfunctionofAngular,wecanprovideapplication-leveldependencies,whichwillbeavailableforinjectioninallcomponentsanddirectives.

Let'snowusetheDataProviderserviceasanabstractiontoobtaindatafromthePouchDBdatastoreandcreateanewserviceresponsibletoprovideprojectdata.

WewillcreateanewProjectServiceclassonthelib/project/project-service/project-service.jspath,asfollows:

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

import{ReplaySubject}from'rxjs/Rx';

import{DataProvider}from'../../../data-access/data-provider';

@Injectable()

exportclassProjectService{

constructor(@Inject(DataProvider)dataProvider){

}

}

Lookingattheimportsectionofournewmodule,youcanseethatweimportthenecessarydependenciesfromtheAngularcoremodulefordependencyinjection.Ourserviceclassusesthe@Injectabledecoratorsothatwecanprovidethistotheinjectorsofcomponents.Wealso

www.EBooksWorld.ir

Page 106: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

injecttheDataProviderserviceintotheconstructorofournewly-createdservice.

TheReplaySubjectclass,whichweimportfromtheRxJSlibrary,isusedtomakeourservicereactive.AsubjectintheRxJSworldisbothanobserveraswellasanobservable.Itcanobservesomethingforchangesandthenemitfurtherontoallitssubscribers.Youcanthinkofasubjectlikeaproxy,whereitsitsinthemiddlebetweenasourceforchangesandagroupofobservers.Wheneverthesourceemitschanges,thesubjectwillnotifyallsubscribersaboutthesechanges.

Now,theReplaySubjectclassisaspecialkindofsubjectthatallowsyoutoreplayabufferofchangeswhennewsubscribersgetadded.Thisisespeciallyusefulifyoualwaysneedtoprovidesomeinitialdatatosubscribers.Imagineourdata,whichwe'dliketogetpropagatedintotheUI.Wewanttoimmediatelygettheinitialdatawhenwesubscribetoourserviceandthengoingforward,wealsowanttogetnotifiedaboutchanges.UsingaReplySubjectclass,whichisbufferingjustonechange,suitsthisuse-caseperfectly.

Let'slookatthefollowingfigure,whichillustratesthebehaviorofReplaySubject:

www.EBooksWorld.ir

Page 107: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

AsourceconnectedtoanobserverusingaReplaySubjectclass,whichbuffersthemostrecentvalueandemitsonsubscription

Intheprecedingfigure,youcanseethatwe'reconnectingaReplaySubjectclasstoasourcethatisemittingvaluechangesovertime.Aftertwoemissions,anobserversubscribestoourReplaySubjectclass.ReplaySubjectwillthenreplayallbufferedchangestothenewsubscriberasiftheseeventsjustoccurred.Inthisexample,weuseareplaybufferlengthofone.Onsubsequentvalueemissions,thesewillbedirectlyre-emittedtothesubscribersoftheReplaySubjectclass.

Let'sgobacktoourProjectServiceclassandaddsomelogictotheconstructorfunctioninordertoemitprojectdatausingaReplaySubjectclass.

Wewillstartoffwithsomememberfieldinitialization,forwhichwe'regoingtoneedto

www.EBooksWorld.ir

Page 108: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

implementthelogic:

this.dataProvider=dataProvider;

this.projects=[];

//We'reexposingareplaysubjectthatwillemiteventswhenever

//theprojectslistchange

this.change=newReplaySubject(1);

NotethatwecreatedanewReplaySubjectclasswithabufferlengthofone,andassignittothememberfiledwiththenamechange.

WealsoassigntheDataProviderservice,whichwaspreviouslyinjectedintheconstructorparameters,tothedataProvidermemberfield.

Now,it'stimetomakeuseoftheDataProviderserviceinordertosubscribeforanychangeswithinourdatastore.ThisestablishesareactiveconnectiontoourdatathatisstoredinPouchDB:

//Settingupourfunctionalreactivesubscriptiontoreceive

//projectchangesfromthedatabase

this.projectsSubscription=this.dataProvider.getLiveChanges()

//Firstconvertthechangerecordstoactualdocuments

.map((change)=>change.doc)

//Filtersothatweonlyreceiveprojectdocuments

.filter((document)=>document.type==='project')

//Finallywesubscribetothechangeobserveranddealwith

//projectchangesinthefunctionparameter

.subscribe((changedProject)=>{

this.projects=this.projects.slice();

//Oneveryprojectchangeweneedtoupdateourprojectslist

constprojectIndex=this.projects.findIndex(

(project)=>project._id===changedProject._id

);

if(projectIndex===-1){

this.projects.push(changedProject);

}else{

this.projects.splice(projectIndex,1,changedProject);

}

//Emitaneventonourreplaysubject

this.change.next(this.projects);

});

TheobservablethatisreturnedbythegetLiveChanges()functionemitsdatainourdatastoreaschanges.Inadditiontothis,thiswillalsoemitanyfuturechangesthatareappliedtoourstoreafterwe'vereceivedtheinitialdata.Youcanimagineapersistentconnectiontothedatabase,andwheneveradocumentisupdatedinthedatabase,ourobserverwillreceivethischangeasavalue.

Observablesprovidealargeamountofso-calledoperatorsthatallowyoutotransformthedatastreamthatoriginatedattheobservable.YoumightalreadyknowaboutsomeofthesefunctionaloperatorsfromtheECMAScript5arrayextrafunctions,suchasmapandfilter.

www.EBooksWorld.ir

Page 109: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Usingoperators,youcanmodelawholetransformationflowuntilyoufinallysubscribetothedata.

Aswereceivechangedobjectsfromthedatastore,wefirstneedtotransformthemintodocumentobjects.Thisisfairlyeasybecauseeachchangeobjectcontainsadocpropertythatactuallyholdsthewholedataofthechangeddocumentforwhichwehavereceivedanupdate.Usingthemapfunction,wecantransformthechangedobjectsintoprojectobjectsbeforewereturnthembackintothedataflow:

.map((change)=>change.doc)

DataProviderwillprovideuswithdataforallthedocumentsinthestore.Asweareonlyinterestedinprojectdataatthemoment,wealsoapplyafilterthatfiltersoutallthedocumentsthatarenotoftheprojecttype:

.filter((document)=>document.type==='project')

Finally,wecansubscribetothetransformedstream,asfollows:

.subscribe((changedProject)=>{})

Thesubscribeoperatoriswhereweterminateourobservationpath.Thisislikeanendpointatwhichwesitandobserve.Insideoursubscribefunction,welistenforprojectdocumentupdatesandincorporatethemintotheprojectsmemberpropertyofourAppcomponent.Thisincludesnotonlyaddingthenewprojectsthatwereaddedtothedocumentstore,butitalsoincludesupdatingexistingprojects.Eachprojectcontainsauniqueidentifierthatcanbeaccessedbythe_idproperty.Thisallowsustofindtherightactioneasily.

Afterupdatingouractualviewoftheprojectsandstoringthelistofprojectsintheprojectsmemberfield,wecanemittheupdatedlistusingourReplaySubjectclass:

this.change.next(this.projects);

OurProjectServiceclassisnowreadytobeused,andapplicationscomponentsthatneedtoobtainprojectdatacansubscribetotheexposedReplaySubjectclassinordertoreactonthesedatachanges.

Let'srefactorourAppcomponentinlib/app.jsandgetridofthefakeTaskListServicethatwe'veusedsofar:

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

import{ProjectService}from'./project/project-service/project-service';

importtemplatefrom'./app.html!text';

@Component({

selector:'ngc-app',

template,

encapsulation:ViewEncapsulation.None,

providers:[ProjectService]

})

www.EBooksWorld.ir

Page 110: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

exportclassApp{

constructor(@Inject(ProjectService)projectService){

this.projectService=projectService;

this.projects=[];

//Settingupourfunctionalreactivesubscriptiontoreceive

//projectchanges

this.projectsSubscription=projectService.change

//Wesubscribetothechangeobserverofourservice

.subscribe((projects)=>{

this.projects=projects;

});

}

//Ifthiscomponentgetsdestroyed,weneedtorememberto

//cleanuptheprojectsubscription

ngOnDestroy(){

this.projectsSubscription.unsubscribe();

}

}

InourAppcomponent,wenowobtainalistofprojectsusingthechangedobservableonourProjectServiceclass.UsingareactivesubscriptiontoReplaySubjectontheservice,wemakesurethatourprojectslistthatisstoredintheAppcomponentwillalwaysbeupdatedafteranychanges.

WeusetheOnDestroylifecyclehooktounsubscribefromtheProjectServicechangeobservable.Thisisanecessarymanualstepifyouliketodoproperhousekeepinginyourapplication.Dependingonthesource,forgettingtounsubscribecouldleadtomemoryleaks.

Withtheprecedingcode,wealreadyestablishedthebaseforareactivedataarchitecture.Weobserveourdatastoreforchangesandouruserinterfacewillreactonthem:

Thisfigureshowsthereactivedataflowend-to-endfromourdata-storeintotheview

www.EBooksWorld.ir

Page 111: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

ImmutabilityImmutabledatawasoriginallyacoreconceptoffunctionalprogramming.Thistopicwillnotcoverimmutabledatainmuchdepth,butitwillexplainthecoreconceptsothatwecantalkabouthowtoapplythisconcepttoAngularcomponents.

Immutabledatastructuresforceyoutocreateafullcopyofthedatathatyouwanttomodifybeforeyoucandothis.You'llneveroperateonthedatadirectlybutonacopyofthissamedata.Thishasmanybenefitsoverstandardmutabledata,themostobviousprobablybeingtheapplicationstate.Whenyoualwaysoperateonnewcopiesofdata,there'snochancethatyou'remessingupthedatathatyouactuallydidn'twanttomodify.

Let'stakethissimpleexample,whichillustratestheissuesobjectreferencescancause:

constlist=[1,2,3];

console.log(list===list.reverse());//true

Althoughthisseemsoddatfirst,itactuallymakessensethattheoutputofthisexampleistrue.Array.reverse()isamutableoperation,anditwillmodifytheinnardsofthearray.TheactualreferencewillstaythesamebecauseJavaScriptwillnotcreateacopyofthearraytoreverseit.Althoughtechnicallythismakesalotofsense,thisisnotwhatweexpectedinthefirstplacewhenwelookatthiscode.

Wecaneasilychangethisexampletoanimmutableprocedurebycreatingacopyofthearraybeforewereverseit:

constlist=[1,2,3];

console.log(list===list.slice().reverse());//false

Theissuewithreferencesisthattheycancausealotofunexpectedside-effects.Also,ifwecomebacktoourencapsulationtopicfromChapter1,Component-BasedUserInterfaces,theyarecompletelyagainsttheconceptofencapsulation.Althoughwemightthinkthatitwouldbesafetopasscomplexdatatypesintoacapsule,it'sactuallynot.Aswe'redealingwithreferenceshere,thedatacanstillbemodifiedfromtheoutside,andourcapsulewillnothavethecompleteownership.Considerthefollowingexample:

classSum{

constructor(data){

this.data=data;

this.data.sum=data.a+data.b;

}

getSum(){

returnthis.data.sum;

}

}

constdata={a:5,b:8};

varsum=newSum(data);

console.log(sum.getSum());//13

www.EBooksWorld.ir

Page 112: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

console.log(data.sum);//13

EvenifweonlywantedtostorethedatainternallyinourSumclass,wewouldhavecreatedanunwantedside-effectofreferencingandmodifyingthedataobjectthat'soutsidetheinstance.Multiplesuminstanceswouldalsosharethesamedatafromoutsideandcausemoreside-effects.Asadeveloper,you'velearnedtotreatobjectreferencesright,buttheystillcancausealotofproblems.

Wedon'thavetheseproblemswithimmutabledata,whichcanbeillustratedeasilywithprimitivedatatypesinJavaScript.Primitivedatatypesdon'tusereferences,andtheyareimmutablebydesign:

letoriginalString='Hellothere!';

letmodifiedString=originalString.replace(/e/g,3);

console.log(originalString);//Hellothere!

console.log(modifiedString);//H3lloth3r3!

There'snowaywecanmodifyaninstanceofastring.Everymodificationthatweperformonastringwillgenerateanewstring,andthispreventstheunwantedside-effects.

So,whydowestillhaveobjectreferenceswithinprogramminglanguages,eventhoughtheycausesomanyissues?Whyaren'tweperformingalltheseoperationsonimmutabledata,andwhyareweonlydealingwithvaluesratherthanobjectreferences?

Ofcourse,imperativedatastructuresalsocomewiththeirbenefits,anditalwaysdependsonthecontextifimmutabledatabringsvalue.

Oneofthemainreasonsthatisoftenusedagainstimmutabledataisbadperformance.Ofcourse,itcostssomeperformanceifweneedtocreatetonsofcopiesofourdataeverytimewewanttomodifyit.However,therearegreatoptimizationtechniques,whichfullyeliminatetheperformanceissuesthatwewouldusuallyexpectfromimmutabledatastructures.Usingatreedatastructurethatallowsinternalstructuralsharing,copiesofthedatawillbesharedinternally.Thisallowsveryefficientmemorymanagement,whichinsomesituations,evenoutperformsmutabledatastructures.IcanhighlyrecommendthepaperbyChrisOkasakiaboutPurelyFunctionalDataStructuresifyouwouldliketoreadmoreaboutperformanceinimmutabledatastructures.

Tip

JavaScriptdoesnotsupportimmutabledatastructuresoutofthebox.However,youcanuselibraries,suchasImmutable.jsbyFacebook,whichprovideyouwithaneasyAPItodealwithimmutabledata.Immutable.jsevenimplementsstructuralsharingandmakesitaperfectpowertoolifyoudecidetobuildonanimmutablearchitectureinyourapplication.

Aswitheveryparadigm,thereareprosandcons,anddependingonthecontext,oneconceptmayfitbetterthananotherone.Inourapplication,wewon'tuseimmutabledatastructuresthatareprovidedbythird-partylibraries,butwe'llborrowsomeofthebenefitsthatyougetfrom

www.EBooksWorld.ir

Page 113: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

immutabledatabythefollowingimmutableidioms:

It'smucheasiertoreasonaboutimmutabledata:Youcanalwaystellwhyyourdataisinagivenstatebecauseyouknowtheexacttransformationpath.Thismaysoundirrelevant,butinpractice,thisisahugebenefitnotonlyforhumanstowritecode,butalsoforcompilersandinterpreterstooptimizeit.Usingimmutableobjectsmakeschangedetectionmuchfaster:Ifwerelyonimmutablepatternstotreatourdata,wecanrelyonobjectreferencecheckstodetectchange.Wenolongerneedtoperformcomplexdataanalysisandcomparisonfordirtychecking,andcanfullyrelyoncheckingreferences.Wehavetheguaranteethatobjectpropertiesdon'tchangewithouttheobjectidentitychangingaswell.ThismakeschangedetectionaseasyasoldObject===newObject.

www.EBooksWorld.ir

Page 114: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

PurecomponentsTheideaofa"pure"componentisthatitswholestateisrepresentedbyitsinputs,whereallinputsareimmutable.Thisiseffectivelyastatelesscomponent,butadditionally,alltheinputsareimmutable.

Iliketocallsuchcomponents"pure"becausetheirbehaviorcanbecomparedtotheconceptofpurefunctionsinfunctionalprogramming.Apurefunctionisafunctionwhichhasthefollowingproperties:

ItdoesnotrelyonanystateoutsideofthefunctionscopeItalwaysbehavethesameifinputparametersdon'tchangeItneverchangesanystateoutsidethefunctionscope(side-effect)

Withpurecomponents,wehaveasimpleguarantee.Apurecomponentwillneverchangewithoutitsinputparametersbeingchanged.Wecanignoreacomponentanditssubcomponentsinchangedetectionuntiloneofthecomponentinputschanges.Stickingtothisideaaboutcomponentsgivesusseveraladvantages.

It'sveryeasytoreasonaboutpurecomponentsandtheirbehaviorcanbepredictedveryeasily.Let'slookatasimpleillustrationofacomponenttreewhereweusepurecomponents:

www.EBooksWorld.ir

Page 115: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Acomponenttreewithimmutablecomponents

Ifwehavetheguaranteethateachcomponentinourtreehasastablestateuntilanimmutableinputpropertychanges,wecansafelyignorechangedetectionthatwouldusuallybetriggeredbyAngular.Theonlywaythatsuchacomponentcouldchangeisifaninputofthecomponentchanges.Let'ssaythatthere'saneventthatcausestheArootcomponenttochangetheinputbindingvalueoftheBcomponent,whichwillchangethevalueofabindingontheEcomponent.Thisevent,andtheresultingprocedure,wouldmarkacertainpathinourcomponenttreetobecheckedbychangedetection:

www.EBooksWorld.ir

Page 116: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Figurethatshowsamarkedpathforchangedetection(inblack)with"pure"components.

Althoughthestateoftherootcomponentchanged,whichalsochangedinputpropertiesofthesubcomponentsontwolevels,weonlyneedtobeconcernedaboutagivenpathwhenthinkingaboutpossiblechangesinthesystem.Purecomponentsgiveusthepromisethattheywillnotchangeiftheirinputswillnot.Immutabilityplaysabigrolehere.Imaginethatyou'rebindingamutableobjecttothecomponent,B,andtheAcomponentwouldchangeapropertyofthisobject.Asweuseobjectreferencesandmutableobjects,thepropertywouldalsobechangedfortheBcomponent.However,there'snowayfortheBcomponenttonoticethischange,aswecan'ttrackwhoknowsaboutourobjectwithinthecomponenttree.Basically,we'dneedtogobacktoregulardirtycheckingofthewholetreeagain.

Byknowingthatallourcomponentsarepureandthattheirinputsareimmutable,wecantellAngulartodisablechangedetectionuntilaninputpropertyvaluechanges.Thismakesourcomponenttreeveryefficient,andAngularcanoptimizechangedetectioneffectively.Whenthinkingaboutlargecomponenttrees,thiscanmakethedifferencebetweenastunningly-fastapplicationandaslowone.

www.EBooksWorld.ir

Page 117: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

ThechangedetectionofAngularisveryflexible,andeachcomponentgetsitsownchangedetector.WecanconfigurethechangedetectionofacomponentbyspecifyingthechangeDetectionpropertyofthecomponentdecorator.

UsingChangeDetectionStrategy,wecanchoosefromalistofstrategiesthatapplyforthechangedetectionofourcomponent.InordertotellAngularthatourcomponentshouldonlybecheckedifanimmutableinputwaschanged,wecanusetheOnPushstrategy,whichisdesignedexactlyforthispurpose.

Let'stakealookatthedifferentconfigurationpossibilitiesofcomponentchange-detectionstrategiesandsomepossibleusecases:

Change-detectionstrategy

Description

CheckAlways

ThisstrategytellsAngulartocheckthiscomponentduringeverychange-detectioncycle,andthisisobviouslythemostexpensivestrategy.Thisistheonlystrategythatguaranteesthatacomponentgetscheckedforchangesoneverypossibleapplicationstatechange.Ifwe'renotworkingwithstatelessorimmutablecomponents,orweareusinganinconsistentdataflowwithinourapplication,thisisstillthemostreliablechange-detectionmethod.Changedetectionwillbeexecutedoneverybrowsereventthatrunswithinthezoneofthiscomponent.

Detached

ThisstrategytellsAngulartocompletelydetachacomponentsubtreefromchangedetection.Thisstrategycanbeusedtocreateamanualchange-detectionmechanism.

OnPush

ThisstrategytellsAngularthatagivencomponentsubtreewillonlychangeunderoneofthefollowingconditions:

OneoftheinputpropertieschangeswherechangesneedtobeimmutableAneventbindingwithinthecomponentsubtreeisreceivinganevent

Default ThisstrategysimplyevaluatestoCheckAlways

www.EBooksWorld.ir

Page 118: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

PurifyingourtasklistIntheprevioustopic,wechangedourmainapplicationcomponenttouseRxJSObservablesinordertogetnotifiedaboutthedatachangesinourdatastore.

WealsolookedintothebasicsofusingimmutabledatastructuresandthatAngularcanbeconfiguredtoassumecomponentchangesonlyoccurwhencomponentinputchanges("pure"components).Aswe'dliketogettheperformancebenefitsthatresultfromthisoptimization,let'srefactorourtasklistcomponenttomakeuseofthis.

Inthepreviouschapter,webuiltourTaskListcomponentanddirectlyplacedthetaskdatainthecomponent.Wethenrefactoredourcodesothatwecouldplacethetaskdataintoaserviceanduseinjectiontoobtaindata.

Now,we'rerebuildingourTaskListcomponenttomakeit"pure"andonlydependentonitsinputproperties.Aswe'llmakesurethatthedataflowingintothecomponentisalwaysimmutable,wecanusetheOnPushchange-detectionstrategyonourrefactoredcomponent.Thiswillcertainlygiveourtasklistaperformanceboost.

Equallyimportantasperformance,arethestructuralbenefitsthatwegetfromusingpurecomponents.A"pure"componentdoesnotchangeanydatadirectlybecauseit'snotallowedtomodifyapplicationstate.Instead,itusesoutputpropertiestoemiteventsonchangeddata.Thisallowsourparentcomponenttoreacttotheseeventsandperformthenecessarystepstohandlethechanges.Asaresultofthis,theparentcomponentwillpossiblychangetheinputpropertiesofthepurecomponent.Thiswilltriggerchangedetectionandeffectivelychangethestateofthepurecomponent.

Whatmightsoundabitovercomplicatedatfirstisactuallyanimmensebenefittothestructureofourapplication.Thisallowsustoreasonaboutourcomponentwithhighconfidence.Theunidirectionaldataflowaswellasstatelessnaturemakesiteasytounderstand,examine,andtestourcomponents.Also,theloosenatureofinputsandoutputsmakesourcomponentextremelyportable.Wecandecideonaparentcomponent,whatdatawe'dliketorunintoourcomponent,andhowwe'dliketohandlechanges.

Let'stakealookatourTaskListcomponentandhowwechangeittoconformtoourconceptof"pure"components:

import{Component,ViewEncapsulation,Input,Output,EventEmitter,

ChangeDetectionStrategy}from'@angular/core';

importtemplatefrom'./task-list.html!text';

import{Task}from'./task/task';

import{EnterTask}from'./enter-task/enter-task';

import{Toggle}from'../ui/toggle/toggle';

@Component({

selector:'ngc-task-list',

host:{

www.EBooksWorld.ir

Page 119: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

class:'task-list'

},

template,

encapsulation:ViewEncapsulation.None,

directives:[Task,EnterTask,Toggle],

changeDetection:ChangeDetectionStrategy.OnPush

})

exportclassTaskList{

@Input()tasks;

//Eventemitterforemittinganeventoncethetasklisthas

//beenchanged

@Output()tasksUpdated=newEventEmitter();

constructor(){

this.taskFilterList=['all','open','done'];

this.selectedTaskFilter='all';

}

ngOnChanges(changes){

if(changes.tasks){

this.taskFilterChange(this.selectedTaskFilter);

}

}

taskFilterChange(filter){

this.selectedTaskFilter=filter;

this.filteredTasks=this.tasks?this.tasks.filter((task)=>{

if(filter==='all'){

returntrue;

}elseif(filter==='open'){

return!task.done;

}else{

returntask.done;

}

}):[];

}

//Functiontoaddanewtask

addTask(title){

consttasks=this.tasks.slice();

tasks.push({created:+newDate(),title,done:null});

this.tasksUpdated.next(tasks);

}

}

Alltheoperationsinourtasklistcomponentarenowimmutable.Weneverdirectlymodifyourtask'sdatathatwaspassedinasinput,butratherwecreatenewtaskdataarraystoperformmutableoperations.

Fromwhatwe'velearnedfromtheprevioussection,thiseffectivelymakesourcomponenta"pure"component.Thiscomponentitselfisonlyrelyingonitsinputandmakesourcomponentveryeasytoreasonabout.

You'veprobablynoticedthatwe'vealsoconfiguredthechange-detectionstrategyofour

www.EBooksWorld.ir

Page 120: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

component.Aswehavea"pure"componentnow,wecanconfigureourchange-detectionstrategyaccordinglytosavesomeperformance:

@Component({

selector:'ngc-task-list',

changeDetection:ChangeDetectionStrategy.OnPush

})

Aswe'rerenderingaTaskcomponentforeachdatarecordinourtasklist,weshouldalsocheckwhatwecanchangethereinordertoroundthisout.

Let'slookatthechangesinourTaskcomponent:

import{Component,Input,Output,EventEmitter,ViewEncapsulation,HostBinding,

ChangeDetectionStrategy}from'@angular/core';

importtemplatefrom'./task.html!text';

import{Checkbox}from'../../ui/checkbox/checkbox';

@Component({

selector:'ngc-task',

host:{

class:'task'

},

template,

encapsulation:ViewEncapsulation.None,

directives:[Checkbox],

changeDetection:ChangeDetectionStrategy.OnPush

})

exportclassTask{

@Input()task;

//Weareusinganoutputtonotifyourparentaboutupdates

@Output()taskUpdated=newEventEmitter();

@HostBinding('class.task--done')

getdone(){

returnthis.task&&this.task.done;

}

//Weusethisfunctiontoupdatethecheckedstateofourtask

markDone(checked){

this.taskUpdated.next({

title:this.task.title,

done:checked?+newDate():null

});

}

}

WealsousetheOnPushstrategyforourTaskcomponent,andwecandothisbecausewealsohaveapurecomponenthere.Thiscomponentonlydependsonitsinputs.Bothinputsexpectnativevalues(aStringfortitleandaBooleanfordone),whichactuallymakesthemimmutablebynature.ChangesonthetaskwillbecommunicatedusingthetaskUpdatedoutputproperty.

www.EBooksWorld.ir

Page 121: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Now,thisisagoodtimetothinkaboutwheretoplaceourtasklistintheapplication.Aswe'rewritingataskmanagementsystemthatgivesouruserstheabilitytomanagetaskswithinprojects,weneedtohaveacontainerthatwillencapsulatetheconcernsofprojects.WecreateanewProjectcomponent,onthepathlib/project/project.js,whichwilldisplayprojectdetailsandrenderstheTaskListcomponentasasubcomponent:

import{Component,ViewEncapsulation,Input,Output,EventEmitter,

ChangeDetectionStrategy}from'@angular/core';

importtemplatefrom'./project.html!text';

import{TaskList}from'../task-list/task-list';

@Component({

selector:'ngc-project',

host:{

class:'project'

},

template,

encapsulation:ViewEncapsulation.None,

directives:[TaskList],

changeDetection:ChangeDetectionStrategy.OnPush

})

exportclassProject{

@Input()title;

@Input()description;

@Input()tasks;

@Output()projectUpdated=newEventEmitter();

//Thisfunctionshouldbecalledifthetasklistofthe

//projectwasupdated

updateTasks(tasks){

this.projectUpdated.next({

title:this.title,

description:this.description,

tasks

});

}

}

Again,wemakethestateofthiscomponentdependentonlyonitsimmutableinputs,andweusetheOnPushstrategyinordertogetthepositiveperformanceimplicationsofusingapurecomponent.It'salsoimportanttonotethattheupdateTasksfunctionactsassomesortofadelegatefromourTaskListcomponent.WhenweupdateataskinsidetheTaskListcomponent,wecatchtheeventintheprojecttemplateandcalltheupdateTasksfunctionwiththenewupdatedtasklist.Fromhere,we'rejustemittingtheupdatedprojectdatawiththenewtasklistfurtherupinthecomponenttree.

Let'salsotakealookatthetemplateofourProjectcomponentquicklytounderstandthewiringbehindthiscomponent:

<divclass="project__l-header">

<h2class="project__title">{{title}}</h2>

<p>{{description}}</p>

</div>

www.EBooksWorld.ir

Page 122: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

<ngc-task-list[tasks]="tasks"

(tasksUpdated)="updateTasks($event)">

</ngc-task-list>

Thebindinglogicinthetemplatetellsushowthewholedataflowwithourpurifiedcomponentswork.WhiletheProjectcomponentitselfreceivesthelistoftasksasinput,itdirectlyforwardsthisdatatothetasksinputoftheTaskListcomponent.IftheTaskListcomponentfiresatasksUpdatedevent,we'recallingtheupdateTasksmethodontheProjectcomponent,whichinfactjustemitsaprojectUpdatedeventagain.

www.EBooksWorld.ir

Page 123: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

RecapTherefactoringofourtasklistisnowcompleted,andweappliedourknowledgeaboutimmutablecomponentsandobservabledatastructurestogainsomeperformancewinsinthisstructure.Therewon'tbeunnecessarydirtycheckingonourTaskcomponentanymorebecauseweswitchedtotheOnPushchange-detectionstrategy.

WehavealsoreducedthecomplexityoftheTaskListandTaskcomponentsalot,andit'snowfareasiertoreasonaboutthesecomponentsandtheirstate.

Afurtherbenefitofthisrefactoringisthegreatencapsulationlevelthatweachievedusingimmutableinputs.OurTaskListcomponentisnotrelyingonanytaskcontainerasaproject.Wecanalsopassitalistoftasksacrossalltheprojects,anditcanstillworkasexpected.

www.EBooksWorld.ir

Page 124: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

CompositionusingcontentprojectionInthissection,wewillcreateatabbedinterfaceforourProjectcomponentthatwillhelpusfurtherorganizethestructureofourapplicationuserinterface.InordertocreateaTabscomponent,we'lllookatcontentprojectionandcontentchildinjectionusingobservablequerylists.

Inputandoutputpropertiesaregreattoestablishencapsulation,andthisisamainpropertyofpropercomposition.However,sometimestherequirementsarenottoonlypassdatabuttoalsopasscontentfromtheoutsideofacomponentintothecomponent.InShadowDOM,thisisdoneusingso-calledslots.InAngularcomponents,wecancreatecontentprojectionpointsusingthe<ng-content>element.

Let'slookatasimplecontentprojectionexamplethathelpsusunderstandwhatthisisgoodfor:

@Component({

selector:'child',

template:`

<article>

<header>

<h1><ng-contentselect="[data-header]"></ng-content></h1>

</header>

<ng-content></ng-content>

</article>

`

})

exportclassChild{}

@Component({

selector:'app',

template:`

<child>

<headerdata-header>Contentprojectionisgreat</header>

<p>Insertcontentinacontrolledmanner</p>

</child>

`,

directives:[Child]

})

exportclassApp{}

LookingattheAppcomponentinthisexample,wecanseethatwe'veputelementsinsidetheactual<child>element.Usually,thiscontentwouldbeignoredandremovedbyAngularbeforeitrenderstheChildcomponentwithitstemplate.

However,whenusingcontentprojection,theelementsthatareplacedinsideourcomponentHTMLelementcanbesuckedintotheChildcomponent.Thisiswhatcontentprojectionisallabout.ContentprojectionisactuallyverysimilartotheconceptoftransclusionfromAngular1.

www.EBooksWorld.ir

Page 125: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

AllthatweneedtodoinordertoenablecontentprojectionwithinaChildcomponentistoplacea<ng-content>elementwithinitstemplate.Inthisway,wespecifyatwhatlocationsinourcomponenttemplatewewanttoinsertthecontentthatissuckedinfromtheparentcomponent.

Additionally,wecanusetheselectattributeonthe<ng-content>elementtosetaCSS-likeselector.Thisselectorwillbeusedtoonlysuckinspecificelements,whichmatchthisselector.Inthisway,youcanhavemultipleinsertionpointsthatcoverdifferentcontentrequirements.

Elementsfromthecomponentelementcanonlybeinsertedonce,andthecontentprojectionworksbygoingthroughallthe<ng-content>elementsinsequentialorderbyprojectmatchingelements.Ifyouhavemultiplecompetingcontentprojectionpointsinyourtemplatethatareinterestedinthesameelements,thefirstonewillactuallywin.

www.EBooksWorld.ir

Page 126: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

CreatingatabbedinterfacecomponentLet'sintroduceanewUIcomponentinouruifolderintheprojectthatwillprovideuswithatabbedinterfacethatwecanuseforcomposition.Weusewhatwelearnedaboutcontentprojectioninordertomakethiscomponentreusable.

We'llactuallycreatetwocomponents,oneforTabs,whichitselfholdsindividualTabcomponents.

First,let'screatethecomponentclasswithinanewtabs/tabfolderinafilecalledtab.js:

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

importtemplatefrom'./tab.html!text';

@Component({

selector:'ngc-tab',

host:{

class:'tabs__tab'

},

template,

encapsulation:ViewEncapsulation.None

})

exportclassTab{

@Input()name;

@HostBinding('class.tabs__tab--active')active=false;

}

TheonlystatethatwestoreinourTabcomponentiswhetherthetabisactiveornot.Thenamethatisdisplayedonthetabwillbeavailablethroughaninputproperty.

Weuseaclasspropertybindingtomakeatabvisible,basedontheactiveflagwesetaclass;withoutthis,ourtabsarehidden.

Let'stakealookatthetab.htmltemplatefileofthiscomponent:

<ng-content></ng-content>

Thisisitalready?Actually,yesitis!TheTabcomponentisonlyresponsibleforthestorageofitsnameandactivestate,aswellastheinsertionofthehostelementcontentinthecontentprojectionpoint.There'snoadditionaltemplatingthatisneeded.

Now,we'llmoveonelevelupandcreatetheTabscomponentthatwillberesponsibleforgroupingalltheTabcomponents.Aswewon'tincludeTabcomponentsdirectlywhenwewanttocreateatabbedinterfacebutusetheTabscomponentinstead,thisneedstoforwardcontentthatweputintotheTabshostelement.Let'slookathowwecanachievethis.

Inthetabsfolder,wewillcreateatabs.jsfilethatcontainsourTabscomponentcode,asfollows:

www.EBooksWorld.ir

Page 127: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

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

importtemplatefrom'./tabs.html!text';

//WerelyontheTabcomponent

import{Tab}from'./tab/tab';

@Component({

selector:'ngc-tabs',

host:{

class:'tabs'

},

template,

encapsulation:ViewEncapsulation.None,

directives:[Tab]

})

exportclassTabs{

//Thisqueriesthecontentinside<ng-content>andstoresa

//querylistthatwillbeupdatedifthecontentchanges

@ContentChildren(Tab)tabs;

//ThengAfterContentInitlifecyclehookwillbecalledoncethe

//contentinside<ng-content>wasinitialized

ngAfterContentInit(){

this.activateTab(this.tabs.first);

}

activateTab(tab){

//Toactivateatabwefirstconvertthelivelisttoan

//arrayanddeactivatealltabsbeforewesetthenew

//tabactive

this.tabs.toArray().forEach((t)=>t.active=false);

tab.active=true;

}

}

Let'sobservewhat'shappeninghere.Weusedanew@ContentChildrenannotation,inordertoqueryourinsertedcontentfordirectivesthatmatchthetypethatwepasstothedecorator.ThetabspropertywillcontainanobjectoftheQueryListtype,whichisanobservablelisttypethatwillbeupdatedifthecontentprojectionchanges.Youneedtorememberthatcontentprojectionisadynamicprocess,asthecontentinthehostelementcanactuallychange,forexample,usingtheNgFororNgIfdirectives.

WeusetheAfterContentInitlifecyclehook,whichwe'vealreadybrieflydiscussedintheCustomUIelementssectionofChapter2,Ready,Set,Go!ThislifecyclehookiscalledafterAngularhascompletedcontentprojectiononthecomponent.OnlythendowehavetheguaranteethatourQueryListobjectwillbeinitialized,andwecanstartworkingwithchilddirectivesthatwereprojectedascontent.

TheactivateTabfunctionwillsettheTabcomponent'sactiveflag,deactivatinganypreviousactivetab.AstheobservableQueryListobjectisnotanativearray,wefirstneedtoconvertitusingtoArray()beforewestartworkingwithit.

Let'snowlookatthetemplateoftheTabscomponentthatwecreatedinafilecalledtabs.html

www.EBooksWorld.ir

Page 128: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

inthetabsdirectory:

<ulclass="tabs__tab-list">

<li*ngFor="lettaboftabs">

<buttonclass="tabs__tab-button"

[class.tabs__tab-button--active]="tab.active"

(click)="activateTab(tab)">{{tab.name}}</button>

</li>

</ul>

<divclass="tabs__l-container">

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

</div>

ThestructureofourTabscomponentisasfollows.

Firstwerenderallthetabbuttonsinanunorderedlist.Aftertheunorderedlist,wehaveatabscontainerthatwillcontainallourTabcomponentsthatareinsertedusingcontentprojectionandthe<ng-content>element.NotethattheselectorthatweuseisactuallytheselectorweuseforourTabcomponent.TabsthatarenotactivewillnotbevisiblebecausewecontrolthisusingCSSonourTabcomponentclassattributebinding(refertotheTabcomponentcode).

Thisisallthatweneedtocreateaflexibleandwell-encapsulatedtabbedinterfacecomponent.NowwecangoaheadandusethiscomponentinourProjectcomponenttoprovideasegregationofourprojectdetailinformation.

Wewillcreatethreetabsfornow,wherethefirstonewillembedourtasklist.Wewilladdressthecontentoftheothertwotabsinalaterchapter.

Let'smodifyourProjectcomponenttemplateintheproject.htmlfileasafirststep.

InsteadofincludingourTaskListcomponentdirectly,wenowusetheTabsandTabcomponentstonestthetasklistintoourtabbedinterface:

<ngc-tabs>

<ngc-tabname="Tasks">

<ngc-task-list[tasks]="tasks"

(tasksUpdated)="updateTasks($event)">

</ngc-task-list>

</ngc-tab>

<ngc-tabname="Comments"></ngc-tab>

<ngc-tabname="Activities"></ngc-tab>

</ngc-tabs>

Youshouldhavenoticedbynowthatweareactuallynestingtwocomponentswithinthistemplatecodeusingcontentprojection,asfollows:

First,theTabscomponentusescontentprojectiontoselectallthe<ngc-tab>elements.Astheseelementshappentobecomponentstoo(ourTabcomponentwillattachtoelementswiththisname),theywillberecognizedassuchwithintheTabscomponentoncetheyareinserted.

www.EBooksWorld.ir

Page 129: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Inthe<ngc-tab>element,wethennestourTaskListcomponent.IfwegobacktoourTaskcomponenttemplate,whichwillbeattachedtoelementswiththenamengc-tab,wewillhaveagenericprojectionpointthatinsertsanycontentthatispresentinthehostelement.OurtasklistwilleffectivelybepassedthroughtheTabscomponentintotheTabcomponent.

www.EBooksWorld.ir

Page 130: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

RecapInthistopic,wecreatedaveryhandytabbedinterfacecomponentthatwecanusetosegregateouruserinterfaceandprovideafocusedcontextforourusers.Weusedcontentprojectionpointsusingthe<ng-content>elements.Wealsolearnedhowtoaccessinsertedcomponentsusingthe@ContentChildrenannotationandobservablelistsusingtheQueryListtype.

www.EBooksWorld.ir

Page 131: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

MixingprojectedwithgeneratedcontentOurtaskmanagementapplicationsupportsthelistingofmultipleprojectswhereausercanmanagetasks.Weneedtoprovideanavigationthatenablesausertobrowsethroughtheexistingprojects.Asprojectscomefromourdatastore,thenavigationwillneedtobegenerateddynamically.However,wealsowouldliketohavethepossibilityofspecifyingsomenavigationitemswithinournavigation,asstaticcontentwithpuretemplating.

Inthissection,wewillcreateasimplenavigationcomponent,whichwillusecontentprojection,sothatwecanaddstaticnavigationitems.Atthesametime,navigationitemscanbegeneratedfromdataandmixedwiththestaticcontent-basednavigationitems.

Let'sfirsttakealookatanillustrationofthearchitecturaldesignandcompositionthatwe'regoingtousetoimplementournavigation:

Anillustrationofthenavigationcomponenttreeandinteractions

We'lluseanintermediatecomponentbetweentheNavigationandNavigationItemcomponents.TheNavigationSectioncomponentisresponsibleforthedivisionofmultipleitemsintoasection.Thenavigationsectionsalsohaveatitlethatwillbedisplayedontopoftheitemlist.

www.EBooksWorld.ir

Page 132: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

TheillustrationshowstwoNavigationSectioncomponents,wheretheleftoneusespurecontentprojectiontocreateitems,aswehavelearnedintheprevioussection.TherightNavigationSectioncomponentgeneratesitemsusinganinputdatastructure,whichisalistofnavigationitemmodels.

AswehaveintermediatecomponentsbetweentheNavigationandNavigationItemscomponents(wecanonlyhaveoneselectednavigation),wealsoestablishadirectcommunicationpathbetweenthem.Wewillachievethisusingancestorcomponentinjection.

Note

Thearchitecturalapproachforthisnavigationisjustoneofmanypossibleapproaches.Wechoosethisapproachinordertoshowyouhowwecaneasilymixcontentprojectionandgeneratedcontent.Inthisexample,wedon'tusetheAngularroutertoprovidenavigationstateandroutemapping.Thiswillbepartofalaterchapter.

Let'sstartbottomupwiththeNavigationItemcomponentandcreateanewnavigation-item.jsfileinanewly-creatednavigation/navigation-section/navigation-itempath:

//Werelyonthenavigationcomponenttoknowifweareactive

import{Navigation}from'../../navigation';

@Component({

selector:'ngc-navigation-item'

})

exportclassNavigationItem{

@Input()title;

@Input()link;

constructor(@Inject(Navigation)navigation){

this.navigation=navigation;

}

//Here,wearedelegatingtothenavigationcomponenttoseeif

//weareactiveornot

isActive(){

returnthis.navigation.isItemActive(this);

}

//Ifthislinkisactivatedweneedtotellthenavigationcomponent

onActivate(){

this.navigation.activateLink(this.link);

}

}

FromtheNavigationItemcomponentcode,wecanseethatwe'redirectlycommunicatingwiththeNavigationancestorcomponent.WecansimplyinjecttheNavigationComponent,asthisisachildofthecomponent.AstheNavigationitemswillneverexistwithoutaNavigationcomponent,weshouldbefinewiththisdirectdependency.

Let'smoveontotheNavigationSectioncomponentthatistheintermediatecomponent

www.EBooksWorld.ir

Page 133: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

betweentheNavigationcomponentandtheitemsandisresponsibleforthegroupingofitemstogether.

Wewillcreateafilecallednavigation-section.jsinthenavigation/navigation-sectionpath:

@Component({

selector:'ngc-navigation-section',

directives:[NavigationItem]

})

exportclassNavigationSection{

@Input()title;

@Input()items;

}

Holdon!That'sallthatthisneeds?Didn'twesaythatwewantourNavigationSectioncomponenttoalsoberesponsiblefornotonlyprovidingawaytoinsertcontent,butalsoacceptingdatainordertogenerateitems?Well,thisistrue.However,thisisactuallypuretemplatinglogic,anditcanbedonesolelywithinthetemplatefileofthecomponent.AllthatweneedisanoptionalinputwithitemdatathatwewillusetogeneratetheNavigationItemcomponents.

Let'screatetheviewtemplateforthiscomponentinafilenamednavigation-section.html:

<h2class="navigation-section__title">{{title}}</h2>

<ulclass="navigation-section__list">

<ng-contentselect="ngc-navigation-item"></ng-content>

<ngc-navigation-item*ngFor="letitemofitems"

[title]="item.title"

[link]="item.link"></ngc-navigation-item>

</ul>

Well,thiswasn'trocketscience,wasit?However,thisshowsthegreatflexibilitythatwehaveinAngularcomponenttemplates:

Firstly,wecreateacontentprojectionpointthatselectsalltheelementsfromthehostelementthatmatchthenamengc-navigation-item.ThismeansthattheNavigationItemcomponentscanbeplacedoutsidethecomponentinaverystaticfashiontocreate,forexample,staticlinks.AsthemodelpropertiesofnavigationitemsaredirectlyexposedasbindableattributesontheNavigationItemelement,wecanalsoplacethemstaticallyintoapureHTMLtemplatewithregularDOMattributes.Secondly,wecanusetheNgFordirectivetogeneratetheNavigationItemcomponentsinsidethecomponent.Here,wejustiterateoverthelistofnavigationitemmodelsthatactsasanoptionalinputtoourcomponent.Weusebindingsintheitemsmodelsothatwecanevenpropagatechangeintoournavigationitemcomponents.

Asafinalstep,wecreatetheNavigationcomponentitselfthatusescontentprojectionpointssothatwecanmanagetheNavigationSectioncomponentfromoutside.Wecreateafilecallednavigation.jstowritethecodeoftheNavigationcomponent:

www.EBooksWorld.ir

Page 134: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

import{NavigationSection}from'./navigation-section/navigation-section';

@Component({

selector:'ngc-navigation',

directives:[NavigationSection]

})

exportclassNavigation{

@Input()activeLink;

//Checksifagivennavigationitemiscurrentlyactivebyits

//link.Thisfunctionwillbecalledbynavigationitemchild

//components.

isItemActive(item){

returnitem.link===this.activeLink;

}

//Ifalinkwantstobeactivatedwithinthenavigation,this

//functionneedstobecalled.Thiswaychildnavigationitem

//componentscanactivatethemselves.

activateLink(link){

this.activeLink=link;

this.activeLinkChange.next(this.activeLink);

}

}

IntheNavigationcomponent,westorethestateofwhichnavigationitemisactivated.Thisisalsoprovidedasinputtothecomponentsothatwecansettheactivatedlinkwithaninputbindingfromoutside.TheisItemActiveandactivateLinkfunctionsaretheretomonitorandchangethestateoftheactiveitemwithinthenavigation.ThesefunctionsaredirectlyusedwithintheNavigationItemcomponents,whichinjectthenavigationusingancestorcomponentinjection.

Now,theonlybitthatismissingistoincludeournavigationinthemainapplication.Forthis,wewilledittheapp.htmltemplateofthecomponent:

<divclass="app">

<divclass="app__l-side">

<ngc-navigation

[activeLink]="getSelectedProjectLink()"

(activeLinkChange)="selectProjectByLink($event)">

<ngc-navigation-section

title="Projects"

[items]="getProjectNavigationItems()">

</ngc-navigation-section>

</ngc-na

vigation>

</div>

<divclass="app__l-main">

</div>

</div>

Here,weonlyusethegenerativeapproachtowriteaNavigationSectioncomponentwhere

www.EBooksWorld.ir

Page 135: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

weactuallypassalistofnavigationitemmodelsintothenavigationcomponent.ThislistisgeneratedbythegetProjectNavigationItemsfunctiononourmainapplicationcomponentusingtheavailableprojectsfromourobservabledatastructure:

Ascreenshotofthenewly-createdprojectnavigation

www.EBooksWorld.ir

Page 136: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

SummaryInthischapter,welearnedabouthowwecanprofitfromconcepts,suchasreactiveprogramming,observabledatastructures,andimmutableobjects,inordertomakeourapplicationperformbetter,andmostimportantly,simpleandeasytoreasonabout.

Wetouchedonthedifferentchange-detectionstrategiesandlearnedhowtousetheOnPushstrategytogainbetterperformanceincombinationwithimmutabledata.

Webuiltatabbeduserinterfacecomponentthatwecanreusewhereverweneedit,andwelearnedabouttheconceptofcontentprojection.Wealsocreatedasimplenavigationcomponenttreethatusesamixofcontentprojectionandgeneration.ThenavigationitemsalsodirectlycommunicatewiththeirancestorNavigationcomponent,inordertomanagetheirstateusingancestorcomponentinjection.

Asweswitchedtoareactiveapproachtomanagedatawithinourapplication,Iwantyoutoperformalittleexperiment.Ifyou'vedownloadedthefinalchapter'scode,goaheadandopentwobrowserwindowsthatpointtothetaskmanagementapplication.Youwillbeamazedthatwealreadyhaveaworkingreal-timesynchronizationinplacethatallowsustoworkinbothbrowserwindowsandhavebothofthemupdatedatthesametime.Thishasallbeenmadepossiblebecauseofthereactiveandfunctionalwaythatweworkwithdatainourcomponents.

www.EBooksWorld.ir

Page 137: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Chapter4.NoComments,Please!Duringthecourseofthischapter,wewillcreatereusablecomponentstoenablecommentingnotonlyonprojects,butalsoonanyotherentitywithinourapplication.We'llbuildourcommentingsysteminawaythatitwillallowustoplaceitanywherewe'dlikeforouruserstoputcomments.Inordertoprovideouruserswithafeaturetoeditexistingcommentsandalsoaseamlessauthoringexperience,we'llcreateaneditorUIcomponentthatcouldbeusedtomakearbitrarycontentwithinourapplicationeditable.

Discussingsecurityandproperusermanagementinthischapterisstilloutofscope,butwe'regoingtocreateadummyuserservicethatwillhelpussimulatealogged-inuser.Thisservicewillbeusedbythecommentingsystem,andwe'llrefactorourexistingcomponenttomakeuseofittoo.

We'llcoverthefollowingtopicsinthischapter:

Usingcontenteditabletocreateanin-placeeditorUsing@HostBindingand@HostListenertobindcomponentmemberstohostelementpropertiesandeventsCommunicatingdirectlywithviewchildrenusingthe@ViewChildannotationPerformingDOMoperationsbyinjectingandusingElementRefCreatingadummyuserserviceandusingthe@InjectableannotationtoserveitasadependencyinjectionproviderApplyingcustomactionsoncomponentinputchanges,usingtheOnChangeslifecyclehookCreatingasimplepipetoformatrelativetimeintervalsusingtheMoment.jslibrary

www.EBooksWorld.ir

Page 138: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

OneeditortorulethemallSincewewillbeprocessingalotofuserinputwithinourapplication,it'scrucialtoprovideaniceauthoringexperiencetoourusers.Withinthecommentingsystemwe'reabouttocreateinthischapter,weneedawaythroughwhichuserscouldeditexistingcomments,aswellasaddnewcomments.Wecoulduseregulartextareainputandworkwithdialogboxestoeditcomments,butthisseemstooold-fashionedforamodernuserinterface,whichwe'regoingtobuild,anddoesnotreallyprovideagreatuserexperience.Whatwe'relookingforisawaytoeditstuffinplace.Thecommentingsystemwillnotonlybenefitfromsuchanin-placeeditor,butitwillalsohelpuscreatetheeditorcomponentinsuchawaythatwecanuseitforanycontentwithinourapplicationthatwe'dliketomakeeditable.

Inordertobuildourin-placeeditor,we'regoingtousethecontenteditableAPIthatwillenableausertomodifythecontentwithintheHTMLelementsdirectlyinthesitedocument.

ThefollowingexampleillustrateshowwecanusethecontenteditableattributetomakeHTMLelementseditable:

<h1contenteditable>I'maneditabletitle</h1>

<p>Ican'tbeedited</p>

RuntheprecedingexampleonablankHTMLpageandclickontheh1text.Youwillseethattheelementhasbecomeeditableandyoucantypetomodifyitscontent.

Gettingnotifiedaboutchangeswithineditableelementsisfairlyeasy.There'saninputeventemittedoneveryDOMelementthatiseditable,andthiswillallowustoreacttoachangeeasily:

consth1=document.querySelector('h1');

h1.addEventListener('input',(event)=>console.log(h1.textContent);

Withthisexample,wehavealreadycreatedanaiveimplementationofanin-placeeditorwherewe'reabletomonitorchangesappliedbytheuser.Withinthistopic,we'llusethisstandardtechnologytobuildareusablecomponentthatwecanusewhereverwewanttomakethingseditable.

www.EBooksWorld.ir

Page 139: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

CreatinganeditorcomponentFirst,let'screateanewfoldernamededitorwithinouruifolder.Inthisfolder,we'regoingtocreateanewcomponentfilenamededitor.js:

import{Component,ViewChild,Input,Output,ViewEncapsulation,EventEmitter,

HostBinding,HostListener}from'@angular/core';

importtemplatefrom'./editor.html!text';

@Component({

selector:'ngc-editor',

host:{

class:'editor'

},

template,

encapsulation:ViewEncapsulation.None

})

exportclassEditor{

//Usingviewchildreferencewithlocalviewvariablename

@ViewChild('editableContentElement')editableContentElement;

//Contentthatwillbeeditedanddisplayed

@Input()content;

//Creatingahostelementclassattributebindingfromthe

//editModeproperty

@Input()@HostBinding('class.editor--edit-mode')editMode;

@Input()showControls;

@Output()editSaved=newEventEmitter();

@Output()editableInput=newEventEmitter();

//Weneedtomakesuretoreflecttooureditableelementif

//contentgetsupdatedfromoutside

ngOnChanges(){

if(this.editableContentElement&&this.content){

this.setEditableContent(this.content);

}

}

ngAfterViewInit(){

this.setEditableContent(this.content);

}

//Thisreturnsthecontentofourcontenteditable

getEditableContent(){

returnthis.editableContentElement.nativeElement.textContent;

}

//Thissetsthecontentofourcontenteditable

setEditableContent(content){

this.editableContentElement.nativeElement.textContent=

content;

}

//Thisannotationwillcreateaclickeventlisteneronthe

//hostelementthatwillinvoketheunderlyingmethod

@HostListener('click')

www.EBooksWorld.ir

Page 140: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

focusEditableContent(){

if(this.editMode){

this.editableContentElement.nativeElement.focus();

}

}

//Methodthatwillbeinvokedifoureditableelementis

//changed

onInput(){

//EmitaeditableInputeventwiththeeditedcontent

this.editableInput.next(this.getEditableContent());

}

//Onsavewereflectthecontentoftheeditableelementinto

//thecontentfieldandemitanevent

save(){

this.editSaved.next(this.getEditableContent());

this.setEditableContent(this.content);

//SettingeditModetofalsetoswitchtheeditorbackto

//viewingmode

this.editMode=false;

}

//Cancelingtheeditwillnotreflecttheeditedcontentand

//switchbacktoviewingmode

cancel(){

this.setEditableContent(this.content);

this.editableInput.next(this.getEditableContent());

this.editMode=false;

}

//Theeditmethodwillinitializetheeditableelementandset

//thecomponentintoeditmode

edit(){

this.editMode=true;

}

}

Okay,that'squitealotofnewcode.Let'sdissectthedifferentpartsoftheEditorcomponentandgothrougheachpartstepbystep.

WithinourEditorcomponent,we'llneedtointeractwiththenativeDOMelement,whichiseditable.Theeasiestandalsothesafestmethodtodothisistousethe@ViewChilddecoratorinordertoretrieveanelementwithalocalviewreference:

@ViewChild('editableContentElement')editableContentElement;

Inthepreviouschapter,welearnedaboutthe@ContentChildrenannotation,whichhelpsusobtainalistofallthechildcomponentswithincontentprojectionpoints.Ifwewouldliketodothesamewithregularviewchildren,[email protected]@ContentChildrensearchesforcomponentswithincontentprojectionpoints,@ViewChildrenhuntsfortheregularsub-treeofacomponent.

www.EBooksWorld.ir

Page 141: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Ifwewanttosearchthecomponentsub-treeforonesinglecomponent,wecanusethe@ViewChildannotation(pleasenotethat@ViewChildand@ViewChidrenaredifferent).

Queryannotation Description

@ViewChildren(selector)Willquerythecurrentcomponent'sviewforeitherdirectivesorcomponentsandreturnanobjectofthetypeQueryList.Iftheviewisdynamicallyupdated,thislistwillbeupdatedaswell.

@ViewChild(selector) Willqueryforonlythefirstmatchingcomponentordirectiveandreturnaninstanceofit.

Note

Aselectorcanbeeitheradirectiveorcomponenttype,orastringthatcontainsthenameofalocalviewvariable.Ifalocalviewvariablenameisprovided,Angularwillsearchfortheelementcontainingtheviewvariablereference.

Ifyouneedtocommunicatewithviewchildcomponentsdirectly,using@ViewChildand@ViewChildrenannotationsshouldbeyourpreferredway.

Tip

Sometimesyouneedtoruntheinitializationcodeonviewchildrenafteryourcomponentisinitialized.Insuchcases,youcanusetheAfterViewInitlifecyclehook.Whiletheviewchildpropertiesofyourcomponentclasswillstillbeundefinedwithintheconstructorofyourcomponent,theywillbepopulatedandinitializedaftertheAfterViewInitlifecyclecallback.

The@ViewChildand@ViewChildrendecoratorsaregreattoolstointeractwithinyourviewdirectly.Itdoesn'treallymatterwhetheryou'dliketointeractwithaDOMelementoracomponentinstance.BothusecasesarenicelycoveredusingthisdeclarativeAPI.

Let'smovebacktoourEditorcomponentcode.Thenextthingwe'regoingtolookintoarethecomponent'sinputfunctions:

@Input()content;

@Input()@HostBinding('class.editor--edit-mode')editMode;

@Input()showControls;

Thecontentinputpropertyisthemaininterfaceforinteractingwiththecomponentfromoutside.Usingpropertybindings,wecanhaveanypreexistingtextcontentsetupintheeditorcomponent.

TheeditModepropertyisaBooleanvaluethatcontrolswhethertheeditorisineditordisplay

www.EBooksWorld.ir

Page 142: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

mode.Oureditorcomponentwilldependonthisflagtoknowwhethercontentshouldbeeditedornot.Thisallowsustoswitchfromread-onlymodetoeditmodeandbackinteractively.

Thoughaninputproperty,thisflagcanbecontrolledfromoutsidethecomponent.Atthesametime,itcanalsobeusedtocreatepropertybindingofahostelement.Specifically,wecanusetheflagtocreateaclassattributebindingtoaddorremovethemodifierclass,editor--edit-mode.Thisclassisusedtocontrolsomedifferencesinthevisualappearanceoftheeditorwhileineditmode.

Thelastofthethreeinputpropertiesinoureditorcomponent,showControls,controlswhethertheeditorshouldshowthecontrolfunctions.Therearethreecontrolsthatwillbeshownwhenthispropertyevaluatestoatruevalue:

Editbutton:Thiswillbeshownwhenthecomponentisindisplaymode,anditwillswitchthecomponenttoeditmodeusingtheeditModeflag.Savebutton:Thiswillbeshownonlyifthecomponentisineditmode.Thiscontrolwillsavethechangesappliedwithinthecurrenteditmodeandswitchthecomponentbacktodisplaymode.Cancelbutton:Thisisthesameasthesavebutton,andthiscontrolisshownonlywhenthecomponentisineditmode.Ifactivated,thecomponentwillswitchbacktodisplaymode,revertinganychangesthatyoumayhavemade.

Besidesourinputproperties,wealsoneedsomeoutputpropertiestonotifytheouterworldaboutthechangeswithinoureditor.Thefollowingpieceofcodehelpsusdothis:

@Output()editSaved=newEventEmitter();

@Output()editableInput=newEventEmitter();

TheeditSavedeventwillbeemittedoncetheeditedcontentissavedusingthesavebuttoncontrol.Also,it'llbebetterifaneventisemitteduponeveryinputchangewithinoureditablecontentelement.Forthis,weusedtheeditableInputoutputproperty.

Oureditorcomponentworksinasimpleway.Ifthecomponentisineditmode,itshowsanelementthatcanbeedited.However,oncetheeditorswitchesbacktodisplaymode,weseeadifferentelementthatcannotbeedited.ThevisibilityiscontrolledwiththemodifierclasssetbythehostelementpropertybindingtotheeditModeflag.

Angularhasnocontroloverthecontentwithinoureditableelement.WecontrolthiscontentmanuallybyusingnativeDOMoperations.Let'slookathowwedidthis.Firstofall,weneededtousedelegatestoaccesstheelement,sincewe'remostlikelygoingtochangehowwewillreadandwritetoandfromtheeditableelement.Weusedthefollowingtodothis:

getEditableContent(){

returnthis.editableContentElement.nativeElement.textContent;

}

setEditableContent(content){

www.EBooksWorld.ir

Page 143: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

this.editableContentElement.nativeElement.textContent=

content;

}

Tip

NotethatweusedthenativeElementpropertyonoureditableContentElementfield,previouslysetbythe@ViewChilddecorator.

AngulardoesnotdirectlyprovideuswithaDOMelementreferencebutawrapperobjectofthetypeElementRef.It'sbasicallyawrapperaroundthenativeDOMelement,holdingadditionalinformationthatisrelevanttoAngular.

UsingthenativeElementaccessor,wecanobtainareferencetotheunderlyingDOMelement.

Note

TheElementRefwrapperplaysanimportantpartinAngular'splatform-agnosticarchitecture.ItallowsyoutorunAngularindifferentenvironments(forexample,nativemobile,webworkers,orothers).It'spartofanabstractionlayerbetweenthecomponentsandtheirviews.

Wealsoneededawaytosetthecontentoftheeditableelementbasedontheinputthatwewouldreceivefromthecontentinputproperty.WecouldusethelifecyclehookOnInit,whichwillbecalledonlyaftertheinputpropertiesarecheckeduponcomponentinitialization.However,thislifecyclehookfiresonlyonceaftertheinitialization,andweneededawaythatwouldhavehelpedusreacttosubsequentinputchangesofthecontentproperty.Havealookatthefollowingcodesnippet:

ngOnChanges(){

if(this.editableContentElement&&this.content){

this.setEditableContent(this.content);

}

}

TheOnChangeslifecyclehookisexactlywhatweneededhere.Withthis,onceachangeinthecontentinputpropertyisdetected(thisalsoincludesthefirstchangeaftertheinitialization),wecanreflectthechangedcontentontooureditableelement.

Nowwehavealreadyimplementedthereflectionofthecomponentcontentinputpropertyontotheeditablefield.Butwhatabouttheoppositedirection?Weneedtofindawaytoreflectthechangesinoureditableelementontoourcomponentcontentproperty.That'salsocloselyrelatedtotheactionsperformedonthecomponentusingtheavailablecontrolswithineditmode,whichareasfollows:

Inthesaveoperation:Here,wereflecttheeditedcontentfromtheeditableelementbacktothecomponent'scontentproperty.Inthecanceloperation:Here,weignorewhathasbeeneditedbytheuserwithintheeditableelementandsetitscontentbacktothevalueinthecomponent'scontent

www.EBooksWorld.ir

Page 144: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

property:

Let'slookatthecodeforthosetwooperations:

save(){

this.editSaved.next(this.getEditableContent());

this.setEditableContent(this.content);

this.editMode=false;

}

cancel(){

this.setEditableContent(this.content);

this.editableInput.next(this.getEditableContent());

this.editMode=false;

}

Inadditiontothehighlightedcode,whichshowsthereflectionbetweenthecomponent'scontentpropertyandtheeditableelement,weemittedcertaineventsthatwouldhelpusnotifytheoutsideworldaboutthechanges.Inboththeoperations,wesettheeditModeflagtofalseaftercompletion.Thisensuresthatoureditorwillswitchtodisplaymodeafteranyoneoftheoperationsiscompleted.

Theeditmethodwillbecalledfromtheeditcontrolbuttonwhenthecomponentisindisplaymode.Theonlythingitdoesisthatitswitchesthecomponentbacktoeditmode:

edit(){

this.editMode=true;

}

Whateverwe'vediscussedthusfarinrelationtothecodeisgoodenoughforustosetupafullyfunctionalcomponent.However,thelastpartofthecode,whichwehaven'tdiscussedyet,relatestoensuringbetteraccessibilityofoureditor.Sinceoureditorcomponentisabitlargerthantheeditableelement,wealsowanttomakesurethataclickanywhereinsidetheeditorcomponentwillcausetheeditableelementtobefocused.Thefollowingcodemakesthishappen:

@HostListener('click')

focusEditableContent(){

if(this.editMode){

this.editableContentElement.nativeElement.focus();

}

}

Usingthe@HostListenerdecorator,weregisteredaneventbindingonourcomponentelementthatcalledthefocusEditableContentmethod.Insidethismethod,weusedthereferencetotheeditableDOMelementandtriggeredafocus.

Let'slookatthetemplateofourcomponentthatislocatedwithintheeditor.htmlfileinordertoseehowwecouldinteractwiththelogicwithinourcomponent:

<div(input)="onInput($event)"

www.EBooksWorld.ir

Page 145: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

class="editor__editable-content"

contenteditable="true"

#editableContentElement></div>

<divclass="editor__output">{{content}}</div>

<div*ngIf="showControls&&!editMode"class="editor__controls">

<button(click)="edit()"class="editor__icon-edit"></button>

</div>

<div*ngIf="showControls&&editMode"class="editor__controls">

<button(click)="save()"class="editor__icon-save"></button>

<button(click)="cancel()"class="editor__icon-cancel"></button>

</div>

Thelogicwithintheeditorcomponenttemplateisquitestraightforward.Ifyou'vebeenfollowingthecomponentcode,you'llnowbeabletoidentifythedifferentelementsthatcomposethiscomponent'sview.

Thefirstelementwiththeeditor__editable-contentclassisoureditableelementthathasthecontenteditableattribute.Theuserwillbeabletotypeintothiselementwhentheeditorisineditmode.It'simportanttonotethatwe'veannotateditwithalocalviewvariablereference,#editableContentElement,whichwe'reusinginourviewchildqueries.

Thesecondelementwiththeeditor__outputclassisonlytodisplaytheeditorcontentandisonlyvisiblewhentheeditorisindisplaymode.ThevisibilityofboththeelementsiscontrolledusingCSS,basedontheeditor--edit-modemodifierclass,which,ifyourecallfromthecomponentclasscode,issetthroughhostpropertybindingbasedontheeditModeproperty.

ThethreecontrolbuttonsareshownusingtheNgIfdirectiveconditionally.TheshowControlsinputpropertyneedstobetrue,anddependingontheeditModeflag,thescreenwilleithershowtheeditbuttonorthesaveandthecancelbutton:

Screenshotofoureditorcomponentinaction

www.EBooksWorld.ir

Page 146: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

RecapWithinthisbuildingblock,wehavecreatedanin-placeeditorwidget,whichwecanusetograbuserinputforanycontentwithinourapplication.Itallowsustoprovidetheuserwithcontextualeditingcapabilities,whichwillresultinagreatuserexperience.

Wehavealsolearnedaboutthefollowingtopics:

1. UsingcontenteditableHTML5attributetoenablein-placeediting.2. Using@[email protected]. UsingtheElementRefdependencytoperformnativeDOMoperations.4. Implementingthelogic,usingtheOnChangelifecyclehook,toreflectdatabetween

AngularandthecontentthatisnotinimmediatecontrolofAngular.

www.EBooksWorld.ir

Page 147: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

BuildingacommentingsystemIntheprevioustopic,wecreatedaneditorcomponentthatwillsupportusersineditingcontentwithinourapplication.Here,we'regoingtocreateacommentingsystemthatwillenableuserstowritecommentsinvariousareasofourapplication.Thecommentingsystemwilluseoureditorcomponenttomakecommentseditable,andtherebyhelpuserscreatenewcomments:

Anillustrationofthecomponentsub-treeofacommentingsystem

Theprecedingdiagramillustratesthearchitectureofthecomponenttreewithinthecommentingsystemthatweareabouttocreate.

TheCommentscomponentwillberesponsibleforlistingalltheexistingcomments,aswellascreatingnewcomments.

www.EBooksWorld.ir

Page 148: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

EachcommentitselfisencapsulatedintoaCommentcomponent.Commentcomponentsthemselvesuseaneditorthatenablesuserstoeditcommentsoncetheyarecreated.

TheEditorcomponent,whichwebuiltintheprevioustopic,isusedbytheCommentcomponentdirectly,toprovideaninputcontrolforaddingnewcomments.Thisallowsustoreusethefunctionalityofoureditorcomponenttocaptureuserinput.

TheEditorcomponentemitsaneditSavedeventonceeditablecontentissavedusingthecontrolbuttonsoftheeditor.IntheCommentcomponent,wewillcapturetheseeventsandpropagateaneweventupwardtoourCommentscomponent.There,wewilldothenecessaryupdatesbutthenagainemitaneweventtonotifyourparentaboutthechange.Inacompositionofcomponents,eachcomponentwillreactonchangeanddelegatetotheparentcomponentifnecessary.

www.EBooksWorld.ir

Page 149: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

BuildingthecommentcomponentLet'sstartbuildingourcommentingsystembyfleshingouttheCommentcomponentfirst.Inadditiontothecommentitself,we'dliketodisplaytheuser'sprofilewhocommented,andofcourse,thetimeofthecomment.

Todisplaythetime,wewillmakeuseofrelativetimeformatting,asthiswillgiveourusersabetterfeeloftime.Relativetimeformattingdisplaystimestampsintheformat"5minutesago"or"1monthago",incontrasttoabsolutetimestamps,suchas"25.12.201518:00".UsingtheMoment.jslibrary,we'llcreateapipethatwecanusewithincomponenttemplatestoconverttimestampsanddatesintorelativetimeintervals.

Let'screateanewpipewithinanewfoldernamedpipes.Thepipeneedstobecreatedwithinafilenamedfrom-now.js,whichiscreatedunderthepipesfolder:

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

//WeusetheMoment.jslibrarytoconvertdatestorelativetimes

importMomentfrom'moment';

@Pipe({

//Specifyingthenametobeusedwithintemplates

name:'fromNow'

})

//Ourpipewilltransformdatesandtimestampstorelativetimes

//usingMoment.js

exportclassFromNowPipe{

//Thetransformmethodwillbecalledwhenthepipeisused

//withinatemplate

transform(value){

if(value&&(valueinstanceofDate||

typeofvalue==='number')){

returnnewMoment(value).fromNow();

}

}

}

Thispipecannowbeusedwithinthetemplatesofcomponentstoformattimestampsanddatesintorelativetimeintervals.

Let'susethispipeandtheEditorcomponentwecreatedintheprevioustopictocreateourCommentcomponent.Withinafilenamedcomment.html,whichislocatedwithinanewcommentfolderinthecommentsfolder,we'llcreatethetemplateforourCommentcomponent:

<divclass="comment__l-meta">

<divclass="comment__user-picture">

<img[attr.src]="user.pictureDataUri"src="">

</div>

<divclass="comment__user-name">{{user.name}}</div>

<divclass="comment__time">

{{time|fromNow}}

</div>

www.EBooksWorld.ir

Page 150: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

</div>

<divclass="comment__l-main">

<divclass="comment__message">

<ngc-editor[content]="content"

[showControls]="true"

(editSaved)="onContentSaved($event)">

</ngc-editor>

</div>

</div>

Fromtheuserobject,wewillgettheuser'sprofileimageaswellastheusername.Todisplaythetimeofthecommentinarelativeformat,we'llusethefromNowpipethatwecreatedearlier.

Finally,wewillmakeuseofthein-placeeditorcomponenttodisplaythecontentofthecommentandmakeiteditableatthesametime.Wewillbindthecommentcontentpropertytothecontentinputpropertyoftheeditor.Atthesametime,wewilllistenfortheeditSavedeventoftheeditorandcalltheonContentSavedmethodonourcommentcomponentclass.Ifyoulookatourcomponentcodeagain,you'llnoticethatwearere-emittingtheeventwithinthemethodsothattheoutsideworldisalsonotifiedaboutthechangeinthecomment.

Let'stakealookatthecomponentclassthatwewillcreateinafilenamedcomment.js:

import{Component,Input,Output,ViewEncapsulation,EventEmitter}from

'@angular/core';

import{Editor}from'../../ui/editor/editor';

importtemplatefrom'./comment.html!text';

//WeuseourfromNowpipethatconvertstimestampstorelative

//times

import{FromNowPipe}from'../../pipes/from-now';

@Component({

selector:'ngc-comment',

host:{

class:'comment'

},

template,

encapsulation:ViewEncapsulation.None,

directives:[Editor],

pipes:[FromNowPipe]

})

exportclassComment{

//Thetimeofthecommentasatimestamp

@Input()time;

//Theuserobjectoftheuserwhocreatedthecomment

@Input()user;

//Thecommentcontent

@Input()content;

//Ifacommentwaseditedthiseventwillbeemitted

@Output()commentEdited=newEventEmitter();

onContentSaved(content){

this.commentEdited.next(content);

}

}

www.EBooksWorld.ir

Page 151: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Thecomponentcodeisprettystraightforward.Theonlynoticeabledifferencetoothercomponentswe'vecreatedsofaristhepipespropertywithinthecomponent'sannotation.Here,wespecifythatwe'dliketousetheFromNowPipeclassthatwe'vejustcreated.Pipesalwaysneedtobedeclaredwithinthecomponent;otherwise,theycan'tbeusedwithinthecomponent'stemplate.

Asinput,weexpectauserobjectthatispassedalongwiththeuserinputproperty.Thecontentinputpropertyshouldbefilledwiththeactualcommentasastring,whilethetimeinputpropertyshouldbesettoatimestampthatreflectstheactualtimeofthecomment.

WealsohaveanoutputpropertycalledcommentEdited,whichwewillusetonotifythechangesonthecomment.TheonEditSavedmethodwillbecalledbytheeventbindingonourEditorcomponent,whichwillthenemitaneventusingthecommentEditedoutputproperty.

www.EBooksWorld.ir

Page 152: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

BuildingthecommentscomponentWenowhaveallthecomponentsreadyinordertofinishbuildingourcommentingsystem.ThelastmissingpieceofthepuzzleistheCommentscomponent,whichwilllistallthecommentsandprovideaneditortocreatenewcomments.

First,let'stakealookatthetemplateofourCommentscomponentthatwewillcreateinafilenamedcomments.htmlwithinafoldernamedcomments:

<divclass="comments__title">Addnewcomment</div>

<divclass="comments__add-comment-section">

<divclass="comments__add-comment-box">

<ngc-editor[editMode]="true"

[showControls]="false"></ngc-editor>

</div>

<button(click)="addNewComment()"

class="button">Addcomment</button>

</div>

<div*ngIf="comments?.length>0">

<divclass="comments__title">Allcomments</div>

<ulclass="comments__list">

<li*ngFor="letcommentofcomments">

<ngc-comment[content]="comment.content"

[time]="comment.time"

[user]="comment.user"

(commentEdited)="onCommentEdited(comment,$event)">

</ngc-comment>

</li>

</ul>

</div>

YoucanseethedirectusageofanEditorcomponentwithinthecomponent'stemplate.Weareusingthisin-placeeditortoprovideaninputcomponenttocreatenewcomments.Wecouldalsouseatextareahere,butwe'vedecidedtoreuseourEditorcomponent.WewillsettheeditModepropertytotruesoitwillbeinitializedineditmode.WewillalsosettheshowControlsinputtofalsebecausewedon'twanttheeditortobecomeautonomous.Wewillonlyuseitsin-placeeditingcapabilities,butcontrolitfromourCommentscomponent.

Toaddanewcomment,wewilluseabuttonthathasaclickeventbinding,whichcallstheaddNewCommentmethodonourcomponentclass.

Belowthesectionwhereuserscanaddnewcomments,wewillcreateanothersectionthatwilllistalltheexistingcomments.Ifnocommentsexist,wesimplydon'trenderthesection.WiththehelpoftheNgFordirective,wecoulddisplayalltheexistingcommentsandcreateaCommentcomponentforeachrepetition.WewillbindallthecommentdatapropertiestoourCommentcomponentandalsoaddaneventbindingtohandleupdatedcomments.

Let'screatethecomponentclasswithinanewfilenamedcomments.jsinthecommentsfolder:

www.EBooksWorld.ir

Page 153: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

import{Component,Inject,Input,Output,ViewEncapsulation,ViewChild,

EventEmitter}from'@angular/core';

importtemplatefrom'./comments.html!text';

import{Editor}from'../ui/editor/editor';

import{Comment}from'./comment/comment';

import{UserService}from'../user/user-service/user-service';

@Component({

selector:'ngc-comments',

host:{

class:'comments'

},

template,

encapsulation:ViewEncapsulation.None,

directives:[Comment,Editor]

})

exportclassComments{

//Alistofcommentobjects

@Input()comments;

//Eventwhenthelistofcommentshavebeenupdated

@Output()commentsUpdated=newEventEmitter();

//Weareusinganeditorforaddingnewcommentsandcontrolit

//directlyusingareference

@ViewChild(Editor)newCommentEditor;

//We'reusingtheuserservicetoobtainthecurrentlylogged

//inuser

constructor(@Inject(UserService)userService){

this.userService=userService;

}

//Weuseinputchangetrackingtopreventdealingwith

//undefinedcommentlist

ngOnChanges(changes){

if(changes.comments&&

changes.comments.currentValue===undefined){

this.comments=[];

}

}

//AddinganewcommentfromthenewCommentContentfieldthatis

//boundtotheeditorcontent

addNewComment(){

constcomments=this.comments.slice();

comments.splice(0,0,{

user:this.userService.currentUser,

time:+newDate(),

content:this.newCommentEditor.getEditableContent()

});

//Emiteventsotheupdatedcommentlistcanbepersisted

//outsidethecomponent

this.commentsUpdated.next(comments);

//Weresetthecontentoftheeditor

this.newCommentEditor.setEditableContent('');

}

www.EBooksWorld.ir

Page 154: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

//Thismethoddealswitheditedcomments

onCommentEdited(comment,content){

constcomments=this.comments.slice();

//Ifthecommentwaseditedwithezerolengthcontent,we

//willdeletethecommentfromthelist

if(content.length===0){

comments.splice(comments.indexOf(comment),1);

}else{

//Otherwisewe'rereplacingtheexistingcomment

comments.splice(comments.indexOf(comment),1,{

user:comment.user,

time:comment.time,

content

});

}

//Emiteventsotheupdatedcommentlistcanbepersisted

//outsidethecomponent

this.commentsUpdated.next(comments);

}

}

Let'sgothroughindividualcodepartsagainanddiscusswhateachofthemdoes.First,wedeclaredaninputpropertynamedcommentsinourcomponentclass:

@Input()comments;

Thecommentsinputpropertyisalistofcommentobjectsthatcontainsallofthedataassociatedwiththecomments.Thisincludestheuserwhoauthoredthecommentandthetimestamp,aswellasthecontentofthecomment.

Wealsoneedtobeabletoemitaneventonceacommentisaddedoranexistingcommentismodified.Forthispurpose,weusedanoutputpropertynamedcommentsUpdates:

@Output()commentsUpdated=newEventEmitter();

Onceanewcommentisaddedoranexistingoneismodified,wewillemitaneventfromthisoutputpropertywiththeupdatedlistofcomments.

TheEditorcomponentwe'regoingtousetoaddnewcommentswillnothaveitsowncontrolbuttons.WewillusetheshowControlsinputpropertytodisablethem.Instead,wewillcontroltheeditorfromourCommentscomponentdirectly.Therefore,weneedawaytocommunicatewiththeEditorcomponentwithinourcomponentclass.

[email protected],thistime,wedidnotreferenceaDOMelement,whichcontainsalocalviewvariablereference.Wedirectlypassedourcomponenttypeclasstothedecorator.AngularwillsearchforanyEditorcomponentswithinthecommentsviewandprovideuswithareferencetotheinstanceoftheeditor.Thisisshowninthefollowinglineofcode:

@ViewChild(Editor)newCommentEditor;

www.EBooksWorld.ir

Page 155: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

SincetheCommentscomponentonlyhostsoneeditordirectlywithinthecomponenttemplate,wecanusethe@ViewChildannotationtoobtainareferencetoit.Usingthisreference,wecandirectlyinteractwiththechildcomponent.ThiswillallowustocontroltheeditordirectlyfromourCommentscomponent.

Let'smoveontothenextpartofthecode,whichistheCommentscomponentconstructor.Theonlythingwe'vedonehereisinjectauserservicethatwillprovideuswithawaytoobtaininformationofthecurrentlylogged-inuser.Asofnow,thisfunctionalityisonlymocked,andwewillreceiveinformationofadummyuser.WeneedthisinformationintheCommentscomponent,sinceweneedtoknowwhichuserhasactuallyenteredanewcomment:

constructor(@Inject(UserService)userService){

this.userService=userService;

}

Inthenextpartofthecode,wecontrolledhowweshouldreacttothechangesofthecommentsinputproperty.Actually,wewouldneverwantthelistofcommentstoremainundefined.Itshouldbeanemptylistincasetherearenocomments,buttheinputpropertycommentsshouldneverbeundefined.WecontrolledthisbyusingtheOnChangelifecyclehookandoverridingourcommentspropertyifitwassettoundefinedfromoutside:

ngOnChanges(changes){

if(changes.comments&&

changes.comments.currentValue===undefined){

this.comments=[];

}

}

Thissmallchangemakestheinternalhandlingofourcommentdatamuchcleaner.Wedon'tneedadditionalcheckswhenworkingforarraytransformationfunctions,andwecanalwaystreatthecommentspropertyasanarray.

SincetheCommentscomponentisalsoresponsibleforhandlingthelogicthatdealswiththeprocessofaddingnewcomments,weneededamethodthatcouldimplementthisrequirement.Inrelationtothis,weusedsomeimmutablepracticeswelearnedaboutinthepreviouschapter:

addNewComment(){

constcomments=this.comments.slice();

comments.splice(0,0,{

user:this.userService.currentUser,

time:+newDate(),

content:this.newCommentEditor.getEditableContent()

});

this.commentsUpdated.next(comments);

this.newCommentEditor.setEditableContent('');

}

Thereareafewkeyaspectsinthispartofthecode.ThismethodwillbecalledfromourcomponentviewwhentheAddcommentbuttonisclicked.Thisiswhentheuserwillhave

www.EBooksWorld.ir

Page 156: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

alreadyenteredsometextintotheeditorandanewcommentwillhavebeencreated.

First,wewillusetheuserservicethatweinjectedwithintheconstructortoobtaininformationrelatedtothecurrentlylogged-inuser.ThecontentofthenewlycreatedcommentwillbeobtaineddirectlyfromtheEditorcomponentwesetupusingthe@ViewChildannotation.And,thegetEditableContentmethodwillallowustoreceivethecontentoftheeditableelementwithinthein-placeeditor.

Thenextthingwewantedtodowastocommunicateanupdateofthecommentlistwiththeoutsideworld.WeusedthecommentsUpdatedoutputpropertytoemitaneventwiththeupdatedcommentlist.

Finally,wewantedtocleartheeditorusedtoaddnewcomments.Asthein-placeeditorintheviewoftheCommentscomponentisonlyusedtoaddnewcomments,wecanalwaysclearitafteracommentisadded.Thiswillgivetheusertheimpressionthathiscommenthasbeenmovedfromtheeditorintothelistofcomments.Then,onceagain,wecanaccesstheEditorcomponentdirectlyusingournewCommentEditorpropertyandcallthesetEditableContentmethodwithanemptystringtocleartheeditor.Andthisiswhatwe'vedonehere.

OurCommentscomponentwillholdthelistofallthecomments,anditsviewwillcreateaCommentcomponentforeachcommentinthatlist.EachCommentcomponentwilluseanEditorcomponenttoprovidein-placeeditingofitscontent.Theseeditorsworkautonomouslyusingtheirowncontrols,andtheyemitaneventifthecontentischangedoralteredinanyway.Totakecareofthis,weneedtore-emitthiseventwiththenamecommentEditedfromtheCommentcomponent.NowweonlyneedtocatchthiseventwithinourCommentscomponentinordertoupdatethelistofcommentswiththechanges.Thisisillustratedinthefollowingpartofthecode:

onCommentEdited(comment,content){

constcomments=this.comments.slice();

if(content.length===0){

comments.splice(comments.indexOf(comment),1);

}else{

comments.splice(comments.indexOf(comment),1,{

user:comment.user,

time:comment.time,

content

});

}

this.commentsUpdated.next(comments);

}

ThismethodwillbecalledforeachindividualCommentcomponentthatisrepeatedusingtheNgFordirective.Fromtheview,wepassareferencetothecommentobjectconcerned,aswellastheeditedcontentwewouldreceivefromtheCommentcomponentevent.

Thecommentobjectwillonlybeusedtodeterminethepositionoftheupdatedcommentwithinthecommentlist.Ifthenewcommentcontentisempty,wewillremovethecomment

www.EBooksWorld.ir

Page 157: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

fromthelist.Otherwise,wewilljustcreateacopyofthepreviouscommentobject,changethecontentwiththeneweditedcontent,andreplacetheoldcommentobjectinthelistwiththecopy.

Finally,sincewewantedtocommunicatethechangeinthecommentlist,weemittedaneventusingthecommentUpdatedoutputproperty.

Withthis,wehavecompletedourcommentingsystem,andnowit'stimetomakeuseofit.Wealreadyhaveanemptytabpreparedforourprojectcomments,andthisisgoingtobethespotwherewewilladdcommentingcapabilitiesusingourcommentingsystem.

First,let'samendourProjectcomponenttemplate,project/project.html,toincludethecommentingsystem:

...

<ngc-tabs>

<ngc-tabname="Tasks">...</ngc-tab>

<ngc-tabname="Comments">

<ngc-comments[comments]="comments"

(commentsUpdated)="updateComments($event)">

</ngc-comments>

</ngc-tab>

<ngc-tabname="Activities"></ngc-tab>

</ngc-tabs>

Thisisaseasyasitgets.Sincewearepayingattentiontoacleancomponentarchitecture,theinclusionofourcommentingsystemreallyworkslikeabreeze.Theonlythingwenowneedtoensureisthatweprovideapropertyonourprojectwithalistofcomments.WealsoneedawaytoreacttochangesifcommentsareupdatedwithinourCommentscomponent.Forthispurpose,wewillcreateanupdateCommentsmethod.

Let'slookatthecomponentclasschangeswithintheproject/project.jsfile:

exportclassProject{

...

@Input()comments;

...

updateComments(comments){

this.projectUpdated.next({

comments

});

}

}

SincewearealreadydealingwithprojectupdatesinageneralwayandourProjectcomponentisemittingdirectlytoourAppcomponent,whereprojectsdatawillbepersisted,theonlythingweneedtoimplementisanadditionalinputproperty,aswellasamethodtohandlecommentupdates:

www.EBooksWorld.ir

Page 158: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Screenshotofthecommentingsystemintegratedwithinourprojectcomponent

www.EBooksWorld.ir

Page 159: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

RecapWithinthistopic,wehavesuccessfullycreatedafull-fledgedcommentingsystemthatcanbeplacedinvariousareasofourapplicationtoenablecommenting.Userscaninteractwithin-placeeditorstoeditthecontentincomments,whichgivesthemagreatuserexperience.

Whilewritingthecodeforourcommentingsystem,welearnedaboutthefollowingtopics:

1. Implementingasimplepipeusingthe@PipeannotationandtheMoment.jslibrarytoproviderelativetimeformatting

2. UsingtheOnChangeslifecyclehooktopreventunwantedorinvalidvalueswithininputproperties

3. Using@ViewChildtoobtainareferencetothecomponentswithinthecomponentsub-treeinordertoestablishdirectcommunication

4. ReusingtheEditorcomponentasaninputfieldreplacementandasanautonomousin-placeeditorwithintheCommentcomponent

www.EBooksWorld.ir

Page 160: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

SummaryInthischapter,wecreatedasimplein-placeeditorthatwecanuseformakingcontenteditablewithinourapplication.Goingforward,wecanusetheEditorcomponentwhereverwewanttomakecontenteditableforourusers.Theywillnothavetojumpintonastydialogsorseparateconfigurationpages,butwillbeinapositiontoeditdirectly,aspertheircurrentcontext.Thisisagreattoolforenhancingtheuserexperienceforourusers.

BesidesourshinynewEditorcomponent,wecreatedawholecommentingsystemthatcanbeeasilyincludedinareasofourapplicationwherewe'dliketoprovidecommentingcapabilities.WehavenowincludedthecommentingsystemwithinourProjectcomponent,anduserscannowcommentonprojectsbynavigatingtotheCommentstabontheprojectdetails.

Inthenextchapter,we'llusethecomponent-basedrouterofAngulartomakeourapplicationnavigable.

www.EBooksWorld.ir

Page 161: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Chapter5.Component-BasedRoutingRoutingisanintegralpartoftoday'sfrontendapplications.Ingeneral,arouterservestwomainpurposes:

Makingyourapplicationnavigablesothatuserscanusetheirbrowser'sbackbuttonandstoreandsharelinkswithintheapplicationOffloadpartsoftheapplicationcompositionsothattheroutertakesresponsibilitytocomposeyourapplication,basedonroutesandrouteparameters

TherouterthatcomeswithAngularsupportsmanydifferentuse-cases,anditcomeswithaneasy-to-useAPI.ThissupportschildroutersthataresimilartotheAngularUI-Routernestedstates,Ember.jsnestedroutes,orchildroutersintheDurandalframework.Tiedtothecomponenttree,thisalsomakesuseofitsowntreestructuretostorestatesandtoresolverequestedURLs.

Inthischapter,werefactorourcodetousethecomponent-basedrouterofAngular.Wewilllookintothecoreelementsoftherouterandhowtousethemtoenableroutinginourapplication.

Thefollowingtopicswillbecoveredinthischapter:

AnintroductiontotheAngularrouterAnoverviewoftherefactoringtoenabletherouterinourapplicationCompositionbytemplate,compositionbyrouting,andhowtomixthemUsingtheRoutesdecoratortoconfigureroutesandchildroutesUsingtheOnActivaterouterlifecyclehooktoobtainrouteparametersUsingtheRouterOutletdirectivetocreateinsertionpointsthatarecontrolledbytherouterUsingtheRouterLinkdirectiveandtherouterDSLtocreatenavigationlinksQueryingfortheRouterLinkdirectivesusingthe@ChildViewdecoratorinordertoobtainthelink'sactivestate

www.EBooksWorld.ir

Page 162: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

AnintroductiontotheAngularrouterTherouterinAngulariscloselycoupledtoourcomponenttree.ThedesignoftheAngularrouterisbuiltontheassumptionthatacomponenttreeisdirectlyrelatedtoourURLstructure.Thisiscertainlytrueformostofthecases.IfwelookatacomponentB,whichisnestedwithinacomponentA,theURLtorepresentourlocationwouldverylikelybe/a/b.

Inordertospecifythelocationinourtemplatewherewe'dliketoenabletheroutertoinstantiatecomponents,wecanuseso-calledoutlets.Simplybyincludinga<router-outlet>element,wecanmakeuseoftheRouterOutletdirectivetomarktherouterinsertionpointinourtemplate.

Basedonsomerouteconfigurationthatwecanplaceonourcomponent,therouterthendecideswhichcomponentsneedtobeinstantiatedandplacedintotherouteroutlets.Routescanalsobeparameterized,andwecanaccesstheseparameterswithintheinstantiatedcomponents.

Basedonourcomponenttreeandtherouteconfigurationsoncomponentsinthistree,wecanbuildahierarchicalroutinganddecouplechildroutesfromtheirparentroutes.Suchnestedroutesmakeitpossibletospecifyrouteconfigurationonmultiplelayersinourcomponenttreeandreuseparentcomponentsformultiplechildroutes.

www.EBooksWorld.ir

Page 163: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Routerhierarchyestablishedthroughacomponenttree

Let'slookattheelementsoftherouteragaininmoredetail:

Routeconfiguration:Therouteconfigurationisplacedatcomponentlevel,anditcontainsthedifferentroutespossibleforthislevelinthecomponenttree.Byplacingmultiplerouteconfigurationsondifferentcomponentsinthecomponenttree,wecanbuilddecouplednestedrouteseasily.Routeroutlets:Outletsarethelocationsincomponentsthatwillbemanagedbytherouter.Instantiatedcomponentsthatarebasedontherouteconfigurationwillbeplacedintotheseoutlets.Routerlink:ThesearelinksbuiltwithaDSLstylenotationthatenablethedevelopertobuildcomplexlinksthroughtheroutingtree.

www.EBooksWorld.ir

Page 164: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

CompositionbyroutingSofar,weachievedcompositionbyincludingsubcomponentsincomponenttemplates.However,we'dnowliketogivethecontroltotheroutertodecidewhichcomponentshouldbeincludedandwhere.

Thefollowingillustrationprovidesanoverviewofthecomponentarchitectureofourapplication,whichwe'regoingtoenabletoroute:

Acomponenttreedisplayingroutingcomponents(solidline)androuteroutlets

TheProjectcomponentisnownotdirectlyincludedwithourAppcomponent.Instead,weusearouteroutletinthetemplateofourAppcomponent.Thisway,wecangivecontroltotherouter,andletitdecidewhichcomponentshouldbeplacedintotheoutlet.TheAppcomponent'srouterconfigurationwillcontainalltop-levelroutes.Inthecurrentapplication,weonlyhavetheProjectcomponentasasecondary-levelcomponent,butthiswillchangeinfurtherchapters.

TheProjectcomponentcontainschildrouteconfigurationtonavigatetothetasksandcommentsview.However,itdoesnotdirectlycontainarouteroutlet.WeusetheTabs

www.EBooksWorld.ir

Page 165: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

componentasanavigationelementforanysubviews.Asaresult,we'llplacetherouteroutletintotheTabscomponentandincludethecomponentdirectlyinthetemplateoftheProjectcomponent.

www.EBooksWorld.ir

Page 166: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

RouterversustemplatecompositionThecompositionthatwedealtwithsofarwaspurelybasedoninstantiationviatemplateinclusion.Weusedinputandoutputpropertiestodecoupleandencapsulatecomponents,andfollowednicereusablepatterns.

Withtherouter,wefaceaproblemthathasnotyetbeensolvedbyAngularandrequiresthatwefindourownsolution.Aswegivecontroltotheroutertoinstantiateandinsertcomponentsintoourcomponenttree,wecan'tcontrolanybindingsonourinstantiatedcomponent.Whilewepreviouslyreliedonthecleandecouplingofcomponentsusinginputandoutputproperties,wecannolongerdothis.Theonlythingthatarouterprovidesusarerouteparametersthatmayhavebeensetalongtheactivatedroute.

Thisputsusinquiteanastysituation.Basically,weneedtodecidebetweentwodesignswhenwritingcomponents,whichareasfollows:

Weuseagivencomponentpurelyintemplatecompositionand,therefore,relyoninputandoutputpropertiesasthegluebetweentheparentcomponentWeuseacomponentinstantiatedbytherouterandrelyoninput-providedviewrouteparametersanddon'trequirecommunicationwiththeparentcomponent

Well,bothoftheprecedingdesignapproachesaren'tverynice,arethey?Inanidealworld,wewouldnotneedtoapplyanychangestoacomponentwhenweenableitforrouting.Theroutershouldjustenablethecomponentforrouting,butitshouldnotrequireanychangesonthecomponentitself.Unfortunately,there'snoagreementforasolutiontothisproblematthetimeofwritingthisbook.

Aswedon'twanttoloseanycompositioncapabilitiesthatwegainfromrelyingoninputsandoutputsinourTaskListandCommentscomponents,weneedtoafindabettersolutiontoenableroutinginourapplication.

ThefollowingsolutionallowsustoleavetheTaskListandCommentscomponentsuntouchedwhiletheycanstillrelyoninputandoutputproperties.Insteadofexposingthemdirectlytotherouter,wewillbuildwrappercomponentsthatweaddressfromourroutes.Thesewrappersfollowsomemechanicstobridgethisgapbetweentherouterandourcomponents.Thewrappercomponentdealswithanyrouteparametersorroutedatathatmighthavebeensetintheactivatedroute.Theirtemplateshouldonlyincludethecomponentthatiswrappedanditsinputandoutputbindings.TheyhandletherequireddataandfunctionalitytoprovidetheinputandoutputbindingsoftheconcernedcomponentTheymayuseparentcomponentinjectiontoestablishcommunicationwiththeparentandpropagateanyactionthatisrequiredbyemittedevents.Parentcomponentinjectionshouldbeusedwithcautionasitsomewhatbreaksourdecouplingofcomponents.

www.EBooksWorld.ir

Page 167: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

UnderstandingtheroutetreeAngularusestreedatastructurestorepresenttherouterstate.Youcanimaginethateverynavigationinyourapplicationactivatesabranchinthistree.Let'slookatthefollowingexample.

Wehaveanapplicationthatconsistsoffourpossibleroutes:

/:Thisistherootrouteoftheapplication,whichishandledinacomponentcalledA./b/:id:Thisistheroutewherewecanaccessabdetailview,whichishandledinacomponentcalledB.IntheURL,wecanpassanidparameter(thatis,/b/100)./b/:id/c:Thisistheroutewherethebdetailviewhasanothernavigationpossibility,whichrevealsmorespecificdetailsthatwecallc.ThisishandledinaCcomponent./b/:id/d:Thisistheroutewherewecanalsonavigatetoadviewinthebdetailview.Thisishandledbyacomponent,calledD:

Aroutetreeconsistingofanactivebranchofroutesegmentsforanactivatedroute/b/100/d

Let'sassumethatweactivatearouteinourexamplebynavigatingtheURL,/b/100/d.Inthiscase,we'dactivatearoutethatreflectsthestatethatisoutlinedintheprecedingfigure.NotethattheroutesegmentBactuallyconsistsoftwoURLsegments.Thereasonforthisisthat

www.EBooksWorld.ir

Page 168: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

we'vespecifiedthatourrouteBactuallyconsistsofthebidentifierandan:idrouteparameter.

Usingthistreedatastructure,wehaveaperfectabstractiontodealwithnavigationtrees.Wecancomparetrees,checkwhethercertainsegmentsexistinatree,andextractparameterspresentonresolvedroutesegments.

Todemonstratetheuseofroutingtrees,let'stakealookattheOnActivaterouterlifecyclehookthatwecanimplementonournavigablecomponents:

routerOnActivate(currentRouteSegment,

previousRouteSegment,

currentTree,

previousTree)

Whenweimplementthislifecyclehookonourcomponents,wecanrunsomecodeaftertheroutewasactivated.ThecurrentRouteSegmentargumentwillpointtotheRouteSegmentinstancethatwasactivatedonourcomponent.

Let'stakealookatourexampleagainandassumethatwewanttoaccessthe:idparameterintherouterOnActivatehookofourBcomponent:

routerOnActivate(currentRouteSegment){

this.id=currentRouteSegment.getParam('id');

}

UsingthegetParamfunctionontheRouteSegmentinstance,weobtainanyparametersthatareresolvedonthegivensegment.Inourexamplecase,thiswouldreturna100string.

Let'stakealookatamorecomplexexample.Whatifwewanttoaccessthe:idparameterfromtheDcomponentontheddetailview?IntheOnActivatelifecyclehook,we'llreceiveonlytheroutesegmentthatisrelevanttotheDcomponent.ThisonlyconsistsofthedURLsegmentandthisdoesnotincludethe:idparameterfromtheparentroute.WecannowmakeuseoftheRouteTreeinstancetofindtheparentroutesegmentandobtaintheparameterfromthere:

routerOnActivate(currentRouteSegment,

previousRouteSegment,

currentTree){

this.id=currentTree.parent(currentRouteSegment).getParam('id');

}

UsingthecurrentRouteTreeinstance,wecanobtaintheparentofthecurrentroutesegment.Asaresult,we'llreceivetheparentroutesegment(RouteSegmentBintheprecedingfigure)fromwherewecanobtainthe:idparameter.

Asyoucansee,therouterAPIisquiteflexible,anditallowsustoinspectrouteactivityonaveryfinegranularity.Thetreestructuresthatareusedintheroutermakeitpossibletocomparecomplexrouterstatesinourapplicationwithoutbotheringabouttheunderlying

www.EBooksWorld.ir

Page 169: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

complexity.

www.EBooksWorld.ir

Page 170: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

BacktotheroutesAllright,nowit'stimetoimplementroutingforourapplication!Inthefollowingtopics,we'llcreatethefollowingroutesforourapplication:

Routepath Description

/projects/:projectId

ThisroutewillactivatetheProjectcomponentintheoutletofourmainapplicationcomponent.ThisconsistsoftheprojectsURLsegmentaswellasthe:projectIdURLsegmenttospecifytheprojectID.

/projects/:projectId/tasks

ThisroutewillactivatetheTaskListcomponent.WewillcreateaProjectTaskListwrappercomponentinordertodecoupleourTaskListcomponentfromrouting.We'llapplytheproceduredescribedintheprevioussection,Routerversustemplatecomposition.

/projects/:projectId/comments

ThisroutewillactivatetheCommentscomponent.We'llcreateaProjectCommentswrappercomponentinordertodecoupleourCommentscomponentfromrouting.We'llapplytheproceduredescribedintheprevioussection,Routerversustemplatecomposition.

InordertousetherouterofAngular,thefirstthingthatweneedtodoistoaddtherouteprovidertoourapplication.We'lldothisonbootstrapinordertomakesuretherouterprovidersareonlyloadedonce.Let'sopenourboostrap.jsfileandaddthenecessarydependencies:

...

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

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

//Importrouterdependencies

import{HashLocationStrategy,LocationStrategy}from'@angular/common';

import{ROUTER_PROVIDERS}from'@angular/router';

...

bootstrap(App,[

...

ROUTER_PROVIDERS,

provide(LocationStrategy,{

useClass:HashLocationStrategy

})

]);

www.EBooksWorld.ir

Page 171: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Fromtheroutermodule,weimporttheROUTER_PROVIDERSconstantthatcontainsalistofmodulesthatarerequiredtobeexposedasproviderswhenusingtherouter.WealsoimporttheLocationStrategyandHashLocationStrategytypefromthecommonmodulethatneedtobeprovidedmanually.

Usingtheprovidefunction,weprovidetheHashLocationStrategyclassasasubstitutionfortheLocationStrategyabstractclass.Thisway,therouterwillknowwhichstrategytousewhenresolvingURLs.

Thefollowingtwostrategiesexistatthemoment:

HashLocationStrategy:ThiscanbeusedwhentheroutershouldusehashURLs,suchaslocalhost:8080#/child/something.Thislocationstrategymakessenseifyou'reworkinginanenvironmentwheretheHTML5pushstatecan'tbeusedduetobrowserorserverconstraints.ThewholenavigationstatewillbemanagedinthefragmentidentifieroftheURL.PathLocationStrategy:Thisstrategycanbeusedifyou'dliketousetheHTML5pushstatetohandleapplicationURLs.ThismeansthatyourapplicationnavigationbecomestheactualpathoftheURL.Usingtheprecedingexampleofahash-basedURL,thisstrategywouldenablethedirectuseoflocalhost:8080/child/something.AstheinitialrequestswillhittheserverifthestateisencodedinthepathofaURL,you'llneedtoenablethecorrectroutingontheservertomakethisworkproperly.

Afterenablingtherouterforourapplication,wewillneedtomakeourrootcomponentroutable.WecandothisbyincludingarouteconfigurationonourAppcomponent.Let'slookatthenecessarycodetodothis.Weedittheapp.jsfileinourlibfolder:

...

import{Project}from'./project/project';

import{Routes,Route}from'@angular/router';

@Component({

selector:'ngc-app',

...

})

@Routes([

newRoute({path:'projects/:projectId',component:Project})

])

exportclassApp{

...

}

Intheprecedingcode,weimportedtheRoutesdecoratoraswellastheRoutetypefromtheroutermodule.

Inordertoconfigureroutesonourcomponent,wecanusethe@RoutesdecoratorbypassinganarrayofRouteobjectsthatdescribethepossiblechildroutesonthiscomponent.

Let'slookattheavailableoptionsthatwecanpasstotheRouteconstructor:

www.EBooksWorld.ir

Page 172: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Routeproperty Description

path

Thispropertyisrequired.Usingthepath,wecandescribethenavigationURLinthebrowserusingtheroutematcherDSL.Thiscancontainrouteparameterplaceholders.

Someexamplesareasfollows:

Thefollowingroutegetsactivatediftheusernavigatesto/homeinthebrowser:

path:'/home'

Thefollowingroutegetsactivatedwhentheusernavigatestoa/child/somethingURLwheresomethingwillbeavailableasrouteparameterwiththename,id:

path:'/child/:id'

component

Thispropertyisrequired,anditdefineswhichcomponentshouldbeinstantiatedbytherouter.Asalreadyexplainedintheprevioussection,therouterdoesnotallowusheretospecifyanybindingstotheinstantiatedcomponent.

TherouteconfigurationonourAppcomponentcoverstheProjectcomponentbeinginstantiatedontheprojects/:projectIdroutepath.ThismeansthatweuseaprojectIdparameteronthechildroute,whichwillbeavailabletotheProjectcomponent.

WealsoneedtomodifyourAppcomponenttemplateandremovethedirectinclusionoftheProjectcomponentthere.Wenowgivecontroltotheroutertodecidewhichcomponenttodisplay.Forthis,weneedtomakeuseoftheRouterOutletdirectivetoprovideaslotinourtemplatewheretherouterwillinstantiatecomponents.

TheRouterOutletdirectiveispartoftheROUTER_DIRECTIVESconstantthatisexportedbytheroutermodule.Let'simportandaddtheconstanttothedirectiveslistonourcomponent:

...

import{Routes,Route,ROUTER_DIRECTIVES}from'@angular/router';

...

@Component({

selector:'ngc-app',

...

directives:[...,ROUTER_DIRECTIVES],

...

})

...

www.EBooksWorld.ir

Page 173: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

exportclassApp{

...

}

Now,wecanusetheRouterOutletdirectiveinourtemplatetoindicatetheinsertionpositionofinstantiatedcomponentsbytherouter.Let'sopenourAppcomponenttemplatefile,app.html,andmakethenecessarymodifications:

<divclass="app">

...

<divclass="app__l-main">

<router-outlet></router-outlet>

</div>

</div>

ThenextstepistorefactorourProjectcomponentsothatitcanbeusedinrouting.Aswealreadyoutlinedintheprevioussection,theroutercomeswithcertainconstraintswhenitcomestocomponentdesign.FortheProjectcomponent,wedecidetoredesignitinawaysothatwecanonlyuseitwithrouting.Thisisn'tabadthingherebecausewecanexcludethepossibilitythatitwillbereusedsomewhereelseinourapplication.

TheredesignoftheProjectcomponentincludesthefollowingsteps:

1. Gettingridofallinputandoutputpropertiesofthecomponent.2. UsingtheOnActivaterouterlifecyclehooktoobtaintheprojectIdparameterfromthe

activatedroutesegmentoftheAppcomponent.3. ObtainingtheprojectdatadirectlyfromthedatastoreusingtheprojectIdparameter.4. Handlingupdatesontheprojectdatadirectlyonthecomponentinsteadofdelegatingto

theAppcomponent.

Let'smodifytheComponentclasslocatedinlib/project/project.jstoimplementtheprecedingdesignchanges:

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

importtemplatefrom'./project.html!text';

import{Tabs}from'../ui/tabs/tabs';

import{DataProvider}from'../../data-access/data-provider';

import{LiveDocument}from'../../data-access/live-document';

@Component({

selector:'ngc-project',

host:{class:'project'},

template,

encapsulation:ViewEncapsulation.None,

directives:[Tabs]

})

exportclassProject{

constructor(@Inject(DataProvider)dataProvider){

this.dataProvider=dataProvider;

this.tabItems=[

{title:'Tasks',link:['tasks']},

www.EBooksWorld.ir

Page 174: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

{title:'Comments',link:['comments']}

];

}

routerOnActivate(currentRouteSegment){

this.id=currentRouteSegment.getParam('projectId');

this.document=newLiveDocument(this.dataProvider,{

type:'project',

_id:this.id

});

this.document.change.subscribe((data)=>{

this.title=data.title;

this.description=data.description;

this.tasks=data.tasks;

this.comments=data.comments;

});

}

updateTasks(tasks){

this.document.data.tasks=tasks;

this.document.persist();

}

updateComments(comments){

this.document.data.comments=comments;

this.document.persist();

}

ngOnDestroy(){

this.document.unsubscribe();

}

}

Besidesimplementingthesechangesthatarealreadydescribedintheredesigningsteps,wealsomadeuseofanewLiveDocumentutilityclassthatweimportedfromthedata-accessfolder.Thishelpsustokeepourprogrammingreactivewhenwe'reconcernedaboutchangesonasingledataentity.UsingtheLiveDocumentclass,wecanquerythedatabaseforasingleentity,whilethechangepropertyoftheLiveDocumentinstanceisanobservablethatkeepsusnotifiedaboutchangesontheentity.ALiveDocumentinstancealsoexposesthedataoftheentityintoadataproperty,whichcanbeaccesseddirectly.Ifwe'dliketomakeanupdateontheentity,wecanadd,modify,orremovepropertiesonthedataobjectandthenstorethechangesbycallingpersist().

AsourProjectcomponentisnowactivatedbytherouterintheAppcomponent,wecanmakeuseoftheOnActivaterouterlifecyclehookbyimplementingamethod,namedrouterOnActivate.WeusethegetParamfunctionofthecurrentroutesegmenttoobtainthe:projectIdparameteroftheroute.

InthesubscribefunctiononthechangeobservableofourLiveDocumentinstance,weexposetheprojectdatadirectlyontheProjectcomponent.Thissimplifieslateruseintheview.

IntheOnDestroylifecyclehook,wemakesurethatweunsubscribefromthedocumentchange

www.EBooksWorld.ir

Page 175: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

observable.

Now,wecanrelyontheprojectIdrouteparametertobepassedintoourcomponent,whichmakestheProjectcomponentdependontherouter.Wegotridofallinputproperties,andthenwesetthenecessarydatabyqueryingourdatastoreusingtheprojectID.

Now,it'stimetobuildthewrappercomponentsthatwetalkedaboutinordertoroutetoourTaskListandCommentscomponents.

Let'screateanewcomponentcalledProjectTaskList,whichwillserveasawrappertoenabletheTaskListcomponentinrouting.Wewillcreateaproject-task-list.jsfileinthelib/project/project-task-listpath,asfollows:

import{Component,ViewEncapsulation,Inject,forwardRef}from'@angular/core';

importtemplatefrom'./project-task-list.html!text';

import{TaskList}from'../../task-list/task-list';

import{Project}from'../project';

@Component({

selector:'ngc-project-task-list',

...

directives:[TaskList]

})

exportclassProjectTaskList{

constructor(@Inject(forwardRef(()=>Project))project){

this.project=project;

}

updateTasks(tasks){

this.project.updateTasks(tasks);

}

}

Let'salsotakealookatthetemplateintheproject-task-list.htmlfile:

<ngc-task-list[tasks]="project.tasks"

(tasksUpdated)="updateTasks($event)"></ngc-task-list>

WeinjecttheProjectparentcomponentintoourwrappercomponent.Aswecan'trelyonoutputpropertiesanymoretoemitevents,thisistheonlywaytocommunicatewiththeparentProjectcomponent.We'redealingwithacircularreferencehere(ProjectdependsonProjectTaskList,andProjectTaskListdependsonProject),henceweneedtouseaforwardRefhelperfunctiontopreventtheProjecttypeevaluatingtoundefined.

IfwereceiveatasksUpdatedeventinthetemplate,wewillcalltheupdateTasksmethodonourwrappercomponent.Thewrapperthensimplydelegatesthecalltotheprojectcomponent.

Similarly,weusetheprojectdatatoobtainthelistoftasksandcreateabindingtothetasksinputpropertyoftheTaskListcomponent.

www.EBooksWorld.ir

Page 176: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Usingthiswrapperapproachforrouting,we'reabletoleaveourcomponentsunmodifiedwhenenablingthemforrouting.Thisismuchbetterthantheoptiontomakeourtasklistonlyavailablefortherouter.Wewouldlosethefreedomtouseatasklistoutsideofthecontextofaproject,andthenuseitwithpuretemplatecomposition.

FortheCommentscomponent,weperformtheexactsametask,andcreateawrapperonthelib/project/project-commentspath.Besidesdealingwithcommentsinsteadoftasks,thecodelooksexactlythesameaswiththeProjectTaskListwrappercomponent.

Aftercreatingthetwowrappercomponents,wecannowcreatetherouterconfigurationonourProjectcomponent.Let'smodifytheproject/project.jsfiletoenablerouting:

...

import{ProjectTaskList}from'./project-task-list/project-task-list';

import{ProjectComments}from'./project-comments/project-comments';

import{Routes,Route}from'@angular/router';

...

@Component({

selector:'ngc-project',

...

})

@Routes([

newRoute({path:'tasks',component:ProjectTaskList}),

newRoute({path:'comments',component:ProjectComments})

])

exportclassProject{

...

}

Toenablethetasklistandmakecommentsnavigableusingtherouter,wesimplycreatearouterconfigurationthatinstantiatesourwrappercomponents.Wealsospecifythatthetasksrouteshouldbethedefaultrouteifnochildroutewasselected.

www.EBooksWorld.ir

Page 177: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

RoutabletabsOkay,ifyou'vereadthroughthischaptersofar,younowmaywonderwheretherouterwillinstantiatethecomponentsofthechildroutes.We'venotyetincludedarouteroutletintheProjectcomponenttemplatesothattherouterknowswheretoinstantiatecomponents.

Wewon'tincludetheoutletfortheprojectrouterdirectlyintheProjectcomponent.Instead,wewilluseourTabscomponenttotakeoverthisjob.InsteadofusingcontentinsertioninourTabscomponentlikewedidsofar,wenowusearouteroutlettocomposeitscontent.ThiswillmakeourTabscomponentunusablefornonroutingcases,butwecanestablishanicedecouplingbyonlyprovidingtherouteroutlet.ThiswaywecanstillreusetheTabscomponentinotherroutingsituations:

TheAppcomponentincludesarouteroutletdirectly;however,theProjectcomponentreliesontheTabscomponenttoprovidearouteroutlet.

Onahigherlevel,wecandescribethenewdesignofourTabscomponent,asfollows:

Itrendersalltabbuttons,basedonalistofrouterlinksandtitlestoprovidearouternavigationItprovidesarouteroutletthatwillbeusedbytheparentcomponenttoinstantiatenavigatedcomponents

Let'smodifyourTabscomponentinlib/ui/tabs/tabs.jstoimplementthesechanges:

www.EBooksWorld.ir

Page 178: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

...

import{ROUTER_DIRECTIVES}from'@angular/router';

@Component({

selector:'ngc-tabs',

...

directives:[ROUTER_DIRECTIVES]

})

exportclassTabs{

@Input()items;

}

TheROUTER_DIRECTIVESconstantfromtheroutermodulecontainstheRouterOutletdirectiveaswellastheRouterLinkdirective.Byimportingtheconstantandprovidingittothecomponentsdirectiveslist,weenablebothrouterdirectivestobeusedinourtemplate.

TheRouterOutletdirectiveisusedinsidetheTabscomponenttemplatetoindicatetheinstantiationpointfortherouter.

TheRouterLinkdirectivecanbeusedtogenerateroutingURLsfromthetemplateusingtherouterlinkDSL.Thisallowsyoutogeneratenavigationlinksinyourapplicationanditcanbeplacedbothonanchortagsaswellasotherelementswhereitwilltriggernavigationonclick.

Theitemsinputisanarrayoflinkitemsthatcontainatitleandarouterlink.Onourparentprojectcomponent,wealreadypreparetheseitemsintheconstructor.

Let'salsotakeaquicklookatthetemplateofourcomponentinthetabs.htmlfile:

<ulclass="tabs__tab-list">

<li*ngFor="letitemofitems">

<aclass="tabs__tab-button"

[routerLink]="item.link">{{item.title}}</a>

</li>

</ul>

<divclass="tabs__l-container">

<divclass="tabs__tabtabs__tab--active">

<router-outlet></router-outlet>

</div>

</div>

Aswelettherouterdealwiththeactiveviewusingarouteroutlet,there'snoneedanymoretousemultipletabcomponentsthatareswitchedactive.Wewillalwayshaveoneactivetab,andlettherouterhandlethecontent.

Let'sseehowwecanmakeuseofthenewTabscomponentinourProjectcomponenttomaketheconfiguredroutesnavigable.First,weneedtoaddthefollowingcodetoourProjectcomponentconstructortoprovidethenecessarynavigationitemstoourTabscomponent:

this.tabItems=[

{title:'Tasks',link:['tasks']},

{title:'Comments',link:['comments']}

www.EBooksWorld.ir

Page 179: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

];

Inthelinkpropertyofournavigationitems,weusetherouterlinkDSLtospecifywhichrouteshouldbenavigated.Asthenavigationisrelativetotheparentroutesegmentandwe'realreadyinthe/projects/:projectIdroute,theonlythinginourrouterlinkDSLshouldbearelativepathtothetasksandcommentschildroutes.

InthetemplateofourProjectcomponent,wecannowusethetabItemspropertytocreateabindingtotheinputpropertyoftheTabscomponent:

<divclass="project__l-header">

<h2class="project__title">{{title}}</h2>

<p>{{description}}</p>

</div>

<ngc-tabs[items]="tabItems"></ngc-tabs>

www.EBooksWorld.ir

Page 180: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

RefactoringnavigationAsafinalstep,wealsoneedtorefactorournavigationcomponentstorelyontherouter.Sofar,weusedourownroutingthatwasimplementedinacomplexnested-navigationcomponentstructure.WecansimplifythisalotusingtheAngularrouter.

Let'sstartwiththesmallestcomponentfirst,andeditourNavigationItemcomponenttemplateinthelib/navigation/navigation-section/navigation-item/navigation-item.htmlfile:

<aclass="navigation-section__link"

[class.navigation-section__link--active]="isActive()"

[routerLink]="link">{{title}}</a>

Insteadofcontrollingthelinkbehaviorourselves,wenowusetheRouterLinkdirectivetogeneratealinkthatisbasedonthecomponentlinkinputproperty.Tosettheactiveclassonthenavigationlink,westillrelyontheisActivemethodonourcomponent,andthere'snochangerequiredinthetemplate.

Let'slookatthechangestotheComponentclassinthenavigation-item.jsfile:

...

import{RouterLink}from'@angular/router/src/directives/router_link';

@Component({

selector:'ngc-navigation-item',

...

directives:[RouterLink]

})

exportclassNavigationItem{

@Input()title;

@Input()link;

@ViewChild(RouterLink)routerLink;

isActive(){

returnthis.routerLink?

this.routerLink.isActive:false;

}

}

InsteadofrelyingontheNavigationcomponenttomanagetheactivestateofnavigationitems,wenowrelyontheRouterLinkdirective.EachRouterLinkdirectiveprovidesanaccessorproperty,isActive,whichtellsuswhetherthisspecificrouteaddressedbytherouterlinkiscurrentlyactivatedwithinthebrowsersURL.Usingthe@ViewChilddecorator,wecanqueryfortheRouterLinkdirectiveinourviewandthenquerytheisActivepropertytofindoutifthecurrentnavigationitemisactiveornot.

Now,weonlyneedtomakesurethatwepassthenecessaryitemstotheNavigationcomponentinourAppcomponentinordertomakeournavigationworkagain.

www.EBooksWorld.ir

Page 181: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

ThefollowingcodeneedstobechangedintheAppcomponentconstructorintheapp.jsfile:

this.projectsSubscription=projectService.change

.subscribe((projects)=>{

this.projects=projects;

//Wecreatenewnavigationitemsforourprojects

this.projectNavigationItems=this.projects

//Wefirstfilterforprojectsthatarenotdeleted

.filter((project)=>!project.deleted)

.map((project)=>{

return{

title:project.title,

link:['/projects',project._id]

};

});

});

Byfilteringandmappingtheavailableprojects,wecancreatealistofnavigationitemsthatcontainatitleandlinkproperty.ThelinkpropertycontainsaroutelinkDSLthatpointstotheprojectdetailsroutethatisconfiguredintheAppcomponentrouterconfiguration.Bypassinganobjectliteralasasiblingtotheroutename,wecanspecifysomeparametersalongtheroute.Here,wesimplysettheexpectedprojectIdparametertotheIDoftheprojectintheprojectslist.

Now,ournavigationcomponentsmakeuseoftheroutertoenablenavigation.WegotridofourcustomroutingfunctionalityintheNavigationcomponent,andweuserouterlinkDSLtocreatenavigationitems.

www.EBooksWorld.ir

Page 182: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

SummaryInthischapter,welearnedaboutthebasicconceptsoftherouterinAngular.Welookedathowwecanusetheexistingcomponenttreetoconfigurechildroutesinnested-routerscenarios.Usingnested-childroutes,weenabledthereuseofcomponentswithrouteconfigurations.

Wealsolookedattheproblemofrouterversustemplatecompositionandhowtomitigatethisproblemusingwrappercomponents.Inthisway,weclosethegapbetweentherouterandunderlyingcomponentsusinganin-betweenlayer.

WelookedintorouteconfigurationspecificsandthebasicsoftherouterlinkDSL.WealsocoveredthebasicsoftheRouteTreeandRouteSegmentclassesandhowtousethemtoperformin-depthrouteanalysis.

Inthenextchapter,wewilllearnaboutSVGandhowtousethiswebstandardinordertodrawgraphicsinourAngularapplications.WewillvisualizeanactivitylogofourapplicationactivitiesusingSVGandseehowAngularmakesthistechnologyevengreaterbyenablingcomposability.

www.EBooksWorld.ir

Page 183: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Chapter6.KeepingUpwithActivitiesInthischapter,we'llbuildanactivityloginourtaskmanagementsystemusingScalableVectorGraphics(SVG)tobuildgraphicalcomponentsusingAngular.SVGistheperfectcandidatewhenitcomestocomplexgraphicalcontent,andusingAngularcomponents,wecanbuildnicelyencapsulatedandreusablecontent.

Sincewewanttologalltheactivitieswithinourapplication,suchasaddingcommentsorrenamingtasks,wearegoingtocreateacentralrepository.WecanthendisplaytheseactivitiesandrenderthemasanactivitytimelineusingSVG.

Toaddanoverviewofalltheactivitiesandtoprovideauserinputtonarrowtherangeofdisplayedactivities,we'regoingtocreateaninteractiveslidercomponent.Thiscomponentwilluseaprojectiontorendertimestamps,intheformofticksandactivities,directlyontotheslider'sbackground.We'llalsouseSVGtorendertheelementswithinthecomponent.

We'llcoverthefollowingtopicsinthischapter:

AbasicintroductiontoSVGMakingSVGcomposablewithAngularcomponentsUsingnamespacesincomponenttemplatesCreatingasimplepipetoformatcalendartimesusingMoment.jsUsingthe@HostListenerannotationstohandleuserinputeventstocreateaninteractivesliderelementMakinguseofShadowDOMusingViewEncapsulation.Nativeinordertocreatenative-styleencapsulation

www.EBooksWorld.ir

Page 184: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

CreatingaserviceforloggingactivitiesThegoalofthischapteristoprovideawaytokeeptrackofalluseractivitieswithinthetaskmanagementapplication.Forthispurpose,we'llneedasystemthatwillallowustologactivitieswithincomponentsandtoaccessalreadyloggedactivities.

Activities,asentities,shouldbequitegenericandshouldhavethefollowingfieldswiththeirrespectivepurposes:

Subject:Thisfieldshouldbeusedtoreferencethesubjectoftheactivity.Thiscanbeanyidentifierthatidentifiesaforeignentity.Inthecontextofprojects,we'llstoretheprojectIDinthisfield.Servicesandcomponentsthatusetheactivityserviceshouldusethisfieldtofilterspecificactivitiesfurther.Category:Thisfieldprovidesanadditionalwayoftaggingtheactivityfurther.Forprojects,wewillcurrentlyusetwocategories:commentsandtasks.Title:Thisreferstothetitleoftheactivitythatwillprovideaverybriefsummaryofwhattheactivityisabout.ThiscouldbesomethinglikeNewtaskwasaddedorCommentwasdeleted.Message:Thisisthefieldwheretherealbeefoftheactivitygoesinto.Itshouldcontainenoughinformationtoprovidegoodtraceabilityofwhathappenedduringtheactivity.

Inordertodevelopoursystem,we'llcreateanewfilenamedactivity-service.jsundertheactivities/activity-servicepathinourlibfolder.Inthisfile,wewillcreateouractivityserviceclass,whichwe'reenablingfordependencyinjection,byusingthe@Injectableannotation:

@Injectable()

constructor(@Inject(DataProvider)dataProvider,

@Inject(UserService)userService){

exportclassActivityService{

//We'reexposingareplaysubjectthatwillemitevents

//whenevertheactivitieslistchange

this.change=newReplaySubject(1);

this.dataProvider=dataProvider;

this.userService=userService;

this.activities=[];

//We'recreatingasubscriptiontoourdatastoretoget

//updatesonactivities

this.activitiesSubscription=this.dataProvider.getLiveChanges()

.map((change)=>change.doc)

.filter((document)=>document.type==='activity')

.subscribe((changedActivity)=>{

this.activities=this.activities.slice();

//Sinceactivitiescanonlybeaddedwecanassumethat

//thischangeisanewactivity

this.activities.push(changedActivity);

//Sortingtheactivitiesbytimetomakesurethere'sno

//syncissuemessingwiththeordering

this.activities.sort((a,b)=>

www.EBooksWorld.ir

Page 185: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

a.time>b.time?-1:a.time<b.time?1:0);

this.change.next(this.activities);

});

}

//Thismethodislogginganewactivity

logActivity(subject,category,title,message){

//UsingtheDataProvidertocreateanewdocumentinour

//datastore

this.dataProvider.createOrUpdateDocument({

type:'activity',

user:this.userService.currentUser,

time:newDate().getTime(),

subject,

category,

title,

message

});

}

}

Intheconstructorofouractivityservice,we'vesubscribedtochangestoourdatastoreandhavefilteredanyincomingchangebytypesowewillonlyreceiveactivityupdates.

Sinceactivitiescan'tbeeditedordeleted,weonlyneedtobeconcernedaboutnewlyaddedactivities.Weupdatetheinternalarrayofactivitieswithanyaddedactivityinthesubscription.Thisway,we'llnotonlyreceivealltheinitialactivities,butalsotheactivitiesthataresubsequentlyaddeddirectlyfromthedatastore.Otherservicesandcomponentscanthendirectlyaccesstheactivitylistofthesystem.

Inorderforotherapplicationcomponentstoreacttochangesintheactivitylist,we'veexposedaReplaySubjectobservableonthechangememberfield.

InthelogActivitymethod,we'vesimplyaddedanewactivitytothedatastore.UserServicewillprovideuswithinformationonthecurrentlylogged-inuser,andwecanuseDataProvidertowritetothedatastore.

So,wehavecreatedasimpleplatformthatwillhelpuskeeptrackofactivitieswithinourapplication.SincewewantonlyoneinstanceofActivityServicewithinourapplication,let'saddittotheproviderslistonourrootAppcomponent.You'llfindthiscomponentintheapp.jsfile,locatedwithinourlibfolder:

@Component({

selector:'ngc-app',

providers:[ProjectService,UserService,ActivityService]

})

BecausealldependencyinjectorswillinheritthedependenciesfromourAppcomponent,wecaninjectitinanycomponentofourapplicationgoingforward.

www.EBooksWorld.ir

Page 186: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

LoggingactivitiesWehavecreatedanicesystemtologactivities.Nowlet'sgoaheadanduseitwithinourcomponentstokeepanauditofalltheactivities.

First,let'suseActivityServicetologactivitieswithintheTaskListcomponent.ThefollowingcodeexcerpthighlightsthechangesmadetotheTaskListcomponentwithinthetask-list/task-list.jsfileinourlibfolder:

...

import{ActivityService}from'../activities/activity-service/activity-

service';

import{limitWithEllipsis}from'../utilities/string-utilities';

@Component({

selector:'ngc-task-list',

...

})

exportclassTaskList{

//Subjectforloggingactivities

@Input()activitySubject;

onTaskUpdated(task,updatedData){

...

//Creatinganactivitylogfortheupdatedtask

this.activityService.logActivity(

this.activitySubject.id,

'tasks',

'Ataskwasupdated',

'Thetask"${limitWithEllipsis(oldTask.title,30)}"wasupdatedon

#${this.activitySubject.document.data._id}.'

);

}

onTaskDeleted(task){

...

//Creatinganactivitylogforthedeletedtask

this.activityService.logActivity(

this.activitySubject.id,

'tasks',

'Ataskwasdeleted',

'Thetask"${limitWithEllipsis(removed.title,30)}"wasdeletedfrom

#${this.activitySubject.document.data._id}.'

);

}

addTask(title){

...

//Creatinganactivitylogfortheaddedtask

this.activityService.logActivity(

this.activitySubject.id,

'tasks',

'Ataskwasadded',

www.EBooksWorld.ir

Page 187: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

'Anewtask"${limitWithEllipsis(title,30)}"wasaddedto

#${this.activitySubject.document.data._id}.'

);

}

...

}

UsingthelogActivitymethodofActivityService,wecaneasilyloganynumberofactivitieswithinthealreadyexistingTaskListmethodstomodifytasks.

Inthemessagebodyofouractivities,we'veusedanewutilityfunction,limitWithEllipsis,whichwe'veimportedfromanewmodule,namelystring-utilities.Thisfunctiontakesastringandanumberasparameters.Thereturnedstringisatruncatedversionoftheinputstring,whichiscutoffatthepositionspecifiedwiththesecondparameter.Inaddition,there'sanellipsischaracter(...)appendedtothestring.Iwon'tbotheryouwiththerathersimplecodewithinthishelper.Ifyou'dliketoknowhowit'simplemented,youcanalwayschecktheimplementationafterdownloadingthischapter'scode.

Ifyougobacktothespecificationofouractivitylogs,youwillseethatwealwaysneedtospecifyasubjectinordertologactivities.We'veimplementedthisonourTaskListcomponentbyintroducinganewinputparametercalledactivitySubject.TheassumptionhereisthateachactivitysubjectcontainsLiveDocumentstoredunderthedocumentmember.Fromthere,wecanobtaintheIDinthedatastoreanduseitforouractivitymessage.

IfwerevisitourProjectcomponent,youwillseethatwe'realreadyfollowingtheprerequisitesofbeinganactivitysubject.We'vestoredareferencetotheunderlyingLiveDocumentinstanceunderthedocumentmemberfield.

AllweneedtodonowischangethetemplateofourProjectTaskListwrappercomponenttopasstheactivitySubjectprojectinputoftheTaskListcomponent.Let'slookatthechangesinthelib/project/project-task-list/project-task-list.htmlfilequickly:

<ngc-task-list[tasks]="project.tasks"

[activitySubject]="project"

(tasksUpdated)="updateTasks($event)"></ngc-task-list>

Youmightwonderwhywecareaboutthisrathercumbersomewayofdealingwithourtasklist,ifwecouldjustpassinahardreferencetotheprojectanduseprojecttasksandtheprojectIDdirectly.Thebeautifulaspectofourcurrentsolutionisthatwedonothaveanydependencyonaprojectassuch.WecouldalsouseourTaskListcomponentwithoutthecontextofaproject.Andwecanstillpassalistoftaskstothetasksinputanduseadifferentactivitysubjectfortheactivitylogs.

We'realsogoingtouseActivityServicewithintheCommentscomponenttocreatelogsforadded,edited,anddeletedcomments.Sincethestepsinvolvedareverysimilartowhatwe'vejustdonefortheTaskListcomponent,we'regoingtoskipthis.YoucanalwaystakealookatthefinalcodebaseforthischaptertoaddactivitylogsfortheCommentscomponent.

www.EBooksWorld.ir

Page 188: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

LeveragingthepowerofSVGSVGhasbeenapartoftheOpenWebPlatformstandardssince1999andwasfirstrecommendedin2001undertheSVG1.0standard.SVGisaconsolidationoftwoindependentproposalsforanXML-basedvectorimageformat.PrecisionGraphicsMarkupLanguage(PGML)—mainlydevelopedbyAdobeandNetscape—aswellasVectorMarkupLanguage(VML)—whichwasmainlyrepresentedbyMicrosoftandMacromedia—werebothdifferentXMLformatsthatservedthesamepurpose.TheW3CconsortiumdeclinedboththeproposalsinfavorofthenewlydevelopedSVGstandardthatunifiedthebestofbothworldsintoasinglestandard:

TimelineshowingthedevelopmentoftheSVGstandard

Allthreestandardshadacommongoal,whichwastoprovideaformatfortheWebtodisplayvectorgraphicsinthebrowser.SVGisadeclarativelanguagethatspecifiesgraphicalobjectsusingXMLelementsandattributes.

Let'slookatasimpleexampleonhowtocreateanSVGimagewithablackcircle,usingSVG:

<?xmlversion="1.0"encoding="utf-8"?>

<svgversion="1.1"xmlns="http://www.w3.org/2000/svg"

width="20px"height="20px">

<circlecx="10"cy="10"r="10"fill="black"/>

www.EBooksWorld.ir

Page 189: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

</svg>

ThisrathersimpleexamplerepresentsanSVGimagewithablackcircle,whosecenterislocatedatx=10pxandy=10px.Theradiusofthecircleis10px,whichmakesthiscircle20pxinwidthandheight.

TheoriginofthecoordinatesysteminSVGsitsonthetop-leftcorner,wheretheyaxisfacesthesouthdirectionandthexaxiseastward:

ThecoordinatesystemwithinSVG

Usingnotonlyprimitiveshapes,suchascircles,lines,andrectangles,butalsocomplexpolygons,thepossibilitiesforcreatinggraphicalcontentarenearlyunlimited.

SVGisnotonlyusedwithintheWeb,buthasalsobecomeaveryimportantintermediateformatforexchangingvectorgraphicsbetweendifferentapplications.AlmostanyapplicationthatsupportsvectorgraphicsalsosupportstheimportofSVGfiles.

TherealpowerofSVGcomestothesurfacewhenwedonotincludeanSVGfileasanHTMLimage,butratherincludetheSVGcontentdirectlywithinourDOM.SinceHTML5directlysupportstheSVGnamespacewithinanHTMLdocumentandwillrenderthegraphicswedefinewithinourHTML,awholebunchofnewpossibilitiesspringup.WecannowstyleourSVGwithCSS,manipulatetheDOMwithJavaScript,andeasilymakeourSVGinteractive.

www.EBooksWorld.ir

Page 190: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Takingthepreviousexampleofourcircleimagetothenextlevel,wecouldmakeitinteractivebychangingthecirclecolorbyclickingit.First,let'screateaminimalHTMLdocumentandincludeourSVGelementsdirectlywithintheDOM:

<!doctypehtml>

<title>MinimalisticCircle</title>

<svgwidth="20px"height="20px">

<circleid="circle"cx="10"cy="10"r="10"fill="black">

</svg>

<script>

document

.getElementById('circle')

.addEventListener('click',function(event){

event.target.setAttribute('fill','red');

});

</script>

Asyoucansee,wecangetridoftheversionandtheXMLnamespacedeclarationwhenweuseSVGdirectlywithintheDOMofourHTMLdocument.What'sinterestinghereisthatwecantreatSVGverymuchlikeregularHTML.WecanassignanIDandevenclassestoSVGelementsandaccessthemfromJavaScript.

WithinthescripttagofourHTMLdocument,wecandirectlyaccessourcircleelementusingtheIDwe'vepreviouslyassignedtoit.Wecanaddeventlisteners,thewaywealreadyknow,fromregularHTMLelements.Inthisexample,weaddedaclickeventlistenerandchangedthecolorofourcircletored.

Forthesakeofsimplicity,weusedaninlinescripttaginthisexample.ItwouldofcoursebemuchcleanertohaveaseparateJavaScriptfiletodothescripting.

www.EBooksWorld.ir

Page 191: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

StylingSVGI'mapuristwhenitcomestotheseparationofconcernswithintheWeb.Istillstronglybelieveintheseparationofstructure(HTML),appearance(CSS),andbehavior(JavaScript),aswellasproducingthemostmaintainableapplicationswhenfollowingthispractice.

First,itseemsweirdtohaveSVGinyourHTML,andyoumightthinkthatthisbreaksthecontractofacleanseparation.Whyisthisgraphicalcontent,consistingofonlyappearance-relevantdata,sittinginmyHTMLthatissupposedtoonlycontainrawinformation?AfterdealingwithalotofSVGswithinaDOM,IhavecometotheconclusionthatwecanestablishacleanseparationwhenusingSVGbydividingourappearanceresponsibilitiesintothefollowingtwosubgroups:

Graphicalstructure:Thissubgroupdealswiththeprocessofdefiningthebasicstructureofyourgraphicalcontent.Thisisaboutshapesandlayout.Visualappearance:Thissubgroupdealswiththeprocessofdefiningthelookandfeelofourgraphicalstructures,suchascolors,linewidths,linestyles,andtextalignment.

IfweseparatetheconcernsofSVGintothesegroups,wecanactuallygaingreatmaintainability.GraphicalstructureisdefinedbytheSVGshapesthemselves.TheyaredirectlywrittenwithinourHTMLbutdon'thaveaparticularlookandfeel.WeonlystorethebasicstructuralinformationwithinHTML.

Luckily,allthepropertiesofvisualappearance,suchascolors,cannotonlybeexpressedthroughtheattributesinourSVGelements;however,there'sacorrespondingCSSpropertythatallowsustooffloadallthelook-and-feel-relevantaspectsofthestructuretoCSS.

Gobacktotheexamplewherewedrewablackcircle;we'lltweakthisabittofitourdemandsofseparationofconcernssothatwecandistinguishgraphicalstructurefromgraphicalappearance:

<!doctypehtml>

<title>MinimalisticCircle</title>

<svgwidth="20px"height="20px">

<circleclass="circle"cx="10"cy="10"r="10">

</svg>

StylingourgraphicalstructurescannowbeachievedusingCSSbyincludingastylesheetwiththefollowingcontent:

.circle{

fill:black;

}

Thisisfantastic,aswecannownotonlyreusesomegraphicalstructures,butalsoapplydifferentvisualappearanceparametersusingCSS,similartothoseenlighteningmomentswhenwemanagedtoreusesomesemanticHTMLbyonlychangingsomeCSS.

www.EBooksWorld.ir

Page 192: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Let'slookatthemostimportantCSSpropertieswecanusetostyleSVGshapes:

fill:WhileworkingwithsolidSVGshapes,there'salwaysashapefillandstrokeoptionavailable;thefillpropertyspecifiesthecoloroftheshapefill.stroke:ThispropertyspecifiesthecoloroftheSVGshape'soutline.stroke-width:ThispropertyspecifiesthewidthoftheSVGshape'soutlineonsolidshapes.Fornonsolidshapes,suchaslines,thiscanbethoughtofaslinewidth.stroke-dasharray:Thisspecifiesadashpatternforstrokes.Dashpatternsarespace-separatedvaluesthatdefineapattern.stroke-dashoffset:Thisspecifiesanoffsetforthedashpattern,whichisspecifiedwiththestroke-dasharrayproperty.stroke-linecap:Thispropertydefineshowlinecapsshouldberendered.Theycanberenderedassquare,butt,orroundedcaps.stroke-linejoin:Thispropertyspecifieshowlinesarejoinedtogetherwithinapath.shape-rendering:Usingthisproperty,youcanoverridetheshape-renderingalgorithmthat,asthenamesuggests,isusedtorendershapes.Thisisparticularlyusefulifyouneedcrispyedgesonyourshapes.

Foracompletereferenceoftheavailableappearance-relevantSVGattributes,visittheMozillaDeveloperwebsiteathttps://developer.mozilla.org/en-US/docs/Web/SVG/Attribute.

IhopethisbriefintroductiongaveyouabetterfeelingaboutSVGandthegreatpoweritcomeswith.Inthischapter,we'regoingtousesomeofthatpowertocreatenice,interactivegraphicalcomponents.IfyouwouldliketolearnmoreaboutSVG,IstronglyrecommendthatyougothroughthegreatarticlesbySaraSoueidan.

www.EBooksWorld.ir

Page 193: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

BuildingSVGcomponentsWhenbuildingAngularcomponentswithSVGtemplates,thereareacoupleofthingsweneedtobeawareof.Thefirstandmostobviousone,isXMLnamespaces.ModernbrowsersareveryintelligentwhenparsingHTML.Besidesbeingprobablythemostfault-tolerantparserinthehistoryofcomputerscience,DOMparsersareverysmartinrecognizingmarkupandthendecidinghowtotreatit.Theywillautomaticallydecidethecorrectnamespacesforus,basedonelementnames,sowedon'tneedtodealwiththemwhenwritingHTML.

Ifyou'vemessedaroundwiththeDOMAPIabit,youwould'veprobablyrecognizedthattherearetwomethodsforcreatingnewelements.Inthedocumentobject,forexample,there'sacreateElementfunction,butthere'salsocreateElementNSthatacceptsanadditionalnamespaceURIparameter.Also,everycreatedelementhasanamespaceURIpropertythattellsyouthenamespaceofthespecificelement.ThisisimportantsinceHTML5isastandardthatconsistsofatleastthreenamespaces:

HTML:ThisisthestandardHTMLnamespacewiththehttp://www.w3.org/1999/xhtmlURI.SVG:ThisembracesallSVGelementsandattributesandusesthehttp://www.w3.org/2000/svgURI.YoucansometimesseethisnamespaceURIinanxmlnsattributeofthesvgelements.Infact,thisisnotreallyrequired,asthebrowserissmartenoughtodecideonthecorrectnamespaceitself.MathML:ThisisanXML-basedformattodescribemathematicalformulasandissupportedinmostmodernbrowsers.Itusesthehttp://www.w3.org/1998/Math/MathMLnamespaceURI.

Wecanmixalltheseelementsfromdifferentstandardsandnamespaceswithinasingledocument,andourbrowserwillfigureoutthecorrectnamespaceitselfwhenitcreateselementswithintheDOM.

Tip

Ifyouwantmoreinformationonnamespaces,IrecommendthatyougothroughtheNamespacesCrashCoursearticleontheMozillaDeveloperNetworkathttps://developer.mozilla.org/en/docs/Web/SVG/Namespaces_Crash_Course.

AsAngularwillcompiletemplatesforusandrenderelementsintotheDOMusingtheDOMAPI,itneedstobeawareofthenamespaceswhendoingthat.Similartothebrowser,Angularprovidessomeintelligencefordecidingthecorrectnamespacewhilecreatingelements.However,therearesomesituationswhereyouneedtohelpAngularrecognizethecorrectnamespace.

Toillustratesomeofthisbehavior,let'stransformourcircleexamplethatwe'vebeenworkingonintoanAngularcomponent:

@Component({

www.EBooksWorld.ir

Page 194: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

selector:'awesome-circle',

template:`

<svg[attr.width]="size"[attr.height]="size">

<circle[attr.cx]="size/2"[attr.cy]="size/2"

[attr.r]="size/2"fill="black"/>

</svg>

`

})

exportclassAwesomeCircle{

@Input()size;

}

We'vewrappedourcircleSVGgraphicsintoasimpleAngularcomponent.ThesizeinputparameterdeterminestheactualwidthandheightofthecirclebycontrollingtheSVG'swidthandheightattributesandthecircle'scx,cy,andrattributes.

TouseourCirclecomponent,simplyusethefollowingtemplatewithinanothercomponent:

<awesome-circle[size]="20"></awesome-circle>

Note

It'simportanttonotethatweneedtouseattributebindingsonSVGelements,andwecan'tsetDOMelementpropertiesdirectly.ThisisduetothenatureofSVGelementsthathavespecialpropertytypes—forexample,SVGAnimatedLength—thatcanbeanimatedwithSynchronizedMultimediaIntegrationLanguage(SMIL).Insteadofinterferingwiththeserathercomplexelementproperties,wecansimplyuseattributebindingstosettheattributevaluesoftheDOMelement.

Let'sgobacktoournamespacediscussion.AngularwouldknowthatitneedstousetheSVGnamespacetocreatetheelementswithinthistemplate.Itwillfunctioninthiswaysimplybecausewe'reusingthesvgelementasarootelementwithinourcomponent,anditcouldswitchthenamespacewithinthetemplateparserforanychildelementsautomatically.

However,therearecertainsituationswhereweneedtohelpAngulardeterminethecorrectnamespacefortheelementswe'dliketocreate.Thisstrikesusifwe'recreatingnestedSVGcomponentsthatdon'tcontainarootsvgelement:

@Component({

selector:'[awesomeCircle]',

template:`

<svg:circle[attr.cx]="size/2"[attr.cy]="size/2"

[attr.r]="size/2"fill="black"/>

'

})

exportclassAwesomeCircle{

@Input('awesomeCircle')size;

}

@Component({

selector:'app'

www.EBooksWorld.ir

Page 195: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

template:`

<svgwidth="20"height="20">

<g[awesomeCircle]="20"></g>

</svg>

`,

directives:[AwesomeCircle]

})

exportclassApp{}

Inthisexample,we'renestingSVGcomponents,andourAwesomeCirclecomponentdoesnothaveansvgrootelementtotellAngulartoswitchthenamespace.Thisiswhywe'vecreatedthesvgelementwithinourAppcomponentandthenincludedtheAwesomeCirclecomponentinanSVGgroup.

WeneedtoexplicitlytellAngulartoswitchtotheSVGnamespacewithinourCirclecomponent,andwecandothisbyincludingthenamespacenameasaprefixseparatedbyacolon,asyoucanseeinthehighlightedsectionoftheprecedingcodeexcerpt.

IfyouhavemultipleelementsthatneedtobecreatedwithintheSVGnamespaceexplicitly,youcanrelyonthefactthatAngulardoesapplythenamespaceforchildelementstooanddoesgroupallyourelementswithanSVGgroupelement.So,youonlyneedtoprefixthegroupelement<svg:g>...</svg:g>,butnoneofthecontainedSVGelements.

ThisisenoughtoknowaboutAngularinternalswhendealingwithSVG.Let'smoveonandcreatesomerealcomponents!

www.EBooksWorld.ir

Page 196: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

BuildinganinteractiveactivityslidercomponentIntheprevioustopics,we'vecoveredthebasicsofworkingwithSVGanddealingwithSVGinAngularcomponents.Nowit'stimetoapplyourknowledgetothetaskmanagementapplicationandcreatesomecomponentsusingSVG.

Thefirstcomponentwe'llbecreatinginthiscontextisaninteractivesliderthatallowstheusertoselectthetimerangeofactivitiesthatheorsheisinterestedtocheckout.DisplayingasimpleHTML5rangeinputcouldbeasolution,butsincewe'vegainedsomeSVGsuperpower,wecandobetter!We'lluseSVGtorenderourownsliderthatwillshowexistingactivitiesasticksontheslider.Let'slookatamock-upoftheslidercomponentthatwe'regoingtocreate:

Amockupoftheactivityslidercomponent

Ourslidercomponentwillactuallyservetwopurposes.Itshouldbeausercontrolandshouldprovideawaytoselectatimerangeforfilteringactivities.However,itshouldalsoprovideanoverviewofalltheactivitiessothatausercanfiltertherangemoreintuitively.Bydrawingverticalbarsthatrepresentactivities,wecanalreadygivetheuserafeelingoftherangeheorsheisinterestedin.

Firstofall,we'llcreateanewfileforourActivitySlidercomponentcalledactivity-slider.jswithintheactivities/activity-sliderpathanddefineourcomponentclass:

importstylesfrom'./activity-slider.css!text';

@Component({

selector:'ngc-activity-slider',

host:{

class:'activity-slider'

},

styles:[styles],

encapsulation:ViewEncapsulation.Native,

})

exportclassActivitySlider{

www.EBooksWorld.ir

Page 197: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

//Theinputexpectsalistofactivities

@Input()activities;

//Iftheselectionofdaterangechangeswithinourslider

//component,we'llemitachangeevent

@Output()selectionChange=newEventEmitter();

constructor(@Inject(ElementRef)elementRef){

//We'llusethehostelementformeasurementwhendrawing

//theSVG

this.sliderElement=elementRef.nativeElement;

//Thepaddingoneachsideoftheslider

this.padding=20;

}

ngAfterViewInit(){

//We'llneedareferencetotheoverlayrectanglesowecan

//updateitspositionandwidth

this.selectionOverlay=this.sliderElement

.shadowRoot.querySelector('.selection-overlay');

}

}

Thefirstthingweshouldmention,andwhichdiffersfromalltheothercomponentswe'vewrittensofar,isthatwe'reusingViewEncapsulation.Nativeforthiscomponent.AswelearnedfromtheCreatingourapplicationcomponentsectioninChapter2,Ready,Set,Go!,whenweuseViewEncapsulation.Nativeforourcomponentencapsulation,AngularactuallyusesShadowDOMtocreatethecomponent.WebrieflylookedatthisintheShadowDOMsectioninChapter1,Component-BasedUserInterfacesaswell.

UsingShadowDOMforourcomponentwillgiveusthisadvantage:ourcomponentwillbefullyencapsulatedfromtheCSSsideofthings.ThisnotonlymeansthatnoneoftheglobalCSSeswillleakintoourcomponent,butitalsomeansthatwe'llneedtocreatelocalstylesinordertostyleourcomponent.

Sofar,we'veusedaCSSnamingconventioncalledBEMthatprovidesuswithsomenecessaryprefixestoavoidnameclasheswithinCSSandestablishacleanandsimpleCSSspecificity.However,whenusingShadowDOM,wecanforegoprefixestoavoidnameclashes,sincewe'reonlyapplyingstyleslocallywithinthecomponent.

Becausewe'reusingShadowDOMforthiscomponent,weneedtohaveawaytodefinelocalstyles.Angularprovidesuswithanoptiontopassstylesintothecomponentusingthestylespropertyofthecomponentannotation.

Tip

ChromesupportsShadowDOMnativelysinceversion35.WithinFirefox,ShadowDOMcanbeenabledbyvisitingtheabout:configpageandturningonthedom.webcomponents.enabledflag.IE,Edge,andSafaridon'tsupportthisstandardatall;however,wecansetthingsupinawaythattheycoulddealwithShadowDOM,byincludingapolyfillnamedwebcomponents.js.Youcanfindmoreinformationonthispolyfillat

www.EBooksWorld.ir

Page 198: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

https://github.com/webcomponents/webcomponentsjs.

UsingthetextpluginofSystemJS,wecanimportastylesheetcontainingonlythelocalstylesofourcomponentandthenpassthemtothestylesproperty.Byappendinga!textpostfixtotheimportofourCSSfile,wetellSystemJStoloadourCSSfileasrawtext.Notethatthestylespropertyisexpectinganarray,whichiswhywewrapourimportedstylesintoanarrayliteral.

IfyoutakealookatthestylesheetfortheActivitySlidercomponent,youcanimmediatelyseethatwe'renolongerprefixingtheclasseswiththecomponentname:

.slide{

fill:#f9f9f9;

}

.activity{

fill:#3699cb;

}

.time{

fill:#bbb;

font-size:14px;

}

.tick{

stroke:#bbb;

stroke-width:2px;

stroke-dasharray:3px;

}

.selection-overlay{

fill:#d9d9d9;

}

Usually,suchshortclassnameswouldprobablyleadtonameclasheswithinourproject,butsincethestyleswillbelocaltotheShadowDOMofourcomponent,wedon'tneedtoworryaboutnameclashesanymore.

Asaninputparameter,wedefinethelistofactivitiesthatwillbeusednotonlytodeterminetheavailablerangeintheslider,butalsotorenderactivitiesonthebackgroundoftheslider.

Onceaselectionismadebytheuser,ourcomponentwillusetheselectionChangeeventemittertonotifytheoutsideworldaboutthechange.

Withintheconstructor,we'resettingasidethecomponentDOMelementforsomemeasurementweneedtomakeinordertodrawlateron:

this.sliderElement=elementRef.nativeElement;

ByinjectingtheElementRefinstancetotheconstructor,wecaneasilyaccessthenativeDOMelementofourcomponent.

www.EBooksWorld.ir

Page 199: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

ProjectionoftimeOurslidercomponentneedstobeabletoprojecttimestampsintothecoordinatesystemofSVG.Also,whenauserclicksonthetimelinetoselectarange,we'llneedtobeabletoprojectcoordinatesbackintotimestamps.Forthispurpose,weneedtocreatetwoprojectionfunctionswithinourcomponentthatwilluseafewhelperfunctionsandstatestocalculatethevalues,fromcoordinatestotimeandvice-verse:

Visualizationofimportantvariablesandfunctionsforourcalculations

WhilewewillusepercentagetopositionourSVGelementsontheslidercomponent,thepaddingonthesideswillneedtobespecifiedinpixels.ThetotalWidthfunctionwillreturnthetotalwidthoftheareainpixels;thisiswherewe'lldrawtheactivityindicators.ThetimeFirst,timeLast,andtimeSpanvariableswillalsobeusedbythecalculationsandarespecifiedinmilliseconds.

Let'saddsomecodetoourslidertodealwiththeprojectionofouractivitiesonthesliderintheactivity-slider.jsfile:

//Gettingthetotalavailablewidthoftheslider

totalWidth(){

returnthis.sliderElement.clientWidth-this.padding*2;

}

//Projectsatimestampintopercentageforpositioning

projectTime(time){

letposition=this.padding+

(time-this.timeFirst)/this.timeSpan*this.totalWidth();

returnposition/this.sliderElement.clientWidth*100;

}

//Projectsapixelvaluebacktoatimevalue.Thisisrequired

//forcalculatingtimestampsfromuserselection.

projectLength(length){

returnthis.timeFirst+(length-this.padding)/this.totalWidth()*

this.timeSpan;

}

www.EBooksWorld.ir

Page 200: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

SincewehaveputasidethereferencetotherootelementasthesliderElementmembervariable,wecanuseitsclientWidthpropertytogetthefullwidthofthecomponentandsubtractthepadding.Thiswillgiveusthefullwidthoftheareawherewe'dliketodrawactivityindicators,inpixels.

IntheprojectTimefunction,wewillfirsttransformthetimestampintoapositionbyasimpleruleofthree.Becausewehaveaccesstothetimestampofthefirstactivityaswellasthetotaltimespan,thiswillbequiteasimpletask.Oncewedothis,wecanconvertourpositionvalue,whichisofunitpixels,intopercentage,bydividingitbythetotalcomponentwidthandthenmultiplyingitby100.

Toprojectapixelvaluebacktoatimestamp,wecandomoreorlessthereverseofprojectTime,exceptthatwe'renotdealingwithpercentageherebutassumingthelengthparameteroftheprojectLengthfunctionisinpixelunit.

We'veusedsomemembervariables—timeFirst,timeLast,andtimeSpan—withinourprojectioncode,buthowdowesetthesemembervariables?Sincewehaveanactivitiescomponentinput,whichisexpectedtobealistofrelevantactivities,wecanobservetheinputforchangesandsetthevaluesbasedontheinput.Toobservecomponentinputforchanges,wecanusethengOnChangeslifecyclehook:

ngOnChanges(changes){

//Iftheactivitiesinputchangesweneedtore-calculateand

//re-draw

if(changes.activities&&changes.activities.currentValue){

constactivities=changes.activities.currentValue;

//Forlatercalculationswesetasidethetimesofthe

//firstandthelastactivity

if(activities.length===1){

//Ifweonlyhaveoneactivityweusethesametimefor

//firstandlast

this.timeFirst=this.timeLast=activities[0].time;

}elseif(activities.length>1){

//Takefirstandlasttime

this.timeFirst=activities[activities.length-1].time;

this.timeLast=activities[0].time;

}else{

//Noactivitiesyet,soweusethecurrenttimeforboth

//firstandlast

this.timeFirst=this.timeLast=newDate().getTime();

}

//Thetimespanisthetimefromthefirstactivitytothe

//lastactivity.Weneedtolimittolower1fornotmessing

//uplatercalculations.

this.timeSpan=Math.max(1,this.timeLast-this.timeFirst);

}

}

First,weneedtocheckwhetherthechangesincludechangestotheactivitiesinputandthatthecurrentvalueoftheinputisvalid.Aftercheckingfortheinputvalue,wecandetermine

www.EBooksWorld.ir

Page 201: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

ourmembervariables,namelytimeFirst,timeLast,andtimeSpan.WelimitthetimeSpanvariableto1atleast,asourprojectioncalculationswouldbemessedupotherwise.

Theprecedingcodewillensurethatwewillalwaysrecalculateourmembervariableswhentheactivitiesinputchangesandthatwe'dbeusingthemostrecentdata-renderingactivities.

www.EBooksWorld.ir

Page 202: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

RenderingactivityindicatorsWe'vealreadyimplementedthebasicsofthecomponentandlaidthegroundworkfordrawingtimeinformationintothecoordinatesystemofourcomponent.It'stimetouseourprojectionfunctionsanddrawouractivitiesasindicatorsonthesliderusingSVG.

First,let'stakealookattherequiredtemplatethatwearegoingtocreateinafilecalledactivity-slider.htmlwithinouractivity-sliderdirectory:

<svgwidth="100%"height="70px">

<rectx="0"y="30"width="100%"height="40"

class="slide"></rect>

<rect*ngFor="letactivityofactivities"

[attr.x]="projectTime(activity.time)+'%'"

height="40"width="2px"y="30"class="activity"></rect>

</svg>

Sinceweneedtocreateanindicatorforeveryactivitywithinouractivitieslist,wecansimplyusetheNgFordirectivetorepeattherectanglethatrepresentsouractivityindicator.

AsweknowfrombuildingourActivityServiceclassinaprevioustopic,activitiesalwayscontainatimefieldwiththetimestampoftheactivity.Withinourcomponent,wehavealreadycreatedaprojectionfunctionthatconvertstimeintopercentage,relativetoourcomponentwidth.WecansimplyusetheprojectTimefunctionwithinourattributebindingforthexattributeoftherectelementtopositionouractivityindicatorsatthecorrectpositions.

ByusingonlyanSVGtemplateandourbackingfunctiontoprojecttime,wehavecreatedanicelittlechartthatdisplaysactivityindicatorsonatimeline.

Youcanimaginethatifwehavealotofactivities,oursliderwillactuallylookprettystuffed,anditwillbehardtogetafeelingforwhenthoseactivitiesmayhaveoccurred.Weneedtohavesomesortofagridthatwillhelpusassociatethechartwithatimeline.

Asalreadyshowninthemock-upofourslidercomponent,we'renowgoingtointroducesometicksonthesliderbackgroundthatwilldividethesliderintosections.We'llalsolabeleachtickwithacalendartime.Thiswillgiveourusersaroughsensefortimewhenlookingattheactivityindicatorsontheslider.

Let'slookatthecodechangeswithinourActivitySliderclassthatwillenabletherenderingofourticks:

ngOnChanges(changes){

//Iftheactivitiesinputchangesweneedtore-calculateand

//re-draw

if(changes.activities&&changes.activities.currentValue){

...

//Re-calculatetheticksthatwedisplayontopoftheslider

this.computeTicks();

www.EBooksWorld.ir

Page 203: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

...

}

//Thisfunctioncomputes5tickswiththeirtimeandpositionon

//theslider

computeTicks(){

constcount=5;

consttimeSpanTick=this.timeSpan/count;

this.ticks=Array.from({length:count}).map(

(element,index)=>{

returnthis.timeFirst+timeSpanTick*index;

});

}

...

Firstofall,weneedtocreateafunctionthatcomputessometicksforusthatwecanplaceontothetimeline.Forthispurpose,weneedtocreatethecomputeTicksmethodthatwilldividethewholetimelineintofiveequalsegmentsandgeneratetimestampsthatrepresentthepositionintimeforindividualticks.Westoretheseticksinanewticksmembervariable.Withthehelpofthesetimestamps,wecanrenderthetickswithinourvieweasily.

Tip

WeusetheArray.fromES6functiontocreateanewarraywiththedesiredlength,andusethefunctionalarrayextrafunctionmaptogeneratetickmodelobjectsfromthisarray.UsingArray.fromisanicetricktocreateaninitialarrayofagivenlengththatcanbeusedtoestablishfunctionalstyle.

Let'slookatthetemplateofourcomponentandhowwecanuseourarrayoftimestampstorenderticksonourslidercomponent.We'regoingtomodifyourcomponenttemplateinactivity-slider.html:

...

<g*ngFor="lettickofticks">

<text[attr.x]="projectTime(tick)+'%'"y="14"class="time">

{{tick|calendarTime}}</text>

<line[attr.x1]="projectTime(tick)+'%'"

[attr.x2]="projectTime(tick)+'%'"

y1="30"y2="70"class="tick"></line>

</g>

...

Torenderourticks,we'veusedanSVGgroupelementtoplaceourNgFordirectivethatrepeatstheticktimestampswe'vestoredintheticksmembervariable.

Foreachtick,weneedtoplacealabelaswellasalinethatspansoverthesliderbackground.WecanusetheSVGtextelementtorenderourlabelwiththetimestampontopoftheslider.Withintheattributebindingforthexattributeofourtextelement,we'veusedourprojectTimeprojectionfunctiontoreceivetheprojectedpercentagevaluefromourtimestamp.Theycoordinateofourtextelementisfixedatapositionwherethelabelswill

www.EBooksWorld.ir

Page 204: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

justsitontopofourslider.

SVGlinesconsistoffourcoordinates:x1,x2,y1,andy2.Togethertheydefinetwocoordinatepointswherealinewillbedrawnfromonepointtotheother.

Nowwearegettingclosertothefinalsliderthatwespecifiedinthemock-upatthebeginningofthistopic.Thelastmissingpieceofthepuzzleistomakeoursliderinteractivesoausercanselectarangeofactivities.

www.EBooksWorld.ir

Page 205: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

BringingittolifeSofar,we'vecoveredtherenderingofthesliderbackgroundaswellastherenderingoftheactivityindicators.We'vealsogeneratedticksanddisplayedthemwithagridlineandalabeltodisplaythecalendartimeofeachtick.

Well,thatdoesnotreallymakeaslider,doesit?Ofcourse,wealsoneedtohandleuserinputandmakethesliderinteractivesouserscanselectatimerangetheywanttodisplaytheactivitiesfor.

Todothis,addthefollowingchangestothecomponentclass:

ngOnChanges(changes){

//Iftheactivitiesinputchangesweneedtore-calculateand

//re-draw

if(changes.activities&&changes.activities.currentValue){

...

//Settingtheselectiontothefullrange

this.selection={

start:this.timeFirst,

end:this.timeLast

};

//Selectionchangedsoweneedtoemitevent

this.selectionChange.next(this.selection);

}

}

WhenwedetectachangeintheactivitiesinputpropertywithinthengOnChangeslifecyclehook,weinitializeamodelfortheuserselectioninourslidercomponent.Itconsistsofastartandendproperty,bothcontainingtimestampsthatrepresenttheselectedrangeonouractivityslider.

Oncewe'vesetourinitialselection,weneedtousetheselectionChangeoutputpropertytoemitanevent.Thisway,wecanletourparentcomponentknowthattheselectionwithinthesliderhaschanged.

Todisplaytheselectedrange,weuseanoverlayrectanglewithinourtemplatethatwillbeplacedabovethesliderbackground.Ifyoulookatthemock-upimageoftheslideragain,you'llnoticethatthisoverlayispaintedingray:

<rect*ngIf="selection"

[attr.x]="projectTime(selection.start)+'%'"

[attr.width]="projectTime(selection.end)-

projectTime(selection.start)+'%'"

y="30"height="40"class="selection-overlay"></rect>

Thisrectanglewillbeplacedjustaboveoursliderbackgroundandwilluseourprojectionfunctiontocalculatethexandwidthattributes.AsweneedtowaitforchangedetectiontoinitializeourselectionwithinthengOnChangeslifecyclehook,we'lljustcheckforavalid

www.EBooksWorld.ir

Page 206: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

selectionobjectbymakinguseoftheNgIfdirective.

NowweneedtostarttacklinguserinputinourActivitySlidercomponent.Themechanicsforstoringthestateandrenderingourselectionisalreadyinplace,sowecanimplementtherequiredhostlistenerstohandleuserinput:

...

//Ifthecomponentreceivesamousedownevent,weneedtostarta

//newselection

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

onMouseDown(event){

//Startinganewselectionbysettingselectionstartandend

//totheprojectedtimeoftheclickedposition.

this.selection.start=this.selection.end=

this.projectLength(event.offsetX);

//Selectionchangedsoweneedtoemiteventan

this.selectionChange.next(this.selection);

//Settingaflagsoweknowthattheuseriscurrentlymoving

//theselection

this.modifySelection=true;

}

//Wealsoneedtotrackmousemoveswithinourslidercomponent

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

onMouseMove(event){

//Weshouldonlymodifytheselectionifthecomponentisin

//thecorrectmode

if(this.modifySelection){

//Updatetheselectionendwiththeprojectedtimefromthe

//mousecoordinates

this.selection.end=Math.max(this.selection.start,

this.projectLength(event.offsetX));

//Selectionchangedsoweneedtoemiteventan

this.selectionChange.next(this.selection);

//Topreventsideeffects,weshouldstoppropagationand

//preventbrowserdefault

event.stopPropagation();

event.preventDefault();

}

}

//Iftheuserisreleasingthemousebutton,weshouldstopthe

//modifyselectionmode

@HostListener('mouseup')

onMouseUp(){

this.modifySelection=false;

}

//Iftheuserisleavingthecomponentwiththemouse,weshould

//stopthemodifyselectionmode

@HostListener('mouseleave')

onMouseLeave(){

this.modifySelection=false;

}

...

www.EBooksWorld.ir

Page 207: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Intheprecedingcodeexcerpt,wehandledatotaloffoureventsonthesliderhostelement:

onMouseDown:Wesetourselectionmodel'sstartandendpropertieswiththesamevalue.Sincewe'reusingtimestampsfortheseproperties,weprojectedthemousepositionintothetimespacefirst.Themousepositioncomesinpixelsrelativetotheslidercomponent'sorigin.Sinceweknowtheslider'swidthandthetotaltimedurationdisplayed,wecanconvertthisintotimestampseasily.We'reusingtheprojectLengthmethodforthispurpose.Bypassingasecondargumenttothe@HostListenerdecorator,wespecifiedthatwe'dliketopasstheDOMeventtoouronMouseDownmethod.Wealsosetastateflag,modifySelection,inourcomponenttoindicatethataselectionisunderprogress.onMouseMove:Ifthecomponentisinselectionmode(themodifySelectionflagistrue),youcanadjusttheendpropertyoftheselectionobject.Here,wealsomadesurethatweruledoutthepossibilityofcreatinganegativeselectionbyusingMath.maxandlimitingtheendoftheselectiontonotbesmallerthanthestart.onMouseUp:Whentheuserreleasesthemousebutton,thecomponentexitstheselectionmode.ThiscanbedonebysettingthemodifySelectionflagtofalse.onMouseLeave:ThisisthesameastheonMouseUpevent;thedifferenceisthatherethecomponentwilljustexittheselectionmode.

Usingthe@HostListenerdecorator,wewereabletohandleallofthenecessaryuserinputtocompleteourcomponentwiththeinteractiveelementsthatwerestillmissing.

www.EBooksWorld.ir

Page 208: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

RecapInthistopic,welearnedhowtouseSVGinordertocreategraphicalandinteractivecomponentswithAngular.BycreatingattributebindingsonourSVGelementsandcontrollingtheinstantiationofgraphicalelementsusingtheNgForandNgIfdirectives,webuiltacustomslidercomponentthatprovidesaniceoverviewofouractivities.Atthesametime,wealsolearnedhowtohandleuserinputusingthe@HostListenerdecoratorinordertomakeourcomponentinteractive:

Ascreenshotofthefinishedactivityslidercomponent

Tosumthingsup,welearnedaboutthefollowingconcepts:

EncapsulatingcomponentviewsusingViewEncapsulation.NativeandimportinglocalstylesCoveringsomebasicprojectionsoftimestampsontoscreencoordinatestobeusedwithSVGelementsHandlinguserinputandcreatingacustomselectionmechanismusingthe@HostListenerdecorator

www.EBooksWorld.ir

Page 209: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

BuildingtheactivitytimelineSofar,we'vebuiltaservicetologactivitiesandaslidercomponenttoselectatimerangeandprovideanoverviewusingactivityindicators.Sinceweneededtoperformalotofdrawingtaskswithintheslidercomponent,SVGwasaperfectfitforthisusecase.TocompleteourActivitiescomponenttree,westillneedtorendertheactivitiesthatwereselectedusingtheActivitySlidercomponent.

Let'scontinuetoworkonouractivitiescomponenttree.Wellcreateanewcomponentthatwillberesponsibleforrenderinganindividualactivitywithinanactivitytimeline.Let'sstartwiththetemplateoftheActivitycomponent,whichwewillcreateinanewactivities/activity/activity.htmlfile:

<img[attr.src]="activity.user.pictureDataUri"

[attr.alt]="activity.user.name"

class="activity__user-image">

<div[class.activity__info--align-right]="isAlignedRight()"

class="activity__info">

<h3class="activity__title">{{activity.title}}</h3>

<pclass="activity__author">

by{{activity.user.name}}{{activity.time|fromNow}}

</p>

<p>{{activity.message}}</p>

</div>

Eachactivitywillconsistofauserimageaswellasaninformationboxthatwillcontaintheactivitytitle,message,andauthoringdetails.

Ouractivitywilluseaninputtodetermineitsalignment.Thisallowsustoaligntheactivityfromoutsidethecomponent.TheisAlignedRightmethodhelpsussetanadditionalCSSclass,activity__info--align-right,ontheactivityinformationbox.

WealsoneedtocreateacomponentclassforourActivitycomponent,whichwewillcreateunderanewactivities/activity/activity.jsfile:

import{FromNowPipe}from'../../pipes/from-now';

@Component({

selector:'ngc-activity',

//WeareusingtheFromNowpipetodisplayrelativetimes

//withinourtemplate

pipes:[FromNowPipe]

})

exportclassActivity{

@Input()activity;

//Inputthatshouldbeastring'left'or'right'andwill

//determinetheactivityalignmentusingCSS

@Input()alignment;

@Input()@HostBinding('class.activity--start-mark')startMark;

www.EBooksWorld.ir

Page 210: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

@Input()@HostBinding('class.activity--end-mark')endMark;

//Functionwiththatwilltellusiftheactivityshouldbe

//alignedtotheright.It'susedforsettingamodifierclass

//ontheinfoelement.

isAlignedRight(){

returnthis.alignment==='right';

}

}

OurActivitycomponentexpectsfourinputs:

activity:Thispropertytakesthedatamodeloftheactivitythatneedstoberenderedwiththecomponent.ThisistheactivitythatwecreatedusingActivityService.alignment:Thisinputpropertyshouldbesettoastringcontainingthewordleftorright.WeusedthistodeterminewhetherweneededtoaddanadditionalCSSclasstoourtemplateinordertoaligntheactivityinformationboxtotheright.startMark:Thisinputpropertyactsasaninputandahostbindingatthesametime.Ifthisinputissettotrue,theactivitywillgetanadditionalCSSclass,activity--start-mark,whichwillcauseasmallmarkontopofthetimelinetoindicatethetimelinetermination.endMark:InthesamewayasstartMark,thisinputusesahostbindingtosetanadditionalCSSclass,activity--end-mark,whichwillcauseasmallmarkonthebottomofthetimelinetoindicatethetimelinetermination.

TheisAlignedRightmethodisusedwithinthetemplatetodeterminewhetherweneedtoaddanadditionalCSSclasstotheinformationboxinordertoalignittotheright.

WeformattedthetimestampoftheactivityusingtheFromNowpipe,whichwecreatedinChapter4,NoComments,Please!.Inordertousethepipeinthetemplate,weneedtoimportitandaddittothepipespropertyofourcomponentannotation.

Wenowhavealmostallthecomponentstodisplayouractivities.Butstill,there'ssomethingmissing,whichisthegluetocombineActivitySliderwithourActivitycomponentsandalsomakeourcomponentsubtreenavigable.Forthis,we'llcreateanewcomponentcalledActivities.Let'screateanactivities/activities.jsfiletowriteourcomponentclass:

@Component({

selector:'ngc-activities',

...

directives:[ActivitySlider,Activity]

})

exportclassActivities{

@Input()activitySubject;

constructor(@Inject(ActivityService)activityService){

this.activityService=activityService;

}

ngOnChanges(changes){

if(changes.activitySubject){

//Ifwehaveasubscriptiontotheactivitiesservice

www.EBooksWorld.ir

Page 211: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

//alreadyweneedtounsubscribefirst

if(this.activitiesChangeSubscription){

this.activitiesChangeSubscription.unsubscribe();

}

//Whentheprojectdataisupdatedweneedtofilterfor

//activitiesagain

this.activitiesChangeSubscription=

this.activityService.change.subscribe((activities)=>{

//FilterforallactivitiesthathavetheprojectIDassubject

this.activities=activities

.filter((activity)=>activity.subject===

this.activitySubject.document.data._id);

this.onSelectionChange();

});

}

}

Firstofall,weneedtoknowwhichactivitieswewanttodisplaywithinourcomponent.Forthis,weneedtoprovideacomponentinput,namelyactivitySubject.Oncethisisdone,wecanpassanactivitysubjectfromtheparentcomponentanduseittofilteractivitieswe'reinterestedin.

Sincewe'veusedactivitysubjectstologactivitiesaswell,wecanusethesamesubjectstodisplayactivities.InthengOnChangeslifecyclehook,wesetupasubscriptionontheActivityServiceinstancetoreacttonewlycreatedactivities.Becausetheactivityservicewillnotifyuswithanupdatedlistofactivities,wecansimplyusetheArray.prototype.filterfunctiontofilteronlyrelevantitems.We'vemadeuseoftheactivitySubjectinputtoobtaintheIDfromthesubject.

Next,weneedtocreateamethodtoapplyadaterangefiltertoouractivities.TheonSelectionChangemethodwillbecalledfromouractivitiestemplate,wherewecreatedabindingtoourActivitySlidercomponent:

//Iftheselectionwithintheactivitysliderchanges,weneed

//tofilteroutactivitiesagain

onSelectionChange(selection=this.selection){

this.selection=selection;

//Storefilteredactivitiesthatfallintothedaterange

//selectionspecifiedbytheslider

this.selectedActivities=this.selection?this.activities.filter(

(activity)=>activity.time>=this.selection.start

&&activity.time<=this.selection.end

);

}

Wheneverthetimerangeisupdatedbytheuserwithintheslider,we'lloverridetheselectedActivitiesmembervariablewithanewfilteredversionoftheactivities,whichwe'dobtainfromActivityService.Thefilterwillnarrowdowntheactivitiesbycomparingtheactivitytimeagainsttheselectionrangefromtheslidercomponent.

www.EBooksWorld.ir

Page 212: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Nowwewillsetupsomehelperfunctionstobeusedwithinourtemplate:

//Getanalignmentstringbasedontheindex.Activitieswith

//evenindexgetalignedleftwhileoddsgetalignedright.

getAlignment(index){

returnindex%2===0?'left':'right';

}

//Functiontodetermineifanactivityindexisfirst

isFirst(index){

returnindex===0;

}

//Functiontodetermineifanactivityindexislast

isLast(index){

returnindex===this.selectedActivities.length-1;

}

Thethreemethods,namelygetAlignment,isFirst,andisLast,areusedwithinthetemplateasinputfortheActivitycomponent.IfyoutakealookatthecodeofActivityComponentagain,youwillseethatweneedtoprovidesomeinputinordertosetsomeCSSclassesforappearance.Thethreemethodswecreatedherewillbeusedforthispurpose:

//Ifthecomponentgetsdestroyed,weneedtounsubscribefrom

//theactivitieschangeobserver

ngOnDestroy(){

this.activitiesChangeSubscription.unsubscribe();

}

}

Finally,weaddedanOnDestroylifecyclehookthatwillunsubscribeusfromtheactivity'schangeobservable.

Thetemplateforthiscomponentisrathersimple.TheonlythingweneedtodoisrendertheActivitySlidercomponent,aswellasiterateovertheselectedactivitiesandwireintheActivitycomponentforeachiteration:

<ngc-activity-slider[activities]="activities"

(selectionChange)="onSelectionChange($event)">

</ngc-activity-slider>

<divclass="activities__l-container">

<ngc-activity

*ngFor="letactivityofselectedActivities,letindex=index"

[activity]="activity"

[alignment]="getAlignment(index)"

[startMark]="isLast(index)"

[endMark]="isFirst(index)">

</ngc-activity>

</div>

There'snotmuchweneedtoexplainhere.We'veboundactivitiesandouronSelectionChangemethodtotheslidercomponentanditeratedoveralltheselectedactivitiestorenderourActivitycomponents.We'vecreatedalocalviewvariable,index,whichwe

www.EBooksWorld.ir

Page 213: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

willusefortheappearanceinputoftheActivitycomponent.

That'sitforouractivitiespage!We'vecreatedthreecomponentsthatarecomposedtogetheranddisplayanactivitystream,whichprovidesaslidertofilteractivitiesfordates:

Ascreenshotofthefinishedactivitiesview

www.EBooksWorld.ir

Page 214: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

SummaryInthischapter,wecreatedaninteractiveslidercomponentusingSVG.Whiledoingthis,welearnedaboutsomeSVGbasicsandthepowerofSVGwithintheDOM.UsingAngular,wewereabletomakeSVGcomposable,whichitisn'tbynature.Welearnedaboutnamespaces,howAngularhandlesthem,andhowwecantellAngularthatwe'dliketousenamespacesexplicitly.

BesidesusingSVGforourslidercomponent,wealsolearnedhowtouseShadowDOMtocreatenativeviewencapsulation.Asaresultofthis,wewereabletouselocalstylesforourcomponent.Wedon'tneedtoworryaboutCSSnameclashes,specificity,andglobalCSSsideeffectsanymorewhenusinglocalstyles.

ThewholecodeforthischaptercanbefoundintheZIPfileofthebookresourcesthatyoucandownloadfromthePacktPublishingwebsite.

Inthenextchapter,we'regoingtoenhancewhatwe'vebuiltthroughoutthechapterssofar.Wewillcreatesomecomponentstoenrichtheuserexperiencewithinourapplication.

www.EBooksWorld.ir

Page 215: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Chapter7.ComponentsforUserExperienceUserexperienceisacoreconcernfordevelopersbuildingtoday'sapplications.Wearenolongerlivinginaworldwhereusersarecontentedwithanapplicationthatjustworks.Theexpectationsaremuchhigher.Now,anapplicationneedstobehighlyusableandshouldprovideanefficientworkflow;usersalsoexpectittobringthempleasurewhileperformingtasks.

Inthischapter,we'regoingtolookatbuildingsomecomponentsthatwillincreasetheoverallusabilityofourtaskmanagementsystem.Thesefeatureswillenrichthecurrentfunctionalityandprovidemoreefficientworkflows.

Wewilldevelopthefollowingthreetechnicalfeaturesandembedthemintoourcurrentapplication,whereverapplicable:

Tagmanagement:We'llenabletheuseoftagswithingeneratedcontent,suchascomments,activities,andotherareaswheretheycanbeofanyuse.Tagswillhelpusersbuildlinksbetweencontentandnavigationshortcuts.Draganddrop:We'llbuildgenericcomponentsthatwillmakeuseofdraganddropfeaturesabreeze.Byenablingdraganddropfeatures,we'llallowuserstofulfillcertaintaskswithmuchhigherefficiency.Infinitescrolling:We'llbuildacomponentthatwillrevealthecontentoflistswhilescrolling.Thisfeatureisnotgoingtodirectlyincreasetheworkflowperformance,butitwillhelpusincreasetheoverallapplicationperformance.Itwillalsonarrowdowntheuser'scontextbyonlyshowingrelevantinformation.

We'llcoverthefollowingtopicsinthischapter:

CreatingatagmanagementsystemtoenteranddisplaytagsCreatingastatefulpipetorendertagsusingaserviceUsingthesanitize-htmlmoduletosanitizepotentiallyunsafecontentCreatingacomponenttoautocompletetagsduringuserinputGoingthroughthebasicsoftheHTML5draganddropAPICreatingdirectivesfordraggableelementsanddroptargetsUsingdataTransferobjectsandacustomattributetoenableselectivedroptargetsCreatingacustomForOfrepeaterusingtheasterisktemplatesyntaxtoenableinfinitescrollingImplementingcustomchangedetectionusingtheDoChecklifecyclehook,andusingIterableDiffertoapplyDOMchangesPerformingdynamicviewinstantiationusingViewContainer

www.EBooksWorld.ir

Page 216: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

TagmanagementTheclassicalformoftaggingenablesyoutoassociateataxonomywithelementswithinasystemandhelpsyouorganizeyourproject.Itallowsyoutohaveamany-to-manyassociationthatcanbequicklymanaged,andyoucanuseitlatertofilterrelevantinformation.

Inourtaskmanagementsystem,we'regoingtouseaslightlydifferentversionoftags.Ourgoalistoprovideawaytohavesemanticshortcutswithintheapplication.Withthehelpoftags,ausershouldbeabletocross-referenceinformationbetweendifferentpartsofthedata,providingasummaryofthereferencedentityaswellasanavigationshortcutfortheentity.

Forexample,wecanincludeaprojecttagwithinausercomment.AusercanenterthetagbysimplytypingintheprojectID.Whenacommentisdisplayed,weseethetitleoftheprojectandthenumberofopentaskswithintheproject.Butwhenweclickonthetag,wedirectlyreachtheprojectdetailpagewherethetaskislocated.

Inthissection,we'lldeveloptherequiredelementstoprovideawaytouseprojecttagsthatwillenabletheusertocross-referenceotherprojectswithincomments.We'llalsousetagmanagementinouractivities,whichwecreatedinthepreviouschapter.

www.EBooksWorld.ir

Page 217: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

TagdataentityLet'sstartwiththetagentitythatshowshowwecanrepresenttagswithinoursystem.We'llcreateanewTagclassinafileundertags/tag.js:

//Classthatrepresentsatag

exportclassTag{

constructor(textTag,title,link,type){

//ThetextTagpropertyisthetextrepresentationofthetag

this.textTag=textTag;

this.title=title;

this.link=link;

this.type=type;

}

}

Thisclassrepresentstags;wheneverwestoretaginformation,we'llusethisentityasadatavehicle.Let'slookattheindividualfieldsandelaborateontheiruse:

textTag:Thisisthetextrepresentationofatag.Allourtagsneedtobeidentifieduniquelyusingthistextrepresentation.Wecandefinethetextrepresentationoftagsasfollows:

Texttagsalwaysstartwithahashsymbol(#)Texttagsonlycontainwordcharactersortheminussymbol(-)Alltheinnardsofatag,definedbyotherproperties(title,link,andtype),canbeextrapolatedfromthetextTagproperty.ItcanthereforebeconsideredanID.

title:Thisisacomparativelylongertextrepresentationofatag.Itshouldcontainasmuchdetailaboutthesubjectaspossible.Inthecaseofprojecttags,thiscouldmeantheprojecttitle,opentagscount,assignee,andotherimportantinformation.Sincethisisthefieldthatwillberenderedifatagisparsed,it'llbebeneficialifthecontentstaysrelativelycondensed.link:AvalidURL,whichwillbeusedwhenthetagisrendered.ThisURLwillmakelinksclickableandenabletheshortcutnavigation.Inthecaseoftheprojectstagswe'regoingtocreate,thiswillbeaURLfragmentidentifierthatwilllinktothegivenprojectpage.type:Thisisusedtodistinguishbetweendifferenttagsandprovideusawaytoorganizetagsatahighergranularitylevel.

Sofar,sogood.Wenowhaveadatavehiclewecaneasilyconstructtotransferinformationabouttags.

www.EBooksWorld.ir

Page 218: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

GeneratingtagsOurnextstepistocreateafactorythatwillgeneratetagsforus.Allwe'dliketopasstothefactoryisasubject,whichcanbebasicallyanything.Thefactorywillthendeterminethetypeofthesubjectandexecutethenecessarylogictogenerateatagfromit.Thismightsoundabitabstractatfirst,butlet'slookatthecodeofthegenerateTagfunctionwe'llcreateinamoduleundertags/generate-tag.js:

import{Tag}from'./tag';

import{limitWithEllipsis}from'../utilities/string-utilities';

exportconstTAG_TYPE_PROJECT='project';

//ThegenerateTagfunctionisresponsibleforgeneratingnewtag

//objectsdependingonthepassedsubject

exportfunctiongenerateTag(subject){

if(subject.type===TAG_TYPE_PROJECT){

//Ifwe'redealingwithaprojecthere,wegeneratethe

//accordingtagobject

constopenTaskCount=subject.tasks.filter((task)=>!task.done).length;

returnnewTag(

`#${subject._id}`,

`${limitWithEllipsis(subject.title,20)}(${openTaskCount}opentasks)`,

`#/projects/${subject._id}/tasks`,

TAG_TYPE_PROJECT

);

}

}

Let'sexaminethegenerateTagfunctionandwhatwe'retryingtoachievehere.

First,wedeterminedthesubjecttypebycheckingthetypeattributeofthesubjectobject.Inthecaseofprojectdataobjects,weknowthatthetypewillbesetto"project".Thefollowingthreepointssuccinctlyexplainwhatwe'vedone:

1. Sinceweweresurethatweweredealingwithaprojecthere,wegeneratedanewtag.Inthefuture,we'lldealwithothersubjecttypesaswell,sothischeckwillberequired.

2. Wewantedtouseanindicatorforalltheopentasksinthetagtitle.Forthisreason,wedidaquickfilteringofopentaskswithintheprojectandstoredthelengthofthefilteredarrayintheopenTaskCountconstant.

3. NowwecaninstantiateanewTagobjectusingtheprojectIDastextTag.Forthetitlefield,weusedahelperfunction,limitWithElipsis,whichtruncatesprojecttitlesthatarelongerthan20characters.Wealsoappendedtheopentaskscounttothetagtitle.ForthelinkfieldoftheTaginstance,wespecifyaURLthatwillnavigatetotheprojectdetailsview.Finally,weusedtheTAG_TYPE_PROJECTconstanttodefinethetagtypefield.

www.EBooksWorld.ir

Page 219: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

CreatingatagsserviceOkay,we'redonewithsettingupallthesupportingstructuresweneed;wecannowmoveforwardtocreateatagsservice.Atagsservicewillhavethefollowingresponsibilities:

Generatingandcachingtags:Wewon'tcreatetagsinoursystemadhocifweonlywanttorenderthem.Themechanicsofatagsserviceismorelikegeneratedcache.Initially,atagsservicegathersalltherequiredinformationtogenerateallthepossibletagswithinthesystem.Italsoreactstochangesandupdatesthelistoftagsifrequired.Withthis,we'llnotonlysaveonsomeprocessingneeds,butwe'llalsohaveareadilyavailablelisttosearchforexistingtags.Thiswillbeparticularlyusefulifweliketopresenttheavailabletagstotheusersotheycanchoosefromthem.Renderingtags:AtagsserviceisalsoresponsibleforturningtagsintoHTML.ItusesthetitleandlinkfieldsoftheTaginstancestogeneratetheirHTMLrepresentation.Parsingtextcontent:Theparsingfunctionalityofthetagsserviceisresponsibleforfindingtextrepresentationsoftagswithinastring.ItthenusestherenderingfunctiontorenderthesetagsintoHTML.

Let'screateamoduleforourtagsserviceinanewfileundertags/tags-service.js.

First,weneedtocreatetwoutilityfunctionsthatwillhelpusprocesstagsandstringscontainingthetextualrepresentationsoftags.

ThereplaceAllfunctionisasimplesubstituteforamissingJavaScriptfunctiontoreplacemultipletextoccurrenceswithinastringwithoutusingregularexpressions:

//Utilityfunctiontoreplacealltextoccurrencesinastring

functionreplaceAll(target,search,replacement){

returntarget.split(search).join(replacement);

}

ThefindTagsfunctionwillextractanypossibletagfromatextstring.Itdoesthisbyapplyingaregularexpressionthatwillfindmatchesfortagsintheformatdiscussedatthebeginningofthetopic.Thisformatassumesthatourtagsalwaysstartwithahashsymbol,followedbyanywordcharacterordashsymbols.Thisfunctionreturnsalistofallthepossibletexttags:

//Functiontofindanytagswithinastringandreturnanarray

//ofdiscoveredtags

functionfindTags(str){

constresult=[];

constregex=/#[\w\/-]+/g;

letmatch;

while(match=regex.exec(str)){

result.push(match[0]);

}

returnresult;

}

Forourtagsservice,wewillnowdefineanewclassthatwillbeannotatedwith@Injectable

www.EBooksWorld.ir

Page 220: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

sowecanuseitasaproviderinourcomponents:

@Injectable()

exportclassTagsService{

...

}

Let'slookattheconstructorofourTagsServiceclass:

constructor(@Inject(ProjectService)projectService){

//Iftheavailabletagswithinthesystemchanges,wewill

//emitthisevent

this.change=newReplaySubject(1);

//Inordertogenerateprojecttags,we'remakinguseofthe

//ProjectService

this.projectService=projectService;

this.projectService.change.subscribe((projects)=>{

//Onprojectchangeswestorethenewprojectlistandre-

//initializeourtags

this.projects=projects;

this.initializeTags();

});

}

Inordertogenerateandcacheprojecttags,weobviouslyneedProjectService,whichprovidesuswithalistofalltheprojects.InsteadofgrabbingthelistdatafromProjectServiceonce,we'reobservingthelistforchanges.Thisbringsustheadvantagethatwe'llnotonlygettheinitiallistofprojects,butwe'llalsobemadeawareofanychangesmadeintheprojectlist.

WesubscribedtoProjectServiceusingthechangefield.ThisexposesReplaySubject,whichemitstheprojectlist.Afterstoringthecurrentprojectlistasamemberfield,weneedtocalltheinitializeTagsmethod:

//Thismethodisusedinternallytoinitializeallavailabletags

initializeTags(){

//We'recreatingtagsfromallprojectsusingthegenerateTag

//function

this.tags=this.projects.map(generateTag);

//Sincewe'veupdatedthelistofavailabletagsweneedto

//emitachangeevent

this.change.next(this.tags);

}

Asweonlysupportprojecttagscurrently,theonlythingweneedtoconsiderwhilegeneratingtagsistheprojectswehavestoredinourservice.WecansimplymaptheprojectlistwehavestoredintheprojectsmemberfieldusingourgenerateTagfunction.TheArray.prototype.mapfunctionwillreturnanewarraythatisalreadyalistofgeneratedtasksfortheprojects.

www.EBooksWorld.ir

Page 221: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

RenderingtagsOkay,wenowhaveaservicethatusesareactiveapproachtogeneratetagsfromtheavailableprojects.Thisisalreadyaddressingthefirstconcernofourservice.Let'slookatitsotherresponsibilities,whichareparsingtextcontentfortagsandrenderingHTML.

Renderingtagsisnotabigdealsincewehavealreadyabstractedthedatamodeloftagsinacleanway.Weneedtowriteamethodforrenderingtagsthatwillactasapass-throughfunctioniftheargumentisnotavalidTaginstance.Thisway,wecanpassunrecognizedtextrepresentationsoftagsasstrings,anditwilljustreturnusthestring.

SincetagshaveURLsthatpointtoalocation,we'regoingtouseanchorHTMLelementstorepresentourtags.Theseelementsalsohaveclassesthatwillhelpusstyletagsdifferentlythanregularcontent.Let'screateanothermethodwithinthetagsservicethatcanbeusedtorendertagobjectsintoHTML:

renderTag(tag){

if(taginstanceofTag){

return`<aclass="tags__tagtags__tag--${tag.type}"

href="${tag.link}">${tag.title}</a>`;

}else{

returntag;

}

}

Thefollowingmethodcanbeusedtofindatagbyitstextualrepresentation.Thisfunctionwilltrytofindthetagwithinourgeneratedcache,andifunsuccessful,willreturnthetextTagargument.Thisisalsoapass-throughmechanismthatsimplifiesthehandlingwhenweparseawholepieceoftextfortags:

//Thismethodwilllookupatagviaitstextrepresentationor

//returntheinputargumentifnotfound

parseTag(textTag){

returnthis.tags.find(

(tag)=>tag.textTag===textTag

)||textTag;

}

Lastbutnotleast,let'simplementthemainmethodoftheservice.TheparsefunctionscansthewholetextfortagsandreplacesthemwiththeirHTMLrepresentation:

//Thismethodtakessometextinputandreplacesanyfoundand

//validtextrepresentationsoftagswiththegeneratedHTML

//representationofthosetags

parse(value){

//Firstwefindallpossibletagswithinthetext

consttags=findTags(value);

//Foreachfoundtexttag,we'reparsingandrenderingthem

//whilereplacingthetexttagwiththeHTMLrepresentation

//ifapplicable

tags.forEach(

www.EBooksWorld.ir

Page 222: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

(tag)=>value=replaceAll(value,tag,

this.renderTag(this.parseTag(tag))));

);

//Afteralltagshavebeenrendered,we'reusingasanitizer

//toensuresomebasicsecurity

returnvalue;

}

First,weneedtousethefindTagsutilityfunction;thiswillreturnalistofallthetexttagsthatitwouldfindinthestringcontentpassedtotheparsefunction.Usingthistexttaglist,wecantheniteratethroughthelistandsuccessivelyreplaceallthetexttagsinthecontentwiththegeneratedHTMLusingtherenderTagmethod.

www.EBooksWorld.ir

Page 223: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

IntegratingthetaskserviceAlltheconcernsofourtaskservicehavenowbeentakencareof,anditisalreadystoringtagsfortheavailableprojects.Wecannowgoaheadandintegrateourserviceintotheapplication.

SinceourtagsserviceturnstextwithsimplehashtagsintoHTMLwithlinks,apipewouldbeaperfecthelpertointegratethefunctionalitywithinourcomponents.

Let'screateatags.jsfileinourpipesfolderandcreateanewpipeclass,namely,Tags:

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

import{TagsService}from'../tags/tags-service';

@Pipe({

name:'tags',

//Sinceourpipeisdependingonservices,we'redealingwitha

//statefulpipeandthereforesetthepureflagtofalse

pure:false

})

exportclassTagsPipe{

constructor(@Inject(TagsService)tagsService){

this.tagsService=tagsService;

}

//Thetransformmethodwillbecalledwhenthepipeisusedwithinatemplate

transform(value){

if(typeofvalue!=='string'){

returnvalue;

}

//ThepipeisusingtheTagsServicetoparsetheentiretext

returnthis.tagsService.parse(value);

}

}

Wehavealreadycreatedafewpipessofar.However,thispipeisabitdifferentinthatitisn'tapurepipe.Pipesareconsideredpureiftheirtransformfunctionalwaysreturnsthesameoutputforagiveninput.Thisimpliesthatthetransformfunctionshouldnotbedependentonanyotherexternalsourcethatcaninfluencetheoutcomeofthetransform,andtheonlydependenciesaretheinputvalues.ThisisnottrueforourTagspipethough.ItdependsonTagsServicetotransformtheinput,andnewtagscanbestoredinthetagsserviceatanytime.Successivetransformationscansuccessfullyrendertagsthatwerenotexistentjustamomentago.

BytellingAngularthatourpipeisnotpure,wecandisabletheoptimizationitperformsonpurepipes.ThisalsomeansthatAngularwillneedtorevalidatetheoutputofthepipeoneverychangedetection.Thiscanleadtoperformanceissues;therefore,thepureflagshouldbeusedwithcaution.

Allright,asfarasrenderingtagsisconcerned,weareallset.Let'sintegrateourtagsfunctionalityintoourEditorcomponentsowecanmakeuseofthemwithinthecommenting

www.EBooksWorld.ir

Page 224: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

system.

Let'sstartbyeditingtheEditormodulelocatedunderui/editor/editor.js:

...

import{TagsPipe}from'../../pipes/tags';

@Component({

selector:'ngc-editor',

...

pipes:[TagsPipe]

})

exportclassEditor{

...

@Input()enableTags;

...

}

First,weimportedtheTagsPipeclassandreferencedittothepipesconfigurationofthe@Componentannotation.

We'vealsoaddedanewinputtotheenableTagscomponent,whichwillallowustocontrolwhetherweshouldhandletagswithinthecontentoftheeditororignorethem.

That'sit,asfaraschangestothecomponentfileisconcerned.Let'sapplysomechangestothetemplateofthecomponentbyeditingtheui/editor/editor.htmlfile:

...

<div*ngIf="enableTags"class="editor__output"

[innerHtml]="(content||'-')|tags"></div>

<div*ngIf="!enableTags"class="editor__output">

{{content||'-'}}

</div>

...

Theonlychangewe'vemadeinthetemplateiswherewedisplaytheeditorcontent.We'veusedtwotemplateelementsbyemployingtheNgIfasterisktemplatesyntax.Thelatterone,iftagsaredisabled,rendersthecontentasbefore.Iftagsareenabled,we'llbeusingapropertybindingtotheinnerHTMLpropertyofoureditor'soutputHTMLelement.ThisallowsustorendertheHTMLcontent.Inthebinding,we'veusedourTagspipethatwillparsethecontentfortagsusingTagService.

www.EBooksWorld.ir

Page 225: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

CompletionofthetagsserviceLet'sdigressforamomentatthispoint.We'vealreadycreatedataggingsystem,andwejustintegrateditintoourEditorcomponentbyusingtheTagspipe.Ifauserwritesprojecttagsinanycommentnow,theywillberenderedbyTagsService.Thisisfantastic!Userscannowestablishcross-linkstootherprojectswithincomments,whichwillbeautomaticallyrenderedaslinksshowingtheprojecttitleandopentasks.Allauserneedstodoisaddthetextrepresentationsofprojecttagstoacomment.Inthedefaultdatasetofthebook,thiscouldbethe#project-1string.

Thefollowingtwoimagesshowyouanexampleofthecommentingsystem.Thefirstimageisanexampleofaneditorineditmode,underthecommentingsystem,whereatexttagisentered:

Anexamplewhereatexttagisentered

Thesecondimageisanexampleofarenderedtagenabledinthecommentingsystemthroughoureditorintegration:

Anexampleofrenderedtagthrougheditorintegration

We'renotdoneyetwhenitcomestoenteringtags.Wecannotexpectouruserstoknowallthe

www.EBooksWorld.ir

Page 226: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

availabletagswithinthesystemandthenenterthemmanuallywithincomments.Let'slookathowwecanimprovethisinthenextsection:

Inthissection,welookedatthefollowingconcepts:

1. Webuiltatagsservicethatgenerates,caches,andrenderstags.2. Webuiltastatefulpipeusingthepureflag.3. Weusedthe[innerHTML]propertybindingtorenderHTMLcontentintoanelement.

www.EBooksWorld.ir

Page 227: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

SupportingtaginputHere,we'regoingtobuildacomponentanditssupportingstructurestomaketheprocessofenteringtagsasmoothexperienceforourusers.Sofar,theycanwriteprojecttags,butitrequiresthemtoknowtheprojectIDs,whichmakesourtagmanagementquiteuseless.Whatwe'dliketodoisprovidetheuserwithsomechoiceswhentheyareabouttowriteatag.Ideally,weshowthemtheavailabletags,assoonastheystartwritingatagbytypingthehash(#)symbol.

Whatsoundssimpleinthefirstplaceisactuallyquiteatrickythingtoimplement.Ourtaginputneedstodealwiththefollowingchallenges:

Handlinginputeventstomonitortagcreation.Somehow,weneedtoknowwhenauserstartswritingatag,andweneedtoknowwhenthetypedtagnameisupdatedorcanceledbyusinganinvalidtagcharacter.Calculatingthepositionoftheinputcaretoftheuser.Yeah,Iknowthissoundsprettysimple,butitactuallyisn't.Calculatingtheviewportoffsetpositionofauser'sinputcaretrequirestheuseofthebrowser'sSelectionAPI,whichisquitelow-levelandneedssomeabstraction.

Inordertotacklethesechallenges,wearegoingtointroduceautilityclasswecandelegatetheuserinputto.Itwillhelpusfigureoutthedetailswe'reinterestedinanddealwithlow-levelAPIs.

www.EBooksWorld.ir

Page 228: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

CreatingataginputmanagerCreateamoduleinanewfileundertags/tag-input-manager.js.Thefirstbitofcodeisafunctionthatwillhelpusfigureoutthepositionoftheuserinputcaretwhentheuserstartstypingatag:

//Thisfunctioncanbeusedtofindthescreencoordinatesofthe

//inputcursorposition

functiongetRangeBoundlingClientRect(){

constselection=window.getSelection();

if(!selection.rangeCount)return;

constrange=selection.getRangeAt(0);

if(!range.collapsed){

returnrange.getBoundingClientRect();

}

constdummy=document.createElement('span');

range.insertNode(dummy);

constpos=dummy.getBoundingClientRect();

dummy.parentNode.removeChild(dummy);

returnpos;

}

Let'snotgointotoomuchdetailhere.WhatthiscodebasicallydoesisthatittriestofindtheboundingboxDOMRectobject,whichdescribesthetop,right,bottom,andleftoffsetsofthecaretpositionrelativetotheviewport.TheproblemisthattheSelectionAPIdoesnotallowustogetthepositionofthecaretdirectly;itonlyallowsustogetthepositionofthecurrentselection.Incasethecaretisnotplacedcorrectly,wewillneedtoinsertadummyelementatthelocationofthecaretandreturntheboundingboxDOMRectobjectofthedummyelement.Ofcourse,we'dneedtoremovethedummyelementagainbeforewereturntheDOMRectobject.

Nowlet'screateanewclass,TagInputManager,underlib/tags/tag-input-manager.js,whichwilldealwiththeuserinputhandlingfortagcreation:

exportclassTagInputManager{

constructor(){

this.reset();

}

...

Intheconstructor,weneedtocallaninternalresetmethod.ThisresetmethodwillresetthetwomemberfieldsthatTagInputManagerwillexpose.Thepositionmemberwillstorethepositionofthelatestcaret,wheretheuserhadstartedwritingatag.ThetextTagmemberwillstorethecurrenttag,whichisrecognizedbyTagInputManager:

reset(){

this.textTag='';

this.position=null;

www.EBooksWorld.ir

Page 229: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

}

Nowlet'screateamethodtodetermineifauserisintheprogressofenteringatag.IfthetextTagmembercontainsahashsymbolatthebeginning,wecanassumethatthereisatagenteringinprogress:

hasTextTag(){

returnthis.textTag[0]==='#';

}

Wealsoneedamethodthatwillallowustoupdateboththecurrenttexttag,whichisentered,aswellastheupdatedcaretposition:

updateTextTag(textTag,position=this.position){

this.textTag=textTag;

this.position=position;

}

WithintheonKeyDownmethod,weexpecttoreceivedelegatedkeydownevents.Weareconcernedaboutthebackspace,whichshouldalsoremovethelastcharacterofthetagthatiscurrentlyentered.

onKeyDown(event){

//Ifwereceiveabackspace(keycodeis8),weneedto

//removethelastcharacterfromthetexttag

if(event.which===8&&this.hasTextTag()){

this.updateTextTag(this.textTag.slice(0,-1));

}

}

IntheonKeyPressmethod,weexpecttoreceivedelegatedkeypressevents.Thisiswherethemainlogicofthissupportingclasslies.Here,wehandletwodifferentcases:

Ifthepressedkeyisahashsymbol,wewillstartoverwithanewtag.Ifthepressedkeyisnotavalidwordcharacterorahashsymbol,wewillresetittoitsinitialstate,whichwillcancelthetagentry.Otherwise,it'dmeanthatwearedealingwithavalidtagcharacter,andwe'lladdittothecurrenttexttagstring.

Thecodeforthisisasfollows:

onKeyPress(event){

constchar=String.fromCharCode(event.which);

if(char==='#'){

//Ifthecurrentcharacterfromuserinputisahashsymbol

//wecaninitiateanewtexttagandsetthecurrent

//position

this.updateTextTag('#',getRangeBoundlingClientRect());

}elseif((/[\w-]/i).test(this.textTag[0])){

//Ifthecurrentcharacterisnotavalidtagcharacterwe

//resetourstateandassumethetagentrywascanceled

this.reset();

}elseif(this.hasTextTag()){

//Ifwehaveanyothervalidtagcharacterinput,we're

www.EBooksWorld.ir

Page 230: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

//updatingourtexttag

this.updateTextTag(this.textTag+char);

}

}

Okay,sonowwehaveallthesupportweneedtohandletaginput.However,westillneedawaytoshowtheavailabletagsfromTagsServicetotheuser.Forthispurpose,we'llcreateanewTagsSelectcomponent.

www.EBooksWorld.ir

Page 231: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

CreatingatagsselectcomponentTosupporttheuserinfindingtherighttag,we'llprovidethemwithadropdownwiththeavailabletags.Todothis,weneedtouseourTagInputManagerclasstorecognizetagswithinuserinputaswellasfiltertheavailabletagswithuserinput.Let'sbrieflylookattherequirementsofthiscomponent:

DisplaytheavailabletagsgatheredfromTagsServiceinatooltip/calloutboxItshouldsupportalimitationofdisplayedtagsItshouldsupportaninputtofiltertheavailabletagsThecomponentshouldacceptaninputparametertopositionthecalloutboxItshouldemitaneventoncetheuserclicksonataginthelistedtagsThecomponentshouldhideitselfifthefilterisinvalidoriftherearenoelementsmatchingthefilter:

Finishedtagsselectcomponentfilteredwithuserinput

Let'sstartwiththecomponentclassandseehowwefulfilltheserequirements.First,createanewfilecalledtags-select.jsundertags/tags-select:

...

@Component({

selector:'ngc-tags-select',

...

})

exportclassTagsSelect{

...

}

[email protected]'sstartwithimplementingtheinnardsofourcomponent.First,we'lldefinethefollowinginputinthecomponent:

@Input()filter;

www.EBooksWorld.ir

Page 232: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Usingthefilterinput,wecanpassafiltertagtotheTagsSelectcomponent.Thismeansthatwe'llusethefilterinputtofiltertheavailabletagsbytitleandtexttags.

Thelimitinputcanbesettoanynumber.Thisinputisusedtolimitthenumberoffilteredtagsthatcouldbedisplayedwithinthecomponent:

@Input()limit;

ThepositioninputshouldbesettoavalidDOMRectobjectthatcontainsthetopandleftproperties.Theywillbeusedtopositionourcomponent:

@Input()position;

ThetagSelectedoutputpropertyisusedtoemitaneventoncetheuserhasclickedonatagwithinthelistoftags:

@Output()tagSelected=newEventEmitter();

Thefollowingaccessorpropertyisboundtothehostelement'sdisplaystyleproperty.Itwillcontrolwhetherthecomponentisdisplayedorhidden.Weonlydisplaythecomponentifthefilterisvalidandthefilteredtagscontainatleastonetag:

@HostBinding('style.display')

getisVisible(){

if(this.filter[0]==='#'&&this.filteredTags.length>0){

return'block';

}else{

return'none';

}

}

Thefollowingtwoaccessorpropertiesusehostbindingstosetthetopandleftstylesofourhostelementbasedonthepositioninputofthecomponent:

@HostBinding('style.top')

gettopPosition(){

returnthis.position?`${this.position.top}px`:0;

}

@HostBinding('style.left')

getleftPosition(){

returnthis.position?`${this.position.left}px`:0;

}

Let'sinjectTagsServiceintoourcomponentsowecanaccessthelistofavailabletags:

constructor(@Inject(TagsService)tagsService){

this.tagsService=tagsService;

//Thismemberisstoringthefilteredtaglist

this.filteredTags=[];

this.filter='';

}

www.EBooksWorld.ir

Page 233: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

WeneedtousetheOnInitlifecyclehooktosetupasubscriptiontotheTagServicechangeobservable.Thisway,we'llgetaccesstotheinitiallistoftagsaswellasanychangesinthelist.Afterwereceiveanewlistoftags,wewillneedtoreapplythefiltering:

ngOnInit(){

//TheTagsServiceisprovidinguswithallavailabletags

//withintheapplication

this.tagsSubscription=this.tagsService.change.subscribe(

(tags)=>{

//Iftheavailabletagschangewestorethenewlistand

//executefilteringagain

this.tags=tags;

this.filterTags();

}

);

}

Thefollowingisthemethodthatwillbecalledfromthetemplateifatagisclicked.We'lljustre-emitthattagusingthetagSelectedoutput:

onTagClick(tag){

this.tagSelected.next(tag);

}

ThefilterTagsmethodisresponsibleforfilteringandlimitingourtaglistbasedonthefilterandlimitinputpropertiesandtheavailabletagsfromTagsService.Asaresult,itwillstorethefilteredandlimitedlistinthefilteredTagsmemberfield:

filterTags(){

this.filteredTags=this.tags

.filter((tag)=>{

returntag.textTag.indexOf(this.filter.slice(1))!==-1||

tag.title.indexOf(this.filter.slice(1))!==-1;

})

.slice(0,this.limit);

}

Iftheinputpropertiesfilterorlimitchanges,wewillneedtoreapplyourfilteringmethod.ByimplementingthengOnChangeslifecyclehook,wecaneasilymanagethisrequirement:

ngOnChanges(changes){

//Ifthefilterorthelimitinputchanges,we'refilteringthe

//availabletagsagain

if(this.tags&&(changes.filter||changes.limit)){

this.filterTags();

}

}

Finally,weshouldunsubscribefromtheTagsServicechangeobservableiftheTagsSelectcomponentisdestroyed:

ngOnDestroy(){

this.tagsSubscription.unsubscribe();

www.EBooksWorld.ir

Page 234: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

}

Thetemplateforourcomponentisrathersimple.Let'slookattheviewtemplatethatisstoredundertags/tags-select/tags-select.html:

<ulclass="tags-select__list">

<li*ngFor="lettagoffilteredTags"

(click)="onTagClick(tag)"

class="tags-select__item">{{tag.title}}</li>

</ul>

WeusedtheNgFordirectivetoiterateoverallthetagswithinthefilteredTagsmember.Ifatagisclicked,wewillneedtoexecutetheonTagClickedmethodandpassthetagofthecurrentiteration.Inthelisting,we'llonlydisplaythetagtitlethatshouldhelptheuseridentifythetagtheywouldliketouse.

Nowwehavebuiltallthepiecesthatweneedtoenablesmoothtagenteringforourusers.Let'spatchourEditorcomponentagaintoincludeourchanges.

www.EBooksWorld.ir

Page 235: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

IntegratingtaginputwithintheeditorcomponentAsthefirststep,weshouldamendourEditorcomponenttoutilizetheTagInputManagerclass.Weneedtodelegatetheuserinputinsidethecontent-editableelementtothetaginputmanagersoitcandetectanytagentering.Then,we'llusetheinformationfromTagInputManagertocontrolaTagsSelectorcomponent.

First,let'slookattherequiredchangestobemadeinsidetheComponentclasslocatedunderui/editor/editor.js:

...

import{TagsSelect}from'../../tags/tags-select/tags-select';

import{TagInputManager}from'../../tags/tag-input-manager';

@Component({

selector:'ngc-editor',

...

directives:[TagsSelect]

})

exportclassEditor{

...

//We'reusingaTagInputManagertohelpusdealingwithtag

//creation

this.tagInputManager=newTagInputManager();

}

...

//Thismethodiscalledwhentheeditableelementreceivesa

//keydownevent

onKeyDown(event){

//We'redelegatingthekeydowneventtotheTagInputManager

this.tagInputManager.onKeyDown(event);

}

//Thismethodiscalledwhentheeditableelementreceivesa

//keypressevent

onKeyPress(event){

//We'redelegatingthekeypresseventtotheTagInputManager

this.tagInputManager.onKeyPress(event);

}

//ThismethodiscalledifthechildTagSelectcomponentis

//emittinganeventforaselectedtag

onTagSelected(tag){

//Wereplacethepartialtexttagwithintheeditorwiththe

//textrepresentationofthetagthatwasselectedinthe

//TagSelectcomponent.

this.setEditableContent(

this.getEditableContent().replace(

this.tagInputManager.textTag,tag.textTag

)

);

this.tagInputManager.reset();

}

...

www.EBooksWorld.ir

Page 236: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

}

Inour@Componentannotation,weaddedtheTagsSelectcomponenttothedirectivespropertysowecouldusethecomponentwithinthetemplate.

Tohelpusdoallthelow-levelprocessingfortagentry,weusedTagInputManagerandcreatedanewinstanceofitwithinthecomponentconstructor.

Wehavenowcreatedtwomethodsforhandlingkeypressandkeydowneventscomingfromourcontent-editableelement.ThesemethodsdelegatetheeventstoTagInputManager,whichwillhandlealloftheprocessingtoextractatexttagandthepositionofthecaret.

Finally,weaddedamethodthatwillbecalledonceatagisclickedwithintheTagsSelectcomponent.Here,wesimplyreplacedthetexttagthatiscurrentlyenteredwiththetextrepresentationofthetagthatwasclicked.Thisprovidesanaiveimplementationofsomesortofautocomplete.Afterweaddedthetextrepresentationoftheclickedtagtothecontent-editableelement,weresetTagInputManagertoclearitsstate.

TheonlybitleftnowistotoeditthetemplateoftheEditorcomponentinordertoincludetheTagsSelectcomponent.

Intheui/editor/editor.htmlfile,weneedtomakethefollowingchanges:

...

<ngc-tags-select*ngIf="enableTags"

[filter]="tagInputManager.textTag"

[position]="tagInputManager.position"

[limit]="5"

(tagSelected)="onTagSelected($event)">

</ngc-tags-select>

TheNgIfdirectivehelpsusavoidthecomponentfrombeingcreatediftagsarenotenabledwithintheeditor.

WesetthefilterandpositioninputoftheTagsSelectcomponentfromthedatawehaveinourTagInputManagerinstance.

OntheemittedtagSelectedeventoftheTagsSelectcomponent,wecalledtheonTagSelectedmethodontheEditorcomponentwecreatedamomentago.

That'sallweneedtodowiththetemplateoftheEditorcomponent.

www.EBooksWorld.ir

Page 237: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

FinishingupourtaggingsystemCongratulations!You'venowsuccessfullyimplementedthefirstofthethreeusabilitycomponents.

WiththehelpofaTagInputManagerclass,weoffloadedheavylow-levelhandlingofuserinputandtheprocessingoftheusercaretposition.Then,wecreatedacomponenttodisplaytheavailabletagstotheuserandprovidedawayforthemtoselectatagbyclickingonit.InourEditorcomponent,weusedtheTagInputManagerclasstogetherwiththeTagsSelectcomponenttoenablethesmoothenteringoftagswhileeditingcommentsandotherareaswherewe'veenabledtagging.

We'vecoveredthefollowingconceptsinthissection:

1. Weprocessedcomplexuserinputwithinadesignatedmanagerclasstooffloadlogicfromourcomponents.

2. Weusedhostbindingstosetpositionalstyleattributes.3. Weimplementedfullyreactivecomponentsthatrelyonobservablesanddon'tcreateside

effectsduringchangedetection.

www.EBooksWorld.ir

Page 238: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

DraganddropWehavelearnedtouseourcomputermouseandkeyboardwithgreatefficiency.Usingkeyboardshortcuts,differentclickactionsandcontextualmousemenussupportusnicelywhenperformingtasks.However,thereisonepatternthathasgainedmoreattentionagaininapplicationslately,giventhecurrentmobileandtouchdeviceshype.Draganddropactionsareaveryintuitiveandlogicalwaytoexpressactionssuchasmovingorcopyingitems.Onetaskperformedonuserinterfacesbenefitsfromdraganddropparticularly,whichisorderingitemswithinalist.Ifweneedtoorderitemsviaactionmenus,itgetsveryconfusing.Movingitemsstepbystepusingtheupanddownbuttonsworksgreat,butittakesalotoftime.Ifyoucandragitemsaroundanddroptheminaplacewhereyou'dlikethemtobereordered,youcansortalistofitemsextremelyfast.

Inthistopic,wewillbuildtherequiredelementstoenabledraganddropselectively.Wewillusethedraganddropfeaturetoenableuserstoreordertheirtasklists.Bydevelopingreusabledirectivestoprovidethisfunctionality,wecanenablethefeatureatanyotherspotwithinourapplicationlateron.

Toimplementourdirectives,wewillmakeuseoftheHTML5draganddropAPI,whichissupportedinallthemajorbrowsersatthetimeofwritingthisbook.

Sincewewouldliketoreuseourdraganddropbehavioronmultiplecomponents,wewillusedirectivesfortheimplementation.Wearegoingtocreatetwodirectivesinthissection:

Draggabledirective:Thisdirectiveshouldbeattachedtocomponents,whichshouldbeenabledfordraggingDraggabledropzonedirective:Thisdirectiveshouldbeattachedtocomponentsthatwillactasadroptarget

We'llalsoimplementafeaturewherewecanbeselectiveaboutwhatcanbedraggedwhere.Forthis,wewilluseatypeattributeonourdraggabledirectivesaswellanacceptedtypeattributeonourdropzones.

www.EBooksWorld.ir

Page 239: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

ImplementingthedraggabledirectiveThedraggabledirectivewillbeattachedtotheelementthatcanbedraggedontootherelements.Let'sgetstartedwithcreatinganewdirectiveclassunderdraggable/draggable.js:

...

@Directive({

selector:'[draggable]',

host:{

class:'draggable',

//AdditionallytotheclasswealsoneedtosettheHTML

//attributedraggabletoenabledraggablebrowserbehavior

draggable:'true'

}

})

exportclassDraggable{

...

}

Insteadofusingthe@Componentannotation,we'venowusedthe@DirectiveannotationtoletAngularknowthatthefollowingclassisadirectiveclass.BysettingtheHTMLattributedraggabletotrue,wetellthebrowserthatwe'reconsideringthiselementadraggableelement.

Tip

Thebigdifferenceofusingdirectivesincomparisontocomponentsisthattheydon'tembraceaviewbutonlybehavior.Therefore,it'salsopossibletousemanydirectivesonthesameelement,whichisnotpossiblewithcomponents.

Let'slookattheinputforournewlycreatedcomponentclass:

@Input()draggableData;

ThedraggableDatainputisusedtospecifythedatathatrepresentstheelementwhichcanbedragged.ThisdatawillbeserializedtoJSONandtransferredtoourdropzonesonceadragactioniscompleted.

Byspecifyingadraggabletype,wecanbemoreselectivewhentheelementisdraggedoveradropzone.Withinthedropzone,wecanhaveacounterpartthatcontrolswhattypesareacceptabletobedropped.

@Input()draggableType;

Additionallytoourinput,wealsowanttouseahostbindingtosetaspecialclassiftheelementiscurrentlydragged:

@HostBinding('class.draggable--dragging')dragging;

Thisbindingwillsetadraggable--draggingclass,whichwillapplysomespecialstylesthat

www.EBooksWorld.ir

Page 240: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

willmakeiteasytorecognizethatanelementisdragged.

Nowweneedtohandletwoeventswithinourdirectivetoimplementthebehaviorofadraggableelement.ThefollowingDOMeventsaretriggeredbythedraganddropDOMAPI:

dragstart:Thiseventisemittedonelementsthataregrabbedandmovedacrossthescreendragend:Ifthepreviouslyinitiateddraggingoftheelementisended,becauseofasuccessfuldroporareleaseoutsideofavaliddroptarget,thisDOMeventwillbetriggered.

Let'slookattheimplementationofHostListenerforthedragstartevent:

//We'relisteningforthedragstarteventandinitializethe

//dataTransferobject

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

onDragStart(event){

event.dataTransfer.effectAllowed='move';

//SerializeourdatatoJSONandsetitonourdataTransfer

//object

event.dataTransfer.setData(

'application/json',

JSON.stringify(this.draggableData));

//ByaddingthedraggableTypeasadatatypekeywithinour

//ThedataTransferobject,weenabledropzonestoobservethetype

//beforereceivingtheactualdrop.

event.dataTransfer.setData(

`draggable-type:${this.draggableType}`,'');

this.dragging=true;

}

Nowlet'sdiscussthedifferentactionswewillperformintheimplementationofourhostlistener:

1. WewillneedtoaccesstheDOMeventobjectinourhostlistener.Ifweweretocreatethisbindingwithinthetemplate,wewouldprobablyneedtowritesomethingsimilartothis:(dragstart)="onDragStart($event)".Withineventbindings,wecanmakeuseofthesyntheticvariable$event,whichisareferencetotheeventthatwouldhavetriggeredtheeventbinding.Ifweweretocreateaneventbindingonourhostelementusingthe@HostListenerannotation,wewouldneedtoconstructtheparameterlistforthebindingusingthesecondargumentofthedecorator.

2. ThefirstactioninoureventlisteneristosetthedesiredeffectAllowedpropertyonthedataTransferobject.Currently,weonlysupportthemoveeffectasourmainconcernistoreordertaskswithinthetasklistusingdraganddrop.ThedraganddropAPIisverysystem-specific,butusuallytherearedifferentdrageffectsifauserholdsamodifierkey(suchasCtrlorShift)whileinitiatingthedragging.Withinourdraggabledirective,wecanforcethemoveeffectforalldragactions.

3. Inthenextcodesnippet,wewillsetthedatathatshouldbetransferredbydragging.It'simportanttounderstandthecorepurposeofthedraganddropAPI.Itdoesnotonly

www.EBooksWorld.ir

Page 241: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

provideawaytoimplementdraganddropforelementssolelyinyourDOM,butitalsosupportsthedraggingoffilesandotherobjectsintoyourbrowser.Becauseofthis,theAPIundergoessomeconstraints,whereoneofthemismakingitimpossibletotransferdataotherthansimplestringvalues.Inorderforustotransfercomplexobjects,wewillserializethedatafromthedraggableDatainputusingJSON.stringify.

4. AnotherlimitationcausedbysomesecurityconstraintswithintheAPIisthatdatacanonlybereadafterasuccessfuldrop.Thismeansthatwecannotinspectthedataiftheuserisjusthoveringoveranelement.However,weneedtoknowsomefactsaboutthedatawhenhoveringdropzones.Weneedtoknowthetypeofthedraggableelementwhenenteringadropzonesowecanmakethedropzonesignalifthetypeisaccepted.We'reusingasmallworkaroundforthisissue.ThedraganddropAPIhidesthedatawhenwedragdataoveradroptarget.However,ittellsuswhattypeofdataitis.Knowingthisfact,wecanusethesetDatafunctiontoencodeourdraggabletype.Accessingthedatakeysonlyisconsideredsecureandthereforecanbedoneinalldropzoneevents.

5. Finally,we'llsetthedraggingflagtotrue,whichwillcausetheclassbindingtorevalidateandaddthedraggable--draggingclasstotheelement.

Afterdealingwiththedragstartevent,weonlyneedtohandlethedragendeventtocompleteourDraggabledirective.TheonlythingwedowithintheonDragEndmethodthatisboundtothedragendeventissetthedraggingmembertofalse.Thiswillcausethedraggable--draggingclasstoberemovedfromthehostelement:

@HostListener('dragend')

onDragEnd(){

this.dragging=false;

}

That'sitforthebehaviorofourDraggabledirective.Nowweneedtocreateitscounterpartdirectivetoprovidethebehaviorofadropzone.

www.EBooksWorld.ir

Page 242: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

ImplementingadroptargetdirectiveDropzoneswillactascontainerswheredraggableelementscanbedropped.Forthis,we'llcreateanewdirectivecalledDraggableDropZoneunderdraggable/draggable-drop-zone.js:

@Directive({

selector:'[draggableDropZone]'

})

exportclassDraggableDropZone{

...

}

There'snothingspecialaboutthis@Directiveannotation.WeusedanattributeselectorsoitcanbeattachedusingadraggableDropZoneattributeonanyHTMLelement.Usingthefollowinginput,wecanspecifywhattypesofdraggableelementsweacceptinthisdropzone.Thiswillhelptheuseridentifywhethertheyareabletodropoffthedraggableelementswhenapproachingthedropzone:

@Input()dropAcceptType;

Uponsuccessfuldropsintothedropzone,wewillneedtoemitaneventsothatthecomponentsusingourdraganddropfunctionalitycanreactaccordingly.Forthispurpose,let'screateadropDraggableoutputproperty:

@Output()dropDraggable=newEventEmitter();

Theovermemberfieldwillstorethestateifanacceptedelementisintheprocessofbeingdraggedoverthedropzone:

@HostBinding('class.draggable--over')over;

ThefollowingmethodwillbeusedtocheckwhetherourdropzoneshouldacceptanygivendraganddropeventbycheckingagainstourdropAcceptTypemember.IfyourememberthesecurityproblemsweneededtoworkaroundwithwhencreatingtheDraggabledirective,youwillunderstandwhythisdeterminationisrathersimple:

typeIsAccepted(event){

constdraggableType=

Array.from(event.dataTransfer.types).find(

(key)=>key.indexOf('draggable-type')===0

);

returndraggableType&&

draggableType.split(':')[1]===this.dropAcceptType;

}

WecanonlyreadthetypesofthedatawithindataTransferobjectsforcertainevents,wherethedataitselfishiddenuntilasuccessfuldropeventisoccurred.Tobypassthissecuritylimitation,we'veencodedthedraggabletypeinformationintoadatakeyitself.Sincewecanlistallthedatatypessafely,it'snottoohardtoextracttheencodeddraggabletypeinformation.Wewillsearchforadatatypekeythatstartswith"draggable-type"andthen

www.EBooksWorld.ir

Page 243: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

splititbythecolumncharacter.Thevalueafterthecolumncharacterisourtypeinformation,whichwewillthencompareagainstthedropAcceptTypedirectiveinputproperty.

Wewillusetwoeventstodeterminewhetheradraggableelementismovedtoourdropzone:

dragenter:Thisisfiredbyanelementifanotherelementisdraggedoveritdragleave:Thisisfiredbyanelementifthepreviouslyenteredelementhasleftagain

There'soneproblemwiththeprecedingevents,whichisthattheyactuallybubble,andwewillreceiveadragleaveeventifthedraggedelementismovedtoachildelementwithinourdropzone.Becauseofthebubbling,wethenalsoreceivedragenteranddragleaveeventsfromthechildelements.Thisisnotdesiredinourcase,andweneedtobuildsomefunctionalitytoimprovethisbehavior.WewillmakeuseofacountermemberfielddragEnterCount,whichwillcountuptoallthedragentereventsandcountdowntodragleaveevents.Thisway,wecannowsaythatonlyondragleaveevents,wherethecounterbecomeszero,wewillactuallyleavetheinsideofourdropzone.Let'slookatthefollowingdiagramthatillustratestheproblem:

Visualizationofimportantvariablesandfunctionsforourcalculations

Let'simplementthislogictobuildaproperenterandleavebehaviorofourdropzonewithinthedraggable/draggable-drop-zone.jsfile:

constructor(){

//Weneedthiscountertoknowifadraggableisstilloverour

//dropzone

www.EBooksWorld.ir

Page 244: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

this.dragEnterCount=0;

}

//Thedragentereventiscapturedwhenadraggableisdragged

//intoourdropzone

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

onDragEnter(event){

//Onlyhandleeventifthedraggableisacceptedbyourdrop

//zone

if(this.typeIsAccepted(event)){

this.over=true;

//Weusethiscountertodetermineifweloosefocusbecause

//ofchildelementorbecauseoffinalleave

this.dragEnterCount++;

}

}

//Thedragleaveeventiscapturedwhenthedraggableleavesour

//dropzone

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

onDragLeave(event){

//UsingdragEnterCount,wedetermineifthedragleaveeventis

//becauseofchildelementsorbecausethedraggablewasmoved

//outsidethedropzone

if(this.typeIsAccepted(event)&&--this.dragEnterCount===0){

this.over=false;

}

}

Withinboththeevents,wefirstcheckwhethertheeventiscarryingadataTransferobjectofwhichweacceptthetype.AftervalidatingthetypeusingourtypeIsAcceptermethod,wedealwiththecounterandsettheovermemberfieldifrequired.

Weneedtohandleanothereventthatisimportantfordraganddropfunctionality,whichisthedragoverevent.Withinthedragoverevent,wecansettheaccepteddropEffectofthecurrentdraggingaction.Thiswilltellourbrowserthattheinitiateddraggingactionfromourdraggableissuitableforthisdropzone.It'salsoimportantthatwepreventthedefaultbrowserbehaviorsothere'snothinginthewayofourcustomdraganddropbehavior.Let'saddanotherfunctiontocoverthoseconcerns:

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

onDragOver(event){

//Onlyhandleeventifthedraggableisacceptedbyourdrop

//zone

if(this.typeIsAccepted(event)){

//Preventanydefaultdragactionofthebrowserandsetthe

//dropEffectofthedataTransferobject

event.preventDefault();

event.dataTransfer.dropEffect='move';

}

}

Finally,weneedtohandlethemostimportanteventinthedropzone,whichisthedropevent

www.EBooksWorld.ir

Page 245: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

thatistriggeredifauserdropsadraggableintoourdropzone:

//Thiseventwillbecapturedifadraggableelementisdropped

//ontoourdropzone

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

onDrop(event){

//Onlyhandleeventifthedraggableisacceptedbyourdrop

//zone

if(this.typeIsAccepted(event)){

//Firstobtainthedataobjectthatcomeswiththedropevent

constdata=JSON.parse(

event.dataTransfer.getData('application/json')

);

//Aftersuccessfuldrop,wecanresetourstateandemitan

//eventwiththedata

this.over=false;

this.dragEnterCount=0;

this.dropDraggable.next(data);

}

}

Aftercheckingwhetherthedroppedelementisofanacceptedtype,wecannowgoaheadandreadthedataTransferobjectdatafromtheevent.ThisdatawaspreviouslysetbytheDraggabledirectiveandneedstobedeserializedusingJSON.parse.

Sincethedropwassuccessful,wecanresetourdragEnterCountmemberandsettheoverflagtofalse.

Finally,wewillemitthedeserializeddatafromthedraggableelementusingourdropDraggableoutputproperty.

That'sallweneedtohaveahighlyreusabledraganddropbehaviorthatwecannowattachtoanycomponentswithinourapplicationwherewefeeltheneed.

www.EBooksWorld.ir

Page 246: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

IntegratingdraganddropintasklistcomponentWecannowusetheDraggableandDraggableDropZonedirectivesinourTaskListcomponentsowecanenablethereorderingoftasksusingdraganddrop.

Thewaywe'regoingtodothisisbyattachingboththedirectivestothetaskelementswithintheTaskListcomponenttemplate,wherewe'llrenderthem.Yeah,that'sright!WewanttomakeourTaskcomponentadraggablebutalsoadropzoneatthesametime.Thisway,wecandroptasksintoothertasks,andthisgivesusthefoundationforreordering.Whatwewilldoisreorderthelistinadropsothatthedroppedtaskwillbesqueezedintothepositionrightbeforethetaskwhereitwasdropped.

First,let'sapplythedirectivestothe<ngc-task>elementintheTaskListcomponenttemplate,namelytask-list/task-list.html:

<divclass="task-list__l-container">

...

<ngc-task*ngFor="lettaskoffilteredTasks"

line:[task]="task

(taskUpdated)="onTaskUpdated(task,$event)"

(taskDeleted)="onTaskDeleted(task)"

draggable

draggableType="task"

[draggableData]="task"

draggableDropZone

dropAcceptType="task"

(dropDraggable)="onTaskDrop($event,task)">

</ngc-task>

...

</div>

Alright,usingtheprecedingattributes,wehavemadeourtasksnotonlyadraggable,butalsoadropzone.ByspecifyingbothdraggableTypeanddropAcceptTypetothe"task"string,wearetellingourdraganddropbehaviorthatthesetaskelementscanbedroppedintoothertaskelements.OurDraggableDropZonedirectiveissettoemitadropDraggableeventwheneveravaliddraggableisdroppedoff.Tohandledroppedtasks,wecansimplyusethiseventandcreateabindingtoamethodinourTaskListcomponent.

Let'sseewhatweneedtochangewithinourComponentclass,locatedundertask-list/task-list.js,tomakethiswork:

...

import{Draggable}from'../draggable/draggable';

import{DraggableDropZone}from'../draggable/draggable-drop-zone';

@Component({

selector:'ngc-task-list',

...

directives:[...,Draggable,DraggableDropZone]

})

www.EBooksWorld.ir

Page 247: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

exportclassTaskList{

...

onTaskDrop(source,target){

if(source.position===target.position){

return;

}

lettasks=this.tasks.slice();

constsourceIndex=tasks.findIndex(

(task)=>task.position===source.position

);

consttargetIndex=tasks.findIndex(

(task)=>task.position===target.position

);

tasks.splice(targetIndex,

0,

tasks.splice(sourceIndex,1)[0]);

tasks=tasks.map((task,index)=>{

returnObject.assign({},task,{

position:index

});

});

this.tasksUpdated.next(tasks);

}

...

}

Let'selaborateonthebehaviorwe'llseewithintheonTaskDropmethodthatisboundtotheDropZone'sdropDraggableeventinourtemplate:

1. Ifyoucheckthetemplateagain,youwouldseethatweboundtotheonTaskDropmethodwiththefollowingexpression:(dropDraggable)="onTaskDrop($event,task)".Sincethedropzoneemittedaneventwithde-serializeddatathatwasboundusingthedraggableinputpropertydraggableData,wecansafelyassumethatwewillreceiveacopyofthetaskthatwasdroppedintothedropzone.Asasecondparametertoourbinding,weaddedthelocalviewvariabletask,whichisactuallythetaskthatactsasthedropzone.Therefore,wecansaythatthefirstparameterofouronTaskDropmethodrepresentsthesource,whilethesecondrepresentsthetargettask.

2. Asafirstcheckinourmethod,wecomparethesourcepositionwiththetargetposition,andiftheymatch,wecanassumethatthetaskwasdroppedbyitselfandwedon'tneedtoperformanyfurtheractions.

3. Nowwecangetthesourceandtargettaskindiceswithinourtasksarrayandexecuteanestedsplicesothatwecanremovethesourcefromitsoldpositionwithinthearrayandadditrightbeforethepositionofthetarget.

4. Allthat'slefttodonowisrecalculatethepositionfieldsofthetaskssothattheyreflectthereorderedarray.WecandothiseasilybyusingArray.prototype.map.

5. Asthelaststep,weneedtonotifyourparentcomponentthatwe'veupdatedthetasklist.WecansimplyusethetaskUpdatedeventtodoso.Wehaveusedthatsameeventwhentaskswereaddedorremoved.

www.EBooksWorld.ir

Page 248: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Howgreatisthat?Wehavesuccessfullyimplementeddraganddroponourtasklisttoprovideaveryusablefeaturetoreordertasks.

www.EBooksWorld.ir

Page 249: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

RecapitulateondraganddropWiththeuseofthelow-leveldraganddropAPI,usingeventsanddataTransferobjects,wehaveimplementedtwodirectivesthatcannowbeusedtoexecutesmoothdraganddropfunctionalitywithinourapplicationwhereverwedesire.

Withalmostnoeffort,wehaveimplementedourdraganddropbehavioronthetasklisttoprovideanicefeaturetoreorderthetaskswithinthelist.Theonlythingweneededtodo,besideshookingupthedirectives,wastoimplementamethodwherewecouldreorderthetasksbasedontheinformationfromtheDraggableDropZoneevent.

Wehaveworkedwiththefollowingconceptsinthissection:

1. WelearnedthebasicsofHTML5draganddropAPI.2. WeusedthedataTransferobjecttosecurelytransferdatawithindraganddropevents.3. Builtreusablebehaviorpatternsusingdirectives.4. EnrichedthestandarddraganddropAPIbyprovidingourowncustomselection

mechanismsusingacustomdatatypethatencodesdraggable-typeinformation.

www.EBooksWorld.ir

Page 250: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Toinfinityandbeyond!Displayingasimplelistwithanaveragesizedoesnotcomewithalotofchallenges.Assoonasthelistsstartstogrow,challengesstarttoappear.Wecaneasilyoverwhelmauserwithaverylonglist.Longlistscanalsohaveaperformanceimpactonourapplication,especiallywhenitdisplaysdynamiccontent.

Onewaytoaddressthechallengefacedwhendisplayinglonglistsistoprovidepagination.However,pagesdonotalwaystranslateverywell.Whileusingpaginationonadesktopdevicewithamouseseemsveryintuitive,itbecomescumbersomeonmobiledeviceswithtouchsupport.

Inthischapter,we'lllookatadifferentapproachthatcanhelpusmitigatetheperformanceimplicationsoflonglistswhileprovidingasmoothexperienceonmobiledevices.Weareusingapatternsometimesreferredtoasinfinitescrolling.Thegoalistodisplayonlyenoughitemswithinthelisttofillthescreen,andloadmoreitemsondemandiftheuserscrollsdown.

Toimplementsuchabehavior,wecouldwriteawrappercomponentthatwillprovideaninfinitescrollpaneandusecontentinsertiontoembraceourlist.However,wewilluseadifferentapproachtoimplementourinfinitescrollbehaviorandbuildacustomtemplatedirectivesuchasNgFor.

www.EBooksWorld.ir

Page 251: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

TheasterisksyntaxandtemplatesWehaveusedtheNgForandNgIfdirectivesquitealotsofarusingtheasterisk(*)symboltoindicatewe'redealingwithadirectivethatcreatesatemplate.However,wehaven'tlookedattheanatomyoftheasterisktemplatesyntax.Imaginethatitwillcreatesomesortofsyntacticsugarforourtemplate.

CheckoutthisexampleofusingtheNgFordirectivewiththeasterisktemplatesyntax:

<div*ngFor="letiof[1,2,3]">{{i}}</div>

ThetemplateparserofAngularwillhandlealltheattributesthatstartwithanasteriskinaspecialway.Theprecedingexampleisaneasierandmoreconcisewritingstyleofthefollowing:

<templatengFor#i[ngForOf]="[1,2,3]">

<div>{{i}}</div>

</template>

Boththeprecedingexamplesareabsolutelyidentical.Templatedirectives,suchasNgFororNgIf,makeuseofHTMLtemplateelements,whichwe'vebrieflydiscussedinChapter1,Component-BasedUserInterfaces.ThereasonthattheAngularcommondirectivesNgFor,NgIf,andNgSwitchuseHTMLtemplateelementsisactuallyquiteobviousifyouthinkabouttheirnature.AllthreedirectivesneedtoinsertandremovelargeregionsofourDOMdynamically.NgIf,forexample,insertsorremovestheelementit'sattachedto,basedonacondition.Byleveragingtemplateelements,thiscanbesupportedbythebrowser'snativefunctionality.

Note

Ifyoucomparetheexamplesdiscussedhere,it'sobviousthatthefirstwritingstyleismuchsimplertodealwith.Askingyoutowriteaseparatetemplateelementeverytimeyou'dwanttouseNgFororNgIfwouldbequiteapain.Thisistheonlyreasonwhytheasterisksyntaxexists,itsraisond'êtreifyoulike.Insteadofwritingatemplateelementdirectly,wecanuseanasteriskonanattributeandAngularwilltransformtheHTMLportionintoatemplateelementforus.

TheNgFordirectiveusesaTemplateRefdependency,whichcanbeinjectedintotheconstructorofthedirective,toinstantiatethetemplateormultipleinstancesofit,asdesired.The[ngForOf]propertybindingisgeneratedduringde-sugaringbyappendingthewordOfwithintheNgForexpressiontothedirectivename,ngFor.ThebindingiscreatedbytheNgFordirective,whichacceptsaninput,ngForOf.

Considerthefollowingexample:

<div*test="letvariablewithSugartrue">{{variable}}</div>

www.EBooksWorld.ir

Page 252: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Angularwouldde-sugarthisintothefollowingcode:

<templatetest#variable[testWithSugar]="true">

<div>{{variable}}</div>

</template>

That'sjustthewayAngularde-sugarstheasterisktemplatesyntax.It'sashortcuttoattachdirectivesaswellasoneinputbindingstotemplateelements.

There'sstillonethingthatmightlookconfusing,whichisthevariableattributeinthetemplate.Let'slookatanotherexampleofusingtheNgFordirectivebyaliasingtheexposedlocalvariablesofadirectivelikethecurrentindex:

<div*ngFor="letnof[1,2,3];leti=index">{{i}}:{{n}}</div>

Thisexamplewillbede-sugaredtothefollowingtemplate:

<templatengFor#n#i="index"[ngForOf]="[1,2,3]">

<div>{{i}}:{{n}}</div>

</template>

Sowecannowtellfromde-sugaringthatadditionalaliasesormappingswillgetcreatedasvariablemappingsinourtemplateelement.TheindexthatisexposedwithinthecodeoftheNgFordirectiveclassasalocalviewvariableismappedtoalocalviewvariablewithintheinstantiatedcontentofthetemplate.

Sowhat'sgoingonwiththelocalviewvariablenwithinourinstantiatedtemplates?Whycanweaccessn,whenthere'sjustonevariableattributewithoutanyvaluethatwouldtelluswhereit'smappedto?

Wehavelearnedthatwhenweusehashsymbolattributesonregularelements,wecreatealocalviewreference.Wecanusethisreferenceasanidentifierintheviewdirectly,[email protected],whentheviewcompilerofAngulardiscoverswhatlookslikealocalviewreferenceonatemplate,thebehaviorisabitdifferent.

WhatisinvisibletousisthatAngularactuallyimpliesadefaultvalueforvariableattributesontemplateelementsthatdon'thaveanattributevalue.Itwillcreateamappingforalocalviewvariablecalled$implicit.Youcanthinkof$implicitasadefaultvaluethatcanbeexposedindirectivesaslocalviewvariablesandwillprovidesomeeaseofusewhendealingwithtemplateelements.

Itwouldalsobetotallyvalidtowritetheprecedingexampleasfollows:

<templatengFor#n="$implicit"#i="index"[ngForOf]="[1,2,3]">

<div>{{i}}:{{n}}</div>

</template>

Here,theNgFordirectiveisexposingalocalviewvariable$implicit,whichisareferencetothecurrentvalueassociatedwiththeinstanceduringtheiterationoverthearrayitreceives

www.EBooksWorld.ir

Page 253: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

withinthengForOfinput.Usingaplainvariableattributewithoutavalue,Angularwilldefaultamappingto$implicit.Becausewedon'twanttowritethismappingallthetimeourselves,wecanjustspecifyanemptyvariableattributeandAngularwillassumeitforus.

www.EBooksWorld.ir

Page 254: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

CreatinganinfinitescrolldirectiveSincewenowknowabitmoreabouttemplateelementsandhowAngulardealswiththeasterisksyntax,wecanactuallycreateourowncopyofNgFor,whichisadditionallydealingwiththebehaviorofinfinitescrolling.

Let'screateanewfileforourdirectiveunderinfinite-scroll/infinite-scroll.js:

...

@Directive({

selector:'[ngcInfiniteScroll]'

})

exportclassInfiniteScroll{

...

//Thisinputwillbesetbytheforoftemplatesyntax

@Input('ngcInfiniteScrollOf')

setinfiniteScrollOfSetter(value){

this.infiniteScrollOf=value;

...

}

...

applyChanges(changes){

...

this.bulkInsert(insertTuples).forEach((tuple)=>

tuple.view.context.$implicit=tuple.record.item);

}

...

}

WestartoffbydeclaringaregulardirectivethatissensitivetotheattributeselectorngcInfiniteScroll.Theprecedingcodeexcerptonlyshowstherelevantcodeforthetemplateelementhandlingwediscussedintheprevioustopic.Therearesomecodepartsthatwewillcoverlateroninthistopic.YoucanseethatweusedaninputpropertyngcInfiniteScrollOf,whichisusedtopassinthelistoftimesusedwithintheinfinitescrolling.Forinsertedtemplateinstances,wesetthelocalviewvariable$implicittotheactualitemwithinthelistwewereiteratingover.

Wewilldiscusshowwegettoallofthesurroundingcodeshortly,butfirstlet'stakealookathowwecouldusethisdirectivewithinatemplate:

<div*ngcInfiniteScroll="#itemofitems">{{item}}</div>

Theprecedingcode,asperthemechanismsdescribedintheprevioustopic,willde-sugarintothefollowingtemplateelement:

<templatengcInfiniteScroll#item[ngcInfiniteScrollOf]="items">

<div>{{item}}</div>

</template>

Sowhatwecantellnowisthattheitemsarraywillbeplacedasapropertybindingontoour

www.EBooksWorld.ir

Page 255: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

templateelement.ThesameelementalsocontainstheInfiniteScrolldirective.

Afterdiscussinghowthedirectivewillbeusedandhowwecangettherequiredinputintothedirective,let'slookattheimplementationdetailsthatenabletheinfinitescrollbehavior.

Ourdirectiveneedstodealwithquitealotofconcerns.Let'scheckoutahigh-levelrequirementlist:

Itneedstodynamicallycreatenewchildviewsbasedonthetemplateelementandalsoremovechildviewsthatarenolongerrequired.ItneedstodetectchangesontheinputpropertyngcInfiniteScrollOf,whichisboundtoanarraywithinthetemplate.It'snotsufficienttouseasimpleidentitycheck,becausewe'dliketocreateacomparisonofthepreviousarraytothenewarrayandonlyperformviewchangesbasedonthedifferences.Forthispurpose,wewillneedtoimplementtheDoChecklifecyclecallback.Itneedstostoreacountofitemsthatshouldbedisplayedinitially,andbydetectingscrollevents,thedisplayeditemcountshouldincreasesomoreitemscouldbemadevisible.Atthesametime,scrollingshouldtriggerthechangedetectionsothatwecancreatenewinstancesofthetemplatewithintheview.

Let'sstartwiththeconstructorofourdirective:

constructor(@Inject(ViewContainerRef)viewContainerRef,

@Inject(TemplateRef)templateRef,

@Inject(IterableDiffers)iterableDiffers,

@Inject(ChangeDetectorRef)cdr){

//UsingObject.assignwecaneasilyaddallconstructor

//argumentstoourinstance

Object.assign(this,

{viewContainerRef,templateRef,iterableDiffers,cdr});

//Howmanyitemswillbeshowninitially

this.shownItemCount=3;

//Howmanyitemsshouldbedisplayedadditionally,whenwe

//scrolltothebottom

this.increment=3;

}

Weneededtousequitealotofinjecteddependenciesinordertoperformalltheoperationsrequiredtofulfilltheoutlinedrequirementsofourdirective:

TheViewContainerRefdependencyhelpsuscreatenewembeddedviewsbasedonourtemplateelementaswellasdetachorcompletelyremoveexistingviews.TheTemplateRefdependencyisareferencetothetemplateelement,andwecanuseitinconjunctionwiththeViewContainerRefdependencyinordertocreatenewinstances.TheIterableDiffersdependencyisusedtocreateadiffofourinputproperty,whichisthearrayofitemswe'reconcernedaboutinourinfinitescrollrepeater.Itsupportsusinfindingthecreated,removed,anddeleteditems.TheChangeDetectorRefdependencyisusedtotriggerchangedetectionmanuallywhenweactuallyneedit.

www.EBooksWorld.ir

Page 256: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Asthefirststep,weusedObject.assigntostoreallourfunctionparametersintheinstanceofthedirective.Then,wesettwo-membervariablesthatwillstoreinformationrelatedtothenumberofitemsthatshouldbedisplayedandalsothenumberofdisplayeditemsweshouldincreaseuponscrolling.

That'sitfortheconstructor.Wealsoneedtoperformsomeactionsaftertheviewwithinourdirectivehasbeeninitialized.We'llusethengOnInitlifecyclehookforthispurpose:

ngOnInit(){

this.scrollableElement=findScrollableParent(

this.viewContainerRef.element.nativeElement.parentElement);

this.scrollableElement.addEventListener('scroll',this._onScrollListener);

}

Let'slookatthesetwolinesofcodeinmoredetail:

Thewayourinfinitescrollingworksisthatitdetectswhetherthescrollableparentelementhasalreadyscrolledtothebottom.Ifthat'sthecase,we'dneedtorendermoreitemsfromthelist.Inordertocheckwhetherourparentelementhasalreadyscrolledtothebottom,wewillneedareferencetoit.Asscrolleventsdon'tbubble,weneedtobeveryprecisewheretomonitorthem.That'sthereasonwhyweuseautilityfunctiontoscantheDOMtreetofindthenextscrollableparentelement.ThefindScrollableParentfunctionlooksforthefirstparentelementthathasscrollbarsorthewindowobject.Youcancheckthesourcecodeofthischapterifyou'dliketoseetheinternalsofthefunction.Nowwe'veaddedaneventhandlertothefoundscrollableparentelementandregisteredourinternalonScrollmethodasacallback.

www.EBooksWorld.ir

Page 257: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

DetectingchangewithinourtemplatedirectiveNowlet'slookatthecompletecodeofthengcInfiniteScrollOfpropertysetter,whichwehavebrieflylookedatalready:

@Input('ngcInfiniteScrollOf')

setinfiniteScrollOfSetter(value){

this.infiniteScrollOf=value;

//Createanewiterabledifferfortheiterable`value`,ifthe

//differisnotalreadypresent

if(value&&!this.differ){

this.differ=this.iterableDiffers.find(value).create(this.cdr);

}

}

OurpropertysetterwillbecalledbyAngulareverytimethengcInfiniteScrollOfinputpropertychanges.Sincethispropertyisboundbythede-sugaringoftheasterisktemplatesyntaxtothelistwerefertowithinourtemplate,wecanassumethatthevaluewillalwaysbeanarrayorasimilariterablestructure.

Besidesstoringthenewvaluefromtheinputpropertyontoourdirectiveinstance,wealsolazyinitializeamemberfieldcalleddiffer.UsingthefindmethodontheIterableDiffersobject,wecanobtainafactorythatmatchesthetypeofiterableyou'redealingwith(inourcase,thiswillbeplainarrays).Ontheobtainedfactory,wecanthencallthecreatemethodtocreateanewdiffer.ThecreatemethodexpectsaChangeDetectorRefobjecttobepassed.Luckily,wehavethatreadilyavailablethroughaninjectionwithintheconstructor.

Thedifferwillhelpusinalatersteptodetectchangesbetweentheexistingvalueofourarrayandanupdatedone.Wecanthenperformadditions,removals,andmovementsinaveryperformantway.

IfwecallthediffmethodonIterableDiffer,itwillreturnanewIterableDifferobjectthatcontainsallthechangesrelativetothepreviousIterableDifferobject.Inadiffer,wecanthencalloneofthefollowingmethodstoiterateovertherelevantCollectionChangeRecord:

forEachItem:ThisiteratesovereachCollectionChangeRecordwithinthedifferbyprovidingacallbackfunction.Thefirstargumenttothecallbackwillbeachangerecord.forEachPreviousItem:ThisonlyiteratesovereachCollectionChangeRecordwithinthedifferthatalreadyexistedinthepreviousdiffer.forEachAddedItem:Thisonlyiteratesovereachchangerecordthatwasaddedfromthepreviousdifftothecurrentone.forEachMovedItem:Thisonlyiteratesovereachchangerecordthatwasmoved.forEachRemovedItem:Thisonlyiteratesoverchangerecordsthatwereremoved

TheCollectionChangeRecordobjectscontainthefollowingthreemainproperties:

item:Areferencetotheitemwithinthelistwhichwe'reobservingforchangesusingthe

www.EBooksWorld.ir

Page 258: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

differpreviousIndex:TheindexoftheitemwithinthelistbeforethedifferiterablehappenedcurrentIndex:Theindexoftheitemwithinthelistafterthedifferiterable

WecanalsosolelytellfromtheconstellationofpreviousIndexandcurrentIndexwhathappenedtotheitem.ThefollowingmethodsarepresentonanIterableDifferobject:

Addeditems:ThiscanbeidentifiedifpreviousIndexisnullandcurrentIndexissettoavalidnumberMoveditems:ThiscanbeidentifiedifpreviousIndexandcurrentIndexarebothsettoavalidnumberRemoveditems:ThiscanbeidentifiedifpreviousIndexissettoavalidnumberbutcurrentIndexissettonull

Nowlet'slookattheonScrollmethod,whichwillbeinvokedbythescrolleventcallbackofthescrollablecontainerelement.Inthismethod,weneedtohandlethelogicofourbehaviorthatshouldbeexecutedwhenauserscrollsdown:

onScroll(){

//Ifthescrollableparentisscrolledtothebottom,wewill

//increasethecountofdisplayeditems

if(this.scrollableElement&&isScrolledBottom(this.scrollableElement)){

this.shownItemCount=Math.min(this.infiniteScrollOf.length,

this.shownItemCount+this.increment);

//Afterincrementingthenumberofitemsdisplayed,weneed

//totellthechangedetectiontorevalidate

this.cdr.markForCheck();

}

}

IntheonScrollmethod,wefirstcheckedwhetherthescrollbarofthescrollableparentelementhasalreadyscrolledtothebottom.Ifthat'sthecase,wecanassumethatweshoulddisplaymoreitemsfromourlist.

WeincrementedtheshowItemCountmemberbythedefaultincrementvalue,whichwehavesetto3,andaftermodifyingthenumberofdisplayeditems,weusedthechangedetectortomarkoursubtreestructuretobechecked.

Sincewewouldliketousethedifferthatwehavelazyinitializedwithinourinputsettertodetectchangesandperformanyactionsmanually,wewillneedtoimplementtheDoChecklifecyclecallbackonourdirective.Byimplementingthis,wewilldisablethedefaultchangedetectionofAngularandimplementourownwaytodealwithchanges:

ngDoCheck(){

if(this.differ){

//Wearecreatinganewslicebasedonthedisplayeditem

//countandthencreateachangesobjectcontainingthe

//differencesusingtheIterableDiffer

constupdatedList=this.infiniteScrollOf

.slice(0,this.shownItemCount);

www.EBooksWorld.ir

Page 259: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

constchanges=this.differ.diff(updatedList);

if(changes){

//Ifwehaveanychanges,wecallour`applyChanges`method

this.applyChanges(changes);

}

}

}

First,weusedthediffertoobtainachangerecordsetfromthecurrentinfiniteScrollOfarraytothepreviousone.Thedifferwillactuallyalwaysstorethepreviousvalue,soweonlyneedtopassitthecurrentvalue.Thechangerecordswillthenhelpustoperformdifferentactionsforadded,removed,andmoveditems.It'salsoimportanttonotethatwedidnotusethewholelistheretocreateadiff,butasliceofthelistwhereourshowItemCountmembercomesintoplay.Thiswillonlymakethelistthatwe'reconcernedaboutavailableinourinfinitescrollbehavior.

www.EBooksWorld.ir

Page 260: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

AddingandremovingembeddedviewsIfthereareanychangesdetectedbythediffer,wecancalltheapplyChangesmethod,whichdealswiththedetailsofhowtoperformviewupdateswithchangeditems:

applyChanges(changes){

//Firstwecreatearecordlistthatcontainsallmovedand

//removedchangerecords

constrecordViewTuples=[];

changes.forEachRemovedItem((removedRecord)=>

recordViewTuples.push({record:removedRecord}));

changes.forEachMovedItem((movedRecord)=>

recordViewTuples.push({record:movedRecord}));

//Wecannowbulkremoveallmovedandremovedviewsandasa

//resultwegetallmovedrecordsonly

constinsertTuples=this.bulkRemove(recordViewTuples);

//Inadditiontoallmovedrecordswealsoaddarecordforall

//newlyaddedrecords

changes.forEachAddedItem((addedRecord)=>

insertTuples.push({record:addedRecord}));

//Nowwehavestoredallmovedandaddedrecordswithin`

//insertTuples`whichweusetodoabulkinsert.Asaresult

//wegetthelistofthenewlycreatedviews.Onthoseviews

//we'rethencreatingaviewlocalvariable`$implicit`that

//willbindthelistitemstothevariablenameusedwithinthe

//foroftemplatesyntax.

this.bulkInsert(insertTuples).forEach((tuple)=>

tuple.view.context.$implicit=tuple.record.item);

}

Let'slookattheinnardsoftheapplyChangesmethod.ItneedstobecalledfromtheOnChangelifecyclehookwithaparameterthatreflectstherecordchangeswithintheobservedinputarraycalledinfiniteScrollOf.IntheconstantrecordViewTuples,westoredallthechangerecordsthatwereeithermovedorremovedcompletely.NowyoucancallthebulkRemovemethodbypassingtherecordViewTuplesarray.ThebulkRemovemethodwilleitherdetachtheview,incasethereismovement,orcompletelyremovetheview.Thereturnedvalueisalistthatwillcontainonlythetuplesthatweremoved.WestoredthesewithinaconstantcalledinsertTuples.Becausetheyweredetachedfromtheviewcontainer,wewillneedtoreattachthematadifferentpositionwithintheviewcontainer.

NowwecangoaheadandaddalltherecordstotheinsertTuplesarraythatwereaddedaccordingtothelatestdiff.TheinsertTuplesarraynowcontainsallthemovedaswellasaddedrecords.

Usingthislist,wecallthebulkInsertmethod,whichwillreinsertmovedviewsandcreatenewembeddedviewsforaddedrecords.Asaresult,wegetalistofalltheinsertedrecords(movedandadded),whereeachrecordalsocontainsaviewpropertythatpointstotheinsertedview.

www.EBooksWorld.ir

Page 261: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

ThelaststepwithinourapplyChangesmethodshouldnowringabell.Weiteratedthroughthelistofnewlyinsertedviewsandsetthelocalviewvariable$implicitontheviewcontext.Thisway,wecansettherequiredvariablethatisusedtocreatethedefaultvariablemappingsonourtemplateelements,asdiscussedintheprevioustopic.

Inordertounderstandhowwecaninstantiatenewviewsfromourtemplateelement,moveviewsaround,andremoveexistingviews,weneedtounderstandtheviewcontainer.TheViewContainerRefdependencyisprovidedtoourdirectiveorcomponentusinginjectionintheconstructor.Itstoresalistofviewsandprovidessomemethodstoaddnewandremoveexistingviews.EachcomponentwithinAngularcontainsoneviewcontainer.Wecanthenaccessthemethodsontheviewcontainerinordertoprogrammaticallymodifytheview.

TherearefourmainmethodsinViewContainerRefthatwe'reinterestedin:

Method Description

createEmbeddedView

Thismethodwillcreateanewembeddedviewusingatemplatereferenceandinsertthenewlycreatedviewatagivenindexwithintheviewcontainer.Embeddedviewsareviewsinstantiatedfromtemplateelements.

Thefollowingareitsparameters:

templateRef:Thefirstparametershouldbethetemplatereference,whichshouldbeinstantiatedintoanembeddedview.context:Thisisanoptionalcontextobject,whichwillbeavailablefortheinstantiatedtemplateview.Allpropertieswithinthecontextcanbeusedwithintheviewtemplateaslocalviewvariables.index:Theoptionalindexparametercanbeusedtoplacetheinstantiatedviewatagivenpositionwithintheviewcontainer.

Thismethodreturnsthecreatedembeddedview.

detach

Thedetachmethodwillremoveanembeddedviewfromtheviewcontaineratagivenindexwithoutdestroyingtheviewsoitcanbereattachedlaterusingtheinsertmethod.

Thefollowingisitsparameter:

index:Thisistheindexoftheembeddedview,whichshouldbedetached

Thismethodreturnsthedetachedembeddedview.

www.EBooksWorld.ir

Page 262: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

remove

Theremovemethodwillcompletelyremoveanembeddedviewfromtheviewcontainerandalsodestroytheview.Aviewthathasbeendestroyedcan'tsimplybereattachedusingtheinsertmethod.

Thefollowingisitsparameter:

index:Thisistheindexoftheembeddedview,whichshouldberemoved

Thismethodreturnstheremovedembeddedview.

insert

Thismethodwillinsertanexistingviewintotheviewcontainer.

Thefollowingareitsparameters:

viewRef:Theembeddedviewthatshouldbeinsertedintotheviewcontainer.index:Theoptionalindexparameterthatcanbeusedtoplacetheembeddedviewatagivenpositionwithintheviewcontainer.

Thismethodreturnstheinsertedembeddedview.

Let'squicklylookatthebulkRemoveandbulkInsertmethodstoseehowwecanusetheviewcontainertomodifythecontainingviewuponchanges:

bulkRemove(tuples){

...

//Reducingthechangerecordssowecanreturnonlymoved

//records

returntuples.reduceRight((movedTuples,tuple)=>{

//Ifanindexispresentonthechangerecord,itmeansthat

//itsoftype"moved"

if(tuple.record.currentIndex!=null){

//Formovedrecordsweonlydetachtheviewfromtheview

//containerandpushitintothereducedrecordlist

tuple.view=this.viewContainerRef.detach(tuple.record.previousIndex);

movedTuples.push(tuple);

}else{

//Ifwe'redealingwitharecordoftype"removed",we

//completelyremovetheview

this.viewContainerRef.remove(tuple.record.previousIndex);

}

returnmovedTuples;

},[]);

}

WeuseViewContainerReftodetachviewsincasetherecordcontainsavalidcurrentIndexfield.Ifthat'sthecase,weknowthatwearedealingwithaviewthatwillbemoved.Weusethedetachmethodtoexcludetheviewfromitspositionwithintheviewcontainer,butthiswill

www.EBooksWorld.ir

Page 263: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

notdestroytheview.It'simportanttonoteherethatwestoredthereturnedviewfromthedetachmethodontothetuplebeforeweaddedittothemovedTupleslist.Thisway,wewereabletoidentifyitlaterasamoveditem,andwecouldusetheviewtoreattachitusingtheinsertmethodontheviewcontainer.

Inthecasewherethere'snovalidcurrentIndex,wearedealingwithanelementthatwasremovedfromthelist.Insuchcases,we'dneedtousetheremovemethodtocompletelydestroytheviewandremoveitfromtheviewcontainer.

Nowwe'llcallthebulkInsertmethodwithanymovedorinsertedviews.Let'salsolookatthecodeofthismethodbrieflytoseehowwecanhandleviewupdatesthere:

bulkInsert(tuples){

...

tuples.forEach((tuple)=>{

if(tuple.view){

//We'reinsertingbackthedetachedviewatthenewpositionwithinthe

viewcontainer

this.viewContainerRef.insert(tuple.view,

tuple.record.currentIndex);

}else{

//We'redealingwithanewlycreatedviewsowecreateanewembedded

viewontheviewcontainerandstoreitinthechangerecord

tuple.view=

this.viewContainerRef.createEmbeddedView(

this.templateRef,

{},

tuple.record.currentIndex);

}

});

returntuples;

}

Ifthetuplecontainsaviewproperty,weknowwehavepreviouslydetacheditfromadifferentposition.WeareusingtheinsertmethodoftheviewcontainertoreattachitatthenewpositionusingtheinformationfromCollectionChangeRecord.

Ifthere'snoviewproperty,wearedealingwithanewlyaddedrecord.Inthatcase,wesimplyusethecreateEmbeddedViewmethodtocreateanewtemplateinstance.Forthecontextparameter,weneedtopassanewemptyobject.However,we'veupdatedthecontextobjectalreadywithinourapplyChangesmethod.There,weaddedthe$implicitlocalviewvariableforeverycreatedview.

That'sallweneedforourInfiniteScrolldirective,andwecannowaddittothetemplateswherewe'replanningtousethisfunctionality.Let'suseitwithinthetasklistbyaddingthedirectivetothedirectivelistoftheTaskListcomponentwithinthetask-list/task-list.jsfile:

...

import{InfiniteScroll}from'../infinite-scroll/infinite-scroll';

www.EBooksWorld.ir

Page 264: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

@Component({

selector:'ngc-task-list',

...

directives:[...,InfiniteScroll]

})

exportclassTaskList{

...

}

Nowwecansimplyeditthetasklisttemplateintask-list/task-list.htmlandreplacetheNgFordirectivewithourInfiniteScrolldirective:

<ngc-task*ngcInfiniteScroll="lettaskoffilteredTasks"

[[task]="task"

(taskUpdated)="onTaskUpdated(task,$event)"

(taskDeleted)="onTaskDeleted(task)"

draggable

draggableType="task"

[draggableData]="task"

draggableDropZone

dropAcceptType="task"

(dropDraggable)="onTaskDrop($event,task)"></ngc-task>

That'sallweneedtouseourinfinitescrollfunctionality.Thisishighlyreusable,andwecanplaceitwhereverwe'dliketouseitinsteadoftheregularNgForrepeater.

www.EBooksWorld.ir

Page 265: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

FinishingourinfinitescrolldirectiveInthistopic,wecreatedaninfinitescrollingbehaviorbyimplementingatemplatedirectivesimilartoNgFor.WereplacedtheNgFordirectiveinourtasklisttousetheInfiniteScrolldirectiveinstead.Nowwedon'tdisplayallthetasksrightatthebeginning,butassoonastheuserstartstoscroll,newtasksappear.Inscenarioswherewerelyonalistthatispartiallyloadedfromtheserver,ourdirectivecouldevenbeextendedsoitcouldrequestformoreitemsfromtheserverondemand.

We'vecoveredthefollowingsubtopicshere:

Theasterisksyntaxandde-sugaringtotemplateelementsThelocalviewvariable,$implicitImplementingtheOnChangelifecyclehooktoprovidecustomchangedetectionUsingIterableDiffertoanalyzethedifferenceofchangeswithinourarrayinputpropertyandhandlingCollectionChangeRecordobjectstoreactonchangesUsingViewContainerReftoupdatetheviewofacomponentprogrammaticallyUsingTemplateRefasareferencetothetemplateelementwithintemplatedirectives

www.EBooksWorld.ir

Page 266: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

SummaryInthischapter,webuiltthreecomponentstoenhancetheusabilityofourapplication.Userscannowmakeuseoftagstoeasilyannotatecommentswithnavigableitemsthatprovidesummariestothesubject.Theycanusedraganddroptoreordertasksandbenefitfromaninfinitescrollbehavioronthetasklist.

Usabilityisakeyassetintoday'sapplications,andbyprovidinghighlyencapsulatedandreusablecomponentstoaddressusabilityconcerns,wecanmakeourlivesaloteasierwhenbuildingthoseapplications.Thinkingintermsofcomponentswhendealingwithusabilityisaverygoodthing,whichnotonlyeasesdevelopment,butalsoestablishesconsistency.Theconsistencyitselfthenplaysamajorroleinmakinganapplicationusable.

Inthenextchapter,we'regoingtocreatesomeniftycomponentstomanagetimewithinourtaskmanagementsystem.Thiswillalsoincludesomenewuserinputcomponentstoenablesimpleworktime-entryfields.

www.EBooksWorld.ir

Page 267: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Chapter8.TimeWillTellOurtask-managementsystemiscomingintoshape.However,wewerenotconcernedaboutonecrucialaspectofmanagingourprojectssofar.Timeplaysamajorroleinallprojects,andthisisthethingthatisoftenthemostcomplicatedtomanage.

Inthischapter,wewilladdafewfeaturestoourtaskmanagementsystemthatwillhelpouruserstomanagetimemoreefficiently.Reusingsomecomponentsthatwecreatedearlier,wewillbeabletoprovideaconsistentuserexperiencetomanagetime.

Onahigherlevel,wewilldevelopthefollowingfeaturestoenabletimemanagementinourapplication:

Taskdetails:Sofar,wedidnotincludeadetailspageoftasksbecauseallthenecessaryinformationabouttaskscouldbedisplayedonthetasklistofourprojectpage.Whileourtimemanagementwillincreasethecomplexityofourtasksquiteabit,wewillcreateanewdetailviewofprojecttasksthatwillalsobeaccessiblethroughrouting.Effortsmanagement:Wewillincludesomenewdataonourtaskstomanageeffortsontasks.Effortsarealwaysrepresentedbyanestimateddurationoftimeandaneffectivedurationofspenttime.Wewillmakebothpropertiesofeffortsoptionalsothattheycanexistindependently.Wewillcreatenewcomponentstoenableuserstoprovidetimedurationinputeasily.Milestonemanagement:Wewillincludeawaytomanageprojectmilestonesandthenmapthemtoprojecttasks.Thiswillhelpuslatergainanoverviewovertheprojectstatus,anditenablestheusertogrouptasksintosmallerchunksofwork.

Thefollowingtopicswillbecoveredinthischapter:

CreatingaprojecttaskdetailcomponenttoedittaskdetailsandenableanewrouteModifyingourtagmanagementsystemtoincludetasktagsCreatingnewpipestodealwithformattingtimedurationsCreatingtaskinformationcomponentstodisplaytaskoverviewinformationontheexistingtaskcomponentsCreatingatimedurationuseinputcomponentthatenablesuserstoeasilyinputtimedurationsCreatinganSVGcomponenttodisplayprogressontasksCreatinganautocompletecomponenttomanagemilestonesontasks

www.EBooksWorld.ir

Page 268: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

TaskdetailsSofar,ourtasklistwassufficientenoughtodisplayalldetailsoftasksdirectlyinthelisting.However,aswewilladdmoredetailstotasksinthischapter,it'stimetoprovideadetailviewwhereuserscaneditthetask.

WealreadylaidthegroundworkonprojectnavigationusingtherouterinChapter5,Component-BasedRouting,ofthisbook.Addinganewroutablecomponentthatwe'lluseinthecontextofourprojectswillbeabreeze.

Let'screateanewcomponentclassforourprojecttaskdetailviewintheproject/project-task-details/project-task-details.jspath:

@Component({

selector:'ngc-project-task-details',

})

exportclassProjectTaskDetails{

}

AsthiscomponentwillneverexistwithoutaparentProjectcomponent,wecansafelyrelyonthattoobtainthedataweuse.Thiscomponentisn'tusedinpureUIcompositioncases,soit'snotrequiredtocreatearoutablewrappercomponentlikewedidforothercomponentsinChapter5,Component-BasedRouting.WecandirectlyrelyonrouteparametersandobtaintherelevantdatafromtheparentProjectcomponent.

First,weusedependencyinjectioninordertogetareferencetotheparentprojectcomponent:

constructor(@Inject(forwardRef(()=>Project))project){

this.project=project;

}

Similarlytoourroutingwrappercomponents,wemakeuseofparentcomponentinjectiontoobtainareferencetotheparentProjectcomponent.

Now,we'llusetheOnActivatelifecyclehookoftherouteragaintoobtainthetasknumberfromtheactiveroutesegment:

routerOnActivate(currentRouteSegment){

consttaskNr=currentRouteSegment.getParam('nr');

this.projectChangeSubscription=

this.project.document.change.subscribe((data)=>{

this.task=data.tasks.find((task)=>task.nr===+taskNr);

this.projectMilestones=data.milestones||[];

});

}

www.EBooksWorld.ir

Page 269: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Finally,we'llcreateareactivesubscriptiontotheLiveDocumentprojectsthatwillextractthetaskthatweareconcernedaboutandstoreitintothecomponentstaskmember.Inthisway,weensurethatourcomponentwillalwaysreceivethelatesttaskdatawhentheprojectisupdatedoutsideofthecurrenttaskdetailsview.

Ifourcomponentgetsdestroyed,weneedtomakesurethatweunsubscribefromtheRxJSObservablethatisprovidedbytheLiveDocumentproject.Let'simplementthengOnDestroylifecyclehookforthispurpose:

ngOnDestroy(){

this.projectChangeSubscription.unsubscribe();

}

Alright,let'snowtakealookatthetemplateofourcomponent,andseehowwe'lldealwiththetaskdatatoprovideaninterfacetoeditthedetails.We'llcreateaproject-task-details.htmlfileinournewcomponentfolder:

<h3class="task-details__title">

TaskDetailsoftask#{{task?.nr}}

</h3>

<divclass="task-details__content">

<divclass="task-details__label">Title</div>

<ngc-editor[content]="task?.title"

[showControls]="true"

(editSaved)="onTitleSaved($event)"></ngc-editor>

<divclass="task-details__label">Description</div>

<ngc-editor[content]="task?.description"

[showControls]="true"

[enableTags]="true"

(editSaved)="onDescriptionSaved($event)">

</ngc-editor>

</div>

ReusingtheEditorcomponentthatwecreatedinChapter4,NoComments,Please!,ofthisbook,wecanrelyonsimpleUIcompositiontomakethetitleanddescriptionofourtaskseditable.

Aswestoredthetaskdataintothetaskmembervariableonourcomponent,wecanreferencethetitleanddescriptionfieldstocreateabindingtothecontentinputpropertyofoureditorcomponents.

Whilethetitleshouldonlyconsistofplaintext,wecansupportthetaggingfunctionalitythatwecreatedinChapter7,ComponentsforUserExperience,onthedescriptionfieldofthetask.Forthis,wesimplysettheenableTagsinputpropertyofthedescriptionEditorcomponenttotrue.

TheEditorcomponenthasaneditSavedoutputpropertythatwillemittheupdatedcontentonceausersaveshisedits.Now,allweneedtomakesureofisthatwecreateabindingtoourcomponentthatwillpersistthesechanges.Let'screatetheonTitleSavedand

www.EBooksWorld.ir

Page 270: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

onDescriptionSavedmethodsonourComponentclasstohandletheseevents:

onTitleSaved(title){

this.task.title=title;

this.project.document.persist();

}

onDescriptionSaved(description){

this.task.description=description;

this.project.document.persist();

}

ThetaskmemberisjustareferencetothegiventaskintheLiveDocumentprojectoftheProjectcomponent.Thissimplifiesthewaywepersistthedatathatwaschangedonthetask.Afterupdatingthegivenpropertyonthetask,wesimplycallthepersistmethodontheLiveDocumentprojectstostoreourchangesinthedatastore.

Sofar,sogood.WecreatedataskdetailscomponentthatmakesiteasytoeditthetitleanddescriptionoftasksusingourEditorUIcomponent.TheonlythinglefttoenableourcomponentistocreateachildrouteontheProjectcomponent.Let'sopenourProjectcomponentclassinlib/project/project.jstomakethenecessarymodifications:

import{ProjectTaskDetails}from'./project-task-details/project-task-

details';

@Component({

selector:'ngc-project',

})

@Routes([

newRoute({path:'task/:nr',component:ProjectTaskDetails}),

])

exportclassProject{

}

WeaddedanewchildrouteonourProjectcomponent,whichisresponsiblefortheinstantiationofourProjectTaskDetailscomponent.Byincludinga:nrparameterintherouteconfiguration,wecanpasstheconcernedtasknumberintotheProjectTaskDetailscomponent.

Ournewly-createdchildrouteisnowaccessibleintherouterandwecanaccessthetaskdetailviewusingthe/projects/project-1/task/1exampleURL.

InordertomakeourTaskDetailsroutenavigable,weneedtoaddanavigationlinktoourTaskcomponentsothatuserscannavigatetoitintheprojectstasklist.

Forthisrathersimpletask,theonlythingthatweneedtodoisusetheRouterLinkdirective

www.EBooksWorld.ir

Page 271: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

andcreateanewlinkintheTasktemplate,lib/task-list/task/task.html:

<divclass="task__l-box-b">

<a[routerLink]="['../task',task?.nr]"

class="buttonbutton--small">Details</a>

</div>

WeusearelativerouterURLherebecausewe'realreadyonthe/project/tasksroute.Asourtask/:nrrouteispartoftheprojectrouter,weneedtonavigateonelevelbacktoaccessthetaskroute:

Newlycreatedtaskdetailviewwitheditabletitleanddescription

www.EBooksWorld.ir

Page 272: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

EnablingtagsfortasksSofar,thetag-managementsystemthatwecreatedinChapter7,ComponentsforUserExperience,onlysupportsprojecttags.Aswenowcreatedadetailviewtotasks,itwouldbenicetoalsosupporttasktagsdirectlyinourtaggingsystem.Ourtaggingsystemisquiteflexible,andwecanimplementnewtagswithverylittleeffort.Onahigherlevel,weneedtomakethefollowingchangestoenabletasktagsinoursystem:

Editthegenerate-tag.jsmoduleinordertosupportthegenerationoftasktagsfromtaskandprojectdataEdittheTagsServiceinordertoinitializetasktagsusingthegenerate-tag.jsmoduleandcache

Let'sfirstmodifythelib/tags/generate-tag.jsfiletoenabletasktaggeneration:

exportconstTAG_TYPE_TASK='task';

exportfunctiongenerateTag(subject){

if(subject.type===TAG_TYPE_PROJECT){

}elseif(subject.type===TAG_TYPE_TASK){

//Ifwe'redealingwithatask,wegeneratetheaccordingtag

//object

returnnewTag(

`#${subject.project._id}-task-${subject.task.nr}`,

`${limitWithEllipsis(subject.task.title,20)}(${subject.task.done?

'done':'open'})`,

`#/projects/${subject.project._id}/task/${subject.task.nr}`,

TAG_TYPE_TASK

);

}

}

Asweneedtohaveareferencetoprojectdataaswellastotheindividualtaskofthisproject,weexpectthesubjectparametertolooklikethefollowingobject:

{task:…,project:…,type:TAG_TYPE_TASK}

Fromthissubjectobject,wecanthencreateanewTagobject.ForthetextTagfield,weuseaconstructthatincludestheprojectIDaswellasthetasknumber.Likethis,wecanuniquelyidentifythetaskusingasimpletextrepresentation.

Forthelinkfield,weconstructaURLfromtheprojectaswellasthetasknumber.ThisstringwillresolvetoaURLrequiredtoactivatetheTaskDetailsroute,whichweconfiguredintheprevioussection.

OurgenerateTagfunctionisnowreadytocreatetasktags.Now,theonlythinglefttoenabletasktagsinoursystemisthemodificationrequiredintheTagsServiceclass.Let'sopenthe

www.EBooksWorld.ir

Page 273: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

lib/tags/tags-service.jsfileandapplyourchanges:

import{generateTag,TAG_TYPE_TASK}from'./generate-tag';

@Injectable()

exportclassTagsService{

//Thismethodisusedinternallytoinitializeallavailable

//tags

initializeTags(){

//Let'salsocreatetasktags

this.projects.forEach((project)=>{

this.tags=this.tags.concat(project.tasks.map((task)=>{

return{

type:TAG_TYPE_TASK,

project,

task

};

}).map(generateTag));

});

}

}

IntheinitializeTagsmethodofourTagsServiceclass,wenowaddtaskTagobjectsforallavailabletasksinprojects.First,wemapeachprojecttasktotherequiredsubjectobjectbythegenerateTagfunction.Then,wecansimplymaptheresultingarrayusingthegenerateTagfunctiondirectly.TheresultisanarrayofgeneratedtaskTagobjectsthatwethenconcatenateintothetagslistoftheTagsServiceclass.

Thiswasn'ttoocomplicated,right?Thisrelativelysimplechangeresultsinahugeimprovementforourusers.Theycannowreferenceindividualtaskseverywhereinoursystemwhereweenabledtags:

www.EBooksWorld.ir

Page 274: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

TheEditorcomponentdisplayingnewly-addedtasktags

www.EBooksWorld.ir

Page 275: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

ManagingeffortsInthissection,wewillcreatesomecomponentsthathelpuskeeptrackofefforts.Primarily,wewillusethistomanageeffortsontasks,butthiscouldbeappliedtoanypartofourapplicationwhereweneedtokeeptrackoftime.

Effortsinourcontextalwaysconsistoftwocomponents:

Estimatedduration:ThisisthedurationthatisinitiallyestimatedforthetaskEffectiveduration:Thisisthedurationoftimethatisspentonagiventask

Fortimedurations,weassumesometimeunitsandrulesthatwillsimplifytheprocessingoftimeandaligntosomeworkingstandards.Thegoalhereisnottoprovidearazorsharptimemanagementbutsomethingthatisaccurateenoughtobringvalue.Forthispurpose,wedefinethefollowingworkingtimeunits:

Minute:Oneminuteisaregular60secondsHour:Onehouralwaysrepresents60minutesDay:OnedayrepresentsaregularworkdayofeighthoursWeek:Oneweekisequivalenttofiveworkingdays(5*8hours)

www.EBooksWorld.ir

Page 276: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

ThetimedurationinputWecannowstarttowriteacomplexuserinterfacecomponent,whereuserscanenterindividualtimeunitsindifferentinputelement.However,Ibelieveit'smuchmoreconvenienttotreattimedurationinputwithano-UIapproach.Therefore,insteadofbuildingacomplexuserinterface,wecansimplyagreeonatextualshortformtowritedurations,andlettheuserwritesomething,suchas1.5dor5h30m,inordertoprovideinput.Stickingtotheconventionthatwepreviouslyestablished,wecanbuildasimpleparserthatcanhandlethissortofinput.

Thisapproachhasseveraladvantages.Besidesthat,thisisoneofthemosteffectivewaystoentertimedurations,andit'salsoeasyforustoimplement.WecansimplyreuseourEditorcomponenttogathertextinputfromtheuser.Then,weuseaconversionprocesstoparsetheenteredtimeduration.

Let'sspinupanewmodulethathelpsusdealwiththeseconversions.Wecreateanewmoduleinthelib/utilities/time-utilities.jsfile.

First,weneedtohaveaconstantthatdefinesalltheunitsweneedfortheconversionprocess:

exportconstUNITS=[{

short:'w',

milliseconds:5*8*60*60*1000

},{

short:'d',

milliseconds:8*60*60*1000

},{

short:'h',

milliseconds:60*60*1000

},{

short:'m',

milliseconds:60*1000

}];

Thisisalltheunitsthatweneedtodealwithfornow.Youcanseethemillisecondsbeingcalculatedatinterpretationtime.Wecanalsowritethemillisecondsasconstants,butthisprovidesuswithsometransparencyonhowwegettothesevaluesandwecanspearonsomecomments.

Let'slookatourparsingfunction,whichwecanusetoparsetextinputintotimedurations:

exportfunctionparseDuration(formattedDuration){

constpattern=/[\d\.]+\s*[wdhm]/g;

lettimeSpan=0;

letresult;

while(result=pattern.exec(formattedDuration)){

constchunk=result[0].replace(/\s/g,'');

letamount=Number(chunk.slice(0,-1));

letunitShortName=chunk.slice(-1);

timeSpan+=amount*UNITS.find(

(unit)=>unit.short===unitShortName

www.EBooksWorld.ir

Page 277: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

).milliseconds;

}

return+timeSpan||null;

}

Let'sanalyzetheprecedingcodebrieflytoexplainwhatwedohere:

1. First,wedefinearegularexpressionthathelpsusdissectthetextrepresentationofaduration.Thispatternwillextractchunksfromthetextinputthatareimportanttocalculatethedurationbehindthetextrepresentation.Thesechunksalwaysconsistofanumber,followedbyeitherw,d,h,orm.Therefore,thetext10w3d2h30mwillbesplitintothechunks10w,3d,2h,and30m.

2. WeinitializeatimeSpanvariablewith0,sowecanaddallthemillisecondsfromdiscoveredchunkstogetherandlaterreturnthissum.

3. Foreachofthepreviously-extractedchunks,wenowextractthenumbercomponentintoavariablecalledamount,andtheunit(w,d,h,orm)intoavariablecalledunitShortName.

4. Now,wecanlookupthedataintheUNITSconstantfortheunitofthechunkthatwewillprocess,multiplytheamountofmillisecondsoftheunitbytheamountweextractfromthechunk,andthenaddthatresulttoourtimeSpanvariable.

Wellthisisquiteaneatfunctionwebuilthere.Itacceptsaformattedtimedurationstringandconvertsitintomilliseconds.Thisisalreadyhalfofwhatweneedtodealwithtextualrepresentationoftimedurations.ThesecondpieceistheoppositeofwhatwehavewiththeparseDurationfunctiontoconvertadurationinmillisecondsintoaformatteddurationstring:

exportfunctionformatDuration(timeSpan){

returnUNITS.reduce((str,unit)=>{

constamount=timeSpan/unit.milliseconds;

if(amount>=1){

constfullUnits=Math.floor(amount);

constformatted=`${str}${fullUnits}${unit.short}`;

timeSpan-=fullUnits*unit.milliseconds;

returnformatted;

}else{

returnstr;

}

},'').trim();

}

Let'salsoexplainbrieflywhattheformatDurationfunctiondoes:

WeusetheArray.prototype.reducefunctiontoformatastringthatcontainsalltimeunitsandtheiramount.WeiterateoverallavailabletimeunitsintheUNITSconstantstartingwiththelargestunitforweeks.WethendividethetimeSpanvariable,whichisinmilliseconds,bythemillisecondsoftheunitwhichgivesustheamountofthegivenunit.Iftheamountisgreaterthanorequalto1,wecanaddtheunitwiththegivenamountandunitshortnametoourformattedstring.Aswecouldbeleftwithsomefractionsafterthecommaintheamount,whichwewill

www.EBooksWorld.ir

Page 278: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

needtoencodeinsmallerunits,wesubtracttheflooredversionofouramountfromthetimeSpanbeforewereturntothereducefunctionagain.Thisprocessisrepeatedforeveryunit,whereeachunitwillonlyprovideformattedoutputiftheamountisgreaterthanorequalto1.

Thisisallweneedtoconvertbackandforthbetweenformattedtimedurationandtimedurationrepresentedinmilliseconds.

We'lldoonemorethingbeforewecreatetheactualcomponenttoentertimedurations.WewillcreateasimplepipethatbasicallyjustwrapsourformatTimefunction.Forthis,wewillcreateanewlib/pipes/format-duration.jsfile:

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

import{formatDuration}from'../utilities/time-utilities';

@Pipe({

name:'formatDuration'

})

exportclassFormatDurationPipe{

transform(value){

if(value==null||typeofvalue!=='number'){

returnvalue;

}

returnformatDuration(value);

}

}

UsingtheformatTimefunctionofourtime-utilitiesmodule,wenowhavetheabilitytoformatdurationsinmilliseconddirectlyfrominourtemplates.

www.EBooksWorld.ir

Page 279: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

ComponentstomanageeffortsOkay,thisisenoughtimemathforthemoment.Let'snowusetheelementsthatwecreatedtoshapesomecomponentsthatwillhelpusgatheruserinput.

Inthissection,wewillcreatetwocomponentstomanageefforts:

Duration:TheDurationcomponentisasimpleUIcomponent,enablinguserinputoftimedurationsusingtheformattedtimestringswedealtwithintheprevioustopics.ItusesanEditorcomponenttoenableuserinputandmakesuseoftheFormatTimePipepipeaswellastheparseDurationutilityfunction.Efforts:TheEffortscomponentisjustacompositionoftwoDurationcomponentsthatrepresenttheestimatedeffortandtheeffectiveeffortspentonagiventask.Followingastrictruleofcomposition,thiscomponentisimportantforussothatwedon'trepeatourselvesandinsteadcomposealargercomponent.

Let'sstartwiththeDurationcomponentclass,andcreateanewlib/ui/duration/duration.jsfile:

import{FormatDurationPipe}from'../../pipes/format-duration';

import{Editor}from'../../ui/editor/editor';

import{parseDuration}from'../../utilities/time-utilities';

@Component({

selector:'ngc-duration',

directives:[Editor],

pipes:[FormatDurationPipe]

})

exportclassDuration{

@Input()duration;

@Output()durationChange=newEventEmitter();

onEditSaved(formattedDuration){

this.durationChange.next(formattedDuration?

parseDuration(formattedDuration):null);

}

}

There'snothingfancyaboutthiscomponentreallybecausewecreatedthebulkofthelogicalreadyandwesimplycomposeahighercomponenttogether.

Asthedurationinput,weexpectatimedurationinmilliseconds,whilethedurationChangeoutputpropertywillemiteventswhentheuserprovidessomeinput.

TheonEditSavedmethodservesinthebindingtotheEditorcomponentinourcomponent.WhenevertheusersaveshiseditsontheEditorcomponent,we'lltakethisinput,converttheformattedtimedurationintomillisecondsusingtheparseDurationfunction,andre-emittheconvertedvalueusingthedurationChangeoutputproperty.

www.EBooksWorld.ir

Page 280: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Let'slookatthetemplateofourcomponentinthelib/ui/duration/duration.htmlfile:

<ngc-editor[content]="duration|formatDuration"

[showControls]="true"

(editSaved)="onEditSaved($event)"></ngc-editor>

Surprisedwithhowsimpleourtemplateis?Well,thisisexactlywhatweshouldachievewithhighercomponentsonceweestablishagoodfoundationofbasecomponents.Well-organizedcompositionradicallysimplifiesourcode.TheonlythingthatwedealwithhereisourgoodoldEditorcomponent.

WebindthedurationinputpropertyofourDurationcomponenttothecontentinputpropertyoftheEditorcomponent.Aswe'dliketopasstheformattedtimedurationandnotthedurationinmilliseconds,weusetheFormatDurationPipepipetoconvertinthebindingexpression.

IftheEditorcomponentnotifiesusaboutasavededit,wecalltheonEditSavedmethodonourDurationcomponent,whichwillparsetheentereddurationandre-emittheresultingvalue.

Asweinitiallydefinedalleffortstoconsistofanestimatedandaneffectiveduration,wewouldnowliketocreateanothercomponentthatcombinesthesetwodurations.

Let'screateanewEffortscomponentbystartingwithanewtemplateonthelib/efforts/efforts.htmlpath:

<divclass="efforts__label">Estimated:</div>

<ngc-duration[duration]="estimated"

(durationChange)="onEstimatedChange($event)">

</ngc-duration>

<divclass="efforts__label">Effective:</div>

<ngc-duration[duration]="effective"

(durationChange)="onEffectiveChange($event)">

</ngc-duration>

<buttonclass="buttonbutton--small"

(click)="addEffectiveHours(1)">+1h</button>

<buttonclass="buttonbutton--small"

(click)="addEffectiveHours(4)">+4h</button>

<buttonclass="buttonbutton--small"

(click)="addEffectiveHours(8)">+1d</button>

First,weaddtwoDurationcomponentslabelled,wherethefirstoneisusedtogatherinputfortheestimatedtimeandthelateroneforeffectivetime.

Inadditiontothis,weprovidethreesmallbuttonstoincreasetheeffectivedurationbyasimpleclick.Inthisway,theusercanquicklyaddoneorfourhours(halfaworkingday)oracompleteworkingday(whichwedefinedaseighthours).

LookingattheComponentclass,thereshouldbenosurprises.Let'sopenthe

www.EBooksWorld.ir

Page 281: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

lib/efforts/efforts.jscomponentclassfile:

import{Duration}from'../ui/duration/duration';

import{UNITS}from'../utilities/time-utilities';

@Component({

selector:'ngc-efforts',

directives:[Duration]

})

exportclassEfforts{

@Input()estimated;

@Input()effective;

@Output()effortsChange=newEventEmitter();

onEstimatedChange(estimated){

this.effortsChange.next({

estimated,

effective:this.effective

});

}

onEffectiveChange(effective){

this.effortsChange.next({

effective,

estimated:this.estimated

});

}

addEffectiveHours(hours){

this.effortsChange.next({

effective:(this.effective||0)+

hours*UNITS.find((unit)=>unit.short==='h'),

estimated:this.estimated

});

}

}

Thecomponentprovidestwoseparateinputsforestimatedandeffectivetimedurationinmilliseconds.Ifyoutakealookatthecomponenttemplateagain,theseinputpropertiesaredirectlyboundtotheinputpropertiesoftheDurationcomponents.

TheonEstimatedChangeandonEffectiveChangemethodsareusedtocreatebindingstothedurationChangeoutputpropertiesoftheDurationcomponents.AllwedohereisemitanaggregateddataobjectthatcontainstheeffectiveandestimatedtimeinmillisecondsusingtheeffortsChangeoutputproperty.

IntheaddEffectiveHoursmethod,wesimplyemitaneffortsChangeeventandupdatetheeffectivepropertybythecalculatedamountofmilliseconds.WeuseourUNITSconstantfromthetime-utilitiesmoduleinordertogettheamountofmillisecondsforanhour.

Thisisallthatweneedinordertoprovideauserinputtomanageeffortsonourtasks.To

www.EBooksWorld.ir

Page 282: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

completethistopic,wewilladdournewly-createdEffortscomponenttotheProjectTaskDetailcomponentinordertomanageeffortsontasks.

Let'sfirstlookatthecodechangesintheComponentclasslocatedinlib/project/project-task-detail/project-task-detail.js:

import{Efforts}from'../../efforts/efforts';

@Component({

selector:'ngc-project-task-details',

directives:[Editor,Efforts]

})

exportclassProjectTaskDetails{

onEffortsChange(efforts){

if(!efforts.estimated&&!efforts.effective){

this.task.efforts=null;

}else{

this.task.efforts=efforts;

}

this.project.document.persist();

}

}

BesidesprovidingtheEffortscomponenttothedirectiveslistofourProjectTaskDetailcomponent,weaddedanewonEffortsChangemethodthatdealswiththeoutputprovidedbytheEffortscomponent.

Ifbothestimatedandeffectiveeffortisn'tset,orsetto0,we'llsetthetaskeffortstonull.Otherwise,weusetheoutputdataoftheEffortscomponentandassignitasournewtaskefforts.

Afterchangingthetaskefforts,wepersisttheLiveDocumentoftheprojectinthesamewaythatwedoforthetitleandthedescriptionupdatesalready.

Let'scheckoutthechangesinthetemplateofourcomponentlocatedinlib/project/project-task-detail/project-task-detail.html:

<divclass="task-details__content">

<divclass="task-details__label">Efforts</div>

<ngc-efforts[estimated]="task?.efforts?.estimated"

[effective]="task?.efforts?.effective"

(effortsChange)="onEffortsChange($event)">

</ngc-efforts>

</div>

WearebindingtheestimatedandeffectiveinputpropertiesoftheEffortscomponenttothe

www.EBooksWorld.ir

Page 283: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

taskdataintheProjectTaskDetailcomponent.FortheeffortsChangeoutputpropertywe'reusinganexpressionthatisinvokingouronEffortsChangemethodthatwe'vejustcreated:

OurnewEffortscomponentthatconsistsoftwodurationinputcomponents

www.EBooksWorld.ir

Page 284: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

ThevisualeffortstimelineAlthoughthecomponentsthatwecreatedsofartomanageeffortsprovideagoodwaytoeditanddisplayeffortandtimedurations,wecanstillimprovethiswithsomevisualindication.

Inthissection,wewillcreateavisualeffortstimelineusingSVG.Thistimelineshoulddisplaythefollowinginformation:

ThetotalestimateddurationasagraybackgroundbarThetotaleffectivedurationasagreenbarthatoverlaysonthetotalestimateddurationbarAyellowbarthatshowsanyovertime(iftheeffectivedurationisgreaterthantheestimatedduration)

Thefollowingtwofiguresillustratethedifferentvisualstatesofoureffortstimelinecomponent:

Thevisualstateiftheestimateddurationisgreaterthantheeffectiveduration

Thevisualstateiftheeffectivedurationexceedstheestimatedduration(theovertimeisdisplayedasablackbar)

Let'sstartfleshingoutourcomponentbycreatinganewEffortsTimelineComponentclassonthelib/efforts/efforts-timeline/efforts-timeline.jspath:

www.EBooksWorld.ir

Page 285: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

@Component({

selector:'ngc-efforts-timeline',

})

exportclassEffortsTimeline{

@Input()estimated;

@Input()effective;

@Input()height;

ngOnChanges(changes){

this.done=0;

this.overtime=0;

if(!this.estimated&&this.effective||

(this.estimated&&this.estimated===this.effective)){

//Ifthere'sonlyeffectivetimeoriftheestimatedtime

//isequaltotheeffectivetimeweare100%done

this.done=100;

}elseif(this.estimated<this.effective){

//Ifwehavemoreeffectivetimethanestimatedweneedto

//calculateovertimeanddoneinpercentage

this.done=this.estimated/this.effective*100;

this.overtime=100-this.done;

}else{

//Theregularcasewherewehavelesseffectivetimethan

//estimated

this.done=this.effective/this.estimated*100;

}

}

}

Ourcomponenthasthreeinputproperties:

estimated:Thisistheestimatedtimedurationinmillisecondseffective:Thisistheeffectivetimedurationinmillisecondsheight:Thisisthedesiredheightoftheeffortstimelineinpixels

IntheOnChangeslifecyclehook,wesettwocomponentmemberfields,whicharebasedontheestimatedandeffectivetime:

done:Thiscontainsthewidthofthegreenbarinpercentagethatdisplaystheeffectivedurationwithoutovertimethatexceedstheestimateddurationovertime:Thiscontainsthewidthoftheyellowbarinpercentagethatdisplaysanyovertime,whichisanytimedurationthatexceedstheestimatedduration

Let'slookatthetemplateoftheEffortsTimelinecomponentandseehowwecannowusethedoneandovertimememberfieldstodrawourtimeline.

Wewillcreateanewlib/efforts/efforts-timeline/efforts-timeline.htmlfile:

<svgwidth="100%"[attr.height]="height">

<rect[attr.height]="height"

www.EBooksWorld.ir

Page 286: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

x="0"y="0"width="100%"

class="efforts-timeline__remaining"></rect>

<rect*ngIf="done"x="0"y="0"

[attr.width]="done+'%'"[attr.height]="height"

class="efforts-timeline__done"></rect>

<rect*ngIf="overtime"[attr.x]="done+'%'"y="0"

[attr.width]="overtime+'%'"[attr.height]="height"

class="efforts-timeline__overtime"></rect>

</svg>

OurtemplateisSVG-based,anditcontainsthreerectanglesforeachofthebarsthatwewanttodisplay.Thebackgroundbarthatwillbevisibleifthereisremainingeffortwillalwaysbedisplayed.

Abovetheremainingbar,weconditionallydisplaythedoneandtheovertimebarusingthecalculatedwidthsfromourcomponentclass.

Now,wecangoaheadandincludetheEffortsTimelineclassinourEffortscomponent.Thiswayouruserswillhavevisualfeedbackwhentheyedittheestimatedoreffectiveduration,anditprovidesthemasenseofoverview.

Let'slookintothetemplateoftheEffortscomponenttoseehowweintegratethetimeline:

<ngc-efforts-timelineheight="10"

[estimated]="estimated"

[effective]="effective">

</ngc-efforts-timeline>

AswehavetheestimatedandeffectivedurationtimesreadilyavailableinourEffortscomponent,wecansimplycreateabindingtotheEffortsTimelinecomponentinputproperties:

www.EBooksWorld.ir

Page 287: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

TheEffortscomponentdisplayingournewly-createdeffortstimelinecomponent(theovertimeofsixhoursisvisualizedwiththeyellowbar)

www.EBooksWorld.ir

Page 288: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

RecapitulatingoneffortsmanagementInthissection,we'llcreatecomponentsthatallowuserstomanageeffortseasilyandaddasimplebutpowerfultimetrackingtoourtasks.We'vedonethefollowingtoachievethis:

WeimplementedsomeutilityfunctionstodealwiththetimemathinordertoconverttimedurationsinmillisecondsintoformattedtimedurationsandviceversaWecreatedapipetoformattimedurationsinmillisecondsusingourutilityfunctionsWecreatedaDurationUIcomponent,whichwrapsanEditorcomponentandusesourtimeutilitiestoprovideano-UIkindofinputelementtoenterdurationsWecreatedanEffortscomponentthatactsasacompositionoftwoDurationcomponentsforestimatedandeffectivetimeandprovidesadditionalbuttonstoaddeffectivespenttimequicklyWeintegratedtheEffortscomponentintotheProjectTaskDetailcomponentinordertomanageeffortsontasksWecreatedavisualEffortsTimelinecomponentusingSVG,whichdisplaystheoverallprogressonatask

www.EBooksWorld.ir

Page 289: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

SettingmilestonesTrackingtimeisimportant.Idon'tknowhowyoufeelabouttime,butIreallysuckatorganizingmytime.AlthoughalotofpeopleaskmehowImanagetodosomanythings,IbelieveI'mactuallyverybadatmanaginghowIgetthesethingsdone.IfIwereabetterorganizer,Icouldgetthingsdonewithmuchlessenergyinvolved.

Onethingthatalwayshelpsmeorganizemyselfistobreakthingsdownintosmallerworkpackages.Usersthatorganizethemselveswithourtaskmanagementapplicationcanalreadydothisbycreatingtasksinprojects.Whileaprojectistheoverallgoal,wecancreatesmallertaskstoachievethisgoal.However,sometimeswetendtolosesightoftheoverallgoalwhenwe'reonlyfocusedontasks.

Milestonesareaperfectgluebetweenprojectsandtasks.Theymakesurethatwebundletaskstogetherintolargerpackages.Thiswillhelpusalotinorganizingourtasks,andwecanlookatmilestonesoftheprojecttoseetheoverallprojecthealth.However,wecanstillfocusontaskswhenweworkinthecontextofamilestone.

Inthissection,wewillcreatethenecessarycomponentsinordertoaddbasicmilestonefunctionalitytoourapplication.

Toimplementmilestonefunctionalityinourapplication,wewillsticktothefollowingdesigndecisions:

Milestonesshouldbestoredontheprojectlevel,andtaskscancontainanoptionalreferencetoaprojectmilestone.Tokeepthingssimple,theonlyinteractionpointwithmilestonesshouldbeontasklevel.Therefore,creationofmilestoneswillbedoneontasklevel,althoughthecreatedmilestoneswillbestoredonprojectlevel.Milestonescurrentlyonlyconsistofaname.Therearealotmoretomilestonesthatwecanpotentiallybuildintooursystem,suchasdeadlines,dependencies,andothernicethings.However,wewillsticktothebareminimum,whichisamilestonename.

www.EBooksWorld.ir

Page 290: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

CreatinganautocompletecomponentInordertokeepthemanagementofmilestonessimple,wewillcreateanewuserinterfacecomponenttodealwiththedesignconcernsthatwelisted.Ournewautocompletecomponentwillnotonlydisplaypossiblevaluestoselectfrom,butitwillalsoallowustocreatenewitems.WecanthensimplyusethiscomponentonourProjectTaskDetailcomponentinordertomanagemilestones.

Let'slookattheComponentclassofournewautocompletecomponentthatwewillcreateinthelib/ui/auto-complete/auto-complete.jsfile:

import{Editor}from'../editor/editor';

@Component({

selector:'ngc-auto-complete',

directives:[Editor]

})

exportclassAutoComplete{

@Input()items;

@Input()selectedItem;

@Output()selectedItemChange=newEventEmitter();

@Output()itemCreated=newEventEmitter();

}

Onceagain,ourEditorcomponentcanbereusedtocreatethishighercomponent.We'reluckythatwecreatedsuchanicecomponent,asthissavedusalotoftimethroughoutthisproject.

Let'slookattheinputandoutputpropertiesoftheAutoCompletecomponentinmoredetail:

items:Thisiswhereweexpectanarrayofstrings.Thiswillbethelistofitemsausercanchoosefromwhentypingintotheeditor.selectedItem:Thisiswhenwemaketheselecteditemaninputpropertytoactuallymakethiscomponentpure,andwecanrelyontheparentcomponenttosetthispropertyright.selectedItemChange:Thisoutputpropertywillemitaneventiftheselecteditemwaschanged.Aswecreateapurecomponenthere,wesomehowneedtopropagatetheeventofanitemthatwasselectedintheautocompletelist.itemCreated:Thisoutputpropertywillemitaneventifanewitemwasaddedtotheautocompletelist.Updatingthelistofitemsandchangingthecomponentitemsinputpropertywillstillbetheresponsibilityoftheparentcomponent.

Let'saddmorecodetoourcomponent.WeuseanEditorcomponentasmaininputsource.Whileouruserswilltypeintotheeditor,wefiltertheavailableitemsusingthetextinputoftheeditor.Let'screateafilterItemsforthispurpose:

filterItems(filter){

www.EBooksWorld.ir

Page 291: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

this.filter=filter||'';

this.filteredItems=this.items

.filter(

(item)=>item

.toLowerCase()

.indexOf(this.filter.toLowerCase().trim())!==-1)

.slice(0,10);

this.exactMatch=this.items.includes(this.filter);

}

ThefilterItemsmethodhasasingleparameter,whichisthetextthatwewanttouseinordertosearchforrelevantitemsinourlist.

Let'slookatthecontentofthemethodinmoredetail:

Forlateruseinourtemplate,wewillsetasidethefilterquerythatwasusedthelasttimethismethodwascalledInthefilteredItemsmembervariable,wewillstoreafilteredversionoftheitemlistbysearchingfortextoccurrencesofthefilterstringAsalaststep,wealsostoretheinformationifthesearchqueryresultedinanexactmatchofaniteminourlist

Now,weneedtomakesurethatiftheitemsorselectedIteminputpropertieschange,wealsoexecuteourfiltermethodagain.Forthis,wesimplyimplementthengOnChangeslifecyclehook:

ngOnChanges(changes){

if(this.items&&this.selectedItem){

this.filterItems(this.selectedItem);

}

}

Let'snowseehowwedealwiththeeventsprovidedbytheEditorcomponent:

onEditModeChange(editMode){

if(editMode){

this.showCallout=true;

this.previousSelectedItem=this.selectedItem;

}else{

this.showCallout=false;

}

}

Iftheeditorchangestoeditmode,wewanttosavethepreviouslyselecteditem.We'llneedthisiftheuserdecidestocancelhiseditsandswitchbacktothepreviousitem.Ofcourse,thisisalsothepointwhereweneedtodisplaytheautocompletelisttotheuser.

Ontheotherhand,iftheeditmodeisswitchedbacktoreadmode,wewanttohidetheautocompletelistagain:

onEditableInput(content){

this.filterItems(content);

www.EBooksWorld.ir

Page 292: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

}

TheeditableInputeventistriggeredbyoureditoroneveryeditorinputchange.Theeventprovidesuswiththetextcontentthatwasenteredbytheuser.Ifsuchaneventoccurs,weneedtoexecuteourfilterfunctionagainwiththeupdatedfilterquery:

onEditSaved(content){

if(content===''){

this.selectedItemChange.next(null);

}elseif(content!==this.selectedItem&&

!this.items.includes(content)){

this.itemCreated.next(content);

}

}

WhentheeditSavedeventistriggeredbyoureditor,weneedtodecidewhetherweshoulddoeitherofthefollowing:

EmitaneventusingtheselectedItemChangeoutputpropertyifthesavedcontentisanemptystringtosignaltheremovalofaselecteditemtotheparentcomponentEmitaneventusingtheitemCreatedoutputpropertyifvalidcontentisgivenandourlistdoesnotincludeanitemwiththatnametosignalanitemcreation:

onEditCanceled(){

this.selectedItemChange.next(this.previousSelectedItem);

}

OntheeditCanceledeventoftheEditorcomponent,wewanttoswitchbacktothepreviousselecteditem.Forthis,wecansimplyemitaneventusingtheselectedItemChangeoutputpropertyandthepreviousSelectedItemmemberthatweputasideaftertheeditorwasswitchedintoeditmode.

Theseareallthebindingfunctionsthatwewillusetowireupoureditorandinordertoattachtheautocompletefunctionalitytoit.

Therearetwomorerathersimplemethodsthatwewillcreatebeforewetakealookatthetemplateofourautocompletecomponent:

selectItem(item){

this.selectedItemChange.next(item);

}

createItem(item){

this.itemCreated.next(item);

}

Wewillusethesetwofortheclickactionsintheautocompletecalloutfromourtemplate.Let'stakealookatthetemplatesothatyoucanseeallthecodethatwejustcreatedinaction:

<ngc-editor[content]="selectedItem"

[showControls]="true"

www.EBooksWorld.ir

Page 293: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

(editModeChange)="onEditModeChange($event)"

(editableInput)="onEditableInput($event)"

(editSaved)="onEditSaved($event)"

(editCanceled)="onEditCanceled($event)"></ngc-editor>

First,theEditorcomponentisplacedandallnecessarybindingstothehandlermethodsthatwecreatedinourComponentclassareattached.

Now,wewillcreatetheautocompletelistthatwillbedisplayedasacallouttotheuserrightnexttotheeditorinputarea:

<ul*ngIf="showCallout"class="auto-complete__callout">

<li*ngFor="letitemoffilteredItems"

(click)="selectItem(item)"

class="auto-complete__item"

[class.auto-complete__item--selected]="item===selectedItem">{{item}}</li>

<li*ngIf="filter&&!exactMatch"

(click)="createItem(filter)"

class="auto-complete__itemauto-complete__item--create">Create"{{filter}}"

</li>

</ul>

WerelyontheshowCalloutmembersetbytheonEditModeChangemethodofourComponentclasstosignalifweshoulddisplaytheautocompletelistornot.

WetheniterateoverallfiltereditemsusingtheNgFordirectiveandrenderthetextcontentofeachitem.Ifoneoftheitemsgetsclickedon,wewillcallourselectItemmethodwiththeconcerneditemastheparametervalue.

Asthelastlistelement,aftertherepeatedlistitems,weconditionallydisplayanadditionallistelementinordertocreateanonexistingmilestone.Weonlydisplaythisbuttonifthere'savalidfilteralreadyandifthere'snoexactmatchofthefiltertoanexistingmilestone:

www.EBooksWorld.ir

Page 294: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Ourmilestonecomponentplaysnicelytogetherwiththeeditorcomponentusingacleancomposition

Nowthatwearealldonewithourautocompletecomponent,theonlythinglefttodoinordertomanageprojectmilestonesistomakeuseofitintheProjectTaskDetailscomponent.

Let'sopentheComponentclasslocatedinlib/project/project-task-details/project-task-details.jsandapplythenecessarymodifications:

import{AutoComplete}from'../../ui/auto-complete/auto-complete';

@Component({

selector:'ngc-project-task-details',

directives:[…,AutoComplete]

})

exportclassProjectTaskDetails{

constructor(@Inject(forwardRef(()=>Project))project,{

this.projectChangeSubscription=

this.project.document.change.subscribe((data)=>{

this.projectMilestones=data.milestones||[];

});

}

onMilestoneSelected(milestone){

this.task.milestone=milestone;

this.project.document.persist();

}

onMilestoneCreated(milestone){

this.project.document.data.milestones=

this.project.document.data.milestones||[];

this.project.document.data.milestones.push(milestone);

this.task.milestone=milestone;

this.project.document.persist();

}

}

Inthesubscriptiontoprojectchanges,wenowalsoextractanypreexistingprojectmilestonesandstoretheminaprojectMilestonesmembervariable.Thismakesiteasiertoreferenceinthetemplate.

TheonMilestoneSelectedmethodwillbeboundtotheselectItemChangeoutputpropertyoftheAutoCompletecomponent.WeusetheemittedvalueoftheAutoCompletecomponenttosetourtasksmilestoneandpersisttheLiveDocumentprojectusingitspersistmethod.

www.EBooksWorld.ir

Page 295: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

TheonMilestoneCreatedmethodwillbeboundtotheitemCreatedoutputpropertyoftheAutoCompletecomponent.Onsuchanevent,weaddthecreatedmilestonetotheprojectsmilestonelistaswellasassignthecurrenttasktothecreatedmilestone.AfterupdatingtheLiveDocumentdata,weusethepersistmethodtosaveallchanges.

Let'slookintolib/project/project-task-details/project-task-details.htmltoseethenecessarychangesinourtemplate:

<divclass="task-details__content">

<ngc-auto-complete[items]="projectMilestones"

[selectedItem]="task?.milestone"

(selectedItemChange)="onMilestoneSelected($event)"

(itemCreated)="onMilestoneCreated($event)">

</ngc-auto-complete>

</div>

Besidestheoutputpropertybindingsthatyou'realreadyawareof,wealsocreatetwoinputbindingsfortheitemsandselectedIteminputpropertiesoftheAutoCompletecomponent.

Thisisalreadyit.WecreatedanewUIcomponentthatprovidesautocompletionandusedthatcomponenttoimplementmilestonemanagementonourtasks.

Isn'titnicehoweasyitsuddenlyseemstoimplementnewfunctionalitywhenusingcomponentswithproperencapsulation?Thegreatthingaboutcomponent-orienteddevelopmentisthatyourdevelopmenttimefornewfunctionalitydecreasedwiththeamountofreusablecomponentsthatyoualreadycreated.

www.EBooksWorld.ir

Page 296: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

SummaryInthischapter,weimplementedsomecomponentsthathelpouruserskeeptrackoftime.Theycannowlogeffortsontasksandmanagemilestonesonprojects.Wecreatedanewtaskdetailviewthatcanbeaccessedusinganavigationlinkonourtasklist.

Oncemore,weexperiencedthepowerofcompositionusingcomponents,andreusingexistingcomponents,wewereabletoeasilyimplementhighercomponentsthatprovidemorecomplexfunctionality.

Inthenextchapter,wewilllookathowtousethechartinglibraryChartistandcreatesomewrappercomponentsthatallowustobuildreusablecharts.Wewillbuildadashboardforourtaskmanagementsystem,wherewewillseeourchartcomponentsinaction.

www.EBooksWorld.ir

Page 297: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Chapter9.SpaceshipDashboardWhenIwasachild,Ilovedtoplayspaceshippilot.Ipiledupoldcartonboxesanddecoratedtheinteriortolooklikeaspaceshipcockpit.Withamarker,Idrewaspaceshipdashboardontheinsideoftheboxes,andIrememberplayinginthereforhours.

Thethingthat'sspecialaboutthedesignofcockpitsandspaceshipdashboardsisthattheyneedtoprovideanoverviewandcontroloverthewholespaceshiponverylimitedspace.Ithinkthesamealsoappliestoapplicationdashboards.Adashboardshouldprovidetheuserwithanoverviewandasensefortheoverallstatusofwhat'sgoingon.

Inthischapter,wewillcreatesuchadashboardforourtaskmanagementapplication.WewillmakeuseoftheopensourcechartinglibraryChartisttocreategoodlookingresponsivechartsandprovideanoverviewoveropentasksandprojectstatus:

Apreviewofthetaskschartthatwewillbuildduringthecourseofthischapter

www.EBooksWorld.ir

Page 298: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Onahigherlevel,wewillcreatethefollowingcomponentsinthischapter:

Projectsummary:Thisistheprojectsummarythatwillprovideaquickinsightintotheoverallprojectstatus.Byaggregatingalleffortsofcontainingtasks,wecanprovideaniceoveralleffortsstatus,forwhichwehavecreatedthecomponentsinthepreviouschapter.Projectactivitychart:Withoutanylabelsorscales,thisbarchartwilljustgiveaquicksensefortheactivityonprojectsinthelast24hours.Projecttaskschart:Thischartprovidesanoverviewofthetaskprogressonprojects.Usingalinechart,wewilldisplaythecountofopentasksoveracertaintimeperiod.UsingourTogglecomponentthatwecreatedinChapter2,Ready,Set,Go!,ofthisbook,we'llprovideaneasywayfortheusertoswitchthedisplayedtimeframeonthechart.

www.EBooksWorld.ir

Page 299: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

IntroductiontoChartistWewillcreatesomecomponentsinthischapterthatwillrendercharts,andweshouldlookforsomehelpinrenderingthem.Ofcourse,wecanfollowasimilarapproachaswedidinChapter6,KeepingUpwithActivities,whenwedrewouractivitytimeline.However,whenitcomestomorecomplexdatavisualization,it'sbettertorelyonalibrarytodotheheavylifting.

Itshouldn'tbeasurprisethatwe'lluseChartisttofillthisgapbecauseI'vespentalmosttwoyearswritingit.AstheauthorofChartist,Ifeelveryluckythatwe'vefoundaperfectspotinthisbooktomakeuseofit.

I'dliketotaketheopportunitytointroduceyoutoChartistbrieflybeforewediveintotheimplementationofthecomponentsforourdashboard.

TheclaimofChartistissimpleresponsivecharts,andthisisluckilystillthecaseafterthreeyearsofexistence.Icantellyouthatprobablythehardestjobofmaintainingthislibrarywastoprotectitfromfeaturebloating.Therearesomanygreatmovements,technologies,andideasintheopensourcecommunityandtoresistandalwaysstayfocusedontheinitialclaimwasn'teasy.

Letmeshowyouaverybasicexampleofhowyoucancreateasimplelinechartonceyou'veincludedtheChartistscriptsonyourwebsite:

constchart=newChartist.Line('#chart',{

labels:['Mon','Tue','Wed','Thu','Fri'],

series:[

[10,7,2,8,5]

]

});

ThecorrespondingHTMLmarkupthatisrequiredforthisexamplelooksassimpleasthefollowing:

<body>

<divid="chart"class="ct-golden-section"></div>

</body>

ThefollowingfigureshowsyoutheresultingchartthatisrenderedbyChartist:

www.EBooksWorld.ir

Page 300: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

AsimplelinechartrenderedwithChartist

Ibelievethatbysayingthatwe'vebeenstickingtoourclaimtostaysimple,we'venotpromisedtoomuch.

Let'slookatthesecondcoreconcernofChartist,whichistobeperfectlyresponsive.Well,let'sstartwithoneofmymostappreciatedprinciplesinwebdevelopment,whichistheseparationofconcernsinthefrontend.Chartisttriestosticktothisseparationwhereverpossible,whichmeansthatitusesCSSforitsappearance,SVGforthebasicgraphicalstructure,andJavaScriptforanybehavior.Simplybyfollowingthisprinciple,we'vealreadyenabledalotofresponsiveness.WecanuseCSSmediaqueriestoapplydifferentstylestoourchartsondifferentmedia.

WhileCSSisgreatforvisualstyles,thereareplentyofelementsintheprocessofrenderingcharts,whichcan'tbecontrolledsimplybystyling.Afterall,thisisthereasonwhyweuseaJavaScriptlibrarytorendercharts.

So,howcanwecontrolhowChartistrendersourchartsondifferentmediaifwehaven'tgotcontroloverthisinCSS?Well,Chartistprovidessomethingcalledresponsiveconfigurationoverrides.UsingthebrowsersmatchMediaAPI,Chartistisabletoprovideaconfigurationmechanismthatallowsyoutospecifyoptionsthatyouwantoverriddenoncertainmedia.

Let'slookintoasimpleexampleofhowwecaneasilyimplementresponsivebehaviorusingamobile-firstapproach:

constchart=newChartist.Line('#chart',{

labels:['Mon','Tue','Wed','Thu','Fri'],

series:[

[10,7,2,8,5]

www.EBooksWorld.ir

Page 301: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

]

},{

showPoint:true,

showLine:true

},[

['screenand(min-width:400px)',{

showPoint:false

}],

['screenand(min-width:800px)',{

lineSmooth:false

}]

]);

Here,thesecondparametertotheChartist.Lineconstructorsetstheinitialoptions;wecanprovideoverridingoptionsannotatedwithmediaqueriesinanarrayasthethirdparameteroftheconstructor.Inthisexample,we'lloverridetheshowPointoptionforanymedialargerthan400pxinwidth.Medialargerthan800pxinwidthwillreceiveboththeshowPointoverrideaswellasthelineSmoothoverride.

Notonlycanwespecifyrealmediaqueriestotriggersettingchanges,butwecanalsouseanoverridingmechanismthatisverysimilartoCSS.Thisway,wecanimplementvariousapproaches,suchasrangedorexclusivemediaqueries,mobile-first,ordesktop-first.ThisresponsiveoptionsmechanismcanbeusedforalloptionsavailableinChartist.

www.EBooksWorld.ir

Page 302: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Displayingthepreviouschartonthreedifferentmedialefttoright,startingfromamediawithlessthan400px(A),lessthan800px(B),andmorethan800px(C).

Asyoucansee,implementingcomplexresponsivebehaviorisabreezewithChartist.Althoughourtaskmanagementapplicationwasnevermeanttobearesponsivewebapplication,wecanstillbenefitfromthisfeatureinordertooptimizeourcontent.

IfI'vetickledyourfantasywithChartist,Irecommendthatyoucheckouttheproject'swebsiteathttp://gionkunz.github.io/chartist-js.Onthewebsite,youcanalsovisittheliveexamplepageathttp://gionkunz.github.io/chartist-js/examples.html,whereyoucanhacksomechartsdirectlyinthebrowser.

www.EBooksWorld.ir

Page 303: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

ProjectsdashboardInthischapter,we'llcreateaprojectsdashboard,whichwillconsistofthefollowingcomponents:

Taskschart:Thisiswherewe'llprovideavisualoverviewonopentasksovertime.Allprojectswillberepresentedinalinechartthatdisplaystheprogressofopentasks.We'llalsoprovidesomeuserinteractionsothattheusercanchoosebetweendifferenttimeframes.Activitychart:Thiscomponentvisualizesactivitiesinabarchartoveratimeframeof24hours.Thiswillhelpourusersquicklyidentifyoverallandpeakprojectactivities.Projectsummary:Thisiswherewe'lldisplayasummaryofeachprojectwhereweoutlinethemostimportantfacts.Ourprojectsummarycomponentwillalsoincludeanactivitychartcomponentthatvisualizesprojectactivity.Projectsdashboard:Thiscomponentisjustacompositionoftheprevioustwocomponents.Thisisourmaincomponentinthedashboard.Itrepresentsourdashboardpageandisdirectlyexposedtotherouter.

www.EBooksWorld.ir

Page 304: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

CreatingtheprojectsdashboardcomponentFirst,we'llcreateourmaindashboardcomponent.TheProjectsDashboardcomponenthasonlytworesponsibilities:

Obtainingprojectdata,whichisusedtocreatethedashboardComposingthemaindashboardlayoutbyincludingourdashboardsubcomponents

Let'sjumprightinandcreateanewcomponentclassonthepath,lib/projects-dashboard/projects-dashboard.js:

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

importtemplatefrom'./projects-dashboard.html!text';

import{ProjectService}from'../project/project-service/project-service';

@Component({

selector:'ngc-projects-dashboard',

host:{class:'projects-dashboard'},

template,

encapsulation:ViewEncapsulation.None

})

exportclassProjectsDashboard{

constructor(@Inject(ProjectService)projectService){

this.projects=projectService.change;

}

}

Inourdashboardcomponent,we'llusethechangeobservableofProjectServicedirectly.Thisisdifferenttotheusualwaythatwedealwithobservables.Usually,we'dsubscribetotheobservableinourcomponentandthenupdateourcomponentwheneverdatastreamsthrough.However,inourprojectsdashboard,we'redirectlystoringthechangeobservableofProjectServiceonourcomponent.

Now,wecanuseoneofAngular'sasynccorepipesinordertosubscribetotheobservabledirectlyinourview.

Exposingobservablesdirectlyintotheviewandusingtheasyncpipetosubscribetotheobservablecomeswithamainadvantage.

Wedon'tneedtodealwithsubscribingandunsubscribinginourcomponent,astheasyncpipewilldothatforusdirectlyintheview.

Whenanewvalueisemittedwithintheobservable,theasyncpipewillcausetheunderlyingbindingtobeupdated.Also,iftheviewgetsdestroyedbecauseofanyreason,theasyncpipewillautomaticallyunsubscribefromtheobservable.

Tip

BychainingRxJSoperatorstogether,wecanbringanobservablestreamintotherequired

www.EBooksWorld.ir

Page 305: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

shapewithoutperforminganysubscription.Usingtheasyncpipe,wecanthenleaveituptotheviewtosubscribeandunsubscribefromthetransformedobservablestream.Thisencouragesustowritepureandstatelesscomponents,andwhenusedcorrectly,thisisagreatpractice.

Let'stakealookattheviewofourcomponentcreatedintheprojects-dashboard.htmlfileinthesamedirectoryastheComponentclass:

<divclass="projects-dashboard__l-header">

<h2class="projects-dashboard__title">Dashboard</h2>

</div>

<divclass="projects-dashboard__l-main">

<h3class="projects-dashboard__sub-title">Projects</h3>

<ulclass="projects-dashboard__list">

<li*ngFor="letprojectofprojects|async">

<div>{{project.title}}</div>

<div>{{project.description}}</div>

</li>

</ul>

</div>

YoucanseefromthetemplatethatweusetheasyncpipetosubscribetotheprojectsobservableofourComponentclass.Theasyncpipewillinitiallyreturnnull,butonanychangeintheobservable,thiswillreturntheresolvedvaluefromthesubscription.Thismeansthatwedon'tneedtoworryaboutsubscribingtoourprojectlistobservable.Wecansimplymakeuseoftheasyncpipetosubscribeandresolvedirectlyinourview.

Forthemoment,weonlydisplayedtheprojecttitleanddescription,butinthenextsection,wewillcreateanewprojectsummarycomponentthatwilldealwithsomemorecomplexrendering.

www.EBooksWorld.ir

Page 306: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

ProjectsummarycomponentInthissection,we'llcreateaproject-summarycomponentthatwillprovidesomeoverviewinformationforprojects.Besidesthetitleanddescription,thiswillalsoincludeanoverviewoverthetotaleffortsontheprojecttasks.

Let'sfirstbuildthecomponentandmakethenecessarypreparationssothatwecandisplaythetotaleffortoftheunderlyingtasksoftheproject.

We'llstartwiththeComponentclassonthelib/projects-dashboard/project-summary/project-summary.jspath:

...

import{FormatEffortsPipe}from'../../pipes/format-efforts';

import{EffortsTimeline}from'../../efforts/efforts-timeline/efforts-

timeline';

importtemplatefrom'./project-summary.html!text';

@Component({

selector:'ngc-project-summary',

host:{class:'project-summary'},

template,

directives:[EffortsTimeline],

pipes:[FormatEffortsPipe],

encapsulation:ViewEncapsulation.None

})

exportclassProjectSummary{

@Input()project;

ngOnChanges(changes){

if(this.project){

this.totalEfforts=this.project.tasks.reduce(

(totalEfforts,task)=>{

if(task.efforts){

totalEfforts.estimated+=task.efforts.estimated||0;

totalEfforts.effective+=task.efforts.effective||0;

}

returntotalEfforts;

},{

estimated:0,

effective:0

});

}

}

}

Asyou'veprobablyalreadyguessed,wereusedtheEffortsTimelinecomponentthatwecreatedinthepreviouschapter.Asourprojectsummarywillalsoincludeaneffortstimeline,basedonthesamesemanticsasourtotaleffort,there'snoneedtocreateanewcomponentforthis.

www.EBooksWorld.ir

Page 307: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Whatweneedtodo,though,istoaccumulatealltaskeffortsintoanoveralleffort.UsingtheArray.prototype.reducefunction,wecanaccumulatealltaskeffortsrelativelyeasy.

Theresultingobjectfromthereducecallneedstokeepupwiththeformatthatisexpectedofaneffortsobject.Asaninitialvalue,we'llprovideaneffortsobjectwithanestimatedandeffectivetimeofzero.Then,thereducecallbackwilladdanytaskeffortvaluesthatarefoundintheproject.

Let'slookintothetemplatetoseehowwe'regoingtousethistotaleffortsdatatodisplayourEffortsTimelinecomponent:

<divclass="project-summary__title">{{project?.title}}</div>

<divclass="project-summary__description">

{{project?.description}}

</div>

<divclass="project-summary__label">TotalEfforts</div>

<ngc-efforts-timeline[estimated]="totalEfforts?.estimated"

[effective]="totalEfforts?.effective"

height="10"></ngc-efforts-timeline>

<p>{{totalEfforts|formatEfforts}}</p>

Afterdisplayingthetitleanddescriptionoftheproject,weincludedtheEffortsTimelinecomponent,whichwebindtothetotalEffortsmemberthatwejustconstructed.Thistimelinewillnowdisplaythetotalaggregatedamountofeffortsloggedonthetasks.

Inadditiontothetimeline,wealsorenderedaformattedeffortstext,suchastheonethatwealreadyrenderedintheEffortscomponentofthepreviouschapter.Forthis,weusedtheFormatEffortsPipepipe.

Now,what'sstilllefttodoistointegrateourProjectSummarycomponentintotheProjectsDashboardcomponent.

Let'slookatthetemplatemodificationintheprojects-dashboard.htmlcomponenttemplate:

...

<li*ngFor="letprojectofprojects|async">

<ngc-project-summary

[project]="project"

[routerLink]="['/projects',project._id]">

</ngc-project-summary>

</li>

...

Youcanseethatwebindtheprojectlocalviewvariable,whichwascreatedbytheNgFordirectiveinconjunctionwiththeasyncpipe,totheprojectinputpropertyofourProjectSummarycomponent.

WealsousedtheRouterLinkdirectivetoestablishthenavigationontotheProjectDetailsviewiftheuserclicksononeofthesummarytiles.

www.EBooksWorld.ir

Page 308: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

ThemodificationsintheProjectsDashboardcomponentclassarenegligible:

...

import{ROUTER_DIRECTIVES}from'@angular/router';

import{ProjectSummary}from'./project-summary/project-summary';

...

@Component({

selector:'ngc-projects-dashboard',

directives:[ProjectSummary,ROUTER_DIRECTIVES],

...

})

exportclassProjectsDashboard{

...

}

TheonlymodificationthatweappliedtotheComponentclasswastoaddtheProjectSummarycomponentandtheROUTER_DIRECTIVESconstanttothedirectiveslistofthecomponent.TheROUTER_DIRECTIVESconstantincludestheRouterOutletandRouterLinkdirectives,andweusethelatterinourtemplate.

Aprojectsdashboarddisplayingtwoprojectsummarycomponentswiththeaggregatedtotaleffort

Okay,sofarsogood.WecreatedtwonewcomponentsandreusedourEffortsTimelinecomponenttocreateanaggregatedviewonthetasksefforts.Inthenextsection,wewillenrichourProjectSummarycomponentwithaniceChartistchart.

www.EBooksWorld.ir

Page 309: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

CreatingyourfirstchartInthissection,wewillcreateourfirstchartusingChartisttoprovideaprojectactivityoverviewoverthepast24hours.Thisbarchartwillonlyprovidesomevisualcluesabouttheprojectactivity,andourgoalisnottomakeitprovidedetailedinformation.Forthisreason,wewillconfigureittohideanylabels,scales,andgridlines.Theonlyvisiblepartshouldbethebarsofthebarchart.

Beforewestartcreatingtheactivitychartitself,weneedtolookathowweneedtotransformandprepareourdataforthecharts.

Let'slookatwhatdatawealreadyhaveinoursystem.Asfarastheactivitiesgo,theyallhaveatimestamponthemstoredinthetimefield.However,forourchart,wewantsomethingelsedisplayed.Whatwe'relookingforisachartthatdisplaysonebarforeachhourofthepast24hours.Eachbarshouldrepresentthecountofactivitiesinthattimeframe.

Thefollowingillustrationshowsoursourcedata,whichisbasicallyatimestreamofactivityevents.Onthelowerarrow,weseethedatathatweneedtoendupwithforourchart:

Anillustrationdisplayingactivitiesasatimestreamwherethedotsrepresentactivities.Thelowerarrowisshowingarasterizedcountbyhourforthelast24hours.

Let'simplementafunctionthatdoesthetransformationoutlinedinthisimage.We'lladdthisfunctiontoourtime-utilitiesmoduleonthelib/utilities/time-utilities.jspath:

functionrasterize(timeData,timeFrame,quantity,now,fill=0){

//Floortoagiventimeframe

now=Math.floor(now/timeFrame)*timeFrame;

returntimeData.reduce((out,timeData)=>{

//Calculatingthedesignatedindexintherasterizedoutput

constindex=Math.ceil((now-timeData.time)/timeFrame);

//Iftheindexislargerorequaltothedesignedrasterized

//arraylength,wecanskipthevalue

if(index<quantity){

www.EBooksWorld.ir

Page 310: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

out[index]=(out[index]||0)+timeData.weight;

}

returnout;

},Array.from({length:quantity}).fill(fill)).reverse();

}

Let'slookattheinputparametersofournewly-createdfunction:

timeData:Thisparameterisexpectedtobeanarrayofobjectsthatcontainsatimepropertythatissettothetimestampoftheeventthatshouldbecounted.Theobjectsshouldalsocontainaweightproperty,whichisusedtocount.Usingthisproperty,wecancountoneeventastwoorevencountminusvaluestodecreasethecountinaraster.timeFrame:Thisparameterspecifiesthetimespanofeachrasterinmilliseconds.Ifwewanttohave24rasterizedframes,eachconsistingofonehourthisparameterneedstobesetto3,600,000(1h=60min=3,600s=3,600,000ms).quantity:Thisparametersetstheamountofrasterizedframesthatshouldbepresentintheoutputarray.Inthecaseof24framesofonehour,thisparametershouldbesetto24.now:Thisiswhenourfunctionisrasterizingtime,startingatagivenpointintimebackwards.Thenowparametersetsthispointintime.fill:Thisishowwecanspecifyhowwe'dlikeourrasterizedoutputarraytobeinitialized.Inthecaseofouractivitycounts,wewantthistobesetto0.

Thefunctionthatwejustcreatedisnecessarytocreatetheactivitychart.Thetransformationhelpsusprepareprojectactivitiesfortheinputdataofthechart.

It'stimetocreateourfirstchartcomponent!Let'sstartwithanewtemplatecreatedonthelib/projects-dashboard/project-summary/activity-chart/activity-chart.htmlpath:

<div#chartContainer></div>

AsweleavealltherenderinguptoChartist,thisisactuallyalreadyallthatweneed.Chartistneedsanelementasacontainertocreatethechartin.WesetachartContainerlocalviewreferencesothatwecanreferenceitfromourcomponent,andthenpassittoChartist.

Let'smoveonwiththechartcreation,andfleshouttheactivitychartcomponentbycreatingtheComponentclassinactivity-chart.jsinthesamedirectoryasthetemplate:

...

importChartistfrom'chartist';

import{rasterize,UNITS}from'../../../utilities/time-utilities';

@Component({

selector:'ngc-activity-chart',

...

})

exportclassActivityChart{

@Input()activities;

@ViewChild('chartContainer')chartContainer;

ngOnChanges(){

www.EBooksWorld.ir

Page 311: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

this.createOrUpdateChart();

}

ngAfterViewInit(){

this.createOrUpdateChart();

}

...

}

Note

Chartistisavailableforalmostallpackagemanagers,anditalsocomesbundledintheUMDmoduleformat(UniversalModuleFormat),which,infact,isawrappertoenableAMD(AsynchronousModuleDefinition),CommonJSmoduleformat,andglobalexport.

UsingJSPM,wecansimplyinstallChartistbyexecutingthefollowingcommandonthecommandline:

jspminstallchartist

AfterinstallingChartist,wecandirectlyimportitusingES6modulesyntax.

Wealsoimporttherasterizefunctionthatwecreatedsothatwecanuseitlatertoconvertouractivitiesintotheexpectedinputformatforourchart.

Aswerelyonaviewchildasacontainerelementtocreateourchart,weneedtowaitfortheAfterViewInitlifecyclehookinordertoconstructthechart.Atthesametime,weneedtorerenderthechartiftheinputactivitieschange.UsingtheOnChangeslifecyclehook,wecanreactoninputchangesandupdateourchart.

Let'snowlookatthecreateOrUpdateChartfunction,whichdoesexactlywhatitsnamealreadyimplies:

createOrUpdateChart(){

if(!this.activities||!this.chartContainer){

return;

}

consttimeData=this.activities.map((activity)=>{

return{

time:activity.time,

weight:1

};

});

constseries=[

rasterize(

timeData,

UNITS.find((unit)=>unit.short==='h').milliseconds,

24,

+newDate())

];

www.EBooksWorld.ir

Page 312: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

if(this.chart){

this.chart.update({series});

}else{

this.chart=newChartist.Bar(this.chartContainer.nativeElement,{

series

},{

width:'100%',

height:60,

axisY:{

onlyInteger:true,

showGrid:false,

showLabel:false,

offset:0

},

axisX:{

showGrid:false,

showLabel:false,

offset:0

},

chartPadding:0

});

}

}

Let'slookintothecodeinmoredetailandwalkthroughitstepbystep:

AswegetcalledbothfromtheAfterViewInitandOnChangeslifecycle,weneedtomakesurethatboththechartContainerandactivitiesinputsarereadybeforewecontinue.Now,it'stimetoconverttheactivitydatathatwereceiveasinputintotherasterizedformthatisrequiredforthechartthatwe'dliketocreate.WeuseArray.prototype.maptotransformouractivitiesintothetimeDataobjectsthatarerequiredbytherasterizefunction.Wealsopassthenecessaryparameterssothatthefunctionwillrasterizeinto24frames,eachconsistingofonehour.Ifthechartmemberisalreadysettoachartthatwaspreviouslycreated,wecanusetheupdatefunctionontheChartistchartobjecttoonlyupdatewiththenewdata.Ifthere'snochartobjectalready,weneedtocreateanewchart.AsafirstparametertotheChartist.Barconstructor,we'llpasstheDOMelementreferenceofourcontainerviewchild.Chartistwillcreateourchartinthiscontainerelement.Thesecondargumentisourdata,whichwefillwiththeseriesthatwasjustgenerated.Inthechartoptions,we'llseteverythingtoachieveaveryplain-lookingchartwithoutanydetailedinformation.

Thisisgreat!WecreatedourfirstchartcomponentusingChartist!Now,wecangobacktoourProjectSummarycomponentandintegratetheactivitycharttheretoprovideanactivityoverview:

...

import{ActivityService}from'../../activities/activity-service/activity-

service';

import{ActivityChart}from'./activity-chart/activity-chart';

@Component({

www.EBooksWorld.ir

Page 313: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

selector:'ngc-project-summary',

...

directives:[EffortsTimeline,ActivityChart],

...

})

exportclassProjectSummary{

...

constructor(@Inject(ActivityService)activityService){

this.activityService=activityService;

}

ngOnChanges(){

if(this.project){

...

this.activities=this.activityService.change

.map((activities)=>activities.filter((activity)=>activity.subject

===this.project._id));

}

}

}

ThefirstchangehereistoincludetheActivityServicesothatwecanextracttherequiredprojectactivitiestopassthemtotheActivityChartcomponent.WealsoneedtoimporttheActivityChartcomponentanddeclareitasadirectiveonthecomponent.

Asourcomponentreliesontheprojecttobeprovidedasinput,whichissubjecttochange,weneedtoimplementthelogictoextractactivitiesintheOnChangeslifecyclehookofthecomponent.

Beforewepassontheobservableactivitiesstream,weneedtofiltertheactivitiesthatcomethroughthestreamsothatweonlygetactivitiesthatarerelevanttothecurrentproject.Again,wewillusetheasyncpipeinordertosubscribetotheactivitiessothatthere'snoneedtouseasubscribeformwithinthecomponent.TheactivitiespropertywillbedirectlysettoafilteredObservable.

Let'slookatthechangesintheviewoftheProjectSummarycomponentinordertoenableourchart:

...

<divclass="project-summary__label">Activitylast24hours</div>

<ngc-activity-chart[activities]="activities|async">

</ngc-activity-chart>

WeaddourActivityChartcomponentatthebottomofthealreadyexistingtemplate.Wealsocreatethenecessarybindingtopassouractivitiesintothecomponent.Usingtheasyncpipe,wecanresolvetheobservablestreamandpassthefilteredactivitieslistintothechartcomponent.

Finally,ourProjectSummarycomponentlooksgreatandimmediatelyprovidesaprojectinsightbydisplayingtheaggregatedeffortstimelineandaniceactivitychart.Inthenext

www.EBooksWorld.ir

Page 314: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

section,we'lldiveabitdeeperintothechartingcapabilitiesofChartist,andwewillalsoprovidesomeinteractivityusingAngular.

www.EBooksWorld.ir

Page 315: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

VisualizingopentasksInthissection,wewillcreateachartcomponentusingChartist,whichwilldisplaytheopentaskprogressofprojectsovertime.Todothis,we'llusealinechartwithaspecificinterpolationthatprovidesquantizedstepsratherthanlineswithdirectlyconnectedpoints.

Wearealsoprovidingsomeinteractivityinthattheuserwillbeabletoswitchthedisplayedtimeframeusingatogglebutton.ThisallowsustoreusetheTogglecomponentthatwecreatedinChapter2,Ready,Set,Go!,ofthisbook.

Let'sfirstlookatthedatathatwehaveinoursystemandhowwecantransformitintothedataneededbyChartist.

Wecanrelyontwodataattributesofourtasksinordertodrawthemontoatimeline.Thecreatedattributeissettothetimestampatthemomentwhenthetaskwascreated.Ifataskismarkedasdone,thedoneattributeissettothetimestampatthattime.

Aswe'reonlyinterestedintheamountofopentasksatanygiventime,wecansafelypresumeamodelwhereweputalltasksontoasingletimelineandwhereweareonlyconcernedaboutthecreatedanddonetimestampsasevents.Let'slookatthefollowingillustrationtogetabetterunderstandingoftheproblem:

Anillustrationthatshowshowwecanrepresentalltasktimelinesonasingletimelineusingthecreatedanddoneevents.Thecreatedeventscountasa+1,whilethedoneeventscountas

-1.

Thelowerarrowisarepresentationofalltasksofthecreatedanddoneeventsonatimeline.Wecannowusethisinformationasinputtoourrasterizefunctioninordertogetthedatathatweneedforourchart.AsthetimeDataobjectsthatareusedasinputfortherasterizationalsosupportaweightproperty,wecanusethistorepresentthecreated(+1)ordone(-1)events.

www.EBooksWorld.ir

Page 316: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Weneedtomakeaslightmodificationtoourrasterizefunction.Sofar,therasterizefunctiononlycountseventstogetherinframes.However,fortheopentaskcounts,welookintoanaccumulationovertime.Ifthetaskcountchanges,weneedtokeepthevalueuntilitchangesagain.Intransformationofactivitiesintheprevioustopic,wedidn'tusethissamelogic.There,weonlycountedeventsinsideframes,buttherewasnoaccumulation.

Let'slookatthefollowingillustrationtoseethedifferenceascomparedtotherasterizationthatweappliedwhileprocessingactivities:

Anillustrationthatshowshowwecanaccumulatetheopentaskscountovertime

WecancounteachweightpropertyofthetimeDataobjects(events)togetherovertime.Onlyifthere'sachangeoftheaccumulatedvalue,wewillwritethecurrentaccumulatedvalueintotherasterizedoutputarray.

Let'sopenourtime-utilitiesmoduleandapplythechangestotherasterizefunction:

exportfunctionrasterize(timeData,timeFrame,quantity,

now=+newDate(),fill=0,

accumulate=false){

//Floortoagiventimeframe

now=Math.floor(now/timeFrame)*timeFrame;

//Accumulationvalueusedforaccumulationmodetokeeptrack

//ofcurrentvalue

letaccumulatedValue=0;

//Inaccumulationmodeweneedtobesurethatthetimedata

//isordered

if(accumulate){

timeData=timeData.slice().sort(

(a,b)=>a.time<b.time?-1:a.time>b.time?1:0);

}

returntimeData.reduce((rasterized,timeData)=>{

//Increasetheaccumulatedvalue,incaseweneedit

accumulatedValue+=timeData.weight;

//Calculatingthedesignatedindexintherasterizedoutput

//array

www.EBooksWorld.ir

Page 317: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

constindex=Math.ceil((now-timeData.time)/timeFrame);

//Iftheindexislargerorequaltothedesignedrasterized

//arraylength,wecanskipthevalue

if(index<quantity){

rasterized[index]=accumulate?

accumulatedValue:

(rasterized[index]||0)+timeData.weight;

}

returnrasterized;

},Array.from({length:quantity}).fill(fill)).reverse();

}

Let'swalkthroughthechangesthatweappliedtotherasterizefunctiontoallowaccumulationofframes:

Firstofall,weaddedanewparametertoourfunctionwiththenameaccumulate.WeusedtheES6defaultparameterstosettheparametertofalseifnovaluewaspassedintothefunctionwhencalled.WenowdefineanewaccumulatedValuevariable,whichweinitializewith0.Thisvariablewillbeusedtokeeptrackofthesumofallweightvaluesovertime.Thenextbitofcodeisveryimportant.Ifwewanttoaccumulatethesumofallweightvaluesovertime,weneedtomakesurethatthesevaluescomeinsequence.Inordertoensurethis,wesortthetimeDatalistbyitsitemstimeattribute.Inthereducecallback,weincreasetheaccumulatedValuevariablebytheweightvalueofthecurrenttimeDataobject.IfthetimeDataobjectfallsintoarasterizedframe,wedonotincreasethisframe'scountlikewedidbefore.Inaccumulationmode,wesettheframescounttothecurrentvalueinaccumulatedValue.Thiswillresultinallchangedaccumulatedvaluesbeingreflectedintherasterizedoutputarray.

Thisisallthepreparationthatweneedtoprocessthedateinordertorenderouropentaskschart.Let'smoveonandcreatetheComponentclassofournewchartcomponent.

www.EBooksWorld.ir

Page 318: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

CreatinganopentaskschartInthefollowingcomponent,wewillutilizetherefactoredrasterizefunctionoftheprevioustopic.Usingthenewaccumulatefunction,wecannowtrackopentaskcountsovertime.

Let'sstartwiththeComponentclassinanewlib/projects-dashboard/tasks-chart/tasks-chart.jsfiletoimplementourComponentclass:

...

importChartistfrom'chartist';

importMomentfrom'moment';

import{rasterize}from'../../utilities/time-utilities';

@Component({

selector:'ngc-tasks-chart',

...

})

exportclassTasksChart{

@Input()projects;

@ViewChild('chartContainer')chartContainer;

ngOnChanges(){

this.createOrUpdateChart();

}

ngAfterViewInit(){

this.createOrUpdateChart();

}

...

}

Sofar,thislooksexactlylikeourfirstchartcomponentwherewevisualizedprojectactivities.WealsoimportedChartistaswewilluseittorenderourchartinthecreateOrUpdateChartfunctionthatwe'llcreateshortly.Thechartthatwewillcreatewillcontainmuchmoredetailedinformation.Wewillrenderbothaxislabelsandsomescales.Inordertoformatourlabelsthatbasicallycontaintimestamps,weusetheMoment.jslibraryonceagain.

Wealsousetheprojectsinputdataandtransformitwiththeamendedrasterizeutilityfunctioninordertoprepareallthedataforourlinechart.

Let'smoveonandfleshoutthecreateOrUpdateChartmethodofourcomponent:

createOrUpdateChart(){

if(!this.projects||!this.chartContainer){

return;

}

//Createaseriesarraythatcontainsonedataseriesforeach

//project

constseries=this.projects.map((project)=>{

www.EBooksWorld.ir

Page 319: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

//FirstweneedtoreducesalltasksintoonetimeDatalist

consttimeData=project.tasks.reduce((timeData,task)=>{

//ThecreatedtimeofthetaskgeneratesatimeDatawith

//weight1

timeData.push({

time:task.created,

weight:1

});

//Ifthistaskisdone,we'realsogeneratingatimeData

//objectwithweight-1

if(task.done){

timeData.push({

time:task.done,

weight:-1

});

}

returntimeData;

},[]);

//Usingtherasterizefunctioninaccumulationmode,wecan

//createtherequireddataarraythatrepresentsourseries

//data

returnrasterize(timeData,600000,144,+newDate(),

null,true);

});

constnow=+newDate();

//Creatinglabelsforallthetimeframeswe'redisplaying

constlabels=Array.from({

length:144

}).map((e,index)=>now-index*600000).reverse();

if(this.chart){

//Ifwealreadyhaveavalidchartobject,wecansimply

//updatetheseriesdataandlabels

this.chart.update({

series,

labels

});

}else{

//CreatinganewlinechartusingthechartContainerelement

//ascontainer

this.chart=newChartist.Line(this.chartContainer.nativeElement,{

series,

labels

},{

width:'100%',

height:300,

//Usingstepinterpolation,wecancausethechartto

//renderinstepsinsteadofdirectlyconnectedpoints

lineSmooth:Chartist.Interpolation.step({

//Thefillholessettingontheinterpolationwillcause

//nullvaluestobeskippedandmakesourlineto

//connecttothenextvalidvalue

fillHoles:true

}),

www.EBooksWorld.ir

Page 320: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

axisY:{

onlyInteger:true,

low:0,

offset:70,

//We'reusingthelabelinterpolationfunctionfor

//formattingouropentaskscount

labelInterpolationFnc:(value)=>`${value}tasks`

},

axisX:{

//We'reonlydisplayingtwox-axislabelsandgridlines

labelInterpolationFnc:(value,index,array)=>index%(144/4)===0?

Moment(value).calendar():null

}

});

}

}

Okay,that'squiteabitofcodehere.Let'swalkthroughitstepbysteptogainabetterunderstandingofwhat'sgoingon:

1. First,weneedtocreateourtransformedseriesdatabymappingtheprojectslist.Theseriesarrayshouldincludeonedataarrayforeachproject.Eachdataarraywillcontaintheopenprojecttasksovertime.

2. AstherasterizefunctionexpectsalistoftimeDataobjects,wefirstneedtotransformtheprojectstasklistintothisformat.Byreducingthetasklist,wecreateasinglelistofthetimeDataobjects.ThereducefunctioncallbackwillgenerateonetimeDataobjectwithaweightof1foreachtask.Additionally,itwillgenerateatimeDataobjectforeachtaskmarkedasonewiththeweightvalue-1.ThiswillresultinthedesiredrimeDataarray,whichwecanusetoaccumulateandrasterize.

3. AfterpreparingthetimeDatalist,wecancalltherasterizefunctioninordertocreatealistofopentasksoveracertainamountoftimeframes.Weusea10minutetimeframe(600000ms)andrasterizethiswith144frames.Thismakesatotalof24hours.

4. Besidestheseriesdata,wewillalsoneedlabelsforourchart.Wecreateanewarrayandinitializethiswith144timestamps,allofwhicharesettothestartofthe144rasterizedframesthatwedisplayonthechart.

5. Now,wehavetheseriesdataandthelabelsready,andallthat'slefttodoistorenderourchart.

6. UsingthelineSmoothconfiguration,wecanspecifyaspecialkindofinterpolationforourlinechart.Thestepinterpolationwillnotconnecteachpointinourlinechartdirectly,butratheritwillmoveindiscretestepstomovefrompointtopoint.Thisisexactlywhatwe'relookingfortorendertheopentaskcountsovertime.

7. SettingthefillHolesoptiontotrueinthestepinterpolationisveryimportant.Usingthissetting,wecanactuallytellChartistthatitshouldcloseanygapswithinthedata(actuallynullvalues)andconnectthelinetothenextvalidvalue.Withoutthissetting,we'dseegapsonthechartbetweenthetaskcountchangesinourdataarrays.

8. OnelastimportantthinginourcodeisthelabelInterpolationFncoptionthatwesetonthexaxisconfiguration.Thisfunctioncannotonlybeusedtoformatalabelorinterpolateanyexpressionthatmaycomealongwiththelabel,butitalsoallowsusto

www.EBooksWorld.ir

Page 321: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

returnnullinstead.ReturningnullfromthisfunctionwillcauseChartisttoskipthegivenlabelandthecorrespondinggridline.Thisisveryusefulifwe'dliketoskipcertainlabelsbytheirvalueorbytheindexofthelabel.Inourcode,weensurethatweonlyrenderfourlabelsofall144generatedlabels.

Let'stakealookattherathersimpletemplateofourcomponentinthetasks-chart.htmlfileinthesamefolderasourComponentclassfile:

<div#chartContainerclass="tasks-chart__container"></div>

ThesameaswiththeActivityChartcomponent,weonlycreateasimplechartcontainerelement,whichwealreadyreferenceinourComponentclass.

ThisisbasicallyallthatweneededtodoinordertocreateanopentaskschartusingChartist.However,there'sstillsomeroomforimprovementhere:

OpentasksvisualizedwithourtaskschartcomponentusingChartist'sstepinterpolation

www.EBooksWorld.ir

Page 322: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

CreatingachartlegendCurrently,there'snowaytotellexactlywhichofthelinesrepresentswhatproject.Weseeonecoloredlineforeachproject,butwecan'tassociatethesecolors.Whatweneedisasimplelegendthathelpsouruserstoassociatelinechartcolorstoprojects.

Let'slookattherequiredcodechangestoimplementlegendsonourchart.IntheComponentclassofourTasksChartcomponent,weneedtoperformthefollowingmodifications:

...

exportclassTasksChart{

...

ngOnChanges(){

if(this.projects){

//Onchangesoftheprojectsinput,weneedtoupdatethe

//legend

this.legend=this.projects.map((project,index)=>{

return{

name:project.title,

class:`tasks-chart__series--series-${index+1}`

};

});

}

this.createOrUpdateChart();

}

...

}

IntheOnChangeslifecyclehook,wemaptheprojectsinputtoalistofobjectsthatcontainanameandclassproperty,whichwillsupportusinrenderingasimplelegend.Thetemplate`tasks-chart__series--series-${index+1}`stringwillgeneratethenecessaryclassnametorendertherightcolorintoourlegend.

Usingthislegendinformation,wecannowgoaheadandimplementthenecessarytemplatechangestorenderthelegendinourchartcomponent:

<ulclass="tasks-chart__series-list">

<li*ngFor="letseriesoflegend"

class="tasks-chart__series{{series.class}}">

{{series.name}}

</li>

</ul>

<div#chartContainerclass="tasks-chart__container"></div>

Well,thatwasapieceofcake,right?However,theresultspeaksforitself.Wecreatedanicelegendforthechartinjustacoupleofminutes:

www.EBooksWorld.ir

Page 323: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Opentaskschartwithouraddedlegend

www.EBooksWorld.ir

Page 324: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

MakingtaskschartinteractiveCurrently,wehardcodedthetimeframeofouropentaskcharttobe144frames,eachof10minutes,makingatotalof24hoursdisplayedtotheuser.However,maybeouruserswouldwanttochangethisview.

Inthistopic,wewillcreateasimpleinputcontrolusingourTogglecomponent,whichallowsouruserstochangethetimeframesettingsofthechart.

Wewillprovidethefollowingviewsasoptions:

Day:Thisviewwillrasterize144frames,eachconsistingof10minutes,whichmakesatotalof24hoursWeek:Thisviewwillrasterize168frames,eachconsistingofonehour,whichmakesatotalofsevendaysYear:Thisviewwillrasterize360frames,eachrepresentingafullday

Let'sstartwiththeimplementationofourtimeframeswitchbymodifyingtheComponentclasscodeoftheTasksChartcomponent:

...

import{Toggle}from'../../ui/toggle/toggle';

@Component({

...

directives:[Toggle]

})

exportclassTasksChart{

...

constructor(){

//Definetheavailabletimeframeswithinthechartprovided

//totheuserforselection

this.timeFrames=[{

name:'day',

timeFrame:600000,

amount:144

},{

name:'week',

timeFrame:3600000,

amount:168

},{

name:'year',

timeFrame:86400000,

amount:360

}];

//Fromtheavailabletimeframes,we'regeneratingalistof

//namesforlaterusewithintheTogglecomponent

this.timeFrameNames

=this.timeFrames.map((timeFrame)=>timeFrame.name);

//Thecurrentlyselectedtimeframeissettothefirst

//availableone

this.selectedTimeFrame=this.timeFrames[0];

www.EBooksWorld.ir

Page 325: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

}

...

createOrUpdateChart(){

...

constseries=this.projects.map((project)=>{

...

returnrasterize(timeData,

this.selectedTimeFrame.timeFrame,

this.selectedTimeFrame.amount,

+newDate(),null,true);

});

constnow=+newDate();

constlabels=Array.from({

length:this.selectedTimeFrame.amount

}).map((e,index)=>now-index*

this.selectedTimeFrame.timeFrame).reverse();

...

}

...

//CalledfromtheTogglecomponentifanewtimeframewas

//selected

onSelectedTimeFrameChange(timeFrameName){

//Settheselectedtimeframetotheavailabletimeframewith

//thenameselectedintheTogglecomponent

this.selectedTimeFrame=

this.timeFrames.find((timeFrame)=>

timeFrame.name===timeFrameName);

this.createOrUpdateChart();

}

}

Let'sgothroughthesechangesbriefly:

1. Firstofall,weaddedaconstructortoourComponentclassinwhichweinitializedthreenewmembers.ThetimeFramesmemberissettoanarrayoftimeframedescriptionobjects.Theycontainthename,timeFrame,andamountproperties,whicharelaterusedforthecalculations.ThetimeFrameNamesmembercontainsalistoftimeframenames,whichisdirectlyderivedfromthetimeFrameslist.Finally,wehaveaselectedTimeFramemember,whichsimplypointstothefirstavailabletimeframeobject.

2. InthecreateOrUpdateChartfunction,wenolongerrelyonhardcodedvaluesforthetaskcountrasterization,butwerefertothedataintheselectedTimeFrameobject.BychangingthisobjectreferenceandcallingthecreateOrUpdateChartfunctionagain,wecannowswitchtheviewontheunderlyingdatadynamically.

3. Finally,weaddedanewonSelectedTimeFrameChangemethod,whichactsasabindingtotheTogglecomponent,andthiswillbecalledwhenevertheuserselectsadifferenttimeframe.

Let'slookatthenecessarytemplatechangestoenableswitchingoftimeframes:

<ngc-toggle

[buttonList]="timeFrameNames"

www.EBooksWorld.ir

Page 326: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

[selectedButton]="selectedTimeFrame.name"

(selectedButtonChange)="onSelectedTimeFrameChange($event)">

</ngc-toggle>

...

<div#chartContainerclass="tasks-chart__container"></div>

FromthebindingstotheTogglecomponent,youcanalreadytellthatwerelyonthetimeFrameNamesmemberonourcomponenttorepresentallselectabletimeframes.WealsobindtotheselectedButtoninputpropertyoftheTogglecomponentusingtheselectedTimeFrame.nameproperty.OnchangesoftheselectedbuttonintheTogglecomponent,wecalltheonSelectedTimeFrameChangefunction,wherethetimeframeisswitchedandthechartisupdated.

Thisisallthatweneedtoenableswitchingthetimeframeonourchart.Theusercannowchoosebetweentheyear,week,anddayviews.

OurTasksChartcomponentisnowreadytobeintegratedintoourdashboard.WecanachievethiswithsomesmallchangestothetemplateofourProjectsDashboardcomponent:

...

<divclass="projects-dashboard__l-main">

<h3class="projects-dashboard__sub-title">TasksOverview</h3>

<divclass="projects-dashboard__tasks">

<ngc-tasks-chart[projects]="projects|async">

</ngc-tasks-chart>

</div>

...

</div>

Thisisbasicallyallthatweneedtomake,andafterthischange,ourdashboardcontainsournicechartdisplayingopentaskcountsovertime.

InthebindingoftheTasksChartprojectsinputproperty,weusetheasyncpipeonceagaintoresolvetheobservablestreamofprojectsdirectlyintheview.

www.EBooksWorld.ir

Page 327: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

SummaryInthischapter,welearnedaboutChartistandhowtouseitinconjunctionwithAngulartocreategoodlookingandfunctionalcharts.Wecanleveragethepowerofbothworlds,andcreatereusablechartcomponentsthatarenicelyencapsulated.

Justlikeinmostrealcases,wealwayshavealotofdata,buttheonethatweneedinaparticularcase.Welearnedhowwecantransformexistingdataintoaformthatisoptimizedforvisualrepresentation.

Inthenextchapter,wewilllookatbuildingapluginsysteminourapplication.Thiswillallowustodevelopportablefunctionalitythatispackagedintoplugins.Ourpluginsystemwillloadnewpluginsdynamically,andwewilluseittodevelopasimpleagileestimationplugin.

www.EBooksWorld.ir

Page 328: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Chapter10.MakingThingsPluggableI'mahugefanofpluginarchitectures.Besidestheirtremendouslypositiveeffectonyourapplicationandscopemanagement,theyarealsoalotoffuntodevelop.I'drecommendintegratingapluginarchitectureintheirlibraryorapplicationtoanyonewhoasksme.Agoodpluginarchitectureallowsyoutowriteaconciseapplicationcoreandprovideadditionalfunctionalityviaplugins.

Designingyourwholeapplicationinawaythatitallowsyoutobuildapluginarchitecturehasagreateffectontheextensibilityofyoursystem.Thisisbecauseyou'remakingyourapplicationopenforextensibilitybutclosingitformodification.

Whileauthoringmyopensourceprojects,Ialsoexperiencedthatapluginarchitecturehelpsyoumanagethescopeofyourproject.Sometimes,arequestedfeatureisreallyniceandhelpful,butitwillstillbloatthelibrarycore.Insteadofbloatingyourwholeapplicationorlibrarywithsuchfeatures,youcansimplywriteaplugintogetthejobdone.

Inthischapter,wewillcreateourownpluginarchitecturethatwillhelpusextendthefeaturesofourapplicationwithoutbloatingitscore.We'llfirstbuildthepluginAPIinthecoreofourapplicationandthenusetheAPItoimplementanicelittleagileplugin,whichhelpsustoestimatetasksusingstorypoints.

We'llcoverthefollowingtopicsinthischapter:

Designingapluginarchitecture,basedontheAngularecosystemImplementingadecorator-basedpluginAPIUsingComponentResolverandViewContainerReftoinstantiateplugincomponentsintopredefinedslotsinourapplicationImplementingaplugin-loadingmechanismusingSystemJSUsingareactiveapproachinourpluginarchitecturetoenableplugandplaystylepluginsImplementinganagileplugintorecordstorypointsusingthenewpluginAPI

www.EBooksWorld.ir

Page 329: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

PluginarchitectureAtahigherlevel,apluginarchitectureshouldfulfilatleastthefollowingrequirements:

Extensibility:Themainideabehindpluginsistoallowtheextensionofthecorefunctionalityusingisolatedbundlesofcode.Agreatpluginarchitectureallowsyoutoextendthecoreseamlesslyandwithoutnoticeableperformancelosses.Portability:Pluginsshouldbeisolatedenoughsothattheycanbepluggedintothesystemduringruntime.Thereshouldn'tbeanecessitytorebuildasystemtoenableplugins.Ideally,pluginscanevenbeloadedatanytimeduringruntime.Theycanbedeactivatedandactivatedandshouldnotcausethesystemtorunintoaninconsistentstate.Composability:Apluginsystemshouldallowtheuseofmanypluginsinparallelandallowanextensionofthesystembycompositingmultiplepluginstogether.Ideally,thesystemalsoincludesdependencymanagement,pluginversionmanagement,andpluginintercommunication.

Therearealotofdifferentapproachesonhowtoimplementapluginarchitecture.Althoughtheseapproachescanvaryalot,there'salmostalwaysamechanisminplacethatprovidesunifiedextensionpoints.Withoutthis,itwillbehardtoextendasystemuniformly.

I'veworkedwithsomepluginarchitecturesinthepast,andbesidesusingexistingpluginmechanisms,I'vealsoenjoyeddesigningsomeofthemmyself.Thefollowinglistshouldprovideanideaaboutsomeoftheapproachesthatyoucanusewhendesigningapluginsystem:

DSL:Usingdomain-specificlanguagesisonewaytoimplementapluggablearchitecture.Afteryou'veimplementedthecoreofyourapplication,youcandevelopanAPIorevenascriptinglanguagethatallowsyoutodevelopfurtherfeaturesusingthisDSL.AlotofvideogameenginesandCGapplicationsrelyonthisapproach.Althoughthisapproachisveryflexible,itcanalsoleadtoperformanceissuesquickly,andit'spronetointroducingcomplexity.Mostly,theprerequisitestoimplementsuchanarchitecturearetoexposeverylow-levelcoreoperations(suchasaddingUIelements,configuringprocessflows,andsoon)intotheDSL,whichdoesnotprovideclearboundariesandextensionpointsbutisextremelyflexible.SomeexamplesofDSL-basedpluginsystemsaremostofAdobe'sCGapplications,3DStudioMax,andMaya,butalsogameengines,suchasUnrealEngineortheRealVirtualityEnginefromBohemiaInteractiveStudio.Thecoreisthepluginsystem:Anotherapproachistobuildsuchasophisticatedpluginarchitecturethatitfulfilsalltheoutlinedrequirementsinthepreviouslisting(extensibility,portability,andcomposability)andevensomemoresophisticatedrequirementsontop.Thecoreofyourapplicationisonelargepluginsystem.Then,youstarttoimplementeverythingasaplugin.Eventhecoreconcernsofyourapplicationwillbeimplementedasplugins.AperfectexampleofthisapproachistheEclipseIDEwithitsEquinoxcore.Theproblemwiththisapproachisthatyou'relikelytorunintoperformanceproblemsasyourapplicationgrows.Aseverythingisaplugin,

www.EBooksWorld.ir

Page 330: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

optimizationisquitetricky,andplugincompatibilitycanmaketheapplicationunstable.Event-basedextensionpoints:Also,agreatwaytoprovideextensibilityofasystemisbyopeningupthepipelineofyoursystemtoinputfromoutside.Imaginethatforeveryimportantstepinyourapplication,younotifytheoutsideworldaboutthestepandallowinterceptionbeforetheapplicationcontinueswithprocessing.Inthismanner,apluginwilljustbeanadapterthatlistensforthesepipelineeventsofyourapplicationandthenmodifiesthebehaviorasrequired.Apluginitselfcanalsoemitevents,whichthencanbeprocessedbyotherpluginsagain.Thisarchitectureisreallyflexible,asitallowsyoutochangethebehaviorofyourcorefunctionalitywithoutintroducingtoomuchcomplexity.It'salsofairlyeasytoimplementthisapproachevenafteryou'vefinishedyourcorewithoutanythoughtsaboutapluginsystem.I'vebeenfollowingthisapproachinmyopensourceprojectChartistand,sofar,I'vehadverygoodresultswithit.Plugininterfaces:Anapplicationcanexposeasetofinterfacesthatdefinecertainextensionpoints.ThisapproachisheavilyusedintheJavaframeworkwhereit'sknownasServiceProviderInterface(SPI).Providersimplementacertaincontract,whichallowsthecoresystemtorelyonaninterfaceratherthananimplementation.Theseproviderscanthenbecycledbackintothesystemwheretheyaremadeavailabletotheframeworkandotherproviders.Althoughthisisprobablythesafestwaytoprovideextensibilityintermsofuniformness,it'salsothemostrigidone.Apluginwillneverbeallowedtodoanythingelsethatwasspecifiedinthecontractoftheinterfaces.

Youcanseethatallfourapproachesvaryalot.Fromthetop-most,whichprovidesextremeflexibilityatthecostofcomplexityandstability,tothebottom-most,whichisveryrobustbutalsorigid.

Theapproachthatyouchoosewhenimplementingapluginsystemheavilydependsontherequirementsforyourapplication.Ifyoudonotplanonbuildinganapplicationthatcomesbundledinvariousflavorsandwheremultipleversionsforcompletelydifferentconcernsshouldexist,theapproachestothebottomoftheprecedinglistingareprobablymorelikelytheonesthatyoushouldfollow.

www.EBooksWorld.ir

Page 331: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

PluggableUIcomponentsThesystemthatwe'regoingtobuildinthischapterborrowsalotofmechanismsthatarealreadypresentintheAngularframework.Inordertoimplementextensibilityusingplugins,werelyonthefollowingcoreconcepts:

WeusedirectivestoindicateextensionpointsintheUI,whichwecallpluginslots.ThesepluginslotdirectiveswillberesponsibleforthedynamicinstantiationofplugincomponentsandwillinsertthemintotheapplicationUIatthegivenposition.Pluginsexposecomponentsusingaconceptthatwecallpluginplacements.Pluginplacementsdeclarewhatcomponentsofapluginshouldbeplacedintowhichpluginslotsintheapplication.Wealsousepluginplacementstodecidetheorderinwhichcomponentsfromdifferentpluginsshouldbeinsertedintothepluginslots.Forthis,we'lluseapropertycalledpriority.WeusethedependencyinjectionofAngulartoprovidetheinstantiatedplugininformationintotheplugincomponents.Astheplugincomponentswillbeplacedinaspotwherethere'salreadyaninjectorpresent,theywillbeabletoinjectsurroundingcomponentsanddependenciesinordertoconnecttotheapplication.

Let'slookatthefollowingillustrationtopicturethearchitectureofourpluginsystembeforewestartimplementingit:

Thepluginarchitecturethatwe'llimplementinthischapterusingsomebasicUMLandcardinalityannotations

Let'slookatthedifferententitiesinthisdiagramandquicklyexplainwhattheydo:

PluginConfig:ThisES7decoratoristhekeyelementwhenimplementingaplugin.By

www.EBooksWorld.ir

Page 332: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

annotatingapluginclassusingthisdecorator,wecanstoremeta-informationabouttheplugin,whichwillbeusedlaterbyourpluginsystem.Themetadataincludesthepluginname,adescription,andtheplacementinformation.PluginData:Thisisanaggregationclassthatisusedbythepluginsystemtocoupletheinformationaboutaninstantiatedpluginwiththeplacementinformation(whereplugincomponentsshouldbeinstantiated).Thisentityisexposedindependencyinjectiononceaplugincomponentiscreated.Anyplugincomponentcanmakeuseofthisentitytogatherinformationabouttheinstantiationortogainaccesstotheplugininstance.PluginService:Thisistheserviceusedtoglueourpluginsystemtogether.It'smainlyusedtoloadplugins,removeplugins,orusedbythePluginSlotdirectivetogatherplugincomponentstogetherthatarerelevantforcreationinthepluginslot.PluginSlot:ThisdirectiveisusedtomarkUIextensionpointsinourapplication.Whereverwe'dliketomakeitpossibleforpluginstohookintoourapplicationuserinterface,we'llplacethisdirective.Pluginslotsneedtobenamed,andpluginsuseplacementinformationtoreferenceslotsbytheirname.Thiswayaplugincanprovidedifferentcomponentsfordifferentslotsinourapplication.PluginComponent:TheseareregularAngularcomponentsthatcomebundledwithapluginimplementation.AplugincanprovidemultiplecomponentsconfiguredonthepluginusingaPluginPlacementobject.PluginPlacement:Thisisusedinthepluginconfigurationwhereaplugincanhavemultipleplacementconfigurations.Eachplacemententityconsistofareferencetoacomponent,thenameoftheslotwherethecomponentshouldbeinstantiated,andaprioritynumberthathelpsthepluginsystemtoorderplugincomponentscorrectlywhenmultiplecomponentsgetinstantiatedinthesameslot.Plugin:Thisistheactualpluginclasswhenimplementingaplugin.TheclasscontainsthepluginconfigurationannotatedusingthePluginConfigdecorator.ThepluginclassisinstantiatedonceintheapplicationandisalsosharedacrosstheplugincomponentsusingthedependencyinjectionofAngular.Therefore,thisclassisalsoagoodplacetosharedatabetweenplugincomponents.

Now,wehaveanoverviewofwhatwe'regoingtobuildonahigherlevel.Ourpluginsystemisveryrudimentary,butitwillsupportthingssuchashotloadingplugins(plugandplaystyle)andothernicefeatures.Inthenexttopic,we'llstartbyimplementingthepluginAPIcorecomponents.

www.EBooksWorld.ir

Page 333: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

ImplementingthepluginAPILet'sstartwiththelesscomplexentitiesofourpluginAPI.Wecreateanewlib/plugin/plugin.jsfiletocreatethePluginConfigdecoratorandthePluginPlacementclass,whichstorestheinformationwhereplugincomponentsshouldbeplaced.WealsocreatethePluginDataclassinthisfile,whichisusedtoinjectpluginruntimeinformationintoplugincomponents:

exportfunctionPluginConfig(config){

return(type)=>{

type._pluginConfig=config;

};

}

ThePluginConfigdecoratorcontainstheverysimplelogicofacceptingaconfigurationparameter,whichwillthenbestoredontheannotatedclass(theconstructorfunction)onthe_pluginConfigproperty.Ifyouneedarefresheronhowdecoratorswork,it'smaybeagoodtimetoreadthedecoratortopicinChapter1,Component-BasedUserInterfaces,again:

exportclassPluginPlacement{

constructor(options){

this.slot=options.slot;

this.priority=options.priority;

this.component=options.component;

}

}

ThePluginPlacementclassrepresentstheconfigurationobjecttoexposeplugincomponentsintodifferentpluginslotsintheapplicationUI:

exportclassPluginData{

constructor(plugin,placement){

this.plugin=plugin;

this.placement=placement;

}

}

ThePluginDataclassrepresentsthepluginruntimeinformationthatwascreatedduringinstantiationofthepluginaswellasonePluginPlacementobject.ThisclasswillbeusedbythePluginServicetoconveyinformationaboutplugincomponentstothepluginslotsintheapplication.

Thesethreeclassesarethemaininteractionpointswhenimplementingaplugin.

Let'slookatasimpleexampleplugin,togetapictureofhowwecanusethePluginConfigdecoratorandthePluginPlacementclasstoconfigureaplugin:

@PluginConfig({

name:'my-example-plugin',

description:'Asimpleexampleplugin',

www.EBooksWorld.ir

Page 334: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

placements:[

newPluginPlacement({

slot:'plugin-slot-1',

priority:1,

component:PluginComponent1

}),

newPluginPlacement({

slot:'plugin-slot-2',

priority:1,

component:PluginComponent2

})

]

})

exportdefaultclassExamplePlugin{}

UsingthePluginConfigdecorator,implementinganewpluginisabreeze.Wedecidethename,description,andwherewe'dliketoplaceplugincomponentsintheapplicationatdesigntime.

OurpluginsystemusesnamedPluginSlotdirectivestoindicateextensionpointsinourapplicationcomponenttree.InthePluginPlacementobjects,wereferencetheAngularcomponentsbuiltintothepluginandindicateinwhichslottheyshouldbeplacedbyreferencingthepluginslotname.Thepriorityoftheplacementwilltellthepluginslothowtoordertheplugincomponentwhencreated.Thisgetsimportantwhencomponentsofdifferentpluginsgetcreatedinthesamepluginslot.

Okay,let'sdiverightintothecoreofourpluginarchitecturebyimplementingthepluginservice.We'llcreateanewlib/plugin/plugin-service.jsfileandcreateanewPluginServiceclass:

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

import{ReplaySubject}from'rxjs/Rx';

@Injectable()

exportclassPluginService{

...

}

Aswewillcreateaninjectableservice,we'llannotateourPluginServiceclassusingthe@Injectableannotation.WeusetheRxJSReplaySubjecttypeinordertoemiteventsonanychangesoftheactivatedplugins.

Let'slookattheconstructorofourservice:

constructor(){

this.plugins=[];

//Changeobservableifthelistofactivepluginchanges

this.change=newReplaySubject(1);

this.loadPlugins();

}

www.EBooksWorld.ir

Page 335: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

First,weinitializeanewemptypluginsarray.Thiswillbethelistofactiveplugins,whichcontainsruntimeplugindatasuchastheURLwherethepluginwasloadedfrom,theplugintype(constructoroftheclass),ashortcuttotheconfigurationstoredontheplugin(createdbythePluginConfigdecorator)andfinally,theinstanceofthepluginclassitself.

WealsoaddachangememberthatweinitializewithanewRxJSReplaySubject.We'llusethissubjectinordertoemitthelistofactivepluginsonceitchanges.Thisallowsustobuildourpluginsysteminareactivewayandenableplugandplaystyleplugins.

Asalastactionintheconstructor,wecalltheloadPluginsmethodoftheservice.Thiswillperformtheinitialloadingwiththeregisteredplugins:

loadPlugins(){

System.import('/plugins.js').then((pluginsModule)=>{

pluginsModule.default.forEach((pluginUrl)=>

this.loadPlugin(pluginUrl)

);

});

}

TheloadPluginsmethodasynchronouslyloadsafilewiththenameplugins.jsfromtherootpathofourapplicationusingSystemJS.Theplugins.jsfileisexpectedtodefaultexportanarray,whichcontainspreconfiguredpathstopluginsthatshouldbeloadedwiththeapplicationstartup.Thisallowsustoconfigurethepluginsthatwe'realreadyawareofandwhichshouldbepresentbydefault.Usingaseparateandasynchronouslyloadedfileforthisconfigurationgivesusabetterseparationfromthemainapplication.Wecanrunthesameapplicationcodebutusingadifferentplugins.jsfileandcontrolwhatpluginsshouldbepresentbydefault.

TheloadPluginsmethodthenloadseachpluginusingtheURLpresentintheplugins.jsfilebycallingtheloadPluginmethod:

loadPlugin(url){

returnSystem.import(url).then((pluginModule)=>{

constPlugin=pluginModule.default;

constpluginData={

url,

type:Plugin,

//Readingthemetadatapreviouslystoredbythe@Plugin

//decorator

config:Plugin._pluginConfig,

//Createstheplugininstance

instance:newPlugin()

};

this.plugins=this.plugins.concat([pluginData]);

this.change.next(this.plugins);

});

}

TheloadPluginmethodisresponsiblefortheloadingandinstantiationofindividualplugin

www.EBooksWorld.ir

Page 336: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

modules.ItwilltaketheURLofapluginmoduleasparameterandusesSystem.importtodynamicallyloadthepluginmodule.ThebenefitswegetfromusingSystem.importforthisjobisthatwecanloadboth,alreadyexistingmodulesinthebundledapplicationaswellasremoteURL'susingHTTPrequests.Thismakesourpluginsystemveryportable,andwecanevenloadmodulesduringruntimefromadifferentserver,fromNPMorevenGitHub.Ofcourse,SystemJSalsosupportsdifferentmoduleformats,suchasES6modulesorCommonJSmodules,aswellasdifferenttranspilersifthemodulesarenotalreadytranspiled.

Afterthepluginmoduleissuccessfullyloaded,webundleallinformationabouttheloadedplugintogetherintoapluginDataobject.WecanthenaddthisinformationtoourpluginsarrayandemitaneweventonourReplaySubjecttonotifyinterestedpartiesaboutthechange.

Finally,we'llneedamethodtogatherthePluginPlacementdatafromallourpluginsandfilterthembyaslotname.Thisgetsimportantwhenourpluginslotsneedtoknowwhichcomponentstheyshouldinstantiate.Pluginscanexposeanynumberofcomponentsintoanynumberofapplicationpluginslots.ThisfunctionwillbeusedbyourpluginslotswhentheyneedtoknowwhichoftheexposedAngularcomponentsarerelevanttothem:

getPluginData(slot){

returnthis.plugins.reduce((components,pluginData)=>{

returncomponents.concat(

pluginData.config.placements

.filter((placement)=>placement.slot===slot)

.map((placement)=>newPluginData(pluginData,placement))

);

},[]);

ThisisalreadyitforthePluginServiceclasssofar,andwecreatedthecoreofourpluginsystem.Inthenextchapter,wewilldealwiththepluginslotsandlookathowwecaninstantiateplugincomponentsdynamically.

www.EBooksWorld.ir

Page 337: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

InstantiatingplugincomponentsNow,it'stimetolookatthesecondmajorpieceofourpluginarchitecture,whichisthePluginSlotdirectivethatisresponsiblefortheinstantiationofplugincomponentsintherightspots.

Beforewegettoimplementthedirectivethough,let'slookathowwecaninstantiateacomponentdynamicallyinAngular.WealreadycoveredinstantiatingviewsthatcancontaincomponentsinChapter7,ComponentsforUserExperience.Intheinfinitescrolldirective,weusedtheViewContainerReftoinstantiatetemplateelements.However,wehaveadifferentusecasehere.We'dliketoinstantiateasinglecomponentintoanexistingview.

TheViewContainerRefobjectalsoprovidesuswithasolutiontothisproblem.Let'slookataverybasicexampleonhowtousetheViewContainerRefobjecttoinstantiateacomponent.Inthefollowingexample,wemakeuseoffournewconcepts:

Using@ViewChildwithreadoptionssetto{read:ViewContainerRef}toqueryforviewcontainerinsteadoftheelementUsingtheComponentResolverinstancetoobtainthefactoryofthecomponent,whichwewanttoinstantiatedynamicallyUsingReflectiveInjectortocreateanewchildinjectorthatisusedforourinstantiatedcomponentUsingViewContainerRef.createComponenttoinstantiateacomponentandattachittotheunderlyingviewoftheviewcontainer.

ThefollowingcodeexampleshowshowwecandynamicallycreateacomponentusingtheViewContainerRefinstance.

import{Component,Inject,ViewChild,ViewContainerRef,ComponentResolver}from

'@angular/core';

@Component({

selector:'hello-world',

template:'HelloWorld'

})

exportclassHelloWorld{}

@Component({

selector:'app'

template:'<h1#headingRef>App</h1>'

})

exportclassApp{

@ViewChild('headingRef',{read:ViewContainerRef})viewContainer;

constructor(@Inject(ComponentResolver)resolver){

this.resolver=resolver;

}

ngAfterViewInit(){

this.resolver

www.EBooksWorld.ir

Page 338: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

.resolveComponent(HelloWorld)

.then((componentFactory)=>{

this.viewContainer.createComponent(componentFactory);

});

}

}

InjectedintotheconstructoroftheAppcomponent,wecanlateruseComponentResolvertoresolvetheHelloWorldcomponent.Weusethe@ViewChilddecoratortoqueryfortheheadingelementintheAppcomponent.Usually,thiswouldgiveustheElementRefobjectthatisassociatedwiththeviewelement.However,asweneedtheviewcontainerassociatedwiththeelement,wecanusethe{read:ViewContainerRef}optionstoobtaintheViewContainerRefobjectinstead.

IntheAfterViewInitlifecyclehook,wefirstcalltheresolveComponentmethodontheComponentResolverinstance.Thiscallreturnsapromise,whichresolvestoanobjectoftheComponentFactorytype.Angularusescomponentfactoriesinternallyinordertocreatecomponents.

Afterthepromisehasbeenresolved,wecannowusethecreateComponentmethodontheviewcontainerofourheadingelementtocreateourHelloWorldcomponent.

Let'slookatthecreateComponentmethodoftheViewContainerRefobjectinmoredetail:

Method Description

ViewContainerRef.createComponent

ThismethodwillcreateacomponentthatisbasedonthecomponentfactoryprovidedinthecomponentFactoryparameter.Thecompiledcomponentwillthenbeattachedtotheviewcontainerataspecificpositionprovidedbytheindexparameter.

Thefollowingaretheparameters:

componentFactory:Thisisthecomponentfactory,whichwillbeusedtocreateanewcomponent.Index:Thisistheoptionalparametertospecifythepositionintheviewcontaineratwhichthecreatedcomponentshouldbeinserted.Ifthisparameterisnotspecified,thecomponentwillbeinsertedatthelastpositionintheviewcontainer.Injector:Thisisanoptionalparameterthatallowsyoutospecifyacustominjectorforthecreatedcomponent.Thisallowsyoutoprovideadditionaldependenciesforthecreated

www.EBooksWorld.ir

Page 339: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

component.projectableNodes:Thisisanoptionalparametertospecifynodesforcontentprojection.

Thismethodreturnsapromisethatisresolvedwhentheinstantiatedcomponentiscompiled.ThePromiseresolvestoaComponentRefobject,whichcanalsobeusedtodestroythecomponentagainlateron.

Tip

Bydefault,acomponentcreatedwiththeViewContainerRef.createComponentmethodwillinherittheinjectorfromtheparentcomponent,whichmakesthisprocesscontextaware.However,theinjectorparameterofthecreateComponentmethodisespeciallyusefulwhenyouwanttoprovideadditionaldependenciesintothecomponentthatarenotpresentonanyparentinjector.

Let'sgobacktoourPluginSlotdirectivethatisresponsiblefortheinstantiationofrelevantplugincomponents.

First,let'sthinkaboutthehigh-levelrequirementsofourPluginSlotdirectivebeforewediveintothecode:

Thepluginslotshouldcontainanameinputpropertysothatthisnamecanbereferencedfrompluginsthatwanttoprovidecomponentsfortheslot.ThedirectiveneedstoreactonchangesofthePluginServiceandre-evaluatewhatplugincomponentsneedtobeplaced.Intheinitializationofthepluginslot,weneedtoobtainalistofthePluginDataobjectsthatarerelevanttothisparticularslot.WeshouldconsultthegetPluginDatamethodofPluginServiceinordertogetthislist.UsingtheobtainedlistoftherelevantPluginDataobjects,we'llbeabletoinstantiatecomponentsthatareassociatedwiththeplacementinformationusingtheViewContainerRefobjectofourdirective.

Let'screateourPluginSlotdirectiveonthelib/plugin/plugin-slot.jspath:

import{Directive,Input,Inject,provide,ViewContainerRef,

ComponentResolver,ReflectiveInjector}from'@angular/core';

import{PluginData}from'./plugin';

import{PluginService}from'./plugin-service';

@Directive({

selector:'ngc-plugin-slot'

})

exportclassPluginSlot{

@Input()name;

...

}

www.EBooksWorld.ir

Page 340: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Thenameinputinourdirectiveisveryimportantforourpluginmechanism.Byprovidinganametothedirective,wecandefinenamedextensionpointsinourUIandlaterusethisnameinthePluginPlacementdataofthepluginconfigurations:

constructor(@Inject(ViewContainerRef)viewContainerRef,

@Inject(ComponentResolver)componentResolver,

@Inject(PluginService)pluginService){

this.viewContainerRef=viewContainerRef;

this.componentResolver=componentResolver;

this.pluginService=pluginService;

this.componentRefs=[];

//Subscribingtochangesonthepluginserviceandre-

//initializeslotifneeded

this.pluginChangeSubscription=

this.pluginService

.change.subscribe(()=>this.initialize());

}

Intheconstructor,wefirstinjecttheViewContainerRefobject,whichisareferencetotheviewcontainerofthedirective.Aswewanttousetheviewcontainerofthedirectivedirectly,there'snoneedtouse@ViewChildhere.Ifwewanttheviewcontainerofthecurrentdirective,wecansimplyuseinjection.We'llusethisreferencewhilewe'reinstantiatingcomponentsusingtheViewContainerRef.createComponentmethod.

Inordertoresolvecomponentsandtheirfactory,weinjecttheComponentResolverinstance.

ThePluginServiceisinjectedfortworeasons.First,we'dliketosubscribetoanychangesonthelistofactiveplugins,andsecondly,weuseittoobtainrelevantPluginDataobjectsforthisslot.

WeusethecomponentRefsmembertokeeptrackofalreadyinstantiatedplugincomponents.Thiswillhelpusdestroythemlateronwhenaplugingetsdeactivated.

Finally,wecreateanewsubscriptiontoPluginServiceandstorethesubscriptionintothepluginChangeSubscriptionmemberfield.Onanychangesoftheactivatedpluginlist,weexecutetheinitializemethodonourcomponent:

initialize(){

if(this.componentRefs.length>0){

this.componentRefs.forEach(

(componentRef)=>componentRef.destroy());

this.componentRefs=[];

}

constpluginData=

this.pluginService.getPluginData(this.name);

pluginData.sort(

(a,b)=>a.placement.priority<b.placement.priority?

1:a.placement.priority>b.placement.priority?-1:0);

www.EBooksWorld.ir

Page 341: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

returnPromise.all(

pluginData.map((pluginData)=>

this.instantiatePluginComponent(pluginData))

);

}

Let'slookatthefourpartsoftheinitializemethodindetail:

First,wecheckwhetherthispluginslotalreadycontainsinstantiatedplugincomponentsinthecomponentRefsmember.Ifthisisthecase,weusethedetachmethodoftheComponentRefobjectstoremoveallexistinginstances.Afterthis,weinitializethecomponentRefsmemberwithanemptyarray.WeusethegetPluginDatamethodofPluginServicetoobtainalistofthePluginDataobjectsthatarerelevantforthisparticularslot.Wepassthenameofthisslottothemethod,sothePluginServicewillalreadyprovideuswithafilteredlistofplugincomponentsthatareinterestedtobeplacedinourslot.Astherecouldbemanypluginsqueueingforplacementinourslot,weareusingtheprioritypropertyofthePluginPlacementobjectstosortthelistofthePluginDataobjects.Thiswillensurethatplugincomponentswithhigherprioritywillbeplacedbeforetheoneswithalowerpriority.Thisisaniceextrafeaturethatwillcomeinhandywhenwedealwithalotofpluginsfightingforspace.ThelastcodepartinourinitializemethodcallstheinstantiatePluginComponentmethodforeachPluginDataobjectinourlist.

Now,let'screatetheinstantiatePluginComponentmethod,whichiscalledasalaststepintheinitializemethod:

instantiatePluginComponent(pluginData){

returnthis.componentResolver

.resolveComponent(pluginData.placement.component)

.then((componentFactory)=>{

//Gettheinjectorofthepluginslotparentcomponent

constcontextInjector=this.viewContainerRef.parentInjector;

//PreparingadditionalPluginDataproviderforthecreated

//plugincomponent

constproviders=[

provide(PluginData,{

useValue:pluginData

})

];

//We'recreatinganewchildinjectorandprovidethe

//PluginDataprovider

constchildInjector=ReflectiveInjector

.resolveAndCreate(providers,contextInjector);

//Nowwecancreateanewcomponentusingthepluginslotview

//containerandtheresolvedcomponentfactory

constcomponentRef=this.viewContainerRef

.createComponent(componentFactory,

this.viewContainerRef.length,

childInjector);

this.componentRefs.push(componentRef);

});

www.EBooksWorld.ir

Page 342: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

}

Thismethodisresponsiblefortheinstantiationofanindividualplugincomponent.Now,wecanusetheknowledgethatwegainedinthistopicabouttheViewContainerRef.createComponentmethodandtheComponentResolverobjecttoinstantiatecomponentsdynamically.

Inadditiontotheinheritedprovidersfromthecomponentwherethispluginslotisplaced,we'dliketoprovidePluginDatatotheinjectoroftheinstantiatedplugincomponent.UsingAngular'sprovidefunction,wecanspecifypluginDatatoresolveforanyinjectiononthePluginDatatype.

TheReflectiveInjectorclassprovidesuswithsomestaticmethodsthatareusedtocreateinjectors.WecanusetheparentInjectormemberonourviewcontainertoobtaintheinjectorthatispresentinthepluginslotcontext.Then,weusethestaticresolveAndCreatemethodontheReflectiveInjectorclassinordertocreateanewchildinjector.

InthefirstparameteroftheresolveAndCreatemethod,wecanprovidealistofproviders.Thoseproviderswillberesolvedandmadeavailableinournewchildinjector.ThesecondparameteroftheresolveAndCreatemethodacceptstheparentinjectorofthenewly-createdchildinjector.

Finally,weusethecreateComponentmethodoftheViewContainerRefobjecttoinstantiatetheplugincomponent.AsasecondparametertothecreateComponentmethodcall,weneedtopassthepositionintheviewcontainer.Here,wemakeuseofthelengthpropertyofourviewcontainerinordertoplaceitattheveryend.Inthethirdparameter,weoverridethedefaultinjectorofthecomponentwithourcustomchildinjector.Onsuccess,weaddthecreatedComponentRefobjecttoourlistofinstantiatedcomponents.

www.EBooksWorld.ir

Page 343: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

FinalizingourpluginarchitectureCongratulations,you'vejustbuiltyourownpluginarchitectureusingAngular!WecreatedapluginAPIthatcanbeusedtocreatenewpluginsusingthePluginConfigdecorator.PluginServicemanagesthewholepluginloadingandprovidesthePluginDataobjectstotheslotsinourapplicationusingcustominjectors.ThePluginSlotdirectivecanbeusedinthetaskmanagementapplicationtomarkextensionpointsintheuserinterface.UsingtheinheritingnatureofthedependencyinjectioninAngular,plugincomponentswillbeabletoaccesswhatevertheyrequirefromtheirenvironment.

Inthenextsection,wewillcreateourfirstpluginusingthepluginarchitecturethatwejustcreated.

www.EBooksWorld.ir

Page 344: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

BuildinganAgilepluginIntheprevioussection,wecreatedasimplebuteffectivepluginarchitecture,andwewillnowusethispluginAPItobuildourfirstplugininthetaskmanagementapplication.

Beforewegetintotheplugindetails,weshouldfirstagreeonwheretomakeourapplicationextensible.OurpluginsystemisbasedonthePluginSlotdirectives,whichshouldbeplacedsomewhereinourcomponenttreesothatpluginscanexposecomponentstotheseslots.Fornow,wedecidetomaketwospotsinourapplicationextensible:

TaskInfo:Inthelistoftasksdisplayedinaproject,wecurrentlyrenderTaskcomponents.Besidesthetitleofthetask,theTaskcomponentdisplaysadditionalinformationsuchasthetasknumber,thedateofcreation,andmilestones,aswellaseffortsinformationwhereapplicable.ThisadditionalinformationisrenderedontheTaskcomponentusingtheTaskInfossubcomponent.Thisisagoodspottoprovideextensibilityforpluginssothattheycanaddadditionaltaskinformation,whichwillbedisplayedonthetasklistoverview.TaskDetail:AnothergreatspottoprovideextensibilityistheProjectTaskDetailscomponent.Thisiswherewecaneditthedetailsofatask,whichmakesitagreatcomponenttoopenupforextensionbyplugins.

BesidesaddingthePluginSlotdirectivetothedirectiveslistoftheTaskInfoscomponent,wemodifythetemplatelocatedatlib/task-list/task/task-infos/task-infos.html:

...

<ngc-task-infotitle="Efforts"

[info]="task.efforts|formatEfforts">

</ngc-task-info>

<ngc-plugin-slotname="task-info"></ngc-plugin-slot>

AfterincludingthePluginSlotdirectiveandbysettingthenameinputpropertytotask-info,weprovideanextensionpointforpluginswheretheycanprovideadditionalcomponents.

Let'sapplythesamechangestotheProjectTaskDetailscomponenttemplateinlib/project/project-task-details/project-task-details.html:

...

<divclass="task-details__content">

...

<ngc-plugin-slotname="task-detail"></ngc-plugin-slot>

</div>

Rightbeforetheendofthetaskdetailscontentelement,weincludeanotherpluginslotwiththenametask-detail.Byprovidingcomponentsforthisslot,pluginscanhookintotheeditviewoftasks.

Okay,soourextensionpointsaresetupforpluginstoprovideadditionalcomponentsona

www.EBooksWorld.ir

Page 345: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

tasklevel.YoucanseethatpreparingthesespotsusingthePluginSlotdirectiveisreallyapieceofcake.

Now,wecanlookintotheimplementationofourAgileplugin,whichwillmakeuseoftheextensionpointsthatwejustexposed.

Theagilepluginthatwewillcreatewillprovidefunctionalitytologstorypointsontasks.StorypointsarecommonlyusedinAgileprojectmanagement.Theyshouldprovideasenseforcomplexity,andtheyarerelativetoaso-calledreferencestory.IfyouwanttoknowmoreaboutAgileprojectmanagementandhowtoestimateusingstorypoints,Ireallyrecommendthebook,AgileEstimatingandPlanningbyMikeCohn.

Let'sstartwithourpluginclassandthenecessaryconfiguration.Wecreatethepluginoutsideourregularlibfolder,justtoindicatetheportablenatureofplugins.

WecreateanewAgilePluginclassontheplugins/agile/agile.jspath:

import{PluginConfig,PluginPlacement}from'../../lib/plugin/plugin';

@PluginConfig({

name:'agile',

description:'Agiledevelopmentplugintomanagestorypointsontasks',

placements:[]

})

exportdefaultclassAgilePlugin{

constructor(){

this.storyPoints=[0.5,1,2,3,5,8,13,21];

}

}

Thepluginclassformsthecentralentrypointofourplugin.WeusethePluginConfigdecorator,whichwecreatedaspartofourpluginAPI.Besidesthenameanddescription,wealsoneedtoconfigureanyplacementswherewemapplugincomponentstoapplicationpluginslots.However,aswehaven'tgotanyplugincomponentyettoexpose,ourlistremainsemptyforthemoment.

It'salsoimportanttonotethatapluginmodulealwaysneedstodefaultexportthepluginclass.Thisisjusthowwe'veimplementedtheplugin-loadingmechanismsinourPluginServiceclass.

LookingbackatthesetwolinesintheloadPluginmethodofPluginServiceshowsyouthatwerelyonthedefaultexportofpluginmodules:

returnSystem.import(url).then((pluginModule)=>{

constPlugin=pluginModule.default;

...

Whenthepluginmoduleissuccessfullyloaded,weobtainthedefaultexportbyreferencingthedefaultpropertyonthemodule.

www.EBooksWorld.ir

Page 346: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Sofar,wecreatedourpluginentrymodule.Thisactsasapluginconfigurationcontainer,anditisnotrelatedtoAngularinanyway.Usingtheplacementsconfiguration,wecanthenexposeourpluginAngularcomponentsoncewe'vecreatedthem.

www.EBooksWorld.ir

Page 347: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

AgiletaskinfocomponentLet'smoveontothefirstAgileplugincomponentthatwewanttoexpose.First,wecreatethecomponent,whichwillbeexposedintotheslotwiththenametask-info.Belowthetasktitleonthetasklist,ourAgileinformationcomponentshoulddisplaythestoredstorypoints.

WecreateanewComponentclassontheplugins/agile/agile-task-info/agile-task-info.jspath:

...

import{Task}from'../../../lib/task-list/task/task';

@Component({

selector:'ngc-agile-task-info',

encapsulation:ViewEncapsulation.None,

template,

host:{

class:'task-infos__info'

}

})

exportclassAgileTaskInfo{

constructor(@Inject(Task)taskComponent){

this.task=taskComponent.task;

}

}

Youcanseethatweimplementedaregularcomponenthere.There'snothingspecialaboutthiscomponentatall.

WeimporttheTaskcomponenttogetthetypeinformationtoinjectitinourconstructor.AsthepluginslotisplacedinsideoftheTaskInfoscomponent,which,infact,isalwaysachildofaTaskcomponent,thisisasafeinjection.

Intheconstructor,wethentaketheinjectedTaskcomponentandextractthetaskdataintoalocaltaskmember.

Wealsoborrowthetask-infos__infoclassoftheTaskInfoscomponentinordertogetthesamelooklikeothertaskinformationthatisalreadypresentonthetask.

Let'stakealookatthetemplateoftheAgileTaskInfocomponentthatislocatedinthesamepathintheagile-task-info.htmlfile:

<div*ngIf="task.storyPoints||task.storyPoints===0">

<strong>StoryPoints:</strong>{{task.storyPoints}}

</div>

Followingthesamemark-upthatweusedintheTaskInfocomponent,wedisplaythestoryPointstasksifpresent.

Alright,nowwecanexposetheplugincomponentinthepluginconfigurationusinga

www.EBooksWorld.ir

Page 348: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

PluginPlacementobject.Let'smakethenecessarymodificationtoouragile.jsmodulefile:

...

import{AgileTaskInfo}from'./agile-task-info/agile-task-info';

@PluginConfig({

name:'agile',

description:'Agiledevelopmentplugintomanagestorypointsontasks',

placements:[

newPluginPlacement({slot:'task-info',priority:1,

component:AgileTaskInfo})

]

})

exportdefaultclassAgilePlugin{

...

}

Now,weincludeanewPluginPlacementobjectinourpluginconfiguration,whichmapsourAgileTaskInfocomponenttobeexposedintotheapplicationpluginslotwiththenametask-info:

TaskinfodisplayingadditionalinformationthatisprovidedbyourAgileplugin

Thiswouldalreadybeenoughfortheplugintowork.However,aswedon'thaveanydatafilledasstoryPointsonourtasks,thispluginwouldn'treallyshowusanythingatthemoment.

www.EBooksWorld.ir

Page 349: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

AgiletaskdetailscomponentLet'screateanotherplugincomponent,whichcanbeusedtoenterstorypoints.Forthis,wewillcreateanewAgileTaskDetailcomponentontheplugins/agile/agile-task-detail/agile-task-detail.jspath:

...

import{Project}from'../../../lib/project/project';

import{ProjectTaskDetails}from'../../../lib/project/project-task-

details/project-task-details';

import{Editor}from'../../../lib/ui/editor/editor';

@Component({

selector:'ngc-agile-task-detail',

encapsulation:ViewEncapsulation.None,

template,

host:{class:'agile-task-detail'},

directives:[Editor]

})

exportclassAgileTaskDetail{

constructor(@Inject(Project)project,

@Inject(ProjectTaskDetails)projectTaskDetails){

this.project=project;

this.projectTaskDetails=projectTaskDetails;

this.plugin=placementData.plugin.instance;

}

onStoryPointsSaved(storyPoints){

this.projectTaskDetails.task.storyPoints=+storyPoints||0;

this.project.document.persist();

}

}

There'snothingreallyfancywiththiscomponenteither.Ourtargetslotisthetask-detailpluginslot,whichisplacedinsidetheProjectTaskDetailscomponent.Therefore,it'ssafeforboththeProjectTaskDetailsandProjectcomponentstobeinjectedintoourplugincomponent.TheProjectTaskDetailscomponentisusedtoobtainthetaskdataincontext.WeuseLiveDocumentthatisstoredontheProjectcomponenttopersistanychangesthatwemaketothetaskdataoftheproject.

WereuseanEditorcomponenttoobtainuserinputandstoretheinputdataintheonStoryPointsSavedcall-back.ThisisthesamemechanismweknowfromotherareaswhereweusetheEditorcomponent.Whenthestorypointsgetedited,wefirstupdatethetaskdatamodelthatisstoredintheProjectTaskDetailscomponent.Afterthis,wecanusetheLiveDocumentpersistmethodtosavethechanges.

Let'slookatthetemplateofourAgileTaskDetailcomponentintheplugins/agile/agile-task-detail/agile-task-detail.htmlfile:

<divclass="task-details__label">StoryPoints</div>

<ngc-editor[content]="projectTaskDetails.task?.storyPoints"

www.EBooksWorld.ir

Page 350: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

[showControls]="true"

(editSaved)="onStoryPointsSaved($event)"></ngc-editor>

WecreateadirectbindingfromthecontentinputpropertyofoureditortothestoryPointspropertyofthetaskdata.

Whenaneditissaved,wecalltheonStoryPointsSavedcallbackwiththeupdatedvalue:

TaskdetailsdisplayingthenewAgilestorypointsthatareexposedbyourAgileplugin

Beforeweexposeournewly-createdplugincomponentusinganewPluginPlacementobjectonthepluginconfiguration,we'llenhancethecomponentonemoretime.Thiswouldbeniceifweprovidetwobuttonsonthecomponentthatallowstheusertoincreaseordecreasethestorypointstothenextcommonstorypointvalueinrange.AswealreadystoredthelistofcommonstorypointsontheAgilepluginclass,let'sseehowwecanmakeuseofthis:

...

import{PluginData}from'../../../lib/plugin/plugin';

@Component({

selector:'ngc-agile-task-detail',

...

})

exportclassAgileTaskDetail{

constructor(...,@Inject(PluginData)pluginData){

...

this.plugin=pluginData.plugin.instance;

}

...

increaseStoryPoints(){

constcurrent=this.projectTaskDetails.task.storyPoints||0;

conststoryPoints=this.plugin.storyPoints.slice().sort((a,b)=>a>b?1

:a<b?-1:0);

www.EBooksWorld.ir

Page 351: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

this.projectTaskDetails.task.storyPoints=

storyPoints.find((storyPoints)=>storyPoints>current)||current;

this.project.document.persist();

}

decreaseStoryPoints(){

constcurrent=this.projectTaskDetails.task.storyPoints||0;

conststoryPoints=this.plugin.storyPoints.slice().sort((a,b)=>a<b?1

:a>b?-1:0);

this.projectTaskDetails.task.storyPoints=

storyPoints.find((storyPoints)=>storyPoints<current)||current;

this.project.document.persist();

}

}

WhilewepreviouslyinjectedtheProjectandProjectTaskDetailscomponentsthatareprovidedbythecomponentlevelinjectors,wenowmakeuseoftheprovidersthatweaddedduringinstantiationinourPluginSlotdirective.Here,weprovidedPluginData,whichwecannowusetogetareferencebacktotheplugincomponent.

ThenexthigherorlowerstorypointvalueisfoundbyincreaseStoryPointsanddecreaseStoryPoints.ThisisdonebysearchingthelistofcommonstorypointsthatarestoredonourAgilePluginclass.UsingthepluginclassinstancethatispresentontheinjectedPluginData,wecaneasilyaccessthislist.Afterstoringthemodifiedstorypoints,wethenusetheLiveDocumentinstanceoftheprojectcomponenttopersisttheadjustedstorypoints.

InthetemplateofourAgileTaskDetailcomponent,wesimplyaddtwobuttonsthatallowtheusertoincreaseordecreasethestorypointsthatarebasedonournewly-createdmethods:

...

<button(click)="decreaseStoryPoints()"

class="buttonbutton--small">-</button>

<button(click)="increaseStoryPoints()"

class="buttonbutton--small">+</button>

Okay,let'snowaddtheAgileTaskDetailcomponenttothepluginconfigurationusinganewPluginPlacementobject,whichreferencesthetask-detailpluginslot:

...

import{AgileTaskDetail}from'./agile-task-detail/agile-task-detail';

@PluginConfig({

...

placements:[

newPluginPlacement({slot:'task-info',priority:1,

component:AgileTaskInfo}),

newPluginPlacement({slot:'task-detail',priority:1,

component:AgileTaskDetail})

]

})

exportdefaultclassAgilePlugin{

...

}

www.EBooksWorld.ir

Page 352: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Isn'tthisgreat?Youcreatedafully-portablepluginthatenablesthemanagementofAgilestorypointsontasks.

Thetaskdetailsviewwithstorypointsandadditionalincrease/decreasebuttons

TheonlythingthatisleftistoaddtheplugintothelistofpluginsthatshouldbeloadedinitiallybythePluginServicedirective.Forthis,we'llcreateaplugins.jsfileontherootofourapplicationandaddthefollowingcontent:

exportdefault[

'/plugins/agile/agile.js'

];

Now,ifwelaunchourapplication,thepluginwillbeloadedbythePluginServiceandPluginSlotdirectiveswillinstantiatetheAgileplugincomponentswhereappropriate.

www.EBooksWorld.ir

Page 353: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

RecapitulatingonourfirstpluginWelldone!Yousuccessfullyimplementedyourfirstplugin!Inthissection,weusedtheAPIofourpluginarchitecturetocreateaplugintomanageAgilestorypoints.WeusedthePluginPlacementclasstomapourplugincomponentstodifferentslotsintheUIofourapplication.WealsomadeuseofthePluginDataobjectthatisprovidedtoeachinstantiatedcomponentinthepluginslotinordertoaccesstheplugininstance.

Theadvantageofimplementingfunctionalitylikethisinsideapluginshouldbeobvious.Weaddedanadditionalfeaturetoourapplicationwithoutbuildingupadditionaldependencies.OurAgilefeatureiscompletelyportable.Third-partydeveloperscanwriteindependentplugins,andtheycanbeloadedbyoursystem.Thisisabigadvantage,andithelpsuskeepourcoreslimwhileprovidinggreatextensibility.

www.EBooksWorld.ir

Page 354: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

ManagingpluginsWealreadybuiltthecoreofthepluginarchitectureandafirstpluginthatrunsinthissystem.Wecanusetheplugins.jsfileontherootofourapplicationtoregisterplugins.Thesystemisactuallyfullyfunctionalalready.However,itwouldbenicetoprovideawaytomanageourpluginsduringruntime.

Inthissection,wewillbuildanewroutablecomponent,whichwilllistallactivepluginsinthesystem.Afterwe'vedonethis,we'llalsoaddsomeelementswhichallowuserstounloadactivepluginsaswellasloadnewpluginsduringruntime.Duetothereactivenatureofourpluginsystem,thebrowserdoesnotneedtoberefreshedinorderfornewly-loadedpluginstobecomeactive.Themomentapluginisloaded,itwillimmediatelybemadeavailabletorelevantpluginslots.

Let'sstartwithanewManagePluginscomponentclassonthelib/manage-plugins/manage-plugins.jspath:

...

import{PluginService}from'../plugin/plugin-service';

@Component({

selector:'ngc-manage-plugins',

...

})

exportclassManagePlugins{

constructor(@Inject(PluginService)pluginService){

this.plugins=pluginService.change;

}

}

OurManagePluginscomponentisquitesimple.WeinjectthePluginServiceintotheconstructorofthecomponentandsetthememberfieldpluginstopointtothechangeobservableofPluginService.Aswe'llalwaysgetthelatestpluginlistemittedbythisobservable,wecanthensimplyusetheasyncpipeintheviewtosubscribetotheobservable.

Let'slookatthetemplateofournewcomponentinlib/manage-plugins/manage-plugins.html:

<divclass="manage-plugins__l-header">

<h2class="manage-plugins__title">ManagePlugins</h2>

</div>

<divclass="manage-plugins__l-main">

<h3class="manage-plugins__sub-title">ActivePlugins</h3>

<divclass="manage-plugins__section">

<tableclass="manage-plugins__table">

<thead>

<tr>

<th>Name</th>

<th>Url</th>

<th>Description</th>

www.EBooksWorld.ir

Page 355: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

<th>Placements</th>

</tr>

</thead>

<tbody>

<tr*ngFor="letpluginofplugins|async">

<td>{{plugin.config.name}}</td>

<td>{{plugin.url}}</td>

<td>{{plugin.config.description}}</td>

<td>

<div*ngFor="letplacementofplugin.config.placements"

class="manage-plugins__placement">

{{placement.slot}}

</div>

</td>

</tr>

</tbody>

</table>

</div>

</div>

WeuseanHTMLtabletodisplaythelistofactiveplugins.Onthetablebodyrows,weusetheNgFordirectivetoiterateoverthelistofactiveplugins,whichwesubscribetousingtheasyncpipe.

Inthepluginobjects,wegoteverythingworthdisplayingalreadypresent.ByiteratingoverthePluginPlacementobjectsthatarestoredontheconfigpropertyontheplugindata,wecanevendisplaytheslotnameswhereourpluginsprovidecomponents.

Now,theonlythinglefttodotoenableournewcomponentistomakeitroutableandtoaddittothenavigationofourapplication.Let'smakethenecessarymodificationinthelib/app.jsmoduleforthis:

...

import{ManagePlugins}from'./manage-plugins/manage-plugins';

@Component({

selector:'ngc-app',

...

})

@Routes([

...

newRoute({path:'plugins',component:ManagePlugins})

])

exportclassApp{

...

}

Weaddedanewroute,solet'saddittoournavigationinlib/app.html:

<divclass="app">

<divclass="app__l-side">

<ngc-navigation[openTasksCount]="openTaskCount">

...

www.EBooksWorld.ir

Page 356: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

<ngc-navigation-sectiontitle="Admin">

<ngc-navigation-itemtitle="ManagePlugins"

[link]="['/plugins']">

</ngc-navigation-item>

</ngc-navigation-section>

</ngc-navigation>

</div>

<divclass="app__l-main">

<router-outlet></router-outlet>

</div>

</div>

InanewAdminnavigationsection,weaddanewnavigation-itemthatlinkstothenewly-created"plugins"route:

OurnewManagePluginscomponentdisplayingatableofactivepluginsandtheirexposedplacements

www.EBooksWorld.ir

Page 357: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

LoadingnewpluginsatruntimeWealreadyhaveeverythinginplacetoprovideapagetoseeallactiveplugins.However,wesaidthatitwouldbenicetobeabletomanagethislist.Ausershouldbeabletoremoveactivepluginsaswellasmanuallyloadadditionalplugins.

Let'saddthesecapabilitiestoourManagePluginscomponent.Beforewecandothis,we'llneedanadditionalmethodonourPluginServiceclass,whichisthepartresponsiblefortheloadingofplugins.Sofar,wedidn'tconsiderthefunctionalitytoremoveactiveplugins.Let'sopenPluginServiceinlib/plugin/plugin-service.jstoaddthisfunctionality:

...

@Injectable()

exportclassPluginService{

...

removePlugin(name){

constplugin=this.plugins.find(

(plugin)=>plugin.name===name);

if(plugin){

constplugins=this.plugins.slice();

plugins.splice(plugins.indexOf(plugin),1);

this.plugins=plugins;

this.change.next(this.plugins);

}

}

}

Well,thiswaseasy!WeprovideanewremovePluginmethod,whichtakesapluginnameasparameter.Wethenlookuptheplugininthepluginsarray,andifapluginwasfoundwiththisname,weremoveitfromthelist.Additionally,afterwe'veremovedtheplugin,weemitachangeeventwiththeupdatedlist.Asallpluginslotsintheapplicationaresubscribedtothischangeobservable,theywillupdateandreinitializerelevantplugincomponentsautomatically.

Let'snowapplythenecessarychangestoourManagePluginscomponentclassinordertonotonlyremovepluginsbutalsotoloadadditionalplugins:

...

@Component({

selector:'ngc-manage-plugins',

...

})

exportclassManagePlugins{

constructor(@Inject(PluginService)pluginService){

...

this.pluginService=pluginService;

}

removePlugin(name){

this.pluginService.removePlugin(name);

}

www.EBooksWorld.ir

Page 358: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

loadPlugin(loadUrlInput){

this.pluginService.loadPlugin(loadUrlInput.value);

loadUrlInput.value='';

}

}

Now,wealsostorethePluginServiceonourcomponent.IntheremovePluginandtheloadPluginfunctions,wedelegatetothePluginServicetotakethenecessaryactions.

TheloadPluginmethodwillreceiveanElementRefobjectthatpointstotheinputfield,wheretheuserenterstheURLfromwhichweloadanewplugin.WecanpassthevalueoftheinputfieldtotheloadPluginmethodofPluginService,whichdealswiththerest.Wealsosettheinputfieldvaluetoanemptystringoncewe'vesubmittedthiscall.

Let'sopenthetemplateatlib/manage-plugins/manage-plugins.htmltoapplytherequiredchangesintheviewofourcomponent:

...

<divclass="manage-plugins__l-main">

<h3class="manage-plugins__sub-title">ActivePlugins</h3>

<divclass="manage-plugins__section">

...

<td>

<button(click)="removePlugin(plugin.name)"

class="buttonbutton--small">remove</button>

</td>

...

</div>

<h3class="manage-plugins__sub-title">LoadPlugin</h3>

<divclass="manage-plugins__section">

<divclass="manage-plugins__load-elements">

<input#loadUrlReftype="text"

placeholder="EnterpluginURL"

class="manage-plugins__load-url">

<button(click)="loadPlugin(loadUrlRef)"

class="button">Load</button>

</div>

</div>

</div>

Weaddedanadditionalbuttonforeverylistedplugininthetable,whichcontainsabindingexpressionthatcallstheremovePluginmethodwiththecurrentlyiteratedpluginname.

Wealsoaddedanewsectionafterthelistedpluginstoloadnewplugins.Inthissection,weuseaninputfieldtoenterthepluginURLaswellasabuttontoexecutetheloading.UsingaloadUrlReflocalviewreference,wecanpassareferencetotheinputDOMelementtotheloadPluginmethodonourcomponent:

www.EBooksWorld.ir

Page 359: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

AcompletedManagePluginscomponentwiththeabilitytoremoveandloadpluginmodulesatruntime

Now,wehaveeverythinginplacetomanageourplugins.PluginsinitiallyloadedfromURLspresentintherootplugins.jsfilecannowbeunloadedusingtheREMOVEbuttoninthepluginslisting.NewpluginscanbeloadedandactivatedbyenteringtheURLofaplugin,whichcouldbealocalURL,bundledandmappedmodule,orevenaremoteURLonadifferentserver.

www.EBooksWorld.ir

Page 360: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

SummaryInthischapter,welookedatdifferentapproachesonhowtoimplementapluginarchitecture.WethencreatedourowndesignforapluginarchitecturethatleveragessomeAngularmechanismsandworksontheconceptofUIextensionpointsthatwecallslots.

WeimplementedapluginAPIthatprovidesgreatdeveloperexperiencebyleveragingES7decoratorstomaketheconfigurationofnewpluginsapieceofcake.WeimplementedthecoreofourpluginsystemusingaservicetoloadandunloadpluginsthatarebasedontheSystemJSmoduleloader.ThisallowedustomakeuseoftheadvancedloadingpossibilitiesthatareprovidedbySystemJS.Pluginscanbetranspiledinrealtime,canbelocatedonalocalURL,remoteURL,orevenbebundledintothemainapplication.

Weimplementedourfirstplugin,whichprovidessomecomponentstomanageAgilestorypointsonourtasks.Thepluginwascreatedoutsideourregularprojectlibfolder,whichshouldunderlinetheportablenatureofourpluginsystem.

Finally,wecreatedanewroutablecomponenttomanagepluginsatruntime.Duetothereactivenatureofourpluginsystem,pluginscanbeloadedandunloadedduringapplicationruntimewithoutanyunwantedside-effects.

Whenyou'replayingwiththesourcecodeofthischapter,Ihighlyrecommendthatyouplaywiththeloadingmechanismofourpluginarchitecture.Theflexibilitythatweachievedwithverylittleeffortisfantastic.YoucanunloadtheAgilepluginandloaditagainbyprovidingtheURLtothepluginmainmodule.Youcaneventrytoplacethewholepluginsfolderontoaremoteserverandloadthepluginfromthere.JustmakesurethatyouconsiderthenecessaryCross-OriginResourceSharing(CORS)headers.

ThewholecodeforthischaptercanbefoundintheZIPfileofthebookresourcesthatyoucandownloadfromPacktPublishing.YoucanrefertotheDownloadingtheexamplecodesectioninthePrefaceofthebook.

Inthenextandlastchapterofthisbook,we'lllookathowwecantestthecomponentsthatwe'vecreatedsofar.So,staytunedforthisoverduetopic!

www.EBooksWorld.ir

Page 361: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Chapter11.PuttingThingstotheTestWritingtestsiscrucialforthemaintainabilityofyourcode.It'saknownfactthathavingagoodrangeofteststhatcovermostofyourfunctionalityisequallyimportantasthefunctionalityitself.

Thefirstthingthatcomestomindwhenthinkingabouttestsisprobablycodequalityassurance.Youtestthecodethatyouwrite,sothisisdefinitelytrue.However,therearemanyotherimportantaspectsofwritingtests:

Resistancetounexpectedchange:Yourtestsdefinewhatyourcodeissupposedtodo.Theytestwhetheryourcodeconformstoyourspecifications.Thishasseveralbenefits,wherethemostobviousisprobablyaresistancetounexpectedchangeinthefuture.Ifyoumodifythecodeinthefuture,you'lllesslikelybreakyourexistingcodebecauseyourtestswillvalidatewhethertheexistingfunctionalitystillworksasspecified.Documentation:Yourtestsdefinewhatyourcodeshoulddo.Atthesametime,theydisplaytheAPIcallsthatarerequiredtousetheconcernedfunctionality.Thisistheperfectdocumentationforanydeveloper.WheneverIwanttounderstandhowalibraryreallyworks,thetestsarethefirstthingthatIlookat.Avoidingunnecessarycode:Thepracticeofwritingtestsforcesyoutolimityourcodetofulfiltherequirementsofyourspecificationandnothingmore.Anycodeinyourapplicationthatisnotreachedinyourautomatedtestscanbeconsidereddeadcode.Ifyousticktoamercilessrefactoringapproach,you'dthenremovesuchunusedcodeASAP.

Sofar,wehaven'tconsideredtestinginourbookatall,andgivenitsimportance,youmaywonderwhyIcomeupwiththisnowinthelastchapter.Inarealproject,we'ddefinitelycreatetestsmuchearlierifnotatfirst.However,Ihopeyouunderstandthatinthisbook,wepostponedthisratherimportanttopicuntiltheend.Ireallylovetesting,butaswe'remainlyfocusedonthecomponentarchitectureofAngular,placingthischapterattheendseemedmorelogical.

Inthischapter,we'lllookintohowtoperformproperunittestingonyourcomponents.We'llfocusonunittesting;automatedend-to-endtestingisbeyondthescopeofthisbook.Still,we'lllookintohowtotestuserinteractiononcomponents,althoughnotonthelevelitwouldbedoneinend-to-endtesting.

Inthischapter,wewilldelveintothefollowingtopics:

AnintroductiontotheJasminetestingframeworkWritingsimpleJavaScripttestsforcomponentsCreatingatests.htmlfile,whichservesasanin-browsertestrunnerCreatingJasminespiesandobservingcomponentoutputpropertiesLearningaboutAngulartestingutilities,suchasinject,async,TestComponentBuilder,DebugElement,andmore

www.EBooksWorld.ir

Page 362: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

MockingcomponentsMockingexistingservicesCreatingtestsforourAutoCompleteUIcomponentCreatingtestsforourpluginsystem

www.EBooksWorld.ir

Page 363: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

AnintroductiontoJasmineJasmineisaverysimpletestingframework,whichcomeswithanAPIthatallowsyoutowriteBehavior-drivenDevelopment(BDD)styletests.BDDisanagilesoftwaredevelopmentprocessofdefiningspecificationsinawrittenformat.

InBDD,wedefinethatanagileuserstoryconsistsofmultiplescenarios.Thesescenarioscloselyrelatetoorevenreplacetheacceptancecriteriaofastory.Theydefinerequirementsonahigherlevel,andtheyaremostlywrittennarrative.Eachscenariothenconsistsofthreeparts:

Given:Thispartisusedtodescribetheinitialstateofthescenario.Thetestcodeiswhereweperformallthesetupthatisneededtoexecutethetestscenario.When:Thispartreflectsthechangesthatweperformtothesystemundertest.Usually,thispartconsistsofsomeAPIcallsandactionsthatreflectthebehaviorofauserofthesystem.Then:Thispartspecifieswhatthesystemshouldlooklikeafterthegivenstateandthechangesappliedinthewhenpart.Inourcode,thisisthepartthatisusuallyattheendofourtestsfunction,whereweuseassertionlibrariestoverifythestateofthesystem.JasminecomeswithanAPIthatmakesitveryeasytowritetestswhichstructureaccordingtotheBDDstyle.Let'slookataverysimpleexampleofhowweuseJasminetowriteatestforashoppingcartsystem:

describe('Buyingitemsintheshop',()=>{

it('shouldincreasethebasketcount',()=>{

//Given

constshop=newShop();

//When

shop.buy('Toothpaste');

shop.buy('Shampoo');

//Then

expect(shop.basket.length).toBe(2);

expect(shop.basket).toContain('Toothpaste');

expect(shop.basket).toContain('Shampoo');

});

});

Jasmineprovidesuswithadescribefunction,whichallowsustogroupcertainscenariosonthesamesubject.Inthisexample,weusedthedescribefunctiontoregisteranewtestsuitefortestsaboutbuyingitemsinashop.

Usingtheitfunction,wecanregisterindividualscenarios,whichwe'dliketogettested.Inthedescribecallbackfunction,wecanregisterasmanyscenariosusingtheitfunctionaswelike.

InsidethecallbackfunctionoftheJasmineitfunction,wecanstartwritingourtest.WeuseaBDDstyletostructurethecodeinsideourtest.

www.EBooksWorld.ir

Page 364: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Youdon'tnecessarilyneedtorunJasmineinthebrowser,butifyoudothis,you'llgetanicesummaryreportofalltestsandtheirstate:

Jasmineprovidesanicevisualreportoverallyourtestspecifications,whichalsoallowsyoutorerunindividualtestsandprovidesyouwithmoreoptions

Jasminecomesinthreepartsthatarerelevanttous:

Jasminecore:ThiscontainsthetestdefinitionAPIs,theassertionlibrary,andalltheothercorepartsofthetestingframeworkJasmineHTML:ThisistheHTMLreporter,whichwillwritealltestsresultstothebrowserdocumentandevenprovideoptionstorerunindividualtestsJasmineboot:ThisisthefilethatbootstrapstheJasmineframeworkforthebrowserandperformsanysetupthatisneededwiththeHTMLreporter

Inourproject,wewilluseJasmineandtheprecedingpartsdirectlyfromaCDN,sowedon'tneedtoinstallanythingtogetstarted.Wecreateanewtests.htmlfile,whichwillserveasarunnerforourtests.Inconjunctionwithlive-server,wecanalwayshavethispageopeninourbrowser.Thiswaywe'llgetimmediatefeedbackonourtestswhiledeveloping.

Tip

JasminealsoplaysnicewithtestrunnerssuchasKarmatorunyourtests.Karmaisapopulartestrunner,whichallowsyoutorunyourtestsinparallelusingtheKarmaCLIorintegrateitinyourbuildpipeline.Thisalsoallowsyoutoruntestsindifferentbrowsers.Inthischapter,wewillusetheJasmineHTMLandJasmineboottorunourtestsdirectlyinthebrowser.Thisallowsustoskiptherathercomplexsetupthatwe'dneedtoundertakeifweusedKarmaasourtestrunner.

Let'slookatthecodeofthetests.htmlfilethatwecreateintherootfolderofourapplication,rightnexttotheindex.htmlfile,whichisalreadypresent:

...

<scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/es6-shim/0.35.0/es6-

www.EBooksWorld.ir

Page 365: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

shim.min.js"></script>

<scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/angular.js/2.0.0-

beta.17/angular2-polyfills.js"></script>

<scriptsrc="jspm_packages/system.js"></script>

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

<scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/jasmine/2.4.1/jasmine.js">

</script>

<scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/jasmine/2.4.1/jasmine-

html.js"></script>

<scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/jasmine/2.4.1/boot.js">

</script>

BesidesloadingtheusualsuspectsforourAngularapplication(ES6shim,Angularpolyfills,andSystemJS),wenowalsoloadthethreemaincomponentsofJasmine.

Bydefault,Jasmineexecutesallregisteredtestsonthewindow'sloadevent.However,aswewillloadourtestsusingSystemJS,weneedtodeferthebootstrapofJasmineuntilSystemJShascompletelyloadedourtests:

<script>

window._jasmineOnLoad=window.onload;

window.onload=null;

returnSystem.import('./all.spec')

.then(window._jasmineOnLoad)

.catch(console.error.bind(console));

...

</script>

WefirstputasidethefunctionthatwasregisteredbyJasminebootonwindow.onload.Westorethefunctioninatemporary_jasmineOnLoadglobalvariable.

Now,weuseSystemJStoimportourentrypointmoduleforourtests,whichwillbestoredintheall.spec.jsfile.SystemJSreturnsaPromisethatwillberesolvedifthetestmodulehasbeenloadedandexecutedsuccessfully.WecanusethethenfunctionofthereturnedPromisetoexecutetheJasminebootfunctionstoredinwindow._jasmineOnLoad.Inthisway,wemakesurethatJasmineisbootedafterallourtestshavebeenregistered.

www.EBooksWorld.ir

Page 366: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

WritingourfirsttestNowthatweareallsetwiththeJasminesetup,wecanstartwritingourfirsttest.Inthissection,wewillcreateafirsttestfortheAutoCompletecomponentthatwecreatedinChapter8,TimeWillTell,ofthisbook.

AsAngularcomponentsarejustclasses,wecanalreadytestalotofthefunctionalitybyinstantiatingtheComponentclassandtestingitsmethods.Teststhatcanbeperformedlikethisshouldalwaysbeconsideredfirst.ThesetestscanrunwithoutAngularbootstrappingthecomponent.

TheAutoCompletecomponentfiltersdisplayedresultsbasedontheavailableitemsandafiltercriteria.Inthefollowingtest,we'llverifythatthefiltermethodonthecomponentworksasexpected.

Tip

Inthisbook,wefollowthepracticetostoretestfilesbyappendinga.spec.jsfiletothenameofthefilethathastobetested.We'llalsostorethesetestfilesinthesamefolderofthesubject.Thismakesitmucheasiertokeepthecontext.

We'llcreateanewauto-complete.spec.jsfileinthefolderoftheAutoCompletecomponentatlib/ui/auto-complete:

import{describe,expect,it,}from'@angular/core/testing';

import{AutoComplete}from'./auto-complete';

describe('AutoComplete',()=>{

it('shouldfilteritemscorrectly',()=>{

//Given

constautoComplete=newAutoComplete();

autoComplete.items=['One','two','three'];

//When

autoComplete.filterItems('o');

//Then

expect(autoComplete.filteredItems).toEqual(['One','two']);

});

});

AsweloadedJasminepriortoexecutingourtest,wecouldrelyontheglobaldescribe,it,andexpectfunctionsthatareexposedbyJasmine.However,AngularprovidesuswithsomenicewrappersoftheJasminefunctions,whichwecanimportfromthemodulelocatedin@angular/core/testing.

Asyoucansee,wedon'treallyneedtoactivatetheAutoCompletecomponentinordertotestsomeofitsfunctionality.Bysimplytestingthecomponentclass,wecanalreadyexecutesomeofourexecutablespecifications.

www.EBooksWorld.ir

Page 367: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

WefollowaBDDapproachtostructureourtest,andintheGivensection,weinstantiateanewAutoCompletecomponentclass,andthenweinitializetheitemslistwithsometestitems.Eveniftheitemsfieldisactuallyacomponentinput,wecansimplydisregardthisfactinordertotestthefilteringfunctionality.

IntheWhensectionofourtest,weactuallycallthefilterItemsmethodofthecomponentclassandtestwhetheritdoesfiltertheitemsaccordingtothespecification.

IntheThensection,weusetheexpectfunctionofJasmineinordertoasserttheexpectedstateaftertheWhensection.Asthecomponentshouldfilterallitemswithpartialandcase-insensitivematchesofthefiltercriteria,theexpectedvalueinfilteredItemsshouldbeanarraywiththeOneandtwoitems.

WeusetheassertiontoEqualfunctioninordertoperformadeepequalcheck.IfweusethetoBematcher,we'dcomparethereferencesofthetwoarrays,whichwillresultinanegativematch.

Thisisitforourfirsttest.What'slefttodostillistocreateourmaintestmodulethatisloadedinthetests.htmlfile.

Wecreatedthemainentrypointforallourtestsinaall.spec.jsfileontherootpathofourapplication.Thisfilewillthenincludeallspecificationfilesthatwecreateinourapplication:

import'./lib/ui/auto-complete/auto-complete.spec';

Thisiscurrentlyallthatweneedtomakeourtestrun.Wesimplyimportthetestfilethatwejustcreated.Now,tests.htmlwilluseSystemJStoloadourall.spec.jsfile,andhere,wethenloadtheauto-complete-spec.jsfile.

Wecannowstartlive-serverintherootpathofourapplicationandnavigatetohttp://127.0.0.1:8080/tests.htmlinourbrowser.Aslive-serverwillreloadourbrowseronchanges,wecanstartaddingnewtestswhileweconstantlygetupdatesonourteststateinthebrowser.

www.EBooksWorld.ir

Page 368: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

SpyingoncomponentoutputsAcommonpracticeintestingistousespyfunctioncallsduringtheexecutionoftestsandthenevaluatethesecalls,checkingwhetherallfunctionshavebeencalledcorrectly.

Jasmineprovidesuswithsomenicehelpersinordertousespyfunctioncalls.WecanusethespyOnfunctionofJasmineinordertoreplacetheoriginalfunctionwithaspyfunction.Thespyfunctionwillrecordanycalls,andwecanlateronevaluatehowmanytimesitwascalledandwithwhatparameters.

Let'slookatasimpleexampleofhowtousethespyOnfunction:

classCalculator{

multiply(a,b){

returna*b;

}

pythagorean(a,b){

returnMath.sqrt(this.multiply(a,a)+this.multiply(b,b));

}

}

WewilltestasimpleCalculatorclassthathastwomethods.Themultiplymethodsimplymultipliestwonumbersandreturnstheresult.Thepythagoreanmethodcalculatesthehypotenuseofaright-angledtrianglewithtwosides,aandb.

YoumightremembertheformulaforthePythagoreantheoremfromyourearlyschooldays:

a²+b²=c²

Wewillusethisformulatoproducecfromaandbbygettingthesquarerootoftheresultofa*a+b*b.Forthemultiplications,we'lluseourmultiplymethodinsteadofusingarithmeticoperatorsdirectly.

Now,we'dwanttotestourcalculatorpythagoreanmethod,andasitusesthemultiplymethodtomultiplyaandb,wecanspyonthismethodtoverifyourtestresultindepth:

describe('Calculatorpythagoreanfunction',()=>{

it('shouldcallmultiplyfunctioncorrectly',()=>{

//Given

constcalc=newCalculator();

spyOn(calc,'multiply').and.callThrough();

//When

constresult=calc.pythagorean(6,8);

//Then

expect(result).toBe(10);

expect(calc.mul).toHaveBeenCalled();

expect(calc.mul.calls.count()).toBe(2);

expect(calc.mul.calls.argsFor(0)).toEqual([6,6]);

expect(calc.mul.calls.argsFor(1)).toEqual([8,8]);

www.EBooksWorld.ir

Page 369: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

});

});

ThespyOnfunctionofJasminetakesanobjectasfirstparameterandthefunctionnameontheobjectwhichwe'dliketospyon.

ThiswilleffectivelyreplacetheoriginalmultiplyfunctiononourclassinstancewithanewspyfunctionofJasmine.Bydefault,spyfunctionswillonlyrecordfunctioncalls,andtheywon'tdelegatethecallfurthertotheoriginalfunction.Wecanusethe.and.callThrough()functiontospecifythatwe'dlikeJasminetocalltheoriginalfunction.Thiswayourspyfunctionwillactasaproxyandrecordanycallsatthesametime.

IntheThensectionofourtest,wecantheninspectthespyfunction.UsingthetoHaveBeenCalledmatcher,wecancheckwhetherthespyfunctionwascalledafterall.

Usingthecallspropertyofthespyfunction,wecaninspectinmoredetailandverifythecallcountaswellastheargumentsthatindividualcallsreceived.

UsingtheknowledgethatwegainedaboutJasminespies,wecannowapplythattoourcomponenttests.AsweknowthatalloutputpropertiesofcomponentscontainanEventEmitter,wecanactuallyspyonthemtocheckwhetherourcomponentsendsoutput.

Insidecomponents,wecallthenextmethodonEventEmitterinordertosendoutputtoparentcomponentbindings.Asthisisanasynchronousoperationandwe'dalsoliketotestourcomponentswithoutneedingtoinvolveparentcomponents,wecansimplyspyonthenextmethodofouroutputproperties.

InthenexttwotestsforourAutoCompletecomponent,we'dliketoverifythefunctionalitywhenwesaveaneditintheEditorchildcomponent.Let'squicklyrecaponthisbehavior:

Onsavededits,wegettheonEditSavedmethodontheAutoCompletecomponentthatiscalledIfthesavedvalueisanemptystring,theAutoCompletecomponentshouldemitaselectedItemChangeeventwithanullvalueIfthesavedvalueisnoemptystringandthevalueisnotpresentintheitemsoftheAutoCompletecomponent,anitemCreatedeventshouldbeemitted

Let'screatethetestsforthepreviousexpectedbehaviortothealreadyexistinglib/ui/auto-complete/auto-complete.spec.jstestfile:

...

it('shouldemitselectedItemChangeeventwithnullonemptycontentbeing

saved',()=>{

//Given

constautoComplete=newAutoComplete();

autoComplete.items=['one','two','three'];

autoComplete.selectedItem='three';

spyOn(autoComplete.selectedItemChange,'next');

www.EBooksWorld.ir

Page 370: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

spyOn(autoComplete.itemCreated,'next');

//When

autoComplete.onEditSaved('');

//Then

expect(autoComplete.selectedItemChange.next).toHaveBeenCalledWith(null);

expect(autoComplete.itemCreated.next).not.toHaveBeenCalled();

});

WecreatetwoJasminespieshere.ThefirstonespiesontheselectedItemChangeoutputproperty,whilethesecondonespiesontheitemCreatedoutputproperty.

Aftersimulation,theeditorwassavedwithanemptystring.WecanstartverifyingourspiesintheThensectionofourtest.

ThenextfunctionoftheselectedItemChangeevent,EventEmitter,shouldhavebeencalledwithanullvalue,whilenextofitemCreatedshouldn'thavebeencalledatall.Wecanusethenotpropertyonthereturnedexpectationobjecttoinvertthematcher.

Let'saddasecondtestforthebehaviorwhenaneditorwassavedwithavaluethatdoesnotyetexistintheAutoCompletecomponent:

it('shouldemitanitemCreatedeventoncontentbeingsavedwhichdoesnot

matchanexistingitem',()=>{

//Given

constautoComplete=newAutoComplete();

autoComplete.items=['one','two','three'];

autoComplete.selectedItem='three';

spyOn(autoComplete.selectedItemChange,'next');

spyOn(autoComplete.itemCreated,'next');

//When

autoComplete.onEditSaved('four');

//Then

expect(autoComplete.selectedItemChange.next).not.toHaveBeenCalled();

expect(autoComplete.itemCreated.next).toHaveBeenCalledWith('four');

});

Thistime,wesimulateasavededitwithavalue,whichisn'tanemptystringanddoesnotexistintheautocompleteitemsalready.

IntheThensectionofourcode,weevaluatethespiesandexpectthattheitemCreated.nextfunctionwascalledwithafourstring.

UsingJasminespies,wemanagedtotestourcomponentoutputsuccessfullywithouttheneedtobootstrapAngular.WeperformedthesetestssolelyonthecomponentclassandbycreatingspiesontheEventEmitterthatispresentonalloutputproperties.

www.EBooksWorld.ir

Page 371: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

UtilitiestotestcomponentsSofar,wetestedourcomponentswithplainvanillaJavaScript.Thefactthatcomponentsareinjustregularclassesmakethispossible.However,thiscanonlybedoneforverysimpleuse-cases.Assoonaswe'dliketotestcomponentsforthingsthatinvolvetemplatecompilation,userinteractiononcomponents,changedetection,ordependencyinjection,we'llneedtogetalittlehelpfromAngulartoperformourtests.

Angularcomeswithawholebunchoftestingtoolsthathelpusouthere.Infact,theplatform-agnosticwaythatAngularisbuiltallowsustoexchangetheregularviewadapterwithadebugviewadapter.Thisenablesustorendercomponentsinsuchawaythatallowsustoinspectthemingreatdetail.

ToenablethedebuggingcapabilitiesofAngularwhilerenderingcomponents,weneedtomodifyourmainentrypointforourtestsfirst.

Let'sopenupall.spec.jstomakethenecessarymodifications:

import{setBaseTestProviders}from'@angular/core/testing';

import{TEST_BROWSER_DYNAMIC_PLATFORM_PROVIDERS,

TEST_BROWSER_DYNAMIC_APPLICATION_PROVIDERS}from'@angular/platform-browser-

dynamic/testing';

setBaseTestProviders(TEST_BROWSER_PLATFORM_PROVIDERS,

TEST_BROWSER_APPLICATION_PROVIDERS);

import'./lib/ui/auto-complete/auto-complete.spec';

import'./lib/plugin/plugin.spec';

UsingthesetBaseTestProvidersfunctionofthe@angular/core/testingmodule,wecanactuallyinitializeatestplatforminjector,whichwillthenbeusedinthecontextofourAngulartesting.Thisfunctiontakestwoargumentswherethefirstoneisanarrayofplatformproviders,andthesecondoneisanarrayofapplicationproviders.

Fromthe@angular/platform-browser-dynamic/testingmodule,wecanimporttwoconstantsthatcontainanalreadypreparedlistforbothplatformandapplication-leveldependencies.Herearesomeoftheproviderspresentintheseconstants:

Platform-levelproviders:TheseconsistmostlyofplatforminitializationproviderstodebugApplication-levelproviders:Theseconsistofthefollowing:

DebugDomRootRenderer:ThisoverridesthedefaultDomRendererinthebrowserandenablesdebuggingofelementsusingDebugElementandprobingMockDirectiveResolver:ThisoverridesthedefaultDirectiveResolverandallowsoverridingofdirectivemetadatafortestingpurposesMockViewResolver:ThisoverridesthedefaultViewResolverandallowsoverridingofcomponentviewspecificmetadata

www.EBooksWorld.ir

Page 372: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

UsingthesetBaseTestProvidersfunctionandtheimportedconstantswiththedebuggingproviders,wecannowinitializeourtestenvironment.Aftercallingthisfunctionandpassingourproviders,Angularissetupfortesting.

www.EBooksWorld.ir

Page 373: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

InjectingintestsInjectingAngulardependenciesintestsismadeeasybytwohelperfunctionsthatwecanuse.Theinjectandasyncfunctionsareavailablethroughthe@angular/core/testingpackage,andtheyhelpusinjectdependenciesinourtests.

Let'slookatthissimpleexamplewhereweinjectthedocumentelementusingtheinjectwrapperfunction.Thistestisirrelevantforourapplication,butitillustrateshowwecannowmakeuseofinjectioninourtests:

import{describe,expect,it,inject}from'@angular/core/testing';

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

describe('Applicationinitializedwithtestproviders',()=>{

it('shouldinjectdocument',inject([DOCUMENT],(document)=>{

expect(document).toBe(window.document);

}));

});

Wecansimplyuseinjecttowrapourtestfunction.Theinjectfunctionacceptsanarrayasthefirstparameterthatshouldincludealistofinjectables.Thesecondparameterisouractualtestfunction,whichwillnowreceivetheinjecteddocument.

Theasyncfunctionontheotherhandhelpsuswithadifferentconcerntoo.Whatifourtestsactuallyinvolveasynchronousoperations?Well,astandardasynchronousJasminetestwouldlooklikethefollowing:

describe('Asynctest',()=>{

it('shouldbecompletedbycallingdone',(done)=>{

setTimeout(()=>{

expect(true).toBe(true);

done();

},2000);

});

});

Jasmineprovidesuswithanicewaytospecifyasynchronoustests.Wecansimplyusethefirstparameterofourtestfunctions,whichresolvestoacallbackfunction.Bycallingthiscallbackfunction,inourcasewenameditdone,wetellJasminethatourasynchronousoperationsaredone,andwewouldliketofinishthetest.

Usingcallbackstoindicatewhetherourasynchronoustestisfinishedisavalidoption.However,thiscanmakeourtestquitecomplicatedifmanyasynchronousoperationsareinvolved.It'ssometimesevenimpossibletomonitoralltheasynchronousoperationsthatarehappeningunderthehood,whichalsomakesitimpossibleforustodeterminetheendofourtest.

Thisiswheretheasynchelperfunctioncomesintoplay.AngularusesalibrarycalledZone.jstomonitoranyasynchronousoperationinthebrowser.Simplyput,Zone.jshooksintoany

www.EBooksWorld.ir

Page 374: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

asynchronousoperationandmonitorswheretheyareinitiatedaswellaswhentheyarefinished.Withthisinformation,Angularknowsexactlyhowmanypendingasynchronousoperationsthereare.

Ifwe'reusingtheasynchelper,wetellAngulartoautomaticallyfinishourtestwhenallasynchronousoperationsinourtestaredone.ThehelperusesZone.jstocreateanewzoneanddeterminewhetherallthemicrotasksexecutedwithinthiszonearefinished.

Let'slookathowwecancombineinjectionwithanasynchronousoperationinourtest:

import{describe,expect,it,inject,async}from'@angular/core/testing';

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

describe('Applicationinitializedwithtestproviders',()=>{

it('shouldinjectdocument',async(inject([DOCUMENT],(document)=>{

setTimeout(()=>{

expect(document).toBe(window.document);

},2000);

}))

);

});

Bycombininginjectwithasync(wrapping),wenowhaveanasynchronousoperationinourtest.Theasynchelperwillmakeourtestwaituntilallasynchronousoperationsarecompleted.Wedon'tneedtorelyonacallback,andwehavetheguaranteethateveninternalasynchronousoperationswillcompletebeforeourtestfinishes.

Tip

Zone.jsisdesignedtoworkwithallasynchronousoperationsinthebrowser.ItpatchesallcoreDOMAPIsandmakessurethateveryoperationgoesthroughazone.AngularalsoreliesonZone.jsinordertoinitiatechangedetection.

www.EBooksWorld.ir

Page 375: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

TestcomponentbuilderAngularcomeswithanotherveryimportanttestingutilitytotestcomponentsanddirectives.Sofar,weonlytestedthecomponentclassofourcomponents.However,assoonasweneedtotestcomponentsandtheirbehaviorinourapplication,thisinvolvesafewmorethings:

Testingtheviewofcomponents:It'ssometimesrequiredthatwetesttherenderedviewofcomponents.Withallthebindingsinourview,dynamicinstantiationusingtemplatedirectivesandcontentinsertion,it'srequiredthatwecanhaveawaytotestallthisbehavior.Testingchangedetection:Assoonasweupdateourmodelinourcomponentclass,wewanttotesttheupdatesthatareperformedviachangedetection.Thisinvolvesthewholechangedetectionbehaviorofourcomponents.Userinteraction:Ourcomponenttemplatesprobablycontainasetofeventbindings,whichtriggersomebehavioronuserinteraction.We'dalsoneedawaytotestthestateaftersomeuserinteraction.Overridingandmocking:Inatestingscenario,it'ssometimesrequiredtomockcertainareasinourcomponentsinordertocreateaproperisolationforourtest.Inunittesting,weshouldbeconcernedonlyaboutthespecificbehaviorthatwewanttotest.

TheTestComponentBuilder,whichisavailablethroughthe@angular/compiler/testingpackage,helpsusexactlywiththepreviousconcerns.It'sourmaintooltotestcomponents.

TestComponentBuilderisprovidedtothetestapplicationinjector,whichweinitializedinourall.spec.jsmoduleusingthesetBaseTestProvidersfunction.Thereasonforthisisthatthebuilderitselfalsoreliesonalotofplatformandapplicationdependenciestocreatecomponents.Asallourdependenciesnowcomefromthetestinjectorandmostofthemareoverriddentoenableinspection,thismakesperfectsense.

Let'slookataverysimpleexampleofhowwecanuseTestComponentBuildertotesttheviewrenderingofadummycomponent:

@Component({

selector:'dummy-component',

template:'dummy'

})

classDummyComponent{}

describe('CreatingacomponentwithTestComponentBuilder',()=>{

it('shouldrenderitsviewcorrectly',async(inject([TestComponentBuilder],

(tbc)=>{

tbc.createAsync(DummyComponent).then((fixture)=>{

//When

fixture.detectChanges();

//Then

expect(fixture.nativeElement.textContent).toBe('dummy');

});

}))

);

www.EBooksWorld.ir

Page 376: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

});

AsTestComponentBuilderisexposedinthetestinjector,weneedtousedependencyinjectiontogetholdoftheinstance.Weusetheinjecthelperforthispurpose.Ascreatingacomponentisanasynchronousoperation,wealsoneedtomakeourtestwaitforcompletionusingtheasynchelper.

Inourtestfunction,wecallthecreateAsyncmethodofTestComponentBuilderandpassareferencetoDummyComponent,whichwewanttocreate.ThismethodreturnsaPromise,whichwillresolveoncethecomponentissuccessfullycompiled.

Inthethencallbackofthereturnedpromise,we'llreceiveaspecialfixtureobjectoftheComponentFixturetype.WecanthencallthedetectChangesmethodonthisfixtureobject,whichwillexecutechangedetectiononthecreatedcomponent.Afterthisinitialchangedetection,theviewofourdummycomponentisupdated.WecannowusethenativeElementpropertyofthefixtureinordertoaccesstherootDOMelementofthecreatedcomponent.

Let'slookattheComponentFixturetypeandtheavailablefieldsinmoredetail:

Member Description

detectChanges()

Thisexecuteschangedetectionontherootcomponentthatwascreatedinthecontextofthefixture.ThetemplatebindingswillnotbeevaluatedautomaticallyaftercreatingacomponentusingTestComponentBuilder.It'sourownresponsibilitytotriggerchangedetection.Evenafterwechangethestateofourcomponents,we'dneedtotriggerchangedetectionagain.

destroy()

Thismethoddestroystheunderlyingcomponentandperformsanycleanupthatisrequired.ThiscanbeusedtotesttheOnDestroycomponent'slifecycle.

componentInstanceThispropertypointstothecomponentclassinstance,andthisisourmaininteractionpointifwewanttointeractwiththecomponent.

nativeElement

ThisisareferencetothenativeDOMelementattherootofthecreatedcomponent.ThispropertycanbeusedtoinspecttherenderedDOMofourcomponentdirectly.

elementRef

ThisistheElementRefwrapperaroundtherootelementofthecreatedcomponent.

www.EBooksWorld.ir

Page 377: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

debugElement

ThispropertypointstoaninstanceofDebugElementthatwascreatedbyDebugDomRootRendererinthecomponentviewrenderingpipeline.Thedebugelementprovidesuswithsomeniceutilitiestoinspecttherenderedelementtreeandtestinguserinteraction.We'lltakeacloserlookatthislaterinanothersection.

We'venowlookedataverysimpledummycomponentandhowtotestitusingTestComponentBuilderinconjunctionwiththeinjectandasynchelperfunctions.

Thisisgreat,butitdoesn'treallyreflectthecomplexitythatwefacewhenweneedtotestrealcomponents.Realcomponentshavealotmoredependenciesthanourdummycomponent.Werelyonchilddirectivesandprobablyoninjectedservicestoobtaindata.

Ofcourse,theTestComponentBuilderalsoprovidesuswiththetoolsthatweneedinordertotestmorecomplexcomponentsandkeepthenecessaryisolationinaunittest.

Let'sfirstlookatanexamplewherewe'dliketotestaParentComponentcomponent,whichusesaChildComponentcomponenttorenderalistofnumbers.Aswe'donlyliketotestParentComponent,we'renotinterestedinhowChildComponentrendersthislist.WewanttoremovethebehaviorofthechildcomponentfromourtestbyprovidingamockcomponentforChildComponentduringourtest,whichallowsustoeasilyverifythatthedataisreceivedbythechildcomponent:

@Component({

selector:'child',

template:'<ul><li*ngFor="letnofnumbers">Item:{{n}}</li></ul>'

})

classChildComponent{

@Input()numbers;

}

@Component({

selector:'parent',

template:'<child[numbers]="numbers"></child>',

directives:[ChildComponent]

})

classParentComponent{

numbers=[1,2,3];

}

Thisisourstartingpoint.Wehavetwocomponents,wherewe'llonlybeinterestedintestingtheparentcomponent.However,thechildcomponentisrequiredbytheparentcomponent,anditimpliesaveryspecificwaytorenderthenumbersthatarepassedbytheparent.Wewouldonlyliketotestwhetherournumberswerepassedsuccessfullytothechildcomponent.Wedon'twanttoinvolvetherenderinglogicofthechildcomponentinourtest.Thisisveryimportantbecausechangingonlythechildcomponentcouldthenbreakourparentcomponent

www.EBooksWorld.ir

Page 378: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

test,whichwewanttoavoid.

Thethingwewanttoachievenowistocreateamockofourchildcomponentinthecontextofourtest:

@Component({

selector:'child',

template:'{{numbers.toString()}}'

})

classMockChildComponent{

@Input()numbers;

}

InourMockChildComponentclass,it'simportantthatweusethesameselectorpropertyastherealcomponent.Otherwise,themockingwillnotwork.Inthetemplate,weuseaverysimpleoutputofthenumbersinput,whichenablesaneasyinspection.

It'salsoimportantthatweprovidethesameinputpropertiesastheoriginalcomponent.Otherwise,wewon'timitatetherealcomponentcorrectly.

Now,wecangoaheadandperformourtest.UsinganadditionalmethodofTestComponentBuilder,weareabletooverridetherealChildComponentwithourmockcomponent:

describe('ParentComponent',()=>{

it('shouldpassdatatochildcorrectly',async(inject([TestComponentBuilder],

(tbc)=>{

tbc

.overrideDirective(ParentComponent,ChildComponent,MockChildComponent)

.createAsync(ParentComponent).then((fixture)=>{

fixture.detectChanges();

expect(fixture.nativeElement.textContent).toBe('1,2,3');

});

}))

);

});

UsingtheoverrideDirectivemethodonTestBuilderComponent,wecanmodifytheparentcomponent'sdirectivesmetadatabeforewecreateit.Inthisway,we'reabletoexchangetherealchildcomponentwithourMockChildComponentclass.

Asaresult,wedecoupleParentComponentfromChildComponentinthecontextofourtest.Weneedthislevelofseparationinordertocreateaproperisolationofourunittest.Asourmockchildcomponentsimplyrendersthestringrepresentationofthepassedarray,wecaneasilytestthetextcontentofourfixture.

Tip

Thedefinitionofaunittestistotestasingleunitandisolatetheunitfromanydependencies.Ifwewanttosticktothisparadigm,we'dneedtocreateamockforeverydependent

www.EBooksWorld.ir

Page 379: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

component.Thiscaneasilygetusintoasituationwhereweneedtomaintainmorecomplexityonlyforthesakeofourtests.Thekeyhereliesinfindingtherightbalance.Youshouldmockdependenciesthathaveagreatimpactonoursubjectandignoredependenciesthathavelowimpactonthefunctionalitywe'dliketotest.

Let'slookatadifferentusecasewherewehaveacomponentthatinjectsaserviceinordertoobtaindata.Aswealsowanttotestonlyourcomponentandnottheserviceitrelieson,wesomehowneedtosneakinamockserviceinsteadoftherealserviceintoourcomponent.TestComponentBuilderalsoprovidesamethodtomodifytheprovidersmetadataofdirectives,whichcomesinveryhandyforthiscase.

First,wedeclareourbasecomponentandaservicethatitrelieson.Inthisexample,theNumbersComponentclassinjectstheNumbersServiceclass,anditobtainsanarraywithnumbersfromit:

@Injectable()

classNumbersService{

numbers=[1,2,3,4,5,6];

}

@Component({

selector:'numbers-component',

template:'{{numbers.toString()}}',

providers:[NumbersService]

})

classNumbersComponent{

constructor(@Inject(NumbersService)numbersService){

this.numbers=numbersService.numbers;

}

}

Now,weneedtocreateamockservicethatprovidesthedatarequiredinourtestandisolatesourcomponentfromtheoriginalservice:

@Injectable()

classMockNumbersServiceextendsNumbersService{

numbers=[1,2,3];

}

Inthissimplifiedexample,wejustprovideadifferentsetofnumbers.However,inarealmockingcase,wecanexcludealotofstepsthatareunnecessaryandcouldpotentiallycreatesideeffects.Usingamockservicealsoensuresthatourtest,whichisfocusedontheNumbersComponentclass,willnotbreakbecauseofachangeintheNumbersServiceclass.

Byextendingtherealservice,wecanleveragesomeofthebehaviorofouroriginalservicewhileoverridingcertainfunctionalityinourmock.Youneedtobecarefulwiththisapproachthough,aswerelyontheoriginalservicebydoingthis.Ifyou'dliketocreateafullyisolatedtest,youshouldprobablyoverrideallmethodsandproperties.Oryoucancreateacompletelyindependentmockservice,whichprovidesthesamemethodsandpropertiesthatareusedin

www.EBooksWorld.ir

Page 380: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

yourtest.

Tip

WhenusingTypeScript,youshoulduseinterfacesforthispurposewherebothyourrealserviceaswellasyourmockserviceimplementthesameinterface.

Let'snowlookatthetestcaseandhowwecanuseTestComponentBuildertoprovideourmockserviceinsteadoftherealone:

describe('NumbersComponent',()=>{

it('shouldrendernumberscorrectly',async(inject([TestComponentBuilder],

(tbc)=>{

tbc

.overrideProviders(NumbersComponent,[

provide(NumbersService,{

useClass:MockNumbersService

})

])

.createAsync(NumbersComponent).then((fixture)=>{

fixture.detectChanges();

expect(fixture.nativeElement.textContent).toBe('1,2,3');

});

}))

);

});

UsingtheoverrideProvidersmethodonTestComponentBuilder,wecanprovideadditionalproviderstothecomponentundertest.Thisallowsustooverrideexistingprovidersthatarealreadypresentonthecomponent.Usingtheprovidefunctionofthe@angular/coremodule,wecancreateaproviderwhichprovidesonrequestsforNumberServicebutalsoresolvestoaMockNumberService.

TestComponentBuilderallowsustoperformtestsinaverysimple,isolated,andflexiblefashion.Itplaysamajorrolewhenwritingunittestsforcomponents.Ifyou'dliketoreadmoreabouttheavailablemethodsonTestComponentBuilder,youcanvisittheofficialdocumentationwebsiteathttps://angular.io/docs/ts/latest/api/core/testing/TestComponentBuilder-class.html.

Now,it'stimetousewhatwelearnedaboutTestComponentBuilderserviceandstarttotestourapplicationcomponentsinaction!

www.EBooksWorld.ir

Page 381: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

TestingcomponentsinactionIntheprevioustopic,welearnedabouttheTestComponentBuilderserviceandhowtouseittocreatecomponentsinourtestingenvironment.Welearnedabouttheinjectandasynchelpersaswellashowtomockcomponentsandservices.

Let'snowusethisknowledgetoworkonourtestsfortheAutoCompletecomponent.Let'saddanothertesttotheauto-complete.spec.jsfileonthelib/ui/auto-completepath.

AstheAutoCompletecomponentreliesontherathercomplexEditorcomponent,it'sprobablyagoodideatomockourEditorcomponentbeforewestartwritingatest:

@Component({

selector:'ngc-editor',

template:'{{content}}'

})

exportclassMockEditor{

@Input()content;

}

Thismightlookabittenuous,butthisisactuallyallthatweneedforourcurrenttestsontheAutoCompletecomponent.TheEditorcomponentshouldjustacceptacontentinput,whichisthemaininteractionbetweenthetwocomponents.InthetemplateofourMockEditorcomponent,wejustrenderthecontentinputproperty.Thisway,wecaneasilyverifytheresultofusingtheAutoCompletecomponent.

Let'susethismockeditortowriteournexttest:

it('shouldinitializeeditorwithselecteditem',

async(inject([TestComponentBuilder],(tcb)=>{

tcb

.overrideDirective(AutoComplete,Editor,MockEditor)

.createAsync(AutoComplete).then((fixture)=>{

//Given

fixture.componentInstance.items=['one','two','three'];

fixture.componentInstance.selectedItem='two';

//When

fixture.detectChanges();

//Then

expect(fixture.nativeElement.textContent.trim())

.toBe('two');

});

})));

Inourtests,we'dliketotestwhethertheAutoCompletecomponentinitializesEditor(respectively,ourMockEditorcomponent)withtherightcontent.WetestwhetherselectedItemofourAutoCompletecomponentsuccessfullyreflectsintotheeditor.

WeuseTestComponentBuilder,whichcreatescomponentsasynchronously.Usingtheasync

www.EBooksWorld.ir

Page 382: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

helperfunction,wetellJasminetowaitforallasynchronousoperationstocompleteforthistest.

UsingtheComponentFixturethatisprovidedbyTestComponentBuilder,wecanstarttointeractwiththecreatedcomponent.UsingthecomponentInstancememberofthecomponentfixture,wecansettherequiredinputpropertiesofourAutoCompletecomponent.

Aswe'reresponsibleforthetriggeringofchangedetectionmanuallyinourtests,weusethedetectChangesmethodonourfixturetoupdatethecomponentview,basedonthenewstate.Thiswillinitiatethechangedetectionlifecycleonourcomponentandperformthenecessaryviewupdates.

AftertheviewupdatesbothofourAutoCompletecomponentandtheunderlyingMockEditorcomponent,wecanrunourassertionstovalidatetheupdatedDOMbygettingthetextcontentofthenativeElementpropertyonourfixture.

Forthisparticulartest,we'refinewiththisapproach.However,inotherscenarioswherewehavemoreDOMelementsinvolved,itwouldn'tbesufficienttoassertontherootcomponent'stextContentpropertydirectly.Thiswouldprobablyincludealotofnoise,whichwe'renotinterestedinforourassertion.Weshouldalwaystrytonarrowourassertiontothefewestdetailspossible.

AswehaveaccesstothenativeDOMelementonourfixture,wecansimplyusetheDOMAPItoselectchildelementsinordertonarrowourassertion:

expect(fixture.nativeElement.querySelector('ngc-

editor').textContent.trim()).toBe('two');

ThiswouldsuccessfullyselecttheDOMelementofourmockeditor,andwecanonlycheckthetextcontentinsidetheeditor.

Althoughthiswouldbeafeasibleapproach,Angularprovidesuswithamuchbetterapproachtoachievethisgoal.

ProvidedbyComponentFixture,wehaveaccesstotheDebugElementtreethatiscreatedbyDebugDomRootRendererinthecontextofourtest.DebugElementallowsusadvancedinspectionoftheelementtreethatwascreatedbyAngularwhenrenderingourcomponents.ItalsocontainsanadvancedqueryingAPI,whichallowsustosearchforcertainelementsinthetree.

Let'srewriteourtesttousetheadvancedcapabilitiesprovidedbyDebugElement:

...

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

...

it('shouldinitializeeditorwithselecteditem',

async(inject([TestComponentBuilder],(tcb)=>{

tcb

.overrideDirective(AutoComplete,Editor,MockEditor)

www.EBooksWorld.ir

Page 383: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

.createAsync(AutoComplete).then((fixture)=>{

...

expect(fixture.debugElement.query(By.directive(MockEditor)).nativeElement.text

Content.trim()).toBe('two');

});

})));

ThequeryandqueryAllmethodsthatareavailableoneveryDebugElementobjectallowustoquerytheAngularviewtreelikewewouldqueryaDOMtreeusingquerySelectorandquerySelectorAll.Thedifferencehereisthatwecanuseapredicatehelpertoqueryformatchingelements.UsingtheByhelperclass,wecancreatethesepredicates,whichwillthenbeusedinordertoquerytheDebugElementtree.

TherearecurrentlythreedifferentpredicatesavailableusingtheByhelper:

Member Description

By.all()Thisisthepredicate,whichwillresultinqueryingforallthechildDebugElementobjectofthecurrentDebugElementobject

By.css(selector)Thisisthepredicate,whichwillresultinqueryingforDebugElementusingthespecifiedCSSselector

By.directive(type)Thisisthepredicate,whichwillresultinqueryingforDebugElementthatcontainthespecifieddirective

Goingbacktoourtest,wecannowusethequerymethodonthefixturedebugelementinordertoqueryforoureditor.Aswe'veexchangedtherealEditorcomponentwithourMockEditorcomponent,weneedtoqueryforthelatter.WeuseaBy.directive(MockEditor)predicate,whichwillsuccessfullyqueryfortheDebugElementobjectthatrepresentsthehostelementofourMockEditorcomponent.

ThequerymethodoftheDebugElementobjectwillalwaysreturnanewDebugElementobjectofthefirstfoundelementiftherewasamatch.Itwillreturnnullifthequeriedelementwasnotfound.

ThequeryAllmethodoftheDebugElementwillreturnanarrayofmanyDebugElementwhichcontainsallelementsthatmatchthepredicate.Iftherewerenomatchingelements,thismethodwillreturnanemptyarray.

www.EBooksWorld.ir

Page 384: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

TestingcomponentinteractionAlthoughUIinteractiontestingisprobablypartofend-to-endtesting,we'lllookathowtotestbasicuserinteractiononyourcomponentsinthistopic.

Inthistopic,we'lltesttheautocompletecomponentbehavioriftheuserclicksonaniteminthecalloutwindowthatshowsallavailableitems.

Let'saddthistesttothealreadyexistingauto-complete.spec.jsmodule:

it('shouldemitselectedItemChangeonclickincallout',

async(inject([TestComponentBuilder],(tcb)=>{

tcb

.overrideDirective(AutoComplete,Editor,MockEditor)

.createAsync(AutoComplete).then((fixture)=>{

spyOn(fixture.componentInstance.selectedItemChange,'next');

fixture.componentInstance.items=['one','two','three'];

fixture.componentInstance.selectedItem='one';

fixture.componentInstance.onEditModeChange(true);

fixture.componentInstance.onEditableInput('');

fixture.detectChanges();

fixture.debugElement

.queryAll(By.css('.auto-complete__item'))

.find((item)=>item.nativeElement.textContent.trim()==='two')

.triggerEventHandler('click');

expect(fixture.componentInstance.selectedItemChange.next).toHaveBeenCalledWith

('two');

});

})));

First,wewanttosetupaJasminespyontheselectedItemChangeEventEmitternextfunctionforourtest.Thisway,wecanchecklaterwhetherourAutoCompletecomponentsuccessfullyemittedtheeventwhentheuserselectsanitemfromthecallout.

IntheGivensectionofourtestcode,wealsocalltheonEditModeChangedandonEditableInputmethodsontheAutoCompletecomponentinstance.Withthesecalls,wesimulatetheeditorthatwasused,andthere'scurrentlynocontentintheeditor.Thiswillresultinthedesiredfiltering,whichwillpresentallavailableitemsinthecalloutforselection.

IntheWhensectionofourcode,wefirstneedtotriggerchangedetectiononthefixture.Thisresultsinthecalloutwithallavailableauto-completeitemsbeingrenderedintheAutoCompletecomponent.

Now,wecansimulatetheclickeventononeofourautocompleteitemstofishingtheactionsinthistest.

First,we'llselectallDebugElementobjectthatmatchtheCSSclassofourautocompleteitemsinthecallout.Thiswillprovideuswithanarraycontainingalltheelements,wherewecan

www.EBooksWorld.ir

Page 385: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

nowusetheArray.prototype.findmethodtoselectonespecificitembasedonthecontainedtext.

OntheDebugElementresultingfromourquery,wenowcallthetriggerEventHandlermethodtosimulateaclickevent.Thiswillactuallynottriggerarealclickevent,butratheritwillexecutethehandlerattachedtothebindingintheviewdirectly.

Aftersimulatingaclickontheautocompleteitemwiththetextcontentoftwo,wecannowinspectourspyontheselectedItemChange.nextfunction.Accordingtothebehaviorinourcomponent,thisshouldhavebeencalledwiththeselecteditemvalue.

TestinguserinteractiononcomponentsismadeveryeasyusingtheDebugElement.WealsodecoupleourtestsfromtheunderlyingDOMeventsbytakingtheshortcutenabledbythetriggerEventHandlermethod.

Tip

ThetriggerEventHandlermethodoperatesonthevirtualelementtreeofAngular,ratherthantheactualDOMtree.Duetothis,wecanalsousethismethodtotriggereventhandlersthatareattachedtocomponentoutputproperties.

www.EBooksWorld.ir

Page 386: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

TestingourpluginsystemIntheprevioussections,wecreatedtestsfortheAutoCompletecomponent,whichisarathersimpleUIcomponent.However,welearnedaboutallthetechniquesthatarerequiredtoperformtestingonmorecomplexcomponentsorevensystemsofcomponents.

Now,we'lllookintotestingthepluginsystemthatwascreatedinChapter10,MakingThingsPluggable.

It'sprobablyagoodtimetorecaponthepluginsystemarchitectureoverviewbeforeworkingonthistopic.Asalwayswithtesting,it'scrucialtounderstandexactlywhat'shappeninginthesystemundertest.

Let'screateanewplugin.spec.jsfileinthelib/pluginpath.

Beforeweimplementourfirsttestfunctionforthissubject,wewillneedtocreatesomedummycomponentsandpluginstotestoursystemwith.Let'screatetheseatthetopofourtestingmodule:

@Component({

selector:'dummy-plugin-component-1',

template:'dummy1'

})

exportclassDummyPluginComponent1{}

@Component({

selector:'dummy-plugin-component-2',

template:'dummy2'

})

exportclassDummyPluginComponent2{}

@Component({

selector:'dummy-application',

template:'dummy-slot:<ngc-plugin-slotname="dummy-slot"></ngc-plugin-slot>',

directives:[PluginSlot]

})

exportclassDummyApplication{}

Nothingspecialhere.Wedeclaretwodummycomponentswithastatictemplatethatwillserveusinperformingourplugintests.Additionally,wecreatedadummyapplicationcomponent,whichwillbeourmaintestingcomponent.Inthefollowingtests,wewillmakeuseofadummycomponenttotestourPluginSlotdirective,asopposedtotestingacomponentdirectly.

Next,we'llneedtomockourPluginServiceinjectable,whichisdesignedtoloadpluginsasynchronouslyfromURLs.Inourmock,we'dwanttooverridethisfunctionality.InsteadofloadingpluginsfromURLs,wewanttoloadsomepredefinedtestplugins:

@Injectable()

www.EBooksWorld.ir

Page 387: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

exportclassMockPluginServiceextendsPluginService{

constructor(){

super();

this.change={

subscribe(){}

};

}

loadPlugins(){}

}

WeoverridetheloadPluginsmethodtoavoidanypluginsbeingloadedduringtheconstructionoftheservice.WealsooverridetheRxJSsubjectpresentonthechangepropertyinordertopreventanyreactivebehaviorofourpluginsystembecausethiswouldonlydisturbourtests.

Let'sdiverightintoourfirsttestwherewewanttotestaverybasicpluginwithoneplugincomponent,tobeinstantiatedcorrectlybythePluginSlotdirective.First,wesetupourteststructureusingthedescribeanditfunctions:

describe('PluginSlot',()=>{

beforeEachProviders(()=>[

provide(PluginService,{

useClass:MockPluginService

})

]);

it('shouldcreatedummycomponentintodesignated

slot',async(inject([TestComponentBuilder,PluginService],(tcb,pluginService)

=>{

tcb.createAsync(DummyApplication).then((fixture)=>{

...

});

}));

TheonlydifferenceheretowhatwealreadyknewisthatweuseanewbeforeEachProvidersfunctionfromthe@angular/core/testingmodule.Thisfunctionallowsustosetupsomedefaultprovidersthatareusedinourtests.AsallourpluginsystemtestswillrelyonthepresenceofPluginService,weusethisfunctiontosetupthemockproviderresolvingtoourMockPluginServiceclass.

InsteadofusingbeforeEachProviders,wecouldalsousetheoverrideProvidersmethodintheTestComponentBuildertoprovideadditionalinjectables.However,thiswilllimittheusetotheinsideofourcomponents.Ifwewanttointeractwiththeservicefromourtestfunction,weneedtousethebeforeEachProvidershelper.

Usingtheinjecthelper,weinjectTestComponentBuilderandPluginService,whichweprovidedusingthebeforeEachProvidershelper.

Let'snowimplementthemissingtestbodyinsideofthePromisecallbackafterexecuting

www.EBooksWorld.ir

Page 388: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

createAsync.

Asafirststep,wedefineanewdummyplugin,whichusesthePluginConfigdecoratorfromthepreviouschapter.WecreateaPluginPlacementinthepluginmetadata,whichincludesamappingofDummyPluginComponent1intotheslotwiththenamedummy-slot.IfyoutakealookattheDummyApplicationcomponentthatweuseinthistestagain,youcanseethatitcontainsaPluginSlotdirectivewiththenameattributesettodummy-slot:

@PluginConfig({

name:'dummy-plugin',

description:'DummyPlugin',

placements:[

newPluginPlacement({slot:'dummy-slot',priority:1,component:

DummyPluginComponent1})

]

})

classDummyPlugin{}

ThispluginshouldnowcausetheDummyPluginComponent1componenttoberenderedinthepluginslotofourDummyApplicationclass.

Asanextstep,weaddtheDummyPluginclasstothepluginslistofourMockPluginServicemockservice:

pluginService.plugins=[{

type:DummyPlugin,

config:DummyPlugin._pluginConfig,

instance:newDummyPlugin()

}];

Theobjectthatwe'readdingtothepluginsarrayoftheMockPluginServicesimplysimulatesapluginthatwouldnormallybeloadedinPluginService.

Next,weputasideareferencetothePluginSlotdirective,whichisplacedinourDummyApplicationcomponent.Forthis,wecanusethequerymethodontheDebugElementrootofourfixture.Weuseapredicate,whichallowsustoquerybythedirectivetypeofourPluginSlotcomponent:

constpluginSlot=fixture.debugElement

.query(By.directive(PluginSlot))

.injector

.get(PluginSlot);

Weneedthereferencetothedirectiveinstanceofthepluginslotinordertoinitializetheslotpriortoourtestassertion.Thisisanimportantstepbecausewecan'trelyontheobservablesubjectinourMockPluginServiceclasstoinitializeourPluginSlotdirective.Weexplicitlydisabledthereactivefeaturesofourpluginsysteminordertoperformpropertesting.Therefore,weneedtomanuallyinitializeourpluginslotbeforewecanperformanyassertion.

www.EBooksWorld.ir

Page 389: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Afterexecutingthequerywiththedirectivepredicate(searchingforanelementwhichcontainsthePluginSlotdirective),we'llreceiveDebugElementofourpluginslotelement.Inordertogetthedirectiveinstance,weusetheelementinjectorpresentoneachDebugElementobject.

TheinitializemethodonthePluginSlotcomponentinstancewillcreateallrelevantplugincomponents.Luckily,thiswillalsoreturnaPromisetous,whichwillberesolvedonceallcomponentshavebeencreatedintheviewofourApplicationDummycomponent:

pluginSlot.initialize().then(()=>{

fixture.detectChanges();

expect(fixture.nativeElement.textContent).toBe('dummy-slot:dummy1');

});

InthecallbackofthePromisereturnedbytheinitializemethodofthePluginSlotinstance,wecanfinallyassertonthetextcontentoftherootelementofourDummyApplicationcomponent.

AstheDummyPluginComponent1classhasasimplestatictemplatethatcontainsthetextdummy1,weshouldseeacompletetextcontentofdummy-slot:dummy1inourapplicationview.

Thisisitforourfirstplugintest.Now,wewilllookatasecondtest,whichwe'llusetoverifyanotherfeatureofourpluginsystem.Ourpluginsystemshouldalsobeabletorendertwocomponentsofthesamepluginintotwoseparatepluginslots.However,inthetemplateofourDummyApplicationcomponent,wecurrentlyonlyhaveonepluginslotwiththenamedummy-slot.

InordertomodifythetemplateofourDummyApplicationcomponentjustforaparticulartest,wecanusetheoverrideTemplatemethodonTestBuilderComponent:

it('shouldcreatetwodummycomponentsofsamepluginintodifferent

slots',async(inject([TestComponentBuilder,PluginService],(tcb,pluginService)

=>{

consttemplate='dummy-slot1:<ngc-plugin-slotname="dummy-slot1">

</ngc-plugin-slot>dummy-slot2:<ngc-plugin-slotname="dummy-slot2"></ngc-

plugin-slot>';

tcb.overrideTemplate(DummyApplication,template)

.createAsync(DummyApplication).then((fixture)=>{

...

});

}))

);

Inourtestfunction,wecreateanewtemplateforourDummyApplicationcomponent.We'readdingtwopluginslotstothetemplatewiththeirnameattributessettodummy-slot1anddummy-slot2.

Now,wecanusetheoverrideTemplatemethodonTestComponentBuildertooverridethe

www.EBooksWorld.ir

Page 390: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

DummyApplicationcomponenttemplatebeforewecreateit.Thisprovidesuswiththenecessaryflexibilitytoreusemockanddummycomponentsfordifferenttests.

Let'stakealookatthecodethatcomesinthecreateAsyncpromisecallback:

@PluginConfig({

name:'dummy-plugin',

description:'DummyPlugin',

placements:[

newPluginPlacement({slot:'dummy-slot',priority:1,component:

DummyPluginComponent1}),

newPluginPlacement({slot:'dummy-slot',priority:2,component:

DummyPluginComponent2})

]

})

classDummyPlugin{}

First,wecreateanewDummyPluginpluginclassandusethePluginConfigdecoratortoconfigureit.Intheplacementmetadata,weconfigurethemappingssothatwemaptwocomponentsintodifferentpluginslots.ThefirstcomponentismappedtothepluginslotwiththenameDummySlot1,whilethesecondonewillgototheslotwiththenameDummySlot2.We'veoverriddenourDummyApplicationtemplatetoincludebothofthesepluginslots.

WenowaddourDummyPluginclasstotheMockPluginServiceclassandsimulatethepluginbeingloaded:

pluginService.plugins=[{

type:DummyPlugin,

config:DummyPlugin._pluginConfig,

instance:newDummyPlugin()

}];

ThefollowingcodequeriesforDebugElementsinthefixtureusingthequeryAllmethod.WeuseapredicatethatqueriesforallelementscontainingthePluginSlotdirective.WithanadditionalcalltoArray.prototype.map,wetransformthearrayinordertogetbackthecomponentinstancesofthediscoveredPluginSlotcomponentsdirectly:

constpluginSlots=fixture.debugElement

.queryAll(By.directive(PluginSlot))

.map((debugElement)=>debugElement.injector.get(PluginSlot));

Now,it'stimetocompleteourtest.UsingthePromise.allfunction,we'reabletostreamlineanarrayofPromisesintoasinglePromise,whichwillresolveonceallunderlyingPromisesareresolved.WecanthenmapourpluginSlotsarraybyexecutingtheinitializemethodoneachofthePluginSlotcomponents.ThiswillreturnanarrayofPromisestous,whichwillresolvewhenallcomponentswithinthepluginslotsarecreated:

Promise.all(

pluginSlots.map((pluginSlot)=>pluginSlot.initialize())

).then(()=>{

fixture.detectChanges();

www.EBooksWorld.ir

Page 391: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

expect(fixture.nativeElement.textContent).toBe('dummy-slot1:dummy1dummy-

slot2:dumm

y2');

});

InthethencallbackoftheconsolidatedpromiseusingthePromise.allfunction,wecanfinallyperformourassertion.WiththeoverriddentemplateofourDummyApplicationcomponentandtheoutputofourtwoplugincomponentsinthetwoseparatedpluginslots,weshouldgetatextcontentofdummy-slot1:dummy1dummy-slot2:dummy2.

Thisisthelasttestthatwelookatinthischapter.However,therearemoretestsinthecodethatcomeswiththisbook.Justcheckoutthecoderepositoryandgetyourhandsonthosetestsyourself.

www.EBooksWorld.ir

Page 392: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

SummaryInthischapter,welearnedhowtowriteconciseunittestsforourcomponents.WefollowedaBDDstyleapproachofwritingtests,andwealsocoveredthebasicsoftheJavaScripttestingframework,Jasmine.

WelearnedaboutthedebuggingtoolsthatareavailableinAngularandhowtosetupaninjectorenvironmentfortesting.UsingTestComponentBuilder,wewereabletoperformtestsinaveryflexiblebutpreciseway.WealsolearnedabouttheviewtreeofmultipleDebugElementthatarecreatedalongwithTestComponentBuilderrunninginthedebugenvironment.Thisallowedustoperformcleverinspectionandapplypracticalqueriestotherenderedviewsinordertoassertexpectedresults.

Weusedtheinjectandasynchelperstoinjectdependenciesand,atthesametime,runasynchronoustests.Webuiltbothmockanddummycomponentsinordertoisolateourtestsfromtherestofourapplication.

www.EBooksWorld.ir

Page 393: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

AppendixA.TaskManagementApplicationSourceCodeThesourcecodeofthetaskmanagementapplicationbuiltinthisbookisavailablefromthePacktdownloadservers.Thelinksforeachchapter'sfinalcodearelistedinthisappendix.Thisappendixalsoprovidesinstructionsonhowtousethedownloadedcodeandthestepsrequiredinordertorunthecode.Furthermore,ithelpsyoutroubleshootsomeofthecommonproblemsthatyoucanexperiencewhenworkingwiththetaskmanagementapplicationandsomehintsonhowtoresolvethem.

www.EBooksWorld.ir

Page 394: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

DownloadThefollowinglistprovidesdownloadlinksforeachchapterofthebook.Thedownloadlinksreferencedownloadablearchivefiles,whichneedtobeunpackedonyourlocalharddrive:

Chapter Link

Chapter2,Ready,Set,Go!

https://github.com/PacktPublishing/Mastering-Angular-2-Components/tree/master/angular-2-components-chapter-2

Chapter3,ComposingwithComponents

https://github.com/PacktPublishing/Mastering-Angular-2-Components/tree/master/angular-2-components-chapter-3

Chapter4,NoComments,Please!

https://github.com/PacktPublishing/Mastering-Angular-2-Components/tree/master/angular-2-components-chapter-4

Chapter5,Component-BasedRouting

https://github.com/PacktPublishing/Mastering-Angular-2-Components/tree/master/angular-2-components-chapter-5

Chapter6,KeepingUpwithActivities

https://github.com/PacktPublishing/Mastering-Angular-2-Components/tree/master/angular-2-components-chapter-6

Chapter7,ComponentsforUserExperience

https://github.com/PacktPublishing/Mastering-Angular-2-Components/tree/master/angular-2-components-chapter-7

Chapter8,TimeWillTell https://github.com/PacktPublishing/Mastering-Angular-2-Components/tree/master/angular-2-components-chapter-8

Chapter9,SpaceshipDashboard

https://github.com/PacktPublishing/Mastering-Angular-2-Components/tree/master/angular-2-components-chapter-9

Chapter10,MakingThingsPluggable

https://github.com/PacktPublishing/Mastering-Angular-2-Components/tree/master/angular-2-components-chapter-10

Chapter11,PuttingThingstotheTest

https://github.com/PacktPublishing/Mastering-Angular-2-Components/tree/master/angular-2-components-chapter-11

www.EBooksWorld.ir

Page 395: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Thecompletesourcecodeisalsoavailableathttp://www.packtpub.com.

www.EBooksWorld.ir

Page 396: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

PrerequisitesThetaskmanagementapplicationisbuiltusingNode.jstechnologies;therefore,itrequiresthatyouhaveNode.jsinstalledonyourmachinebeforeyoucanrunanyofthecode.

YoucandownloadandinstallNode.jsfromthewebsite,http://nodejs.org.InordertobuildtheSasssourcefilesandstartaserverwithlive-reload,twoglobalnodemodulesarerequired:

npminstall-ggulplive-server

www.EBooksWorld.ir

Page 397: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

UsageAfterdownloadingthesourcecodeofanindividualchapter,you'llneedtoinstallNPMaswellasJSPMdependencies.Byrunningthefollowingtwolinesofcodeonyourconsole,you'llmakesurealldependenciesareinstalled.Makesurethatyourunthesecommandsfrominsidethedownloadedandextractedcodefolder:

npminstall

jspminstall

Afteryou'veinstalledtherequireddependencies,youcangoaheadandstarttheapplication:

npmstart

TheNPMstartscriptwillinvokegulpinordertocompileanySassfilesaswellasliveservertostartastaticserverwithlive-reload.PleasereadtheprerequisitestopiconhowtoinstalltheseglobalNode.jsmodules.

www.EBooksWorld.ir

Page 398: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

TroubleshootingWe'vecarefullyconsideredvariousenvironmentswherethetaskmanagementsourcecodewillbeexecuted.However,there'salwaysachancethatyou'llexperiencesomeissueswhenworkingwiththesourcecode.Thistopicwillprovideyouwithsolutionsforcommonproblemswhenworkingwiththetaskmanagementapplication.

www.EBooksWorld.ir

Page 399: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

CleaningIndexDBtoresetdataThetaskmanagementapplicationusesadatastorethatispersistedinyourbrowser.Ifyouusetheapplicationextensivelyacrossmultiplechapters,chancesarehighthatyourdataiscausingsomeapplicationinstability.

Ifyou'dliketocleanthelocaldatabaseusedintheapplication,youcanrunthefollowinglineofcodeonthedebuggerconsoleofyourbrowser.It'simportantthatyourunthecodesnippetinthedebuggerofyourbrowserwiththetaskmanagementapplicationopen.Ifyourbrowserpointstoadifferentorigin,theapplicationdatabasecan'tbedeleted.Afterdeletingthedatabase,theapplicationcanbereloaded,anditwillrecreatethedatabasewiththeinitialsampledata:

indexedDB.deleteDatabase('_pouch_angular-2-components');

www.EBooksWorld.ir

Page 400: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

EnablingwebcomponentsinFirefoxChapter6,KeepingUpwithActivities,reliesonthepresenceofShadowDOMinthebrowser.ChromesupportsShadowDOMnativelyafterversion35.InFirefox,youcanenableShadowDOMbyvisitingtheabout:configpageandturningonthedom.webcomponents.enabledflag.

IE,Edge,andSafaridon'tsupportthisstandardatall;however,wecanteachthemhowtodealwithShadowDOMbyincludingapolyfillnamedwebcomponents.js.Youcanfindmoreinformationonthispolyfillathttps://github.com/webcomponents/webcomponentsjs.

www.EBooksWorld.ir

Page 401: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

IndexA

2alityURL/Classes

activityslidercomponentbuilding/Buildinganinteractiveactivityslidercomponentprojectionoftime/Projectionoftimeactivityindicators,rendering/Renderingactivityindicatorsimplementing/Bringingittolife,Recap

activitytimelinebuilding/Buildingtheactivitytimeline

Agilepluginbuilding/BuildinganAgilepluginAgiletaskinfocomponent/AgiletaskinfocomponentAgiletaskdetailscomponent/Agiletaskdetailscomponent

Angular2componentarchitecture/Angular'scomponentarchitecture

applicationrunning/Runningtheapplication,Recap

asterisksyntaxandtemplates/Theasterisksyntaxandtemplates

autocompletecomponent/Creatinganautocompletecomponent

www.EBooksWorld.ir

Page 402: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

BBehavior-drivenDevelopment(BDD)styletests/AnintroductiontoJasmineBootstrapping

about/Bootstrapping

www.EBooksWorld.ir

Page 403: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

CChartist

about/IntroductiontoChartistchartlegend

creating/Creatingachartlegendclasses,ECMAScript6

about/Classescommentingsystem

building/Buildingacommentingsystemcommentcomponent,building/Buildingthecommentcomponent,Buildingthecommentscomponent

componentarchitectureabout/Angular'scomponentarchitectureinAngular2/Everythingisacomponent

componentbuildertesting/Testcomponentbuilder

componentoutputsspyingon/Spyingoncomponentoutputs

componentstesting,utilitiesfor/Utilitiestotestcomponentsproviders/Utilitiestotestcomponentsinaction,testing/Testingcomponentsinactioninteraction,testing/Testingcomponentinteraction

components,userinterfacesencapsulation/Encapsulationcomposability/Composabilitycode,structuring/Components,inventedbynaturemetrics/MyUIframeworkwishliststandards/Timefornewstandardstemplateelements/TemplateelementsshadowDOM/ShadowDOM

composabilityabout/Composability

compositioncontentprojectionused/Compositionusingcontentprojectionbyrouting/Compositionbyrouting

containerabout/Components,inventedbynature

contentprojected,mixingwithgeneratedcontent/Mixingprojectedwithgeneratedcontent

contentprojectionused,forcomposition/Compositionusingcontentprojection

customUIelements

www.EBooksWorld.ir

Page 404: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

about/CustomUIelements,Recapcheckbox/CustomUIelementstogglebuttons/CustomUIelements

www.EBooksWorld.ir

Page 405: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Ddata

about/Data–Faketorealdecorators,ECMAScript6

about/Decoratorsdownloadlinks/Downloaddraganddrop

about/Draganddropdraggabledirective,implementing/Implementingthedraggabledirectivedroptargetdirective,implementing/Implementingadroptargetdirectiveintasklistcomponent,implementing/Integratingdraganddropintasklistcomponentrecapitulate/Recapitulateondraganddrop

draggabledirectiveimplementing/Implementingthedraggabledirective

droptargetdirectiveimplementing/Implementingadroptargetdirective

DSL/Pluginarchitecture

www.EBooksWorld.ir

Page 406: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

EECMAScript6

about/JavaScriptofthefuturefeatures/IspeakJavaScript,translate,please!classes/Classesmodules/Modulestemplatestrings/TemplatestringsversusTypeScript/ECMAScriptorTypeScript?decorators/Decorators

editorabout/Oneeditortorulethemallcomponent,creating/Creatinganeditorcomponent,Recap

editorcomponenttaginput,integrating/Integratingtaginputwithintheeditorcomponent

effortsmanaging/Managingeffortsestimatedduration/Managingeffortseffectiveduration/Managingeffortstimedurationinput/Thetimedurationinputmanaging,componentsfor/Componentstomanageeffortsdurationcomponent/Componentstomanageeffortscomponent/Componentstomanageeffortsvisualeffortstimeline/Thevisualeffortstimelinerecapitulatingon/Recapitulatingoneffortsmanagement

embeddedviewsadding/Addingandremovingembeddedviewsremoving/Addingandremovingembeddedviews

encapsulationabout/Encapsulation,Therightlevelofencapsulation,Recap

event-basedextensionpoints/Pluginarchitecture

www.EBooksWorld.ir

Page 407: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

GGangofFour(GoF)

about/Decorators

www.EBooksWorld.ir

Page 408: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Hhelloworldcomponent

writing/Yourfirstcomponentwriting,JavaScriptused/JavaScriptofthefuture

HTMLURL/BuildingSVGcomponents

www.EBooksWorld.ir

Page 409: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Iimmediatelyinvokedfunctionexpression(IIFE)

about/Modulesimmutability

about/Immutabilitybenefits/Immutability

IndexDBcleaning,torestdata/CleaningIndexDBtoresetdata

infinitescrolldirectivecreating/Creatinganinfinitescrolldirectivefinishing/Finishingourinfinitescrolldirective

inputgeneratingoutput/Inputgeneratesoutput,Recap

www.EBooksWorld.ir

Page 410: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

JJasmine

about/AnintroductiontoJasminecore/AnintroductiontoJasmineHTML/AnintroductiontoJasmineboot/AnintroductiontoJasminefirsttest,writing/Writingourfirsttest

JavaScriptusing/JavaScriptofthefutureECMAScript6/JavaScriptofthefuture

JSPMabout/SystemJSandJSPM,Startingfromscratchapplication,creating/GettingstartedwithJSPM

www.EBooksWorld.ir

Page 411: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Llifecyclehooks

URL/CustomUIelementslocalviewvariables/Inputgeneratesoutputloggingactivities

service,creatingfor/Creatingaserviceforloggingactivitiesabout/Loggingactivities

www.EBooksWorld.ir

Page 412: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

MMathML

URL/BuildingSVGcomponentsmilestones

setting/Settingmilestonesautocompletecomponent,creating/Creatinganautocompletecomponent

modules,ECMAScript6about/Modules

MozillaDeveloperURL/StylingSVG

MozillaDeveloperNetworkURL,fordocumentation/Modules

www.EBooksWorld.ir

Page 413: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

NNamespacesCrashCoursearticle

URL/BuildingSVGcomponentsnavigation

refactoring/Refactoringnavigation,SummaryNode.js

about/Node.jsandNPMURL/Node.jsandNPM

nodepackagemanager(NPM)about/Node.jsandNPM

NodeVersionManager(NVM)URL/GettingstartedwithJSPM

NPMstartscript/Usage

www.EBooksWorld.ir

Page 414: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Oobject-orientedprogramming(OOP)

about/Components–Theorgansofuserinterfacesobservabledatastructures

reactiveprogrammingwith/Reactiveprogrammingwithobservabledatastructuresopentasks

visualizing/Visualizingopentaskschart,creating/Creatinganopentaskschartchartlegend,creating/Creatingachartlegendtaskschart,makinginteractive/Makingtaskschartinteractive

www.EBooksWorld.ir

Page 415: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

PpluggableUIcomponents

about/PluggableUIcomponentspluginarchitecture,finalizing/Finalizingourpluginarchitecture

plugin,architectureabout/Pluginarchitectureextensibility/Pluginarchitectureportability/Pluginarchitecturecomposability/Pluginarchitecture

pluginAPIimplementing/ImplementingthepluginAPIplugincomponents,instantiating/Instantiatingplugincomponents

plugininterfaces/Pluginarchitectureplugins

managing/Managingpluginsnewplugins,loadingatruntime/Loadingnewpluginsatruntime

pluginsystemtesting/Testingourpluginsystem

PrecisionGraphicsMarkupLanguage(PGML)/LeveragingthepowerofSVGprerequisites/Prerequisitesprojectsdashboard

about/Projectsdashboardtaskschart/Projectsdashboardactivitychart/Projectsdashboardprojectsummary/Projectsdashboardcomponent,creating/Creatingtheprojectsdashboardcomponentsummarycomponent/Projectsummarycomponentfirstchart,creating/Creatingyourfirstchart

purecomponentsabout/Purecomponents

www.EBooksWorld.ir

Page 416: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Rreactiveprogramming

withobservabledatastructures/Reactiveprogrammingwithobservabledatastructures

routeabout/Backtotheroutesroutabletabs/Routabletabs

routerabout/AnintroductiontotheAngularrouterconfiguring/AnintroductiontotheAngularrouteroutlets/AnintroductiontotheAngularrouterlink/AnintroductiontotheAngularrouterversustemplatecomposition/Routerversustemplatecomposition

routetreeabout/Understandingtheroutetree

routingcompositionby/Compositionbyrouting

www.EBooksWorld.ir

Page 417: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Sservice

creating,forloggingactivities/Creatingaserviceforloggingactivitiesloggingactivities/Loggingactivities

ServiceProviderInterface(SPI)/PluginarchitectureshadowDOM

about/ShadowDOMSVG

about/LeveragingthepowerofSVGstyling/StylingSVGgraphicalstructure/StylingSVGvisualappearance/StylingSVGcomponents,building/BuildingSVGcomponentsURL/BuildingSVGcomponents

SynchronizedMultimediaIntegrationLanguage(SMIL)/BuildingSVGcomponentsSystemJS

about/SystemJSandJSPM

www.EBooksWorld.ir

Page 418: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Ttabbedinterfacecomponent

creating/Creatingatabbedinterfacecomponent,Recaptaginput

supporting/Supportingtaginputmanager,creating/Creatingataginputmanagertagsselectcomponent,creating/Creatingatagsselectcomponentintegrating,withineditorcomponent/Integratingtaginputwithintheeditorcomponenttaggingsystem,finishingup/Finishingupourtaggingsystem

tagmanagementabout/Tagmanagementtagdataentity/Tagdataentitytags,generating/Generatingtagstagsservice,creating/Creatingatagsservicetags,rendering/Renderingtagstaskservice,integrating/Integratingthetaskservicetagsservice,finishing/Completionofthetagsservice

tagsenabling,fortasks/Enablingtagsfortasks

tagsselectcomponentcreating/Creatingatagsselectcomponent

tagsservicecreating/Creatingatagsservicefinishing/Completionofthetagsservice

taskdetailsabout/Taskdetails

tasklistcreating/Creatingatasklist,Recappurifying/Purifyingourtasklist,Recap

tasksmanaging/Managingtasksvision/Visionfiltering/Filteringtaskstags,enablingfor/Enablingtagsfortasks

templatecompositionversusrouter/Routerversustemplatecomposition

templatedirectivechange,detecting/Detectingchangewithinourtemplatedirective

templateelementsabout/Templateelements

templatestrings,ECMAScript6about/Templatestrings

www.EBooksWorld.ir

Page 419: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

testsinjectingin/Injectingintests

timedurationinputabout/Thetimedurationinput

toolsabout/ToolsNode.js/Node.jsandNPMnodepackagemanager(NPM)/Node.jsandNPMSystemJS/SystemJSandJSPMJSPM/SystemJSandJSPM

troubleshooting/TroubleshootingTypeScript

versusECMAScript6/ECMAScriptorTypeScript?

www.EBooksWorld.ir

Page 420: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Uuserinterfaces

about/Thinkingoforganismscomponents/Components–Theorgansofuserinterfaces

www.EBooksWorld.ir

Page 421: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

VVectorMarkupLanguage(VML)/LeveragingthepowerofSVGvisualeffortstimeline/Thevisualeffortstimeline

www.EBooksWorld.ir

Page 422: Mastering Angular 2 Components - EBooksWorlddl.ebooksworld.ir/motoman/Packt.Mastering.Angular.2.Components.pdf · Chapter 2, Ready, Set, Go!, will get the reader started on their

Wwebcomponents

enabling,inFirefox/EnablingwebcomponentsinFirefoxwebcomponents.js

URL/Buildinganinteractiveactivityslidercomponent

www.EBooksWorld.ir