![Page 1: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/1.jpg)
![Page 2: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/2.jpg)
Angular2Cookbook
![Page 3: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/3.jpg)
TableofContents
Angular2CookbookCreditsAbouttheAuthorAbouttheReviewerwww.PacktPub.com
Whysubscribe?CustomerFeedbackDedicationPreface
WhatthisbookcoversWhatyouneedforthisbookWhothisbookisforConventionsReaderfeedbackCustomersupport
DownloadingtheexamplecodeErrataPiracyQuestions
1.StrategiesforUpgradingtoAngular2IntroductionComponentizingdirectivesusingcontrollerAsencapsulation
GettingreadyHowtodoit...Howitworks...There'smore...Seealso
MigratinganapplicationtocomponentdirectivesGettingreadyHowtodoit...Howitworks...There'smore...Seealso
ImplementingabasiccomponentinAngularJS1.5GettingreadyHowtodoit...Howitworks...There'smore...Seealso
![Page 4: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/4.jpg)
NormalizingservicetypesGettingreadyHowtodoit...Howitworks...There'smore...Seealso
ConnectingAngular1andAngular2withUpgradeModuleGettingreadyHowtodoit...
ConnectingAngular1toAngular2Howitworks...There'smore...Seealso
DowngradingAngular2componentstoAngular1directiveswithdowngradeComponentGettingreadyHowtodoit...Howitworks...Seealso
DowngradeAngular2providerstoAngular1serviceswithdowngradeInjectableGettingreadyHowtodoit...Seealso
2.ConqueringComponentsandDirectivesIntroductionUsingdecoratorstobuildandstyleasimplecomponent
GettingreadyHowtodoit...
WritingtheclassdefinitionWritingthecomponentclassdecorator
Howitworks...There'smore...Seealso
PassingmembersfromaparentcomponentintoachildcomponentGettingreadyHowtodoit...
ConnectingthecomponentsDeclaringinputs
Howitworks...There'smore...
AngularexpressionsUnidirectionaldatabindingMembermethods
![Page 5: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/5.jpg)
SeealsoBindingtonativeelementattributes
Howtodoit...Howitworks...Seealso
RegisteringhandlersonnativebrowsereventsGettingreadyHowtodoit...Howitworks...There'smore...Seealso
GeneratingandcapturingcustomeventsusingEventEmitterGettingreadyHowtodoit...
CapturingtheeventdataEmittingacustomeventListeningforcustomevents
Howitworks...There'smore...Seealso
AttachingbehaviortoDOMelementswithdirectivesGettingreadyHowtodoit...
AttachingtoeventswithHostListenersHowitworks...There'smore...Seealso
ProjectingnestedcontentusingngContentGettingreadyHowtodoit...Howitworks...There'smore...Seealso
UsingngForandngIfstructuraldirectivesformodel-basedDOMcontrolGettingreadyHowtodoit...Howitworks...There'smore...Seealso
ReferencingelementsusingtemplatevariablesGettingreadyHowtodoit...
![Page 6: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/6.jpg)
There'smore...Seealso
AttributepropertybindingGettingreadyHowtodoit...Howitworks...There'smore...Seealso
UtilizingcomponentlifecyclehooksGettingreadyHowtodoit...Howitworks...There'smore...Seealso
ReferencingaparentcomponentfromachildcomponentGettingreadyHowtodoit...Howitworks...There'smore...Seealso
Configuringmutualparent-childawarenesswithViewChildandforwardRefGettingreadyHowtodoit...
ConfiguringaViewChildreferenceCorrectingthedependencycyclewithforwardRefAddingthedisablebehavior
Howitworks...There'smore...
ViewChildrenSeealso
Configuringmutualparent-childawarenesswithContentChildandforwardRefGettingreadyHowtodoit...
ConvertingtoContentChildCorrectingdatabinding
Howitworks...There'smore...
ContentChildrenSeealso
3.BuildingTemplate-DrivenandReactiveFormsIntroductionImplementingsimpletwo-waydatabindingwithngModel
![Page 7: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/7.jpg)
Howtodoit...Howitworks...There'smore...Seealso
ImplementingbasicfieldvalidationwithaFormControlGettingreadyHowtodoit...Howitworks...There'smore...
ValidatorsandattributedualityTaglesscontrols
SeealsoBundlingcontrolswithaFormGroup
GettingreadyHowtodoit...Howitworks...There'smore...
FormGroupvalidatorsErrorpropagation
SeealsoBundlingFormControlswithaFormArray
GettingreadyHowtodoit...Howitworks...There'smore...Seealso
ImplementingbasicformswithNgFormGettingreadyHowtodoit...
DeclaringformfieldswithngModelHowitworks...There'smore...Seealso
ImplementingbasicformswithFormBuilderandformControlNameGettingreadyHowtodoit...Howitworks...There'smore...Seealso
CreatingandusingacustomvalidatorGettingreadyHowtodoit...
![Page 8: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/8.jpg)
Howitworks...There'smore...
RefactoringintovalidatorattributesSeealso
CreatingandusingacustomasynchronousvalidatorwithPromisesGettingreadyHowtodoit...Howitworks...There'smore...
ValidatorexecutionSeealso
4.MasteringPromisesIntroductionUnderstandingandimplementingbasicPromises
GettingreadyHowtodoit...Howitworks...There'smore...
DecoupledandduplicatedPromisecontrolResolvingaPromisetoavalueDelayedhandlerdefinitionMultiplehandlerdefinitionPrivatePromisemembers
SeealsoChainingPromisesandPromisehandlers
Howtodoit...Chainedhandlers'datahandoffRejectingachainedhandler
Howitworks...There'smore...
Promisehandlertreescatch()
SeealsoCreatingPromisewrapperswithPromise.resolve()andPromise.reject()
Howtodoit...Promisenormalization
Howitworks...There'smore...Seealso
ImplementingPromisebarrierswithPromise.all()Howtodoit...Howitworks...
![Page 9: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/9.jpg)
There'smore...Seealso
CancelingasynchronousactionswithPromise.race()GettingreadyHowtodoit...Howitworks...Seealso
ConvertingaPromiseintoanObservableHowtodoit...Howitworks...There'smore...Seealso
ConvertinganHTTPserviceObservableintoaZoneAwarePromiseGettingreadyHowtodoit...Howitworks...Seealso
5.ReactiveXObservablesIntroduction
TheObserverPatternReactiveXandRxJSObservablesinAngular2ObservablesandPromises
BasicutilizationofObservableswithHTTPGettingreadyHowtodoit...Howitworks...
Observable<Response>TheRxJSmap()operatorSubscribe
There'smore...HotandcoldObservables
SeealsoImplementingaPublish-SubscribemodelusingSubjects
GettingreadyHowtodoit...Howitworks...There'smore...
NativeRxJSimplementationSeealso
CreatinganObservableauthenticationserviceusingBehaviorSubjectsGettingready
![Page 10: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/10.jpg)
Howtodoit...InjectingtheauthenticationserviceAddingBehaviorSubjecttotheauthenticationserviceAddingAPImethodstotheauthenticationserviceWiringtheservicemethodsintothecomponent
Howitworks...There'smore...Seealso
BuildingageneralizedPublish-Subscribeservicetoreplace$broadcast,$emit,and$onGettingreadyHowtodoit...
IntroducingchannelabstractionHookingcomponentsintotheserviceUnsubscribingfromchannels
Howitworks...There'smore...
ConsiderationsofanObservable'scompositionandmanipulationSeealso
UsingQueryListsandObservablestofollowchangesinViewChildrenGettingreadyHowtodoit...
DealingwithQueryListsCorrectingtheexpressionchangederror
Howitworks...Hatetheplayer,notthegame
SeealsoBuildingafullyfeaturedAutoCompletewithObservables
GettingreadyHowtodoit...
UsingtheFormControlvalueChangesObservableDebouncingtheinputIgnoringserialduplicatesFlatteningObservablesHandlingunorderedresponses
Howitworks...Seealso
6.TheComponentRouterIntroductionSettingupanapplicationtosupportsimpleroutes
GettingreadyHowtodoit...
SettingthebaseURL
![Page 11: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/11.jpg)
DefiningroutesProvidingroutestotheapplicationRenderingroutecomponentswithRouterOutlet
Howitworks...There'smore...
InitialpageloadSeealso
NavigatingwithrouterLinksGettingreadyHowtodoit...Howitworks...There'smore...
RouteorderconsiderationsSeealso
NavigatingwiththeRouterserviceGettingreadyHowtodoit...Howitworks...There'smore...Seealso
SelectingaLocationStrategyforpathconstructionHowtodoit...There'smore...
ConfiguringyourapplicationserverforPathLocationStrategyBuildingstatefulroutebehaviorwithRouterLinkActive
GettingreadyHowtodoit...Howitworks...There'smore...Seealso
ImplementingnestedviewswithrouteparametersandchildroutesGettingreadyHowtodoit...
AddingaroutingtargettotheparentcomponentDefiningnestedchildviewsDefiningthechildroutesDefiningchildviewlinksExtractingrouteparameters
Howitworks...There'smore...
RefactoringwithasyncpipesSeealso
![Page 12: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/12.jpg)
WorkingwithmatrixURLparametersandroutingarraysGettingreadyHowtodoit...Howitworks...There'smore...Seealso
AddingrouteauthenticationcontrolswithrouteguardsGettingreadyHowtodoit...
ImplementingtheAuthserviceWiringuptheprofileviewRestrictingrouteaccesswithrouteguardsAddingloginbehaviorAddingthelogoutbehavior
Howitworks...There'smore...
TheactualauthenticationSecuredataandviews
Seealso7.Services,DependencyInjection,andNgModule
IntroductionInjectingasimpleserviceintoacomponent
GettingreadyHowtodoit...Howitworks...There'smore...Seealso
ControllingserviceinstancecreationandinjectionwithNgModuleGettingreadyHowtodoit...
SplittinguptherootmoduleHowitworks...There'smore...
InjectingdifferentserviceinstancesintodifferentcomponentsServiceinstantiation
SeealsoServiceinjectionaliasingwithuseClassanduseExisting
GettingreadyDualservicesAunifiedcomponent
Howtodoit...Howitworks...
![Page 13: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/13.jpg)
There'smore...Refactoringwithdirectiveproviders
SeealsoInjectingavalueasaservicewithuseValueandOpaqueTokens
GettingreadyHowtodoit...Howitworks...There'smore...Seealso
Buildingaprovider-configuredservicewithuseFactoryGettingreadyHowtodoit...
DefiningthefactoryInjectingOpaqueTokenCreatingproviderdirectiveswithuseFactory
Howitworks...There'smore...Seealso
8.ApplicationOrganizationandManagementIntroductionComposingpackage.jsonforaminimumviableAngular2application
GettingreadyHowtodoit...
package.jsondependenciespackage.jsondevDependenciespackage.jsonscripts
SeealsoConfiguringTypeScriptforaminimumviableAngular2application
GettingreadyHowtodoit...
Declarationfilestsconfig.json
Howitworks...Compilation
There'smore...SourcemapgenerationSinglefilecompilation
SeealsoPerformingin-browsertranspilationwithSystemJS
GettingreadyHowtodoit...Howitworks...
![Page 14: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/14.jpg)
There'smore...Seealso
ComposingapplicationfilesforaminimumviableAngular2applicationGettingreadyHowtodoit...
app.component.tsapp.module.tsmain.tsindex.htmlConfiguringSystemJS
SeealsoMigratingtheminimumviableapplicationtoWebpackbundling
GettingreadyHowtodoit...
webpack.config.jsSeealso
IncorporatingshimsandpolyfillsintoWebpackGettingreadyHowtodoit...Howitworks...Seealso
HTMLgenerationwithhtml-webpack-pluginGettingreadyHowtodoit...Howitworks...Seealso
SettingupanapplicationwithAngularCLIGettingreadyHowtodoit...
RunningtheapplicationlocallyTestingtheapplication
Howitworks...ProjectconfigurationfilesTypeScriptconfigurationfilesTestconfigurationfilesCoreapplicationfilesEnvironmentfilesAppComponentfilesAppComponenttestfiles
There'smore...Seealso
9.Angular2Testing
![Page 15: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/15.jpg)
IntroductionCreatingaminimumviableunittestsuitewithKarma,Jasmine,andTypeScript
GettingreadyHowtodoit...
WritingaunittestConfiguringKarmaandJasmineConfiguringPhantomJSCompilingfilesandtestswithTypeScriptIncorporatingWebpackintoKarmaWritingthetestscript
Howitworks...There'smore...Seealso
WritingaminimumviableunittestsuiteforasimplecomponentGettingreadyHowtodoit...
UsingTestBedandasyncCreatingaComponentFixture
Howitworks...Seealso
Writingaminimumviableend-to-endtestsuiteforasimpleapplicationGettingreadyHowtodoit...
GettingProtractorupandrunningMakingProtractorcompatiblewithJasmineandTypeScriptBuildingapageobjectWritingthee2etestScriptingthee2etests
Howitworks...There'smore...Seealso
UnittestingasynchronousserviceGettingreadyHowtodoit...Howitworks...There'smore...
TestingwithoutinjectionSeealso
UnittestingacomponentwithaservicedependencyusingstubsGettingreadyHowtodoit...
Stubbingaservicedependency
![Page 16: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/16.jpg)
TriggeringeventsinsidethecomponentfixtureHowitworks...Seealso
UnittestingacomponentwithaservicedependencyusingspiesGettingreadyHowtodoit...
SettingaspyontheinjectedserviceHowitworks...There'smore...Seealso
10.PerformanceandAdvancedConceptsIntroductionUnderstandingandproperlyutilizingenableProdModewithpureandimpurepipes
GettingreadyHowtodoit...
GeneratingaconsistencyerrorIntroducingchangedetectioncomplianceSwitchingonenableProdMode
Howitworks...There'smore...Seealso
WorkingwithzonesoutsideAngularGettingreadyHowtodoit...
ForkingazoneOverridingzoneeventswithZoneSpec
Howitworks...There'smore...
Understandingzone.run()Microtasksandmacrotasks
SeealsoListeningforNgZoneevents
zone.jsNgZoneGettingreadyHowtodoit...
DemonstratingthezonelifecycleHowitworks...
Theutilityofzone.jsSeealso
ExecutionoutsidetheAngularzoneHowtodoit...
![Page 17: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/17.jpg)
Howitworks...There'smore...Seealso
ConfiguringcomponentstouseexplicitchangedetectionwithOnPushGettingreadyHowtodoit...
ConfiguringtheChangeDetectionStrategyRequestingexplicitchangedetection
Howitworks...There'smore...Seealso
ConfiguringViewEncapsulationformaximumefficiencyGettingreadyHowtodoit...
EmulatedstylingencapsulationNostylingencapsulationNativestylingencapsulation
Howitworks...There'smore...Seealso
ConfiguringtheAngular2RenderertousewebworkersGettingreadyHowtodoit...Howitworks...There'smore...
OptimizingforperformancegainsCompatibilityconsiderations
SeealsoConfiguringapplicationstouseahead-of-timecompilation
GettingreadyHowtodoit...
InstallingAOTdependenciesConfiguringngcAligningcomponentdefinitionswithAOTrequirementsCompilingwithngcBootstrappingwithAOT
Howitworks...There'smore...
GoingfurtherwithTreeShakingSeealso
ConfiguringanapplicationtouselazyloadingGettingready
![Page 18: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/18.jpg)
Howtodoit...Howitworks...There'smore...
AccountingforsharedmodulesSeealso
![Page 19: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/19.jpg)
Angular2Cookbook
![Page 20: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/20.jpg)
Angular2CookbookCopyright©2017PacktPublishing
Allrightsreserved.Nopartofthisbookmaybereproduced,storedinaretrievalsystem,ortransmittedinanyformorbyanymeans,withoutthepriorwrittenpermissionofthepublisher,exceptinthecaseofbriefquotationsembeddedincriticalarticlesorreviews.
Everyefforthasbeenmadeinthepreparationofthisbooktoensuretheaccuracyoftheinformationpresented.However,theinformationcontainedinthisbookissoldwithoutwarranty,eitherexpressorimplied.Neithertheauthor,norPacktPublishing,anditsdealersanddistributorswillbeheldliableforanydamagescausedorallegedtobecauseddirectlyorindirectlybythisbook.
PacktPublishinghasendeavoredtoprovidetrademarkinformationaboutallofthecompaniesandproductsmentionedinthisbookbytheappropriateuseofcapitals.However,PacktPublishingcannotguaranteetheaccuracyofthisinformation.
Firstpublished:January2017
Productionreference:1160117
PublishedbyPacktPublishingLtd.
LiveryPlace
35LiveryStreet
Birmingham
B32PB,UK.
ISBN978-1-78588-192-3
www.packtpub.com
![Page 21: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/21.jpg)
CreditsAuthor
MattFrisbie
ProjectCoordinator
RitikaManoj
Reviewer
PatrickGillespie
Proofreader
SafisEditing
AcquisitionEditor
VinayArgekar
Indexer
FrancyPuthiry
ContentDevelopmentEditor
ArunNadar
Graphics
KirkD'Penha
TechnicalEditor
VivekArora
ProductionCoordinator
DeepikaNaik
CopyEditor
GladsonMonteiro
CoverWork
DeepikaNaik
![Page 22: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/22.jpg)
AbouttheAuthorMattFrisbieiscurrentlyasoftwareengineeratGoogle.HewastheauthorofthePacktPublishingbestsellerAngularJSWebApplicationDevelopmentCookbookandalsohaspublishedseveralvideoseriesthroughO'Reilly.HeisactiveintheAngularcommunity,givingpresentationsatmeetupsanddoingwebcasts.
WritingabookonAngular2whiletheframeworkitselfwasunfinishedwasanimmenselychallengingendeavor.Fragmentedexamples,incompletedocumentation,andanascentdevelopercommunitywerejustahandfulofthemanyroadblocksIencounteredonthejourneytofinishingthistitle,anditwasonlybecauseofalegionofsupportersthatthisbookwasfinishedandwasabletodojusticetotheframework.
ThisbookwouldnothavebeenpossiblewithoutthetirelessworkofallthePacktstaffinvolved.I'dspecificallyliketothankArunNadar,VivekArora,MerwynD'Souza,andVinayArgekarfortheireditorialoversightandexpertise,aswellasPatrickGillespieforhisworkascontentreviewer.I'dalsoliketothankJordan,Zoey,Scott,andmyfamilyandfriendsforcheeringmeon.
![Page 23: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/23.jpg)
AbouttheReviewerPatrickGillespiehasbeenintosoftwaredevelopmentsince1996.Hehasbothabachelor'sandamaster'sdegreeincomputerscience.Inhissparetime,heenjoysphotography,spendingtimewithhisfamily,andworkingonvarioussideprojectsforhiswebsite(http://patorjk.com/).
![Page 24: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/24.jpg)
www.PacktPub.comForsupportfilesanddownloadsrelatedtoyourbook,pleasevisitwww.PacktPub.com.
DidyouknowthatPacktofferseBookversionsofeverybookpublished,withPDFandePubfilesavailable?YoucanupgradetotheeBookversionatwww.PacktPub.comandasaprintbookcustomer,youareentitledtoadiscountontheeBookcopy.Getintouchwithusatservice@packtpub.comformoredetails.
Atwww.PacktPub.com,youcanalsoreadacollectionoffreetechnicalarticles,signupforarangeoffreenewslettersandreceiveexclusivediscountsandoffersonPacktbooksandeBooks.
https://www.packtpub.com/mapt
Getthemostin-demandsoftwareskillswithMapt.MaptgivesyoufullaccesstoallPacktbooksandvideocourses,aswellasindustry-leadingtoolstohelpyouplanyourpersonaldevelopmentandadvanceyourcareer.
![Page 25: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/25.jpg)
Whysubscribe?FullysearchableacrosseverybookpublishedbyPacktCopyandpaste,print,andbookmarkcontentOndemandandaccessibleviaawebbrowser
![Page 26: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/26.jpg)
CustomerFeedbackThankyouforpurchasingthisPacktbook.Wetakeourcommitmenttoimprovingourcontentandproductstomeetyourneedsseriously—that'swhyyourfeedbackissovaluable.Whateveryourfeelingsaboutyourpurchase,pleaseconsiderleavingareviewonthisbook'sAmazonpage.Notonlywillthishelpus,moreimportantlyitwillalsohelpothersinthecommunitytomakeaninformeddecisionabouttheresourcesthattheyinvestintolearn.
Youcanalsoreviewforusonaregularbasisbyjoiningourreviewers'club.Ifyou'reinterestedinjoining,orwouldliketolearnmoreaboutthebenefitsweoffer,pleasecontactus:[email protected].
![Page 27: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/27.jpg)
DedicationTomygrandparents,RichardandMargery.Here'stoupholdingthefamilyhonor.
![Page 28: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/28.jpg)
Preface"Everybodyhasaplanuntiltheygetpunchedinthemouth."-MikeTyson,undisputedheavyweightchampionboxer
Soonafteritscreationin2009,AngularJSgrewintoawidelypopularfoundationaltoolforbuildingfrontendapplications.Asyearsandreleaseswentby,andtheJavaScriptcommunitymatured,theworldofclient-sideprogrammingbroadenedbeyondwhatAngularwasoriginallydesignedfor.Itscaretakerstookstockanddecidedthatasweepingoverhauloftheframeworkwasinorder.
AngularJS,nowAngular1,stillexistsandwillbesupportedfortheyearstocome,butinitswakeliesAngular2—awhollydifferentanimalbuiltforthefutureofclient-sidecomputing.Angular2abandonsantipatternsbythefistfuland,instead,isreshapedintoapreciseandelegantsoftwareinstrument.Itembracestheimpendingrenaissanceofwebtechnologies,buildingatopES6,webcomponents,webworkers,TypeScript,andreactiveprogramming,tonameafew.Itbringsframeworkmodularitytonewheights,buildingitselfaroundtheconceptthatanymodularpieceofAngular2shouldbeeasilydiscardedorreplaced.Bestofall,Angular2offersabountifulcollectionofconfigurationandtoolingthatwillmakeyourapplicationsrunatbreakneckspeed.
Tomanydevelopers,Angular2isfrighteningbecausesomuchofitisnewandunfamiliar.ThisbookexiststoofferyouanapproachablepathtoafullunderstandingofAngular2,whatitoffers,andhowbesttouseit.Youwillfindbothsimpleexamplestosetafoundationalunderstanding,andcomplexdemonstrationstohintattheframework'spower.Thebookisorganizedintorecipesthatareindependentofeachother,soyouareabletojumpinatanypointandimmediatelybeginlearning.
![Page 29: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/29.jpg)
WhatthisbookcoversThisbookisuptodateforthe2.4releaseandiscompatiblethroughthe4.0releaseaswell,anditdoesnothaveanycodebasedonthebetaorreleasecandidates.
Chapter1,StrategiesforUpgradingtoAngular2,isanoverviewofanumberofwaystomigrateanAngular1applicationtoAngular2.Althoughthereisnoone-size-fits-allupgradestrategy,youwillfindthattheserecipesdemonstratesomewaysthatwillallowyoutopreservealargeamountofyourexistingAngular1codebase.
Chapter2,ConqueringComponentsandDirectives,givesabroadanddeepsetofexamplesinvolvingwhatAngular2componentsareandhowtousethem.Angular2applicationsarebuiltentirelyofcomponents,andthischapteroffersyouatotalrundownoftheirrole.
Chapter3,BuildingTemplate-DrivenandReactiveForms,coversthereworkedAngular2formmodules.Angular2offersyoutwoprimarystylesoferectingformfeatures,andthischaptercoversbothofthemindepth.
Chapter4,MasteringPromises,showshowthePromiseobjecthasaroleinAngular2.AlthoughRxJShassubsumedsomeoftheusefulnessofPromises,theyarestillfirst-classcitizensinES6andstillplayacrucialrole.
Chapter5,ReactiveXObservables,givesyouacrashcourseinhowAngular2hasembracedreactiveprogramming.ItincludesrecipesthatdemonstratethebasicsofObservablesandSubjects,aswellasadvancedimplementationsthattakeRxJStoitslimits.
Chapter6,TheComponentRouter,takesyouthroughthetotallyreworkedroutingmoduleinAngular2.ItcoversbothroutingbasicsaswellasanarrayofadvancedroutingconceptsuniquetoAngular2.
Chapter7,Services,DependencyInjection,andNgModule,describesthenewandimproveddependencyinjectionandmodulestrategiesofAngular2.Itgivesyouallthepiecesyouneedtobreakyourapplicationintoindependentservicesandmodules,aswellasidealstrategiesforconnectingthosepiecestogether.
Chapter8,ApplicationOrganizationandManagement,isabroadoverviewofhowyoucanmanageyourAngular2applicationinsideandoutsidetheclient.Angular2introducesanumberoflayersofcomplexitythatrequireadvancedtooling,andthischapterwillguideyouthroughhowtoapproachthem.
Chapter9,Angular2Testing,willguideyouthroughbothhowtosetuptestsuitesforAngular2aswellashowtowritevarioustypesoftestsforthesesuites.Manydevelopersavoidtestingwhenlearningaframeworkanew,andthischaptergentlyguidesyouthroughAngular2'sexcellent
![Page 30: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/30.jpg)
testinfrastructure.
Chapter10,PerformanceandAdvancedConcepts,isacrashcourseonthedizzyingarrayofcomplexconceptsthatAngular2comeswithoutofthebox.Thischaptercoversprogramorganizationandarchitecture,frameworkfeaturesandtooling,aswellascompile-timeoptimizations.
![Page 31: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/31.jpg)
WhatyouneedforthisbookEveryrecipeinthisbookisaccompaniedbyalinktothebook'scompanionsite,http://ngcookbook.herokuapp.com/.RecipesthatinvolvecodeexampleswillincludealinktoaliveexampleonPlunker.Thiswillallowyoutoinspectandtestcodeinrealtimewithouthavingtoworryaboutcompilation,localservers,oranythingofthatilk.Itmustbenoted,however,thatthissetupisonlyappropriateforexperimentationandshouldnotbeusedforuser-facingorproductionapplications.
Angular2comesinbothJavaScriptandTypeScriptflavors,butthisbookaimsdirectlyattheTypeScriptedition,sinceitissyntacticallysuperior(asyouwillsoonrealize).Forproperproductionapplications,TypeScriptwillbecompiledintoJavaScriptbeforeitisservedtothebrowser.Thewaythisbookaccomplishesthis(andmanyothercodepreparationtasks)isinsideaNode.jsinstallonyourlocalmachine.Node.jsincludestheNodePackageManager(npm),whichletsyouinstallandrunopensourceJavaScriptsoftwarefromthecommandline.
SomechaptersinthisbookwillrequirethatyouhaveNode.jsinstalledbeforerunningcommandsandlaunchingalocalserverortestsuite.Furthermore,itisrecommended(butnotrequired)thatyouinstalltheNodeVersionManagerontopofNode.js,whichwillmakemanagingyourinstalledpackagesmucheasier.
![Page 32: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/32.jpg)
WhothisbookisforTheuniverseofAngular2learningmaterialsiscurrentlyfragmentedandgross.Thisbookisforbothbeginnerdeveloperslookingtosinktheirteethintoanewframework,aswellasadvanceddevelopersinterestedinroundingouttheirknowledgeofaframeworkthatembracesthecomingworldofwebtech.
Fornewerdevelopers,ingestingallthesenewtechnologiesatoncemayseemoverwhelming.Theorganizationandpaceofthisbookisdesignedsothattopicsaregraduallyintroduced,anddesigndecisionsandrationalesareexplained.Don'tworry,thisbookisstillforyou.
![Page 33: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/33.jpg)
ConventionsInthisbook,youwillfindanumberoftextstylesthatdistinguishbetweendifferentkindsofinformation.Herearesomeexamplesofthesestylesandanexplanationoftheirmeaning.
Codewordsintext,databasetablenames,foldernames,filenames,fileextensions,pathnames,dummyURLs,userinput,andTwitterhandlesareshownasfollows:"Karmareadsitsconfigurationoutofakarma.conf.jsfile."
Ablockofcodeissetasfollows:
<p>{{date}}</p>
<h1>{{title}}</h1>
<h3>Writtenby:{{author}}</h3>
Whenwewishtodrawyourattentiontoaparticularpartofacodeblock,therelevantlinesoritemsaresetinbold:
@Component({
selector:'article',
template:`
<p>{{currentDate|date}}</p>
<h1>{{title}}</h1>
<h3>Writtenby:{{author}}</h3>
`
})
Anycommand-lineinputoroutputiswrittenasfollows:
npminstallkarmajasmine-corekarma-jasmine--save-dev
npminstallkarma-cli-g
Newtermsandimportantwordsareshowninbold.
Note
Warningsorimportantnotesappearinaboxlikethis.
Tip
Tipsandtricksappearlikethis.
![Page 34: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/34.jpg)
ReaderfeedbackFeedbackfromourreadersisalwayswelcome.Letusknowwhatyouthinkaboutthisbook-whatyoulikedordisliked.Readerfeedbackisimportantforusasithelpsusdeveloptitlesthatyouwillreallygetthemostoutof.Tosendusgeneralfeedback,[email protected],andmentionthebook'stitleinthesubjectofyourmessage.Ifthereisatopicthatyouhaveexpertiseinandyouareinterestedineitherwritingorcontributingtoabook,seeourauthorguideatwww.packtpub.com/authors.
![Page 35: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/35.jpg)
CustomersupportNowthatyouaretheproudownerofaPacktbook,wehaveanumberofthingstohelpyoutogetthemostfromyourpurchase.
![Page 36: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/36.jpg)
DownloadingtheexamplecodeYoucandownloadtheexamplecodefilesforthisbookfromyouraccountathttp://www.packtpub.com.Ifyoupurchasedthisbookelsewhere,youcanvisithttp://www.packtpub.com/supportandregistertohavethefilese-maileddirectlytoyou.
Youcandownloadthecodefilesbyfollowingthesesteps:
1. Loginorregistertoourwebsiteusingyoure-mailaddressandpassword.2. HoverthemousepointerontheSUPPORTtabatthetop.3. ClickonCodeDownloads&Errata.4. EnterthenameofthebookintheSearchbox.5. Selectthebookforwhichyou'relookingtodownloadthecodefiles.6. Choosefromthedrop-downmenuwhereyoupurchasedthisbookfrom.7. ClickonCodeDownload.
Oncethefileisdownloaded,pleasemakesurethatyouunziporextractthefolderusingthelatestversionof:
WinRAR/7-ZipforWindowsZipeg/iZip/UnRarXforMac7-Zip/PeaZipforLinux
ThecodebundleforthebookisalsohostedonGitHubathttps://github.com/PacktPublishing/Angular-2-Cookbook.Wealsohaveothercodebundlesfromourrichcatalogofbooksandvideosavailableathttps://github.com/PacktPublishing/.Checkthemout!
![Page 37: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/37.jpg)
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.
![Page 38: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/38.jpg)
PiracyPiracyofcopyrightedmaterialontheInternetisanongoingproblemacrossallmedia.AtPackt,wetaketheprotectionofourcopyrightandlicensesveryseriously.IfyoucomeacrossanyillegalcopiesofourworksinanyformontheInternet,pleaseprovideuswiththelocationaddressorwebsitenameimmediatelysothatwecanpursuearemedy.
Pleasecontactusatcopyright@packtpub.comwithalinktothesuspectedpiratedmaterial.
Weappreciateyourhelpinprotectingourauthorsandourabilitytobringyouvaluablecontent.
![Page 39: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/39.jpg)
QuestionsIfyouhaveaproblemwithanyaspectofthisbook,[email protected],andwewilldoourbesttoaddresstheproblem.
![Page 40: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/40.jpg)
Chapter1.StrategiesforUpgradingtoAngular2Thischapterwillcoverthefollowingrecipes:
ComponentizingdirectivesusingthecontrollerAsencapsulationMigratinganapplicationtocomponentdirectivesImplementingabasiccomponentinAngularJS1.5NormalizingservicetypesConnectingAngular1andAngular2withUpgradeModuleDowngradingAngular2componentstoAngular1directiveswithdowngradeComponentDowngradingAngular2providerstoAngular1serviceswithdowngradeInjectable
![Page 41: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/41.jpg)
IntroductionTheintroductionofAngular2intotheAngularecosystemwillsurelybeinterpretedandhandleddifferentlyforalldevelopers.SomewillsticktotheirexistingAngular1codebases,somewillstartbrandnewAngular2codebases,andsomewilldoagradualorpartialtransition.
ItisrecommendedthatyoubecomefamiliarwiththebehaviorofAngular2componentsbeforeyoudiveintotheserecipes.ThiswillhelpyouframementalmodelsasyouadaptyourexistingapplicationstobemorecompliantwiththeAngular2style.
![Page 42: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/42.jpg)
ComponentizingdirectivesusingcontrollerAsencapsulationOneoftheunusualconventionsintroducedinAngular1wastherelationshipbetweendirectivesandthedatatheyconsumed.Bydefault,directivesusedaninheritedscope,whichsuitedtheneedsofmostdevelopersjustfine.Whilethiswaseasytouse,ithadtheeffectofintroducingextradependenciesinthedirectives,andalsotheconventionthatdirectivesoftendidnotownthedatatheywereconsuming.Additionally,thedatainterpolatedinthetemplatewasunclearinrelationtowhereitwasbeingassignedorowned.
Angular2utilizescomponentsasthebuildingblocksoftheentireapplication.Thesecomponentsareclass-basedandarethereforeinsomewaysatoddswiththescopemechanismsofAngular1.Transitioningtoacontroller-centricdirectivemodelisalargesteptowardscompliancewiththeAngular2standards.
Note
Thecode,links,andaliveexamplerelatedtothisrecipeareavailableathttp://ngcookbook.herokuapp.com/8194.
![Page 43: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/43.jpg)
GettingreadySupposeyourapplicationcontainsthefollowingsetupthatinvolvesthenesteddirectivesthatsharedatausinganisolatescope:
[index.html]
<divng-app="articleApp">
<article></article>
</div>
[app.js]
angular.module('articleApp',[])
.directive('article',function(){
return{
controller:function($scope){
$scope.articleData={
person:{firstName:'Jake'},
title:'LesothoYachtClubMembershipBooms'
};
},
template:`
<h1>{{articleData.title}}</h1>
<attributionauthor="articleData.person.firstName">
</attribution>
`
};
})
.directive('attribution',function(){
return{
scope:{author:'='},
template:`<p>Writtenby:{{author}}</p>`
};
});
![Page 44: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/44.jpg)
Howtodoit...Thegoalistorefactorthissetupsothattemplatescanbeexplicitaboutwherethedataiscomingfromandsothatthedirectiveshaveownershipofthisdata:
[app.js]
angular.module('articleApp',[])
.directive('article',function(){
return{
controller:function(){
this.person={firstName:'Jake'};
this.title='LesothoYachtClubMembershipBooms';
},
controllerAs:'articleCtrl',
template:`
<h1>{{articleCtrl.title}}</h1>
<attribution></attribution>
`
};
})
.directive('attribution',function(){
return{
template:`<p>Writtenby:{{articleCtrl.author}}</p>`
};
});
Inthissecondimplementation,anywhereyouusethearticledata,youarecertainofitsorigin.Thisisbetter,butthechilddirectiveisstillreferencingtheparentcontroller,whichisn'tidealsinceitisintroducinganunneededdependency.Theattributiondirectiveinstanceshouldbeprovidedwiththedata,anditshouldinsteadinterpolatefromitsowncontrollerinstance:
[app.js]
angular.module('articleApp',[])
.directive('article',function(){
return{
controller:function(){
this.person={firstName:'Jake'};
this.title='LesothoYachtClubMembershipBooms';
},
controllerAs:'articleCtrl',
template:`
<h1>{{articleCtrl.title}}</h1>
<attributionauthor="articleCtrl.person.firstName">
</attribution>
`
};
})
.directive('attribution',function(){
return{
![Page 45: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/45.jpg)
controller:function(){},
controllerAs:'attributionCtrl',
bindToController:{author:'='},
template:`<p>Writtenby:{{attributionCtrl.author}}</p>`
};
});
Muchbetter!Youprovidethechilddirectivewithastand-incontrollerandgiveitanaliasintheattributionCtrltemplate.ItisimplicitlyboundtothecontrollerinstanceviabindToControllerinthesamewayyouwouldaccomplisharegularisolatescope;however,thebindingisdirectlyattributedtothecontrollerobjectinsteadofthescope.
Nowthatyouhaveintroducedthenotionofdataownership,supposeyouwanttomodifyyourapplicationdata.What'smore,youwantdifferentpartsofyourapplicationtobeabletomodifyit.Anaïveimplementationofthiswouldbesomethingasfollows:
[app.js]
angular.module('articleApp',[])
.directive('attribution',function(){
return{
controller:function(){
this.capitalize=function(){
this.author=this.author.toUpperCase();
}
},
controllerAs:'attributionCtrl',
bindToController:{author:'='},
template:`
<png-click="attributionCtrl.capitalize()">
Writtenby:{{attributionCtrl.author}}
</p>`
};
});
Thedesiredbehaviorisforyoutoclickontheauthor,anditwillbecomecapitalized.However,inthisimplementation,thearticlecontroller'sdataismodifiedintheattributioncontroller,whichdoesnotownit.Itispreferableforthecontrollerthatownsthedatatoperformtheactualmodificationandinsteadsupplyaninterfacethatanoutsideentity—here,theattributiondirective—coulduse:
[app.js]
angular.module('articleApp',[])
.directive('article',function(){
return{
controller:function(){
this.person={firstName:'Jake'};
this.title='LesothoYachtClubMembershipBooms';
this.capitalize=function(){
![Page 46: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/46.jpg)
this.person.firstName=
this.person.firstName.toUpperCase();
};
},
controllerAs:'articleCtrl',
template:`
<h1>{{articleCtrl.title}}</h1>
<attributionauthor="articleCtrl.person.firstName"
upper-case-author="articleCtrl.capitalize()">
</attribution>
`
};
})
.directive('attribution',function(){
return{
controller:function(){},
controllerAs:'attributionCtrl',
bindToController:{
author:'=',
upperCaseAuthor:'&'
},
template:`
<png-click="attributionCtrl.upperCaseAuthor()">
Writtenby:{{attributionCtrl.author}}
</p>`
};
});
Vastlysuperior!Youarestillabletonamespacewithintheclickbinding,buttheowningdirectivecontrollerisprovidingamethodtooutsideentitiesinsteadofjustgivingthemdirectdataaccess.
![Page 47: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/47.jpg)
Howitworks...Whenacontrollerisspecifiedinthedirectivedefinitionobject,onewillbeexplicitlyinstantiatedforeachdirectiveinstancethatiscreated.Thus,itisnaturalforthiscontrollerobjecttoencapsulatethedatathatitownsandforittobedelegatedtheresponsibilityofpassingitsdatatothemembersthatrequireit.
Thefinalimplementationaccomplishesseveralthings:
Improvedtemplatenamespacing:Whenyouusethe$scopepropertiesthatspanmultipledirectivesornestings,youarecreatingascenariowheremultipleentitiescanmanipulateandreaddatawithoutbeingabletoconcretelyreasonaboutwhereitiscomingfromorwhatiscontrollingit.Improvedtestability:Ifyoulookateachofthedirectivesinthefinalimplementation,you'llfindtheyarenottoodifficulttotest.Theattributiondirectivehasnodependenciesotherthanwhatareexplicitlypassedtoit.Encapsulation:Introducingthenotionofdataownershipinyourapplicationaffordsyouamuchmorerobuststructure,betterreusability,andadditionalinsightandcontrolinvolvingpiecesofyourapplicationinteracting.Angular2style:Angular2usesthe@Inputand@Outputannotationsoncomponentdefinitions.Mirroringthisstylewillmaketheprocessoftransitioningtoanapplicationeasier.
![Page 48: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/48.jpg)
There'smore...Youwillnoticethat$scopehasbeenmadetotallyirrelevantintheseexamples.Thisisgoodasthereisnonotionof$scopeinAngular2,whichmeansyouareheadingtowardshavinganupgradeableapplication.Thisisnottosaythat$scopedoesnotstillhaveutilityinanAngular1application,andsurely,therearescenarioswherethisisunavoidable,likewith$scope.$apply().
However,thinkingabouttheapplicationpiecesinthiscomponentstylewillallowyoutobemoreadequatelypreparedtoadoptAngular2conventions.
![Page 49: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/49.jpg)
SeealsoMigratinganapplicationtocomponentdirectivesdemonstrateshowtorefactorAngular1toacomponentstyleImplementingabasiccomponentinAngularJS1.5detailshowtowriteanAngular1componentNormalizingservicetypesgivesinstructiononhowtoalignyourAngular1servicetypesforAngular2compatibility
![Page 50: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/50.jpg)
MigratinganapplicationtocomponentdirectivesInAngular1,thereareseveralbuilt-indirectives,includingngControllerandngInclude,thatdeveloperstendtoleanonwhenbuildingapplications.Whilenotanti-patterns,usingthesefeaturesmovesawayfromhavingacomponent-centricapplication.
Allthesedirectivesareactuallysubsetsofcomponentfunctionality,andtheycanbeentirelyrefactoredout.
Note
Thecode,links,andaliveexamplerelatedtothisrecipeareavailableathttp://ngcookbook.herokuapp.com/1008/.
![Page 51: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/51.jpg)
GettingreadySupposeyourinitialapplicationisasfollows:
[index.html]
<divng-app="articleApp">
<ng-includesrc="'/press_header.html'"></ng-include>
<divng-controller="articleCtrlasarticle">
<h1>{{article.title}}</h1>
<p>Writtenby:{{article.author}}</p>
</div>
<scripttype="text/ng-template"
id="/press_header.html">
<divng-controller="headerCtrlasheader">
<strong>
AngularChronicle-{{header.currentDate|date}}
</strong>
<hr/>
</div>
</script>
</div>
[app.js]
angular.module('articleApp',[])
.controller('articleCtrl',function(){
this.title='FoodFightEruptsDuringDiplomaticLuncheon';
this.author='Jake';
})
.controller('headerCtrl',function(){
this.currentDate=newDate();
});
Note
NotethatthisexampleapplicationcontainsalargenumberofverycommonAngular1patterns;youcanseethengControllerdirectivessprinkledthroughout.Also,itusesanngIncludedirectivetoincorporateaheader.Keepinmindthatthesedirectivesarenotinappropriateforawell-formedAngular1application.However,youcandobetter,andthisinvolvesrefactoringtoacomponent-drivendesign.
![Page 52: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/52.jpg)
Howtodoit...Component-drivenpatternsdon'tneedtobefrighteninginappearance.Inthisexample(andforessentiallyallAngular1applications),youcandoacomponentrefactorwhileleavingtheexistingtemplatelargelyintact.
BeginwiththengIncludedirective.Movingthistoacomponentdirectiveissimple—itbecomesadirectivewithtemplateUrlsettothetemplatepath:
[index.html]
<divng-app="articleApp">
<header></header>
<divng-controller="articleCtrlasarticle">
<h1>{{article.title}}</h1>
<p>Writtenby:{{article.author}}</p>
</div>
<scripttype="text/ng-template"
id="/press_header.html">
<divng-controller="headerCtrlasheader">
<strong>
AngularChronicle-{{header.currentDate|date}}
</strong>
<hr/>
</div>
</script>
</div>
[app.js]
angular.module('articleApp',[])
.controller('articleCtrl',function(){
this.title='FoodFightEruptsDuringDiplomaticLuncheon';
this.author='Jake';
})
.controller('headerCtrl',function(){
this.currentDate=newDate();
})
.directive('header',function(){
return{
templateUrl:'/press_header.html'
};
});
Next,youcanalsorefactorngControllereverywhereitappears.Inthisexample,youfindtwoextremelycommonappearancesofngController.Thefirstisattheheadofthepress_header.htmltemplate,actingasthetop-levelcontrollerforthattemplate.Often,thisresultsinneedingasuperfluouswrapperelementjusttohousetheng-controllerattribute.ThesecondisngControllernestedinsideyourprimaryapplicationtemplate,controllingsomearbitraryportionoftheDOM.Bothofthesecanberefactoredtocomponentdirectivesby
![Page 53: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/53.jpg)
reassigningngControllertoadirectivecontroller:
[index.html]
<divng-app="articleApp">
<header></header>
<article></article>
</div>
[app.js]
angular.module('articleApp',[])
.directive('header',function(){
return{
controller:function(){
this.currentDate=newDate();
},
controllerAs:'header',
template:`
<strong>
AngularChronicle-{{header.currentDate|date}}
</strong>
<hr/>
`
};
})
.directive('article',function(){
return{
controller:function(){
this.title='FoodFightEruptsDuringDiplomaticLuncheon';
this.author='Jake';
},
controllerAs:'article',
template:`
<h1>{{article.title}}</h1>
<p>Writtenby:{{article.author}}</p>
`
};
});
Tip
Notethattemplateshereareincludedinthedirectiveforvisualcongruity.Forlargeapplications,itispreferredthatyouusetemplateUrlandlocatethetemplatemarkupinitsownfile.
![Page 54: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/54.jpg)
Howitworks...Generallyspeaking,anapplicationcanberepresentedbyahierarchyofnestedMVCcomponents.ngIncludeandngControlleractassubsetsofacomponentfunctionality,andsoitmakessensethatyouareabletoexpandthemintofullcomponentdirectives.
Intheprecedingexample,theultimateapplicationstructureiscomprisedofonlycomponents.Eachcomponentisdelegateditsowntemplate,controller,andmodel(byvirtueofthecontrollerobjectitself).SticklerswilldisputewhetherornotAngularbelongstotrueMVCstyle,butinthecontextofcomponentrefactoring,thisisirrelevant.Here,youhavedefinedastructurethatiscompletelymodular,reusable,testable,abstractable,andeasilymaintainable.ThisisthestyleofAngular2,andthevalueofthisshouldbeimmediatelyapparent.
![Page 55: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/55.jpg)
There'smore...Analertdeveloperwillnoticethatnoattentionispaidtoscopeinheritance.Thisisadifficultproblemtoapproach,mostlybecausemanyofthepatternsinAngular1aredesignedforamishmashbetweenascopeandcontrollerAs.Angular2isbuiltaroundstrictinputandoutputbetweennestedcomponents;however,inAngular1,scopeisinheritedbydefault,andnesteddirectives,bydefault,haveaccesstotheirencompassingcontrollerobjects.
Thus,totrulyemulateanAngular2style,onemustconfiguretheirapplicationtoexplicitlypassdataandmethodstochildren,similartothecontrollerAsencapsulationrecipe.However,thisdoesnotprecludedirectdataaccesstoancestralcomponentdirectivecontrollers;itmerelywagsafingeratitsinceitaddsadditionaldependencies.
![Page 56: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/56.jpg)
SeealsoComponentizingdirectivesusingcontrollerAsencapsulationshowsyouasuperiormethodoforganizingAngular1directivesImplementingabasiccomponentinAngularJS1.5detailshowtowriteanAngular1componentNormalizingservicetypesgivesinstructiononhowtoalignyourAngular1servicetypesforAngular2compatibility
![Page 57: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/57.jpg)
ImplementingabasiccomponentinAngularJS1.5The1.5releaseofAngularJSintroducedanewtool:thecomponent.Whileitisn'texactlysimilartotheconceptoftheAngular2component,itdoesallowyoutobuilddirective-stylepiecesinanexplicitlycomponentizedfashion.
Note
Thecode,links,andaliveexamplerelatedtothisrecipeareavailableathttp://ngcookbook.herokuapp.com/7756/.
![Page 58: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/58.jpg)
GettingreadySupposeyourapplicationhadadirectivedefinedasfollows:
[index.html]
<divng-app="articleApp">
<article></article>
</div>
[app.js]
angular.module('articleApp',[])
.directive('article',function(){
return{
controller:function(){
this.person={firstName:'Jake'};
this.title='PoliceBustIllegalSnailRacingRing';
this.capitalize=function(){
this.person.firstName=
this.person.firstName.toUpperCase();
};
},
controllerAs:'articleCtrl',
template:`
<h1>{{articleCtrl.title}}</h1>
<attributionauthor="articleCtrl.person.firstName"
upper-case-author="articleCtrl.capitalize()">
</attribution>
`
};
})
.directive('attribution',function(){
return{
controller:function(){},
controllerAs:'attributionCtrl',
bindToController:{
author:'=',
upperCaseAuthor:'&'
},
template:`
<png-click="attributionCtrl.upperCaseAuthor()">
Writtenby:{{attributionCtrl.author}}
</p>`
};
});
![Page 59: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/59.jpg)
Howtodoit...SincethisapplicationisalreadyorganizedaroundthecontrollerAsencapsulation,youcanmigrateittousethecomponent()definitionintroducedintheAngular1.5release.
Componentsacceptanobjectdefinitionsimilartoadirective,buttheobjectdoesnotdemandtobereturnedbyafunction—anobjectliteralisallthatisneeded.ComponentsutilizethebindingspropertyinthisobjectdefinitionobjectinthesamewaythatbindToControllerworksfordirectives.Withthis,youcaneasilyintroducecomponentsinthisapplicationinsteadofdirectives:
[index.html]
<divng-app="articleApp">
<article></article>
</div>
[app.js]
angular.module('articleApp',[])
.component('article',{
controller:function(){
this.person={firstName:'Jake'};
this.title='PoliceBustIllegalSnailRacingRing';
this.capitalize=function(){
this.person.firstName=
this.person.firstName.toUpperCase();
};
},
controllerAs:'articleCtrl',
template:`
<h1>{{articleCtrl.title}}</h1>
<attributionauthor="articleCtrl.person.firstName"
upper-case-author="articleCtrl.capitalize()">
</attribution>`
})
.component('attribution',{
controller:function(){},
controllerAs:'attributionCtrl',
bindings:{
author:'=',
upperCaseAuthor:'&'
},
template:`
<png-click="attributionCtrl.upperCaseAuthor()">
Writtenby:{{attributionCtrl.author}}
</p>
`
});
![Page 60: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/60.jpg)
Howitworks...Noticethatsinceyourcontroller-centricdataorganizationmatcheswhatacomponentdefinitionexpects,notemplatemodificationsarenecessary.Components,bydefault,willutilizeanisolatescope.What'smore,theywillnothaveaccesstothealiasofthesurroundingcontrollerobjects,somethingthatcannotbesaidforcomponent-styledirectives.Thisencapsulationisanimportantofferingofthenewcomponentfeature,asithasdirectparitytohowcomponentsoperateinAngular2.
![Page 61: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/61.jpg)
There'smore...Sinceyouhavenowentirelyisolatedeachindividualcomponent,thereisonlyasinglecontrollerobjecttodealwithineachtemplate.Thus,Angular1.5automaticallyprovidesaconvenientaliasforthecomponent'scontrollerobject,namely—$ctrl.ThisisprovidedwhetherornotacontrollerAsaliasisspecified.Therefore,afurtherrefactoringyieldsthefollowing:
[index.html]
<divng-app="articleApp">
<article></article>
</div>
[app.js]
angular.module('articleApp',[])
.component('article',{
controller:function(){
this.person={firstName:'Jake'};
this.title='PoliceBustIllegalSnailRacingRing';
this.capitalize=function(){
this.person.firstName=
this.person.firstName.toUpperCase();
};
},
template:`
<h1>{{$ctrl.title}}</h1>
<attributionauthor="$ctrl.person.firstName"
upper-case-author="$ctrl.capitalize()">
</attribution>
`
})
.component('attribution',{
controller:function(){},
bindings:{
author:'=',
upperCaseAuthor:'&'
},
template:`
<png-click="$ctrl.upperCaseAuthor()">
Writtenby:{{$ctrl.author}}
</p>
`
});
![Page 62: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/62.jpg)
SeealsoComponentizingdirectivesusingcontrollerAsencapsulationshowsyouasuperiormethodoforganizingAngular1directivesMigratinganapplicationtocomponentdirectivesdemonstrateshowtorefactorAngular1toacomponentstyleNormalizingservicetypesgivesinstructiononhowtoalignyourAngular1servicetypesforAngular2compatibility
![Page 63: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/63.jpg)
NormalizingservicetypesAngular1developerswillbequitefamiliarwiththefactory/service/providertrifecta.Inmanyways,thishasgonelargelyunalteredinAngular2conceptually.However,intheinterestofupgradinganexistingapplication,thereisonethingthatshouldbedonetomakethemigrationaseasyaspossible:eliminatefactoriesandreplacethemwithservices.
Note
Thecode,links,andaliveexamplerelatedtothisrecipeareavailableathttp://ngcookbook.herokuapp.com/5637/.
![Page 64: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/64.jpg)
GettingreadySupposeyouhadasimpleapplicationasfollows:
[index.html]
<divng-app="articleApp">
<article></article>
</div>
[app.js]
angular.module('articleApp',[])
.factory('ArticleData',function(){
vartitle='IncumbentSenatorOustedbyStalkofBroccoli';
return{
getTitle:function(){
returntitle;
},
author:'Jake'
};
})
.component('article',{
controller:function(ArticleData){
this.title=ArticleData.getTitle();
this.author=ArticleData.author;
},
template:`
<h1>{{$ctrl.title}}</h1>
<p>Writtenby:{{$ctrl.author}}</p>
`
});
![Page 65: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/65.jpg)
Howtodoit...Angular2isclass-based,anditincludesitsservicetypesaswell.Theexampleheredoesnothaveaservicetypethatiscompatiblewithaclassstructure.Soitmustbeconverted.Thankfully,thisisquiteeasytodo:
[index.html]
<divng-app="articleApp">
<article></article>
</div>
[app.js]
angular.module('articleApp',[])
.service('ArticleData',function(){
vartitle='IncumbentSenatorOustedbyStalkofBroccoli';
this.getTitle=function(){
returntitle;
};
this.author='Jake';
})
.component('article',{
controller:function(ArticleData){
this.title=ArticleData.getTitle();
this.author=ArticleData.author;
},
template:`
<h1>{{$ctrl.title}}</h1>
<p>Writtenby:{{$ctrl.author}}</p>
`
});
![Page 66: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/66.jpg)
Howitworks...Youstillwanttokeepthenotionoftitleprivate,butyoualsowanttomaintaintheAPIthattheinjectedservicetypeisproviding.Servicesaredefinedbyafunctionthatactsasaconstructor,andaninstancecreatedfromthisconstructoriswhatisultimatelyinjected.Here,youaresimplymovinggetTitle()andauthortobedefinedonthethiskeyword,whichtherebymakesitapropertyonallinstances.Notethattheuseinthecomponentandtemplatedoesnotchangeinanyway,anditshouldn't.
![Page 67: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/67.jpg)
There'smore...Thesimplesttoimplementservicetypes,Angular1factorieswereoftenusedfirstbymanydevelopers,includingmyself.Somedevelopersmighttakeoffenseatthefollowingclaim,butIdon'tthinktherewaseveragoodreasonforbothfactoriesandservicestoexist.Bothhaveahighdegreeofredundancy,andifyoudigdownintotheAngularsourcecode,youwillseethattheyessentiallyconvergetothesamemethods.
Whyservicesoverfactoriesthen?ThenewworldofJavaScript,ES6,andTypeScriptisbeingbuiltaroundclasses.Theyareafarmoreelegantwayofexpressingandorganizinglogic.Angular1servicesareanimplementationofprototype-basedclasses,whichwhenusedcorrectlyfunctioninessentiallythesamewayasformalES6/TypeScriptclasses.Ifyoustophere,youwillhavemodifiedyourservicestobemoreextensibleandcomprehensible.Ifyouintendtoupgrade,youwillfindthatAngular1serviceswillcleanlyupgradetoAngular2services.
![Page 68: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/68.jpg)
SeealsoComponentizingdirectivesusingcontrollerAsencapsulationshowsyouasuperiormethodfororganizingAngular1directivesMigratinganapplicationtocomponentdirectivesdemonstrateshowtorefactorAngular1toacomponentstyleImplementingabasiccomponentinAngularJS1.5detailshowtowriteanAngular1component
![Page 69: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/69.jpg)
ConnectingAngular1andAngular2withUpgradeModuleAngular2comeswiththeabilitytoconnectittoanexistingAngular1application.ThisisobviouslyadvantageoussincethiswillallowyoutoutilizeexistingcomponentsandservicesinAngular1intandemwithAngular2'scomponentsandservices.UpgradeModuleisthetoolthatissupportedbyAngularteamstoaccomplishsuchafeat.
Note
Thecode,links,andaliveexampleinrelationtothisrecipeareavailableathttp://ngcookbook.herokuapp.com/4137/.
![Page 70: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/70.jpg)
GettingreadySupposeyouhadaverysimpleAngular1applicationasfollows:
[index.html]
<!DOCTYPEhtml>
<html>
<head>
<!--Angular1scripts-->
<scriptsrc="angular.js"></script>
</head>
<body>
<divng-app="hybridApp"
ng-init="val='Angular1bootstrappedsuccessfully!'">
{{val}}
</div>
</body>
</html>
ThisapplicationinterpolatesavaluesetinanAngularexpressionsoyoucanvisuallyconfirmthattheapplicationhasbootstrappedandisworking.
![Page 71: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/71.jpg)
Howtodoit...Beginbydeclaringthetop-levelangularmoduleinsideitsownfile.Insteadofusingascripttagtofetchtheangularmodule,requireAngular1,importit,andcreatetherootAngular1module:
[ng1.module.ts]
import'angular'
exportconstNg1AppModule=angular.module('Ng1AppModule',[]);
Angular2shipswithanupgrademoduleoutofthebox,whichisprovidedinsideupgrade.js.ThetwoframeworkscanbeconnectedwithUpgradeModule.
Note
ThisrecipeutilizesSystemJSandTypeScript,thespecificationsforwhichlieinsideaverycomplicatedconfigfile.Thisisdiscussedinalaterchapter,sodon'tworryaboutthespecifics.Fornow,youarefreetoassumethefollowing:
SystemJSisconfiguredtocompileTypeScript(.ts)filesontheflySystemJSisabletoresolvetheimportandexportstatementsinTypeScriptfilesSystemJSisabletoresolveAngular1and2libraryimports
Angular2requiresatop-levelmoduledefinitionaspartofitsbaseconfiguration:
[app/ng2.module.ts]
import{NgModule}from'@angular/core';
import{BrowserModule}from'@angular/platform-browser';
import{UpgradeModule}from'@angular/upgrade/static';
import{RootComponent}from'./root.component';
@NgModule({
imports:[
BrowserModule,
UpgradeModule,
],
bootstrap:[
RootComponent
],
declarations:[
RootComponent
]
})
exportclassNg2AppModule{
constructor(publicupgrade:UpgradeModule){}
}
exportclassAppModule{}
![Page 72: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/72.jpg)
Tip
Thereasonwhythismoduledefinitionexiststhiswayisn'tcriticalforunderstandingthisrecipe.Angular2modulesarecoveredinChapter7,Services,DependencyInjection,andNgModule.
CreatetherootcomponentoftheAngular2application:
[app/root.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'root',
template:`
<p>Angular2bootstrappedsuccessfully!</p>
`
})
exportclassRootComponent{}
SinceAngular2willoftenbootstrapfromatop-levelfile,createthisfileasmain.tsandbootstraptheAngular2module:
[main.ts]
import{platformBrowserDynamic}
from'@angular/platform-browser-dynamic';
import{Ng1AppModule}from'./app/ng1.module';
import{Ng2AppModule}from'./app/ng2.module';
platformBrowserDynamic()
.bootstrapModule(Ng2AppModule);
ConnectingAngular1toAngular2
Don'tuseanng-apptobootstraptheAngular1application;instead,dothisafteryoubootstrapAngular2:
[main.ts]
import{platformBrowserDynamic}
from'@angular/platform-browser-dynamic';
import{Ng1AppModule}from'./app/ng1.module';
import{Ng2AppModule}from'./app/ng2.module';
platformBrowserDynamic()
.bootstrapModule(Ng2AppModule)
.then(ref=>{
ref.instance.upgrade
.bootstrap(document.body,[Ng1AppModule.name]);
});
![Page 73: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/73.jpg)
Withthis,you'llbeabletoremoveAngular1'sJSscript,theng-appdirective,andaddintherootelementoftheAngular2app:
[index.html]
<!DOCTYPEhtml>
<html>
<head>
<!--Angular2scripts-->
<scriptsrc="zone.js"></script>
<scriptsrc="reflect-metadata.js"></script>
<scriptsrc="system.js"></script>
<scriptsrc="system-config.js"></script>
</head>
<body>
<divng-init="val='Angular1bootstrappedsuccessfully!'">
{{val}}
</div>
<root></root>
</body>
</html>
Note
ThenewscriptslistedherearedependenciesofanAngular2application,butunderstandingwhatthey'redoingisn'tcriticalforunderstandingthisrecipe.Thisisexplainedlaterinthebook.
Withallthis,youshouldseeyourAngular1applicationtemplatecompileandtheAngular2componentrenderproperlyagain.ThismeansthatyouaresuccessfullyrunningAngular1andAngular2frameworkssidebyside.
![Page 74: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/74.jpg)
Howitworks...Makenomistake,whenyouuseUpgradeModule,youcreateanAngular1andAngular2apponthesamepageandconnectthemtogether.Thisadapterinstancewillallowyoutoconnectpiecesfromeachframeworkandusetheminharmony.
Morespecifically,thiscreatesanAngular1applicationatthetoplevelandallowsyoutousespiecesofanAngular2applicationinsideit.
![Page 75: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/75.jpg)
There'smore...Whileusefulforexperimentationandupgradingpurposes,thisshouldnotbeasolutionthatanyapplicationshouldrelyoninaproductioncontext.Youhaveeffectivelydoubledtheframeworkpayloadsizeandintroducedadditionalcomplexityinanexistingapplication.AlthoughAngular2isafarmoreperformantframework,donotexpecttohavethesamepristineresultswiththeUpgradeModulecross-pollination.
Thatsaid,asyouwillseeinsubsequentrecipes,youcannowuseAngular2componentsinanAngular1applicationusingtheadaptertranslationmethods.
![Page 76: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/76.jpg)
SeealsoDowngradingAngular2componentstoAngular1directiveswithdowngradeComponentdemonstrateshowtouseanAngular2componentinsideanAngular1applicationDowngradeAngular2providerstoAngular1serviceswithdowngradeInjectable,whichdemonstrateshowtouseanAngular2serviceinsideanAngular1application
![Page 77: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/77.jpg)
DowngradingAngular2componentstoAngular1directiveswithdowngradeComponentIfyouhavefollowedthestepsinConnectingAngular1andAngular2withUpgradeModule,youshouldnowhaveahybridapplicationthatiscapableofsharingdifferentelementswiththeopposingframework.
Tip
IfyouareunfamiliarwithAngular2components,itisrecommendedthatyougothroughthecomponentschapterbeforeyouproceed.
ThisrecipewillallowyoutofullyutilizeAngular2componentsinsideanAngular1template.
Note
Thecode,links,andaliveexampleinrelationtothisrecipeareavailableathttp://ngcookbook.herokuapp.com/1499/.
![Page 78: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/78.jpg)
GettingreadySupposeyouhadthefollowingAngular2componentthatyouwantedtouseinanAngular1application:
[app/article.component.ts]
import{Component,Input}from'@angular/core';
@Component({
selector:'ng2-article',
template:`
<h1>{{title}}</h1>
<p>Writtenby:{{author}}</p>
`
})
exportclassArticleComponent{
@Input()author:string
title:string='UnicycleJoustingRecognizedasOlympicSport';
}
BeginbycompletingtheConnectingAngular1andAngular2withUpgradeModulerecipe.
![Page 79: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/79.jpg)
Howtodoit...Angular1hasnocomprehensionofhowtoutilizeAngular2components.TheexistingAngular2frameworkwilldutifullyrenderitifgiventheopportunity,butthedefinitionitselfmustbeconnectedtotheAngular1frameworksothatitmayberequestedwhenneeded.
Beginbyaddingthecomponentdeclarationstothemoduledefinition;thisisusedtolinkthetwoframeworks:
[app/app.module.ts]
import{NgModule}from'@angular/core';
import{BrowserModule}from'@angular/platform-browser';
import{UpgradeModule}from'@angular/upgrade/static';
import{RootComponent}from'./root.component';
import{ArticleComponent}from'./article.component';
@NgModule({
imports:[
BrowserModule,
UpgradeModule,
],
declarations:[
RootComponent,
ArticleComponent
],
bootstrap:[
RootComponent
]
})
exportclassNg2AppModule{
constructor(publicupgrade:UpgradeModule){}
}
ThisconnectsthecomponentdeclarationtotheAngular2context,butAngular1stillhasnoconceptofhowtointerfacewithit.Forthis,you'llneedtousedowngradeComponent()todefinetheAngular2componentasanAngular1directive.GivetheAngular1directiveadifferentHTMLtagtorenderinsidesoyoucanbecertainthatit'sAngular1doingtherenderingandnotAngular2:
[main.ts]
import{Component,Input}from'@angular/core';
import{downgradeComponent}from'@angular/upgrade/static';
import{Ng1AppModule}from'./ng1.module';
@Component({
selector:'ng2-article',
template:`
<h1>{{title}}</h1>
![Page 80: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/80.jpg)
<p>Writtenby:{{author}}</p>
`
})
exportclassArticleComponent{
@Input()author:string
title:string='UnicycleJoustingRecognizedasOlympicSport';
}
Ng1AppModule.directive(
'ng1Article',
downgradeComponent({component:ArticleComponent}));
Finally,sincethiscomponenthasaninput,you'llneedtopassthisvalueviaabindingattribute.EventhoughthecomponentisstillbeingdeclaredasanAngular1directive,you'llusetheAngular2bindingsyntax:
[index.html]
<!DOCTYPEhtml>
<html>
<head>
<!--Angular2scripts-->
<scriptsrc="zone.js"></script>
<scriptsrc="reflect-metadata.js"></script>
<scriptsrc="system.js"></script>
<scriptsrc="system-config.js"></script>
</head>
<body>
<divng-init="authorName='JakeHsu'">
<ng1-article[author]="authorName"></ng1-article>
</div>
<root></root>
</body>
</html>
Theinputandoutputmustbeexplicitlydeclaredatthetimeofconversion:
[app/article.component.ts]
import{Component,Input}from'@angular/core';
import{downgradeComponent}from'@angular/upgrade/static';
import{Ng1AppModule}from'./ng1.module';
@Component({
selector:'ng2-article',
template:`
<h1>{{title}}</h1>
<p>Writtenby:{{author}}</p>
`
})
exportclassArticleComponent{
@Input()author:string
![Page 81: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/81.jpg)
title:string='UnicycleJoustingRecognizedasOlympicSport';
}
Ng1AppModule.directive(
'ng1Article',
downgradeComponent({
component:ArticleComponent,
inputs:['author']
}));
Theseareallthestepsrequired.Ifdoneproperly,youshouldseethecomponentrenderalongwiththeauthor'snamebeinginterpolatedinsidetheAngular2componentthroughAngular1'sng-initdefinition.
![Page 82: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/82.jpg)
Howitworks...YouaregivingAngular1theabilitytodirectAngular2toacertainelementintheDOMandsay,"Ineedyoutorenderhere."Angular2stillcontrolsthecomponentviewandoperation,andineverysense,themainthingwereallycareaboutisafullAngular2componentadaptedforuseinanAngular1template.
Tip
downgradeComponent()takesanobjectspecifyingthecomponentasanargumentandreturnsthefunctionthatAngular1isexpectingforthedirectivedefinition.
![Page 83: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/83.jpg)
SeealsoConnectingAngular1andAngular2withUpgradeModuleshowsyouhowtorunAngular1and2frameworkstogetherDowngradeAngular2providerstoAngular1serviceswithdowngradeInjectabledemonstrateshowtouseanAngular2serviceinsideanAngular1application
![Page 84: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/84.jpg)
DowngradeAngular2providerstoAngular1serviceswithdowngradeInjectableIfyouhavefollowedthestepsinConnectingAngular1andAngular2withUpgradeModule,youshouldnowhaveahybridapplicationthatiscapableofsharingdifferentelementswiththeopposingframework.IfyouareunfamiliarwithAngular2providers,itisrecommendedthatyougothroughthedependencyinjectionchapterbeforeyouproceed.
Likewithtemplatedcomponents,interchangeabilityisalsoofferedtoservicetypes.ItispossibletodefineaservicetypeinAngular2andtheninjectitintoanAngular1context.
Note
Thecode,links,andaliveexampleinrelationtothisrecipeareavailableathttp://ngcookbook.herokuapp.com/2824/.
![Page 85: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/85.jpg)
GettingreadyBeginwiththecodewritteninConnectingAngular1andAngular2withUpgradeModule.
![Page 86: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/86.jpg)
Howtodoit...First,definetheserviceyouwouldliketoinjectintoanAngular1component:
[app/article.service.ts]
import{Injectable}from'@angular/core';
@Injectable()
exportclassArticleService{
article:Object={
title:'ResearchShowsMoonNotActuallyMadeofCheese',
author:'JakeHsu'
};
}
Next,definetheAngular1componentthatshouldinjectit:
[app/article.component.ts]
exportconstng1Article={
template:`
<h1>{{article.title}}</h1>
<p>{{article.author}}</p>
`,
controller:(ArticleService,$scope)=>{
$scope.article=ArticleService.article;
}
};
ArticleServicewon'tbeinjectedyetthough,sinceAngular1hasnoideathatthisserviceexists.Doingthisisverysimple,however.First,you'lllisttheserviceproviderintheAngular2moduledefinitionasyounormallywould:
[app/ng2.module.ts]
import{NgModule}from'@angular/core';
import{BrowserModule}from'@angular/platform-browser';
import{UpgradeModule}from'@angular/upgrade/static';
import{RootComponent}from'./root.component';
import{ArticleService}from'./article.service';
@NgModule({
imports:[
BrowserModule,
UpgradeModule,
],
declarations:[
RootComponent
],
providers:[
![Page 87: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/87.jpg)
ArticleService
],
bootstrap:[
RootComponent
]
})
exportclassNg2AppModule{
constructor(publicupgrade:UpgradeModule){}
}
Still,Angular1doesnotunderstandhowtousetheservice.
InthesamewayyouconvertanAngular2componentdefinitionintoanAngular1directive,convertanAngular2serviceintoanAngular1factory.UsedowngradeInjectableandaddtheAngular1componentandtheconvertedservicetotheAngular1moduledefinition:
[app/ng1.module.ts]
import'angular';
import{ng1Article}from'./article.component';
import{ArticleService}from'./article.service';
import{downgradeInjectable}from'@angular/upgrade/static';
exportconstNg1AppModule=angular.module('Ng1AppModule',[])
.component('ng1Article',ng1Article)
.factory('ArticleService',downgradeInjectable(ArticleService));
That'sall!YoushouldbeabletoseetheAngular1componentrenderwiththedatapassedfromtheAngular2service.
![Page 88: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/88.jpg)
SeealsoConnectingAngular1andAngular2withUpgradeModuleshowsyouhowtorunAngular1and2frameworkstogetherDowngradingAngular2componentstoAngular1directiveswithdowngradeComponentdemonstrateshowtouseanAngular2componentinsideanAngular1application
![Page 89: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/89.jpg)
Chapter2.ConqueringComponentsandDirectivesYourobjectiveistoiteratethroughthisanddisplaythearticletitleonlyifitissetasactive.Thischapterwillcoverthefollowingrecipes:
UsingdecoratorstobuildandstyleasimplecomponentPassingmembersfromaparentcomponenttoachildcomponentBindingtonativeelementattributesRegisteringhandlersonnativebrowsereventsGeneratingandcapturingcustomeventsusingEventEmitterAttachingbehaviortoDOMelementswithDirectivesProjectingnestedcontentusingngContentUsingngForandngIfstructuraldirectivesformodel-basedDOMcontrolReferencingelementsusingtemplatevariablesAttributepropertybindingUtilizingcomponentlifecyclehooksReferencingaparentcomponentfromachildcomponentConfiguringmutualparent-childawarenesswithViewChildandforwardRefConfiguringmutualparent-childawarenesswithContentChildandforwardRef
![Page 90: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/90.jpg)
IntroductionDirectivesasyoucametoknowtheminAngular1havebeendoneawaywith.Intheirplace,wehavetwonewentities:componentsandthenewversionofdirectives.Angular2applicationsarenowcomponent-driven;withfurtherexposure,youwilldiscoverwhythisstyleissuperior.
Muchofthesyntaxisentirelynewandmayseemstrangeatfirst.Fearnot!TheunderpinningsoftheAngular2styleareelegantandmarvelousoncecompletelyunderstood.
![Page 91: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/91.jpg)
UsingdecoratorstobuildandstyleasimplecomponentWhenwritinganapplicationcomponentinTypeScript,thereareseveralnewparadigmsthatyoumustbecomefamiliarandcomfortablewith.Thoughpossiblyintimidatinginitially,youwillfindthatyou'llbeabletocarryovermuchofyourcomprehensionofAngular1directives.
Note
Thecodeandaliveexampleofthisareavailableathttp://ngcookbook.herokuapp.com/6577/.
![Page 92: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/92.jpg)
GettingreadyOneofthesimplestimaginablecomponentswecanbuildisatemplateelementthatinterpolatessomevaluesintoitstemplate.InAngular1,onewaythiscouldbeachievedwasbycreatinganelementdirective,attachingsomedatatothescopeinsidethelinkfunction,andatemplatethatwouldreferencethedata.Iselectedthisdescriptionspecificallybecausenearlyallthoseconceptshavebeenbinned.
Supposeyouwanttocreateasimplearticlecomponentwithapseudotemplateasfollows:
<p>{{date}}</p>
<h1>{{title}}</h1>
<h3>Writtenby:{{author}}</h3>
YouwanttocreateacomponentthatwillliveinsideitsownHTMLtag,renderthetemplate,andinterpolatethevalues.
![Page 93: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/93.jpg)
Howtodoit...TheelementalbuildingblockofAngular2applicationsisthecomponent.Thiscomponentcouldgenerallybedefinedwithtwopieces:thecoreclassdefinitionandtheclassdecoration.
Writingtheclassdefinition
AllAngular2componentsbeginasaclass.Thisclassisusedtoinstantiatethecomponent,andanydatarequiredinsidethecomponenttemplatewillbeaccessiblefromtheclass'sproperties.Thus,thefoundationalclassforthecomponentwouldappearasfollows:
[app/article.component.ts]
exportclassArticleComponent{
currentDate:date;
title:string;
author:string;
constructor(){
this.currentDate=newDate();
this.title=`
FlightSecurityRestrictionstoInclude
Insults,MeanFaces
`;
this.author='Jake';
}
};
HereareafewthingstonoteforthosewhoarenewtoTypeScriptorES6ingeneral:
Youwillnotetheclassdefinitionisprefixedwithanexportkeyword.ThisisadherencetothenewES6moduleconvention,whichnaturallyisalsopartofTypeScript.AssumingtheArticleclassisdefinedinthefoo.tsfile,itcanbeimportedtoadifferentmoduleusingtheimportkeyword,andthepathtothatmodulewouldbeimport{Article}from'./foo';(thisassumesthattheimportingfileisinthesamedirectoryasfoo.ts).ThetitledefinitionusesthenewES6templatestringsyntax,apairofbackticks(``)insteadofthetraditionalsetofquotes('').Youwillfindyoubecomequitefondofthis,asitmeansthe''+''+''messinessformerlyusedtodefinemultilinetemplateswouldnolongerbenecessary.Allthepropertiesofthisclassaretyped.TypeScript'stypingsyntaxtakestheformofpropertyName:propertyType=optionalInitValue.JavaScriptis,ofcourse,alooselytypedlanguage,andJavaScriptiswhatthebrowserisinterpretinginthiscase.However,writingyourapplicationinTypeScriptallowsyoutoutilizetypesafetyatcompiletime,whichwillallowyoutoavoidundesirableandunanticipatedbehavior.AllES6classescomewithapredefinedconstructor()method,whichisinvokeduponinstantiation.Here,youareusingtheconstructortoinstantiatethepropertiesoftheclass,whichisaperfectlyfinestrategyformemberinitialization.Havingthememberproperty
![Page 94: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/94.jpg)
definitionoutsidetheconstructorisallowed,sinceitisusefulforaddingtypestoproperties;thus,hereyouaresimplyobviatingtheuseoftheconstructorsinceyouareabletoaddatypeandassignthevalueinthesameline.Amoresuccinctstylecouldbeasfollows:
[app/article.component.ts]
exportclassArticleComponent{
currentDate:date=newDate();
title:string=`
FlightSecurityRestrictionstoInclude
Insults,MeanFaces
`;
author:string='Jake';
}
TheTypeScriptcompilerwillautomaticallymovethememberinitializationprocessinsidetheclassconstructor,sothisversionandthepreviousonearebehaviorallyidentical.
Writingthecomponentclassdecorator
Althoughyouhavecreatedaclassthathasinformationassociatedwithit,itdoesnotyethaveanywaytointerfacewiththeDOM.Furthermore,thisclassisyettobeassignedwithanymeaninginthecontextofAngular2.Youcanaccomplishthiswithdecorators.
Note
DecoratorsareafeatureofTypeScriptbutnotbyanymeansuniquetothelanguage.Pythondevelopers,amongmanyothers,shouldbequitefamiliarwiththeconceptofclassmodulationviaexplicitdecoration.Generallyspeaking,itallowsyoutohavearegularizedmodificationofthedefinedclassesusingseparatelydefineddecorators.However,inAngular2,youwilllargelybeutilizingthedecoratorsprovidedtoyoubytheframeworktodeclarethevariousframeworkelements.Decoratorssuchas@ComponentaredefinedintheAngularsourcecodeasaComponentfunction,andthefunctionisappliedasadecoratorusingthe@symbol.
An@prefixsignalsthattheimportedfunctionshouldbeappliedasadecorator.Thesedecoratorsarevisuallyobviousbutwillusuallynotexistbythemselvesorwithanemptyobjectliteral.ThisisbecauseAngular2decoratorsaregenerallymadeusefulbytheirdecoratormetadata.ThisconceptcanbemademoreconcreteherebyusingthepredefinedComponentdecorator.
NothingisavailableforfreeinAngular2.Inordertousethecomponentdecorator,itmustbeimportedfromtheAngular2coremoduleintothemodulethatwishestouseit.YoucanthenprependthisComponenttotheclassdefinition:
[app/article.component.ts]
import{Component}from'@angular/core';
@Component({})
![Page 95: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/95.jpg)
exportclassArticleComponent{
currentDate:date=newDate();
title:string=`
FlightSecurityRestrictionstoInclude
Insults,MeanFaces
`;
author:string='Jake';
}
Asmentionedbefore,the@ComponentdecoratoracceptsaComponentMetadataobject,whichintheprecedingcodeisjustanemptyobjectliteral(notethattheprecedingcodewillnotcompile).Conceptually,thismetadataobjectisverysimilartothedirectivedefinitionobjectinAngular1.Here,youwanttoprovidethedecoratormetadataobjectwithtwoproperties,namelyselectorandtemplate:
[app/article.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'article',
template:`
<p>{{currentDate|date}}</p>
<h1>{{title}}</h1>
<h3>Writtenby:{{author}}</h3>
`
})
exportclassArticleComponent{
currentDate:date=newDate();
title:string=`
FlightSecurityRestrictionstoInclude
Insults,MeanFaces
`;
author:string='Jake';
}
NotethatselectoristhestringthatwillbeusedtofindwherethecomponentshouldbeinsertedintheDOM,andtemplateisobviouslythestringifiedHTMLtemplate.
Withallthis,youwillbeabletoseeyourarticlecomponentinactionwiththe<article></article>tag.
![Page 96: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/96.jpg)
Howitworks...TheclassdefinitionhassupplantedtheAngular1conceptofhavingacontroller.Thecomponentinstancesofthisclasshavememberpropertiesthatcanbeinterpolatedandboundintothetemplate,similarto$scopeinAngular1.
Inthetemplate,theinterpolationanddatabindingprocessesseemtooccurmuchinthesameway,theydidinAngular1.Thisisnotactuallythecase,whichisvisitedingreaterdetaillaterinthischapter.Thebuilt-indatemodifier,whichresemblesanAngular1filter,isnowdubbedwithapipealthoughitworksinaverysimilarfashion.
TheselectormetadatapropertyisastringrepresentingaCSSselector.Inthisdefinition,youtargetalltheoccurrencesofanarticletag,buttheselectorspecificityanddetailisofcourseabletohandleagreatdealofadditionalcomplexity.Usethistoyouradvantage.
![Page 97: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/97.jpg)
There'smore...TheconceptthatmustbeinternalizedforAngular2neophytesisthetotalencapsulationofacomponent.ThisbookwillgointofurtherdetailaboutthedifferentabilitiesoftheComponentMetadataobject,buttheparadigmtheyandallclassdecoratorsintroduceistheconceptthatAngular2componentsareself-describing.Byexaminingtheclassdefinition,youcanwhollyreasonthedata,service,class,andinjectabledependencies.InAngular1,thiswasnotpossiblebecauseofthe"scopesoup"pattern.
OnecouldarguethatinAngular1,CSSstylingwasasecond-classcitizen.Thisisnolongerthecasewithcomponents,asthemetadataobjectoffersrobustsupportforcomplexstyling.Forexample,toitalicizetheauthorinyourArticlecomponent,usethiscode:
[app/article.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'article',
template:`
<p>{{currentDate|date}}</p>
<h1>{{title}}</h1>
<h3>Writtenby:{{author}}</h3>
`,
styles:[`
h3{
font-style:italic;
}
`]
})
exportclassArticleComponent{
currentDate:date=newDate();
title:string=`
FlightSecurityRestrictionstoInclude
Insults,MeanFaces
`;
author:string='Jake';
}
Angular2willusethisstylespropertyandcompileitintoageneratedstylesheet,whichitwillthenapplytoonlythiscomponent.YoudonothavetoworryabouttherestoftheHTMLh3tagsbeinginadvertentlystyled.ThisisbecauseAngular2'sgeneratedstylesheetwillensurethatonlythiscomponent—anditschildren—aresubjecttotheCSSruleslistedinthemetadataobject.
Note
Thisisintendedtoemulatethetotalmodularityofwebcomponents.However,sincewebcomponentsdonotyethaveuniversalsupport,Angular2'sdesignessentiallyperformsapolyfill
![Page 98: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/98.jpg)
forthisbehavior.
![Page 99: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/99.jpg)
SeealsoPassingmembersfromaparentcomponentintoachildcomponentgoesthroughthebasicsofdownwarddataflowbetweencomponentsUsingngForandngIfstructuraldirectivesformodel-basedDOMcontrolinstructsyouonhowtoutilizesomeofAngular2'scorebuilt-indirectivesUtilizingcomponentlifecyclehooksgivesanexampleofhowyoucanintegratewithAngular2'scomponentrenderingflow
![Page 100: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/100.jpg)
PassingmembersfromaparentcomponentintoachildcomponentWiththedepartureoftheAngular1.xconceptof$scopeinheritance,mentally(partiallyorentirely)remodelinghowinformationwouldbepassedaroundyourapplicationisamust.Initsplace,youhaveanentirelynewsystemofpropagatinginformationthroughouttheapplication'shierarchy.
Gonealsoistheconceptofdefaultingtobidirectionaldatabinding.Althoughitmadeforanapplicationthatwassimplertoreasonabout,bidirectionaldatabindingincursanunforgivablyexpensivedragonperformance.Thisnewsystemoperatesinanasymmetricfashion:membersarepropagateddownwardsthroughthecomponenttree,butnotupwardsunlessexplicitlyperformed.
Note
Thecode,links,andaliveexampleofthisareavailableathttp://ngcookbook.herokuapp.com/6543/.
![Page 101: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/101.jpg)
GettingreadySupposeyouhadasimpleapplicationthatintendedto(butcurrentlycannot)passdatafromaparentArticleComponenttoachildAttributionComponent:
[app/components.ts]
import{Component}from'@angular/core';
@Component({
selector:'attribution',
template:`
<h3>Writtenby:{{author}}</h3>
`
})
exportclassAttributionComponent{
author:string;
}
@Component({
selector:'article',
template:`
<h1>{{title}}</h1>
<attribution></attribution>
`
})
exportclassArticleComponent{
title:string='BelchingChoirBeginsWorldTour';
name:string='Jake';
}
![Page 102: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/102.jpg)
Howtodoit...Inthisinitialimplementation,thecomponentsdefinedherearenotyetawareofeachother,andthe<attribution></attribution>tagwillremaininertintheDOM.Thisisagoodthing!Itmeansthesetwocomponentsarecompletelydecoupled,andyouareabletoonlyintroduceconnectionlogicasnecessary.
Connectingthecomponents
First,sincethe<attribution></attribution>tagappearsinsidetheArticlecomponent,youmustmakethecomponentawareoftheexistenceofAttributionComponent.ThisisaccomplishedbyintroducingthecomponentinthemoduleinwhichArticleComponentisalsodeclared:
[app/app.module.ts]
import{NgModule}from'@angular/core';
import{BrowserModule}from'@angular/platform-browser';
import{ArticleComponent,AttributionComponent}
from'./components';
@NgModule({
imports:[
BrowserModule
],
declarations:[
ArticleComponent,
AttributionComponent
],
bootstrap:[
ArticleComponent
]
})
exportclassAppModule{}
Note
Forthepurposeofthisrecipe,don'tconcernyourselfjustyetwiththedetailsofwhatNgModuleisdoing.Intheexampleinthisrecipe,theentireapplicationisjustaninstanceofArticleComponentwithAttributionComponentinsideit.So,allthecomponentdeclarationscanbedoneinsidethesamemodule.
Withthis,youwillseethatArticleComponentisabletomatchthe<attribution></attribution>tagwiththeAttributionComponentdefinition.
Note
Insideasinglemodule,theorderofthedefinitioncouldmatteralot.ES6andTypeScriptclass
![Page 103: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/103.jpg)
declarationsarenothoisted,soyoucannotreferencethematallbeforethedeclarationwithoutgeneratingerrors.Inthisrecipe,sinceArticleComponentisdefinedbeforeAttributionComponent,theformercannotdirectlyreferencethelatterinsideitsdefinition.
IfyouweretoinsteaddefineAttributionComponentinsideaseparatemoduleandimportitwiththemoduleloader,theorderissuebecomesirrelevant.Asyouwillnotice,thisisoneoftheexcellentbenefitsofhavingahighlymodularapplicationstructure.
OnecaveattothisisthatAngulardoesmakeitpossibletodoout-of-orderclassreferencesusingaforwardRef.However,ifsolvingtheorderproblemispossiblebysplittingitintoseparatemodules,thatispreferredoverforwardRef.
Thisbeingthecase,goaheadandsplityourcomponentfileintotwoseparatemodulesandimportthemaccordingly:
[app/app.module.ts]
import{NgModule}from'@angular/core';
import{BrowserModule}from'@angular/platform-browser';
import{ArticleComponent}from'./article.component';
import{AttributionComponent}from'./attribution.component';
@NgModule({
imports:[
BrowserModule
],
declarations:[
ArticleComponent,
AttributionComponent
],
bootstrap:[
ArticleComponent
]
})
exportclassAppModule{}
[app/article.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'article',
template:`
<h1>{{title}}</h1>
<attribution></attribution>
`
})
exportclassArticleComponent{
title:string='BelchingChoirBeginsWorldTour';
name:string='Jake';
![Page 104: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/104.jpg)
}
[app/attribution.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'attribution',
template:`
<h3>Writtenby:{{author}}</h3>
`
})
exportclassAttributionComponent{
author:string;
}
Declaringinputs
SimilartotheAngular1directive'sscopepropertyobject,inAngular2,youmustdeclarethemembersoftheparentcomponenttobringthemdowntothechildcomponent.InAngular1,thiscouldbedoneimplicitlywithaninherited$scope,butthisisnolongerthecase.Angular2componentinputsmustbeexplicitlydefined.
Tip
AnotherimportantdifferencebetweenAngular1andAngular2isthat@InputinAngular2isaunidirectionaldatabindingfeature.Dataupdateswillflowdownwards,andtheparentwillnotbeupdatedunlessexplicitlynotified.
TheprocessofdeclaringinputsinachildcomponentisdonethroughtheInputdecorator,butthedecoratorisinvokedinsidetheclassdefinitioninsteadofdoingsoinfrontofit.Inputisimportedfromthecoremoduleandinvokedinsidetheclassdefinitionthatispairedwithamember.
Note
Don'tletthisconfuseyou.Theimplementationoftheactualdecoratingfunctionishiddenfromyousinceitisimportedasasingletarget,sodon'tthinkmuchaboutwhatthe@Input()syntaxisdoing.ThereisadefinedInputfunctionintheAngularsource,andyouarecertainlyinvokingthismethodhere.However,foryourpurposes,itismerelydeclaringthememberthatfollowsitastheonethatwillbepassedinexplicitlyfromtheparentcomponent.YouuseitinthesamewayastheComponentdecorator,justinadifferentplace.
[app/attribution.component.ts]
import{Component,Input}from'@angular/core';
@Component({
selector:'attribution',
![Page 105: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/105.jpg)
template:`
<h3>Writtenby:{{author}}</h3>
`
})
exportclassAttributionComponent{
@Input()author:string;
}
Next,youmustpassthevalueboundtothechildcomponenttagtotheparentcomponent.Inthecontextofthisrecipe,youwanttopassthenamepropertyoftheArticlecomponentobjecttotheauthorpropertyoftheAttributioncomponentobject.Onewayofaccomplishingthisisbyusingthesquarebracketnotationonthetagattribute,whichspecifiestheattributestringasbounddata:
[app/article.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'article',
template:`
<h1>{{title}}</h1>
<attribution[author]="name"></attribution>
`
})
exportclassArticleComponent{
title:string='BelchingChoirBeginsWorldTour';
name:string='Jake';
}
Withthis,youhavesuccessfullypassedamemberpropertydown,fromaparenttoachildcomponent!
![Page 106: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/106.jpg)
Howitworks...Recallthatthestartingpointofthisexamplewasthatwehadtwocomponentsthatdidn'tknowtheotherexists,eventhoughtheyaredefinedandexportedinsidethesamemodule.Theprocessdemonstratedinthisrecipeistoprovidethechildcomponenttotheparentcomponent,configurethechildcomponenttoexpectthatamemberwillbeboundtoaninputattribute,andfinallyprovidethatmemberinthetemplateoftheparentcomponent.
![Page 107: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/107.jpg)
There'smore...Somepeoplehaveanissuewiththesquarebracketnotation.ItisvalidHTML,butsomedevelopersfeelitisunintuitiveandlooksodd.
Tip
Additionally,thebracketnotationisnotvalidXML.DevelopersusingHTMLgeneratedthroughXSLTwillnotbeabletoutilizethenewsyntax.Fortunately,everywherethenewAngular2syntaxutilizesnewnew[]or()syntax,thereisanequivalentsyntaxthattheframeworksupportswhichwillbehaveidentically.
Insteadofusingpairsofsquarebrackets,youcanprefixtheattributenamewithbind-anditwillbehaveidentically:
[app/article.component.ts]
import{Component,Input}from'@angular/core';
@Component({
selector:'article',
template:`
<h1>{{title}}</h1>
<attributionbind-author="name"></attribution>
`
})
exportclassArticleComponent{
title:string='BelchingChoirBeginsWorldTour';
name:string='Jake';
}
Angularexpressions
Notethatthevalueoftheattributenameisnotastringbutanexpression.Angularknowshowtoevaluatethisexpressioninthecontextoftheparentcomponent.AsisthecasewithAngularexpressionsthough,youaremorethanwelcometoprovideastaticvalueandAngularwillhappilyevaluateitandprovideittothechildcomponent.
Forexample,thefollowingchangewouldhardcodethechildcomponenttoassignthestring"MikeSnifferpippets"astheauthorproperty:
[app/article.component.ts]
import{Component,Input}from'@angular/core';
import{AttributionComponent}from'./attribution.component';
@Component({
selector:'article',
template:`
![Page 108: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/108.jpg)
<h1>{{title}}</h1>
<attribution[author]="'MikeSnifferpippets'"></attribution>
`,
directives:[AttributionComponent]
})
exportclassArticleComponent{
title:string='BelchingChoirBeginsWorldTour';
name:string='Jake';
}
Unidirectionaldatabinding
Thedatabindingyouhavesetupinthisrecipeisactuallyunidirectional.Morespecifically,changesintheparentcomponentmemberwillpropagatedownwardstothechildcomponent,butchangestothechildcomponentmemberwillnotpropagateupwards.Thiswillbeexploredfurtherinanotherrecipe,butitisimportanttokeepinmindthattheAngular2dataflowis,bydefault,downwardsthroughthecomponenttree.
Membermethods
Angulardoesn'tcareaboutthenatureoftheboundvalue.TypeScriptwillenforcetypecorrectnessshouldyoudeviatefromthedeclaredtype,butyouarewelcometopassparentmethodstothechildwiththisstrategyaswell.
Tip
Keepinmindthatpassingamethodboundinthiswaydoesnotenforcethecontextinwhichitisevaluated.Iftheparentcomponentpassesamembermethodthatutilizesthethiskeywordandthechildcomponentevaluatesit,thiswillrefertothechildcomponentinstanceandnottheparentcomponent.Therefore,ifthemethodtriestoaccessthememberdataontheparentcomponent,itwillnotbeavailable.
Thereareanumberofwaystomitigatethisproblem.Generallythough,ifyoufindyouarepassingaparentmembermethoddowntothechildcomponentandinvokingit,thereisprobablyabetterwaytodesignyourapplication.
![Page 109: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/109.jpg)
SeealsoUsingdecoratorstobuildandstyleasimplecomponentdescribesthebuildingblocksofimplementinganAngular2componentBindingtonativeelementattributesshowshowAngular2interfaceswithHTMLelementattributesRegisteringhandlersonnativebrowsereventsdemonstrateshowyoucaneasilyattachbehaviortobrowserevents.GeneratingandcapturingcustomeventsusingEventEmitterdetailshowtopropagateinformationupwardsbetweencomponents.UsingngForandngIfstructuraldirectivesformodel-basedDOMcontrolinstructsyouonhowtoutilizesomeofAngular2'scorebuilt-indirectives.
![Page 110: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/110.jpg)
BindingtonativeelementattributesInAngular1,itwasexpectedthatthedeveloperwouldutilizethebuilt-inreplacementdirectivesforelementattributesthathadmeaningfulDOMbehaviorattachedtothem.ThiswasduetothefactthatmanyoftheseattributeshadbehaviorthatwasincompatiblewithhowAngular1databindingoperated.InAngular2,thesespecialattributedirectivesaredoneawaywith,andthebindingbehaviorandsyntaxissubsumedintothenormalbindingbehavior.
Note
Thecode,links,andaliveexampleofthisareavailableathttp://ngcookbook.herokuapp.com/8313/.
![Page 111: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/111.jpg)
Howtodoit...Bindingtothenativeattributeisassimpleasplacingsquarebracketsaroundtheattributeandtreatingitasnormalbounddata:
[app/logo.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'logo',
template:'<img[src]="logoUrl">'
})
exportclassLogoComponent{
logoUrl:string=
'//angular.io/resources/images/logos/standard/logo-nav.png';
}
Withthissetup,the<img>elementwilldutifullyfetchandshowtheimagewhenitisprovidedbyAngular.
![Page 112: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/112.jpg)
Howitworks...Thisisadifferentsolutiontothesameproblemthatng-srcsolvedinAngular1.Thebrowserislookingforansrcattributeonthetag.Sincethesquarebracketsareincludedaspartoftheattributestring,thebrowserwillnotfindoneandthereforenotmakearequest.[src]willonlymakeanimagerequestoncethevalueisfilledandprovidedtotheelement.
![Page 113: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/113.jpg)
SeealsoPassingmembersfromaparentcomponentintoachildcomponentgoesthroughthebasicsofdownwarddataflowbetweencomponents.Registeringhandlersonnativebrowsereventsdemonstrateshowyoucaneasilyattachbehaviortobrowserevents.AttachingbehaviortoDOMelementswithdirectivesdemonstrateshowtoattachbehaviortoelementswithattributedirectives.ReferencingelementsusingtemplatevariablesdemonstratesAngular2'snewtemplatevariableconstruct.AttributepropertybindingshowsAngular2'scleverwayofdeepreferencingelementproperties.
![Page 114: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/114.jpg)
RegisteringhandlersonnativebrowsereventsInAngular2,theotherhemisphereofbindingthatisneededforafullyfunctioningapplicationiseventbinding.TheAngular2eventbindingsyntaxissimilartothatofdatabinding.
Note
Thecode,links,andaliveexampleofthisareavailableathttp://ngcookbook.herokuapp.com/4437/.
![Page 115: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/115.jpg)
GettingreadySupposeyouwantedtocreateanarticleapplicationthatcountedshares,andyoubeganwiththefollowingskeleton:
[app/article.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'article',
template:`
<h1>{{title}}</h1>
<p>Shares:{{shareCt}}</p>
<button>Share</button>
`
})
exportclassArticleComponent{
title:string='PoliceApprehendTiramisuThieves';
shareCt:number=0;
}
![Page 116: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/116.jpg)
Howtodoit...TheAngular2eventbindingsyntaxisaccomplishedwithapairofparenthesessurroundingtheeventtype.Inthiscase,eventsthatyouwishtolistenforwillhaveatypepropertyofclick,andthisiswhattheywillbeboundagainst.Thevalueoftheboundeventattributeisanexpression,soyoucaninvokethemethodasahandlerwithinit:
[app/article.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'article',
template:`
<h1>{{title}}</h1>
<p>Shares:{{shareCt}}</p>
<button(click)="share()">Share</button>
`
})
exportclassArticleComponent{
title:string='PoliceApprehendTiramisuThieves';
shareCt:number=0;
share():void{
++this.shareCt;
}
}
![Page 117: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/117.jpg)
Howitworks...Angularwatchesfortheeventbindingsyntax(click)andaddsaclicklistenertoArticleComponent,boundtotheshare()handler.Whenthiseventisobserved,itevaluatestheexpressionattachedtotheevent,whichinthiscasewillinvokeamethoddefinedonthecomponent.
![Page 118: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/118.jpg)
There'smore...Sincecapturingtheeventmustoccurinanexpression,youareprovidedwithan$eventparameterintheexpression,whichwillusuallybepassedasanargumenttothehandlermethod.ThisissimilartotheprocessinAngular1.Inspectingthis$eventobjectrevealsitasthevanillaclickeventgeneratedbythebrowser:
[app/article.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'article',
template:`
<h1>{{title}}</h1>
<p>Shares:{{shareCt}}</p>
<button(click)="share($event)">Share</button>
`
})
exportclassArticleComponent{
title:string='PoliceApprehendTiramisuThieves';
shareCt:number=0;
share(e:Event):void{
console.log(e);//MouseEvent
++this.shareCt;
}
}
Note
Youwillalsonotethattheshare()methodhereisdemonstratinghowtypingcanbeappliedtotheparametersandthereturnvalueofthemethod:
myMethod(arg1:arg1type,arg2:arg2type,...):returnType
Aswithmemberbinding,youarealsoabletouseanalternateeventbindingsyntaxifyoudonotcaretouseasetofparentheses.Prefixingon-totheeventattributewillprovideyouwithidenticalbehavior:
import{Component}from'@angular/core';
@Component({
selector:'article',
template:`
<h1>{{title}}</h1>
<p>Shares:{{shareCt}}</p>
<buttonon-click="share($event)">Share</button>
`
})
exportclassArticle{
![Page 119: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/119.jpg)
title:string='PoliceApprehendTiramisuThieves';
shareCt:number=0;
share(e:Event):void{
++this.shareCt;
}
}
![Page 120: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/120.jpg)
SeealsoBindingtonativeelementattributesshowshowAngular2interfaceswithHTMLelementattributes.GeneratingandcapturingcustomeventsusingEventEmitterdetailshowtopropagateinformationupwardsbetweencomponents.AttachingbehaviortoDOMelementswithdirectivesdemonstrateshowtoattachbehaviortoelementswithattributedirectives.AttributepropertybindingshowsAngular2'scleverwayofdeepreferencingelementproperties.
![Page 121: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/121.jpg)
GeneratingandcapturingcustomeventsusingEventEmitterInthewakeofthedisappearanceof$scope,Angularwasleftwithavoidforpropagatinginformationupthecomponenttree.Thisvoidisfilledinpartbycustomevents,andtheyrepresenttheYintothedownwarddatabindingYang.
Note
Thecode,links,andaliveexampleofthisareavailableathttp://ngcookbook.herokuapp.com/8611/.
![Page 122: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/122.jpg)
GettingreadySupposeyouhadanArticleapplicationasfollows:
[app/text-editor.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'text-editor',
template:`
<textarea></textarea>
`
})
exportclassTextEditorComponent{}
[app/article.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'article',
template:`
<h1>{{title}}</h1>
<p>Wordcount:{{wordCount}}</p>
<text-editor></text-editor>
`
})
exportclassArticleComponent{
title:string=`
MaternityWardResortstoRockPaperScissorsFollowing
BabyMixup`;
wordCount:number=0;
updateWordCount(e:number):void{
this.wordCount=e;
}
}
Thisapplicationwillideallybeabletoreadthecontentoftextareawhenthereisachange,andalsocountthenumberofwordsandreportittotheparentcomponenttobeinterpolated.Asisthecase,noneofthisisimplemented.
![Page 123: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/123.jpg)
Howtodoit...AdeveloperthinkingintermsofAngular1wouldattachng-modeltotextarea,use$scope.$watchonthemodeldata,andpassthedatatotheparentvia$scopeorsomeothermeans.Unfortunatelyforsuchadeveloper,theseconstructsareradicallydifferentornon-existentinAngular2.Fearnot!Thenewimplementationismoreexpressive,moremodular,andmuchcleaner.
Capturingtheeventdata
ngModelstillexistsinAngular2,anditwouldcertainlybesuitablehere.However,youdon'tactuallyneedtousengModelatall,andinthiscase,itallowsyoutobemoreexplicitaboutwhenyourapplicationtakesaction.First,youmustretrievethetextfromthetextareaelementandmakeitusableinTextEditorComponent:
[app/text-editor.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'text-editor',
template:`
<textarea(keyup)="emitWordCount($event)"></textarea>
`
})
exportclassTextEditorComponent{
emitWordCount(e:Event):void{
console.log(
(e.target.value.match(/\S+/g)||[]).length);
}
}
Excellent!Asclaimed,youdon'tneedtousengModeltoacquiretheelement'scontents.What'smore,youarenowabletoutilizenativebrowsereventstoexplicitlydefinewhenyouwantTextEditorComponenttotakeaction.
Withthis,youaresettingalisteneronthenativebrowser'skeyupevent,firedfromthetextareaelement.Thiseventhasatargetpropertythatexposesthevalueofthetextintheelement,whichisexactlywhatyouwanttouse.Thecomponentthenusesasimpleregularexpressiontocountthenumberofnon-whitespacesequences.Thisisyourwordcount.
Emittingacustomevent
console.logdoesnothelptoinformtheparentcomponentofthewordcountyouarecalculating.Todothis,youneedtocreateacustomeventandemititupwards:
[app/text-editor.component.ts]
![Page 124: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/124.jpg)
import{Component,EventEmitter,Output}from'@angular/core';
@Component({
selector:'text-editor',
template:`
<textarea(keyup)="emitWordCount($event)"></textarea>
`
})
exportclassTextEditorComponent{
@Output()countUpdate=newEventEmitter<number>();
emitWordCount(e:Event):void{
this.countUpdate.emit(
(e.target.value.match(/\S+/g)||[]).length);
}
}
Usingthe@OutputdecoratorallowsyoutoinstantiateanEventEmittermemberonthechildcomponentthattheparentcomponentwillbeabletolistento.ThisEventEmittermember,likeanyotherclassmember,isavailableasthis.countUpdate.Thechildcomponentisabletosendeventsupwardbyinvokingtheemit()methodonthismember,andtheargumenttothismethodisthevaluewhichyouwishtosendtotheevent.Here,sinceyouwanttosendanintegercountofwords,youinstantiatetheEventEmittermemberbytypingitasa<number>emitter.
Listeningforcustomevents
Sofar,youarethroughwithonlyhalftheimplementation,asthesecustomeventsarebeingfiredoffintotheetherofthebrowserwithnolisteners.Sincethemethodyouneedtouseisalreadydefinedontheparentcomponent,allyouneedtodoishookintotheeventlistenertothatmethod.
The()templatesyntaxisusedtoaddlistenerstoevents,andAngulardoesnotdiscriminatebetweennativebrowsereventsandeventsthatoriginatefromEventEmitters.Thus,sinceyoudeclaredthechildcomponent'sEventEmitteras@Output,youwillbeabletoaddalistenerforeventsthatcomefromitontheparentcomponent,asfollows:
[app/article.component.ts]
import{Component}from'angular2/core';
@Component({
selector:'article',
template:`
<h1>{{title}}</h1>
<p>Wordcount:{{wordCount}}</p>
<text-editor(countUpdate)="updateWordCount($event)">
</text-editor>
`
})
exportclassArticleComponent{
![Page 125: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/125.jpg)
title:string=`
MaternityWardResortstoRockPaperScissorsFollowing
BabyMixup`;
wordCount:number=0;
updateWordCount(e:number):void{
this.wordCount=e;
}
}
Withthis,yourapplicationshouldcorrectlycountthewordsintheTextEditorcomponentandupdatethevalueintheArticlecomponent.
![Page 126: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/126.jpg)
Howitworks...Using@OutputinconjunctionwithEventEmitterallowsyoutocreatechildcomponentsthatexposeanAPIfortheparentcomponenttohookinto.TheEventEmittersendstheeventsupwardwithitsemitmethod,andtheparentcomponentcansubscribetothembybindingtotheemitteroutput.
Theflowofthisexampleisasfollows:
1. Thekeystrokeinsidetextareacausesthenativebrowser'skeyupevent.2. TheTextEditorcomponenthasalistenersetonthisevent,sotheattachedexpressionis
evaluated,whichwillinvokeemitWordCount.3. TheemitWordCountinspectstheEventobjectandextractsthetextfromtheassociated
DOMelement.ItparsesthetextforthenumberofcontainedwordsandinvokestheEventEmitter.emitmethod.
4. TheEventEmittermethodemitsaneventassociatedwiththedeclaredcountUpdate@Outputmember.
5. TheArticleComponentseesthiseventandinvokestheattachedexpression.TheexpressioninvokesupdateWordCount,passingintheeventvalue.
6. TheArticleComponentpropertyisupdated,andsincethisvalueisinterpolatedintheview,Angularhonorsthedatabindingprocessbyupdatingtheview.
![Page 127: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/127.jpg)
There'smore...ThenameEventEmitterisabitdeceiving.Ifyou'repayingattention,youwillnoticethattheparentcomponentmembermethodinvokedinthehandlerdoesnothaveatypedparameter.Youwillalsonoticethatyouaredirectlyassigningthatparametertothemembertypedasnumber.Thisshouldseemoddasthetemplateexpressioninvokingthemethodispassing$event,whichyouusedearlierasabrowserEventobject.Thisseemslikeamismatchbecauseitisamismatch.Ifyoubindtonativebrowserevents,theeventyouwillobservecanonlybethenativebrowsereventobject.Ifyoubindtocustomevents,theeventyouwillobserveiswhateverwaspassedwhenemitwasinvoked.Here,theparametertoupdateWordCount()issimplytheintegeryouprovidedwiththis.countUpdate.emit().
Alsonotethatyouarenotrequiredtoprovideavaluefortheemittedevent.YoucanstilluseEventEmittertosignaltoaparentcomponentthataneventhasoccurredandthatitshouldevaluatetheboundexpression.Todothis,yousimplycreateanuntypedemitterwithnewEventEmitter()andinvokeemit()withnoarguments.$eventshouldbeundefined.
Itisnotpossibletopassmultiplevaluesascustomevents.Tosendmultiplepiecesofdata,youneedtocombinethemintoanobjectorarray.
![Page 128: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/128.jpg)
SeealsoBindingtonativeelementattributesshowshowAngular2interfaceswithHTMLelementattributes.Registeringhandlersonnativebrowsereventsdemonstrateshowyoucaneasilyattachbehaviortobrowserevents.
![Page 129: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/129.jpg)
AttachingbehaviortoDOMelementswithdirectivesInthecourseofcreatingapplications,youwilloftenfinditusefultobeabletoattachcomponent-stylebehaviortoDOMelements,butwithouttheneedtohavetemplating.IfyouweretoattempttoconstructanAngular2componentwithoutprovidingatemplateinsomeway,youwillmeetwithasternerrortellingyouthatsomeformoftemplateisrequired.
HereliesthedifferencebetweenAngular2componentsanddirectives:componentshaveviews(whichcantaketheformofatemplate,templateUrl,or@Viewdecorator),whereasdirectivesdonot.Theyotherwisebehaveidenticallyandprovideyouwiththesamebehavior.
Note
Thecode,links,andaliveexampleofthisareavailableathttp://ngcookbook.herokuapp.com/3292/.
![Page 130: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/130.jpg)
GettingreadySupposeyouhavethefollowingapplication:
[app/article.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'article',
template:`<h1>{{title}}</h1>`,
styles:[`
h1{
text-overflow:ellipsis;
white-space:nowrap;
overflow:hidden;
max-width:300px;
}
`]
})
exportclassArticleComponent{
title:string=`PresidentialCandidatesRespondto
AllegationsInvolvingAbilitytoDunk`;
}
Currently,thisapplicationisusingCSStotruncatethearticletitlewithanellipsis.YouwouldliketoexpandthisapplicationsothattheArticlecomponentrevealstheentiretitlewhenclickedbysimplyaddinganHTMLattribute.
![Page 131: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/131.jpg)
Howtodoit...Beginbydefiningthebasicclassthatwillpowertheattributedirectiveandaddittotheapplicationmodule:
[app/click-to-reveal.directive.ts]
exportclassClickToRevealDirective{
reveal(target){
target.style['white-space']='normal';
}
}
[app/app.module.ts]
import{NgModule}from'@angular/core';
import{BrowserModule}from'@angular/platform-browser';
import{ArticleComponent}from'./article.component';
import{ClickToRevealDirective}
from'./click-to-reveal.directive';
@NgModule({
imports:[
BrowserModule
],
declarations:[
ArticleComponent,
ClickToRevealDirective
],
bootstrap:[
ArticleComponent
]
})
exportclassAppModule{}
First,youmustdecoratetheClickToRevealDirectiveclassas@DirectiveanduseitinsidetheArticlecomponent:
[app/click-to-reveal.directive.ts]
import{Directive}from'@angular/core';
@Directive({
selector:'[click-to-reveal]'
})
exportclassClickToRevealDirective{
reveal(target){
target.style['white-space']='normal';
}
}
![Page 132: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/132.jpg)
Next,addtheattributetotheelementthatyouwishtoapplythedirectiveto:
[app/article.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'article',
template:`<h1click-to-reveal>{{title}}</h1>`,
styles:[`
h1{
text-overflow:ellipsis;
white-space:nowrap;
overflow:hidden;
max-width:300px;
}
`]
})
exportclassArticleComponent{
title:string;
constructor(){
this.title=`PresidentialCandidatesRespondto
AllegationsInvolvingAbilitytoDunk`;
}
}
Note
NotethattheDirectiveisusinganattributeCSSselectortoassociateitselfwithanyelementsthathaveclick-to-reveal.ThisofcourseapproximatesanAngular1attribute'sdirectivebehavior,butthisformisfarmoreflexiblesinceitcanwieldtheinnatematchabilityofselectors.
NowthattheArticlecomponentisawareofClickToRevealDirective,youmustprovideittheabilitytoattachitselftoclickevents.
AttachingtoeventswithHostListeners
Anattentivedeveloperwillhavenoticedthatupuntilthispointinthechapter,youhavecreatedcomponentsthatlistentotheeventsgeneratedbythechildren.Thisisnoproblemsinceyoucanexpressivelysetlistenersinaparentcomponenttemplateonthechildtag.
However,inthissituation,youarelookingtoaddalistenertothesameelementthatthedirectiveisbeingattachedto.What'smore,youdonothaveagoodwayofaddinganeventbindingexpressiontothetemplatefrominsideadirective.Ideally,youwouldliketonothavetoexposethismethodfrominsidethedirective.Howshouldyouproceedthen?
ThesolutionistoutilizeanewAngularconstructcalledHostListener.Simplyput,itallowsyoutocaptureself-originatingeventsandhandletheminternally:
![Page 133: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/133.jpg)
[app/click-to-reveal.directive.ts]
import{Directive,HostListener}from'@angular/core';
@Directive({
selector:'[click-to-reveal]'
})
exportclassClickToRevealDirective{
@HostListener('click',['$event.target'])
reveal(target){
target.style['white-space']='normal';
}
}
Withthis,clickeventsonthe<h1>elementshouldsuccessfullyinvokethereveal()method.
![Page 134: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/134.jpg)
Howitworks...Thedirectiveneedsawaytoattachtonativeclickevents.Furthermore,itneedsawaytocaptureobjectssuchas$eventthatAngularprovidestoyou;theseobjectswouldnormallybecapturedinthebindingexpression.
@HostListenerdecoratesadirectivemethodtoactasthedesignatedeventhandler.Thefirstargumentinitsinvocationistheeventidentificationstring(here,click,butitcouldjustaseasilybeacustomeventfromEventEmitter),andthesecondargumentisanarrayofstringargumentsthatareevaluatedasexpressions.
![Page 135: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/135.jpg)
There'smore...YouarenotrestrictedtooneHostListenerinsideadirective.Usingitmerelyassociatesaneventwithadirectivemethod.SoyouareabletostackmultipleHostListenerdeclarationsonasinglehandler,forexample,tolistenforbothaclickandmouseoverevent.
![Page 136: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/136.jpg)
SeealsoUsingdecoratorstobuildandstyleasimplecomponentdescribesthebuildingblocksofimplementinganAngular2componentPassingmembersfromaparentcomponentintoachildcomponentgoesthroughthebasicsofdownwarddataflowbetweencomponentsUsingngForandngIfstructuraldirectivesformodel-basedDOMcontrolinstructsyouinhowtoutilizesomeofAngular2'scorebuilt-indirectivesAttributepropertybindingshowsAngular2'scleverwayofdeepreferencingelementproperties
![Page 137: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/137.jpg)
ProjectingnestedcontentusingngContentUtilizingcomponentsasstandalonetagsthatareself-containedandwhollymanagetheircontentsisacleanpattern,butyouwillfrequentlyfindthatyourcomponenttagsdemandthattheyenclosecontent.
Note
Thecode,links,andaliveexampleofthisareavailableathttp://ngcookbook.herokuapp.com/6172/.
![Page 138: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/138.jpg)
GettingreadySupposeyouhadthefollowingapplication:
[app/ad-section.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'ad-section',
template:`
<ahref="#">{{adText}}</a>
`
})
exportclassAdSectionComponent{
adText:string='Selfiesticks40%off!';
}
[app/article.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'article',
template:`
<h1>{{title}}</h1>
<p>U.S.senatorsareupinarmsfollowingtherecentruling
strippingthemoftheirbelovedselfiesticks.</p>
<p>Abipartisancommitteedraftedaresolutiontosmuggle
selfiesticksontothefloorbyanymeansnecessary.</p>
`
})
exportclassArticleComponent{
title:string='SelfieSticksBannedfromSenateFloor';
}
YourobjectivehereistomodifythissothattheAdSectioncomponentcanbeincorporatedintotheArticlecomponentwithoutinterferingwithitscontent.
![Page 139: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/139.jpg)
Howtodoit...TheAdSectioncomponentwantstoincorporateanextraelementaroundtheexistingArticlecontent.Thisiseasytoaccomplish:
[app/article.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'article',
template:`
<h1>{{title}}</h1>
<ad-section>
<p>U.S.senatorsareupinarmsfollowingtherecentruling
strippingthemoftheirbelovedselfiesticks.</p>
<p>Abipartisancommitteedraftedaresolutiontosmuggle
selfiesticksontothefloorbyanymeansnecessary.</p>
</ad-section>
`
})
exportclassArticleComponent{
title:string='SelfieSticksBannedfromSenateFloor';
}
Youwillnoticethoughthatthisisadestructiveoperation.WhenrenderingAdSectionComponent,Angularisnotconcernedaboutanycontentthatisinsideit.ItseesthatAdSectionComponenthasatemplateassociatedwithit,anditdutifullysupplantstheelement'scontentswithit;[email protected],thatwipesoutthe<p>tagsthatyouwanttoretain.
Topreservethem,youmustinstructAngularhowitshouldmanagewrappedcontent.Thiscanbeaccomplishedwithan<ng-content>tag:
[app/ad-section.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'ad-section',
template:`
<ahref="#">{{adText}}</a>
<ng-contentselect="p"></ng-content>
`
})
exportclassAdSectionComponent{
adText:string='Selfiesticks40%off!';
}
Withthis,theadanchorelementisinsertedbeforethewrappedcontent.
![Page 140: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/140.jpg)
Howitworks...Similartohowng-transcludeworkedinAngular1,ng-contentservestointerpolatethecomponenttag'swrappedcontentintoitstemplate.Thedifferencehereisthatng-contentusesaselectattributetotargetthewrappedelements.ThisissimplyaCSSselector,operatinginthesamewayinwhich@ComponentdecoratorshandletheselectorpropertyinComponentMetadata.
![Page 141: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/141.jpg)
There'smore...Theselectattributeinthisexamplewassuperfluous,asitendedupselectingtheentiretyofthewrappedcontent.Ofcourse,iftheselectvalueonlymatchedsomeofthewrappedcontent,itwouldteaseoutonlythoseelementsandinterpolatethem.<ng-content>willbydefaultinserttheentiretyofthewrappedcontentifyoudeclinetoprovideitwithaselectvalue.
AlsonotethattheselectattributeisalimitedCSSselector.Itisnotcapableofperformingcomplexselectionssuchas:nth-child,anditisonlyabletotargettop-levelelementsinsidethewrappingtags.Forexample,inthisapplication,theparagraphtaginside<div><p>Blah</p></div>wouldnotbeincludedwithaselect="p"attributevalue.
![Page 142: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/142.jpg)
SeealsoReferencingaparentcomponentfromachildcomponentdescribeshowacomponentcangainadirectreferencetoitsparentviainjectionConfiguringmutualparent-childawarenesswithViewChildandforwardRefinstructsyouonhowtoproperlyuseViewChildtoreferencechildcomponentobjectinstancesConfiguringMutualParent-ChildAwarenesswithContentChildandforwardRefinstructsyouonhowtoproperlyuseContentChildtoreferencechildcomponentobjectinstances
![Page 143: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/143.jpg)
UsingngForandngIfstructuraldirectivesformodel-basedDOMcontrolAnydeveloperthathasusedaclientframeworkisintimatelyfamiliarwithtwobasicoperationsinanapplication:iterativerenderingfromacollectionandconditionalrendering.ThenewAngular2implementationslookabitdifferentbutoperateinmuchthesameway.
Note
Thecode,links,andaliveexampleareavailableathttp://ngcookbook.herokuapp.com/3211/.
![Page 144: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/144.jpg)
GettingreadySupposeyouhadthefollowingapplication:
[app/article-list.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'article-list',
template:''
})
exportclassArticleListComponent{
articles:Array<Object>=[
{title:'Foo',active:true},
{title:'Bar',active:false},
{title:'Baz',active:true}
];
}
Yourobjectiveistoiteratethroughthisanddisplaythearticletitleonlyifitissetasactive.
![Page 145: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/145.jpg)
Howtodoit...SimilartoAngular1,Angular2providesyouwithdirectivestoaccomplishthistask.ngForisusedtoiteratethroughthearticlescollection:
[app/article-list.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'article-list',
template:`
<div*ngFor="letarticleofarticles;leti=index">
<h1>
{{i}}:{{article.title}}
</h1>
</div>
`
})
exportclassArticleListComponent{
articles:Array<Object>=[
{title:'Foo',active:true},
{title:'Bar',active:false},
{title:'Baz',active:true}
];
}
SimilartongFor,ngIfcanbeincorporatedasfollows:
[app/article-list.component.ts]
import{Component}from'angular2/core';
@Component({
selector:'article-list',
template:`
<div*ngFor="letarticleofarticles;leti=index">
<h1*ngIf="article.active">
{{i}}:{{article.title}}
</h1>
</div>
`
})
exportclassArticleListComponent{
articles:Array<Object>=[
{title:'Foo',active:true},
{title:'Bar',active:false},
{title:'Baz',active:true}
];
}
![Page 146: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/146.jpg)
Withthis,youwillseethatonlytheobjectsinthearticlesarraywithactive:truearerendered.
![Page 147: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/147.jpg)
Howitworks...Atfirst,theasteriskandpoundsignnotationcouldbeconfusingformanydevelopers.Formostapplications,youwillnotneedtoknowhowthissyntacticsugaractuallyworksbehindthescenes.
Inreality,Angulardecomposesallthestructuraldirectivesprefixedwith*toutilizeatemplate.First,AngularbreaksdownngForandngIftousethetemplatedirectivesonthesameelement.Thesyntaxdoesnotchangemuchyet:
[app/article-list.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'article-list',
template:`
<divtemplate="ngForletarticleofarticles;leti=index">
<h1template="ngIfarticle.active">
{{i}}:{{article.title}}
</h1>
</div>
`
})
exportclassArticleList{
articles:Array<Object>,
constructor(){
this.articles=[
{title:'Foo',active:true},
{title:'Bar',active:false},
{title:'Baz',active:true}
];
}
}
Followingthis,Angulardecomposesthistemplatedirectiveintoawrapping<template>element:
[app/article-list.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'article-list',
template:`
<templatengForlet-article[ngForOf]="articles"let-i="index">
<div>
<template[ngIf]="article.active">
<h1>
{{i}}:{{article.title}}
![Page 148: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/148.jpg)
</h1>
</template>
</div>
</template>
`
})
exportclassArticleList{
articles:Array<Object>,
constructor(){
this.articles=[
{title:'Foo',active:true},
{title:'Bar',active:false},
{title:'Baz',active:true}
];
}
}
Note
Notethatboththeversionsdisplayedhere—eitherusingthetemplatedirectiveorthe<template>element—willbehaveidenticallytousingtheoriginalstructuraldirectives.Thatbeingsaid,theregenerallywillnotbeareasontoeverdoitthisway;thisismerelyademonstrationtoshowyouhowAngularunderstandsthesedirectivesbehindthescenes.
Tip
WheninspectingtheactualDOMoftheseexamplesusingngForandngIf,youwillbeabletoseeAngular'sautomaticallyaddedHTMLcommentsthatdescribehowitinterpretsyourmarkupandtranslatesitintotemplatebindings.
![Page 149: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/149.jpg)
There'smore...ThetemplateelementisbornoutoftheWebComponents'specification.TemplatesareadefinitionofhowaDOMsubtreecaneventuallybedefinedasaunit,buttheelementsthatappearwithinitarenotcreatedoractiveuntilthetemplateisactuallyusedtocreateaninstancefromthattemplate.Notallwebbrowserssupportwebcomponents,soAngular2doesapolyfilltoemulatepropertemplatebehavior.
Inthisway,thengFordirectiveisactuallycreatingawebcomponenttemplatethatutilizesthesubordinatengForOfbinding,whichisapropertyofNgFor.EachinstanceinarticleswillusethetemplatetocreateaDOMsection,andwithinthissection,thearticleandindextemplatevariableswillbeavailableforinterpolation.
![Page 150: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/150.jpg)
SeealsoUsingdecoratorstobuildandstyleasimplecomponentdescribesthebuildingblocksofimplementinganAngular2componentPassingmembersfromaparentcomponentintoachildcomponentgoesthroughthebasicsofdownwarddataflowbetweencomponentsBindingtonativeelementattributesshowshowAngular2interfaceswithHTMLelementattributesAttachingbehaviortoDOMelementswithdirectivesdemonstrateshowtoattachbehaviortoelementswithattributedirectivesAttributepropertybindingshowsAngular2'scleverwayofdeepreferencingelementpropertiesUtilizingcomponentlifecyclehooksgivesanexampleofhowyoucanintegratewithAngular2'scomponentrenderingflow.
![Page 151: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/151.jpg)
ReferencingelementsusingtemplatevariablesManydeveloperswillbeginwithAngular2andreachforsomethingthatresemblesthetrustworthyng-modelinAngular1.NgModelexistsinAngular2,butthereisanewwayofreferencingelementsinthetemplate:localtemplatevariables.
Note
Thecode,links,andaliveexampleofthisareavailableathttp://ngcookbook.herokuapp.com/5094/.
![Page 152: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/152.jpg)
GettingreadySupposeyouhadthefollowingapplicationandwantedtodirectlyaccesstheinputelement:
[app/article.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'article',
template:`
<input>
<h1>{{title}}</h1>
`
})
exportclassArticleComponent{}
![Page 153: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/153.jpg)
Howtodoit...Angular2allowsyoutohavea#assignmentwithinthetemplateitself,whichcanconsequentlybereferencedfrominsidethetemplate.Forexample,refertothefollowingcode:
[app/article.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'article',
template:`
<input#title>
<h1>{{title}}</h1>
`
})
exportclassArticleComponent{}
Withthis,youwillsee[objectHTMLInputElement](orsomethingsimilar,dependingonyourbrowser)interpolatedintothe<h1>tag.Thismeansthatthe#titleinsidethe<input>tagisnowdirectlyreferencingtheelementobject,whichofcoursemeansthatthevalueoftheelementshouldbeavailableforyou.
Don'tgettooexcitedjustyet!Ifyouattempttointerpolatetitle.valueandthenmanipulatetheinputfield,youwillnotseethebrowserupdate.ThisisbecauseAngular2nolongersupportsbidirectionaldatabindinginthisway.Fearnot,forthesolutiontothisproblemlieswithinthenewAngular2databindingpattern.
AngularwilldeclinetoupdatetheDOMuntilitthinksitneedsto.Thisneedisdeterminedbywhatbehaviorintheapplicationmightcausetheinterpolateddatatochange.Aboundevent,whichwillpropagateupwardsthroughthecomponenttree,maycauseadatachange.Thus,youcancreateaneventbindingonanelement,andthemerepresenceofthiseventbindingwilltriggerAngulartoupdatethetemplate:
[app/article.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'article',
template:`
<input#title(keyup)="0">
<h1>{{title.value}}</h1>
`
})
exportclassArticleComponent{}
Here,thekeyupeventfromthetextinputisboundtoanexpressionthatiseffectivelyano-op.
![Page 154: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/154.jpg)
SincetheeventwilltriggeranupdateoftheDOM,youcansuccessfullypulloutthelatestvaluepropertyfromthetitleinputelementobject.Withthis,youhavesuccessfullyboundtheinputvaluetotheinterpolatedstring.
![Page 155: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/155.jpg)
There'smore...Ifyouaren'tcrazyaboutthe#notation,youcanalwaysreplaceitwithval-andstillachieveidenticalbehavior:
[app/article.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'article',
template:`
<inputval-title(keyup)="0">
<h1>{{title.value}}</h1>
`
})
exportclassArticleComponent{}
Also,it'simportanttorecallthatthesetemplatevariablesareonlyaccessiblewithinthetemplate.Ifyouwanttopassthembacktothecontroller,you'llhavetouseitasahandlerargument:
[app/article.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'article',
template:`
<input#title(keyup)="setTitle(title.value)">
<h1>{{myTitle}}</h1>
`
})
exportclassArticleComponent{
myTitle:string;
setTitle(val:string):void{
this.myTitle=val;
}
}
![Page 156: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/156.jpg)
SeealsoReferencingaparentcomponentfromachildcomponentdescribeshowacomponentcangainadirectreferencetoitsparentviainjectionConfiguringmutualparent-childawarenesswithViewChildandforwardRefinstructsyouonhowtoproperlyuseViewChildtoreferencechildcomponentobjectinstancesConfiguringmutualparent-childawarenesswithContentChildandforwardRefinstructsyouonhowtoproperlyuseContentChildtoreferencechildcomponentobjectinstances
![Page 157: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/157.jpg)
AttributepropertybindingOneofthegreatnewbenefitsofthenewAngularbindingstyleisthatyouareabletomoreaccuratelytargetwhatyouarebindingto.Formerly,theHTMLattributethatwasusedasadirectiveordatatokenwassimplyusedasamatchingidentifier.Now,youareabletousepropertybindingswithinthebindingmarkupforboththeinputandoutput.
Note
Thecode,links,andaliveexampleofthisisavailableathttp://ngcookbook.herokuapp.com/8565/.
![Page 158: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/158.jpg)
GettingreadySupposeyouhadthefollowingapplication:
[app/article.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'article',
template:`
<input#title(keydown)="setTitle(title.value)">
<h1>{{myTitle}}</h1>
`
})
exportclassArticleComponent{
myTitle:string;
setTitle(val:string):void{
this.myTitle=val;
}
}
Yourobjectiveistomodifythissothatitexhibitsthefollowingbehavior:
The<h1>tagisnotupdatedwiththevalueoftheinputfielduntiltheuserstrikestheEnterkey.Ifthe<h1>valuedoesnotmatchthevalueinthetitleinput(callthisstate"stale"),thetextcolorshouldbered.Ifitdoesmatch,itshouldbegreen.
![Page 159: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/159.jpg)
Howtodoit...BothofthesebehaviorscanbeachievedwithAngular2'sattributepropertybinding.First,youcanchangetheeventbindingsothatonlyanEnterkeywillinvokethecallback:
[app/article.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'article',
template:`
<input#title(keydown.enter)="setTitle(title.value)">
<h1>{{myTitle}}</h1>
`
})
exportclassArticleComponent{
myTitle:string;
setTitle(val:string):void{
this.myTitle=val;
}
}
Next,youcanuseAngular'sstylebindingtodirectlyassignavaluetoastyleproperty.ThisrequiresaddingaBooleantothecontrollerobjecttomaintainthestate:
[app/article.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'article',
template:`
<input#title(keydown.enter)="setTitle(title.value)">
<h1[style.color]="isStale?'red':'green'">{{myTitle}}</h1>
`
})
exportclassArticleComponent{
myTitle:string='';
isStale:boolean=false;
setTitle(val:string):void{
this.myTitle=val;
}
}
Closer,butthisstillprovidesnowayofreachingastalestate.Toachievethis,addanotherkeydowneventbinding:
[app/article.component.ts]
![Page 160: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/160.jpg)
import{Component}from'@angular/core';
@Component({
selector:'article',
template:`
<input#title
(keyup.enter)="setTitle(title.value)"
(keyup)="checkStale(title.value)">
<h1[style.color]="isStale?'red':'green'">
{{myTitle}}
</h1>
`
})
exportclassArticleComponent{
myTitle:string='';
privateisStale:boolean=false;
setTitle(val:string):void{
this.myTitle=val;
}
checkStale(val:string):void{
this.isStale=val!==this.myTitle;
}
}
Withthis,thecolorofthe<h1>tagshouldcorrectlykeeptrackofwhetherthedataisstaleornot!
![Page 161: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/161.jpg)
Howitworks...ThesimpleexplanationisthatAngularprovidesyouwithalotofsyntacticalsugar,butataverybasiclevelwithoutinvolvingalotofcomplexity.Ifyouweretoinspectthekeyupevent,youwouldofcoursenoticethatthereisnoenterpropertyavailable.AngularoffersyouanumberofthesepseudopropertiessothatcheckingthekeyCodeofthepressedkeyisnotnecessary.
Inasimilarway,Angularalsoallowsyoutobindtoandaccessstylepropertiesdirectly.Itisinferredthatthestylebeingaccessedreferstothehostelement.
![Page 162: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/162.jpg)
There'smore...Noteherethatyouhaveassignedtwohandlerstowhatisessentiallythesameevent.Notonlythis,butrearrangingtheorderofthebindingmarkupwillbreakthisapplication'sdesiredbehavior.
Note
Whentwohandlersareassignedtothesameevent,Angularwillexecutethehandlersintheorderthattheyaredefinedinthemarkup.
![Page 163: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/163.jpg)
SeealsoBindingtonativeelementattributesshowshowAngular2interfaceswithHTMLelementattributesRegisteringhandlersonnativebrowsereventsdemonstrateshowyoucaneasilyattachbehaviortobrowsereventsGeneratingandcapturingcustomeventsusingEventEmitterdetailshowtopropagateinformationupwardsbetweencomponentsAttachingbehaviortoDOMelementswithdirectivesdemonstrateshowtoattachbehaviortoelementswithattributedirectives
![Page 164: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/164.jpg)
UtilizingcomponentlifecyclehooksAngular'scomponentrenderingprocesshasalargenumberoffacets,anddifferenttypesofdataandreferenceswillbecomeavailableatdifferenttimes.Toaccountforthis,Angular2allowscomponentstosetcallbacks,whichwillbeexecutedatdifferentpointsinthecomponent'slifecycle.
Note
Thecode,links,andaliveexampleofthisareavailableathttp://ngcookbook.herokuapp.com/2048/.
![Page 165: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/165.jpg)
GettingreadySupposeyoubeganwiththefollowingapplication,whichsimplyallowstheadditionandremovalofarticlesfromasingleinput:
[app/article-list.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'article-list',
template:`
<input(keyup.enter)="add($event)">
<article*ngFor="lettitleoftitles;leti=index"
[articleTitle]="title">
<button(click)="remove(i)">X</button>
</article>
`
})
exportclassArticleListComponent{
titles:Array<string>=[];
add(e:Event):void{
this.titles.push(e.target.value);
e.target.value='';
}
remove(index:number){
this.titles.splice(index,1);
}
}
[app/article.component.ts]
import{Component,Input}from'@angular/core';
@Component({
selector:'article',
template:`
<h1>
<ng-content></ng-content>{{articleTitle}}
</h1>
`
})
exportclassArticleComponent{
@Input()articleTitle:string;
}
Yourobjectiveistouselifecyclehookstokeeptrackoftheprocessofaddingandremovingoperations.
![Page 166: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/166.jpg)
Howtodoit...Angularallowsyoutoimporthookinterfacesfromthecoremodule.Theseinterfacesaremanifestedasclassmethods,whichareinvokedattheappropriatetime:
[app/article.component.ts]
import{Component,Input,ngOnInit,ngOnDestroy}
from'@angular/core';
@Component({
selector:'article',
template:`
<h1>
<ng-content></ng-content>{{articleTitle}}
</h1>
`
})
exportclassArticleComponentimplementsOnInit,OnDestroy{
@Input()articleTitle:string;
ngOnInit(){
console.log('created',this.articleTitle);
}
ngOnDestroy(){
console.log('destroyed',this.articleTitle);
}
}
Withthis,youshouldseelogseachtimeanewArticleComponentisaddedorremoved.
![Page 167: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/167.jpg)
Howitworks...Differenthookshavedifferentsemanticmeanings,buttheywilloccurinawell-definedorder.Eachhook'sexecutionguaranteesthatacertainbehaviorofacomponentisjustcompleted.
Thehooksthatarecurrentlyavailabletoyouintheorderofexecutionareasfollows:
ngOnChanges
ngOnInit
ngDoCheck
ngAfterContentInit
ngAfterContentChecked
ngAfterViewInit
ngAfterContentChecked
ngOnDestroy
Itisalsopossibleforthird-partylibrariestoextendtheseandaddtheirownhooks.
![Page 168: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/168.jpg)
There'smore...Usinghooksisoptional,andAngularwillonlyinvokethemifyouhavedefinedthem.Theuseoftheimplementsinterfacedeclarationisoptional,butitwillsignaltotheTypeScriptcompilerthatacorrespondingmethodshouldbeexpected,whichisobviouslyagoodpractice.
![Page 169: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/169.jpg)
SeealsoReferencingaparentcomponentfromachildcomponentdescribeshowacomponentcangainadirectreferencetoitsparentviainjectionConfiguringmutualparent-childawarenesswithViewChildandforwardRefinstructsyouonhowtoproperlyuseViewChildtoreferencechildcomponentobjectinstancesConfiguringmutualparent-childawarenesswithContentChildandforwardRefinstructsyouonhowtoproperlyuseContentChildtoreferencechildcomponentobjectinstances
![Page 170: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/170.jpg)
ReferencingaparentcomponentfromachildcomponentInthecourseofbuildinganapplication,youmayencounterascenariowhereitwouldbeusefultoreferenceaparentcomponentfromachildcomponent,suchastoinspectmemberdataorinvokepublicmethods.InAngular2,thisisactuallyquiteeasytoaccomplish.
Note
Thecode,links,andaliveexampleofthisareavailableathttp://ngcookbook.herokuapp.com/4907/.
![Page 171: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/171.jpg)
GettingreadySupposeyoubeginwiththefollowingArticleComponent:
[app/article.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'article',
template:`
<feedback[val]="likes"></feedback>
`
})
exportclassArticleComponent{
likes:number=0;
incrementLikes():void{
this.likes++;
}
}
Yourobjectiveistoimplementthefeedbackcomponentsothatitdisplaysthenumberoflikespassedtoit,buttheparentcomponentcontrolstheactuallikecountandpassesthatvaluein.
![Page 172: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/172.jpg)
Howtodoit...Beginbyimplementingthebasicstructureofthechildcomponent:
[app/feedback.component.ts]
import{Component,Input}from'@angular/core';
@Component({
selector:'feedback',
template:`
<h1>Numberoflikes:{{val}}</h1>
<button(click)="likeArticle()">Likethisarticle!</button>
`
})
exportclassFeedbackComponent{
@Input()val:number;
likeArticle():void{}
}
Sofar,noneofthisshouldsoundsurprising.Clickingonthebuttoninvokesanemptymethod,andyouwantthismethodtoinvokeamethodfromtheparentcomponent.However,youcurrentlylackareferencetodothis.Listingthecomponentinthechildcomponentconstructorwillmakeitavailabletoyou:
[app/feedback.component.ts]
import{Component,Input}from'@angular/core';
import{ArticleComponent}from'./article.component';
@Component({
selector:'feedback',
template:`
<h1>Numberoflikes:{{val}}</h1>
<button(click)="like()">Likethisarticle!</button>
`
})
exportclassFeedbackComponent{
@Input()val:number;
privatearticleComponent:ArticleComponent;
constructor(articleComponent:ArticleComponent){
this.articleComponent=articleComponent;
}
like():void{
this.articleComponent.incrementLikes();
}
}
![Page 173: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/173.jpg)
Withareferencetotheparentcomponentnowavailable,youareeasilyabletoinvokeitspublicmethod,namelyincrementLikes().Atthispoint,thetwocomponentsshouldcommunicatecorrectly.
![Page 174: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/174.jpg)
Howitworks...Verysimply,Angular2recognizesthatyouareinjectingacomponentthatistypedinthesamewayastheparent,anditwillprovidethatparentforyou.Thisisthefullparentinstance,andyouarefreetointeractwithitasyouwouldnormallyinteractwithanycomponentinstance.
Tip
Noticethatitisrequiredthatyoustoreareferencetothecomponentinsidetheconstructor.Unlikewhenyouinjectaservice,thechildcomponentwillnotautomaticallymaketheArticleComponentinstanceavailabletoyouasthis.articleComponent;youneedtodothismanually.
![Page 175: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/175.jpg)
There'smore...Anastutedeveloperwillnoticethatthiscreatesaveryrigiddependencyfromthechildcomponenttotheparentcomponent.Thisisindeedthecase,butnotnecessarilyabadthing.Often,itisusefultoallowcomponentstomoreeasilyinteractwitheachotherattheexpenseoftheirmodularity.Andgenerally,thiswillbeajudgmentcallonyourpart.
![Page 176: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/176.jpg)
SeealsoPassingmembersfromaparentcomponentintoachildcomponentgoesthroughthebasicsofdownwarddataflowbetweencomponentsRegisteringhandlersonnativebrowsereventsdemonstrateshowyoucaneasilyattachbehaviortobrowsereventsGeneratingandcapturingcustomeventsusingEventEmitterdetailshowtopropagateinformationupwardsbetweencomponentsConfiguringmutualparent-childawarenesswithViewChildandforwardRefinstructsyouonhowtoproperlyuseViewChildtoreferencechildcomponentobjectinstancesConfiguringmutualparent-childawarenesswithContentChildandforwardRefinstructsyouonhowtoproperlyuseContentChildtoreferencechildcomponentobjectinstances
![Page 177: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/177.jpg)
Configuringmutualparent-childawarenesswithViewChildandforwardRefDependingonyourapplication'sseparationofconcerns,itmightmakesenseforachildcomponentinyourapplicationtoreferenceaparent,andatthesametime,fortheparenttoreferencethechild.Therearetwosimilarimplementationsthatallowyoutoaccomplishthis:usingViewChildandContentChild.Thisrecipewilldiscussthemboth.
Note
Thecode,links,andaliveexampleofthisareavailableathttp://ngcookbook.herokuapp.com/1315/.
![Page 178: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/178.jpg)
GettingreadyBeginwiththerecipesetupshowninReferencingaparentcomponentfromachildcomponent.Yourobjectiveistoaddtheabilitytoenableanddisablethelikebuttonfromtheparentcomponent.
![Page 179: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/179.jpg)
Howtodoit...Theinitialsetuponlygivesthechildaccesstotheparent,whichisonlyhalfofwhatyouneed.Theotherhalfistogivetheparentaccesstothechild.
GettingareferencetoFeedbackComponentthatyouseeintheArticleComponenttemplateviewcanbedoneintwoways,andthefirstwaydemonstratedherewilluseViewChild.
ConfiguringaViewChildreference
UsingViewChildwillallowyoutoextractacomponentreferencefrominsidetheview.Moreplainly,inthisexample,usingViewChildwillgiveyoutheabilitytoreferencetheFeedbackComponentinstancefrominsidetheArticleComponentcode.
First,configureArticleComponentsothatitwillretrievethecomponentreference:
[app/article.component.ts]
import{Component,ViewChild}from'@angular/core';
import{FeedbackComponent}from'./feedback.component';
@Component({
selector:'article',
template:`
<inputtype="checkbox"
(click)="changeLikesEnabled($event)">
<feedback[val]="likes"></feedback>
`
})
exportclassArticleComponent{
@ViewChild(FeedbackComponent)
feedbackComponent:FeedbackComponent;
likes:number=0;
incrementLikes():void{
this.likes++;
}
changeLikesEnabled(e:Event):void{
this.feedbackComponent.setLikeEnabled(e.target.checked);
}
}
ThemainthemeinthisnewcodeisthattheViewChilddecoratorimplicitlyunderstandsthatitshouldtargettheviewofthiscomponent,findtheinstanceofFeedbackComponentthatisbeingrenderedthere,andassignittothefeedbackCompnentmemberoftheArticleComponentinstance.
CorrectingthedependencycyclewithforwardRef
![Page 180: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/180.jpg)
Atthispoint,youshouldbeseeingyourapplicationthrowingnewerrors,mostlikelyaboutbeingunabletoresolvetheparametersforFeedbackComponent.Thisoccursbecauseyouhavesetupacyclicdependency:FeedbackComponentdependsonArticleComponentandArticleComponentdependsonFeedbackComponent.Thankfully,thisproblemexistsinthedomainofAngulardependencyinjection,soyoudon'treallyneedthemodule,justatokenthatrepresentsit.Forthispurpose,Angular2providesyouwithforwardRef,whichallowsyoutouseamoduledependencyinsideyourclassdefinitionbeforeitisdefined.Useitasfollows:
[app/feedback.component.ts]
import{Component,Input,Inject,forwardRef}
from'@angular/core';
import{ArticleComponent}from'./article.component';
@Component({
selector:'feedback',
template:`
<h1>Numberoflikes:{{val}}</h1>
<button(click)="likeArticle()">
Likethisarticle!
</button>
`
})
exportclassFeedbackComponent{
@Input()val:number;
privatearticleComponent:ArticleComponent;
constructor(@Inject(forwardRef(()=>ArticleComponent))
articleComponent:ArticleComponent){
this.articleComponent=articleComponent;
}
likeArticle():void{
this.articleComponent.incrementLikes();
}
}
Addingthedisablebehavior
Withthecycleproblemresolved,addthesetLikeEnabled()methodthattheparentcomponentisinvoking:
[app/feedback.component.ts]
import{Component,Input,Inject,forwardRef}
from'@angular/core';
import{ArticleComponent}from'./article.component';
@Component({
selector:'feedback',
![Page 181: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/181.jpg)
template:`
<h1>Numberoflikes:{{val}}</h1>
<button(click)="likeArticle()"
[disabled]="!likeEnabled">
Likethisarticle!
</button>
`
})
exportclassFeedbackComponent{
@Input()val:number;
privatelikeEnabled:boolean=false;
privatearticleComponent:ArticleComponent;
constructor(@Inject(forwardRef(()=>ArticleComponent))
articleComponent:ArticleComponent){
this.articleComponent=articleComponent;
}
likeArticle():void{
this.articleComponent.incrementLikes();
}
setLikeEnabled(newEnabledStatus:boolean):void{
this.likeEnabled=newEnabledStatus;
}
}
Withthis,togglingthecheckboxshouldenableanddisablethelikebutton.
![Page 182: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/182.jpg)
Howitworks...ViewChilddirectsAngulartofindthefirstinstanceofFeedbackComponentpresentinsidetheArticleComponentviewandassignittothedecoratedclassmember.Thereferencewillbeupdatedalongwithanyviewupdates.Thisdecoratedmemberwillrefertothechildcomponentinstanceandcanbeinteractedwithlikeanynormalobjectinstance.
Note
It'simportanttorememberthedualityofthecomponentinstanceanditsrepresentationinthetemplate.Forexample,FeedbackComponentisrepresentedbyafeedbacktag(pre-render)andaheadertagandabutton(post-render),butneitheroftheseformtheactualcomponent.TheFeedbackComponentinstanceisaJavaScriptobjectthatlivesinthememory,andthisistheobjectyouwantaccessto.Ifyoujustwantedareferencetothetemplateelements,thiscouldbeaccomplishedbyatemplatevariable,forexample.
![Page 183: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/183.jpg)
There'smore...SinceAngularperformshierarchicalrendering,ViewChildwillnotbereadyuntiltheviewisinitialized,butrather,aftertheAfterViewInitlifecyclehook.Thiscanbedemonstratedasfollows:
[app/article.component.ts]
import{Component,ViewChild,ngAfterViewInit}
from'@angular/core';
import{FeedbackComponent}from'./feedback.component';
@Component({
selector:'article',
template:`
<inputtype="checkbox"
(click)="changeLikesEnabled($event)">
<feedback[val]="likes"></feedback>
`
})
exportclassArticleComponentimplementsAfterViewInit{
@ViewChild(FeedbackComponent)
feedbackComponent:FeedbackComponent;
likes:number=0;
constructor(){
console.log(this.feedbackComponent);
}
ngAfterViewInit(){
console.log(this.feedbackComponent);
}
incrementLikes():void{
this.likes++;
}
changeLikesEnabled(e:Event):void{
this.feedbackComponent.setLikeEnabled(e.target.checked);
}
}
Thiswillfirstlogundefinedinsidetheconstructorastheview,andtherefore,FeedbackComponentdoesnotyetexist.OncetheAfterViewInitlifecyclehookoccurs,youwillbeabletoseeFeedbackComponentloggedtotheconsole.
ViewChildren
Ifyouwouldliketogetareferencetomultiplecomponents,youcanperformanidenticalreferenceacquisitionusingViewChildren,whichwillprovideyouwithaQueryListofallthe
![Page 184: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/184.jpg)
matchingcomponentsintheview.
Tip
AQueryListcanbeusedlikeanarraywithitstoArray()method.Italsoexposesachangesproperty,whichemitsaneventeverytimeamemberofQueryListchanges.
![Page 185: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/185.jpg)
SeealsoUtilizingcomponentlifecyclehooksgivesanexampleofhowyoucanintegratewithAngular2'scomponentrenderingflowReferencingaparentcomponentfromachildcomponentdescribeshowacomponentcangainadirectreferencetoitsparentviainjectionConfiguringmutualparent-childawarenesswithContentChildandforwardRefinstructsyouonhowtoproperlyuseContentChildtoreferencechildcomponentobjectinstances
![Page 186: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/186.jpg)
Configuringmutualparent-childawarenesswithContentChildandforwardRefThecompaniontoAngular'sViewChildisContentChild.Itperformsasimilarduty;itretrievesareferencetothetargetchildcomponentandmakesitavailableasamemberoftheparentcomponentinstance.ThedifferenceisthatContentChildretrievesthemarkupthatexistsinsidetheparentcomponent'sselectortags,whereasViewChildretrievesthemarkupthatexistsinsidetheparentcomponent'sview.
Thedifferenceisbestdemonstratedbyacomparisonofbehavior,sothisrecipewillconverttheexamplefromConfiguringMutualParent-ChildAwarenesswithViewChildandforwardReftouseContentChildinstead.
Note
Thecode,links,andaliveexampleofthisareavailableathttp://ngcookbook.herokuapp.com/7386/.
![Page 187: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/187.jpg)
GettingreadyBeginwiththecodefromtheConfiguringmutualparent-childawarenesswithViewChildandforwardRefrecipe.
![Page 188: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/188.jpg)
Howtodoit...Beforeyoubegintheconversion,you'llneedtonesttheArticleComponenttagsinsideanotherrootcomponent,asContentChildwillnotworkfortheroot-levelbootstrappedapplicationcomponent.CreateawrappedRootComponent:
[app/root.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'root',
template:`
<article></article>
`
})
exportclassRootComponent{}
ConvertingtoContentChild
ContentChildisintroducedtocomponentsinessentiallythesamewayasViewChild.InsideArticleComponent,performthisconversionandreplacethe<feedback>tagwith<ng-content>:
[app/article.component.ts]
import{Component,ContentChild}from'@angular/core';
import{FeedbackComponent}from'./feedback.component';
@Component({
selector:'article',
template:`
<inputtype="checkbox"
(click)="changeLikesEnabled($event)">
<ng-content></ng-content>
`
})
exportclassArticleComponent{
@ContentChild(FeedbackComponent)
feedbackComponent:FeedbackComponent;
likes:number=0;
incrementLikes():void{
this.likes++;
}
changeLikesEnabled(e:Event):void{
this.feedbackComponent.setLikeEnabled(e.target.checked);
}
}
![Page 189: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/189.jpg)
Ofcourse,thiswillonlybeabletofindthechildcomponentifthe<article></article>taghascontentinsideofit:
[app/root.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'root',
template:`
<article>
<feedback></feedback>
</article>
`
})
exportclassRootComponent{}
Note
You'llnoticethatthelikecountvaluebeingpassedtothechildcomponentasaninputhasbeenremoved.Verysimply,thatconventionwillnotworkanymore,asbindingitherewoulddrawthelikecountfromRootComponent,whichdoesnothavethisinformation.
Correctingdatabinding
TheFeedbackComponentwillneedtoretrievethelikecountdirectly:
[app/feedback.component.ts]
import{Component,Inject,forwardRef}from'@angular/core';
import{ArticleComponent}from'./article.component';
@Component({
selector:'feedback',
template:`
<h1>Numberoflikes:{{val}}</h1>
<button(click)="likeArticle()"
[disabled]="!likeEnabled">
Likethisarticle!
</button>
`
})
exportclassFeedbackComponent{
privateval:number;
privatelikeEnabled:boolean=false;
privatearticleComponent:ArticleComponent;
constructor(@Inject(forwardRef(()=>ArticleComponent))
articleComponent:ArticleComponent){
this.articleComponent=articleComponent;
this.updateLikes();
![Page 190: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/190.jpg)
}
updateLikes(){
this.val=this.articleComponent.likes;
}
likeArticle():void{
this.articleComponent.incrementLikes();
this.updateLikes();
}
setLikeEnabled(newEnabledStatus:boolean):void{
this.likeEnabled=newEnabledStatus;
}
}
That'sit!TheapplicationshouldbehaveidenticallytothesetupfromtheGettingreadysectionoftherecipe.
![Page 191: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/191.jpg)
Howitworks...ContentChilddoesnearlythesamethingasViewChild;itjustlooksinadifferentplace.ContentChilddirectsAngulartofindthefirstinstanceofFeedbackComponentpresentinsidetheArticleComponenttags.Here,thisstepreferstoanythingthatisinterpolatedby<ng-content>.Itthenassignsthefoundcomponentinstancetothedecoratedclassmember.Thereferenceisupdatedalongwithanyviewupdates.Thisdecoratedmemberwillrefertothechildcomponentinstanceandcanbeinteractedwithlikeanynormalobjectinstance.
![Page 192: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/192.jpg)
There'smore...SinceAngularperformshierarchicalrendering,ContentChildwillnotbereadyuntiltheviewisinitialized,butrather,aftertheAfterContentInitlifecyclehook.Thiscanbedemonstratedasfollows:
[app/article.component.ts]
import{Component,ContentChild,ngAfterContentInit}
from'@angular/core';
import{FeedbackComponent}from'./feedback.component';
@Component({
selector:'article',
template:`
<inputtype="checkbox"
(click)="changeLikesEnabled($event)">
<ng-content></ng-content>
`
})
exportclassArticleComponentimplementsAfterContentInit{
@ContentChild(FeedbackComponent)
feedbackComponent:FeedbackComponent;
likes:number=0;
constructor(){
console.log(this.feedbackComponent);
}
ngAfterContentInit(){
console.log(this.feedbackComponent);
}
incrementLikes():void{
this.likes++;
}
changeLikesEnabled(e:Event):void{
this.feedbackComponent.setLikeEnabled(e.target.checked);
}
}
Thiswillfirstlogundefinedinsidetheconstructorasthecontent,andthereforeFeedbackComponentdoesnotyetexist.OncetheAfterContentInitlifecyclehookoccurs,youwillbeabletoseeFeedbackComponentloggedtotheconsole.
ContentChildren
Ifyouwouldliketogetareferencetomultiplecomponents,youcanperformanidenticalreferenceacquisitionprocessusingContentChildren,whichwillprovideyouwithQueryList
![Page 193: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/193.jpg)
ofallthematchingcomponentsinsidethecomponent'stags.
Tip
AQueryListcanbeusedlikeanarraywithitstoArray()method.Italsoexposesachangesproperty,whichemitsaneventeverytimeamemberofQueryListchanges.
![Page 194: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/194.jpg)
SeealsoUtilizingcomponentlifecyclehooksgivesanexampleofhowyoucanintegratewithAngular2'scomponentrenderingflow.Referencingaparentcomponentfromachildcomponentdescribeshowacomponentcangainadirectreferencetoitsparentviainjection.Configuringmutualparent-childawarenesswithViewChildandforwardRefinstructsyouonhowtoproperlyuseViewChildtoreferencechildcomponentobjectinstances.
![Page 195: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/195.jpg)
Chapter3.BuildingTemplate-DrivenandReactiveFormsThischapterwillcoverthefollowingrecipes:
Implementingsimpletwo-waydatabindingwithngModelImplementingbasicfieldvalidationwithaFormControlBundlingFormControlswithaFormGroupBundlingFormControlswithaFormArrayImplementingbasicformswithngFormImplementingbasicformswithFormBuilderandformControlNameCreatingandusingacustomvalidatorCreatingandusingacustomasynchronousvalidatorwithPromises
![Page 196: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/196.jpg)
IntroductionFormsareimportantelementalconstructsfornearlyeverywebapplication,andtheyhavebeenreimaginedforthebetterinAngular2.Angular1formswereveryuseful,buttheyweretotallydependentontheconventionsofngModel.Angular2'snewfoundconventionsremoveitfromngModeldependenceandofferafreshapproachtoformandinformationmanagementthatultimatelyfeelscleanerandmoreapproachable.
Fundamentally,itisimportanttounderstandwhereandwhyformsareuseful.Therearemanyplacesinanapplicationwheremultitudinousinputdemandsassociation,andformsarecertainlyusefulinthiscontext.Angular2formsarebestusedwhenvalidatingthesaidinput,especiallysowhenmultiple-fieldandcross-fieldvalidationisrequired.Additionally,Angularformsmaintainthestateofvariousformelements,allowingtheusertoreasonthe"history"ofaninputfield.
ItisalsocriticaltorememberthattheAngular2formbehavior,muchinthesamewayasitseventanddatabinding,isgettingintegratedwiththealreadyrobustbrowserformbehavior.Browsersarealreadyverycapableofsubmittingdata,recallingdatauponapagereload,simplevalidation,andotherbehaviorsthatprettymuchallformsrelyupon.Angular2doesn'tredefinethese;rather,itintegrateswiththesebehaviorsinordertolinkinotherbehaviorsanddatathatarepartofeitheraframeworkoryourapplication.
Tip
Inthischapter,beawareofthedualityoftheuseofFormsModuleandReactiveFormsModule.Theybehaveverydifferentlyandarealmostalwaysusedseparatelywhenitcomestoformconstruction.
![Page 197: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/197.jpg)
Implementingsimpletwo-waydatabindingwithngModelAngular2stillhastwo-waydatabinding,butthewayitbehavesisabitdifferentthanwhatyou'reusedto.Thisrecipewillbeginwithaverysimpleexampleandthenbreakitdownintopiecestodescribewhatit'sactuallydoing.
Note
Thecode,links,andaliveexamplerelatedtothisrecipeareavailableathttp://ngcookbook.herokuapp.com/0771/.
![Page 198: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/198.jpg)
Howtodoit...Two-waydatabindingusesthengModeldirective,whichisincludedinFormsModule.Addthisdirectivetoyourapplicationmodule:
[app/app.module.ts]
import{NgModule}from'@angular/core';
import{BrowserModule}from'@angular/platform-browser';
import{FormsModule}from'@angular/forms';
import{ArticleEditorComponent}from'./article-editor.component';
@NgModule({
imports:[
BrowserModule,
FormsModule
],
declarations:[
ArticleEditorComponent
],
bootstrap:[
ArticleEditorComponent
]
})
exportclassAppModule{}
Next,fleshoutyourcomponent,whichwillhavetwoinstancesofinputboundtothesamecomponentmemberusingngModel:
[app/article-editor.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'article-editor',
template:`
<input[(ngModel)]="title">
<input[(ngModel)]="title">
<h2>{{title}}</h2>
`
})
exportclassArticleEditorComponent{
title:string;
}
That'sallthat'srequired!Youshouldseeinputmodificationsinstantlyreflectedintheotherinputaswellasin<h2>itself.
![Page 199: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/199.jpg)
Howitworks...Whatyou'rereallydoingisbindingtotheeventandpropertythatngModelassociateswiththisinput.Whenthecomponent'stitlememberchanges,theinputisboundtothatvalueandwillupdateitsownvalue.Whentheinput'svaluechanges,itemitsanevent,whichngModelwillbindtoandextractthevaluefrombeforepropagatingittothecomponent'stitlemember.
Note
Thebanana-in-a-boxsyntax[()]issimplyindicativeofthebindingdonetoboththeinputpropertywith[]andtheinputeventswith().
Inreality,thisisshorthandforthefollowing:
[app/article-editor.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'article-editor',
template:`
<input[ngModel]="title"(ngModelChange)="title=$event">
<input[ngModel]="title"(ngModelChange)="title=$event">
<h2>{{title}}</h2>
`
})
exportclassArticleEditorComponent{
title:string;
}
Youwillfindthatthisbehavesidenticallytowhatwediscussedbefore.
![Page 200: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/200.jpg)
There'smore...Youmightstillfindthatthere'sabittoomuchsyntacticalsugarhappeninghereforyourtaste.You'rebindingtongModel,butsomehow,itisequivalenttotheinputvalue.Similarly,you'rebindingtongModelChangeevents,whichareallemittinga$eventthatappearstobeonlyastring.
Thisisindeedcorrect.ThengModeldirectiveunderstandswhatitisapartofandisabletointegrate[ngModel]and(ngModelChange)correctlytoassociatethedesiredbindings.
Thecoreofthesebindingsisessentiallydoingthefollowing:
[app/article-editor.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'article-editor',
template:`
<input[value]="title"(input)="title=$event.target.value">
<input[value]="title"(input)="title=$event.target.value">
<h2>{{title}}</h2>
`
})
exportclassArticleEditorComponent{
//Initializetitle,otherwiseyou'llget"undefined"
title:string='';
}
![Page 201: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/201.jpg)
SeealsoImplementingsimpletwo-waydatabindingwithngModeldemonstratesthenewwayinAngular2tocontrolbidirectionaldataflowImplementingbasicfieldvalidationwithaFormControldetailsthebasicbuildingblockofanAngularformBundlingFormControlswithaFormGroupshowshowtocombineFormControlsBundlingFormControlswithaFormArrayshowshowtohandleiterableformelementsImplementingbasicformswithngFormdemonstratesAngular'sdeclarativeformconstructionImplementingbasicformswithFormBuilderandformControlNameshowshowtousetheFormBuilderservicetoquicklyputtogethernestedformsCreatingandusingacustomvalidatordemonstrateshowtocreateacustomdirectivethatbehavesasinputvalidationCreatingandusingacustomasynchronousvalidatorwithPromisesshowshowAngularallowsyoutohaveadelayedevaluationofaformstate
![Page 202: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/202.jpg)
ImplementingbasicfieldvalidationwithaFormControlThesimplestformbehaviorimaginablewouldbethevalidationofasingleinputfield.Mostofthetime,utilizing<form>tagsandgoingthroughtherestoftheboilerplateisgoodpractice,butforthepurposeofcheckingasingleinput,it'spreferabletodistillthisdowntothebareminimumrequiredinordertouseinputchecking.
Note
Thecode,links,andaliveexamplerelatedtothisrecipeareavailableathttp://ngcookbook.herokuapp.com/4076/.
![Page 203: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/203.jpg)
GettingreadySupposethefollowingisyourinitialsetup:
[app/article-editor.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'article-editor',
template:`
<p>Articletitle(required):</p>
<inputrequired>
<button>Save</button>
<h1>{{title}}</h1>
`
})
exportclassArticleEditorComponent{
title:string;
}
Yourgoalistochangethisinawaythatclickingthesavebuttonwillvalidatetheinputandupdatethetitlememberonlyifitisvalid.
![Page 204: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/204.jpg)
Howtodoit...ThemostelementalcomponentofAngularformsistheFormControlobject.Inordertobeabletoassessthestateofthefield,youfirstneedtoinstantiatethisobjectinsidethecomponentandassociateitwiththefieldusingtheformControldirective.FormControllivesinsideReactiveFormsModule.Additasamoduleimport:
[app/app.module.ts]
import{NgModule}from'@angular/core';
import{BrowserModule}from'@angular/platform-browser';
import{ReactiveFormsModule}from'@angular/forms';
import{ArticleEditorComponent}from'./article-editor.component';
@NgModule({
imports:[
BrowserModule,
ReactiveFormsModule
],
declarations:[
ArticleEditorComponent
],
bootstrap:[
ArticleEditorComponent
]
})
exportclassAppModule{}
Withthis,youcanuseFormControlinsideArticleEditorComponent.InstantiateFormControlinsidethecomponentandbindtheinputelementtoitusingtheformControldirective:
[app/article-editor.component.ts]
import{Component}from'@angular/core';
import{FormControl}from'@angular/forms';
@Component({
selector:'article-editor',
template:`
<p>Articletitle(required):</p>
<input[formControl]="titleControl"required>
<button>Save</button>
<h1>{{title}}</h1>
`
})
exportclassArticleEditorComponent{
title:string;
titleControl:FormControl=newFormControl();
}
![Page 205: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/205.jpg)
NowthatyouhavecreatedaFormControlobjectandassociateditwithaninputfield,youwillbeabletouseitsvalidationAPItocheckthestateofthefield.Allthatisleftistouseitinsidethesubmitclickhandler:
[app/article-editor.component.ts]
import{Component}from'@angular/core';
import{FormControl}from'@angular/forms';
@Component({
selector:'article-editor',
template:`
<p>Articletitle(required):</p>
<input[formControl]="titleControl"required>
<button(click)="submitTitle()">Save</button>
<h1>{{title}}</h1>
`
})
exportclassArticleEditorComponent{
title:string;
titleControl:FormControl=newFormControl();
submitTitle():void{
if(this.titleControl.valid){
this.title=this.titleControl.value;
}else{
alert("Titlerequired");
}
}
}
Withthis,thesubmitclickhandlerwillbeabletochecktheinput'svalidationstateandvaluewiththesameobject.
![Page 206: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/206.jpg)
Howitworks...TheformControldirectiveservesonlytobindanexistingFormControlobjecttoaDOMelement.TheFormControlobjectthatyouinstantiateinsidethecomponentconstructorcaneitherutilizevalidationattributesinsideanHTMLtag(asisdoneinthisexample),oracceptAngularvalidatorswheninitialized;or,itcandoboth.
Note
It'sextremelyimportanttonotethatjustbecausetheFormControlobjectisinstantiated,itdoesnotmeanthatitisabletovalidatetheinputimmediately.
Withoutaninitializedvalue,anemptyinputfieldwillbeginitslifewithavalueofnull,whichinthepresenceofarequiredattributeisofcourseinvalid.However,inthisexample,ifyouweretocheckwhethertheFormControlobjectbecomesvalidimmediatelyafteryouinstantiateitintheconstructor,theFormControlobjectwoulddutifullyinformyouthatthestateisvalidsinceithasnotbeenboundtotheDOMelementyet,andtherefore,novalidationsarebeingviolated.Sincetheinputelement'sformControlbindingwillnotoccuruntilthecomponenttemplatebecomespartoftheactualDOM,youwillnotbeabletochecktheinputstateuntilthebindingiscompleteorinsidethengAfterContentCheckedlifecyclehook.Notethatthispertainstotheexampleunderconsideration.
OncetheformControldirectivecompletesthebinding,theFormControlobjectwillexistasaninputwrapper,allowingyoutouseitsvalidandvaluemembers.
![Page 207: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/207.jpg)
There'smore...ThisrecipeusesReactiveFormsModule,whichissimplertounderstandsinceallofthesetupisexplicit.WhenyouuseFormsModuleinstead,youdiscoverthatalotofwhatisaccomplishedinthisrecipecouldbedoneautomaticallyforyou,suchastheinstantiationandbindingofFormControlobjects.Italsorevolvesaroundthepresenceofa<form>tag,whichisthedefactotop-levelFormControlcontainer.ThisrecipeservestodemonstrateoneofthesimplestformsofAngularformbehavior.
Validatorsandattributeduality
Asmentionedinthisrecipe,validationdefinitionscancomefromtwoplaces.Here,youusedastandardizedHTMLtagattributethatAngularrecognizesandautomaticallyincorporatesintotheFormControlvalidationspecification.YoucouldhavejustaseasilyelectedtoutilizeanAngularValidatortoaccomplishthesametaskinstead.ThiscanbeaccomplishedbyimportingAngular'sdefaultValidatorsandinitializingtheFormControlobjectwiththerequiredvalidator:
[app/article-editor.component.ts]
import{Component}from'@angular/core';
import{FormControl,Validators}from'@angular/forms';
@Component({
selector:'article-editor',
template:`
<p>Articletitle(required):</p>
<input[formControl]="titleControl">
<button(click)="submitTitle()">Save</button>
<h1>{{title}}</h1>
`
})
exportclassArticleEditorComponent{
title:string;
//Firstargumentisthedefaultinputvalue
titleControl:FormControl=
newFormControl(null,Validators.required);
submitTitle():void{
if(this.titleControl.valid){
this.title=this.titleControl.value;
}else{
alert("Titlerequired");
}
}
}
Taglesscontrols
![Page 208: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/208.jpg)
Asyoumightsuspect,thereisnoreasonaFormControlmustbeboundtoaDOMelement.FormControlisanelementalpieceofformlogicthatactsasanatomicpieceofstatefulinformation,whetherornotthisinformationisderivedfrom<input>.SayyouwantedtoaddaFormControlthatwouldpreventquickformsubmissionbyonlybecomingvalidafter10seconds.YoucouldexplicitlycreateaFormControlobjectthatwouldtieintothecombinedformvalidationbutwouldnotbeassociatedwithaDOMelement.
![Page 209: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/209.jpg)
SeealsoImplementingsimpletwo-waydatabindingwithngModeldemonstratesthenewwayinAngular2tocontrolbidirectionaldataflowBundlingFormControlswithaFormGroupshowshowtocombineFormControlobjectsBundlingFormControlswithaFormArrayshowshowtohandleiterableformelements
![Page 210: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/210.jpg)
BundlingcontrolswithaFormGroupNaturally,formsinapplicationsfrequentlyexisttoaggregatemultipleinstancesofinputintoaunifiedbehavior.Onecommonbehavioristoassesswhetheraformisvalid,whichofcourserequiresthatallofitssubfieldsarevalid.ThiswillmostcommonlybeachievedbybundlingmultipleFormControlobjectsintoaFormGroup.Thiscanbedoneindifferentways,withvaryingdegreesofexplicitness.Thisrecipecoversanentirelyexplicitimplementation,thatis,everythingherewillbecreatedand"joined"manually.
Note
Thecode,links,andaliveexamplerelatedtothisrecipeareavailableathttp://ngcookbook.herokuapp.com/3052.
![Page 211: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/211.jpg)
GettingreadySupposeyoubeganwiththefollowingskeletonapplication:
[app/article-editor.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'article-editor',
template:`
<p>Title:<input></p>
<p>Text:<input></p>
<p><button(click)="saveArticle()">Save</button></p>
<hr/>
<p>Preview:</p>
<divstyle="border:1pxsolid#999;margin:50px;">
<h1>{{article.title}}</h1>
<p>{{article.text}}</p>
</div>
`
})
exportclassArticleEditorComponent{
article:{title:string,text:string}={};
saveArticle():void{}
}
Yourgoalistoupdatethearticleobject(andconsequentlythetemplate)onlyifalltheinputfieldsarevalid.
![Page 212: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/212.jpg)
Howtodoit...First,addthenecessarycodetoattachnewFormControlobjectstoeachinputfieldandvalidatethemwiththebuilt-inrequiredvalidator:
[app/article-editor.component.ts]
import{Component}from'@angular/core';
import{FormControl,Validators}
from'@angular/forms';
@Component({
selector:'article-editor',
template:`
<p>Title:<input[formControl]="titleControl"></p>
<p>Text:<input[formControl]="textControl"></p>
<p><button(click)="saveArticle()">Save</button></p>
<hr/>
<p>Preview:</p>
<divstyle="border:1pxsolid#999;margin:50px;">
<h1>{{article.title}}</h1>
<p>{{article.text}}</p>
</div>
`
})
exportclassArticleEditorComponent{
article:{title:string,text:string}={};
titleControl:FormControl
=newFormControl(null,Validators.required);
textControl:FormControl
=newFormControl(null,Validators.required);
saveArticle():void{}
}
Atthispoint,youcouldindividuallyinspecteachinput'sFormControlobjectandcheckwhetheritisvalid.However,ifthisformgrowsto100fields,itwouldbecomeunbearablytedioustomaintainthem.Therefore,youcanbundletheseFormControlobjectsintoasingleFormGroupinstead:
[app/article-editor.component.ts]
import{Component}from'@angular/core';
import{FormControl,FormGroup,Validators}
from'@angular/forms';
@Component({
selector:'article-editor',
template:`
<p>Title:<input[formControl]="titleControl"></p>
<p>Text:<input[formControl]="textControl"></p>
![Page 213: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/213.jpg)
<p><button(click)="saveArticle()">Save</button></p>
<hr/>
<p>Preview:</p>
<divstyle="border:1pxsolid#999;margin:50px;">
<h1>{{article.title}}</h1>
<p>{{article.text}}</p>
</div>
`
})
exportclassArticleEditorComponent{
article:{title:string,text:string}={};
titleControl:FormControl
=newFormControl(null,Validators.required);
textControl:FormControl
=newFormControl(null,Validators.required);
articleFormGroup:FormGroup=newFormGroup({
title:this.titleControl,
text:this.textControl
});
saveArticle():void{}
}
FormGroupobjectsalsoexposevalidandvaluemembers,soyoucanusethesetoverifyandassigndirectlyfromtheobject:
[app/article-editor.component.ts]
import{Component}from'@angular/core';
import{FormControl,FormGroup,Validators}
from'@angular/forms';
@Component({
selector:'article-editor',
template:`
<p>Title:<input[formControl]="titleControl"></p>
<p>Text:<input[formControl]="textControl"></p>
<p><button(click)="saveArticle()">Save</button></p>
<hr/>
<p>Preview:</p>
<divstyle="border:1pxsolid#999;margin:50px;">
<h1>{{article.title}}</h1>
<p>{{article.text}}</p>
</div>
`
})
exportclassArticleEditorComponent{
article:{title:string,text:string}={};
titleControl:FormControl
=newFormControl(null,Validators.required);
textControl:FormControl
=newFormControl(null,Validators.required);
articleFormGroup:FormGroup=newFormGroup({
![Page 214: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/214.jpg)
title:this.titleControl,
text:this.textControl
});
saveArticle():void{
if(this.articleFormGroup.valid){
this.article=this.articleFormGroup.value;
}else{
alert("Missingfield(s)!");
}
}
Withthisaddition,yourformshouldnowbeworkingfine.
![Page 215: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/215.jpg)
Howitworks...BothFormControlandFormGroupinheritfromtheabstractbaseclasscalledAbstractControl.Whatthismeansforyouisthatbothofthemexposethesamebaseclassmethods,butFormGroupwillaggregateitscompositionofAbstractControlobjectstobereadfromitsownmembers.Asyoucanseeintheprecedingcode,validactsasalogicalANDoperatorforallthechildren(meaningeverysinglechildmustreturntrueforittoreturntrue);valuereturnsanobjectofthesametopologyastheoneprovidedattheinstantiationofFormGroup,butwitheachFormControlvalueinsteadoftheFormControlobject.
Note
Asyoumightexpect,sinceFormGroupexpectsanobjectwithAbstractControlproperties,youarefreetonestaFormGroupinsideanotherFormGroup.
![Page 216: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/216.jpg)
There'smore...YouareabletoaccessaFormGroup'scontainedFormControlmembersviathecontrolsproperty.ThestringthatyouusedtokeytheFormControlmembers—eitheruponFormGroupinstantiation,orwiththeaddControlmethod—isusedtoretrieveit.Inthisexample,thetextFormControlobjectcouldberetrievedinsideacomponentmethodviathis.articleCtrlGroup.controls.text.
Tip
TheAngulardocumentationwarnsyoutospecificallynottomodifytheunderlyingFormControlcollectiondirectly.Thismayleadtoanundefineddatabindingbehavior.So,alwaysbesuretousetheFormGroupmembermethodsaddControlandremoveControlinsteadofdirectlymanipulatingthecollectionofFormControlobjectsthatyoupassuponinstantiation.
FormGroupvalidators
LikeControl,aFormGroupcanhaveitsownvalidators.ThesecanbeprovidedwhentheFormGroupisinstantiated,andtheybehaveinthesamewaythataFormControlvalidatorwouldbehave.ByaddingvalidatorsattheFormGrouplevel,FormGroupcanoverridethedefaultbehaviorofonlybeingvalidwhenallitscomponentsarevalidoraddingextravalidationclauses.
Errorpropagation
Angularvalidatorsnotonlyhavetheabilitytodeterminewhethertheyarevalidornot,buttheyarealsocapableofreturningerrormessagesdescribingwhatiswrong.Forexample,whentheinputfieldsareempty,ifyouweretoexaminetheerrorspropertyofthetextFormControlobjectviathis.articleCtrlGroup.controls.text.errors,itwouldreturn{required:true}.Thisisthedefaulterrormessageofthebuilt-inrequiredvalidator.However,ifyouweretoinspecttheerrorspropertyontheparentFormGroupviathis.articleCtrlGroup.errors,youwillfindittobenull.
Thismaybecounter-intuitive,butitisnotamistake.ErrormessageswillonlyappearontheFormControlinstancethatiscausingthem.Ifyouwishtoaggregateerrormessages,youwillhavetotraversethenestedcollectionsofFormControlobjectsmanually.
![Page 217: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/217.jpg)
SeealsoImplementingsimpletwo-waydatabindingwithngModeldemonstratesthenewwayinAngular2tocontrolbidirectionaldataflowImplementingbasicfieldvalidationwithaFormControldetailsthebasicbuildingblockofanAngularformBundlingFormControlswithaFormArrayshowshowtohandleiterableformelements
![Page 218: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/218.jpg)
BundlingFormControlswithaFormArrayYouwillmostlikelyfindthatFormGroupsaremorethancapableofservingyourneedsforthepurposeofcombiningmanyFormControlobjectsintoonecontainer.However,thereisoneverycommonpatternthatmakesitssistertype,theFormArray,extremelyuseful:variablelengthclonedinputs.
Note
Thecode,links,andaliveexamplerelatedtothisrecipeareavailableathttp://ngcookbook.herokuapp.com/2816/.
![Page 219: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/219.jpg)
GettingreadySupposeyouhadthefollowingskeletonapplication:
[app/article-editor.component.ts]
import{Component}from'@angular/core';
import{FormControl,Validators}
from'@angular/forms';
@Component({
selector:'article-editor',
template:`
<p>Tags:</p>
<ul>
<li*ngFor="lettoftagControls;leti=index">
<input[formControl]="t">
</li>
</ul>
<p><button(click)="addTag()">+</button></p>
<p><button(click)="saveArticle()">Save</button></p>
`
})
exportclassArticleEditorComponent{
tagControls:Array<FormControl>=[];
addTag():void{}
saveArticle():void{}
}
Yourobjectiveistomodifythiscomponentsothatanarbitrarynumberoftagscanbeaddedandsoallthetagscanbevalidatedtogether.
![Page 220: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/220.jpg)
Howtodoit...Inmanyways,aFormArraybehavesmoreorlessidenticallytoaFormGroup.ItisimportedinthesamewayandinheritedfromAbstractControl.Also,itisinstantiatedinasimilarwayandcanaddandremoveFormControlinstances.First,addtheboilerplatetoyourapplication;thiswillallowyoutoinstantiateaninstanceofaFormArrayandpassitthearrayofFormControlobjectsalreadyinsidethecomponent.SinceyoualreadyhaveabuttonthatismeanttoinvoketheaddTagmethod,youshouldalsoconfigurethismethodtopushanewFormControlontotagControl:
[app/article-editor.component.ts]
import{Component}from'@angular/core';
import{FormControl,FormArray,Validators}
from'@angular/forms';
@Component({
selector:'article-editor',
template:`
<p>Tags:</p>
<ul>
<li*ngFor="lettoftagControls;leti=index">
<input[formControl]="t">
</li>
</ul>
<p><button(click)="addTag()">+</button></p>
<p><button(click)="saveArticle()">Save</button></p>
`
})
exportclassArticleEditorComponent{
tagControls:Array<FormControl>=[];
tagFormArray:FormArray=newFormArray(this.tagControls);
addTag():void{
this.tagFormArray
.push(newFormControl(null,Validators.required));
}
saveArticle():void{}
}
Note
Atthispoint,it'simportantthatyoudon'tconfuseyourselfwithwhatyouareworkingwith.InsidethisArticleEditorcomponent,youhaveanarrayofFormControlobjects(tagControls)andyoualsohaveasingleinstanceofFormArray(tagFormArray).TheFormArrayinstanceisinitializedbybeingpassedthearrayofFormControlobjects,whichitwillthenbeabletomanage.
![Page 221: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/221.jpg)
NowthatyourFormArrayismanagingthetag'sFormControlobjects,youcansafelyuseitsvalidator:
[app/article-editor.component.ts]
import{Component}from'@angular/core';
import{FormControl,FormArray,Validators}
from'@angular/forms';
@Component({
selector:'article-editor',
template:`
<p>Tags:</p>
<ul>
<li*ngFor="lettoftagControls;leti=index">
<input[formControl]="t">
</li>
</ul>
<p><button(click)="addTag()">+</button></p>
<p><button(click)="saveArticle()">Save</button></p>
`
})
exportclassArticleEditorComponent{
tagControls:Array<FormControl>=[];
tagFormArray:FormArray=newFormArray(this.tagControls);
addTag():void{
this.tagFormArray
.push(newFormControl(null,Validators.required));
}
saveArticle():void{
if(this.tagFormArray.valid){
alert('Valid!');
}else{
alert('Missingfield(s)!');
}
}
}
![Page 222: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/222.jpg)
Howitworks...Becausethetemplateisreactingtotheclickevent,youareabletouseAngulardatabindingtoautomaticallyupdatethetemplate.However,itisextremelyimportantthatyounotetheasymmetryinthisexample.ThetemplateisiteratingthroughthetagControlsarray.However,whenyouwanttoaddanewFormControlobject,youpushittotagFormArray,whichwillinturnpushittothetagControlsarray.TheFormArrayobjectactsasthemanagerofthecollectionofFormControlobjects,andallmodificationsofthiscollectionshouldgothroughthemanager,notthecollectionitself.
Tip
TheAngulardocumentationwarnsyoutospecificallynotmodifytheunderlyingFormControlcollectiondirectly.Thismayleadtoundefineddatabindingbehavior,soalwaysbesuretousetheFormArraymemberspush,insert,andremoveAtinsteadofdirectlymanipulatingthearrayofFormControlobjectsthatyoupassuponinstantiation.
![Page 223: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/223.jpg)
There'smore...Youcantakethisexampleonestepfurtherbyaddingtheabilitytoremovefromthislistaswell.SinceyoualreadyhavetheindexinsidethetemplaterepeaterandFormArrayoffersindex-basedremoval,thisissimpletoimplement:
[app/article-editor.component.ts]
import{Component}from'@angular/core';
import{FormControl,FormArray,Validators}
from'@angular/forms';
@Component({
selector:'article-editor',
template:`
<p>Tags:</p>
<ul>
<li*ngFor="lettoftagControls;leti=index">
<input[formControl]="t">
<button(click)="removeTag(i)">X</button>
</li>
</ul>
<p><button(click)="addTag()">+</button></p>
<p><button(click)="saveArticle()">Save</button></p>
`
})
exportclassArticleEditorComponent{
tagControls:Array<FormControl>=[];
tagFormArray:FormArray=newFormArray(this.tagControls);
addTag():void{
this.tagFormArray
.push(newFormControl(null,Validators.required));
}
removeTag(idx:number):void{
this.tagFormArray.removeAt(idx);
}
saveArticle():void{
if(this.tagFormArray.valid){
alert('Valid!');
}else{
alert('Missingfield(s)!');
}
}
}
ThisallowsyoutocleanlyinsertandremoveFormControlinstanceswhilelettingAngulardatabindingdoalloftheworkforyou.
![Page 224: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/224.jpg)
SeealsoImplementingsimpletwo-waydatabindingwithngModeldemonstratesthenewwayinAngular2tocontrolbidirectionaldataflowImplementingbasicformswithngFormdemonstratesAngular'sdeclarativeformconstructionImplementingbasicformswithFormBuilderandformControlNameshowshowtousetheFormBuilderservicetoquicklyputtogethernestedforms
![Page 225: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/225.jpg)
ImplementingbasicformswithNgFormThebasicdenominationsofAngularformsareFormControl,FormGroup,andFormArrayobjects.However,itisoftennotdirectlynecessarytousetheseobjectsatall;Angularprovidesmechanismswithwhichyoucanimplicitlycreateandassigntheseobjectsandattachthemtotheform'sDOMelements.
Note
Thecode,links,andaliveexamplerelatedtothisrecipeareavailableathttp://ngcookbook.herokuapp.com/5116/.
![Page 226: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/226.jpg)
GettingreadySupposeyoubeganwiththefollowingskeletonapplication:
[app/article-editor.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'article-editor',
template:`
<p><inputplaceholder="Articletitle"></p>
<p><textareaplaceholder="Articletext"></textarea></p>
<p><button(click)="saveArticle()">Save</button></p>
`
})
exportclassArticleEditorComponent{
saveArticle():void{}
}
YourobjectiveistocollectalloftheformdataandsubmititusingAngular'sformconstructs.
![Page 227: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/227.jpg)
Howtodoit...Youshouldbeginbyreorganizingthisintoanactualbrowserform.Angulargivesyoualotofdirectivesandcomponentsforthis,andimportingtheFormsModulewillgiveyouaccesstoalltheonesyouneedmostofthetime:
[app/app.module.ts]
import{NgModule}from'@angular/core';
import{BrowserModule}from'@angular/platform-browser';
import{FormsModule}from'@angular/forms';
import{ArticleEditorComponent}from'./article-editor.component';
@NgModule({
imports:[
BrowserModule,
FormsModule
],
declarations:[
ArticleEditorComponent
],
bootstrap:[
ArticleEditorComponent
]
})
exportclassAppModule{}
Inaddition,youshouldreconfigurethebuttonsoitbecomesanactualsubmitbutton.Thehandlershouldbetriggeredwhentheformissubmitted,soyoucanreattachthelistenertotheform'snativesubmiteventinsteadofthebutton'sclickevent.AngularprovidesanngSubmitEventEmitterontopofthisevent,sogoaheadandattachthelistenertothis:
[app/article-editor.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'article-editor',
template:`
<form(ngSubmit)="saveArticle()">
<p><inputplaceholder="Articletitle"></p>
<p><textareaplaceholder="Articletext"></textarea></p>
<p><buttontype="submit">Save</button></p>
</form>
`
})
exportclassArticleEditorComponent{
saveArticle():void{}
}
![Page 228: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/228.jpg)
Next,youshouldconfiguretheformtopasstheformdatatothehandlerthroughatemplatevariable.
Note
TheformelementwillhaveanNgFormobject(andinsidethis,aFormGroup)automaticallyassociatedwithitwhenyouimportFormsModuleintotheencompassingmodule.AngularcreatesandassociatestheNgForminstancebehindthescenes.
OnewayyoucanaccessthisinstanceisbyassigningthengFormdirectiveasatemplatevariable.It'sabitofsyntacticalmagic,butusing#f="ngForm"signalstoAngularthatyouwanttobeabletoreferencetheform'sNgFormfromthetemplateusingthefvariable.
Onceyoudeclarethetemplatevariable,youareabletopassthengForminstancetothesubmithandlerasanargument,specificallyassaveArticle(f).
Thisleavesyouwiththefollowing:
[app/article-editor.component.ts]
import{Component}from'@angular/core';
import{NgForm}from'@angular/forms';
@Component({
selector:'article-editor',
template:`
<form#f="ngForm"
(ngSubmit)="saveArticle(f)">
<p><inputplaceholder="Articletitle"></p>
<p><textareaplaceholder="Articletext"></textarea></p>
<p><buttontype="submit">Save</button></p>
</form>
`
})
exportclassArticleEditorComponent{
saveArticle(f:NgForm):void{
console.log(f);
}
}
Whenyoutestthismanually,youshouldseeyourbrowserlogginganNgFormobjecteverytimeyouclickontheSavebutton.Insidethisobject,youshouldseeashinynewFormGroupandalsothengSubmitEventEmitterthatyouarelisteningto.Sofar,sogood!
DeclaringformfieldswithngModel
Youmayhavenoticedthatnoneoftheformfieldshavebeencollected.This,ofcourse,isbecauseAngularhasnotbeeninstructedtopayattentiontothem.Forthis,FormsModuleprovidesyouwithngModel,whichwilldocertainthingsforyou:
![Page 229: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/229.jpg)
InstantiateaFormControlobject.AttachittotheDOMelementthatincorporatesthengModelattribute.LocatetheFormGroupthattheelementlivesinsideandaddtoittheFormControlitjustcreated.ThestringvalueofthenameattributewillbeitskeyinsidetheFormGroup.
Note
Thislastbulletisimportant,asattemptingtousengModelwithoutanencompassingformcontrolconstructtoattachitselftowillresultinerrors.Thisformcontrolconstructcanbetheform'sFormGroupitself,oritcanevenbeachildFormGroupinstance.
Withthis,goaheadandaddngModeltoeachofthetextinputfields:
import{Component}from'@angular/core';
import{NgForm}from'@angular/forms';
@Component({
selector:'article-editor',
template:`
<form#f="ngForm"
(ngSubmit)="saveArticle(f)">
<p><inputngModel
name="title"
placeholder="Articletitle"></p>
<p><textareangModel
name="text"
placeholder="Articletext"></textarea></p>
<p><buttontype="submit">Save</button></p>
</form>
`
})
exportclassArticleEditorComponent{
saveArticle(f:NgForm):void{
console.log(f);
}
}
Yourformshouldnowbefullyfunctional.Inthesubmithandler,youcanverifythatFormGrouphastwoFormControlobjectsattachedtoitbyinspectingf.form.controls,whichshouldgiveyouthefollowing:
{
text:FormControl{...},
title:FormControl{...}
}
![Page 230: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/230.jpg)
Howitworks...Inessence,youareusingthehierarchicalnatureoftheDOMtodirecthowyourFormControlarchitectureisstructured.ThetopmostNgForminstanceiscoupledwithaFormGroup;insidethis,therestoftheform'sFormControlobjectswillreside.
EachngModeldirectsitsreferencedFormControltotheFormGroupownedbytheNgForminstance.Withthisnestedstructurenowassembled,itispossibletoreadandreasonthestateoftheentireformfromtheNgFormobject.Thisbeingthecase,passingthisobjecttothesubmithandlerwillallowyoutomanageeveryaspectofforminspectionandvalidation.
![Page 231: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/231.jpg)
There'smore...If,instead,youwantedtogroupsomeofthesefieldstogether,thiscanbeaccomplishedbysimplywrappingthemwithanngModelGroupdirective.SimilartongModel,thisautomaticallyinstantiatesaFormGroupandattachesittotheparentFormGroup;also,itwilladdanyenclosedFormControlorFormGroupobjectstoitself.Forexample,refertothefollowing:
[app/article.component.ts]
import{Component}from'@angular/core';
import{NgForm}from'@angular/forms';
@Component({
selector:'article-editor',
template:`
<form#f="ngForm"
(ngSubmit)="saveArticle(f)">
<divngModelGroup="article">
<p><inputngModel
name="title"
placeholder="Articletitle"></p>
<p><textareangModel
name="text"
placeholder="Articletext"></textarea></p>
</div>
<p><buttontype="submit">Save</button></p>
</form>
`
})
exportclassArticleEditorComponent{
saveArticle(f:NgForm):void{
console.log(f);
}
}
Now,inspectingf.form.controlswillrevealthatithasasingleFormGroupkeyedbyarticle:
{
article:FormGroup:{
controls:{
text:FormControl{...},
title:FormControl{...}
},
...
}
}
Sincethismatchesthestructureyousetupinthetemplate,itchecksout.
![Page 232: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/232.jpg)
SeealsoImplementingsimpletwo-waydatabindingwithngModeldemonstratesthenewwayinAngular2tocontrolbidirectionaldataflowImplementingbasicformswithFormBuilderandformControlNameshowshowtousetheFormBuilderservicetoquicklyputtogethernestedforms
![Page 233: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/233.jpg)
ImplementingbasicformswithFormBuilderandformControlNameOutofthebox,Angularprovidesawayforyoutoputtogetherformsthatdon'trelyonthetemplatehierarchyfordefinition.Instead,youcanuseFormBuildertoexplicitlydefinehowyouwanttostructuretheformobjectsandthenmanuallyattachthemtoeachinput.
Note
Thecode,links,andaliveexamplerelatedtothisrecipeareavailableathttp://ngcookbook.herokuapp.com/9302/.
![Page 234: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/234.jpg)
GettingreadySupposeyoubeganwiththefollowingskeletonapplication:
[app/article-editor.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'article-editor',
template:`
<p><inputplaceholder="Articletitle"></p>
<p><textareaplaceholder="Articletext"></textarea></p>
<p><button(click)="saveArticle()">Save</button></p>
`
})
exportclassArticleEditorComponent{
constructor(){}
saveArticle():void{}
}
YourobjectiveistocollectalloftheformdataandsubmititusingAngular'sformconstructs.
![Page 235: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/235.jpg)
Howtodoit...FormBuilderisincludedinReactiveFormsModule,soyouwillneedtoimportthesetargetsintotheapplicationmodule:
[app/app.module.ts]
import{NgModule}from'@angular/core';
import{BrowserModule}from'@angular/platform-browser';
import{ReactiveFormsModule}from'@angular/forms';
import{ArticleEditorComponent}from'./article-editor.component';
@NgModule({
imports:[
BrowserModule,
ReactiveFormsModule
],
declarations:[
ArticleEditorComponent
],
bootstrap:[
ArticleEditorComponent
]
})
exportclassAppModule{}
Additionally,youwillneedtoinjectitintoyourcomponenttomakeuseofit.InAngular2,thiscansimplybeaccomplishedbylistingitasatypedconstructorparameter.TheFormBuilderusesthegroup()methodtoreturnthetop-levelFormGroup,whichyoushouldassigntoyourcomponentinstance.Fornow,youwillpassanemptyobjectasitsonlyargument.
Withallthis,youcanintegratethearticleGroupFormGroupintothetemplatebyattachingitinsideaformtagusingtheformGroupdirective:
[app/article-editor.component.ts]
import{Component,Inject}from'@angular/core';
import{FormBuilder,FormGroup}from'@angular/forms';
@Component({
selector:'article-editor',
template:`
<form[formGroup]="articleGroup"
(ngSubmit)="saveArticle()">
<p><inputplaceholder="Articletitle"></p>
<p><textareaplaceholder="Articletext"></textarea></p>
<p><buttontype="submit">Save</button></p>
</form>
`
})
![Page 236: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/236.jpg)
exportclassArticleEditorComponent{
articleGroup:FormGroup;
constructor(@Inject(FormBuilder)formBuilder:FormBuilder){
this.articleGroup=formBuilder.group({});
}
saveArticle():void{}
}
Withallthis,youhavesuccessfullycreatedthestructureforyourform,butFormGroupisstillnotconnectedtothemultipleinput.Forthis,youwillfirstsetupthestructureofthecontrolsinsidethebuilderandconsequentlyattachthemtoeachinputtagwithformControlName,asfollows:
[app/article-editor.component.ts]
import{Component,Inject}from'@angular/core';
import{FormBuilder,FormGroup,Validators}from'@angular/forms';
@Component({
selector:'article-editor',
template:`
<form[formGroup]="articleGroup"
(ngSubmit)="saveArticle()">
<p><inputformControlName="title"
placeholder="Articletitle"></p>
<p><textareaformControlName="text"
placeholder="Articletext"></textarea></p>
<p><buttontype="submit">Save</button></p>
</form>
`
})
exportclassArticleEditorComponent{
articleGroup:FormGroup;
constructor(@Inject(FormBuilder)formBuilder:FormBuilder){
this.articleGroup=formBuilder.group({
title:[null,Validators.required],
text:[null,Validators.required]
});
}
saveArticle():void{
console.log(this.articleGroup);
}
}
Withthis,yourformwillhavetwoFormControlobjectsinstantiatedinsideit,andtheywillbeassociatedwithproperinputelements.WhenyouclickonSubmit,youwillbeabletoseetheinputFormControlsinsideFormGroup.However,youmayprefertonamespacetheseFormControlobjectsinsideanarticledesignation,andyoucaneasilydothisbyintroducinganngFormGroupandacorrespondinglevelofindirectioninsidetheformBuilderdefinition:
![Page 237: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/237.jpg)
[app/article-editor.component.ts]
import{Component,Inject}from'@angular/core';
import{FormBuilder,FormGroup,Validators}from'@angular/forms';
@Component({
selector:'article-editor',
template:`
<form[formGroup]="articleGroup"
(ngSubmit)="saveArticle()">
<divformGroupName="article">
<p><inputformControlName="title"
placeholder="Articletitle"></p>
<p><textareaformControlName="text"
placeholder="Articletext"></textarea></p>
</div>
<p><buttontype="submit">Save</button></p>
</form>
`
})
exportclassArticleEditorComponent{
articleGroup:FormGroup;
constructor(@Inject(FormBuilder)formBuilder:FormBuilder){
this.articleGroup=formBuilder.group({
article:formBuilder.group({
title:[null,Validators.required],
text:[null,Validators.required]
})
});
}
saveArticle():void{
console.log(this.articleGroup);
}
}
Now,thetitleandtextFormControlobjectswillexistnestedinsideanarticleFormGroupandtheycanbesuccessfullyvalidatedandinspectedinthesubmithandler.
![Page 238: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/238.jpg)
Howitworks...Asyoumightsuspect,thearrayslivinginsidetheformBuilder.groupdefinitionswillbeappliedasargumentstoaFormControlconstructor.ThisisnicesinceyoucanavoidthenewFormControl()boilerplatewhencreatingeachcontrol.ThestringthatkeystheFormControlislinkedtoitwithformControlName.BecauseyouareusingformControlNameandformGroupName,youwillneedtohavetheformBuildernestedstructurematchexactlytowhatisthereinthetemplate.
![Page 239: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/239.jpg)
There'smore...ItistotallyunderstandablethathavingtoduplicatethestructureinthetemplateandtheFormBuilderdefinitionisalittleannoying.Thisisespeciallytrueinthiscase,asthepresenceofformGroupdoesn'treallyaddanyvaluablebehaviorsinceitisattachedtoaninertdivelement.Instead,youmightwanttobeabletodothisarticlenamespacegroupingwithoutmodifyingthetemplate.ThisbehaviorcanbeaccomplishedwithformControl,whosebehaviorissimilartoformModel(itbindstoanexistinginstanceonthecomponent).
Note
Notetheparadigmthatisbeingdemonstratedwiththesedifferentkindsofformdirectives.WiththingssuchasngForm,formGroup,formArray,andformControl,Angularisimplicitlycreatingandlinkingtheseinstances.IfyouchoosetonotuseFormBuildertodefinehowFormControlsbehave,thiscanbeaccomplishedbyaddingvalidationdirectivestothetemplate.Ontheotherhand,youalsohaveformModelandformControl,whichbindtotheinstancesofthesecontrolobjectsthatyoumustmanuallycreateonthecomponent.
[app/article-editor.component.ts]
import{Component,Inject}from'@angular/core';
import{FormBuilder,FormControl,FormGroup,Validators}
from'@angular/forms';
@Component({
selector:'article-editor',
template:`
<form[formGroup]="articleGroup"
(ngSubmit)="saveArticle()">
<p><input[formControl]="titleControl"
placeholder="Articletitle"></p>
<p><textarea[formControl]="textControl"
placeholder="Articletext"></textarea></p>
<p><buttontype="submit">Save</button></p>
</form>
`
})
exportclassArticleEditorComponent{
titleControl:FormControl
=newFormControl(null,Validators.required);
textControl:FormControl
=newFormControl(null,Validators.required);
articleGroup:FormGroup;
constructor(@Inject(FormBuilder)formBuilder:FormBuilder){
this.articleGroup=formBuilder.group({
article:formBuilder.group({
title:this.titleControl,
text:this.textControl
![Page 240: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/240.jpg)
})
});
}
saveArticle():void{
console.log(this.articleGroup);
}
}
Importantly,notethatyouhavecreatedanidenticaloutputoftheoneyoucreatedearlier.titleandtextarebundledinsideanarticleFormGroup.However,thetemplatedoesn'tneedtohaveanyreferencetothisintermediateFormGroup.
![Page 241: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/241.jpg)
SeealsoImplementingsimpletwo-waydatabindingwithngModeldemonstratesthenewwayinAngular2tocontrolbidirectionaldataflowImplementingbasicfieldvalidationwithaFormControldetailsthebasicbuildingblockofanAngularformImplementingbasicformswithngFormdemonstratesAngular'sdeclarativeformconstruction
![Page 242: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/242.jpg)
CreatingandusingacustomvalidatorThebasicbuilt-invalidatorsthatAngularprovideswillgetyouofftheground,butifyourapplicationreliesonforms,youwillundoubtedlycometoapointwhereyouwillwanttodefineyourownvalidatorlogic.
Note
Thecode,links,andaliveexamplerelatedtothisrecipeareavailableathttp://ngcookbook.herokuapp.com/8574/.
![Page 243: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/243.jpg)
GettingreadySupposeyouhadstartedwiththefollowingskeletonapplication:
[app/article-editor.component.ts]
import{Component}from'@angular/core';
import{FormControl,Validators}from'@angular/forms';
@Component({
selector:'article-editor',
template:`
<h2>PsychStudyonHumilityWinsMajorAward</h2>
<textarea[formControl]="bodyControl"
placeholder="Articletext"></textarea>
<p><button(click)="saveArticle()">Save</button></p>
`
})
exportclassArticleEditorComponent{
articleBody:string='';
bodyControl:Control
=newFormControl(null,Validators.required);
saveArticle():void{
if(this.bodyControl.valid){
alert('Valid!');
}else{
alert('Invalid!');
}
}
}
Yourobjectiveistoaddanadditionalvalidationtothetextareathatwilllimititto10words.(Theeditorialstaffisbigonbrevity.)
![Page 244: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/244.jpg)
Howtodoit...IfyoulookatthefunctionsignatureofanAbstractControl,youwillnoticethatthevalidatorargumentisjustaValidatorFn.ThisvalidatorfunctioncanbeanyfunctionthatacceptsanAbstractControlobjectasitssoleargumentandreturnsanobjectkeyedwithstringsfortheerrorobject.Thiserrorobjectactsasadictionaryoferrors,andavalidatorcanreturnasmanyerrorsasapplicable.Thevalueofthedictionaryentrycan(andshould)containmetadataaboutwhatiscausingtheerror.Iftherearenoerrorsfoundbythecustomvalidator,itshouldjustreturnnull.
Thesimplestwaytoimplementthisisbyaddingamembermethodtothecomponent:
[app/article-editor.component.ts]
exportclassArticleEditor{
articleBody:string
bodyCtrl:Control
constructor(){
this.articleBody='';
this.bodyCtrl=newControl('',Validators.required);
}
wordCtValidator(c:Control):{[key:string]:any}{
letwordCt:number=(c.value.match(/\S+/g)||[]).length;
returnwordCt<=10?
null:
{'maxwords':{'limit':10,'actual':wordCt}};
}
saveArticle(){
if(this.bodyCtrl.valid){
alert('Valid!');
}else{
alert('Invalid!');
}
}
}
Note
Here,you'reusingaregularexpressiontomatchanynon-whitespacestrings,whichcanbetreatedasa"word."YoualsoneedtoinitializetheFormControlobjecttoanemptystringsinceyouareusingthestringprototype'smatchmethod.Sincethisregularexpressionwillreturnnullwhentherearenomatches,afallback||[]clauseisaddedtoalwaysyieldsomethingthathasalengthmethod.
Nowthatthevalidatormethodisdefined,youneedtoactuallyuseitonFormControl.Angularallowsyoutobundleanarrayofvalidatorsintoasinglevalidator,evaluatingtheminorder:
[app/article-editor.component.ts]
![Page 245: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/245.jpg)
import{Component}from'@angular/core';
import{FormControl,Validators}from'@angular/forms';
@Component({
selector:'article-editor',
template:`
<h2>PsychStudyonHumilityWinsMajorAward</h2>
<textarea[formControl]="bodyControl"
placeholder="Articletext"></textarea>
<p><button(click)="saveArticle()">Save</button></p>
`
})
exportclassArticleEditorComponent{
articleBody:string='';
bodyControl:FormControl=newFormControl(null,
[Validators.required,this.wordCtValidator]);
wordCtValidator(c:FormControl):{[key:string]:any}{
letwordCt:number
=((c.value||'').match(/\S+/g)||[]).length;
returnwordCt<=10?
null:
{maxwords:{limit:10,actual:wordCt}};
}
saveArticle():void{
if(this.bodyControl.valid){
alert('Valid!');
}else{
alert('Invalid!');
}
}
}
Withthis,yourFormControlshouldnowonlybevalidwhenthereare10wordsorfewerandtheinputisnotempty.
![Page 246: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/246.jpg)
Howitworks...AFormControlexpectsaValidatorFnwithaspecifiedreturntype,butitdoesnotcarewhereitcomesfrom.Therefore,youwereabletodefineamethodinsidethecomponentclassandjustpassitalongwhenFormControlwasinstantiated.
TheFormControlobjectassociatedwithagiveninputmustbeabletohavevalidatorsassociatedwithit.Inthisrecipe,youfirstimplementedcustomvalidationusingexplicitassociationviatheinstantiationargumentsanddefiningthevalidatorasasimplestandaloneValidationFn.
![Page 247: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/247.jpg)
There'smore...Yourinnersoftwareengineershouldbetotallydissatisfiedwiththissolution.Thevalidatoryoujustdefinedcannotbeusedoutsidethiscomponentwithoutinjectingtheentirecomponent,andexplicitlylistingeveryvalidatorwheninstantiatingtheFormControlisamajorpain.
Refactoringintovalidatorattributes
AsuperiorsolutionistoimplementaformalValidatorclass.Thishasseveralbenefits:youwillbeabletoimport/exporttheclassandusethevalidatorasanattributeinthetemplate,whichobviatestheneedforbundlingvalidatorswithValidators.compose.
Yourstrategyshouldbetocreateadirectivethatcanfunctionnotonlyasanattribute,butalsoassomethingthatAngularcanrecognizeasaformalValidatorandautomaticallyincorporateitassuch.ThiscanbeaccomplishedbycreatingadirectivethatimplementstheValidatorinterfaceandalsobundlesthenewValidatordirectiveintotheexistingNG_VALIDATORStoken.
Note
Fornow,don'tworryaboutthespecificsofwhatishappeningwiththeprovidersarrayinsidethedirectivemetadataobject.Thiswillbecoveredindepthinthechapterondependencyinjection.AllthatyouneedtoknowhereisthatthiscodeisallowingtheFormControlobjectboundtotextareatoassociatethecustomvalidatoryouarebuildingwithit.
First,movethevalidationmethodtoitsowndirectivebyperformingthestepsmentionedintheprecedingparagraph:
[app/max-word-count.validator.ts]
import{Directive}from'@angular/core';
import{Validator,FormControl,NG_VALIDATORS}
from'@angular/forms';
@Directive({
selector:'[max-word-count]',
providers:[{
provide:NG_VALIDATORS,
useExisting:MaxWordCountValidator,
multi:true
}]
})
exportclassMaxWordCountValidatorimplementsValidator{
validate(c:FormControl):{[key:string]:any}{
letwordCt:number=((c.value||'')
.match(/\S+/g)||[]).length;
returnwordCt<=10?
null:
{maxwords:{limit:10,actual:wordCt}};
![Page 248: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/248.jpg)
}
}
Next,addthisdirectivetotheapplicationmodule:
[app/app.module.ts]
import{NgModule}from'@angular/core';
import{BrowserModule}from'@angular/platform-browser';
import{ReactiveFormsModule}from'@angular/forms';
import{ArticleEditorComponent}from'./article-editor.component';
import{MaxWordCountValidator}from'./max-word-count.validator';
@NgModule({
imports:[
BrowserModule,
ReactiveFormsModule
],
declarations:[
ArticleEditorComponent,
MaxWordCountValidator
],
bootstrap:[
ArticleEditorComponent
]
})
exportclassAppModule{}
Thismakesitavailabletoallthecomponentsinthismodule.What'smore,theproviderconfigurationyouspecifiedbeforeallowsyoutosimplyaddthedirectiveattributetoanyinput,andAngularwillbeabletoincorporateitsvalidationfunctionintothatFormControl.Theintegrationisasfollows:
[app/article-editor.component.ts]
import{Component}from'@angular/core';
import{FormControl}from'@angular/forms';
@Component({
selector:'article-editor',
template:`
<h2>PsychStudyonHumilityWinsMajorAward</h2>
<textarea[formControl]="bodyControl"
required
max-word-count
placeholder="Articletext"></textarea>
<p><button(click)="saveArticle()">Save</button></p>
`
})
exportclassArticleEditorComponent{
articleBody:string='';
bodyControl:FormControl=newFormControl();
![Page 249: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/249.jpg)
saveArticle():void{
if(this.bodyControl.valid){
alert('Valid!');
}else{
alert('Invalid!');
}
}
}
Thisisalreadyfarsuperior.TheMaxWordCountdirectivecannowbeimportedandusedanywhereinourapplicationbysimplylistingitasadirectivedependencyinacomponent.There'snoneedfortheValidator.composenastinesswheninstantiatingaFormControlobject.
Tip
ThisisespeciallyusefulwhenyouareimplicitlycreatingtheseFormControlobjectswithformControlandotherbuilt-informdirectives,whichformanyapplicationswillbetheprimaryformutilizationmethod.Buildingyourcustomvalidatorasanattributedirectivewillintegrateseamlesslyinthesesituations.
Youshouldstillbedissatisfiedthough,asthevalidatorishardcodedtocheckfor10words.Youwouldinsteadliketoleavethisuptotheinputthatisusingit.Therefore,youshouldchangethedirectivetoacceptasingleparameter,whichwilltaketheformoftheattribute'svalue:
[app/max-word-count.validator.ts]
import{Directive}from'@angular/core';
import{Validator,FormControl,NG_VALIDATORS}
from'@angular/forms';
@Directive({
selector:'[max-word-count]',
inputs:['rawCount:max-word-count'],
providers:[{
provide:NG_VALIDATORS,
useExisting:MaxWordCountValidator,
multi:true
}]
})
exportclassMaxWordCountValidatorimplementsValidator{
rawCount:string;
validate(c:FormControl):{[key:string]:any}{
letwordCt:number=
((c.value||'').match(/\S+/g)||[]).length;
returnwordCt<=this.maxCount?
null:
{maxwords:{limit:this.maxCount,actual:wordCt}};
}
getmaxCount():number{
![Page 250: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/250.jpg)
returnparseInt(this.rawCount);
}
}
[app/article-editor.component.ts]
import{Component}from'@angular/core';
import{FormControl}from'@angular/forms';
@Component({
selector:'article-editor',
template:`
<h2>PsychStudyonHumilityWinsMajorAward</h2>
<textarea[formControl]="bodyControl"
required
max-word-count="10"
placeholder="Articletext"></textarea>
<p><button(click)="saveArticle()">Save</button></p>
`
})
exportclassArticleEditorComponent{
articleBody:string='';
bodyControl:FormControl=newFormControl();
saveArticle():void{
if(this.bodyControl.valid){
alert('Valid!');
}else{
alert('Invalid!');
}
}
}
Nowyouhavedefinedthevalueoftheattributeasaninputtothevalidator,whichyoucanthenusetoconfigurehowthevalidatorwilloperate.
![Page 251: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/251.jpg)
SeealsoCreatingandusingacustomasynchronousvalidatorwithPromisesshowshowAngularallowsyoutohaveadelayedevaluationoftheformstate
![Page 252: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/252.jpg)
CreatingandusingacustomasynchronousvalidatorwithPromisesAstandardvalidatoroperatesundertheassumptionthatthevalidityofacertaininputcanbecalculatedinashortamountoftimethattheapplicationcanwaittogetoverwithbeforeitcontinuesfurther.What'smore,Angularwillrunthisvalidationeverytimethevalidatorisinvoked,whichmightbequiteoftenifformvalidationisboundtorapid-fireeventssuchaskeypresses.
Therefore,itmakesgoodsensethataconstructexiststhatwillallowyoutosmoothlyhandlethevalidationproceduresthattakeanarbitraryamountoftimetoexecuteorproceduresthatmightnotreturnatall.Forthis,AngularoffersasyncValidator,whichisfullycompatiblewithPromises.
Note
Thecode,links,andaliveexamplerelatedtothisrecipeareavailableathttp://ngcookbook.herokuapp.com/7811/.
![Page 253: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/253.jpg)
GettingreadySupposeyouhadstartedwiththefollowingskeletonapplication:
[app/article-editor.component.ts]
import{Component}from'@angular/core';
import{FormControl}from'@angular/forms';
@Component({
selector:'article-editor',
template:`
<h2>NewYork-StylePizzaActuallySaucyCardboard</h2>
<textarea[formControl]="bodyControl"
placeholder="Articletext">
</textarea>
<p><button(click)="saveArticle()">Save</button></p>
`
})
exportclassArticleEditorComponent{
articleBody:string='';
bodyControl:FormControl=newFormControl();
saveArticle():void{
if(this.bodyControl.valid){
alert('Valid!');
}else{
alert('Invalid!');
}
}
}
Yourobjectiveistoconfigurethisforminawaythatitwillbecomevalidonly5secondsaftertheuserenterstheinputinordertodetersimplespambots.
![Page 254: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/254.jpg)
Howtodoit...First,createyourvalidatorclass,andinsideit,placeastaticvalidationmethod.Thisissimilartoasynchronousvalidationmethod,butitwillinsteadreturnaPromiseobject,passingtheresultdatatotheresolvemethod.TheFormControlobjectacceptstheasyncValidatorasitsthirdargument.Ifyouweren'tusinganynormalValidators,youcouldleaveitasnull.
Tip
AsyouwouldcombineseveralValidatorsintooneusingValidators.compose,asyncValidatorscanbecombinedusingValidators.composeAsync.
Createthevalidatorskeletoninitsownfile:
[app/delay.validator.ts]
import{FormControl,Validator}from'@angular/forms';
exportclassDelayValidatorimplementsValidator{
staticvalidate(c:FormControl):Promise<{[key:string]:any}>{
}
}
Thoughthevalidatordoesnotyetdoanything,youmaystilladdittothecomponent:
[app/article-editor.component.ts]
import{Component}from'@angular/core';
import{FormControl,Validators}from'@angular/forms';
import{DelayValidator}from'./delay.validator';
@Component({
selector:'article-editor',
template:`
<h2>NewYork-StylePizzaActuallySaucyCardboard</h2>
<textarea[formControl]="bodyControl"
placeholder="Articletext">
</textarea>
<p><button(click)="saveArticle()">Save</button></p>
`
})
exportclassArticleEditorComponent{
articleBody:string='';
bodyControl:FormControl=
newFormControl(null,null,DelayValidator.validate);
saveArticle():void{
if(this.bodyControl.valid){
alert('Valid!');
}else{
![Page 255: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/255.jpg)
alert('Invalid!');
}
}
}
Thevalidatormustreturnapromise,butthispromisedoesn'teverneedtoberesolved.Furthermore,you'dliketosetthedelaytoonlyonetimeperrendering.Sointhiscase,youcanjustattachthepromisetotheFormControl:
[app/delay.validator.ts]
import{FormControl,Validator}from'@angular/forms';
exportclassDelayValidatorimplementsValidator{
staticvalidate(c:FormControl):Promise<{[key:string]:any}>{
if(c.pristine&&!c.value){
returnnewPromise;
}
if(!c.delayPromise){
c.delayPromise=newPromise((resolve)=>{
setTimeout(()=>{
console.log('resolve');
resolve();
},5000);
});
}
returnc.delayPromise;
}
}
Withthisaddition,theformwillremaininvaliduntil5secondsafterthefirsttimethevalueofthetextareaischanged.
![Page 256: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/256.jpg)
Howitworks...Asynchronousvalidatorsarehandledindependentlyviaregular(synchronous)validators,butotherthantheirinternallatencydifferences,theyultimatelybehaveinnearlytheexactsameway.TheimportantdifferenceisthatanasyncValidator,apartfromthevalidandinvalidstatesthatitshareswithanormalValidator,hasapendingstate.TheFormControlwillremaininthisstateuntilapromiseismadeindicatingtheValidatorwillreturneitherresolvesorrejects.
Note
AFormControlinapendingstateistreatedasinvalidforthepurposeofcheckingthevalidityofaggregatingconstructs,suchasFormGrouporFormArray.
IntheValidatoryoujustcreated,checkingthepristinepropertyofFormControlisafinewayofascertainingwhetherornottheformis"fresh."Beforetheusermodifiestheinput,pristineistrue;followinganymodification(evenremovingalloftheenteredtext),pristinebecomesfalse.Therefore,itisaperfecttoolinthisexample,asitallowsustohavetheFormControlmaintaintheformstatewithoutovercomplicatingtheValidator.
![Page 257: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/257.jpg)
There'smore...It'scriticaltonotetheformthatthisvalidatortakes.ThevalidationmethodinsidetheDelayValidatorclassisastaticmethodandnowhereistheDelayValidatorclassbeinginstantiated.Thepurposeoftheclassismerelytohousethevalidator.Therefore,youareunabletostoreinformationinsidethisclass,sincetherearenoinstancesinwhichyoucandoso.
Tip
Inthisexample,youmightbetemptedtoaddmemberdatatothevalidatorsinceyouwanttotrackwhethertheinputhasbeenmodifiedyet.Doingsoisverymuchananti-pattern!TheFormControlobjectshouldactasyoursolesourceofstatefulinformationinthisscenario.AFormControlobjectisinstantiatedforeachinputfield,andthereforeitistheideal"datastore"withwhichyoucantrackwhattheinputisdoing.
Validatorexecution
Ifyouweretoinspectwhenthevalidatormethodisbeingcalled,youwouldfindthatitexecutesonlyonakeypressinsidetextarea.Thismayseemarbitrary,butthedefaultFormControl/inputassignmentistoevaluatethevalidatorsofFormControlonachangeeventemittedfromtheinput.FormControlobjectsexposearegisterOnChangemethod,whichletsyouhookontothesamepointthatthevalidatorswillbeevaluated.
![Page 258: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/258.jpg)
SeealsoCreatingandusingacustomvalidatordemonstrateshowtocreateacustomdirectivethatbehavesasinputvalidation
![Page 259: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/259.jpg)
Chapter4.MasteringPromisesThischapterwillcoverthefollowingrecipes:
UnderstandingandimplementingbasicPromisesChainingPromisesandPromisehandlersCreatingPromisewrapperswithPromise.resolve()andPromise.reject()ImplementingPromisebarrierswithPromise.all()CancelingasynchronousactionswithPromise.race()ConvertingaPromiseintoanObservableConvertinganHTTPserviceObservableintoZoneAwarePromise
![Page 260: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/260.jpg)
IntroductionInAngular1,promisesactedasstrangebirds.Theywereessentialforbuildingrobustasynchronousapplications,butusingthemseemedtocomeataprice.Theirimplementationbywayofthe$qserviceandthedualityofpromiseanddeferredobjectsseemedbizarre.Nonetheless,onceyouwereabletomasterthem,itwaseasytoseehowtheycouldbethefoundationofextremelyrobustimplementationsinthesingle-threadedevent-drivenworldofJavaScript.
Fortunately,fordeveloperseverywhere,ES6formallyembracesthePromisefeatureasacentralcomponent.SinceTypeScriptisasupersetofES6,youwillbepleasedtoknowthatyoucanwieldpromiseseverywhereinAngularwithoutextrabaggage.AlthoughObservablessubsumealotoftheutilityofferedbypromises,thereisstillverymuchaplacefortheminyourtoolkit.
Tip
BeingabletousePromisesnativelyisaprivilegeofTypeScripttoaJavaScripttranspilation.Asofnow,somebrowserssupportPromisesnatively,whilesomedonot.Goodnewsisthatifyou'rewritingyourapplicationsinTypeScriptandaretranspilingthemproperly,youdon'thavetoworryaboutthis!Really,theonlytimeyouwouldneedtoconsidertheactualtranspilationmechanicsiswhenyouneedinformationrelatedtotheperformanceorpayloadsizebenefitsofnativeimplementationsversustheirrespectivepolyfills,andthisshouldneverbeanissuefornearlyallapplications.
![Page 261: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/261.jpg)
UnderstandingandimplementingbasicPromisesPromisesareveryusefulinmanyofthecoreaspectsofAngular.Althoughtheyarenolongerboundtothecoreframeworkservice,theystillmanifestthemselvesthroughoutAngular'sAPIs.TheimplementationisconsiderablysimplerthanAngular1,butthemainrhythmshaveremainedconsistent.
Note
Youcanrefertothecode,links,andaliveexampleofthisathttp://ngcookbook.herokuapp.com/5195.
![Page 262: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/262.jpg)
GettingreadyBeforeyoustartusingpromises,youshouldfirstunderstandtheproblemtheyaretryingtosolve.Withoutworryingtoomuchabouttheinternals,youcanclassifytheconceptofaPromiseintothreedistinctstages:
Initialization:IhaveapieceofworkthatIwanttoaccomplish,andIwanttodefinewhatshouldhappenwhenthisworkiscompleted.Idonotknowwhetherthisworkwillbeevercompleted;also,theworkmayeitherfailorsucceed.Pending:Ihavestartedthework,butithasnotbeencompletedyet.Completed:Theworkisfinished,andthepromiseassumesafinalstate.The"completed"stateassumestwoforms:resolvedandrejected.Thesecorrespondtosuccessandfailure,respectively.
Thereismorenuancetohowpromiseswork,butfornow,thisissufficienttogetintosomeofthecode.
![Page 263: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/263.jpg)
Howtodoit...Apromiseimplementationinoneofitssimplestformsisasfollows:
//promisesareinstantiatedwiththe'new'keyword
varpromise=newPromise(()=>{});
ThefunctionpassedtothePromiseconstructoristhepieceofworkthatisexpectedtoexecuteasynchronously.Theformaltermforthisfunctionisexecutor.
Note
ThePromiseconstructordoesn'tcareatallabouthowtheexecutorfunctionbehaves.Itmerelyprovidesitwiththetworesolveandrejectfunctions.Itisleftuptotheexecutorfunctiontoutilizethemappropriately.Notethattheexecutorfunctiondoesn'tneedtobeasynchronousatall;however,ifitisn'tasynchronous,thenyoumightnotneedaPromiseforwhatyouaretryingtoaccomplish.
Whenthisfunctionisexecuted,internallyitunderstandswhenitiscompleted;however,ontheoutside,thereisnoconstructthatrepresentstheconceptof"runthiswhentheexecutorfunctioniscompleted".Therefore,itsfirsttwoparametersaretheresolveandrejectfunctions.Thepromisewrappingtheexecutorfunctionisinthependingstateuntiloneoftheseisinvoked.Onceinvoked,thepromiseirreversiblyassumestherespectivestate.
Note
Theexecutorfunctionisinvokedimmediatelywhenthepromiseisinstantiated.Justasimportantly,itisinvokedbeforethepromiseinstantiationisreturned.Thismeansthatifthepromisereacheseitherafulfilledorrejectedstateinsidetheexecutorsynchronously,thenthereturnvalueofnewPromise(...)willbethefreshlyconstructedPromisewitharesolvedorrejectedstatus,skippingthependingstateentirely.
Thereturnvalueofexecutorisunimportant.Nomatterwhatitreturns,thepromiseconstructorwillalwaysreturnthefreshlycreatedpromise.
Thefollowingcodedemonstratesfivedifferentexamplesofwaysthatapromisecanbeinstantiated,resolved,orrejected:
//Thisexecutorispassedresolveandreject,butis
//effectivelyano-op,sothepromisep2willforever
//remaininthe'pending'state.
constp1=newPromise((resolve,reject)=>{});
//Thisexecutorinvokes'resolve'immediately,so
//p2willtransitiondirectlytothe'fulfilled'state.
constp2=newPromise((resolve,reject)=>resolve());
![Page 264: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/264.jpg)
//Thisexecutorinvokes'reject'immediately,so
//p3willtransitiondirectlytothe'rejected'state.
//Atransitiontothe'rejected'statewillalsothrow
//anexception.Thisexceptionisthrownafterthe
//executorcompletes,soanylogicfollowingthe
//invocationofrejectwillstillbeexecuted.
constp3=newPromise((resolve,reject)=>{
reject();
//Thislog()printsbeforetheexceptionisthrown
console.log('Igotrejected!');
});
//Thisexecutorinvokes'resolve'immediately,so
//p4willtransitiondirectlytothe'fulfilled'state.
//Onceapromiseexitsthe'pending'state,itcannotchange
//again,soeventhoughrejectisinvokedafterwards,the
//finalstateofp4isstill'fulfilled'.
constp4=newPromise((resolve,reject)=>{
resolve();
reject();
});
//Thisexecutorassignsitsresolvefunctiontoavariable
//intheencompassinglexicalscopesoitcanbecalled
//outsidethepromisedefinition.
varouterResolve;
constp5=newPromise((resolve,reject)=>{
outerResolve=resolve();
});
//Stateofp5is'pending'
outerResolve();
//Stateofp5is'fulfilled'
Withwhatyou'vedonesofar,youwillnotfindthepromiseconstructtobeofmuchuse;thisisbecauseallthattheprecedingcodeaccomplishesisthesettingupofthestateofasinglepromise.Therealvalueemergeswhenyousetthesubsequentstatehandlers.APromiseobject'sAPIexposesathen()method,whichallowsyoutosethandlerstobeexecutedwhenthePromisereachesitsfinalstate:
//p1isasimplepromisetowhichyoucanattachhandlers
constp1=newPromise((resolve,reject)=>{});
//p1exposesathen()methodwhichacceptsa
//resolvehandler(onFulfilled),anda
//rejecthandler(onRejected)
p1.then(
//onFulfilledisinvokedwhenresolve()isinvoked
()=>{},
//onRejectedisinvokewhenreject()isinvoked
()=>{});
![Page 265: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/265.jpg)
//Iflefthere,p1willforeverremain"pending"
//Usingthe'new'keywordstillallowsyoutocalla
//methodonthereturnedinstance,sodefiningthe
//then()handlersimmediatelyisallowed.
//
//Instantlyresolvesp2
constp2=newPromise((resolve,reject)=>resolve())
.then(
//Thismethodwillimmediatelybeinvokedfollowing
//thep2executorinvokingresolve()
()=>console.log('resolved!'));
//"resolved!"
//Instantlyrejectsp3
constp3=newPromise((resolve,reject)=>reject())
.then(
()=>console.log('resolved!'),
//Thissecondmethodwillimmediatelybeinvokedfollowing
//thep3executorinvokingreject()
()=>console.log('rejected!'));
//"rejected!"
constp4=newPromise((resolve,reject)=>reject())
//Ifyoudon'trequireuseoftheresolvehandler,
//catch()allowsyoutodefinejusttheerrorhandling
.catch(()=>console.log('rejected!'));
//executorparameterscanbecapturedoutsideitslexical
//scopeforlaterinvocation
varouterResolve;
constp5=newPromise((resolve,reject)=>{
outerResolve=resolve;
}).then(()=>console.log('resolved!'));
outerResolve();
//"resolved!"
![Page 266: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/266.jpg)
Howitworks...PromisesinJavaScriptconfertothedevelopertheabilitytowriteasynchronouscodeinparallelwithsynchronouscodemoreeasily.InJavaScript,thiswasformerlysolvedwithnestedcallbacks,colloquiallyreferredtoas"callbackhell."Asinglecallback-orientedfunctionmightbewrittenasfollows:
//agenericasynchronouscallbackfunction
functionasyncFunction(data,successCallback,errorCallback){
//asyncFunctionwillperformsomeoperationthatmaysucceed,
//mayfail,ormaynotreturnatall,anyofwhich
//occursinanunknownamountoftime
//thispseudo-responsecontainsasuccessboolean,
//andthereturneddataifsuccessful
asyncOperation(data,function(response){
if(response.success===true){
successCallback(response.data);
}else{
errorCallback();
}
});
};
Ifyourapplicationdoesnotdemandanysemblanceofin-orderorcollectivecompletion,thenthefollowingwillsuffice:
functionsuccessCallback(data){
//asyncFunctionsucceeded,handledataappropriately
};
functionerrorCallback(){
//asyncFunctionfailed,handleappropriately
};
asyncFunction(data1,successCallback,errorCallback);
asyncFunction(data2,successCallback,errorCallback);
asyncFunction(data3,successCallback,errorCallback);
Thisisalmostneverthecasethough.Often,yourapplicationwilleitherdemandthatthisdataisacquiredinasequence,orthatanoperationthatrequiresmultipleasynchronouslyacquiredpiecesofdataexecutesonceallofthedatahasbeensuccessfullyacquired.Inthiscase,withoutaccesstopromises,thecallbackhellemerges:
asyncFunction(data1,(foo)=>{
asyncFunction(data2,(bar)=>{
asyncFunction(data3,(baz)=>{
//foo,bar,bazcannowallbeusedtogether
combinatoricFunction(foo,bar,baz);
},errorCallback);
},errorCallback);
![Page 267: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/267.jpg)
},errorCallback);
Thisso-calledcallbackhellhereisreallyjustanattempttoserializethreeasynchronouscalls,buttheparametrictopologyoftheseasynchronousfunctionsforcesthedevelopertosubjecttheirapplicationtothisugliness.
![Page 268: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/268.jpg)
There'smore...Animportantpointtorememberaboutpromisesisthattheyallowyoutobreakapartacalculationintotwoparts:thepartthatunderstandswhenthepromise's"execution"hasbeencompletedandthepartthatsignalstotherestoftheprogramthattheexecutionhasbeencompleted.
DecoupledandduplicatedPromisecontrol
BecauseapromisecangiveawaythecontrolofwhodecideswherethePromisewillbemadeready,multipleforeignpartsofthecodecansetthestateofthepromise.
Apromiseinstancecanbeeitherresolvedorrejectedatmultipleplacesinsidetheexecutor::
constp=newPromise((resolve,reject)=>{
//thefollowingarepseudo-methods,eachofwhichcanbecalled
//independentlyandasynchronously,ornotatall
functioncanHappenFirst(){resolve();};
functionmayHappenFirst(){resolve();}
functionmightHappenFirst(){reject();};
});
Apromiseinstancecanalsoberesolvedatmultipleplacesoutsidetheexecutor:
varouterResolve;
constp=newPromise((resolve,reject)=>{
outerResolve=resolve;
});
//thefollowingarepseudo-methods,eachofwhichcanbecalled
//independentlyandasynchronously,ornotatall
functioncanHappenFirst(){outerResolve();};
functionmayHappenFirst(){outerResolve();}
functionmightHappenFirst(){outerResolve();};
Note
OnceaPromise'sstatebecomesfulfilledorrejected,attemptstorejectorresolvethatpromisefurtherwillbesilentlyignored.Apromisestatetransitionoccursonlyonce,anditcannotbealteredorreversed.
ResolvingaPromisetoavalue
Partofthecentralconceptofpromiseconstructsisthattheyareableto"promise"thattherewillbeavalueavailablewhenthepromiseisresolved.
Statesdonotnecessarilyhaveadatavalueassociatedwiththem;theyonlyconfertothepromiseadefinedstateofevaluation:
varresolveHandler=()=>{},
![Page 269: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/269.jpg)
rejectHandler=()=>{};
constp0=newPromise((resolve,reject)=>{
//statecanbedefinedwithanyofthefollowing:
//resolve();
//reject();
//resolve(myData);
//reject(myData);
}).then(resolveHandler,rejectHandler);
Anevaluatedpromise(resolvedorrejected)isassociatedwithahandlerforeachofthestates.Thishandlerisinvokeduponthepromise'stransitionintothatrespectivestate.Thesehandlerscanaccessthedatareturnedbytheresolutionorrejection:
constp1=newPromise((resolve,reject)=>{
//console.infoistheresolvehandler,
//console.erroristherejecthandler
resolve(123);
}).then(console.info,console.error);
//(info)123
//resettodemonstratereject()
constp2=newPromise((resolve,reject)=>{
//console.infoistheresolvehandler,
//console.erroristherejecthandler
reject(456);
}).then(console.info,console.error);
//(error)456
Delayedhandlerdefinition
Unlikecallbacks,handlerscanbedefinedatanypointinthepromiselifecycle,includingafterthepromisestatehasbeendefined:
constp3=newPromise((resolve,reject)=>{
//immediatelyresolvethepromise
resolve(123);
});
//subsequentlydefineahandler,willbeimmediately
//invokedsincepromiseisalreadyresolved
p3.then(console.info);
//(info)123
Multiplehandlerdefinition
Similartohowasingledeferredobjectcanberesolvedorrejectedatmultipleplacesintheapplication,asinglepromisecanhavemultiplehandlersthatcanbeboundtoasinglestate.For
![Page 270: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/270.jpg)
example,asinglepromisewithmultipleresolvedhandlersattachedtoitwillinvokeallthehandlersiftheresolvedstateisreached;thesameistrueforrejectedhandlers:
constp4=newPromise((resolve,reject)=>{
//Invokeresolve()after1second
setTimeout(()=>resolve(),1000);
});
constcb=()=>console.log('called');
p4.then(cb);
p4.then(cb);
//After1second:
//"called"
//"called"
PrivatePromisemembers
AnextremelyimportantdeparturefromAngular1isthatthestateofapromiseistotallyopaquetotheexecution.Formerly,youwereabletoteaseoutthestateofthepromiseusingthepseudo-private$$stateproperty.WiththeformalES6Promiseimplementation,thestatecannotbeinspectedbyyourapplication.Youcan,however,gleanthestateofapromisefromtheconsole.Forexample,inspectingapromiseinGoogleChromeyieldssomethinglikethefollowing:
Promise{
[[PromiseStatus]]:"fulfilled",
[[PromiseValue]]:123
}
Note
PromiseStatusandPromiseValueareprivateSymbols,whichareanewconstructinES6.Symbolcanbethoughtofasauniquekeythatisusefulforsettingpropertiesonobjectsthatshouldn'tbeeasilyaccessedfromelsewhere.Forexample,ifapromiseweretousethe'PromiseStatus'stringtokeyaproperty,itcouldbeeasilyusedoutsidetheobject,evenifthepropertywassupposedtoremainprivate.WithES6privatesymbols,however,asymbolisuniquewhengenerated,andthereisnogoodwaytoaccessitinsidetheinstance.
![Page 271: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/271.jpg)
SeealsoChainingPromisesandPromisehandlersdetailshowyoucanwieldthispowerfulchainingconstructtoserializeasynchronousoperationsCreatingPromisewrapperswithPromise.resolve()andPromise.reject()demonstrateshowtousethecorePromiseutilities
![Page 272: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/272.jpg)
ChainingPromisesandPromisehandlersMuchofthepurposeofpromisesistoallowthedevelopertoserializeandreasonaboutindependentasynchronousactions.ThiscanbeaccomplishedbyutilizingthePromisechainingfeature.
Note
Thecode,links,andaliveexampleofthisareavailableathttp://ngcookbook.herokuapp.com/6828/.
![Page 273: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/273.jpg)
Howtodoit...Thepromisehandlerdefinitionmethodthen()returnsanotherpromise,whichcanhavefurtherhandlersdefineduponit—inahandlercalledchain:
varsuccessHandler=()=>{console.log('called');};
varp=newPromise((resolve,reject)=>{resolve();})
.then(successHandler)
.then(successHandler)
.then(successHandler);
//called
//called
//called
Chainedhandlers'datahandoff
Chainedhandlerscanpassdatatotheirsubsequenthandlersinthefollowingmanner:
varsuccessHandler=(val)=>{
console.log(val);
returnval+1;
};
varp=newPromise((resolve,reject)=>{resolve(0);})
.then(successHandler)
.then(successHandler)
.then(successHandler);
//0
//1
//2
Rejectingachainedhandler
Returningnormallyfromapromisehandler(nottheexecutor)will,bydefault,signalchildpromisestatestobecomeresolved.However,ifeithertheexecutororthesubsequenthandlersthrowanuncaughtexception,theywill,bydefault,reject;thiswillservetocatchtheexception:
constp=newPromise((resolve,reject)=>{
//executorwillimmediatelythrowanexception,forcing
//areject
throw123;
})
.then(
//childpromiseresolvedhandler
data=>console.log('resolved',data),
//childpromiserejectedhandler
data=>console.log('rejected',data));
![Page 274: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/274.jpg)
//"rejected",123
Note
Notethattheexception,hereanumberprimitive,isthedatathatispassedtotherejectionhandler.
![Page 275: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/275.jpg)
Howitworks...APromisereachingafinalstatewilltriggerchildpromisestofollowitinturn.Thissimplebutpowerfulconceptallowsyoutobuildbroadandfault-tolerantpromisestructuresthatelegantlymeshcollectionsofdependentasynchronousactions.
![Page 276: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/276.jpg)
There'smore...Thetopologyofpromiseslendsitselftosomeinterestingutilizationpatterns.
Promisehandlertrees
Promisehandlerswillexecuteintheorderthatthepromisesaredefined.Ifapromisehasmultiplehandlersattachedtoasinglestate,thenthatstatewillexecuteallitshandlersbeforeresolvingthefollowingchainedpromise:
constincr=val=>{
console.log(val);
return++val;
};
varouterResolve;
constfirstPromise=newPromise((resolve,reject)=>{
outerResolve=resolve;
});
//definefirstPromise'shandler
firstPromise.then(incr);
//appendanotherhandlerforfirstPromise,andcollect
//thereturnedpromiseinsecondPromise
constsecondPromise=firstPromise.then(incr);
//appendanotherhandlerforthesecondpromise,andcollect
//thereturnedpromiseinthirdPromise
constthirdPromise=secondPromise.then(incr);
//atthispoint,invokingouterResolve()will:
//resolvefirstPromise;firstPromise'shandlersexecutes
//resolvesecondPromise;secondPromises'shandlerexecutes
//resolvethirdPromise;nohandlersdefinedyet
//additionalpromisehandlerdefinitionorderis
//unimportant;theywillberesolvedasthepromises
//sequentiallyhavetheirstatesdefined
secondPromise.then(incr);
firstPromise.then(incr);
thirdPromise.then(incr);
//thesetupcurrentlydefinedisasfollows:
//firstPromise->secondPromise->thirdPromise
//incr()incr()incr()
//incr()incr()
//incr()
outerResolve(0);
//0
//0
//0
![Page 277: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/277.jpg)
//1
//1
//2
Note
Sincethereturnvalueofahandlerdecideswhetherornotthepromisestateisresolvedorrejected,anyofthehandlersassociatedwithapromiseisabletosetthestate—which,asyoumayrecall,canonlybesetonce.Thedefiningoftheparentpromisestatewilltriggerthechildpromisehandlerstobeexecuted.
Itshouldnowbeapparenthowthetreesofthepromisefunctionalitycanbederivedfromthecombinationofpromisechainingandhandlerchaining.Whenusedproperly,theycanyieldextremelyelegantsolutionsfordifficultanduglyasynchronousactionserializations.
catch()
Thecatch()methodisashorthandforpromise.then(null,errorCallback).Usingitcanleadtoslightlycleanerpromisedefinitions,butitisnothingmorethansyntacticalsugar:
varouterReject;
constp=newPromise((resolve,reject)=>{
outerReject=reject;
})
.catch(()=>console.log('rejected!'));
outerReject();
//"rejected"
Tip
Itisalsopossibletochainp.then().catch().Anerrorthrownbytheoriginalpromisewillpropagatethroughthepromisecreatedbythen(),causeittoreject,andreachthepromisecreatedbycatch().Itcreatesoneextralevelofpromiseindirection,buttoanoutsideobserver,itwillbehavethesame.
![Page 278: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/278.jpg)
SeealsoUnderstandingandimplementingbasicPromisesgivesanextensiverundownofhowandwhytousePromisesCreatingPromisewrapperswithPromise.resolve()andPromise.reject()demonstrateshowtousethecorePromiseutilities
![Page 279: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/279.jpg)
CreatingPromisewrapperswithPromise.resolve()andPromise.reject()Itisusefultohavetheabilitytocreatepromiseobjectsthathavealreadyreachedafinalstatewithadefinedvalue,andalsotobeabletonormalizeJavaScriptobjectsintopromises.Promise.resolve()andPromise.reject()affordyoutheabilitytoperformboththeseactions.
Note
Thecode,links,andaliveexampleofthisareavailableathttp://ngcookbook.herokuapp.com/9315/.
![Page 280: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/280.jpg)
Howtodoit...LikeallotherstaticPromisemethods,Promise.resolve()andPromise.reject()returnapromiseobject.Inthiscase,thereisnoexecutordefinition.
Ifoneofthesemethodsisprovidedwithanon-promiseargument,thereturnedpromisewillassumeeitherafulfilledorrejectedstate(correspondingtotheinvokedmethod).ThismethodwillpasstheargumenttoPromise.resolve()andPromise.reject(),alongwithanycorrespondinghandlers:
Promise.resolve('foo');
//Promise{[[PromiseStatus]]:"resolved",[[PromiseValue]]:"foo"}
Promise.reject('bar');
//Promise{[[PromiseStatus]]:"rejected",[[PromiseValue]]:"bar"}
//(error)Uncaught(inpromise)bar
Theprecedingcodeisbehaviorallyequivalenttothefollowing:
newPromise((resolve,reject)=>resolve('foo'));
//Promise{[[PromiseStatus]]:"resolved",[[PromiseValue]]:"foo"}
newPromise((resolve,reject)=>reject('bar'));
>>Promise{[[PromiseStatus]]:"rejected",[[PromiseValue]]:"bar"}
//(error)Uncaught(inpromise)bar
Promisenormalization
Promise.resolve()willuniquelyhandlescenarioswhereitispassedwithapromiseobjectasitsargument.Promise.resolve()willeffectivelyoperateasano-op,returningtheinitialpromiseargumentwithoutanymodification.Itwillnotmakeanattempttocoercetheargumentpromise'sstate:
consta=Promise.resolve('baz');
console.log(a);
//Promise{status:'resolved',value:'baz'}
constb=Promise.resolve(a);
console.log(b);
//Promise{status:'resolved',value:'baz'}
console.log(a===b);
//true
constc=Promise.reject('qux');
//Errorqux
console.log(c)
//Promise{status:'rejected',value:'qux'}
![Page 281: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/281.jpg)
constd=Promise.resolve(c);
console.log(d);
//Promise{status:'rejected',value:'qux'}
console.log(c===d);
//true
![Page 282: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/282.jpg)
Howitworks...WhenthinkingaboutPromisesinthecontextofthem"promising"toeventuallyassumeavalue,thesemethodsaresimplyamelioratinganylatentperiodseparatingthependingandfinalstates.
Thedichotomyisverysimple:
Promise.reject()willreturnarejectedpromisenomatterwhatitsargumentis.Evenifitisapromiseobject,thevalueofthereturnedpromisewillbethatofthepromiseobject.Promise.resolve()willreturnafulfilledpromisewiththewrappedvalueifthatvalueisnotapromise.Ifitisapromise,itbehavesasano-op.
![Page 283: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/283.jpg)
There'smore...Importantly,thebehaviorofPromise.resolve()isnearlythesameashow$q.when()operatedinAngular1.$q.when()wasabletonormalizepromiseobjects,butitwouldalwaysreturnanewlycreatedpromiseobject:
//Angular1
consta=$q(()=>{});
console.log(a);
//Promise{...}
constb=$q.when(a);
console.log(b);
//Promise{...}
console.log(a===b);
//false
![Page 284: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/284.jpg)
SeealsoUnderstandingandimplementingbasicPromisesgivesanextensiverundownofhowandwhytousePromisesImplementingPromisebarrierswithPromise.all()showyouhowPromisescanbecomposableCancelingasynchronousactionswithPromise.race()guidesyouthroughtheprocessofimplementingazero-failure-tolerantPromisesystem
![Page 285: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/285.jpg)
ImplementingPromisebarrierswithPromise.all()Youmayfindyourapplicationrequirestheuseofpromisesinanall-or-nothingtypeofsituation.Thatis,itwillneedtocollectivelyevaluateagroupofpromises,andthiscollectionwillresolveasasinglepromiseifandonlyifallofthecontainedpromisesareresolved;ifanyoneofthemisrejected,theaggregatepromisewillberejected.
Note
Thecode,links,andaliveexampleofthisareavailableathttp://ngcookbook.herokuapp.com/8496/.
![Page 286: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/286.jpg)
Howtodoit...ThePromise.all()methodacceptsaniterablecollectionofpromises(forexample,anarrayofPromiseobjectsoranobjectwithanumberofpromiseproperties),anditwillattempttoresolveallofthemasasingleaggregatepromise.Theparameteroftheaggregateresolvedhandlerwillbeanarrayorobjectthatmatchestheresolvedvaluesofthecontainedpromises:
varouterResolveA,outerResolveB;
constpromiseA=newPromise((resolve,reject)=>{
outerResolveA=resolve;
});
constpromiseB=newPromise((resolve,reject)=>{
outerResolveB=resolve;
});
constmultiPromiseAB=Promise.all([promiseA,promiseB])
.then((values)=>console.log(values));
outerResolveA(123);
outerResolveB(456);
//[123,456]
Ifanyofthepromisesinthecollectionarerejected,theaggregatepromisewillberejected.Theparameteroftheaggregaterejectedhandlerwillbethereturnedvalueoftherejectedpromise:
varouterResolveC,outerRejectD;
constpromiseC=newPromise((resolve,reject)=>{
outerResolveC=resolve;
});
constpromiseD=newPromise((resolve,reject)=>{
outerRejectD=reject;
});
constmultiPromiseCD=Promise.all([promiseC,promiseD])
.then(
values=>console.log(values),
rejectedValue=>console.error(rejectedValue));
//resolveacollectionpromise,nohandlerexecution
outerResolveC(123);
//rejectacollectionpromise,rejectionhandlerexecutes
outerRejectD(456);
//(error)456
![Page 287: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/287.jpg)
Howitworks...Asdemonstrated,theaggregatepromisewillreachthefinalstateonlywhenalloftheenclosedpromisesareresolved,orwhenasingleenclosedpromiseisrejected.Usingthistypeofpromiseisusefulwhenthecollectionofpromisesdonotneedtoreasonaboutoneanother,butcollectivecompletionistheonlymetricofsuccessforthegroup.
Inthecaseofacontainedrejection,theaggregatepromisewillnotwaitfortheremainingpromisestocomplete,butthosepromiseswillnotbepreventedfromreachingtheirfinalstate.Onlythefirstpromisetoberejectedwillbeabletopassrejectiondatatotheaggregatepromiserejectionhandler.
![Page 288: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/288.jpg)
There'smore...Promise.all()isinmanywaysextremelysimilartoanoperating-system-levelprocesssynchronizationbarrier.Aprocessbarrierisacommonpointinthethreadinstructionexecutionthatacollectionofprocesseswillreachindependentlyandatdifferenttimes,andnoprocesscanproceedfurtheruntilallhavereachedthispoint.Inthesameway,Promise.all()willnotproceedunlesseitherallofthecontainedpromiseshavebeenresolved—reachedthebarrier—orasinglecontainedrejectionwillpreventthatstatefromeverbeingachieved,inwhichcasethefailoverhandlerlogicwilltakeover.
SincePromise.all()allowsyoutohavearecombinationofpromises,italsoallowsyourapplication'sPromisechainstobecomeadirectedacyclicgraph(DAG).Thefollowingisanexampleofapromiseprogressiongraphthatdivergesfirstandconvergeslater:
![Page 289: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/289.jpg)
Thislevelofcomplexityisuncommon,butitisavailableforuseshouldyourapplicationrequireit.
![Page 290: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/290.jpg)
SeealsoCreatingPromiseWrapperswithPromise.resolve()andPromise.reject()demonstrateshowtousethecorePromiseutilitiesCancelingasynchronousactionswithPromise.race()guidesyouthroughtheimplementationofazero-failure-tolerantPromisesystem
![Page 291: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/291.jpg)
CancelingasynchronousactionswithPromise.race()ES6introducesPromise.race(),whichisabsentfromthe$qspecinAngular1.LikePromise.all(),thisstaticmethodacceptsaniterablecollectionofpromiseobjects;whicheveroneresolvesorrejectsfirstwillbecometheresultofthepromisewrappingthecollection.Thismayseemlikeunusualbehavior,butitbecomesquiteusefulwhenyou'rebuildingacancellationbehaviorintothesystem.
Note
Thecode,links,andaliveexampleofthisareavailableathttp://ngcookbook.herokuapp.com/4362/.
![Page 292: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/292.jpg)
GettingreadySupposeyoustartedwithasimplepromisethatresolvestoavalueafter3seconds:
constdelayedPromise=newPromise((resolve,reject)=>
setTimeout(resolve.bind(null,'foobar'),3000))
.then(val=>console.log(val));
Youwouldliketohavetheabilitytodetachapartofyourapplicationfromwaitingforthispromise.
![Page 293: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/293.jpg)
Howtodoit...Asimplesolutionwouldbetoexposethepromise'srejecthandlerandjustinvokeitfromwhateveristoperformthecancelation.However,itispreferabletostopwaitingforthispromiseinsteadofdestroyingit.
Note
AconcreteexampleofthiswouldbeaslowbutcriticalHTTPrequestthatyourapplicationmakes.YoumightnotwanttheUItowaitforittocomplete,butyoumayhaveresolvehandlersattachedtotherequestthatyoustillwanttohandletheresult,onceitisreturned.
Instead,youcantakeadvantageofPromise.race()andintroduceacancellationpromisealongsidetheoriginalone:
//Usethismethodtocapturethecancellationfunction
varcancel;
constcancelPromise=newPromise((resolve,reject)=>{
cancel=reject;
});
constdelayedPromise=newPromise((resolve,reject)=>
setTimeout(resolve.bind(null,'foobar'),3000));
//Promise.race()createsanewpromise
Promise.race([cancelPromise,delayedPromise])
.then(
val=>console.log(val),
()=>console.error('cancelled!'));
//Ifyouinvokecancel()before3secondselapses
//(error)"cancelled!"
//Instead,if3secondselapses
//"foobar"
Now,ifdelayedPromiseresolvesfirst,thepromisecreatedbyPromise.race()willlogthevaluepassedtoithere,foobar.If,however,youinvokecancel()beforeithappens,thenthatsamePromisewillprintacancelled!error.
![Page 294: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/294.jpg)
Howitworks...Promise.race()justwaitsforanyofitsinnerpromisestoarriveatthefinalstate.Itcreatesandreturnsanewpromisethatisbeholdentothestateofthecontainedpromises.Whenitobservesthatanyofthemtransitionstothefinalstate,thenewpromisealsoassumesthisstate.
Note
Inthisexample,theexecutorofcancelPromiseanddelayedPromiseareinvokedbeforePromise.race()iscalled.Sincepromisesonlycareaboutthestateofotherpromises,itisn'timportantthatthepromisespassedtoPromise.race()needtobealreadytechnicallystarted.
NotethattheuseofPromise.race()doesn'taffecttheimplementationofdelayedPromise.Evenwhencancel()isinvoked,delayedPromisewillstillberesolvedanditshandlerswillstillbeexecutednormally,unawarethatthesurroundingPromise.race()hasalreadybeenrejected.YoucanprovethistoyourselfbyaddingaresolvehandlertodelayedPromise,invokingcancel()andseeingtheresolvehandlerofdelayedPromisebeingexecutedanyway:
varcancel;
constcancelPromise=newPromise((resolve,reject)=>{
cancel=reject;
});
constdelayedPromise=newPromise((resolve,reject)=>
setTimeout(resolve.bind(null,'foobar'),3000))
.then(()=>console.log('stillresolved!'));
Promise.race([cancelPromise,delayedPromise])
.then(
val=>console.log(val),
()=>console.error('cancelled!'));
cancel();
//(error)cancelled!
//After3secondselapses
//"stillresolved!"
![Page 295: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/295.jpg)
SeealsoCreatingPromisewrapperswithPromise.resolve()andPromise.reject()demonstrateshowtousethecorePromiseutilitiesImplementingPromisebarrierswithPromise.all()showyouhowPromisescanbecomposable
![Page 296: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/296.jpg)
ConvertingaPromiseintoanObservableObservablesandPromisesservedifferentpurposesandaregoodatdifferentthings,butinaspecificpartofanapplication,youwillalmostcertainlywanttobedealingwithasingledenomination.Thismeansconvertingobservablesintopromisesandviceversa.ThankstoRxJS,thisisquitesimple.
Tip
FormoreonRxJSObservables,refertoChapter5,ReactiveXObservables,whichcoversthemindepth.
Note
Thecode,links,andaliveexampleofthisareavailableathttp://ngcookbook.herokuapp.com/5244/.
![Page 297: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/297.jpg)
Howtodoit...ThereisagooddealofparitybetweenPromiseandObservable.Therearediscretesuccessanderrorcases,andtheconceptofsuccessfulcompletiononlycorrespondstothesuccesscase.
RxJSobservablesexposeafromPromisemethod,whichwrapsPromiseasanObservable:
import{Observable}from'rxjs/Rx';
varouterResolve,outerReject;
constp1=newPromise((resolve,reject)=>{
outerResolve=resolve;
outerReject=reject;
});
varo1=Observable.fromPromise(p1);
NowthatyouhaveanObservableinstance,youcanutilizeitssubscribe()events,whichcorrespondtothestateofthePromiseinstance:
import{Observable}from'rxjs/Rx';
varouterResolve,outerReject;
constp1=newPromise((resolve,reject)=>{
outerResolve=resolve;
outerReject=reject;
});
varo1=Observable.fromPromise(p1);
o1.subscribe(
//onNexthandler
()=>console.log('resolved!'),
//onErrorhandler
()=>console.log('rejected'),
//onCompletedhandler
()=>console.log('finished!'));
outerResolve();
//"resolved!"
//"finished!"
![Page 298: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/298.jpg)
Howitworks...ThenewObservableinstancedoesn'treplacethepromise.ItjustattachesitselftothePromise'sresolvedandrejectedstates.Whenthishappens,itemitseventsandinvokestherespectivecallbacks.TheObservableinstanceisboundtothestateofthePromise,butPromiseisnotawarethatanythinghasbeenattachedtoitsinceitblindlyexposesitsresolveandrejecthooks.
Tip
NotethatonlyaresolvedPromisewillinvoketheonCompletedhandler;rejectingthepromisewillnotinvokeit.
![Page 299: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/299.jpg)
There'smore...ObservablesandPromisesareinterchangeableifyouaresoinclined,butdoconsiderthattheyarebothappropriateindifferentsituations.
Observablesaregoodatstream-typeoperations,wherethelengthofthestreamisindeterminate.ItiscertainlypossibletohaveanObservablethatonlyeveremitsoneevent,butanObservablewillnotbroadcastthisstatetolistenersthatareattachedlater,unlessyouconfigureittodoso(suchasBehaviorObservable).
Promisesaregoodatmaskingasynchronousbehavior.TheyallowyoutowritecodeandsethandlersuponthePromiseasifthepromisedstateorvaluewasrealizedatthetimeofexecution.Ofcourseit'snot,buttheabilitytodefineahandlersynchronouslyatruntimeandhavethePromiseinstancedecidewhenit'sappropriatetoexecuteit,aswellastheabilitytochainthesehandlers,isextremelyvaluable.
![Page 300: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/300.jpg)
SeealsoUnderstandingandimplementingbasicPromisesgivesanextensiverundownofhowandwhytousePromisesConvertinganHTTPserviceObservableintoZoneAwarePromisegivesyouanAngular-centricviewofhowPromises,Observables,andZonesintegrateinsideanAngularapplication
![Page 301: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/301.jpg)
ConvertinganHTTPserviceObservableintoaZoneAwarePromiseInAngular2,theRxJSasynchronousobservablesarefirst-classcitizensandmuchofthecoretoolkithasbeenconfiguredtorelyuponthem.Nonetheless,itisstillvaluabletobeabletohaveconversionbetweenthem,especiallysincetheyhavesimilarduties.
Tip
FormoreonRxJSObservables,refertoChapter5,ReactiveXObservables,whichcoversthemindepth.
Note
Thecode,links,andaliveexampleofthisareavailableathttp://ngcookbook.herokuapp.com/0905/.
![Page 302: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/302.jpg)
GettingreadyYou'llbeginwiththefollowingsimplisticapplication:
[app/article.component.ts]
import{Component}from'@angular/core';
import{Http}from'@angular/http';
@Component({
selector:'article',
template:`
<p></p>
`
})
exportclassArticleComponent{
constructor(privatehttp:Http){
//Fordemopurposes,havethisplunkrequestitselfto
//avoidcrossoriginerrors
console.log(
http.get('//run.plnkr.co/plunks/TBtcNDRelAOHDVpIuWw1'));
}
}
//Observable{...}
SupposeyourgoalwastoconvertthisHTTPcalltousepromisesinstead.
![Page 303: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/303.jpg)
Howtodoit...Forthepurposesofthisrecipe,youdon'treallyneedtounderstandanydetailsaboutthehttpserviceorRxJSasynchronousobservables.Allthatyouneedtoknowisthatanymethodexposedbythehttpservicewillreturnanobservable.Happily,theRxJSimplementationcanexposea.toPromise()methodthatconvertstheobservableintoitsequivalentPromiseobject:
[app/article.component.ts]
import{Component}from'@angular/core';
import{Http}from'@angular/http';
import'rxjs/Rx';
@Component({
selector:'article',
template:`
<p></p>
`
})
exportclassArticleComponent{
constructor(privatehttp:Http){
//Fordemopurposes,havethisplunkrequestitselfto
//avoidcrossoriginerrors
console.log(
http.get('//run.plnkr.co/plunks/TBtcNDRelAOHDVpIuWw1')
.toPromise());
}
}
//ZoneAwarePromise{...}
![Page 304: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/304.jpg)
Howitworks...TheHTTPservice,bydefault,returnsanobservable;however,withouttheimportedRxmodule,thiswillthrowanerror,sayingitcannotfindthetoPromise()method.
TheRxmoduleconferstoanobservabletheabilitytoconvertitselfintoapromiseobject.Angular2isintentionallynotutilizingtheObservablespecwithalltheRxJSoperatorstoallowyoutospecifyexactlywhichonesyouwant.Becausetheoperatorsexistasseparatemodules,thisleadstoasmallerpayloadsenttothebrowser.
Oncethe.toPromise()methodisinvoked,theobjectiscreatedtoaZoneAwarePromiseinstance.
Note
Thissoundsgnarly,butreally,it'sjustwrappingthePromiseimplementationaszone.jssothattheAngularzoneisawareofanyactionsthePromisecouldcausethatitshouldbeawareof.Foryourpurposes,thiscanbetreatedasaregularPromise.
![Page 305: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/305.jpg)
SeealsoUnderstandingandimplementingbasicPromisesgivesanextensiverundownofhowandwhytousePromisesConvertingaPromiseintoanObservablegivesyouanexampleofhowRxJScanbeusedtoconvertbetweenthesetwopowerfultypes
![Page 306: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/306.jpg)
Chapter5.ReactiveXObservablesThischapterwillcoverthefollowingrecipes:
BasicutilizationofObservableswithHTTPImplementingaPublish-SubscribemodelusingSubjectsCreatinganObservableAuthenticationServiceusingBehaviorSubjectsBuildingageneralizedPublish-Subscribeservicetoreplace$broadcast,$emit,and$onUsingQueryListsandObservablestofollowthechangesinViewChildrenBuildingafullyfeaturedAutoCompletewithObservables
![Page 307: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/307.jpg)
IntroductionBeforeyougetintothemeatofAngular2Observables,itisimportanttofirstunderstandtheproblemyouaretryingtosolve.
Afrequentlyencounteredscenarioinsoftwareiswhereyouareexpectingsomeentitytobroadcastthatsomethinghappened;let'scallthisan"event"(distinctfromabrowserevent).Youwouldliketohookintothisentityandattachbehaviortoitwheneveraneventoccurs.Youwouldalsoliketobeabletodetachfromthisentitywhenyounolongercareabouttheeventsitisbroadcasting.
ThereismorenuanceandadditionalcomplexitytoObservablesthatthischapterwillcover,butthisconceptofeventsunderscoresthefundamentalpatternthatisusefultoyouasthedeveloper.
![Page 308: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/308.jpg)
TheObserverPatternTheObserverPatternisn'talibraryorframework.ItisjustasoftwaredesignpatternuponwhichReactiveXObservablesarebuilt.Manylanguagesandlibrariesimplementthispattern,andReactiveXisjustoneoftheseimplementations;however,ReactiveXistheonethatAngular2hasformallyincorporatedintoitself.
TheObserverPatterndescribestherelationshipbetweensubject,whichwasdescribedasthe"entity"earlier,anditsobservers.Thesubjectisawareofanyobserversthatarewatchingit.Whenaneventisemitted,thesubjectisabletopassthiseventtoeachobserverviamethodsthatareprovidedwhentheobserverbeginstosubscribeit.
![Page 309: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/309.jpg)
ReactiveXandRxJSTheReactiveXlibraryisimplementedinnumerouslanguages,includingPython,Java,andRuby.RxJS,theJavaScriptimplementationoftheReactiveXlibrary,isthedependencythatAngular2utilizestoincorporateObservablesintonativeframeworkbehavior.SimilartoPromises,youcancreateastandaloneObservableinstancethroughdirectinstantiation,butmanyAngular2methodsandserviceswillalsoutilizeanObservableinterfacebydefault.
![Page 310: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/310.jpg)
ObservablesinAngular2Angular2integratesObservablesinawidevarietyofways.Ifyouarenewtothem,youmayinitiallyfeeloddusingthem.However,itisimportantyourecognizethatObservablesprovideasuperiorsoftwaredevelopmentpattern.
AlongwiththebulkRxJSmodulerxjs/Rx,youarealsoprovidedwiththestrippeddownObservablemodulerxjs/Observable.Thisminimalmoduleallowsindividualpiecesofnon-essentialbehaviortobeimportedasrequiredinordertoreducemodulebloat.Forexample,whenusingthislightweightObservablemodule,usingoperatorsorothersuchReactiveXconventionsnecessitatesthatyouexplicitlyincorporatethesemodules,inordertoextendtheavailableObservableinterface.
![Page 311: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/311.jpg)
ObservablesandPromisesBothObservablesandPromisesoffersolutionstoasynchronousconstructs,butObservablesaremorerobust,extensible,anduseful.AlthoughPromisesareavailablebydefaultintheES6specification,youwillquicklyrealizethattheybecomebrittlewhenyouattempttoapplythemoutsidetherealmofbasicapplicationbehavior.
TheReactiveXlibraryofferspowerfultoolingtowhichPromisescannotcompare.Observablesarecomposable,allowingyoutotransformandcombinethemintonewObservables.Theyalsoencapsulatetheconceptofacontinuousstreamofevents-aparadigmthatisencounteredinclient-sideprogrammingextremelyfrequentlyandthatPromisesdonottranslatewellto.
![Page 312: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/312.jpg)
BasicutilizationofObservableswithHTTPInAngular2,theHttpmodulenowbydefaultutilizestheObservablepatterntowrapXMLHttpRequest.Fordevelopersthatarefamiliarwiththepattern,itreadilytranslatestotheasynchronousnatureofrequeststoremoteresources.Fordevelopersthatarenewertothepattern,learningtheinsandoutsofHttpObservablesisagoodwaytowrapyourheadaroundthisnewparadigm.
Note
Thecode,links,andaliveexamplerelatedtothisareavailableathttp://ngcookbook.herokuapp.com/4121.
![Page 313: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/313.jpg)
GettingreadyForthepurposeofthisexample,you'lljustserveastaticJSONfiletotheapplication.HowevernotethatthiswouldbenodifferentifyouweresendingrequeststoadynamicAPIendpoint.
Beginbycreatingaskeletoncomponent,includingallthenecessarymodulesformakingHTTPrequests:
[app/article.component.ts]
import{Component}from'@angular/core';
import{Http}from'@angular/http';
@Component({
selector:'article',
template:`
<h1>{{title}}</h1>
<p>{{author}}</p>
`
})
exportclassArticleComponent{
title:string;
body:string;
constructor(privatehttp:Http){
}
}
Forthisexample,assumethereisaJSONfileinsidethestaticdirectorynamedarticle.json:
[article.json]
{
"title":"OrthopedicDoctorsAskCityforMoreSidewalkCracks",
"author":"JakeHsu"
}
![Page 314: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/314.jpg)
Howtodoit...SinceyouhavealreadyinjectedtheHttpservice,youcanbeginbydefiningthegetrequest:
[app/article.component.ts]
import{Component}from'@angular/core';
import{Http}from'@angular/http';
@Component({
selector:'article',
template:`
<h1>{{title}}</h1>
<p>{{author}}</p>
`
})
exportclassArticleComponent{
title:string;
body:string;
constructor(privatehttp_:Http){
http_.get('static/article.json');
}
}
ThiscreatesanObservableinstance,butyoustillneedtoaddinstructionsonhowtohandletherawstringoftheresponse.
Note
Atthispoint,youwillnoticethatthisdoesnotactuallyfireabrowserGETrequest.Thisiscoveredinthisrecipe'sThere'smoresection.
SinceyouknowtherequestwillreturnJSON,youcanutilizethejson()methodthataResponsewouldexpose.Thiscanbedoneinsidethemap()method.However,theObservabledoesnotexposethemap()methodbydefault,soyoumustimportitfromtherxjsmodule:
[app/article.component.ts]
import{Component}from'@angular/core';
import{Http}from'@angular/http';
import'rxjs/add/operator/map';
@Component({
selector:'article',
template:`
<h1>{{title}}</h1>
<p>{{author}}</p>
`
})
exportclassArticleComponent{
![Page 315: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/315.jpg)
title:string;
author:string;
constructor(privatehttp_:Http){
http_.get('static/article.json')
.map(response=>response.json());
}
}
Sofarsogood,butyou'restillnotdone.TheprecedingcodewillcreatetheObservableinstance,butyoustillhavetosubscribetoitinordertohandleanydataitwouldemit.Thiscanbeaccomplishedwiththesubscribe()method,whichallowsyoutoattachthecallbackanderrorhandlingmethodsofobserver:
[app/article.component.ts]
import{Component}from'@angular/core';
import{Http}from'@angular/http';
import'rxjs/add/operator/map';
@Component({
selector:'article',
template:`
<h1>{{title}}</h1>
<p>{{author}}</p>
`
})
exportclassArticleComponent{
title:string;
author:string;
constructor(privatehttp_:Http){
http_.get('static/article.json')
.map(response=>response.json())
.subscribe(
article=>{
this.title=article.title;
this.author=article.author;
},
error=>console.error(error));
}
}
Withallofthis,theGETrequestwillreturntheJSONfile,andtheresponsedatawillbeparsedanditsdatainterpolatedintotheDOMbythecomponent.
![Page 316: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/316.jpg)
Howitworks...Theprevioussectiongaveagoodhigh-leveloverviewofwhatwashappening,butitisusefultobreakthingsdownmorecarefullytounderstandwhateachindividualstepaccomplishes.
Observable<Response>
TheHttpserviceclassexposesthemethodsget(),post(),put(),andsoon—alltheHTTPverbsthatyouwouldexpect.EachofthesewillreturnObservable<Response>,whichwillemitaResponseinstancewhentherequestisreturned:
console.log(http_.get('static/article.json'));
//Observable{...}
Note
Itsoundsobvious,butObservablesareobservedbyanobserver.TheobserverwillwaitforObservabletoemitobjects,whichinthisexampletakestheformofResponse.
TheRxJSmap()operator
TheResponseinstanceexposesajson()method,whichconvertsthereturnedserializedpayloadstringintoitscorrespondingin-memoryobjectrepresentation.Youwouldliketobeabletopassaregularobjecttotheobserverhandler,sotheidealtoolhereisawedgemethodthatstillgivesyouanObservableintheend:
console.log(http_.get('static/article.json')
.map(response=>response.json()));
//Observable{source:Observable,operator:MapOperator,...}
RecallthatthecanonicalformofObservablesisastreamofevents.Inthiscase,weknowtherewillonlyeverbeoneevent,whichistheHTTPresponse.Nonetheless,allthenormaloperatorsthatwouldbeusedonastreamofeventscanjustaseasilybeusedonthissingle-eventObservable.
InthesamewaythatArray.map()canbeusedtotransformeachinstanceinthearray,Observable.map()allowsyoutotransformeacheventemittedfromObservable.Morespecifically,itcreatesanotherObservablethatemitsthemodifiedeventpassedfromtheinitialobservable.
Subscribe
Observableinstancesexposeasubscribe()methodthatacceptsanonNexthandler,anonErrorhandler,andanonCompletedhandlerasarguments.ThesehandlerscorrespondtotheeventsinthelifecycleoftheObservablewhenitemitsResponseinstances.TheparameterfortheonNextmethodiswhateverisemittedfromtheObservable.Inthiscase,theemitteddatais
![Page 317: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/317.jpg)
thereturnedvaluefrommap(),soitwillbetheparsedobjectthathasreturnedafterinvokingjson()ontheResponseinstance.
Allthesemethodsareoptional,butinthisexample,theonNextandonErrormethodsareuseful.
Note
Together,thesemethodswhenprovidedtosubscribe()constitutewhatisidentifiedastheobserver.
http_.get('static/article.json')
.map(respose=>respose.json())
.subscribe(
article=>{
this.title=article.title;
this.body=article.body;
},
error=>console.error(error));
Withallofthistogether,thebrowserwillfetchtheJSONandparseit,andthesubscriberwillpassitsdatatotherespectivecomponentmembers.
![Page 318: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/318.jpg)
There'smore...Whenconstructingthisrecipepiecebypiece,ifyouarewatchingyourbrowser'snetworkrequestsasyouassembleit,youwillnoticethattheactualGETrequestisnotfireduntilthesubscribe()methodisinvoked.ThisisbecausethetypeObservableyouareusingis"cold".
HotandcoldObservables
The"cold"designationmeansthattheObservabledoesnotbegintoemituntilanobserverbeginstosubscribetoit.Thisisdifferentfroma"hot"Observable,whichwillemititemseveniftherearenoobserverssubscribedtoit.Sincethismeansthateventsthatoccurbeforeanobserverisattachedarelost,HTTPObservablesdemandacolddesignation.
TheonNextmethodistermed"emission"sincethereisassociateddatathatisbeingemitted.TheonCompletedandonErrormethodsaretermed"notifications,"astheyrepresentsomethingofsignificance,buttheydonothaveanassociatedeventthatwouldbeconsideredpartofthestream.
![Page 319: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/319.jpg)
SeealsoImplementingaPublish-SubscribemodelusingSubjectsshowsyouhowtoconfigureinputandoutputforRxJSObservablesBuildingafullyfeaturedAutoCompletewithObservablesgivesyouabroadtourofsomeoftheutilitiesofferedtoyouaspartoftheRxJSlibrary
![Page 320: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/320.jpg)
ImplementingaPublish-SubscribemodelusingSubjectsAngular2willoftenprovideyouwithanObservableinterfacetoattachtoforfree,butitisimportanttoknowhowtheyarecreated,configured,andused.Morespecifically,itisvaluableforyoutoknowhowtotakeObservablesandapplythemtorealscenariosthatwillbeencounteredintheclient.
Note
Thecode,links,andaliveexampleofthisareavailableathttp://ngcookbook.herokuapp.com/4839/.
![Page 321: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/321.jpg)
GettingreadySupposeyoustartedwiththefollowingskeletonapplication:
[app/click-observer.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'click-observer',
template:`
<button>
Emitevent!
</button>
<p*ngFor="letclickofclicks;leti=index">
{{i}}:{{click}}
</p>
`
})
exportclassClickObserverComponent{
clicks:Array<Event>=[];
}
Yourgoalistoconvertthissothatallthebuttonclickeventsareloggedintotherepeatedfield.
![Page 322: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/322.jpg)
Howtodoit...Accomplishingthiswithacomponentmembermethodandusingitintheclickeventbindinginthetemplateispossible,butthisdoesn'tcapturetherealvalueofObservables.YouwanttobeabletoexposeanObservableonClickObserverComponent.Thiswillallowanyotherpartofyourapplicationtosubscribetotheseclickeventsandhandletheminitsownway.
Instead,youwouldliketobeabletofunneltheclickeventsfromthebuttonintotheObservable.WitharegularObservableinstance,thisisn'tpossiblesinceitisonlyactingastheSubscribepartofthePublish-Subscribemodel.ToaccomplishthePublishaspect,youmustuseaSubjectinstance:
[app/click-observer.component.ts]
import{Component}from'@angular/core';
import{Subject}from'rxjs/Subject';
@Component({
selector:'click-observer',
template:`
<button>
Emitevent!
</button>
<p*ngFor="letclickofclicks;leti=index">
{{i}}:{{click}}
</p>
`
})
exportclassClickObserverComponent{
clickEmitter:Subject<Event>=newSubject();
clicks:Array<Event>=[];
}
ReactiveXSubjectsactasboththeObservableandtheObserver.Therefore,itexposesboththesubscribe()method,usedfortheSubscribebehavior,andthenext()method,usedforthePublishbehavior.
Note
Inthisexample,thenext()methodisusefulbecauseyouwanttoexplicitlyspecifywhenanemissionshouldoccurandwhatthatemissionshouldcontain.TherearelotsofwaysofinstantiatingObservablesinordertoimplicitlygenerateemissions,suchas(butcertainlynotlimitedto)Observable.range().Inthesecases,Observableunderstandshowitsinputbehaves,andthusitdoesnotneeddirectionastowhenemissionsoccurandwhattheyshouldcontain.
Inthiscase,youcanpasstheeventdirectlytonext()inthetemplateclickhandlerdefinition.
![Page 323: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/323.jpg)
Withthis,allthatisleftistopopulatethearraybydirectingtheemissionsintoit:
[app/click-observer.component.ts]
import{Component}from'@angular/core';
import{Subject}from'rxjs/Subject';
@Component({
selector:'click-observer',
template:`
<button(click)="clickEmitter.next($event)">
Emitevent!
</button>
<p*ngFor="letclickofclicks;leti=index">
{{i}}:{{click}}
</p>
`
})
exportclassClickObserverComponent{
clickEmitter:Subject<Event>=newSubject();
clicks:Array<Event>=[];
constructor(){
this.clickEmitter
.subscribe(clickEvent=>this.clicks.push(clickEvent));
}
}
That'sall!Withthis,youshouldseeclickeventspopulateinthebrowserwitheachsuccessivebuttonclick.
![Page 324: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/324.jpg)
Howitworks...ReactiveXObservablesandObserversaredistinct,buttheirbehaviorismutuallycompatibleinsuchawaythattheirunion,Subject,canactaseitheroneofthem.Inthisexample,theSubjectisusedastheinterfacetofeedinEventobjectsasthePublishmodalityaswellastohandletheresultthatwouldcomeoutastheSubscribemodality.
![Page 325: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/325.jpg)
There'smore...Thewaythisisconstructedmightfeelabitstrangetoyou.ThecomponentisexposingtheSubjectinstanceasthepointwhereyourapplicationwillattachobserverhandlers.
However,youwanttopreventotherpartsoftheapplicationfromaddingadditionalevents,whichisstillpossibleshouldtheychoosetousethenext()method.What'smore,theSubjectinstanceisreferenceddirectlyinsidethetemplateandexposingittheremayfeelabitodd.Therefore,itisdesirable,andcertainlygoodsoftwarepractice,toonlyexposetheObservablecomponentoftheSubject.
Todothis,youmustimporttheObservablemoduleandutilizetheSubjectinstance'smembermethod,namelyasObservable().ThismethodwillcreateanewObservableinstancethatwilleffectivelypipetheobservedemissionsfromtheSubjectintothenewObservable,whichwillbeexposedasapubliccomponentmember:
[app/article.component.ts]
import{Component}from'@angular/core';
import{Observable}from'rxjs/Observable';
import{Subject}from'rxjs/Subject';
@Component({
selector:'click-observer',
template:`
<button(click)="publish($event)">
Emitevent!
</button>
<p*ngFor="letclickofclicks;leti=index">
{{i}}:{{click}}
</p>
`
})
exportclassClickObserverComponent{
clickEmitter:Observable<Event>;
privateclickSubject_:Subject<Event>=newSubject();
clicks:Array<Event>=[];
constructor(){
this.clickEmitter=this.clickSubject_.asObservable();
this.clickEmitter.subscribe(clickEvent=>
this.clicks.push(clickEvent));
}
publish(e:Event):void{
this.clickSubject_.next(e);
}
}
![Page 326: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/326.jpg)
NoweventhoughonlythiscomponentisreferencingclickEmitter,everycomponentthatusesclickEmitterwillnotneedorbeabletotouchthesource,Subject.
NativeRxJSimplementation
Thishasallbeenagreatexample,butthisissuchacommonpatterninthattheRxJSlibraryalreadyprovidesabuilt-inwayofimplementingit.TheObservableclassexposesastaticmethodfromEvent(),whichtakesinanelementthatisexpectedtogenerateeventsandtheeventtypetolistento.
However,youneedareferencetotheactualelement,whichyoucurrentlydonothave.Forthepresentimplementation,theAngular2ViewChildfacultieswillgiveyouaverynicereferencetothebutton,whichwillthenbepassedtothefromEvent()methodoncethetemplatehasbeenrendered:
[app/click-observer.component.ts]
import{Component,ViewChild,ngAfterViewInit}
from'@angular/core';
import{Observable}from'rxjs/Observable';
import'rxjs/add/observable/fromEvent';
@Component({
selector:'click-observer',
template:`
<button#btn>
Emitevent!
</button>
<p*ngFor="letclickofclicks;leti=index">
{{i}}:{{click}}
</p>
`
})
exportclassClickObserverComponentimplementsAfterViewInit{
@ViewChild('btn')btn;
clickEmitter:Observable<Event>;
clicks:Array<Event>=[];
ngAfterViewInit(){
this.clickEmitter=Observable.fromEvent(
this.btn.nativeElement,'click');
this.clickEmitter.subscribe(clickEvent=>
this.clicks.push(clickEvent));
}
}
Withallofthis,thecomponentshouldstillbehaveidentically.
![Page 327: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/327.jpg)
SeealsoBasicutilizationofObservableswithHTTPdemonstratesthebasicsofhowtouseanobservableinterfaceCreatinganObservableauthenticationserviceusingBehaviorSubjectsinstructsyouonhowtoreactivelymanagethestateinyourapplicationBuildingafullyfeaturedAutoCompletewithObservablesgivesyouabroadtourofsomeoftheutilitiesofferedtoyouaspartoftheRxJSlibrary
![Page 328: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/328.jpg)
CreatinganObservableauthenticationserviceusingBehaviorSubjectsOneofthemostobviousandusefulcasesoftheObserverPatternistheoneinwhichasingleentityinyourapplicationunidirectionallycommunicatesinformationtoafieldoflistenersontheoutside.Theselistenerswouldliketobeabletoattachanddetachfreelyfromthesinglebroadcastingentity.Agoodinitialexampleofthisisthelogin/logoutcomponent.
Note
Thecode,links,andaliveexampleofthisareavailableathttp://ngcookbook.herokuapp.com/6957/.
![Page 329: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/329.jpg)
GettingreadySupposeyouhavethefollowingskeletonapplication:
[app/login.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'login',
template:`
<button*ngIf="!loggedIn"
(click)="loggedIn=true">
Login
</button>
<button*ngIf="loggedIn"
(click)="loggedIn=false">
Logout
</button>
`
})
exportclassLoginComponent{
loggedIn:boolean=false;
}
Asitpresentlyexists,thiscomponentwillallowyoutotogglebetweenthelogin/logoutbutton,butthereisnoconceptofsharedapplicationstate,andothercomponentscannotutilizetheloginstatethatthiscomponentwouldtrack.
YouwouldliketointroducethisstatetoasharedservicethatisoperatedusingtheObserverPattern.
![Page 330: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/330.jpg)
Howtodoit...Beginbycreatinganemptyserviceandinjectingitintothiscomponent:
[app/authentication.service.ts]
import{Injectable}from'@angular/core';
@Injectable()
exportclassAuthService{
privateauthState_:AuthState;
}
exportconstenumAuthState{
LoggedIn,
LoggedOut
}
NoticethatyouareusingaTypeScriptconstenumtokeeptrackoftheuser'sauthenticationstate.
Note
Ifyou'renewtoES6andTypeScript,thesekeywordsmayfeelabitbizarretoyou.TheconstkeywordisfromtheES6specification,signifyingthatthisvalueisreadonlyoncedeclared.InvanillaES6,thiswillthrowanerror,usuallySyntaxError,atruntime.WithTypeScriptcompilationthough,constwillbecaughtatcompiletime.
TheenumkeywordisanofferingofTypeScript.Itisnotdissimilartoaregularobjectliteral,butnotethattheenummembersdonothavevalues.
Throughouttheapplication,youwillreferencetheseviaAuthState.LoggedInandAuthState.LoggedOut.IfyoureferencethecompiledJavaScriptthatTypeScriptgenerates,youwillseethattheseareactuallyassignedintegervalues.Butforthepurposesofbuildinglargeapplications,thisallowsustodevelopacentralizedrepositoryofpossibleAuthStatevalueswithoutworryingabouttheiractualvalues.
Injectingtheauthenticationservice
Astheskeletonservicecurrentlyexists,youaregoingtoinstantiateaSubjectthatwillemitAuthState,butthereisnowayavailablecurrentlytointeractwithit.Youwillsetthisupinabit.First,youmustinjectthisserviceintoyourcomponent:
[app/app.module.ts]
import{NgModule}from'@angular/core';
import{BrowserModule}from'@angular/platform-browser';
import{LoginComponent}from'./login.component';
import{AuthService}from'./authentication.service';
![Page 331: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/331.jpg)
@NgModule({
imports:[
BrowserModule
],
declarations:[
LoginComponent
],
providers:[
AuthService
],
bootstrap:[
LoginComponent
]
})
exportclassAppModule{}
Thisisallwellandgood,buttheserviceisstillunusableasis.
Tip
NotethatthepathyouimportyourAuthServicefrommayvarydependingonwhereitliesinyourfiletree.
AddingBehaviorSubjecttotheauthenticationservice
Thecoreofthisserviceistomaintainaglobalapplicationstate.Itshouldexposeitselftotherestoftheapplicationbylettingotherpartssaytotheservice,"Letmeknowwheneverthestatechanges.Also,I'dliketoknowwhatthestateisrightnow."TheperfecttoolforthistaskisBehaviorSubject.
Note
RxJSSubjectsalsohaveseveralsubclasses,andBehaviorSubjectisoneofthem.Fundamentally,itfollowsalltherhythmsofSubjects,butthemaindifferenceisthatitwillemititscurrentstatetoanyobserverthatbeginstolistentoit,asifthateventisentirelynew.Incaseslikethis,whereyouwanttokeeptrackofthestate,thisisextremelyuseful.
AddaprivateBehaviorSubject(initializedtotheLoggedOutstate)andapublicObservabletoAuthService:
[app/authentication.service.ts]
import{Injectable}from'@angular/core';
import{BehaviorSubject}from'rxjs/BehaviorSubject';
import{Observable}from'rxjs/Observable';
@Injectable()
exportclassAuthService{
privateauthManager_:BehaviorSubject<AuthState>
![Page 332: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/332.jpg)
=newBehaviorSubject(AuthState.LoggedOut);
privateauthState_:AuthState;
authChange:Observable<AuthState>;
constructor(){
this.authChange=this.authManager_.asObservable();
}
}
exportconstenumAuthState{
LoggedIn,
LoggedOut
}
AddingAPImethodstotheauthenticationservice
RecallthatyoudonotwanttoexposetheBehaviorSubjectinstancetooutsideactors.Instead,youwouldliketoofferonlyitsObservablecomponent,whichyoucanopenlysubscribeto.Furthermore,youwouldliketoallowoutsideactorstosettheauthenticationstate,butonlyindirectly.Thiscanbeaccomplishedwiththefollowingmethods:
[app/authentication.service.ts]
import{Injectable}from'@angular/core';
import{BehaviorSubject}from'rxjs/BehaviorSubject';
import{Observable}from'rxjs/Observable';
@Injectable()
exportclassAuthService{
privateauthManager_:BehaviorSubject<AuthState>
=newBehaviorSubject(AuthState.LoggedOut);
privateauthState_:AuthState;
authChange:Observable<AuthState>;
constructor(){
this.authChange=this.authManager_.asObservable();
}
login():void{
this.setAuthState_(AuthState.LoggedIn);
}
logout():void{
this.setAuthState_(AuthState.LoggedOut);
}
emitAuthState():void{
this.authManager_.next(this.authState_);
}
privatesetAuthState_(newAuthState:AuthState):void{
this.authState_=newAuthState;
this.emitAuthState();
}
}
exportconstenumAuthState{
![Page 333: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/333.jpg)
LoggedIn,
LoggedOut
}
Outstanding!Withallofthis,outsideactorswillbeabletosubscribetoauthChangeObservableandwillindirectlycontrolthestatevialogin()andlogout().
Tip
NotethattheObservablecomponentofBehaviorSubjectisnamedauthChange.NamingthedifferentcomponentsoftheelementsintheObserverPatterncanbetricky.ThisnamingconventionwasselectedtorepresentwhataneventemittedfromtheObservableactuallymeant.Quiteliterally,authChangeistheanswertothequestion,"WhateventamIobserving?".Therefore,itmakesgoodsemanticsensethatyourcomponentsubscribestoauthChangeswhentheauthenticationstatechanges.
Wiringtheservicemethodsintothecomponent
LoginComponentdoesnotyetutilizetheservice,soaddinitsnewlycreatedmethods:
[app/login.component.ts]
import{Component}from'@angular/core';
import{AuthService,AuthState}from'./authentication.service';
@Component({
selector:'login',
template:`
<button*ngIf="!loggedIn"
(click)="login()">
Login
</button>
<button*ngIf="loggedIn"
(click)="logout()">
Logout
</button>
`
})
exportclassLoginComponent{
loggedIn:boolean;
constructor(privateauthService_:AuthService){
authService_.authChange.subscribe(
newAuthState=>
this.loggedIn=(newAuthState===AuthState.LoggedIn));
}
login():void{
this.authService_.login();
}
![Page 334: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/334.jpg)
logout():void{
this.authService_.logout();
}
}
Withallofthisinplace,youshouldbeabletoseeyourlogin/logoutbuttonsfunctionwell.ThismeansyouhavecorrectlyincorporatedObservableintoyourcomponent.
Tip
Thisrecipeisagoodexampleofconventionsyou'rerequiredtomaintainwhenusingpublic/private.Notethattheinjectedserviceisdeclaredasaprivatememberandwrappedwithpubliccomponentmembermethods.Anythingthatanotherpartoftheapplicationcallsoranythingthatisusedinsidethetemplateshouldbeapublicmember.
![Page 335: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/335.jpg)
Howitworks...CentraltothisimplementationisthateachcomponentthatislisteningtoObservablehasanidempotenthandlingofeventsthatareemitted.EachtimeanewcomponentisconnectedtoObservable,itinstructstheservicetoemitwhateverthecurrentstateis,usingemitAuthState().Necessarily,allcomponentsdon'tbehaveanydifferentlyiftheyseethesamestateemittedmultipletimesinarow;theywillonlyaltertheirbehavioriftheyseeachangeinthestate.
Noticehowyouhavetotallyencapsulatedtheauthenticationstateinsidetheauthenticationservice,andatthesametime,haveexposedandutilizedareactiveAPIfortheentireapplicationtobuildupon.
![Page 336: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/336.jpg)
There'smore...Twocriticalcomponentsofhookingintoservicessuchasthesearethesetupandteardownprocesses.AfastidiousdeveloperwillhavenoticedthatevenifaninstanceofLoginComponentisdestroyed,thesubscriptiontoObservablewillstillpersist.This,ofcourse,isextremelyundesirable!
Fortunately,thesubscribe()methodofObservablesreturnsaninstanceofSubscription,whichexposesanunsubscribe()method.Youcanthereforecapturethisinstanceupontheinvocationofsubscribe()andtheninvokeitwhenthecomponentisbeingtorndown.
SimilartolistenerteardowninAngular1,youmustinvoketheunsubscribemethodwhenthecomponentinstanceisbeingdestroyed.Happily,theAngular2lifecycleprovidesyouwithsuchamethod,ngOnDestroy,inwhichyoucaninvokeunsubscribe():
[app/login.component.ts]
import{Component,ngOnDestroy}from'@angular/core';
import{AuthService,AuthState}from'./authentication.service';
import{Subscription}from'rxjs/Subscription';
@Component({
selector:'login',
template:`
<button*ngIf="!loggedIn"
(click)="login()">
Login
</button>
<button*ngIf="loggedIn"
(click)="logout()">
Logout
</button>
`
})
exportclassLoginComponentimplementsOnDestroy{
loggedIn:boolean;
privateauthChangeSubscription_:Subscription;
constructor(privateauthService_:AuthService){
this.authChangeSubscription_=
authService_.authChange.subscribe(
newAuthState=>
this.loggedIn=(newAuthState===AuthState.LoggedIn));
}
login():void{
this.authService_.login();
}
logout():void{
![Page 337: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/337.jpg)
this.authService_.logout();
}
ngOnDestroy(){
this.authChangeSubscription_.unsubscribe();
}
}
Nowyourapplicationissafefrommemoryleaksshouldanyinstanceofthiscomponenteverbedestroyedinthelifetimeofyourapplication.
![Page 338: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/338.jpg)
SeealsoBasicutilizationofObservableswithHTTPdemonstratesthebasicsofhowtouseanobservableinterfaceImplementingaPublish-SubscribemodelusingSubjectsshowsyouhowtoconfigureinputandoutputforRxJSObservablesBuildingageneralizedPublish-Subscribeservicetoreplace$broadcast,$emit,and$onassemblesarobustPubSubmodelforconnectingapplicationcomponentswithchannelsBuildingafullyfeaturedAutoCompletewithObservablesgivesyouabroadtourofsomeoftheutilitiesofferedtoyouaspartoftheRxJSlibrary
![Page 339: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/339.jpg)
BuildingageneralizedPublish-Subscribeservicetoreplace$broadcast,$emit,and$onInAngular1,the$emitand$broadcastbehaviorswereindeedveryusefultools.Theygaveyoutheabilitytosendcustomeventsupwardsanddownwardsthroughthescopetreetoanylistenersthatmightbewaitingforsuchanevent.Thispushedthedevelopertowardsaveryusefulpattern:theabilityformanycomponentstobeabletotransmiteventstoandfromacentralsource.However,using$emitand$broadcastforsuchapurposewasgrosslyinappropriate;theyhadtheeffectoffeedingtheeventthroughhugenumbersofscopesonlytoreachthesingleintendedtarget.
Note
Inthepreviouseditionofthisbook,thecorrespondingrecipedemonstratedhowtobuildaPublish-Subscribeservicethatusedthe$emitand$rootScopeinjection.Theversioninthisrecipe,althoughdifferentinahandfulofways,achievessimilarresultsinasubstantiallycleanerandmoreelegantfashion.
Itispreferabletocreateasingleentitythatcanserveasagenericthroughwayforeventstopassfrompublisherstotheirsubscribers.
Note
Thecode,links,andaliveexampleofthisareavailableathttp://ngcookbook.herokuapp.com/2417/.
![Page 340: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/340.jpg)
GettingreadyBeginwithaskeletonserviceinjectedintoacomponent:
[app/node.component.ts]
import{Component,Input}from'@angular/core';
import{PubSubService}from'./publish-subscribe.service';
@Component({
selector:'node',
template:`
<p>Heard{{count}}of{{subscribeChannel}}</p>
<button(click)="send()">Send{{publishChannel}}</button>
`
})
exportclassNodeComponent{
@Input()publishChannel:string;
@Input()subscribeChannel:string;
count:number=0;
constructor(privatepubSubService_:PubSubService){}
send(){}
}
[app/publish-subscribe.service.ts]
import{Injectable}from'@angular/core';
@Injectable()
exportclassPubSubService{
constructor(){}
publish(){}
subscribe(){}
}
![Page 341: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/341.jpg)
Howtodoit...Thegroundworkforthisimplementationshouldbeprettyobvious.TheserviceisgoingtohostasingleSubjectinstancethatisgoingtofunneleventsofanytypeintotheserviceandoutthroughtheobserversoftheSubject.
First,implementthefollowingsothatsubscribe()andpublish()actuallydoworkwhenyouinvolvetheSubjectinstance:
[app/publish-subscribe.service.ts]
import{Injectable}from'@angular/core';
import{Subject}from'rxjs/Subject';
import{Observable}from'rxjs/Observable';
import{Observer}from'rxjs/Observer';
import{Subscriber}from'rxjs/Subscriber;
@Injectable()
exportclassPubSubService{
privatepublishSubscribeSubject_:Subject<any>=newSubject();
emitter_:Observable<any>;
constructor(){
this.emitter_=this.publishSubscribeSubject_.asObservable();
}
publish(event:any):void{
this.publishSubscribeSubject_.next(event);
}
subscribe(handler:NextObserver<any>):Subscriber{
returnthis.emitter_.subscribe(handler);
}
}
Thisisterrificforaninitialimplementation,butyieldsaproblem:everyeventpublishedtothisservicewillbebroadcastedtoallthesubscribers.
Introducingchannelabstraction
Itispossibleandinfactquiteeasytorestrictpublishandsubscribeinsuchawaythattheywillonlypayattentiontothechanneltheyspecify.First,modifypublish()tonesttheeventinsidetheemittedobject:
[app/publish-subscribe.service.ts]
import{Injectable}from'@angular/core';
import{Subject}from'rxjs/Subject';
import{Observable}from'rxjs/Observable';
import{Observer}from'rxjs/Observer';
![Page 342: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/342.jpg)
import{Subscriber}from'rxjs/Subscriber;
@Injectable()
exportclassPubSubService{
privatepublishSubscribeSubject_:Subject<any>=newSubject();
emitter_:Observable<any>;
constructor(){
this.emitter_=this.publishSubscribeSubject_.asObservable();
}
publish(channel:string,event:any):void{
this.publishSubscribeSubject_.next({
channel:channel,
event:event
});
}
subscribe(handler:NextObserver<any>):Subscriber{
returnthis.emitter_.subscribe(handler);
}
}
Withthis,youarenowabletoutilizesomeObservablebehaviortorestrictwhicheventsthesubscriptionispayingattentionto.
Observableemissionscanhavefilter()andmap()appliedtothem.filter()willreturnanewObservableinstancethatonlyemitswhicheveremissionsevaluateastrueinitsfilterfunction.map()returnsanewObservableinstancethattransformsallemissionsintoanewvalue.
[app/publish-subscribe.service.ts]
import{Injectable}from'@angular/core';
import{Subject}from'rxjs/Subject';
import{Observable}from'rxjs/Observable';
import{Observer}from'rxjs/Observer';
import{Subscriber}from'rxjs/Subscriber;
import'rxjs/add/operator/filter';
import'rxjs/add/operator/map';
@Injectable()
exportclassPubSubService{
privatepublishSubscribeSubject_:Subject<any>=newSubject();
emitter_:Observable<any>;
constructor(){
this.emitter_=this.publishSubscribeSubject_.asObservable();
}
publish(channel:string,event:any):void{
this.publishSubscribeSubject_.next({
channel:channel,
![Page 343: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/343.jpg)
event:event
});
}
subscribe(channel:string,handler:((value:any)=>void)):Subscriber{
returnthis.emitter_
.filter(emission=>emission.channel===channel)
.map(emission=>emission.event)
.subscribe(handler);
}
}
Hookingcomponentsintotheservice
Theserviceiscomplete,butthecomponentdoesn'tyethavetheabilitytouseit.Usetheinjectedservicetolinkthecomponenttothechannelsspecifiedbyitsinputstrings:
[app/node.component.ts]
import{Component,Input}from'@angular/core';
import{PubSubService}from'./publish-subscribe.service';
@Component({
selector:'node',
template:`
<p>Heard{{count}}of{{subscribeChannel}}</p>
<button(click)="send()">Send{{publishChannel}}</button>
`
})
exportclassNodeComponent{
@Input()publishChannel:string;
@Input()subscribeChannel:string;
count:number=0;
constructor(privatepubSubService_:PubSubService){}
send(){
this.pubSubService_
.publish(this.publishChannel,{});
}
ngAfterViewInit(){
this.pubSubService_
.subscribe(this.subscribeChannel,
event=>++this.count);
}
}
Tip
Thepublish()methodhasanemptyobjectliteralasitssecondargument.Thisisthepayloadforthepublishedmessage,whichisn'tusedinthisrecipe.Ifyouwanttosenddataalongwitha
![Page 344: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/344.jpg)
message,thisiswhereitwouldgo.
Withallofthis,testyourapplicationwiththefollowing:
[app/root.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'root',
template:`
<nodesubscribeChannel="foo"
publishChannel="bar">
</node>
<nodesubscribeChannel="bar"
publishChannel="foo">
</node>
`
})
exportclassRootComponent{}
Youwillseethatchannelpublishingandsubscribingishappeningasyouwouldexpect.
Unsubscribingfromchannels
Ofcourse,youwanttoavoidmemoryleakswhereverpossible.Thisrequiresthatyouexplicitlycompletethecleanupprocesswhenyourcomponentinstanceisdestroyed:
[app/node.component.ts]
import{Component,Input,OnDestroy}from'@angular/core';
import{PubSubService}from'./publish-subscribe.service';
import{Subscription}from'rxjs/Subscription';
@Component({
selector:'node',
template:`
<p>Heard{{count}}of{{subscribeChannel}}</p>
<button(click)="send()">Send{{publishChannel}}</button>
`
})
exportclassNodeComponentimplementsOnDestroy{
@Input()publishChannel:string;
@Input()subscribeChannel:string;
count:number=0;
privatepubSubServiceSubscription_:Subscription;
constructor(privatepubSubService_:PubSubService){}
send(){
this.pubSubService_
.publish(this.publishChannel,{});
![Page 345: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/345.jpg)
}
ngAfterViewInit(){
this.pubSubService_
.subscribe(this.subscribeChannel,
event=>++this.count);
}
ngOnDestroy(){
this.pubSubServiceSubscription_.unsubscribe();
}
}
![Page 346: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/346.jpg)
Howitworks...Eachtimepublish()isinvoked,theprovidedeventiswrappedbytheprovidedchannelandsubmittedtoacentralSubject,whichisprivateinsidetheservice.Atthesametime,thefactthateachinvocationofsubscribe()wantstolistentoadifferentchannelpresentsaproblem.ThisisbecauseanObservabledoesnotdrawdistinctionsregardingwhatisbeingemittedwithoutexplicitdirection.
Youareabletoutilizethefilter()andmap()operatorstoestablishacustomizedviewoftheemissionsofSubjectandusethisviewintheapplicationoftheObserverhandler.Eachtimesubscribe()isinvoked,itcreatesanewObservableinstance;however,theseareallmerelypointsofindirectionfromtheonetrueObservable,whichisownedbytheprivateinstancehiddeninsidetheservice.
![Page 347: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/347.jpg)
There'smore...It'simportanttounderstandwhythisserviceisnotbuiltinadifferentway.
AnimportantfeatureofObservablesistheirabilitytobecomposed.Thatis,severalObservableinstancesindependentlyemittingeventscanbecombinedintooneObservableinstance,whichwillemitalltheeventsfromacombinedsource.Thiscanbeaccomplishedinseveraldifferentways,includingflatMap()ormerge().ThisabilityiswhatisbeingreferredtowhenReactiveXObservablesaredescribedas"composable."
Therefore,adevelopermightseethiscompositionabilityandthinkitwouldbesuitableforaPublish-Subscribeentity.TheentitywouldacceptObservableinstancesfromthepublishers.TheywouldbecombinedtocreateasingleObservableinstance,andsubscriberswouldattachObservabletothiscombination.Whatcouldpossiblygowrong?
ConsiderationsofanObservable'scompositionandmanipulation
OneprimaryconcernisthatthecomposedObservablethatthesubscribersarebeingattachedtowillchangeconstantly.Asisthecasewithmap()andfilter(),anymodulationperformedonanObservableinstance,includingcomposition,willreturnanewObservableinstance.ThisnewinstancewouldbecometheObservablethatsubscriberswouldattachto,andthereinliestheproblem.
Let'sexaminethisproblemstepbystep:
1. PubSubserviceemitseventsfromObservableA.2. NodeXsubscribestotheserviceandreceiveseventsfromObservableA.3. SomeotherpartoftheapplicationaddsObservableBtothePubSubservice.4. ThePubSubservicecomposesObservableAandObservableBintoObservableAB.5. NodeYsubscribestotheserviceandreceiveseventsfromObservableAB.
Notethatinthiscase,NodeXwouldstillreceiveeventsfromonlyObservableAsincethatistheObservableinstancewhereitinvokedsubscribe().
Certainly,therearestepsthatcanbetakentomitigatethisproblem,suchashavinganadditionallevelofindirectionbetweenthesubscribeObservableandthecomposedObservable.However,awiseengineerwillstepbackatthispointandtakestockofthesituation.Publish-Subscribeissupposedtobearelatively"dumb"protocol,meaningthatitshouldn'tbedelegatedtoomuchresponsibilityaroundmanagingtheeventsithasbeenpassedwith—messagesinandmessagesout,withnorealconcernforwhatiscontainedaslongastheygetthere.OnecouldmakeaverystrongargumentthatintroducingObservablesinthePublishsidegreatlyovercomplicatesthings.
![Page 348: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/348.jpg)
Inthecaseofthisrecipe,youhavedevelopedanelegantandsimpleversionofaPublish-Subscribemodule,anditfeelsrighttodelegatecomplexityoutsideofit.InthecaseofentitieswantingtousePublishwithObservables,asolutionmightbetojustpipetheObservableemissionsintotheservice'spublish()method.
![Page 349: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/349.jpg)
SeealsoBasicutilizationofObservableswithHTTPdemonstratesthebasicsofhowtouseanobservableinterfaceImplementingaPublish-SubscribemodelusingSubjectsshowsyouhowtoconfigureinputandoutputforRxJSObservablesCreatinganObservableauthenticationserviceusingBehaviorSubjectsinstructsyouonhowtoreactivelymanagethestateinyourapplicationBuildingafullyfeaturedAutoCompletewithObservablesgivesyouabroadtourofsomeoftheutilitiesofferedtoyouaspartoftheRxJSlibrary
![Page 350: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/350.jpg)
UsingQueryListsandObservablestofollowchangesinViewChildrenOneveryusefulpieceofbehaviorincomponentsistheabilitytotrackchangestothecollectionsofchildrenintheview.Inmanyways,thisisquiteanebuloussubject,asthenumberofwaysinwhichviewcollectionscanbealteredisnumerousandsubtle.Thankfully,Angular2providesasolidfoundationfortrackingthesechanges.
Note
Thecode,links,andaliveexampleofthisareavailableathttp://ngcookbook.herokuapp.com/4112/.
![Page 351: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/351.jpg)
GettingreadySupposeyoubeginwiththefollowingskeletonapplication:
[app/inner.component.ts]
import{Component,Input}from'@angular/core';
@Component({
selector:'inner',
template:`<p>{{val}}`
})
exportclassInnerComponent{
@Input()val:number;
}
[app/outer.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'outer',
template:`
<button(click)="add()">Moar</button>
<button(click)="remove()">Less</button>
<button(click)="shuffle()">Shuffle</button>
<inner*ngFor="letioflist"
val="{{i}}">
</inner>
`
})
exportclassOuterComponent{
list:Array<number>=[];
add():void{
this.list.push(this.list.length)
}
remove():void{
this.list.pop();
}
shuffle():void{
//simpleassignmentshuffle
this.list=this.list.sort(()=>(4*Math.random()>2)?1:-1);
}
}
Asis,thisisaverysimplelistmanagerthatgivesyoutheabilitytoadd,remove,andshufflealistinterpolatedasInnerComponentinstances.Youwanttheabilitytotrackwhenthislistundergoeschangesandkeepreferencestothecomponentinstancesthatcorrespondtotheviewcollection.
![Page 352: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/352.jpg)
Howtodoit...BeginbyusingViewChildrentocollecttheInnerComponentinstancesintoasingleQueryList:
[app/outer.component.ts]
import{Component,ViewChildren,QueryList}from'@angular/core';
import{InnerComponent}from'./inner.component';
@Component({
selector:'outer',
template:`
<button(click)="add()">Moar</button>
<button(click)="remove()">Less</button>
<button(click)="shuffle()">Shuffle</button>
<inner*ngFor="letioflist"
val="{{i}}">
</inner>
`
})
exportclassOuterComponent{
@ViewChildren(InnerComponent)innerComponents:
QueryList<InnerComponent>;
list:Array<number>=[];
add():void{
this.list.push(this.list.length)
}
remove():void{
this.list.pop();
}
shuffle():void{
//simpleassignmentshuffle
this.list=this.list.sort(()=>(4*Math.random()>2)?1:-1);
}
}
Easy!Now,oncetheviewofOuterComponentisinitialized,youwillbeabletousethis.innerComponentstoreferenceQueryList.
DealingwithQueryLists
QueryListsarestrangebirdsinAngular2,butlikemanyotherfacetsoftheframework,theyarejustaconventionthatyouwillhavetolearn.Inthiscase,theyareanimmutableanditerablecollectionthatexposesahandfulofmethodstoinspectwhattheycontainandwhenthesecontentsarealtered.
Inthiscase,thetwoinstancepropertiesyoucareaboutarelastandchanges.last,asyoumight
![Page 353: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/353.jpg)
expect,willreturnthelastinstanceofQueryList—inthiscase,aninstanceofInnerComponentifQueryListisnotempty.changeswillreturnanObservablethatwillemitQueryListwheneverachangeoccursinsideit.InthecaseofacollectionofInnerComponentinstances,theaddition,removal,andshufflingoptionswillallberegisteredaschanges.
Usingtheseproperties,youcanveryeasilysetupOuterComponenttokeeptrackofwhatthevalueofthelastInnerComponentinstanceis:
import{Component,ViewChildren,QueryList}from'@angular/core';
import{InnerComponent}from'./inner.component';
@Component({
selector:'app-outer',
template:`
<button(click)="add()">Moar</button>
<button(click)="remove()">Less</button>
<button(click)="shuffle()">Shuffle</button>
<app-inner*ngFor="letioflist"
val="{{i}}">
</app-inner>
<p>Valueoflast:{{lastVal}}</p>
`
})
exportclassOuterComponent{
@ViewChildren(InnerComponent)innerComponents:
QueryList<InnerComponent>;
list:Array<number>=[];
lastVal:number;
constructor(){}
add(){
this.list.push(this.list.length)
}
remove(){
this.list.pop();
}
shuffle(){
this.list=this.list.sort(()=>(4*Math.random()>2)?1:-1);
}
ngAfterViewInit(){
this.innerComponents.changes
.subscribe(e=>this.lastVal=(e.last||{}).val);
}
}
Withallofthis,youshouldbeabletofindthatlastValwillstayuptodatewithanychangesyouwouldtriggerintheInnerComponentcollection.
![Page 354: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/354.jpg)
Correctingtheexpressionchangederror
Ifyouruntheapplicationasis,youwillnoticethatanerroristhrownafteryouclickontheMoarbuttonthefirsttime:
Expressionhaschangedafteritwaschecked
ThisisanerroryouwillmostlikelyseefrequentlyinAngular2.Themeaningissimple:sinceyouare,bydefault,operatingindevelopmentmode,Angularwillchecktwicetoseethatanyboundvaluesdonotchangeafterallofthechangedetectionlogichasbeenresolved.Inthecaseofthisrecipe,theemissionbyQueryListmodifieslastVal,whichAngulardoesnotexpect.Thus,you'llneedtoexplicitlyinformtheframeworkthatthevalueisexpectedtochangeagain.ThiscanbeaccomplishedbyinjectingChangeDetectorRef,whichallowsyoutotriggerachangedetectioncycleoncethevalueischanged:
import{Component,ViewChildren,QueryList,ngAfterViewInit,
ChangeDetectorRef}from'@angular/core';
import{InnerComponent}from'./inner.component';
@Component({
selector:'outer',
template:`
<button(click)="add()">Moar</button>
<button(click)="remove()">Less</button>
<button(click)="shuffle()">Shuffle</button>
<inner*ngFor="letioflist"
val="{{i}}">
</inner>
<p>Valueoflast:{{lastVal}}</p>
`
})
exportclassOuterComponentimplementsAfterViewInit{
@ViewChildren(InnerComponent)innerComponents:
QueryList<InnerComponent>;
list:Array<number>=[];
lastVal:number;
constructor(privatechangeDetectorRef_:ChangeDetectorRef){}
add():void{
this.list.push(this.list.length)
}
remove():void{
this.list.pop();
}
shuffle():void{
//simpleassignmentshuffle
this.list=this.list.sort(()=>(4*Math.random()>2)?1:-1);
}
![Page 355: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/355.jpg)
ngAfterViewInit(){
this.innerComponents.changes
.subscribe(innerComponents=>{
this.lastVal=(innerComponents.last||{}).val;
this.changeDetectorRef_.detectChanges();
});
}
}
Atthispoint,everythingshouldworkcorrectlywithnoerrors.
![Page 356: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/356.jpg)
Howitworks...OncetheOuterComponentviewisinitialized,youwillbeabletointeractwithQueryListthatisobtainedusingViewChildren.EachtimethecollectionthatQueryListwrapsismodified,theObservableexposedbyitschangespropertywillemitQueryList,signalingthatsomethinghaschanged.
Hatetheplayer,notthegame
Importantly,Observable<QueryList>doesnottrackchangesinthearrayofnumbers.IttracksthegeneratedcollectionofInnerComponents.ThengForstructuraldirectiveisresponsibleforgeneratingthelistofInnerComponentinstancesintheview.ItisthiscollectionthatQueryListisconcernedwith,nottheoriginalarray.
Thisisagoodthing!ViewChildrenshouldonlybeconcernedwiththecomponentsastheyhavebeenrenderedinsidetheview,notthedatathatcausedthemtoberenderedinsuchafashion.
Oneimportantconsiderationofthisisthatuponeachemission,itisentirelypossiblethatQueryListwillbeempty.Asshownabove,sincetheObserveroftheQueryList.changesObservabletriestoreferenceapropertyoflast,itisnecessarytohaveafallbackobjectliteralintheeventthatlastreturnsundefined.
![Page 357: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/357.jpg)
SeealsoBasicUtilizationofObservableswithHTTPdemonstratesthebasicsofhowtouseanobservableinterfaceBuildingafullyfeaturedAutoCompletewithObservablesgivesyouabroadtourofsomeoftheutilitiesofferedtoyouaspartoftheRxJSlibrary
![Page 358: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/358.jpg)
BuildingafullyfeaturedAutoCompletewithObservablesRxJSObservablesaffordyoualotoffirepower,anditwouldbeashametomissoutonthem.Ahugelibraryoftransformationsandutilitiesarebakedrightinthatallowyoutoelegantlyarchitectcomplexportionsofyourapplicationinareactivefashion.
Inthisrecipe,you'lltakeanaïveautocompleteformandbuildarobustsetoffeaturestoenhancebehaviorandperformance.
Note
Thecode,links,andaliveexampleofthisareavailableathttp://ngcookbook.herokuapp.com/8629/.
![Page 359: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/359.jpg)
GettingreadyBeginwiththefollowingapplication:
[app/app.module.ts]
import{NgModule}from'@angular/core';
import{BrowserModule}from'@angular/platform-browser';
import{SearchComponent}from'./search.component';
import{APIService}from'./api.service';
import{HttpModule}from'@angular/http';
@NgModule({
imports:[
BrowserModule,
HttpModule
],
declarations:[
SearchComponent
],
providers:[
APIService
],
bootstrap:[
SearchComponent
]
})
exportclassAppModule{}
[app/search.component.ts]
import{Component}from'@angular/core';
import{APIService}from'./api.service';
@Component({
selector:'search',
template:`
<input#queryField(keyup)="search(queryField.value)">
<p*ngFor="letresultofresults">{{result}}</p>
`
})
exportclassSearchComponent{
results:Array<string>=[];
constructor(privateapiService_:APIService){}
search(query:string):void{
this.apiService_
.search(query)
.subscribe(result=>this.results.push(result));
}
}
![Page 360: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/360.jpg)
[app/api.service.ts]
import{Injectable}from'@angular/core';
import{Http}from'@angular/http';
import{Observable}from'rxjs/Rx';
@Injectable()
exportclassAPIService{
constructor(privatehttp_:Http){}
search(query:string):Observable<string>{
returnthis.http_
.get('static/response.json')
.map(r=>r.json()['prefix']+query)
//Belowisjustacleverwayofrandomly
//delayingtheresponsebetween0to1000ms
.concatMap(
x=>Observable.of(x).delay(Math.random()*1000));
}
}
YourobjectiveistodramaticallyenhancethisusingRxJS.
![Page 361: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/361.jpg)
Howtodoit...Asis,thisapplicationislisteningforkeyupeventsinthesearchinput,performinganHTTPrequesttoastaticJSONfileandaddingtheresponsetoalistofresults.
UsingtheFormControlvalueChangesObservable
Angular2hasobservablebehavioralreadyavailabletoyouinanumberofplaces.OneofthemisinsideReactiveFormsModule,whichallowsyoutouseanObservablethatisattachedtoaforminput.ConvertthisinputtouseFormControl,whichexposesavalueChangesObservable:
[app/app.module.ts]
import{NgModule}from'@angular/core';
import{BrowserModule}from'@angular/platform-browser';
import{SearchComponent}from'./search.component';
import{APIService}from'./api.service';
import{HttpModule}from'@angular/http';
import{ReactiveFormsModule}from'@angular/forms';
@NgModule({
imports:[
BrowserModule,
HttpModule,
ReactiveFormsModule
],
declarations:[
SearchComponent
],
providers:[
APIService
],
bootstrap:[
SearchComponent
]
})
exportclassAppModule{}
[app/search.component.ts]
import{Component}from'@angular/core';
import{APIService}from'./api.service';
import{FormControl}from'@angular/forms';
@Component({
selector:'search',
template:`
<input[formControl]="queryField">
<p*ngFor="letresultofresults">{{result}}</p>
`
})
![Page 362: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/362.jpg)
exportclassSearchComponent{
results:Array<string>=[];
queryField:FormControl=newFormControl();
constructor(privateapiService_:APIService){
this.queryField.valueChanges
.subscribe(query=>this.apiService_
.search(query)
.subscribe(result=>this.results.push(result)));
}
}
Debouncingtheinput
Eachtimetheinputvaluechanges,Angularwilldutifullyfireoffarequestandhandletheresponseassoonasitisready.Inthecasewheretheuserisqueryingaverylongterm,suchassupercalifragilisticexpialidocious,itmaybenecessaryforyoutoonlysendoffasinglerequestonceyouthinkthey'redonewithtyping,asopposedto34requests,oneforeachtimetheinputchanges.
RxJSObservableshavethisbuiltin.debounceTime(delay)willcreateanewObservablethatwillonlypassalongthelatestvaluewhentherehaven'tbeenanyothervaluesfor<delay>ms.ThisshouldbeaddedtothevalueChangesObservablesincethisisthesourcethatyouwishtodebounce.200mswillbesuitableforyourpurposes:
[app/search.component.ts]
import{Component}from'@angular/core';
import{APIService}from'./api.service';
import{FormControl}from'@angular/forms';
@Component({
selector:'search',
template:`
<input[formControl]="queryField">
<p*ngFor="letresultofresults">{{result}}</p>
`
})
exportclassSearchComponent{
results:Array<string>=[];
queryField:FormControl=newFormControl();
constructor(privateapiService_:APIService){
this.queryField.valueChanges
.debounceTime(200)
.subscribe(query=>this.apiService_
.search(query)
.subscribe(result=>this.results.push(result)));
}
}
![Page 363: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/363.jpg)
Note
Theoriginofthetermdebouncecomesfromtheworldofcircuits.Mechanicalbuttonsorswitchesutilizemetalcontactstoopenandclosecircuitconnections.Whenthemetalcontactsareclosed,theywillbangtogetherandreboundbeforebeingsettled,causingbounce.Thisbounceisproblematicinthecircuit,asitwilloftenregisterasarepeattogglingoftheswitchorbutton—obviouslybuggybehavior.Theworkaroundforthisistofindawaytoignoretheexpectedbouncenoise—debouncing!Thiscanbeaccomplishedbyeitherignoringthebouncenoiseorintroducingadelaybeforereadingthevalue,bothofwhichcanbedonewithhardwareorsoftware.
Ignoringserialduplicates
Sinceyouarereadinginputfromatextbox,itisverypossiblethattheuserwilltypeonecharacter,thentypeanothercharacterandpressbackspace.FromtheperspectiveoftheObservable,sinceitisnowdebouncedbyadelayperiod,itisentirelypossiblethattheuserinputwillbeinterpretedinsuchawaythatthedebouncedoutputwillemittwoidenticalvaluessequentially.RxJSoffersexcellentprotectionagainstthis,distinctUntilChanged(),whichwilldiscardanemissionthatwillbeaduplicateofitsimmediatepredecessor:
[app/search.component.ts]
import{Component}from'@angular/core';
import{APIService}from'./api.service';
import{FormControl}from'@angular/forms';
@Component({
selector:'search',
template:`
<input[formControl]="queryField">
<p*ngFor="letresultofresults">{{result}}</p>
`
})
exportclassSearchComponent{
results:Array<string>=[];
queryField:FormControl=newFormControl();
constructor(privateapiService_:APIService){
this.queryField.valueChanges
.debounceTime(200)
.distinctUntilChanged()
.subscribe(query=>this.apiService_
.search(query)
.subscribe(result=>this.results.push(result)));
}
}
FlatteningObservables
YouhavechainedquiteafewRxJSmethodsuptothispoint,andseeingnestedsubscribe()
![Page 364: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/364.jpg)
invocationsmightfeelabitfunnytoyou.ItshouldmakesensesincethevalueChangesObservablehandlerisinvokingaservicemethod,whichreturnsaseparateObservable.InTypeScript,thisiseffectivelyrepresentedasObservable<Observable<string>>.Gross!
Sinceyouonlyreallycareabouttheemittedstringscomingfromtheservicemethod,itwouldbemucheasiertojustcombinealltheemittedstringscomingoutofeachreturnedObservableintoasingleObservable.Fortunately,RxJSmakesthiseasywithflatMap,whichflattensalltheemissionsfromtheinnerObservablesintoasingleouterObservable.InTypeScript,usingflatMapwouldconvertthisintoObservable<string>,whichisexactlywhatyouneed:
[app/search.component.ts]
import{Component}from'@angular/core';
import{APIService}from'./api.service';
import{FormControl}from'@angular/forms';
@Component({
selector:'search',
template:`
<input[formControl]="queryField">
<p*ngFor="letresultofresults">{{result}}</p>
`
})
exportclassSearchComponent{
results:Array<string>=[];
queryField:FormControl=newFormControl();
constructor(privateapiService_:APIService){
this.queryField.valueChanges
.debounceTime(200)
.distinctUntilChanged()
.flatMap(query=>this.apiService_.search(query))
.subscribe(result=>this.results.push(result));
}
}
Handlingunorderedresponses
Whentestinginputnow,youwillsurelynoticethatthedelayintentionallyintroducedinsidetheAPIservicewillcausetheresponsestobereturnedoutoforder.Thisisaprettyeffectivesimulationofnetworklatency,soyou'llneedagoodwayofhandlingthis.
Ideally,youwouldliketobeabletothrowoutObservablesthatareinflightonceyouhaveamorerecentquerytoexecute.Forexample,considerthatyou'vetypedgandtheno.Nowoncethesecondqueryforgoisreturnedandifthefirstqueryforghasn'treturnedyet,you'dliketojustthrowitoutandforgetaboutitsincetheresponseisnowirrelevant.
RxJSalsomakesthisveryeasywithswitchMap.ThisdoesthesamethingsasflatMap,butit
![Page 365: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/365.jpg)
willunsubscribefromanyin-flightObservablesthathavenotemittedanyvaluesyet:
[app/search.component.ts]
import{Component}from'@angular/core';
import{APIService}from'./api.service';
import{FormControl}from'@angular/forms';
@Component({
selector:'search',
template:`
<input[formControl]="queryField">
<p*ngFor="letresultofresults">{{result}}</p>
`
})
exportclassSearchComponent{
results:Array<string>=[];
queryField:FormControl=newFormControl();
constructor(privateapiService_:APIService){
this.queryField.valueChanges
.debounceTime(200)
.distinctUntilChanged()
.switchMap(query=>this.apiService_.search(query))
.subscribe(result=>this.results.push(result));
}
}
YourAutoCompleteinputshouldnowbedebouncedanditshouldignoreredundantrequestsandreturnin-orderresults.
![Page 366: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/366.jpg)
Howitworks...Therearealotofmovingpiecesgoingoninthisrecipe,butthecorethemeremainsthesame:RxJSObservablesexposemanymethodsthatcanpipetheoutputfromoneobservableintoanentirelydifferentobservable.Itcanalsocombinemultipleobservablesintoasingleobservable,aswellasintroducestate-dependentoperationsintoastreamoftheinput.Attheendofthisrecipe,thepowerofreactiveprogrammingshouldbeobvious.
![Page 367: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/367.jpg)
SeealsoBasicUtilizationofObservableswithHTTPdemonstratesthebasicsofhowtouseanobservableinterfaceImplementingaPublish-SubscribemodelusingSubjectsshowsyouhowtoconfigureinputandoutputforRxJSObservablesCreatinganObservableauthenticationserviceusingBehaviorSubjectsinstructsyouonhowtoreactivelymanagethestateinyourapplicationBuildingageneralizedPublish-Subscribeservicetoreplace$broadcast,$emit,and$onassemblesarobustPubSubmodelforconnectingapplicationcomponentswithchannels
![Page 368: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/368.jpg)
Chapter6.TheComponentRouterThischapterwillcoverthefollowingrecipes:
SettingupanapplicationtosupportsimpleroutesNavigatingwithrouterLinksNavigatingwiththeRouterserviceSelectingLocationStrategyforPathConstructionBuildingstatefulRouterLinkbehaviorwithRouterLinkActiveImplementingnestedviewswithrouteparametersandchildroutesWorkingwithMatrixURLparametersandroutingarraysAddingrouteauthenticationcontrolswithrouteguards
![Page 369: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/369.jpg)
IntroductionFewfeaturesofAngular2shouldbeanticipatedmorethantheComponentRouter.ThisnewroutingimplementationaffordsyouadazzlingarrayoffeaturesthatweremissingorseverelylackinginAngular1.
Angular2implementsmatrixparameters;thisisanentirelynewsyntaxforURLstructures.OriginallyproposedbyTimBerners-Leein1996,thissemicolon-basedsyntaxgivesyoutheabilitytorobustlyassociateparametersnotjustwithasingleURL,butwithdifferentlevelsinthatURL.YourapplicationcannowintroduceanadditionaldimensionofapplicationstateintheURLs.
Additionally,ComponentRoutergivesyouamethodofelegantlynestingviewswithineachotheraswellasasimplewayofdefiningroutesandlinkstothesecomponenthierarchies.Foryou,thismeansyourapplicationscantrulytakemaximaladvantageofdefininganapplicationasanindependentmodule.
Finally,ComponentRouterfullyembracesintegrationwithObservablestructuresandprovidesyouwithsomebeautifulwaysofnavigatingandcontrollingnavigationwithinyourapplication.
![Page 370: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/370.jpg)
SettingupanapplicationtosupportsimpleroutesCentraltothebehaviorofsingle-pageapplicationsistheabilitytoperformnavigationwithoutaformalbrowserpagereload.Angular2iswell-equippedtoworkaroundthedefaultbrowserpagereloadbehaviorandallowyoutodefinearoutingstructurewithinit,whichwillmakeitlookandfeellikeactualpagenavigation.
Note
Thecode,links,andaliveexampleofthisareavailableathttp://ngcookbook.herokuapp.com/6214.
![Page 371: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/371.jpg)
GettingreadySupposeyouhavethefollowingfunctiondefinedglobally:
functionvisit(uri){
//Forthisrecipe,youdon'tcareaboutthestateortitle
window.history.pushState(null,null,uri);
}
ThepurposeofthisistomerelyallowyoutonavigateinsidethebrowserfromJavaScriptusingtheHTML5HistoryAPI.
![Page 372: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/372.jpg)
Howtodoit...InorderforAngulartosimulatepagenavigationinsidethebrowser,thereareseveralstepsyoumusttaketocreateanavigablesingle-pageapplication.
SettingthebaseURL
ThefirststepinconfiguringyourapplicationistospecifythebaseURL.ThisinstructsthebrowserwhatnetworkrequestsperformedwitharelativeURLshouldbeginwith.
AnytimethepagemakesarequestusingarelativeURL,whengeneratingthenetworkrequest,itwillusethecurrentdomainandthenappendtherelativeURL.ForrelativeURLs,aprepended"/"meansitwillalwaysusetherootdirectory.Iftheforwardslashisunavailable,therelativepathwillprependwhateverisspecifiedasthebasehref.AnyURLbehavior,includingrequestingstaticresources,anchorlinks,andthehistoryAPI,willexhibitthisbehavior.
Note
<base>isnotapartofAngularbutratheradefaultHTML5element.
Herearesomeexamplesofthis:
[Example1]
//<basehref="/">
//initialpagelocation:foo.com
visit('bar');
//newpagelocation:foo.com/bar
visit('bar');
//newpagelocation:foo.com/bar
//Thebrowserrecognizesthatthisisarelativepath
//withnoprepended/andsoitwillvisitthepageatthe
//same"depth"asbefore.
visit('bar/');
//newpagelocation:foo.com/bar/
//Sameasbefore,butthetrailingslashwillbeimportantonce
//youinvokethisagain.
visit('bar/');
//newpagelocation:foo.com/bar/bar/
//ThebrowserrecognizesthattheURLendswitha/,andso
//visitingarelativepathistreatedasanavigationintoa
//subpath
visit('/qux');
//newpagelocation:foo.com/qux
//Witha/prependedtotheURL,thebrowserrecognizesthatit
![Page 373: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/373.jpg)
//shouldnavigatefromtherootdomain
[Example2]
//<basehref="xyz/">
//initialpagelocation:foo.com
visit('bar');
//newpagelocation:foo.com/xyz/bar
//BaseURLisprependedtotherelativeURL
visit('bar');
//newpagelocation:foo.com/xyz/bar
//Aswasthecasebefore,thelocalpathistreatedthesame
//bythebrowser
visit('/qux');
//newpagelocation:foo.com/qux
//Notethatinthiscase,youspecifiedarelativepath
//originatingfromtherootdomain,sothebasehrefisignored
Definingroutes
Next,youneedtodefinewhatyourapplication'sroutesare.Forthepurposeofthisrecipe,itismoreimportanttounderstandthesetupofroutingthanhowtodefineandnavigatebetweenroutes.So,fornow,youwilljustdefineasinglecatchallroute.
Asyoumightsuspect,routeviewsinAngular2aredefinedascomponents.Eachroutepathisrepresentedattheveryleastbythestringthatthebrowser'slocationwillmatchagainstandthecomponentthatitwillmapto.ThiscanbedonewithanobjectimplementingtheRoutesinterface,whichisanarrayofroutedefinitions.
Itmakessensethattheroutedefinitionsshouldhappenveryearlyintheapplicationinitialization,soyou'lldoitinsidethetop-levelmoduledefinition.
First,createyourviewcomponentthatthisroutewillmapto:
[app/default.component.ts]
import{Component}from'@angular/core';
@Component({
template:'Defaultcomponent!'
})
exportclassDefaultComponent{}
Next,whereveryourapplicationmoduleisdefined,importRouterModuleandtheRoutesinterface,namelyDefaultComponent,anddefineacatchallrouteinsidetheRoutesarray:
[app/app.module.ts]
![Page 374: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/374.jpg)
import{NgModule}from'@angular/core';
import{BrowserModule}from'@angular/platform-browser';
import{RouterModule,Routes}from'@angular/router';
import{RootComponent}from'./root.component';
import{DefaultComponent}from'./default.component';
constappRoutes:Routes=[
{path:'**',component:DefaultComponent}
];
@NgModule({
imports:[
BrowserModule
],
declarations:[
DefaultComponent,
RootComponent
],
bootstrap:[
RootComponent
]
})
exportclassAppModule{}
Providingroutestotheapplication
You'vedefinedtheroutesinanobject,butyourapplicationstillisnotawarethattheyexist.YoucandothiswiththeforRootmethoddefinedinRouterModule.Thisfunctiondoesallthedirtyworkofinstallingyourroutesintheapplicationaswellaspassingalonganumberofroutingprovidersforuseelsewhereintheapplication:
[app/app.module.ts]
import{NgModule}from'@angular/core';
import{BrowserModule}from'@angular/platform-browser';
import{RouterModule,Routes}from'@angular/router';
import{RootComponent}from'./root.component';
import{DefaultComponent}from'./default.component';
constappRoutes:Routes=[
{path:'**',component:DefaultComponent}
];
@NgModule({
imports:[
BrowserModule,
RouterModule.forRoot(appRoutes)
],
declarations:[
DefaultComponent,
RootComponent
],
![Page 375: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/375.jpg)
bootstrap:[
RootComponent
]
})
exportclassAppModule{}
Withthis,yourapplicationisfullyconfiguredtounderstandtherouteyouhavedefined.
RenderingroutecomponentswithRouterOutlet
Thecomponentneedsaplacetoberendered,andinAngular2,thistakestheformofaRouterOutlettag.Thisdirectivewillbetargetedbythecomponentattachedtotheactiveroute,andthecomponentwillberenderedinsideit.Tokeepthingssimple,inthisrecipe,youcanusethedirectiveinsidetherootapplicationcomponent:
[app/app.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'root',
template:`
<h1>Rootcomponent</h1>
<router-outlet></router-outlet>
`
})
exportclassRootComponent{}
That'sall!YourapplicationnowhasasingleroutedefinedthatwillrenderDefaultComponentinsideRootComponent.
![Page 376: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/376.jpg)
Howitworks...Thisrecipedoesn'tshowaverycomplicatedexampleofrouting,sinceeverypossibleroutethatyoucanvisitwillleadyoutothesamecomponent.
Nonetheless,itdemonstratesseveralfundamentalprinciplesofAngularrouting:
Initsmostbasicform,arouteiscomprisedofastringpath(matchedagainstthebrowserpath)andthecomponentthatshouldberenderedwhenthisrouteisactive.RoutesareinstalledviaRouterModule.Inthisexample,sincethereisonlyonemodule,youcandothisonceusingforRoot().However,keepinmindthatyoucanbreakyourroutingstructureintopiecesandbetweendifferentNgModules.Navigatingtoaroutewillcauseacomponenttoberenderedinsideadifferentcomponent-morespecifically,whereverthe<router-outlet>tagexists.Therearemanywaysinwhichthiscanbeconfiguredandmademorecomplex,butforthepurposeofthissimplemodule,youdon'tneedtoworryaboutthesedifferentways.
![Page 377: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/377.jpg)
There'smore...Angular2applicationswillnotraiseissueswhenoperatingwithnoformofrouting.IfyourapplicationdoesnotneedtounderstandandmanagethepageURL,thenfeelfreetototallydiscardtheroutingfilesandmodulesfromyourapplication.
Initialpageload
Theflowyouarehopingyouruserswouldgothroughisasfollows:
1. Theuservisitshttp://www.foo.com/.2. Theservermatchestheemptyroutetoindex.html.3. Thepageloads,requestingstaticfiles.4. TheAngularstaticfilesareloadedandapplicationisbootstrapped.5. Theuserclicksonthelinksandnavigatesaroundthesite.6. SinceAngulariswhollymanagingthenavigationandrouting,everythingworksasexpected.
Thisistheidealcase.Consideradifferentcase:
1. Theuserhasalreadyvisitedhttp://www.foo.com/beforeandbookmarkedit.2. Theuserentersfoo.com/barintheirURLbarandnavigatestoitfromtheredirectly.3. Theserverseestherequestpathas/barandtriestohandletherequest.
Dependingonhowyourserverisconfigured,thismightcauseproblemsforyou.Thisisbecausethelasttimetheuservisitedfoo.com/bar,norequestforthatresourcereachedtheserverbecauseAngularwasonlyemulatingarealnavigationevent.
Thisscenarioisdiscussedelsewhereinthischapter,butkeepinmindthatwithoutacorrectlyconfiguredserver,theuserinthesecondcasemightseea404pageerrorinsteadofyourapplication.
![Page 378: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/378.jpg)
SeealsoNavigatingwithrouterLinksdemonstrateshowtonavigatearoundAngularapplicationsNavigatingwiththeRouterserviceusesanAngularservicetonavigatearoundanapplicationBuildingstatefulRouterLinkbehaviorwithRouterLinkActiveshowshowtointegrateapplicationbehaviorwithaURLstate
![Page 379: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/379.jpg)
NavigatingwithrouterLinksNavigatingaroundasinglepageapplicationisafundamentaltask,andAngularoffersyouabuilt-indirective,routerLink,toaccomplishthis.
Note
Thecode,links,andaliveexampleofthisareavailableathttp://ngcookbook.herokuapp.com/9983/.
![Page 380: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/380.jpg)
GettingreadyBeginwiththeapplicationsetupassembledintheSettingupanapplicationtosupportsimpleroutesrecipe.
Yourgoalistoaddanadditionalroutetothisapplicationaccompaniedbyacomponent;also,youwanttobeabletonavigatebetweenthemusinglinks.
![Page 381: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/381.jpg)
Howtodoit...Tobegin,createanothercomponent,ArticleComponent,andanassociatedroute:
[app/article/article.component.ts]
import{Component}from'@angular/core';
@Component({
template:'Articlecomponent!'
})
exportclassArticleComponent{}
Next,installanarticlerouteaccompaniedbythisnewcomponent:
[app/app.module.ts]
import{NgModule}from'@angular/core';
import{BrowserModule}from'@angular/platform-browser';
import{RouterModule,Routes}from'@angular/router';
import{RootComponent}from'./root.component';
import{DefaultComponent}from'./default.component';
import{ArticleComponent}from'./article.component';
constappRoutes:Routes=[
{path:'article',component:ArticleComponent},
{path:'**',component:DefaultComponent}
];
@NgModule({
imports:[
BrowserModule,
RouterModule.forRoot(appRoutes)
],
declarations:[
DefaultComponent,
ArticleComponent,
RootComponent
],
bootstrap:[
RootComponent
]
})
exportclassAppModule{}
Withtheroutesdefined,youcannowbuildarudimentarynavbarcomprisedofrouterLinks.Themarkupsurroundingthe<router-outlet>tagwillremainirrespectiveoftheroute,sotherootappcomponentseemslikeasuitableplaceforthenavlinks.
TherouterLinkdirectiveisavailableaspartofRouterModule,soyoucangostraighttoadding
![Page 382: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/382.jpg)
someanchortags:
[app/root.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'root',
template:`
<h1>Rootcomponent</h1>
<a[routerLink]="''">Default</a>
<a[routerLink]="'article'">Article</a>
<router-outlet></router-outlet>
`
})
exportclassRootComponent{}
Inthiscase,sincetheroutesaresimpleandstatic,bindingrouterLinktoastringisallowed.routerLinkalsoacceptsthearraynotation:
[app/root.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'root',
template:`
<h1>Rootcomponent</h1>
<a[routerLink]="['']">Default</a>
<a[routerLink]="['article']">Article</a>
<router-outlet></router-outlet>
`
})
exportclassRootComponent{}
Tip
Forthepurposeofthisrecipe,thearraynotationdoesn'taddanything.However,whendevelopingmorecomplicatedURLstructures,thearraynotationbecomesuseful,asitallowsyoutogeneratelinksinapiecewisefashion.
![Page 383: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/383.jpg)
Howitworks...Atahighlevel,thisisnodifferentthanthebehaviorofavanillahrefattribute.Afterall,theroutesbehaveinthesamewayandarestructuredsimilarly.TheimportantdifferencehereisthatusingarouterLinkdirectiveinsteadofhrefallowsyoutomovearoundyourapplicationtheAngularway,withouteverhavingtheanchortagclickinterpretedbythebrowserasanon-Angularnavigationevent.
![Page 384: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/384.jpg)
There'smore...Ofcourse,therouterLinkdirectiveisalsosuperiorasitismoreextensibleasatoolfornavigating.SinceitisanHTMLattributeafterall,there'snoreasonrouterLinkcan'tbeattachedto,forexample,abuttoninstead:
[app/root.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'root',
template:`
<h1>Rootcomponent</h1>
<button[routerLink]="['']">Default</button>
<button[routerLink]="['article']">Article</button>
<router-outlet></router-outlet>
`
})
exportclassRootComponent{}
What'smore,you'llalsonotethatthearraynotationallowsthedynamicgenerationoflinksviaallofthetremendousdatabindingthatAngularaffordsyou:
[app/root.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'root',
template:`
<h1>Rootcomponent</h1>
<a[routerLink]="[defaultPath]">Default</a>
<a[routerLink]="[articlePath]">Article</a>
<router-outlet></router-outlet>
`
})
exportclassRootComponent{
defaultPath:string='';
articlePath:string='article';
}
AstheURLstructuregetsevermoreadvanced,itwillbeeasytoseehowacleverapplicationofdatabindingcouldmakeforsomeveryelegantdynamiclinkgeneration.
Routeorderconsiderations
TheorderingofroutesinsidetheRoutesdefinitionspecifiesthedescendingpriorityofeachofthem.Inthisrecipe'sexample,supposeyouweretoreversetheorderoftheroutes:
![Page 385: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/385.jpg)
[app/app.module.ts]
import{NgModule}from'@angular/core';
import{BrowserModule}from'@angular/platform-browser';
import{RouterModule,Routes}from'@angular/router';
import{RootComponent}from'./root.component';
import{DefaultComponent}from'./default.component';
import{ArticleComponent}from'./article.component';
constappRoutes:Routes=[
{path:'**',component:DefaultComponent},
{path:'article',component:ArticleComponent}
];
@NgModule({
imports:[
BrowserModule,
RouterModule.forRoot(appRoutes)
],
declarations:[
DefaultComponent,
ArticleComponent,
RootComponent
],
bootstrap:[
RootComponent
]
})
exportclassAppModule{}
Whenexperimenting,youwillfindthatthebrowser'sURLchangescorrectlywiththevariousrouterLinkinteractions,butbothrouteswilluseDefaultComponentastherenderedview.Thisissimplybecausealltheroutesmatchthe**catchall,andAngulardoesn'tbothertotraversetheroutesanyfurtheronceithasamatchingroute.Keepthisinmindwhenauthoringlargeroutetables.
![Page 386: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/386.jpg)
SeealsoSettingupanapplicationtosupportsimpleroutesshowsyouthebasicsofAngularroutingNavigatingwiththeRouterserviceusesanAngularservicetonavigatearoundanapplicationBuildingstatefulRouterLinkbehaviorwithRouterLinkActiveshowshowtointegrateapplicationbehaviorwithaURLstateImplementingnestedviewswithrouteparametersandchildroutesgivesanexampleofhowtoconfigureAngularURLstosupportnestinganddatapassingWorkingwithmatrixURLparametersandroutingarraysdemonstratesAngular'sbuilt-inmatrixURLsupport
![Page 387: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/387.jpg)
NavigatingwiththeRouterserviceThecompaniontousingrouterLinkinsidethetemplatetonavigateisdoingitfrominsideJavaScript.Angularexposesthenavigate()methodfrominsideaservice,whichallowsyoutoaccomplishexactlythis.
Note
Thecode,links,andaliveexampleofthisareavailableathttp://ngcookbook.herokuapp.com/8004/.
![Page 388: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/388.jpg)
GettingreadyBeginwiththeapplicationthatexistsattheendoftheHowtodoit...sectionoftheNavigatingwithrouterLinksrecipe.
Yourgoalistoaddanadditionalrouteaccompaniedbyacomponenttothisapplication;also,youwishtobeabletonavigatebetweenthemusinglinks.
![Page 389: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/389.jpg)
Howtodoit...InsteadofusingrouterLink,whichisthemostsensiblechoiceinthissituation,youcanalsotriggeranavigationusingtheRouterservice.First,addnavbuttonsandattachsomeemptyclickhandlerstothem:
[app/root.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'root',
template:`
<h1>Rootcomponent</h1>
<button(click)="visitDefault()">Default</button>
<button(click)="visitArticle()">Article</button>
<router-outlet></router-outlet>
`
})
exportclassRootComponent{
visitDefault():void{}
visitArticle():void{}
}
Next,importtheRouterserviceanduseitsnavigate()methodtochangethepagelocation:
[app/root.component.ts]
import{Component}from'@angular/core';
import{Router}from'@angular/router';
@Component({
selector:'root',
template:`
<h1>Rootcomponent</h1>
<button(click)="visitDefault()">Default</button>
<button(click)="visitArticle()">Article</button>
<router-outlet></router-outlet>
`
})
exportclassRootComponent{
constructor(privaterouter:Router){}
visitDefault():void{
this.router.navigate(['']);
}
visitArticle():void{
this.router.navigate(['article']);
}
}
Withthisaddition,youshouldbeabletonavigatearoundyourapplicationinthesamewayyou
![Page 390: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/390.jpg)
didbefore.
![Page 391: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/391.jpg)
Howitworks...TheRouterserviceexposesanAPIwithwhichyoucancontrolyourapplication'snavigationbehavior,amongmanyotherthings.Itsnavigate()methodacceptsanarray-structuredroute,whichoperatesidenticallytotheArraysboundtorouterLink.
![Page 392: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/392.jpg)
There'smore...Obviously,thisisanutterantipatternforbuildingapplicationsthataredesignedtoscale.Inthisscenario,routerLinkisamuchmoresuccinctandeffectivechoiceforbuildingasimplenavbar.Nevertheless,theRouterserviceisanequallyeffectivetoolfortraversinganAngularapplication'sroutestructure.
![Page 393: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/393.jpg)
SeealsoNavigatingwithrouterLinksdemonstrateshowtonavigatearoundAngularapplicationsBuildingstatefulRouterLinkbehaviorwithRouterLinkActiveshowshowtointegrateapplicationbehaviorwithaURLstateWorkingwithmatrixURLparametersandroutingarraysdemonstratesAngular'sbuilt-inmatrixURLsupportAddingrouteauthenticationcontrolswithrouteguardsdetailstheentireprocessofconfiguringprotectedroutesinyourapplication
![Page 394: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/394.jpg)
SelectingaLocationStrategyforpathconstructionAsimplebutimportantchoiceforyourapplicationiswhichtypeofLocationStrategyyouwanttomakeuseof.ThefollowingtwoURLsareequivalentwhentheirrespectiveLocationStrategyisselected:
PathLocationStrategy:foo.com/barHashLocationStrategy:foo.com/#/bar
Note
Thecode,links,andaliveexampleofthisareavailableathttp://ngcookbook.herokuapp.com/1355/.
![Page 395: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/395.jpg)
Howtodoit...Angular2willdefaulttoPathLocationStrategy.ShouldyouwanttoselectHashLocationStrategy,itcanbeimportedfromthe@angular/commonmodule.Onceimported,itcanbelistedasaproviderinsideanobjectliteral:
[app/app.module.ts]
import{NgModule}from'@angular/core';
import{BrowserModule}from'@angular/platform-browser';
import{RouterModule,Routes}from'@angular/router';
import{RootComponent}from'./root.component';
import{DefaultComponent}from'./default.component';
import{ArticleComponent}from'./article.component';
import{LocationStrategy,HashLocationStrategy}
from'@angular/common';
constappRoutes:Routes=[
{path:'article',component:ArticleComponent},
{path:'**',component:DefaultComponent},
];
@NgModule({
imports:[
BrowserModule,
RouterModule.forRoot(appRoutes)
],
declarations:[
DefaultComponent,
ArticleComponent,
RootComponent
],
providers:[
{provide:LocationStrategy,useClass:HashLocationStrategy}
],
bootstrap:[
RootComponent
]
})
exportclassAppModule{}
Withthisaddition,yourapplicationwilltransitiontoprefix#/toallapplication-definedURLs.Thiswilloccurintransparencewiththerestofyourapplication,whichcanuseitsroutingdefinitionsnormallywithouthavingtoworryaboutprefixing#/.
![Page 396: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/396.jpg)
There'smore...Therearetradeoffsforeachofthesestrategies.AstheAngulardocumentationnotes,onceyouchooseone,itisinadvisabletoswitchtotheothersincebookmarks,SEO,anduserhistorywillallbecoupledtotheURLstrategyutilizedduringthatvisit.
PathLocationStrategy:
Here,theURLsappearnormaltotheenduserTheservermustbeconfiguredtohandlepageloadsfromanyapplicationpathThisallowsthehybridserver-siderenderingofroutesforimprovedperformance
HashLocationStrategy:
Here,theURLsmaylookfunnytotheenduser.Noserverconfigurationisrequirediftherootdomainservesindex.html.Thisisagoodoptionifyouonlywanttoservestaticfiles(forexample,anAmazonAWS-basedsite).Itcannotbeeasilyintermixedwithhybridserver-siderendering.
ConfiguringyourapplicationserverforPathLocationStrategy
Angularissmartenoughtorecognizethebrowserstateandmanageitaccordinglyoncebootstrappingoccurs.However,bootstrappingrequiresaninitialloadofthestaticcompiledJSassets,whichwillbootstrapAngularoncethebrowserloadsthem.Whentheuserinitiallyvisitsarootdomain,suchasfoo.com,theserverisnormallyconfiguredtorespondwithindex.html,whichwillinturnrequestthestaticassetsatrendertime.So,Angularwillwork!
However,incaseswheretheuserinitiallyvisitsanon-rootpath,suchasfoo.com/bar,thebrowserwillsendarequesttotheserveratfoo.com/bar.Ifyouaren'tcarefulwhensettingupyourserver,acommonmistakeyoumaycommitishavingonlytherootfoo.compathreturnindex.html.
InorderforPathLocationStrategytoworkcorrectlyinallcases,youmustconfigureyourwebservertosetupacatchallrouteforalltherequeststhathavepathsintendedforthesingle-pageapplication'srouteintheclient,andtoinvariablyreturnindex.html.Inotherwords,visitingfoo.com,foo.com/bar,orfoo.com/bar/bazasthefirstpageinthebrowserwillallreturnthesamething:index.html.Onceyoudothis,postbootstrapAngularwillexaminethecurrentbrowserpathandrecognizewhichpathitisonandwhatviewneedstobedisplayed.
![Page 397: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/397.jpg)
BuildingstatefulroutebehaviorwithRouterLinkActiveItisoftenthecasewhenbuildingapplicationsthatyouwillwanttobuildfeaturesthatwouldinvolvewhichpagetheapplicationiscurrentlyon.Whenthisisaone-timeinspection,itisn'taproblem,asbothAngularanddefaultbrowserAPIsallowyoutoeasilyinspectthecurrentpage.
ThingsgetabitstickierwhenyouwantthestateofthepagetoreflectthestateoftheURL,forexample,ifyouwanttovisuallyindicatewhichlinkcorrespondstothecurrentpage.Afrom-scratchimplementationofthiswouldrequiresomesortofstatemachinethatwouldknowwhennavigationeventsoccurandwhatandhowtomodifyateachgivenroute.
Fortunately,Angular2givesyousomeexcellenttoolstodothisrightoutofthebox.
Note
Thecode,links,andaliveexampleofthisareallavailableathttp://ngcookbook.herokuapp.com/3308/.
![Page 398: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/398.jpg)
GettingreadyBeginwiththeArrayandanchor-tag-basedimplementationshownintheNavigatingwithrouterLinksrecipe.
YourgoalistouseRouterLinkActivetointroducesomesimplestatefulroutebehavior.
![Page 399: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/399.jpg)
Howtodoit...RouterLinkActiveallowsyoutoconditionallyapplyclasseswhenthecurrentroutematchesthecorrespondingrouterLinkonthesameelement.ProceeddirectlytoaddingitasanattributedirectivetoeachlinkaswellasamatchingCSSclass:
[app/root.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'root',
template:`
<h1>Rootcomponent</h1>
<a[routerLink]="['']"
[routerLinkActive]="'active-navlink'">Default</a>
<a[routerLink]="['article']"
[routerLinkActive]="'active-navlink'">Article</a>
<router-outlet></router-outlet>
`,
styles:[`
.active-navlink{
color:red;
text-transform:uppercase;
}
`]
})
exportclassRootComponent{}
Thisisallyouneedforlinkstobecomeactive!YouwillnoticethatAngularwillconditionallyapplytheactive-navlinkclassbasedonthecurrentroute.
However,whentestingthis,youwillnoticethatthe/articleroutemakesboththelinksappearactive.Thisisduetothefactthatbydefault,AngularmarksallrouterLinksthatmatchthecurrentrouteasactive.
Note
Thisbehaviorisusefulincaseswhereyoumaywanttoshowahierarchyoflinksasactiveforexample,atroute/user/123/detail,itcouldmakesensethattheseparatelinks/user,/user/123,and/user/123/detailareallshownasactive.
However,inthecaseofthisrecipe,thisbehaviorisnotusefultoyou,andAngularhasanotherrouterdirective,routerLinkActiveOptions,whichbindstoanoptionsobject.Theexactpropertyinsidetheoptionsobjectisusefulinthiscase;itcontrolswhethertheactivestateshouldonlybeappliedincasesofanexactmatch:
[app/root.component.ts]
![Page 400: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/400.jpg)
import{Component}from'@angular/core';
import{Router}from'@angular/router';
@Component({
selector:'root',
template:`
<h1>Rootcomponent</h1>
<a[routerLink]="['']"
[routerLinkActive]="'active-navlink'"
[routerLinkActiveOptions]="{exact:true}">Default</a>
<a[routerLink]="['article']"
[routerLinkActive]="'active-navlink'"
[routerLinkActiveOptions]="{exact:true}">Article</a>
<router-outlet></router-outlet>
`,
styles:[`
.active-navlink{
color:red;
text-transform:uppercase;
}
`]
})
exportclassRootComponent{}
Nowyouwillfindthateachlinkwillonlybeactiveatitsrespectiveroute.
![Page 401: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/401.jpg)
Howitworks...TherouterLinkActiveimplementationsubscribestonavigationchangeeventsthatAngularemitsfromtheRouterservice.WhenitseesaNavigationEndevent,itperformsanupdateofalltheattachedHTMLtags,whichincludesaddingandstrippingapplicable"active"CSSclassesthattheelementisboundtoviathedirective.
![Page 402: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/402.jpg)
There'smore...IfyouneedtobindrouterLinkActivetoadynamicvalue,theprecedingsyntaxwillallowyoutodoexactlythat.Forexample,youcanbindtoa
component
memberand
modify
itelsewhere,andAngularwillhandleeverythingforyou.However,ifthisisnotrequired,AngularwillhandlerouterLinkActivewithoutthedatabindingbrackets.Inthiscase,thevalueofthedirectivenolongerneedstobeanAngularexpression,soyoucanremovethenestedquotes.
Thefollowingisbehaviorallyidentical:
[app/root.component.ts]
import{Component}from'@angular/core';
import{Router}from'@angular/router';
@Component({
selector:'root',
template:`
<h1>Rootcomponent</h1>
<a[routerLink]="['']"
routerLinkActive="active-navlink"
[routerLinkActiveOptions]="{exact:true}">
Default</a>
<a[routerLink]="['article']"
routerLinkActive="active-navlink"
[routerLinkActiveOptions]="{exact:true}">
Article</a>
<router-outlet></router-outlet>
`,
styles:[`
.active-navlink{
color:red;
text-transform:uppercase;
}
`]
})
exportclassRootComponent{}
![Page 403: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/403.jpg)
SeealsoSettingupanapplicationtosupportsimpleroutesshowsyouthebasicsofAngularroutingNavigatingwithrouterLinksdemonstrateshowtonavigatearoundAngularapplicationsBuildingstatefulRouterLinkbehaviorwithRouterLinkActiveshowshowtointegrateapplicationbehaviorwithaURLstateImplementingnestedviewswithrouteparametersandchildroutesgivesanexampleofhowtoconfigureAngularURLstosupportnestinganddatapassingAddingrouteauthenticationcontrolswithrouteguardsdetailstheentireprocessofconfiguringprotectedroutesinyourapplication
![Page 404: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/404.jpg)
ImplementingnestedviewswithrouteparametersandchildroutesAngular2'scomponentrouteroffersyouthenecessaryconceptofchildroutes.Asyoumightexpect,thisbringstheconceptofrecursivelydefinedviewstothetable,whichaffordsyouanincrediblyusefulandelegantwayofbuildingyourapplication.
Note
Thecode,links,andaliveexampleofthisareavailableathttp://ngcookbook.herokuapp.com/7892/.
![Page 405: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/405.jpg)
GettingreadyBeginwiththeArrayandanchor-tag-basedimplementationshowninNavigatingwithrouterLinksrecipe.
Yourgoalistoextendthissimpleapplicationtoinclude/article,whichwillbethelistview,and/article/:id,whichwillbethedetailview.
![Page 406: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/406.jpg)
Howtodoit...First,modifytheroutestructureforthissimpleapplicationbyextendingthe/articlepathtoincludeitssubpaths:/and/:id.Routesaredefinedhierarchically,andeachroutecanhavechildroutesusingthechildrenproperty.
Addingaroutingtargettotheparentcomponent
First,youmustmodifytheexistingArticleComponentsothatitcancontainchildviews.Asyoumightexpect,thechildviewisrenderedinexactlythesamewayasitisdonefromtherootcomponent,usingRouterOutlet:
[app/article.component.ts]
import{Component}from'@angular/core';
@Component({
template:`
<h2>Article</h2>
<router-outlet></router-outlet>
`
})
exportclassArticleComponent{}
Thiswon'tdoanythingyet,butaddingRouterOutletdescribestoAngularhowroutecomponenthierarchiesshouldberendered.
Definingnestedchildviews
Inthisrecipe,youwouldliketohavetheparentArticleComponentcontainachildview,eitherArticleListComponentorArticleDetailComponent.Forthesimplicityofthisrecipe,youcanjustdefineyourlistofarticlesasanarrayofintegers.
Definetheskeletonofthesetwocomponentsasfollows:
[app/article-list.component.ts]
import{Component}from'@angular/core';
@Component({
template:`
<h3>ArticleList</h3>
`
})
exportclassArticleListComponent{
articleIds:Array<number>=[1,2,3,4,5];
}
[app/article-detail.component.ts]
![Page 407: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/407.jpg)
import{Component}from'@angular/core';
@Component({
template:`
<h3>ArticleDetail</h3>
<p>Showingarticle{{articleId}}</p>
`
})
exportclassArticleDetailComponent{
articleId:number;
}
Definingthechildroutes
Atthispoint,nothingintheapplicationyetpointstoeitherofthesechildroutes,soyou'llneedtodefinethemnow.
ThechildrenpropertyofarouteshouldjustbeanotherRoute,whichshouldrepresentthenestedroutesthatareappendedtotheparentroute.
Note
Inthisway,youaredefiningasortofrouting"tree,"whereeachrouteentrycanhavemanychildroutesdefinedrecursively.Thiswillbediscussedingreaterdetaillaterinthischapter.
Furthermore,youshouldalsousetheURLparameternotationtodeclare:articleIdasavariableintheroute.Thisallowsyoutopassvaluesinsidetherouteandthenretrievethesevaluesinsidethecomponentthatisrendered.
Addtheseroutedefinitionsnow:
[app/app.module.ts]
import{NgModule}from'@angular/core';
import{BrowserModule}from'@angular/platform-browser';
import{RouterModule,Routes}from'@angular/router';
import{RootComponent}from'./root.component';
import{DefaultComponent}from'./default.component';
import{ArticleComponent}from'./article.component';
import{ArticleListComponent}from'./article-list.component';
import{ArticleDetailComponent}from'./article-detail.component';
constappRoutes:Routes=[
{path:'article',component:ArticleComponent,
children:[
{path:'',component:ArticleListComponent},
{path:':articleId',component:ArticleDetailComponent}
]
},
{path:'**',component:DefaultComponent},
];
![Page 408: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/408.jpg)
@NgModule({
imports:[
BrowserModule,
RouterModule.forRoot(appRoutes)
],
declarations:[
DefaultComponent,
ArticleComponent,
ArticleListComponent,
ArticleDetailComponent,
RootComponent
],
bootstrap:[
RootComponent
]
})
exportclassAppModule{}
You'llnotethatArticleListComponentiskeyedbyanemptystring.Thisshouldmakesense,aseachoftheseroutesarejoinedtotheirparentroutestocreatethefullroute.Ifyouweretojoineachrouteinthistreewithitsancestralpathtogetthefullroute,theroutedefinitionyou'vejustcreatedwouldhavethefollowingthreeentries:
/article=>ArticleComponent
ArticleListComponent
/article/4=>ArticleComponent
ArticleDetailComponent<articleId=4>
/**=>DefaultComponent
Note
Notethatinthiscase,thenumberofactualroutescorrespondstothenumberofleavesoftheURLtreesincethearticleparentroutewillalsomaptothechildarticle's+''route.Dependingonhowyouconfigureyourroutestructure,theleaf/routeparitywillnotalwaysbethecase.
Definingchildviewlinks
Withtheroutesbeingmappedtothechildcomponents,youcanfleshoutthechildviews.StartingwithArticleList,createarepeatertogeneratethelinkstoeachofthechildviews:
[app/article-list.component.ts]
import{Component}from'@angular/core';
@Component({
template:`
<h3>ArticleList</h3>
<p*ngFor="letarticleIdofarticleIds">
<a[routerLink]="articleId">
Article{{articleId}}
![Page 409: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/409.jpg)
</a>
</p>
`
})
exportclassArticleListComponent{
articleIds:Array<number>=[1,2,3,4,5];
}
Note
NotethatrouterLinkislinkingtotherelativepathofthedetailview.Sincethecurrentpathforthisviewis/article,arelativerouterLinkof4willnavigatetheapplicationto/article/4uponaclick.
Theselinksshouldwork,butwhenyouclickonthem,theywilltakeyoutothedetailviewthatcannotdisplayarticleIdfromtheroutesinceyouhavenotextractedityet.
InsideArticleDetailComponent,createalinkthatwilltaketheuserbacktothearticle/route.Sinceroutesbehavelikedirectories,youcanjustusearelativepaththatwilltaketheuseruponelevel:
[app/article-detail.component.ts]
import{Component}from'@angular/core';
@Component({
template:`
<h3>ArticleDetail</h3>
<p>Showingarticle{{articleId}}</p>
<a[routerLink]="'../'">Backup</a>
`
})
exportclassArticleDetailComponent{
articleId:number;
}
Extractingrouteparameters
AcrucialdifferencebetweenAngular1and2istherelianceonObservableconstructs.Inthecontextofrouting,Angular2wieldsObservablestoencapsulatethatroutingoccursasasequenceofeventsandthatvaluesareproducedatdifferentstatesintheseeventsandwillbereadyeventually.
Moreconcretely,routeparamsinAngular2arenotexposeddirectly,butratherthroughanObservableinsideActivatedRoute.YoucansetObserveronitsparamsObservabletoextracttherouteparamsoncetheyareavailable.
InjecttheActivatedRouteinterfaceandusetheparamsObservabletoextractarticleIdand
![Page 410: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/410.jpg)
assignittotheArticleDetailComponentinstancemember:
[app/article-detail/article-detail.component.ts]
import{Component}from'@angular/core';
import{ActivatedRoute}from'@angular/router';
@Component({
template:`
<h3>ArticleDetail</h3>
<p>Showingarticle{{articleId}}</p>
<a[routerLink]="'../'">Backup</a>
`
})
exportclassArticleDetailComponent{
articleId:number;
constructor(privateactivatedRoute_:ActivatedRoute){
activatedRoute_.params
.subscribe(params=>this.articleId=params['articleId']);
}
}
Withthis,youshouldbeabletoseethearticleIdparameterinterpolatedintoArticleDetailComponent.
![Page 411: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/411.jpg)
Howitworks...Inthisapplication,youhavenestedcomponents,AppComponentandArticleComponent,bothofwhichcontainRouterOutlet.Angularisabletotaketheroutinghierarchyyoudefinedandapplyittothecomponenthierarchythatitmapsto.Morespecifically,foreveryRouteyoudefineinyourroutinghierarchy,thereshouldbeanequalnumberofRouterOutletsinwhichtheycanrender.
![Page 412: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/412.jpg)
There'smore...Tosome,itwillfeelstrangetoneedtoextracttherouteparamsfromanObservableinterface.Ifthissolutionfeelsabitclunkytoyou,therearewaysoftidyingitup.
Refactoringwithasyncpipes
RecallthatAngularhastheabilitytointerpolateObservabledatadirectlyintothetemplateasitbecomesready.EspeciallysinceyoushouldonlyeverexpecttheparamObservabletoemitonce,youcanuseittoinsertarticleIdintothetemplatewithoutexplicitlysettinganObserver:
[app/article-detail.component.ts]
import{Component}from'@angular/core';
import{ActivatedRoute}from'@angular/router';
@Component({
template:`
<h3>ArticleDetail</h3>
<p>Showingarticle
{{(activatedRoute.params|async).articleId}}</p>
<a[routerLink]="'../'">Backup</a>
`
})
exportclassArticleDetailComponent{
constructor(activatedRoute:ActivatedRoute){}
}
Eventhoughthisworksperfectlywell,usingaprivatereferencetoaninjectedservicedirectlyintothetemplatemayfeelabitfunnytoyou.AsuperiorstrategyistograbareferencetothepublicObservableinterfaceyouneedandinterpolatethatinstead:
[app/article-detail.component.ts]
import{Component}from'@angular/core';
import{Observable}from'rxjs/Observable';
import{ActivatedRoute,Params}from'@angular/router';
@Component({
template:`
<h3>ArticleDetail</h3>
<p>Showingarticle{{(params|async).articleId}}</p>
<a[routerLink]="'../'">Backup</a>
`
})
exportclassArticleDetailComponent{
params:Observable<Params>;
constructor(privateactivatedRoute_:ActivatedRoute){
this.params=activatedRoute_.params;
}
![Page 413: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/413.jpg)
}
![Page 414: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/414.jpg)
SeealsoNavigatingwithrouterLinksdemonstrateshowtonavigatearoundAngularapplicationsNavigatingwiththeRouterserviceusesanAngularservicetonavigatearoundanapplicationBuildingstatefulRouterLinkbehaviorwithRouterLinkActiveshowshowtointegrateapplicationbehaviorwithaURLstateWorkingwithmatrixURLparametersandroutingarraysdemonstratesAngular'sbuilt-inmatrixURLsupportAddingrouteauthenticationcontrolswithrouteguardsdetailstheentireprocessofconfiguringprotectedroutesinyourapplication
![Page 415: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/415.jpg)
WorkingwithmatrixURLparametersandroutingarraysAngular2introducesnativesupportforanawesomefeaturethatseemstobefrequentlyoverlooked:matrixURLparameters.Essentially,theseallowyoutoattachanarbitraryamountofdatainsideaURLtoanyroutinglevelinAngular,andgivingyoutheabilitytoreadthatdataoutasaregularURLparameter.
Note
Thecode,links,andaliveexampleofthisareavailableathttp://ngcookbook.herokuapp.com/4553/.
![Page 416: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/416.jpg)
GettingreadyBeginwiththecodecreatedattheendoftheHowtodoit...sectioninImplementingnestedviewswithrouteparametersandchildroutes.
YourgoalistopassarbitrarydatatoboththeArticleListandArticleDetaillevelsofthisapplicationviaonlytheURL.
![Page 417: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/417.jpg)
Howtodoit...routerLinkarraysareprocessedserially,soanystringthatwillbecomepartoftheURLthatisfollowedbyanobjectwillhavethatobjectconvertedintomatrixURLparameters.Itwillbeeasiertounderstandthisbyexample,sobeginbypassinginsomedummydatatotheArticleListviewfromrouterLink:
[app/root.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'root',
template:`
<h1>Rootcomponent</h1>
<a[routerLink]="['']">
Default</a>
<a[routerLink]="['article',{listData:'foo'}]">
Article</a>
<router-outlet></router-outlet>
`
})
exportclassRootComponent{}
Now,ifyouclickonthislink,youwillseeyourbrowsernavigatetothefollowingpathwhilestillsuccessfullyrenderingtheArticleListview:
/article;listData=foo
Toaccessthisdata,simplyextractitfromtheActivatedRouteparams:
[app/article-list.component.ts]
import{Component}from'@angular/core';
import{ActivatedRoute}from'@angular/router';
@Component({
template:`
<h3>ArticleList</h3>
<p*ngFor="letarticleIdofarticleIds">
<a[routerLink]="[articleId]">
Article{{articleId}}
</a>
</p>
`
})
exportclassArticleListComponent{
articleIds:Array<number>=[1,2,3,4,5];
constructor(privateactivatedRoute_:ActivatedRoute){
activatedRoute_.params
![Page 418: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/418.jpg)
.subscribe(params=>{
console.log('Listparams:');
console.log(window.location.href)
console.log(params);
});
}
}
Whentheviewisloaded,you'llseethefollowing:
Listparams:
/article;listData=foo
Object{listData:"foo"}
Awesome!Dothesameforthedetailview:
[app/article-list.component.ts]
import{Component}from'@angular/core';
import{ActivatedRoute}from'@angular/router';
@Component({
template:`
<h3>ArticleList</h3>
<p*ngFor="letarticleIdofarticleIds">
<a[routerLink]="[articleId,{detailData:'bar'}]">
Article{{articleId}}
</a>
</p>
`
})
exportclassArticleListComponent{
articleIds:Array<number>=[1,2,3,4,5];
constructor(privateactivatedRoute_:ActivatedRoute){
activatedRoute_.params
.subscribe(params=>{
console.log('Listparams:');
console.log(window.location.href)
console.log(params);
});
}
}
Addthesameamountofloggingtothedetailview:
[app/article-detail.component.ts]
import{Component}from'@angular/core';
import{ActivatedRoute}from'@angular/router';
@Component({
template:`
![Page 419: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/419.jpg)
<h3>ArticleDetail</h3>
<p>Showingarticle{{articleId}}</p>
<a[routerLink]="'../'">Backup</a>
`
})
exportclassArticleDetailComponent{
articleId:number;
constructor(privateactivatedRoute_:ActivatedRoute){
activatedRoute_.params
.subscribe(params=>{
console.log('Detailparams:');
console.log(window.location.href)
console.log(params);
this.articleId=params['articleId']
});
}
}
Whenyouvisitadetailpage,you'llseethefollowinglogged:
Detailparams:
/article;listData=foo/1;detailData=bar
Object{articleId:"1",detailData:"foo"}
Veryinteresting!NotonlyisAngularabletoassociatedifferentmatrixparameterswithdifferentroutinglevels,butithascombinedboththeexpectedarticleIdparameterandtheunexpecteddetailDataparameterintothesameObservableemission.
![Page 420: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/420.jpg)
Howitworks...AngularisabletoseamlesslyconvertfromaroutingarraycontainingamatrixparamobjecttoaserializedURLcontainingthematrixparams,thenbackintoadeserializedJavaScriptobjectcontainingtheparameterdata.ThisallowsyoutostorearbitrarydatainsideURLsatdifferentlevels,withouthavingtocramitallintoaquerystringattheend.
![Page 421: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/421.jpg)
There'smore...NoticethatwhenyouclickonBackupinthedetailview,thelistDataURLparamispreserved.Angularwilldutifullymaintainthestateasyounavigatethroughouttheapplication,sousingmatrixparameterscanbeaveryeffectivewayofstoringstatefuldatathatsurvivesnavigationorpagereloads.
![Page 422: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/422.jpg)
SeealsoNavigatingwithrouterLinksdemonstrateshowtonavigatearoundAngularapplicationsNavigatingwiththeRouterserviceusesanAngularservicetonavigatearoundanapplicationBuildingstatefulRouterLinkbehaviorwithRouterLinkActiveshowshowtointegrateapplicationbehaviorwithaURLstateImplementingnestedviewswithrouteparametersandchildroutesgivesanexampleofhowtoconfigureAngularURLstosupportnestinganddatapassing
![Page 423: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/423.jpg)
AddingrouteauthenticationcontrolswithrouteguardsThenatureofsingle-pageapplicationswhollycontrollingtheprocessofroutingaffordsthemtheabilitytocontroleachstageoftheprocess.Foryou,thismeansthatyoucaninterceptroutechangesastheyhappenandmakedecisionsaboutwheretheusershouldgo.
Note
Thecode,links,andaliveexampleofthisareavailableathttp://ngcookbook.herokuapp.com/6135/.
![Page 424: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/424.jpg)
GettingreadyInthisrecipe,you'llbuildasimplepseudo-authenticatedapplicationfromscratch.
Yougoalistoprotectusersfromcertainviewswhentheyarenotauthenticated,andatthesametime,implementasensiblelogin/logoutflow.
![Page 425: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/425.jpg)
Howtodoit...Beginbydefiningtwoinitialviewswithroutesinyourapplication.OnewillbeaDefaultview,whichwillbevisibletoeverybody,andonewillbeaProfileview,whichwillbeonlyvisibletoauthenticatedusers:
[app/app.module.ts]
import{NgModule}from'@angular/core';
import{BrowserModule}from'@angular/platform-browser';
import{RouterModule,Routes}from'@angular/router';
import{RootComponent}from'./root.component';
import{DefaultComponent}from'./default.component';
import{ProfileComponent}from'./profile.component';
constappRoutes:Routes=[
{path:'profile',component:ProfileComponent},
{path:'**',component:DefaultComponent}
];
@NgModule({
imports:[
BrowserModule,
RouterModule.forRoot(appRoutes)
],
declarations:[
DefaultComponent,
ProfileComponent,
RootComponent
],
bootstrap:[
RootComponent
]
})
exportclassAppModule{}
[app/default.component.ts]
import{Component}from'@angular/core';
@Component({
template:`
<h2>Defaultview!</h2>
`
})
exportclassDefaultComponent{}
[app/profile.component.ts]
import{Component}from'@angular/core';
@Component({
template:`
![Page 426: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/426.jpg)
<h2>Profileview</h2>
Username:<input>
<button>Update</button>
`
})
exportclassProfileComponent{}
Obviously,thisdoesnotdoanythingyet.
ImplementingtheAuthservice
AsdoneintheObservableschapter,youwillimplementaservicethatwillmaintainthestateentirelywithinaBehaviorSubject.
Note
RecallthataBehaviorSubjectwillrebroadcastitslastemittedvaluewheneveranObserverissubscribedtoit.Thismeansitrequiressettingtheinitialstate,butforanauthenticationservicethisiseasy;itcanjuststartintheunauthenticatedstate.
Forthepurposeofthisrecipe,let'sassumethatausernameofnullmeanstheuserisnotauthenticatedandanyotherstringvaluemeanstheyareauthenticated:
[app/auth.service.ts]
import{Injectable}from'@angular/core';
import{BehaviorSubject}from'rxjs/BehaviorSubject';
import{Observable}from'rxjs/Observable';
@Injectable()
exportclassAuthService{
privateauthSubject_:BehaviorSubject<any>=
newBehaviorSubject(null);
usernameEmitter:Observable<string>;
constructor(){
this.usernameEmitter=this.authSubject_.asObservable();
this.logout();
}
login(username:string):void{
this.setAuthState_(username);
}
logout():void{
this.setAuthState_(null);
}
privatesetAuthState_(username:string):void{
this.authSubject_.next(username);
}
![Page 427: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/427.jpg)
}
Notethatnowherearewestoringtheusernameasastring.ThestateoftheauthenticationlivesentirelywithinBehaviorSubject.
Wiringuptheprofileview
Next,makethisserviceavailabletotheentireapplicationandwireuptheprofileview:
[app/app.module.ts]
import{NgModule}from'@angular/core';
import{BrowserModule}from'@angular/platform-browser';
import{RouterModule,Routes}from'@angular/router';
import{RootComponent}from'./root.component';
import{DefaultComponent}from'./default.component';
import{ProfileComponent}from'./profile.component';
import{AuthService}from'./auth.service';
constappRoutes:Routes=[
{path:'profile',component:ProfileComponent},
{path:'**',component:DefaultComponent}
];
@NgModule({
imports:[
BrowserModule,
RouterModule.forRoot(appRoutes)
],
declarations:[
DefaultComponent,
ProfileComponent,
RootComponent
],
providers:[
AuthService
],
bootstrap:[
RootComponent
]
})
exportclassAppModule{}
[app/profile.component.ts]
import{Component}from'@angular/core';
import{AuthService}from'./auth.service';
import{Observable}from'rxjs/Observable';
@Component({
template:`
<h2>Profileview</h2>
Username:<input#unvalue="{{username|async}}">
<button(click)=update(un.value)>Update</button>
![Page 428: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/428.jpg)
`
})
exportclassProfileComponent{
username:Observable<string>;
constructor(privateauthService_:AuthService){
this.username=authService_.usernameEmitter;
}
update(username:string):void{
this.authService_.login(username);
}
}
Tip
It'sveryhandytousetheasyncpipewheninterpolatingvalues.Recallthatwhenyouinvokesubscribe()onaserviceObservablefrominsideaninstantiatedviewcomponent,youmustinvokeunsubscribe()ontheSubscriptionwhenthecomponentisdestroyed;otherwise,yourapplicationwillhavealeakedlistener.MakingtheObservableavailabletotheviewsavesyouthistrouble!
Withtheprofileviewwiredup,addlinksandinterpolatetheusernameintotherootappviewinanavbar,togiveyourselftheabilitytonavigatearound.Youdon'thavetorevisitthefile;justaddallthelinksyou'llneedinthisrecipenow:
[app/root.component.ts]
import{Component}from'@angular/core';
import{Router}from'@angular/router';
import{AuthService}from'./auth.service';
import{Observable}from'rxjs/Observable';
@Component({
selector:'root',
template:`
<h3*ngIf="!!(username|async)">
Hello,{{username|async}}.
</h3>
<a[routerLink]="['']">Default</a>
<a[routerLink]="['profile']">Profile</a>
<a*ngIf="!!(username|async)"
[routerLink]="['login']">Login</a>
<a*ngIf="!!(username|async)"
[routerLink]="['logout']">Logout</a>
<router-outlet></router-outlet>
`
})
exportclassRootComponent{
username:Observable<string>;
![Page 429: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/429.jpg)
constructor(privateauthService_:AuthService){
this.username=authService_.usernameEmitter;
}
}
Tip
Forconsistency,hereyouareusingtheasyncpipetomakethecomponentdefinitionsimpler.However,sinceyouhavefourinstancesinthetemplatereferencingthesameObservable,itmightbebetterdowntheroadtoinsteadsetonesubscribertoObservable,bindittoastringmemberinRootComponent,andinterpolatethisinstead.Angular'sdatabindingmakesthiseasyforyou,butyouwouldstillneedtoderegisterthesubscriberwhenthisisdestroyed.However,sinceitistheapplication'srootcomponent,youshouldn'treallyexpectthistohappen.
Restrictingrouteaccesswithrouteguards
Sofarsogood,butyouwillnoticethattheprofileviewisallowingtheusertoeffectivelyloginwilly-nilly.Youwouldinsteadliketorestrictaccesstothisviewandonlyallowtheusertovisititwhentheyarealreadyauthenticated.
Angulargivesyoutheabilitytoexecutecode,inspecttheroute,andredirectitasnecessarybeforethenavigationoccursusingaRouteGuard.
Note
Guardisabitofamisleadingtermhere.YoushouldthinkofthisfeatureasarouteshimthatletsyouaddlogicthatexecutesbeforeAngularactuallygoestothenewroute.Itcanindeed"Guard"aroutefromanunauthenticateduser,butitcanalsojustaseasilyconditionallyredirect,savethecurrentURL,orperformothertasks.
SincetheRouteGuardneedstohavethe@Injectabledecorator,itmakesgoodsensetotreatitasaservicetype.
StartoffwiththeskeletonAuthGuardServicedefinedinsideanewfileforrouteguards:
[app/route-guards.service.ts]
import{Injectable}from'@angular/core';
import{CanActivate}from'@angular/router';
@Injectable()
exportclassAuthGuardServiceimplementsCanActivate{
constructor(){}
canActivate(){
//Thismethodisinvokedduringroutechangesifthis
//classislistedintheRoutes
![Page 430: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/430.jpg)
}
}
Beforehavingthisdoanything,importthemoduleandaddittoRoutes:
[app/app.module.ts]
import{NgModule}from'@angular/core';
import{BrowserModule}from'@angular/platform-browser';
import{RouterModule,Routes}from'@angular/router';
import{RootComponent}from'./root.component';
import{DefaultComponent}from'./default.component';
import{ProfileComponent}from'./profile.component';
import{AuthService}from'./auth.service';
import{AuthGuardService}from'./route-guards.service';
constappRoutes:Routes=[
{
path:'profile',
component:ProfileComponent,
canActivate:[AuthGuardService]
},
{
path:'**',
component:DefaultComponent
}
];
@NgModule({
imports:[
BrowserModule,
RouterModule.forRoot(appRoutes)
],
declarations:[
DefaultComponent,
ProfileComponent,
RootComponent
],
providers:[
AuthService,
AuthGuardService
],
bootstrap:[
RootComponent
]
})
exportclassAppModule{}
Now,eachtimetheapplicationmatchesaroutetoaprofileandtriestonavigatethere,thecanActivatemethoddefinedinsideAuthGuardServicewillbecalled.Thereturnvalueoftruemeansthenavigationcanoccur;thereturnvalueoffalsemeansthenavigationiscancelled.
![Page 431: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/431.jpg)
Tip
canActivatecaneitherreturnabooleanoranObservable<boolean>.Beaware,shouldyoureturnObservable,theapplicationwilldutifullywaitfortheObservabletoemitavalueandcompleteitbeforenavigating.
Sincetheapplication'sauthenticationstatelivesinsideBehaviorSubject,allthismethodneedstodoissubscribe,checktheusername,andnavigateifitisnotnull.ItsuitsthistoreturnObservable<boolean>:
[app/route-guards.service.ts]
import{Injectable}from'@angular/core';
import{CanActivate,Router}from'@angular/router';
import{AuthService}from'./auth.service';
import{Observable}from'rxjs/Observable';
@Injectable()
exportclassAuthGuardServiceimplementsCanActivate{
constructor(privateauthService_:AuthService,
privaterouter_:Router){}
canActivate():Observable<boolean>{
returnthis.authService_.usernameEmitter.map(username=>{
if(!username){
this.router_.navigate(['login']);
}else{
returntrue;
}
});
}
}
Onceyouimplementthis,youwillnoticethatthenavigationwillneveroccur,eventhoughtheserviceisemittingtheusernamecorrectly.ThisisbecausetherecipientofthereturnvalueofcanActivateisn'tjustwaitingforanObservableemission;itiswaitingfortheObservabletocomplete.SinceyoujustwanttopeekattheusernamevalueinsideBehaviorSubject,youcanjustreturnanewObservablethatreturnsonevalueandtheniscompletedusingtake():
[app/route-guards.service.ts]
import{Injectable}from'@angular/core';
import{CanActivate,Router}from'@angular/router';
import{AuthService}from'./auth.service';
import{Observable}from'rxjs/Observable';
import'rxjs/add/operator/take';
@Injectable()
exportclassAuthGuardServiceimplementsCanActivate{
constructor(privateauthService_:AuthService,
privaterouter_:Router){}
![Page 432: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/432.jpg)
canActivate():Observable<Boolean>{
returnthis.authService_.usernameEmitter.map(username=>{
if(!username){
this.router_.navigate(['login']);
}else{
returntrue;
}
}).take(1);
}
}
Superb!However,thisapplicationstilllacksamethodtoformallyloginandlogout.
Addingloginbehavior
Sincetheloginpagewillneeditsownview,itshouldgetitsownrouteandcomponent.Oncetheuserlogsin,thereisnoneedtokeepthemontheloginpage,soyouwanttoredirectthemtothedefaultviewoncetheyaredone.
First,createthelogincomponentanditscorrespondingview:
[app/login.component.ts]
import{Component}from'@angular/core';
import{Router}from'@angular/router';
import{AuthService}from'./auth.service';
@Component({
template:`
<h2>Loginview</h2>
<input#un>
<button(click)="login(un.value)">Login</button>
`
})
exportclassLoginComponent{
constructor(privateauthService_:AuthService,
privaterouter_:Router){}
login(newUsername:string):void{
this.authService_.login(newUsername);
this.authService_.usernameEmitter
.subscribe(username=>{
if(!!username){
this.router_.navigate(['']);
}
});
}
}
[app/app.module.ts]
import{NgModule}from'@angular/core';
![Page 433: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/433.jpg)
import{BrowserModule}from'@angular/platform-browser';
import{RouterModule,Routes}from'@angular/router';
import{RootComponent}from'./root.component';
import{DefaultComponent}from'./default.component';
import{ProfileComponent}from'./profile.component';
import{LoginComponent}from'./login.component';
import{AuthService}from'./auth.service';
import{AuthGuardService}from'./route-guards.service';
constappRoutes:Routes=[
{
path:'login',
component:LoginComponent
},
{
path:'profile',
component:ProfileComponent,
canActivate:[AuthGuardService]
},
{
path:'**',
component:DefaultComponent
}
];
@NgModule({
imports:[
BrowserModule,
RouterModule.forRoot(appRoutes)
],
declarations:[
LoginComponent,
DefaultComponent,
ProfileComponent,
RootComponent
],
providers:[
AuthService,
AuthGuardService
],
bootstrap:[
RootComponent
]
})
exportclassAppModule{}
Youshouldnowbeabletologin.Thisisallwellandgood,butyouwillnoticethatwiththisimplemented,updatingtheusernameintheprofileviewwillnavigatetothedefaultview,exhibitingthesamebehaviordefinedinthelogincomponent.ThisisbecausethesubscriberisstilllisteningtoAuthServiceObservable.YouneedtoaddinanOnDestroymethodtocorrectlyteardowntheloginview:
![Page 434: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/434.jpg)
[app/login.component.ts]
import{Component,ngOnDestroy}from'@angular/core';
import{Router}from'@angular/router';
import{AuthService}from'./auth.service';
import{Subscription}from'rxjs/Subscription';
@Component({
template:`
<h2>Loginview</h2>
<input#un>
<button(click)="login(un.value)">Login</button>
`
})
exportclassLoginComponentimplementsOnDestroy{
privateusernameSubscription_:Subscription;
constructor(privateauthService_:AuthService,
privaterouter_:Router){}
login(newUsername:string):void{
this.authService_.login(newUsername);
this.usernameSubscription_=this.authService_
.usernameEmitter
.subscribe(username=>{
if(!!username){
this.router_.navigate(['']);
}
});
}
ngOnDestroy(){
//Onlyinvokeunsubscribe()ifthisexists
this.usernameSubscription_&&
this.usernameSubscription_.unsubscribe();
}
}
Addingthelogoutbehavior
Finally,youwanttoaddawayforuserstologout.Thiscanbeaccomplishedinanumberofways,butagoodimplementationwillbeabletodelegatethelogoutbehaviortoitsassociatedmethodswithoutintroducingtoomuchboilerplatecode.
Ideally,youwouldlikefortheapplicationtojustbeabletonavigatetothelogoutrouteandletAngularhandletherest.This,too,canbeaccomplishedwithcanActivate.First,defineanewRouteGuard:
[app/route-guards.service.ts]
import{Injectable}from'@angular/core';
import{CanActivate,Router}from'@angular/router';
![Page 435: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/435.jpg)
import{AuthService}from'./auth.service';
import{Observable}from'rxjs/Observable';
import'rxjs/add/operator/take';
@Injectable()
exportclassAuthGuardServiceimplementsCanActivate{
constructor(privateauthService_:AuthService,
privaterouter_:Router){}
canActivate():Observable<boolean>{
returnthis.authService_.usernameEmitter.map(username=>{
if(!username){
this.router_.navigate(['login']);
}else{
returntrue;
}
}).take(1);
}
}
@Injectable()
exportclassLogoutGuardServiceimplementsCanActivate{
constructor(privateauthService_:AuthService,
privaterouter_:Router){}
canActivate():boolean{
this.authService_.logout();
this.router_.navigate(['']);
returntrue;
}
}
Thisbehaviorshouldbeprettyself-explanatory.
Tip
YourcanActivatemethodmustmatchthesignaturedefinedintheCanActivateinterface,soeventhoughitwillalwaysnavigatetoanewview,youshouldaddareturnvaluetopleasethecompilerandtohandleanycaseswheretheprecedingcodeshouldfallthrough.
Next,addthelogoutcomponentandtheroute.Thelogoutcomponentwillneverberendered,buttheroutedefinitionrequiresthatitismappedtoavalidcomponent.SoLogoutComponentwillconsistofadummyclass:
[app/logout.component.ts}
import{Component}from'@angular/core';
@Component({
template:''
})
![Page 436: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/436.jpg)
exportclassLogoutComponent{}
[app/app.module.ts]
import{NgModule}from'@angular/core';
import{BrowserModule}from'@angular/platform-browser';
import{RouterModule,Routes}from'@angular/router';
import{RootComponent}from'./root.component';
import{DefaultComponent}from'./default.component';
import{ProfileComponent}from'./profile.component';
import{LoginComponent}from'./login.component';
import{LogoutComponent}from'./logout.component';
import{AuthService}from'./auth.service';
import{AuthGuardService,LogoutGuardService}
from'./route-guards.service';
constappRoutes:Routes=[
{
path:'login',
component:LoginComponent
},
{
path:'logout',
component:LogoutComponent,
canActivate:[LogoutGuardService]
},
{
path:'profile',
component:ProfileComponent,
canActivate:[AuthGuardService]
},
{
path:'**',
component:DefaultComponent
}
];
@NgModule({
imports:[
BrowserModule,
RouterModule.forRoot(appRoutes)
],
declarations:[
LoginComponent,
LogoutComponent,
DefaultComponent,
ProfileComponent,
RootComponent
],
providers:[
AuthService,
AuthGuardService,
LogoutGuardService
],
![Page 437: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/437.jpg)
bootstrap:[
RootComponent
]
})
exportclassAppModule{}
Withthis,youshouldhaveafullyfunctionallogin/logoutbehaviorprocess.
![Page 438: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/438.jpg)
Howitworks...ThecoreofthisimplementationisbuiltaroundObservablesandRouteGuards.ObservablesallowyourAuthServicemoduletomaintainthestateandexposeitsimultaneouslythroughBehaviorSubject,andRouteGuardsallowyoutoconditionallynavigateandredirectatyourapplication'sdiscretion.
![Page 439: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/439.jpg)
There'smore...Applicationsecurityisabroadandinvolvedsubject.Therecipeshownhereinvolveshowtosmoothlymoveyouruseraroundtheapplication,butitisbynomeansarigoroussecuritymodel.
Theactualauthentication
Youshouldalwaysassumetheclientcanmanipulateitsownexecutionenvironment.Inthisexample,evenifyouprotectthelogin/logoutmethodsonAuthServiceaswellasyoucan,itwillbeeasyfortheusertogainaccesstothesemethodsandauthenticatethemselves.
Userinterfaces,whichAngularapplicationssquarelyfallinto,arenotmeanttobesecure.Securityresponsibilitiesfallontheserversideoftheclient/servermodelsincetheuserdoesnotcontrolthatexecutionenvironment.Inanactualapplication,thelogin()methodherewouldmakeanetworkrequestgetsomesortofatokenfromtheserver.Twoverypopularimplementations,JSONWebTokensandCookieauth,dothisindifferentways,buttheyareessentiallyvariationsofthesametheme.Angularorthebrowserwillstoreandsendthesetokens,butultimatelytheservershouldactasthegatekeeperofsecureinformation.
Securedataandviews
Anysecureinformationyoumightsendtotheclientshouldbebehindserver-basedauthentication.Formanydevelopers,thisisanobviousfact,especiallywhendealingwithanAPI.However,Angularalsorequeststemplatesandstaticfilesfromtheserver,andsomeoftheseyoumightnotwanttoservetothewrongpeople.Inthiscase,youwillneedtoconfigureyourservertoauthenticaterequestsforthesestaticfilesbeforeyouservethemtotheclient.
![Page 440: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/440.jpg)
SeealsoNavigatingwiththeRouterserviceusesanAngularservicetonavigatearoundanapplicationBuildingstatefulRouterLinkbehaviorwithRouterLinkActiveshowshowtointegrateapplicationbehaviorwithaURLstateImplementingnestedviewswithrouteparametersandchildroutesgivesanexampleofhowtoconfigureAngularURLstosupportnestinganddatapassingWorkingwithmatrixURLparametersandroutingarraysdemonstratesAngular'sbuilt-inmatrixURLsupport
![Page 441: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/441.jpg)
Chapter7.Services,DependencyInjection,andNgModuleThischapterwillcoverthefollowingrecipes:
InjectingasimpleserviceintoacomponentControllingserviceinstancecreationandinjectionwithNgModuleServiceinjectionaliasingwithuseClassanduseExistingInjectingavalueasaservicewithuseValueandOpaqueTokensBuildingaprovider-configuredservicewithuseFactory
![Page 442: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/442.jpg)
IntroductionAngular1gaveyouahodgepodgeofdifferentservicetypes.Manyofthemhadagreatdealofoverlap.Manyofthemwereconfusing.Andallofthemweresingletons.
Angular2hastotallythrownawaythisconcept.Initsplace,thereisashinynewdependencyinjectionsystemthatisfarmoreextensibleandsensiblethanitspredecessor.Itallowsyoutohaveatomicandnon-atomicservicetypes,aliasing,factories,andallkindsofincrediblyusefultoolsforuseinyourapplication.
Ifyouarelookingtouseservicesmuchinthesamewayasearlier,youwillfindthatyourunderstandingofservicetypeswilleasilycarryovertothenewsystem.Butfordeveloperswhowantmoreoutoftheirapplications,thenewworldofdependencyinjectionisincrediblypowerfulandobviouslybuiltforapplicationsthatcanscale.
![Page 443: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/443.jpg)
InjectingasimpleserviceintoacomponentThemostcommonusecasewillbeforacomponenttodirectlyinjectaserviceintoitself.Althoughtherhythmsofdefiningservicetypesandusingdependencyinjectionremainmostlythesame,it'simportanttogetagoodholdofthefundamentalsofAngular2'sdependencyinjectionschema,asitdiffersinseveralimportantways.
Note
Thecode,links,andaliveexampleofthisareavailableathttp://ngcookbook.herokuapp.com/4263.
![Page 444: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/444.jpg)
GettingreadySupposeyouhadthefollowingskeletonapplication:
[app/root.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'root',
template:`
<h1>rootcomponent!</h1>
<button(click)="fillArticle()">Showarticle</button>
<h2>{{title}}</h2>
`
})
exportclassRootComponent{
title:string;
constructor(){}
fillArticle(){}
}
Yourobjectiveistoimplementaservicethatcanbeinjectedintothiscomponentandreturnanarticletitletofillthetemplate.
![Page 445: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/445.jpg)
Howtodoit...Asyoumightexpect,servicesinAngular2arerepresentedasclasses.Similartocomponents,servicesaredesignatedassuchwithan@Injectabledecorator.Createthisserviceinitsownfile:
[app/article.service.ts]
import{Injectable}from'@angular/core';
@Injectable()
exportclassArticleService{
privatetitle_:string=`
CFOYodelsQuarterlyEarningsCall,StockSkyrockets
`
}
Thisservicehasaprivatetitlethatyouneedtotransfertothecomponent,butfirstyoumustmaketheserviceitselfavailabletothecomponent.Thiscanbedonebyimportingtheservice,thenlistingitintheproviderspropertyoftheapplicationmodule:
[app/app.module.ts]
import{NgModule}from'@angular/core';
import{BrowserModule}from'@angular/platform-browser';
import{RootComponent}from'./root.component';
import{ArticleService}from'./article.service';
@NgModule({
imports:[
BrowserModule
],
declarations:[
RootComponent
],
providers:[
ArticleService
],
bootstrap:[
RootComponent
]
})
exportclassAppModule{}
Nowthattheservicecanbeprovided,injectitintothecomponent:
[app/root.component.ts]
import{Component}from'@angular/core';
import{ArticleService}from'./article.service';
@Component({
![Page 446: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/446.jpg)
selector:'root',
template:`
<h1>rootcomponent!</h1>
<button(click)="fillArticle()">Showarticle</button>
<h2>{{title}}</h2>
`
})
exportclassRootComponent{
title:string;
constructor(privatearticleService_:ArticleService){}
fillArticle(){}
}
ThisnewcodewillcreateanewinstanceofArticleServicewhenRootComponentisinstantiated,andtheninjectitintotheconstructor.Anythinginjectedintoacomponentwillbeavailableasacomponentinstancemember,whichyoucanusetoconnectaservicemethodtoacomponentmethod:
[app/article.service.ts]
import{Injectable}from'@angular/core';
@Injectable()
exportclassArticleService{
privatetitle_:string=`
CFOYodelsQuarterlyEarningsCall,StockSkyrockets
`;
getTitle(){
returnthis.title_;
}
}
[app/root.component.ts]
import{Component}from'@angular/core';
import{ArticleService}from'./article.service';
@Component({
selector:'root',
template:`
<h1>rootcomponent!</h1>
<button(click)="fillArticle()">Showarticle</button>
<h2>{{title}}</h2>
`
})
exportclassRootComponent{
title:string;
constructor(privatearticleService_:ArticleService){}
fillArticle():void{
![Page 447: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/447.jpg)
this.title=this.articleService_.getTitle();
}
}
![Page 448: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/448.jpg)
Howitworks...Withoutthedecorator,theserviceyouhavejustbuiltisratherplainincomposition.Withthe@Injectable()decoration,theclassisdesignatedtotheAngularframeworkasonethatwillbeinjectedelsewhere.
Note
Designationasaninjectablehasanumberofconsiderationsthatareimportantlydistinctfromjustbeingpassedinparametrically.Whenistheinjectedclassinstantiated?Howisitlinkedtothecomponentinstance?Howareglobalandlocalinstancescontrolled?Thesearealldiscussedinthemoreadvancedrecipesinthischapter.
Designationasaninjectableserviceisonlyonepieceofthepuzzle.Thecomponentneedstobeinformedoftheexistenceoftheservice.Youmustfirstimporttheserviceclassintothecomponentmodule,butthisaloneisnotsufficient.Recallthatthesyntaxusedtoinjectaservicewassimplyawaytolistitasaconstructorparameter.Behindthescenes,Angularissmartenoughtorecognizethatthesecomponentargumentsaretobeinjected,butitrequiresthefinalpiecetoconnecttheimportedmoduletoitsplaceasaninjectedresource.
ThisfinalpiecetakestheformoftheproviderspropertyoftheNgModuledefinition.Forthepurposeofthisrecipe,itisn'timportantthatyouknowthedetailsoftheproperty.Inshort,thisarraydesignatesthearticleServiceconstructorparameterasaninjectableandidentifiesthatArticleServiceshouldbeinjectedintotheconstructor.
![Page 449: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/449.jpg)
There'smore...It'simportanttoacknowledgeherehowtheTypeScriptdecoratorshelpthedependencyinjectionsetup.Decoratorsdonotmodifyaninstanceofaclass;rather,theymodifytheclassdefinition.TheNgModulecontainingtheproviderslistwillbeinitializedpriortoanyinstanceoftheactualcomponentbeinginstantiated.Thus,Angularwillbeawareofalltheservicesthatyoumightwanttoinjectintotheconstructor.
![Page 450: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/450.jpg)
SeealsoControllingserviceinstancecreationandinjectionwithNgModulegivesabroadoverviewofhowAngular2architectsproviderhierarchiesusingmodules
![Page 451: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/451.jpg)
ControllingserviceinstancecreationandinjectionwithNgModuleInastarkdeparturefromAngular1.x,Angular2featuresahierarchicalinjectionscheme.Thishasasubstantialnumberofimplications,andoneofthemoreprominentoneistheabilitytocontrolwhen,andhowmany,servicesarecreated.
Note
Thecode,links,andaliveexampleofthisareavailableathttp://ngcookbook.herokuapp.com/2102/.
![Page 452: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/452.jpg)
GettingreadySupposeyoubeginwiththefollowingsimpleapplication:
[app/root.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'root',
template:`
<h1>rootcomponent!</h1>
<article></article>
<article></article>
`
})
exportclassRootComponent{}
[app/article.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'article',
template:`
<p>Articlecomponent!</p>
`
})
exportclassArticleComponent{}
[app/article.service.ts]
import{Injectable}from'@angular/core';
@Injectable()
exportclassArticleService{
constructor(){
console.log('ArticleServiceconstructor!');
}
}
[app/app.module.ts]
import{NgModule}from'@angular/core';
import{BrowserModule}from'@angular/platform-browser';
import{RootComponent}from'./root.component';
import{ArticleComponent}from'./article.component;
@NgModule({
imports:[
BrowserModule,
],
declarations:[
RootComponent,
ArticleComponent
![Page 453: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/453.jpg)
],
bootstrap:[
RootComponent
]
})
exportclassAppModule{}
YourobjectiveistoinjectasingleinstanceofArticleServiceintothetwochildcomponents.Inthisrecipe,console.loginsidetheArticleServiceconstructorallowsyoutoseewhenoneisinstantiated.
![Page 454: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/454.jpg)
Howtodoit...BeginbyimportingtheserviceintoAppModule,thenprovidingitwiththefollowing:
[app/app.module.ts]
import{NgModule}from'@angular/core';
import{BrowserModule}from'@angular/platform-browser';
import{RootComponent}from'./root.component';
import{ArticleComponent}from'./article.component;
import{ArticleService}from'./article.service;
@NgModule({
imports:[
BrowserModule,
],
declarations:[
RootComponent,
ArticleComponent
],
providers:[
ArticleService
]
bootstrap:[
RootComponent
]
})
exportclassAppModule{}
SinceArticleServiceisprovidedinthesamemodulewhereArticleComponentisdeclared,youarenowabletoinjectArticleServiceintothechildArticleComponentinstances:
[app/article/article.component.ts]
import{Component}from'@angular/core';
import{ArticleService}from'./article.service';
@Component({
selector:'article',
template:`
<p>Articlecomponent!</p>
`
})
exportclassArticleComponent{
constructor(privatearticleService_:ArticleService){}
}
Withthis,youwillfindthatthesameserviceinstanceisinjectedintoboththechildcomponentsastheArticleServiceconstructor,namelyconsole.log,isonlyexecutedonce.
![Page 455: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/455.jpg)
Splittinguptherootmodule
Astheapplicationgrows,itwillmakelessandlesssensetocrameverythingintothesametop-levelmodule.Instead,itwouldbeidealforyoutobreakapartmodulesintochunksthatmakesense.Inthecaseofthisrecipe,itwouldbepreferabletoprovideArticleServicetotheapplicationpiecesthatareactuallygoingtoinjectit.
DefineanewArticleModuleandmovetherelevantmoduleimportsintothatfileinstead:
[app/article.module.ts]
import{NgModule}from'@angular/core';
import{ArticleComponent}from'./article.component';
import{ArticleService}from'./article.service';
@NgModule({
declarations:[
ArticleComponent
],
providers:[
ArticleService
],
bootstrap:[
ArticleComponent
]
})
exportclassArticleModule{}
Then,importthisentiremoduleintoAppModuleinstead:
[app/app.module.ts]
import{NgModule}from'@angular/core';
import{BrowserModule}from'@angular/platform-browser';
import{RootComponent}from'./root.component';
import{ArticleModule}from'./article.module';
@NgModule({
imports:[
BrowserModule,
ArticleModule
],
declarations:[
RootComponent
],
bootstrap:[
RootComponent
]
})
exportclassAppModule{}
![Page 456: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/456.jpg)
Ifyoustophere,you'llfindthattherearenoerrors,butAppModuleisn'tabletorenderArticleComponent.ThisisbecauseAngularmodules,likeothermodulesystems,needtoexplicitlydefinewhatisbeingexportedtoothermodules:
[app/article.module.ts]
import{NgModule}from'@angular/core';
import{ArticleComponent}from'./article.component';
import{ArticleService}from'./article.service';
@NgModule({
declarations:[
ArticleComponent
],
providers:[
ArticleService
],
bootstrap:[
ArticleComponent
],
exports:[
ArticleComponent
]
})
exportclassArticleModule{}
Withthis,youwillstillseethatArticleServiceisinstantiatedonce.
![Page 457: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/457.jpg)
Howitworks...Angular2'sdependencyinjectiontakesadvantageofitshierarchystructurewhenprovidingandinjectingservices.Fromwhereaserviceisinjected,Angularwillinstantiateaservicewhereveritisprovided.Insideamoduledefinition,thiswillonlyeverhappenonce.
Inthiscase,youprovidedArticleServicetobothAppModuleandArticleModule.Eventhoughtheserviceisinjectedtwice(onceforeachArticleComponent),Angularusestheprovidersdeclarationtodecidewhentocreatetheservice.
![Page 458: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/458.jpg)
There'smore...Atthispoint,acuriousdevelopershouldhavelotsofquestionsabouthowexactlythisinjectionschemabehaves.Therearenumerousdifferentconfigurationflavorsthatcanbeusefultothedeveloper,andtheseconfigurationsonlyrequireaminorcodeadjustmentfromtheprecedingresult.
Injectingdifferentserviceinstancesintodifferentcomponents
Asyoumightanticipatefromtheprecedingexplanation,youcanreconfigurethisapplicationtoinjectadifferentArticleServiceinstanceintoeachchild,twointotal.ThiscanbedonebymigratingtheprovidersdeclarationoutofthemoduledefinitionandintotheArticleComponentdefinition:
[app/article.module.ts]
import{NgModule}from'@angular/core';
import{ArticleComponent}from'./article.component';
@NgModule({
declarations:[
ArticleComponent
],
bootstrap:[
ArticleComponent
],
exports:[
ArticleComponent
]
})
exportclassArticleModule{}
[app/article.component.ts]
import{Component}from'@angular/core';
import{ArticleService}from'./article.service';
@Component({
selector:'article',
template:`
<p>Articlecomponent!</p>
`,
providers:[
ArticleService
]
})
exportclassArticleComponent{
constructor(privatearticleService_:ArticleService){}
}
Youcanverifythattwoinstancesarebeingcreatedbyobservingthetwoconsole.logstatements
![Page 459: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/459.jpg)
calledfromtheArticleServiceconstructor.
Serviceinstantiation
Thelocationoftheprovidersalsomeansthatserviceinstanceinstantiationisboundtothelifetimeofthecomponent.Forthisapplication,thismeansthatwheneveracomponentiscreated,ifaserviceisprovidedinsidethatcomponentdefinition,anewserviceinstancewillbecreated.
Forexample,ifyouweretotoggletheexistenceofachildcomponentwithArticleServiceprovidedinsideit,itwillcreateanewArticleServiceeverytimeArticleComponentisconstructed:
[app/root.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'root',
template:`
<h1>rootcomponent!</h1>
<button(click)="toggle=!toggle">Toggle</button>
<article></article>
<article*ngIf="toggle"></article>
`
})
exportclassRootComponent{}
YoucanverifythatnewinstancesarebeingcreatedeachtimengIfevaluatestotruebyobservingadditionalconsole.logstatementscalledfromtheArticleServiceconstructor.
![Page 460: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/460.jpg)
SeealsoInjectingasimpleserviceintoacomponentwalksyouthroughthebasicsofAngular2'sdependencyinjectionschemaServiceinjectionaliasingwithuseClassanduseExistingdemonstrateshowtointerceptdependencyinjectionproviderrequests
![Page 461: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/461.jpg)
ServiceinjectionaliasingwithuseClassanduseExistingAsyourapplicationbecomesmorecomplex,youmaycometoasituationwhereyouwouldliketouseyourservicesinapolymorphicstyle.Morespecifically,someplacesinyourapplicationmaywanttorequestServiceA,butaconfigurationsomewhereinyourapplicationwillactuallygiveitServiceB.Thisrecipewilldemonstrateonewayinwhichthiscanbeuseful,butthisbehaviorallowsyourapplicationtobemoreextensibleinmultipleways.
Note
Thecode,links,andaliveexampleofthisareavailableathttp://ngcookbook.herokuapp.com/1109/.
![Page 462: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/462.jpg)
GettingreadySupposeyoubeginwiththefollowingskeletonapplication.
Dualservices
Youbeginwithtwoservices,ArticleServiceandEditorArticleService,andtheirsharedinterface,ArticleSourceInterface.EditorArticleServiceinheritsfromArticleService:
[app/article-source.interface.ts]
exportinterfaceArticleSourceInterface{
getArticle():Article
}
exportinterfaceArticle{
title:string,
body:string,
//?denotesanoptionalproperty
notes?:string
}
[app/article.service.ts]
import{Injectable}from'@angular/core';
import{Article,ArticleSourceInterface}
from'./article-source.interface';
@Injectable()
exportclassArticleServiceimplementsArticleSourceInterface{
privatetitle_:string=
"ResearchersDetermineHamSandwichNotTuringComplete";
privatebody_:string=
"Computersciencecommunityremainsskeptical";
getArticle():Article{
return{
title:this.title_,
body:this.body_
};
}
}
[app/editor-article.service.ts]
import{Injectable}from'@angular/core';
import{ArticleService}from'./article.service';
import{Article,ArticleSourceInterface}
from'./article-source.interface';
@Injectable()
exportclassEditorArticleServiceextendsArticleService
implementsArticleSourceInterface{
privatenotes_:string="Swingandamiss!";
![Page 463: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/463.jpg)
constructor(){
super();
}
getArticle():Article{
//Combineobjectsandreturnthejoinedobject
returnObject.assign(
{},
super.getArticle(),
{
notes:this.notes_
});
}
}
Aunifiedcomponent
Yourobjectiveistobeabletousethefollowingcomponentsothatboththeseservicescanbeinjectedintothefollowingcomponent:
[app/article.component.ts]
import{Component}from'@angular/core';
import{ArticleService}from'./article.service';
import{Article}from'./article-source.interface';
@Component({
selector:'article',
template:`
<h2>{{article.title}}</h2>
<p>{{article.body}}</p>
<p*ngIf="article.notes">
<i>Notes:{{article.notes}}</i>
</p>
`
})
exportclassArticleComponent{
article:Article;
constructor(privatearticleService_:ArticleService){
this.article=articleService.getArticle();
}
}
![Page 464: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/464.jpg)
Howtodoit...Whenlistingproviders,Angular2allowsyoutodeclareanaliasedreferencethatspecifieswhatserviceshouldactuallybeprovidedwhenoneofthecertaintypesisrequested.SinceAngular2injectionwillfollowthecomponenttreeupwardstofindtheprovider,onewaytodeclarethisaliasisbywrappingthecomponentwithaparentcomponentthatwillspecifythisalias:
[app/default-view.component.ts]
import{Component}from'@angular/core';
import{ArticleService}from'./article.service';
@Component({
selector:'default-view',
template:`
<h3>Defaultview</h3>
<ng-content></ng-content>
`,
providers:[ArticleService]
})
exportclassDefaultViewComponent{}
[app/editor-view.component.ts]
import{Component}from'@angular/core';
import{ArticleService}from'./article.service';
import{EditorArticleService}from'./editor-article.service';
@Component({
selector:'editor-view',
template:`
<h3>Editorview</h3>
<ng-content></ng-content>
`,
providers:[
{provide:ArticleService,useClass:EditorArticleService}
]
})
exportclassEditorViewComponent{}
Note
Notethatboththeseclassesareactingaspassthroughcomponents.Otherthanaddingaheader(whichismerelyforlearningthepurposeofinstructioninthisrecipe),theseclassesareonlyspecifyingaproviderandareunconcernedwiththeircontent.
Withthewrapperclassesdefined,youcannowaddthemtotheapplicationmodule,thenusethemtocreatetwoinstancesofArticleComponent:
[app/app.module.ts]
![Page 465: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/465.jpg)
import{NgModule}from'@angular/core';
import{BrowserModule}from'@angular/platform-browser';
import{RootComponent}from'./root.component';
import{ArticleComponent}from'./article.component';
import{DefaultViewComponent}from'./default-view.component';
import{EditorViewComponent}from'./editor-view.component';
import{ArticleComponent}from'./article.component';
import{ArticleService}from'./article.service';
import{EditorArticleService}from'./editor-article.service';
@NgModule({
imports:[
BrowserModule
],
declarations:[
RootComponent,
ArticleComponent,
DefaultViewComponent,
EditorViewComponent
],
bootstrap:[
RootComponent
]
})
exportclassAppModule{}
[app/root.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'root',
template:`
<default-view>
<article></article>
</default-view>
<hr/>
<editor-view>
<article></article>
</editor-view>
`
})
exportclassRootComponent{}
Withthis,youshouldnowseethattheeditorversionofArticleComponentgetsthenotes,butthedefaultversiondoesnot.
![Page 466: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/466.jpg)
Howitworks...InAngular1,theservicetypethatwassupposedtobeinjectedwasidentifiedfromafunctionparameterbydoingadirectmatchoftheparametersymbol.function(Article)wouldinjecttheArticleservice,function(User)theUserservice,andsoon.Thisledtonastiness,suchastheminification-proofingofconstructorsbyprovidinganarrayofstringstomatchagainst['Article',function(Article){}].
Thisisnolongerthecase.Whenaproviderisregistered,theuseClassoptionutilizesthetwo-partdependencyinjectionmatchingschemeinAngular2.Thefirstpartistheprovidertoken,whichistheparametertypeoftheservicebeinginjected.Inthiscase,privatearticleService_:ArticleServiceusestheArticleServicetokentorequestthataninstancebeinjected.Angular2takesthisandmatchesthistokenagainstthedeclaredprovidersinthecomponenthierarchy.Whenatokenismatched,Angular2willusethesecondpart,theprovideritself,toinjectaninstanceoftheservice.
Inreality,providers:[ArticleService]isashorthandforproviders:[{provide:ArticleService,useClass:ArticleService}].Theshorthandisusefulsinceyouwillalmostalwaysberequestingtheserviceclassthatwouldmatchtheinjectedclass.However,inthisrecipe,youareconfiguringAngular2torecognizeanArticleServicetokenandsousetheEditorArticleServiceprovider.
![Page 467: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/467.jpg)
There'smore...AnattentivedeveloperwillhaverealizedbythispointthattheutilityofuseClassislimitedinthesensethatitdoesnotallowyoutoindependentlycontrolwheretheactualserviceisprovided.Inotherwords,theplacewhereyouintercepttheproviderdefinitionwithuseClassisalsotheplacewherethereplacementclasswillbeprovided.
Inthisexample,useClassissuitablesinceyouareperfectlyhappytoprovideEditorArticleServiceinthesameplacewhereyouarespecifyingthatitshouldbeusedtoreplaceArticleService.However,itisnotdifficulttoimagineascenarioinwhichyouwouldliketospecifythereplacementservicetypebuthaveitinjectedhigherupinthecomponenttree.This,afterall,wouldallowyoutoreuseinstancesofaserviceinsteadofhavingtocreateanewoneforeachuseClassdeclaration.
Forthispurpose,youcanuseuseExisting.Itrequiresyoutoexplicitlyprovidetheservicetypeseparately,butitwillreusetheprovidedinstanceinsteadofcreatinganewone.Fortheapplicationyoujustcreated,youcannowreconfigureitwithuseExisting,andprovideboththeservicesattheRootComponentlevel.
Todemonstratethatyourreasoningabouttheservicebehavioriscorrect,doublethenumberofArticlecomponents,andaddalogstatementtotheconstructorofArticleServicetoensureyouareonlycreatingoneofeachservice:
[app/root.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'root',
template:`
<default-view>
<article></article>
</default-view>
<editor-view>
<article></article>
</editor-view>
<default-view>
<article></article>
</default-view>
<editor-view>
<article></article>
</editor-view>
`
})
exportclassRootComponent{}
[app/article.service.ts]
![Page 468: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/468.jpg)
import{Injectable}from'@angular/core';
import{Article,ArticleSourceInterface}from'./article-source.interface';
@Injectable()
exportclassArticleServiceimplementsArticleSourceInterface{
privatetitle_:string=
"ResearchersDetermineHamSandwichNotTuringComplete";
privatebody_:string=
"Computersciencecommunityremainsskeptical";
constructor(){
console.log('InstantiatedArticleService!');
}
getArticle():Article{
return{
title:this.title_,
body:this.body_
};
}
}
[app/editor-view.component.ts]
import{Component}from'@angular/core';
import{ArticleService}from'./article.service';
import{EditorArticleService}from'./editor-article.service';
@Component({
selector:'editor-view',
template:`
<h3>Editorview</h3>
<ng-content></ng-content>
`,
providers:[
{provide:ArticleService,useExisting:EditorArticleService}
]
})
exportclassEditorViewComponent{}
[app/default-view.component.ts]
import{Component}from'@angular/core';
import{ArticleService}from'./article.service';
@Component({
selector:'default-view',
template:`
<h3>Defaultview</h3>
<ng-content></ng-content>
`
//providersremoved
})
exportclassDefaultViewComponent{}
![Page 469: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/469.jpg)
Inthisconfiguration,withuseClass,youwillseethatoneinstanceofArticleServiceandtwoinstancesofEditorArticleServicearecreated.WhenreplacedwithuseExisting,youwillfindthatonlyoneinstanceofeachiscreated.
Thus,inthisreconfiguredversionoftherecipe,yourapplicationisdoingthefollowing:
AttheRootComponentlevel,itisprovidingEditorArticleServiceAttheEditorViewComponentlevel,itisredirectingArticleServiceinjectiontokenstoEditorArticleService
AttheArticleComponentlevel,itisinjectingArticleServiceusingtheArticleServicetoken
Refactoringwithdirectiveproviders
Ifthisimplementationseemsclunkyandverbosetoyou,youarecertainlyontosomething.Theintermediatecomponentsareperformingtheirjobsquitewell,butaren'treallydoinganythingotherthanshimminginanintermediateprovider'sdeclaration.Insteadofwrappinginacomponent,youcanmigratetheprovider'sstatementintoadirectiveanddoawaywithboththeviewcomponents:
[app/root.component.ts]
import{Component}from'@angular/core';
import{ArticleComponent}from'./article.component';
import{ArticleService}from'./article.service';
import{EditorArticleService}from'./editor-article.service';
@Component({
selector:'root',
template:`
<article></article>
<articleeditor-view></article>
<article></article>
<articleeditor-view></article>
`
})
exportclassRootComponent{}
[app/editor-view.directive.ts]
import{Directive}from'@angular/core';
import{ArticleService}from'./article.service';
import{EditorArticleService}from'./editor-article.service';
@Directive({
selector:'[editor-view]',
providers:[
{provide:ArticleService,useExisting:EditorArticleService}
]
})
exportclassEditorViewDirective{}
![Page 470: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/470.jpg)
[app/app.module.ts]
import{NgModule}from'@angular/core';
import{BrowserModule}from'@angular/platform-browser';
import{RootComponent}from'./root.component';
import{ArticleComponent}from'./article.component';
import{DefaultViewComponent}from'./default-view.component';
import{EditorViewDirective}from'./editor-view.directive';
import{ArticleComponent}from'./article.component';
import{ArticleService}from'./article.service';
import{EditorArticleService}from'./editor-article.service';
@NgModule({
imports:[
BrowserModule
],
declarations:[
RootComponent,
ArticleComponent,
DefaultViewComponent,
EditorViewDirective
],
providers:[
ArticleService,
EditorArticleService
],
bootstrap:[
RootComponent
]
})
exportclassAppModule{}
Yourapplicationshouldworkjustthesame!
![Page 471: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/471.jpg)
SeealsoInjectingasimpleserviceintoacomponentwalksyouthroughthebasicsofAngular2'sdependencyinjectionschemaControllingserviceinstancecreationandinjectionwithNgModulegivesabroadoverviewofhowAngular2architectsproviderhierarchiesusingmodulesInjectingavalueasaservicewithuseValueandOpaqueTokensshowshowyoucanusedependency-injectedtokenstoinjectgenericobjectsBuildingaprovider-configuredservicewithuseFactorydetailstheprocessofsettingupaservicefactorytocreateconfigurableservicedefinitions
![Page 472: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/472.jpg)
InjectingavalueasaservicewithuseValueandOpaqueTokensInAngular1,therewasabroadselectionofservicetypesyoucoulduseinyourapplication.Asubsetofthesetypesallowedyoutoinjectastaticvalueinsteadofaserviceinstance,andthisusefulabilityiscontinuedinAngular2.
Note
Thecode,links,andaliveexampleofthisareavailableathttp://ngcookbook.herokuapp.com/3032/.
![Page 473: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/473.jpg)
GettingreadyBeginwiththefollowingsimpleapplication:
[app/app.module.ts]
import{NgModule}from'@angular/core';
import{BrowserModule}from'@angular/platform-browser';
import{RootComponent}from'./root.component';
import{ArticleComponent}from'./article.component';
@NgModule({
imports:[
BrowserModule
],
declarations:[
RootComponent,
ArticleComponent
],
bootstrap:[
RootComponent
]
})
exportclassAppModule{}
[app/root.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'root',
template:`
<article></article>
`
})
exportclassRootComponent{}
[app/article.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'article',
template:`
<imgsrc="{{logoUrl}}">
<h2>FoolandHisMoneyReunitedatLast</h2>
<p>Author:JakeHsu</p>
`
})
exportclassArticleComponent{}
![Page 474: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/474.jpg)
Howtodoit...Althoughaformalserviceclassdeclarationand@Injectabledecoratordesignationisnolongernecessaryforinjectingavalue,token/providermappingisstillneeded.Sincethereisnolongeraclassavailablethatcanbeusedtotypetheinjectable,somethingelsewillhavetoactasitsreplacement.
Angular2solvesthisproblemwithOpaqueToken.Thismoduleallowsyoutocreateaclasslesstokenthatcanbeusedtopairtheinjectedvaluewiththeconstructorargument.ThiscanbeusedalongsidetheuseValueprovideoption,whichsimplydirectlyprovideswhateveritscontentsareasinjectedvalues.
Defineatokenusingauniquestringinitsconstructor:
[app/logo-url.token.ts]
import{OpaqueToken}from'@angular/core';
exportconstLOGO_URL=newOpaqueToken('logo.url');
Incorporatethistokenintotheapplicationmoduledefinitionasyounormallywould.However,youmustspecifywhatitwillactuallypointtowhenitisinjected.Inthiscase,itshouldresolvetoanimageURLstring:
[app/app.module.ts]
import{NgModule}from'@angular/core';
import{BrowserModule}from'@angular/platform-browser';
import{RootComponent}from'./root.component';
import{ArticleComponent}from'./article.component';
import{LOGO_URL}from'./logo-url.token';
@NgModule({
imports:[
BrowserModule
],
declarations:[
RootComponent,
ArticleComponent
],
providers:[
{provide:LOGO_URL,useValue:
'https://angular.io/resources/images/logos/standard/logo-nav.png'}
],
bootstrap:[
RootComponent
]
})
exportclassAppModule{}
![Page 475: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/475.jpg)
Finally,you'llbeabletoinjectthisintoacomponent.However,sinceyou'reinjectingsomethingthatwasn'tdefinedwiththe@Injectable()decoration,you'llneedtouse@Inject()insidetheconstructortotellAngularthatitshouldbeprovided,usingdependencyinjection.Furthermore,theinjectionwillnotattachitselftothecomponent'sthis,soyou'llneedtodothismanuallyaswell:
[app/article.component.ts]
import{Component,Inject}from'@angular/core';
import{LOGO_URL}from'./logo-url.token';
@Component({
selector:'article',
template:`
<imgsrc="{{logoUrl}}">
<h2>FoolandHisMoneyReunitedatLast</h2>
<p>Author:JakeHsu</p>
`
})
exportclassArticleComponent{
logoUrl:string;
constructor(@Inject(LOGO_URL)privatelogoUrl_){
this.logoUrl=logoUrl_;
}
}
Withthis,youshouldbeabletoseetheimagerenderedinyourbrowser!
![Page 476: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/476.jpg)
Howitworks...OpaqueTokenallowsyoutousenon-classtypesinsideAngular2'sclass-centricproviderschema.Itgeneratesasimpleclassinstancethatessentiallyisjustawrapperforthecustomstringyouprovided.Thisclassiswhatthedependencyinjectionframeworkwillusewhenattemptingtomapinjectiontokenstoproviderdeclarations.Thisgivesyoutheabilitytomorewidelyutilizedependencyinjectionthroughoutyourapplicationsinceyoucannowfeedanytypeofvaluewhereveraservicetypecanbeinjected.
![Page 477: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/477.jpg)
There'smore...Oneotherwayinwhichinjectingvaluesisusefulisthatitgivesyoutheabilitytostuboutservices.Supposeyouwantedtodefineadefaultstubservicethatshouldbeoverriddenwithanexplicitprovidertoenableusefulbehavior.Insuchacase,youcanimagineadefaultarticleentitythatcouldbedifferentlyconfiguredviaadirectivewhilereusingthesamecomponent:
[app/root.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'root',
template:`
<article></article>
<articleeditor-view></article>
`
})
exportclassRootComponent{}
[app/editor-article.service.ts]
import{Injectable}from'@angular/core';
exportconstMockEditorArticleService={
getArticle:()=>({
title:"Mocktitle",
body:"Mockbody"
})
};
@Injectable()
exportclassEditorArticleService{
privatetitle_:string=
"ProminentVeganEmbroiledinScrambledEggsScandal";
privatebody_:string=
"TofuFarmingAllianceretractedtheirendorsement.";
getArticle(){
return{
title:this.title_,
body:this.body_
};
}
}
[app/editor-view.directive.ts]
import{Directive}from'@angular/core';
import{EditorArticleService}from'./editor-article.service';
@Directive({
![Page 478: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/478.jpg)
selector:'[editor-view]',
providers:[EditorArticleService]
})
exportclassEditorViewDirective{}
[app/article.component.ts]
import{Component,Inject}from'@angular/core';
import{EditorArticleService}from'./editor-article.service';
@Component({
selector:'article',
template:`
<h2>{{title}}</h2>
<p>{{body}}</p>
`
})
exportclassArticleComponent{
title:string;
body:string;
constructor(privateeditorArticleService_:EditorArticleService){
letarticle=editorArticleService_.getArticle();
this.title=article.title;
this.body=article.body;
}
}
Withthis,yourArticleComponent,asdefinedintheprecedingcode,wouldusethemockservicewhenthedirectiveisnotattachedandtheactualservicewhenitisattached.
![Page 479: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/479.jpg)
SeealsoControllingserviceinstancecreationandinjectionwithNgModulegivesabroadoverviewofhowAngular2architectsproviderhierarchiesusingmodulesServiceinjectionaliasingwithuseClassanduseExistingdemonstrateshowtointerceptdependencyinjectionproviderrequestsBuildingaprovider-configuredservicewithuseFactorydetailstheprocessofsettingupaservicefactorytocreateconfigurableservicedefinitions
![Page 480: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/480.jpg)
Buildingaprovider-configuredservicewithuseFactoryOnefurtherextensionofdependencyinjectioninAngular2istheabilitytousefactorieswhendefiningyourproviderhierarchy.Aproviderfactoryallowsyoutoacceptinput,performarbitraryoperationstoconfiguretheprovider,andreturnthatproviderinstanceforinjection.
Note
Thecode,links,andaliveexampleofthisareavailableathttp://ngcookbook.herokuapp.com/0049/.
![Page 481: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/481.jpg)
GettingreadyBeginagainwiththedualserviceandarticlecomponentsetupshowninServiceinjectionaliasingwithuseClassanduseExisting,earlierinthechapter.
![Page 482: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/482.jpg)
Howtodoit...ProviderfactoriesinAngular2areexactlyasyoumightimaginetheywouldbe:functionsthatreturnaprovider.ThefactorycanbespecifiedinaseparatefileandreferencedwiththeuseFactoryprovideoption.
Beginbycombiningthetwoservicesintoasingleservice,whichwillbeconfiguredwithamethodcall:
[app/article.service.ts]
import{Injectable}from'@angular/core';
@Injectable()
exportclassArticleService{
privatetitle_:string=
"FlyingSpaghettiMonsterSighted";
privatebody_:string=
"Adherentsinsistwearemissingthepoint";
privatenotes_:string="Spoton!";
privateeditorEnabled_:boolean=false;
getArticle():Object{
vararticle={
title:this.title_,
body:this.body_
};
if(this.editorEnabled_){
Object.assign(article,article,{
notes:this.notes_
});
}
returnarticle;
}
enableEditor():void{
this.editorEnabled_=true;
}
}
Definingthefactory
YourobjectiveistoconfigurethisservicetohaveenableEditor()invokedbasedonabooleanflag.Withproviderfactories,thisispossible.Definethefactoryinitsownfile:
[app/article.factory.ts]
import{ArticleService}from'./article.service';
exportfunctionarticleFactory(enableEditor?:boolean):ArticleService{
return(articleService:ArticleService)=>{
![Page 483: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/483.jpg)
if(enableEditor){
articleService.enableEditor();
}
returnarticleService;
}
}
InjectingOpaqueToken
Splendid!Next,you'llneedtoreconfigureArticleComponenttoinjectatokenratherthanthedesiredservice:
[app/article.token.ts]
import{OpaqueToken}from'@angular/core';
exportconstArticleToken=newOpaqueToken('app.article');
[app/article.component.ts]
import{Component,Inject}from'@angular/core';
import{ArticleToken}from'./article.token';
@Component({
selector:'article',
template:`
<h2>{{article.title}}</h2>
<p>{{article.body}}</p>
<p*ngIf="article.notes">
<i>Notes:{{article.notes}}</i>
</p>
`
})
exportclassArticleComponent{
article:Object;
constructor(@Inject(ArticleToken)privatearticleService_){
this.article=articleService_.getArticle();
}
}
CreatingproviderdirectiveswithuseFactory
Finally,you'llneedtodefinethedirectivesthatspecifyhowtousethisfactoryandincorporatethemintotheapplication:
[app/default-view.directive.ts]
import{Directive}from'@angular/core';
import{ArticleService}from'./article.service';
import{articleFactory}from'./article.factory';
import{ArticleToken}from'./article.token';
@Directive({
![Page 484: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/484.jpg)
selector:'[default-view]',
providers:[
{provide:ArticleToken,
useFactory:articleFactory(),
deps:[ArticleService]
}
]
})
exportclassDefaultViewDirective{}
[app/editor-view.directive.ts]
import{Directive}from'@angular/core';
import{ArticleService}from'./article.service';
import{articleFactory}from'./article.factory';
import{ArticleToken}from'./article.token';
@Directive({
selector:'[editor-view]',
providers:[
{
provide:ArticleToken,
useFactory:articleFactory(true),
deps:[ArticleService]
}
]
})
exportclassEditorViewDirective{}
[app/root.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'root',
template:`
<articledefault-view></article>
<articleeditor-view></article>
`
})
exportclassRootComponent{}
Withthis,youshouldbeabletoseeboththeversionsofArticleComponent.
![Page 485: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/485.jpg)
Howitworks...Thearticlecomponentisredefinedtouseatokeninsteadofaserviceinjection.Withthetoken,Angularwillwalkupthecomponenttreetofindwherethattokenisprovided.Thedirectivesdeclarethatthetokenismappedtoaproviderfactory,whichisamethodinvokedtoreturntheactualprovider.
useFactoryisthepropertythatmapstothefactoryfunction.depsisthepropertythatmapstotheservicedependenciesthatthefactoryhas.
![Page 486: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/486.jpg)
There'smore...Animportantdistinctionatthispointistorecognizethatallthesefactoryconfigurationsarehappeningbeforeanycomponentsareinstantiated.Theclassdecorationthatdefinestheproviderswillinvokethefactoryfunctiononsetup.
![Page 487: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/487.jpg)
SeealsoControllingserviceinstancecreationandinjectionwithNgModulegivesabroadoverviewofhowAngular2architectsproviderhierarchiesusingmodulesServiceinjectionaliasingwithuseClassanduseExistingdemonstrateshowtointerceptdependencyinjectionproviderrequestsInjectingavalueasaservicewithuseValueandOpaqueTokensshowhowyoucanusedependencyinjectedtokenstoinjectgenericobjects
![Page 488: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/488.jpg)
Chapter8.ApplicationOrganizationandManagementThischapterwillcoverthefollowingrecipes:
Composingpackage.jsonforaminimumviableAngular2applicationConfiguringTypeScriptforaminimumviableAngular2applicationPerformingin-browsertranspilationwithSystemJSComposingapplicationfilesforaminimumviableAngular2applicationMigratingtheminimumviableAngular2applicationtoWebpackbundlingIncorporatingshimsandpolyfillsintoWebpackHTMLgenerationwithhtml-webpack-pluginSettingupanapplicationwithAngular'sCLI
![Page 489: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/489.jpg)
IntroductionTheAngular2project'sambitionsgoalsinvolvetheutilizationofadifferentlanguagewithdifferentsyntaxandconstructs,aswellasprovidinghighefficiencyandmodularity.WhatthismeansforyouisthattheprocessofmaintaininganAngular2applicationmaybedifficult.
TheultimategoalistoefficientlyserveHTML,CSS,andJStoawebbrowserandtomakeiteasytodevelopthesourcecomponentsofthesestaticfiles.Howonearrivesatthisendpointcanbeworkedoutinanumberofdifferentways,anditwouldbeanexerciseinfutilitytowriteachapteronallofthem.
Instead,thischapterwillprovideafewopinionatedwaysofarrangingyourAngular2applicationinawaythatitwouldreflectthemostpopularandeffectivestrategies.ItwillalsoshowyouhowtobuildandextendaminimumviableAngular2application.Forsome,thiswillseemabitsimpleandrudimentary.However,themajorityofQuickstartprojectsorcodegenerationframeworkssimplygiveyouarepositoryandafewcommandstoruninordertogetoutofthedoor,andthesecommandsrunwithouttellingyouwhatthey'redoingorhowthey'redoingit!Inthischapter,youwilllearnhowtobuildanAngular2applicationfromthegroundupalongwiththepackagesandtoolsthatwillhelpyoudoitandwhythesemethodswereselected.
![Page 490: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/490.jpg)
Composingpackage.jsonforaminimumviableAngular2applicationWhenthinkingaboutaminimumviableAngular2application,theconfigurationfilesareasclosetothemetaloftheruntimeenvironmentasyou'llget.Inthiscase,therearetwoconfigurationfilesthatwillcontrolhownpmanditsinstalledpackageswillmanagethefilesandthestart-upprocesses:package.jsonandtsconfig.json.
Somepartofthisrecipemaybeareviewfordevelopersthataremoreexperiencedwithnpmanditsfaculties.However,it'simportanttounderstandhowaverysimpleAngular2projectconfigurationcanbestructured,sothatyouareabletowhollyunderstandmorecomplexconfigurationsthatarebuilduponitsfundamentals.
Note
Thecode,links,andaliveexampleofthisareavailableathttp://ngcookbook.herokuapp.com/1332/.
![Page 491: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/491.jpg)
GettingreadyYou'llneedNode.jsinstalledforthisrecipetowork;you'llalsoneedanemptyprojectdirectory.Youshouldcreatethesetwoskeletonconfigurationfilesintherootprojectdirectory:
[package.json]
{
"name":"angular2-minimum-viable-application"
}
[tsconfig.json]
{
"compilerOptions":{
}
}
Tip
Foraquickandeasywaytoensureyouhavenpmsetupandreadytogo,usethefollowing:
npm--versionItshouldspitoutaversionnumberifeverythingissetupproperly.
![Page 492: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/492.jpg)
Howtodoit...You'llstartwithpackage.json.Thepackage.jsonfileforaminimumviableapplicationcontainsthreesections:
dependencies:ThisisalistofpackagetargetsthattheproductionapplicationdirectlydependsupondevDependencies:Thisisalistofpackagetargetsthatthelocalenvironmentneedsforvariousreasons,suchascompilation,runningtests,orlintingscripts:Thesearecustom-definedcommand-lineutilitiesrunthroughnpm
package.jsondependencies
First,youneedtoaddinallthedependenciesthatyourapplicationwillneed.ThisincludesAngular2coremodules,whichliveinsidethenode_modules/@angulardirectory,aswellasahandfuloflibrarydependencies:
core-jsisthepolyfillfortheES6syntaxthattheTypeScriptcompilerdependsupon,suchasSet,Promise,andMap.reflect-metadataisthepolyfillfortheReflectMetadataAPI.ThisallowsyourTypeScripttousedecoratorsthatarenotpartofthestandardTypeScriptspecification,suchas@Component.rxjsisavailablefortheReactiveXJavaScriptobservableslibrary.Angular2nativelyusesObservables,andthisisadirectdependencyoftheframework.SystemJSisthedynamicmoduleloaderthatthisprojectneedsfortwopurposes:toimportandmapallthesourcefiles,andtobeabletoresolvetheES6import/exportdeclarations.zonejsistheZoneJSlibrarythatprovidesAngular2withtheabilitytouseasynchronousexecutioncontexts.Thisisadirectdependencyoftheframework.
Thisleavesyouwiththefollowing:
[package.json]
{
"name":"angular2-minimum-viable-application",
"dependencies":{
"@angular/common":"2.0.0",
"@angular/compiler":"2.0.0",
"@angular/core":"2.0.0",
"@angular/platform-browser":"2.0.0",
"@angular/platform-browser-dynamic":"2.0.0",
"core-js":"^2.4.1",
"reflect-metadata":"^0.1.3",
"rxjs":"5.0.0-beta.12",
"systemjs":"0.19.27",
"zone.js":"^0.6.23"
}
![Page 493: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/493.jpg)
}
package.jsondevDependencies
Next,youneedtospecifythedevDependencies.
Note
Here'sannpmrefresher:devDependenciesaredependenciesthatarespecifictoadevelopmentenvironment.Buildscriptscanusethistodifferentiatebetweenpackagesthatneedtobeincludedinaproductionbundleandonesthatdon't.
lite-serveristhesimplefileserveryou'llusetotestthisapplicationlocally.Thiscouldbereplacedbyanynumberofsimplefileservers.typescriptistheTypeScriptcompiler.concurrentlyisasimplecommand-lineutilityforrunningsimultaneouscommandsfromannpmscript.
Thisleavesyouwiththefollowing:
[package.json]
{
"name":"angular2-minimum-viable-application",
"dependencies":{
"@angular/common":"2.0.0",
"@angular/compiler":"2.0.0",
"@angular/core":"2.0.0",
"@angular/platform-browser":"2.0.0",
"@angular/platform-browser-dynamic":"2.0.0",
"core-js":"^2.4.1",
"reflect-metadata":"^0.1.3",
"rxjs":"5.0.0-beta.12",
"systemjs":"0.19.27",
"zone.js":"^0.6.23"
},
"devDependencies":{
"concurrently":"^2.2.0",
"lite-server":"^2.2.2",
"typescript":"^2.0.2"
}
}
package.jsonscripts
Finally,youneedtocreatethescriptsthatyou'llusetogeneratecompiledfilesandrunthedevelopmentserver:
[package.json]
{
![Page 494: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/494.jpg)
"name":"angular2-minimum-viable-application",
"scripts":{
"lite":"lite-server",
"postinstall":"npminstall-S@types/node@types/core-js",
"start":"tsc&&concurrently'npmruntsc:w''npmrunlite'",
"tsc":"tsc",
"tsc:w":"tsc-w"
},
"dependencies":{
"@angular/common":"2.0.0",
"@angular/compiler":"2.0.0",
"@angular/core":"2.0.0",
"@angular/platform-browser":"2.0.0",
"@angular/platform-browser-dynamic":"2.0.0",
"core-js":"^2.4.1",
"reflect-metadata":"^0.1.3",
"rxjs":"5.0.0-beta.12",
"systemjs":"0.19.27",
"zone.js":"^0.6.23"
},
"devDependencies":{
"concurrently":"^2.2.0",
"lite-server":"^2.2.2",
"typescript":"^2.0.2"
}
}
Eachofthesescriptsservesapurpose,butmostyouwillnotneedtoinvokemanually.Hereisabriefdescriptionofeachofthesescripts:
litestartsoffaninstanceoflite-server.postinstallisthehookdefinitionthatwillrunafternpminstalliscompleted.Inthiscase,afternpmhasinstalledalltheprojectdependencies,youwanttoinstallthedeclarationfilesformodulesthatdonothavethem.npmrecognizesthepre-andpost-prefixesforscriptstrings.Anytimeascriptisrun,npmwillcheckforscriptswithpre-andpost-prefixingthemandrunthembeforeandafterthescript,respectively.Inthisrecipe,prelitewouldrunbeforelite,andpostlitewouldrunafterliteisrun.startisthedefinitionofthedefaultvalueofnpmstart.ThisscriptrunstheTypeScriptcompileroncetocompletion,thensimultaneouslyinvokestheTypeScriptcompilerwatcherandstartsupadevelopmentserver.Itisareservedscriptkeywordinnpm,thusthereisnoneedfornpmrunstart,althoughthatdoeswork.tsckicksofftheTypeScriptcompiler.TheTypeScriptcompilerreadsitssettingsfromthetsconfig.jsonthatexistsinthesamedirectory.tsc:wsetsafilewatchertorecompileuponfilechanges.
![Page 495: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/495.jpg)
SeealsoComposingpackage.jsonforaminimumviableAngular2applicationdescribeshowallthepiecesworkforthecorenodeprojectfileConfiguringTypeScriptforaminimumviableAngular2applicationtalksabouthowtoconfigurethecompilationtosupportanAngular2projectPerformingin-browsertranspilationwithSystemJSdemonstrateshowSystemJScanbeusedtoconnectuncompiledstaticfilestogetherComposingapplicationfilesforaminimumviableAngular2ApplicationwalksyouthroughhowtocreateanextremelysimpleAngular2appfromscratchMigratingtheminimumviableAngular2applicationtoWebpackbundlingdescribeshowtointegrateWebpackintoyourAngularapplicationbuildprocessIncorporatingshimsandpolyfillsintoWebpackgivesyouahandywayofmanagingAngular2polyfilldependenciesHTMLgenerationwithhtml-webpack-pluginshowsyouhowyoucanconfigureannpmpackagetoaddcompiledfilestoyourHTMLautomaticallySettingupanapplicationwithAngularCLIgivesadescriptionofhowtousetheCLI,whatitgivesyou,andwhattheseindividualpiecesdo
![Page 496: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/496.jpg)
ConfiguringTypeScriptforaminimumviableAngular2applicationInordertouseTypeScriptalongsideAngular2,therearetwomajorconsiderations:moduleinteroperabilityandcompilation.You'llneedtohandlebothinordertotakeyourapplication's.tsfiles,mixthemwithexternallibraryfiles,andoutputthefilesthatwouldbecompatiblewithyourtargetdevice.
TypeScriptcomesreadyasannpmpackage,butyouwillneedtotellithowtointeractwiththefilesandmodulesyou'vewritten,andwithfilesfromotherpackagesthatyouwanttouseinyourmodules.
Note
Thecode,links,andaliveexampleofthisareavailableathttp://ngcookbook.herokuapp.com/1053/.
![Page 497: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/497.jpg)
GettingreadyYoushouldfirstcompletetheinstructionsmentionedintheprecedingrecipe.ThiswillgiveyoutheframeworknecessarytodefineyourTypeScriptconfiguration.
![Page 498: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/498.jpg)
Howtodoit...ToconfigureTypeScript,you'llneedtoadddeclarationfilestoincompatiblemodulesandgenerateaconfigurationfilethatwillspecifyhowthecompilershouldwork.
Declarationfiles
TypeScriptdeclarationfilesexisttospecifytheshapeofalibrary.Thesefilescanbeidentifiedbya.d.tssuffix.ThemajorityofnpmpackagesandotherJavaScriptlibrariesalreadyincludethesefilesinastandardizedlocation,sothatTypeScriptcanlocatethemandlearnabouthowthelibraryshouldbeinterpreted.Librariesthatdon'tincludetheseneedtobegiventhefiles,andfortunatelytheopensourcecommunityalreadyprovidesalotofthem.
Twolibrariesthatthisprojectusesdon'thavedeclarationfiles:nodeandcore-js.AsofTypeScript2.0,youareabletonativelyinstallthedeclarationfilesfortheselibrariesdirectlythroughnpm.The-Sflagisashorthandforsavingthemtopackage.json:
npminstall-S@types/node@types/core-js
Asensibleplaceforthisisinsidethepostinstallscript.
tsconfig.json
TheTypeScriptcompilerwilllookforthetsconfig.jsonfiletodeterminehowitshouldcompiletheTypeScriptfilesinthisdirectory.Thisconfigurationfileisn'trequired,asTypeScriptwillfallbacktothecompilerdefaults;however,youwanttomanageexactlyhowthe*.jsand*.map.jsfilesaregenerated.Modifythetsconfig.jsonfiletoappearasfollows:
[tsconfig.json]
{
"compilerOptions":{
"target":"es5",
"module":"commonjs",
"moduleResolution":"node",
"emitDecoratorMetadata":true,
"experimentalDecorators":true,
"noImplicitAny":false
}
}
ThecompilerOptionsproperty,asyoumightexpect,specifiesthesettingsthecompilershouldusewhenthecompilingprocessfindsTypeScriptfiles.Intheabsenceofafilesproperty,TypeScriptwilltraversetheentireprojectdirectorystructuresearchingfor*.tsand*.tsxfiles.
AllthecompilerOptionspropertiescanbespecifiedequivalentlyascommand-lineflags,butdoingsointsconfig.jsonisamoreorganizedwayofgoingaboutyourproject.
![Page 499: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/499.jpg)
targetspecifiestheECMAScriptversionthatthecompilershouldoutput.Forbroadbrowsercompatibility,ES5isasensibledefaulthere.RecallthatECMAScriptisthespecificationuponwhichJavaScriptisbuilt.ThenewestfinishedspecificationisES6(alsocalledES2015),butmanybrowsersdonotfullysupportthisspecificationyet.TheTypeScriptcompilerwillcompileES6constructs,suchasclassandPromise,tonon-nativeimplementations.modulespecifieshowtheoutputfileswillhandlethemodulesintheoutputfiles.SinceyoucannotassumethatbrowsersareabletohandleES6modules,theTypeScriptcompilerwillhavetoconvertthemintoamodulesystemthatbrowsersareabletohandle.CommonJSisasensiblechoicehere.TheCommonJSmodulestyleinvolvesdefiningalltheexportsinasinglemoduleaspropertiesofasingle"exports"object.TheTypeScriptcompileralsosupportsAMDmodules(require.jsstyle),UMDmodules,SystemJSmodules,andofcourse,leavingthemodulesastheirexistingES6modulestyle.It'soutofthescopeofthisrecipetodivedeepintomodules.moduleResolutiondefineshowmodulepathswillberesolved.It'snotcriticalthatyouunderstandtheexactdetailsoftheresolutionstrategy,butthenodesettingwillgiveyoutheproperoutputformat.emitDecoratorMetadataandexperimentalDecoratorsenableTypeScripttohandleAngular2'suseofdecorators.Recalltheadditionofthereflect-metadatalibrarytosupportexperimentaldecorators.TheseflagsarethepointwhereitisabletotieintotheTypeScriptcompiler.noImplicitAnycontrolswhetherornotTypeScriptfilesmustbetyped.Whensettotrue,thiswillthrowanerrorifthereisanymissedtypinginyourproject.Thereisanongoingdiscussionregardingwhetherornotthisflagshouldbeset,asforcingobjectstobetypedisobviouslyusefultopreventerrorsthatmayarisefromambiguityincodebases.Ifyou'dliketoseeanexampleofthecompilerthrowinganerror,setnoImplicitAnytotrueandaddconstructor(foo){}insideAppComponent.Youshouldseethecompilercomplainaboutfoobeinguntyped.
![Page 500: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/500.jpg)
Howitworks...RunningthefollowingcommandwillstartuptheTypeScriptcompilerfromthecommandlineattherootlevelofyourprojectdirectory:
npmruntsc
Thecompilerwilllookfortsconfig.jsonifitisthereandfallbacktoitsdefaultsotherwise.Thesettingswithindirectthecompilerhowtohandleandvalidatethefiles,whichiswhereeverythingyoujustsetupcomesintoplay.
TheTypeScriptcompilerdoesn'trunthecodeormeaningfullyunderstandwhatitdoes,butitcandetectwhendifferentpiecesoftheapplicationaretryingtointeractinawaythatdoesn'tmakesense.The.d.tsdeclarationfileforamodulegivesTypeScriptawaytoinspecttheinterfacethatthemodulewillmakeavailableforconsumptionwhenitisimported.
Forexample,supposethatauthisanexternalmodulethatcontainsaUserclass.Thiswouldthenbeimportedviathefollowing:
import{User}from'./auth';
Byaddingthedeclarationfiletotheimportedmodule,TypeScriptisabletocheckthattheUserclassexists;italsobehavesinthewayyouareattemptingtointhelocalmodule.Ifitseesamismatch,itwillthrowanerroratcompilation.
Compilation
Dependingonyourframeworkexperience,thismaybesomethingyouhaveorhavenothadexperiencewithpreviously.Angular2(amongmanyframeworks)operatesunderthenotionthatJavaScript,asitcurrentlyexists,isinsufficientforwritinggoodcode.Thedefinitionof"good"hereissubjective,butallframeworksthatrequirecompilationwanttoextendormodifyJavaScriptinsomeformoranother.
However,allplatformsthattheseapplicationsneedtorunon—foryourpurposes,webbrowsers—onlyhaveaJavaScriptexecutionenvironmentthatexecutesfromuncompiledcode.Itisn'tfeasibleforyoutoextendhowthebrowserhandlespayloadsordeliversacompiledbinary,sothefilesthatyousendtotheclientmustplaybyitsrules.
TypeScript,bydefinitionanddesign,isastrictsupersetofES6,buttheseextensionscan'tbeusednativelyinabrowser.Eventoday,themajorityofbrowsersstilldonotfullysupportES6.Therefore,asensibleobjectiveistoconvertTypeScriptintoES5.1,whichistheECMAScriptstandardthatissupportedonallmodernbrowsers.Howyouarriveatthisoutputcanoccurinoneoftwoways:
SendtheTypeScripttotheclientasis.Therearein-browsercompilationlibrariesthatcan
![Page 501: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/501.jpg)
performacompilationontheclientandexecutetheresultingES5.1-compliantcodeasnormalJavaScript.Thismethodmakesdevelopmenteasiersinceyourbackenddoesn'tneedtodomuchotherthanservethefiles;however,itdeferscomputingtotheclient,whichdegradesperformanceandisthereforeconsideredabadpracticeforproductionapplications.CompiletheTypeScriptintoJavaScriptbeforesendingittotheclient.Theoverwhelmingmajorityofproductionapplicationswillelecttohandletheirbusinessthisway.EspeciallysincestaticfilesareoftenservedfromaCDNorstaticdirectory,itmakesgoodsensetocompileyourdescriptiveTypeScriptcodebaseintoJavaScriptfilesaspartofareleaseandthenservethosefilestotheclient.
Tip
WhenyoulookatthecompiledJavaScriptthatresultsfromcompilingTypeScript,itcanappearawfullybrutalandunreadable.Don'tworry!ThebrowserdoesnotcarehowmangledtheJavaScriptfilesareaslongastheycanbeexecuted.
Withthecompileroptionsyou'vespecifiedinthisrecipe,theTypeScriptcompilerwilloutputa.jsfileofthesamenamerightnexttoitssource,the.tsfile.
![Page 502: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/502.jpg)
There'smore...BynomeansistheTypeScriptcompilerlimitedtoaone-off.tsfilegeneration.Ifoffersyouabroadrangeoftoolingfunctionsforspecifyingexactlyhowyouroutputfilesshouldappear.
Sourcemapgeneration
TheTypeScriptcompilerisalsocapableofgeneratingsourcemapstogoalongwithoutputfiles.Ifyou'renotfamiliarwiththem,theutilityofsourcemapsstemsfromthenatureofcompilationandminification:filesbeingdebuggedinthebrowsersarenotthefilesthatyouhavewritten.What'smore,whenusingacompiledTypeScript,thecompiledfileswon'tevenbeinthesamelanguage.
Sourcemapsareindexesthatpairwithcompiledfilestodescribehowtheyoriginallyappearedbeforetheywerecompiled.Morespecifically,the.js.mapfilescontainanencodingschemethatassociatesthecompiledand/orminifiedtokenswiththeiroriginalnameandstructureintheuncompiled.tsfile.Browsersthatunderstandhowtousesourcemapscanreconstructhowtheoriginalfileappearedandallowyoutosetbreakpoints,stepthrough,andinspectlexicalconstructsinsideitasifitweretheoriginal.
Sourcemapscanbespecifiedwithaspecialtokenaddedtothecompiledfile://#sourceMappingURL=/dist/example.js.map
Ifyouwanttogeneratesourcemapfilesfortheoutput,youcanspecifythisintheconfigurationfileaswellbyadding"sourceMap":true.Bydefault,the.js.mapfileswillbecreatedinthesameplaceastheoutput.jsfiles;alternatively,youcandirectthecompilertocreatethesourcemapsinsidethe.jsfileitself.
Tip
Eventhoughextraneousmapfileswon'taffecttheresultantapplicationbehavior,addingtheminlinemaybeundesirableifyoudon'twanttobloatyour.jspayloadsizeunnecessarily.Thisisbecauseclientsthatdon'twantorneedthemapfilescan'tdeclinetorequestthem.
Singlefilecompilation
SinceTypeScriptchecksallthelinkedmodulesagainsttheirimportsandexports,there'snoreasonyouneedtohaveallthecompiledfilesexistas1:1mappingstotheirinputfiles.TypeScriptisperfectlyhappytocombinethecompiledfilesintoasinglefileiftheoutputmoduleformatsupportsit.Specifythesinglefilewhereyouwishallthemodulestobecompiledwith"outFile":"/dist/bundle.js".
Note
Certainoutputmoduleformats,suchasCommonJS,won'tworkasconcatenatedmodulesina
![Page 503: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/503.jpg)
singlefile,sousingtheminconjunctionwithoutFilewillnotwork.AsoftheTypeScript1.8release,AMDandsystemoutputformatsaresupported.
IfyouplanonusingSystemJS,thiscompileroptioncanpotentiallyhelpyou,asSystemworkswithvirtuallyanymoduleformat.If,however,you'reusingaCommonJS-basedbundler,suchasWebpack,it'sbesttodelegatethefilecombinationtothebundler.
![Page 504: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/504.jpg)
SeealsoComposingpackage.jsonforaminimumviableAngular2applicationdescribeshowallthepiecesworkforthecorenodeprojectfilePerformingin-browsertranspilationwithSystemJSdemonstrateshowSystemJScanbeusedtoconnectuncompiledstaticfilestogetherComposingapplicationfilesforaminimumviableAngular2applicationwalksyouthroughhowtocreateanextremelysimpleAngular2appfromscratchMigratingtheminimumviableAngular2applicationtoWebpackbundlingdescribeshowtointegrateWebpackintoyourAngularapplicationbuildprocess
![Page 505: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/505.jpg)
Performingin-browsertranspilationwithSystemJSItcanbeoftenusefultobeabletodeliverTypeScriptfilesdirectlytothebrowserandtodeferthetranspilationtoJavaScriptuntilthen.Whilethismethodhasperformancedrawbacks,itisextremelyusefulwhenprototypingandperformingexperimentations.
Note
Thecode,links,andaliveexampleofthisareavailableathttp://ngcookbook.herokuapp.com/2283/.
![Page 506: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/506.jpg)
GettingreadyCreateanemptyprojectdirectoryandcreatethefollowingpackage.jsoninsideit:
[package.json]
{
"scripts":{
"lite-server":"lite-server"
},
"devDependencies":{
"lite-server":"^2.2.2",
"systemjs":"^0.19.38",
"typescript":"^2.0.3"
}
}
Runningnpminstallshouldgetyoureadytowritecode.
![Page 507: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/507.jpg)
Howtodoit...TheTypeScriptnpmpackagecomesbundledwithatranspiler.WhencombinedwithSystemJSasthedesignatedtranspilationutility,thisallowsyoutoserveTypeScriptfilestotheclient;SystemJSwilltranspilethemintobrowser-compatibleJavaScript.
First,createtheindex.htmlfile.ThisfilewillimportthetworequiredJSlibraries:system.jsandtypescript.js.Next,itspecifiesthetypescriptasthedesiredtranspilerandimportsthetop-levelmain.tsfile:
[index.html]
<html>
<head>
<scriptsrc="node_modules/systemjs/dist/system.js">
</script>
<scriptsrc="node_modules/typescript/lib/typescript.js">
</script>
<script>
System.config({
transpiler:'typescript'
});
System.import('main.ts');
</script>
</head>
<body>
<h1id="text"></h1>
</body>
</html>
Next,createthetop-levelTypeScriptfile:
[main.ts]
import{article}from'./article.ts';
document.getElementById('text')
.innerHTML=article;
Finally,createthedependencyTypeScriptfile:
[article.ts]
exportconstarticle="Coolstory,bro";
Withthis,youshouldbeabletostartadevelopmentserverwithnpmrunlite-serverandseetheTypeScriptapplicationrunningnormallyinyourbrowseratlocalhost:3000.
![Page 508: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/508.jpg)
Howitworks...SystemJSisabletoresolvemoduledependenciesaswellasapplythetranspilertothemodulebeforeitreachesthebrowser.Ifyoulookatthetranspiledfilesinabrowserinspector,youcanseetheemittedfilesexistasvanillaJavaScriptIIFEs(instantaneouslyinvokedfunctionexpressions)aswellastheircoupledsourcemaps.Withthesetools,itispossibletobuildasurprisinglycomplexapplicationwithoutanysortofbackendfilemanagement.
![Page 509: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/509.jpg)
There'smore...Unlessyou'reexperimentingordoingaroughproject,doingtranspilationinthebrowserisn'tpreferred.Anycomputationyoucandoontheservershouldbedonewheneverpossible.Additionally,alltheclientstranspilingtheirownfilesallperformhighlyredundantoperationssinceallofthemtranspilethesamefiles.
![Page 510: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/510.jpg)
SeealsoComposingpackage.jsonforaminimumviableAngular2applicationdescribeshowallthepiecesworkforthecorenodeprojectfileConfiguringTypeScriptforaminimumviableAngular2applicationtalksabouthowtoconfigurethecompilationtosupportanAngular2projectComposingapplicationfilesforaminimumviableAngular2applicationwalksyouthroughhowtocreateanextremelysimpleAngular2appfromscratchMigratingtheminimumviableAngular2applicationtoWebpackbundlingdescribeshowtointegrateWebpackintoyourAngularapplicationbuildprocessIncorporatingshimsandpolyfillsintowebpackgivesyouahandywayofmanagingAngular2polyfilldependenciesHTMLgenerationwithhtml-webpack-pluginshowsyouhowyoucanconfigureannpmpackagetoaddcompiledfilestoyourHTMLautomaticallySettingupanapplicationwithAngularCLIgivesadescriptionofhowtousetheCLI,whatitgivesyou,andwhattheseindividualpiecesdo
![Page 511: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/511.jpg)
ComposingapplicationfilesforaminimumviableAngular2applicationWhenapproachingAngular2initially,itisusefultohaveanunderstandingofanapplicationstructurethatistorndowntothebaremetal.Inthecaseofaminimumviableapplication,itwillconsistofasinglecomponent.Sincethisisachapteronapplicationorganization,itisn'tsomuchaboutwhatthatcomponentwilllooklike,butratherhowtotaketheTypeScriptcomponentdefinitionandactuallygetittorenderinawebpage.
Note
Thecode,links,andaliveexampleofthisareavailableathttp://ngcookbook.herokuapp.com/6323/.
![Page 512: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/512.jpg)
GettingreadyThisrecipeassumesyouhavecompletedallthestepsgivenintheComposingconfigurationfilesforaminimumviableAngular2applicationrecipe.Thenpmmoduleinstallationshouldsucceedwithnoerrors:
npminstall
![Page 513: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/513.jpg)
Howtodoit...Thesimplestplacetostartisthecoreapplicationcomponent.
app.component.ts
Implementacomponentinsideanewapp/directoryasfollows;thereshouldbenosurprises:
[app/app.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'app-root',
template:'<h1>AppComponenttemplate!</h1>'
})
exportclassAppComponent{}
Thisisaboutassimpleacomponentcanpossiblyget.Oncethisissuccessfullyrenderedintheclient,thiscomponentshouldjustbeabiglineoftext.
app.module.ts
Next,youneedtodefinetheNgModulethatwillbeassociatedwiththiscomponent.Createanotherfileintheapp/directory,app.module.ts,andhaveitmatchthefollowing:
[app/app.module.ts]
import{NgModule}from'@angular/core';
import{BrowserModule}from'@angular/platform-browser';
import{AppComponent}from'./app.component';
@NgModule({
imports:[BrowserModule],
declarations:[AppComponent],
bootstrap:[AppComponent]
})
exportclassAppModule{}
There'sabitmoregoingonhere:
importsspecifiesthemoduleswhoseexporteddirectives/pipesshouldbeavailabletothismodule.
Tip
ImportingBrowserModulegivesyouaccesstocoredirectivessuchasNgIfandalsospecifiesthetypeofrenderer,eventmanagement,anddocumenttype.Ifyourapplicationisrenderinginawebbrowser,thismodulegivesyouthetoolsyouneedtodothis.
![Page 514: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/514.jpg)
declarationsspecifieswhichdirectives/pipesarebeingexportedbythismodule.Inthiscase,AppComponentisthesoleexport.bootstrapspecifieswhichcomponentsshouldbebootstrappedwhenthismoduleisbootstrapped.Morespecifically,componentslistedherewillbedesignatedforrenderingwithinthismodule.AppComponentneedstobebootstrappedandrenderedsomewhere,andthisiswherethisspecificationwilloccur.
Thiscompletesthemoduledefinition.Atthispoint,youhavesuccessfullylinkedthecomponenttoitsmodule,butthismoduleisn'tbeingbootstrappedanywhereorevenincluded.
main.ts
You'llchangethisnextwithmain.ts,thetop-levelTypeScriptfile:
[app/main.ts]
import{platformBrowserDynamic}
from'@angular/platform-browser-dynamic';
import{AppModule}from'./app.module';
platformBrowserDynamic().bootstrapModule(AppModule);
ThisfiledefinestheNgModuledecoratorthatwillbeusedforAppComponent.Insideit,youspecifythatthemodulemustimportBrowserModule.
Note
RecallthatAngular2isdesignedtobeplatform-independent.Morespecifically,itstrivestoallowyoutowritecodethatmightnotnecessarilyrunonaconventionalwebbrowser.Inthiscase,youaretargetingastandardwebbrowser,soimportingBrowserModulefromtheplatformBrowsertargetisthewayinwhichyoucaninformtheapplicationofthis.Ifyouweretargetingaseparateplatform,youwouldselectadifferentplatformtoimportintoyourrootapplicationcomponent.
ThisNgModuledeclarationalsospecifiesthatAppComponentexistsandshouldbebootstrapped.
Note
BootstrappingishowyoukickoffyourAngular2application,butithasaveryspecificdefinition.Invokingbootstrap()tellsAngulartomountthespecifiedapplicationcomponentontoDOMelementsidentifiedbythecomponent'sselector.Thiskicksofftheinitialroundofchangedetectionanditssideeffects,whichwillcompletethecomponentinitialization.
Sinceyou'vedeclaredthatthismodulewillbootstrapAppComponentwhenitisbootstrapped,thismodulewillinturnbetheonebootstrappedfromthetop-levelTypeScriptfile.Angular2pushesforthisconventionasamain.tsfile:
![Page 515: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/515.jpg)
[app/main.ts]
import{platformBrowserDynamic}
from'@angular/platform-browser-dynamic';
import{AppModule}from'./app.module';
platformBrowserDynamic().bootstrapModule(AppModule);
TheplatformBrowserDynamicmethodreturnsaplatformobjectthatexposesthebootstrapModulemethod.ItconfiguresyourapplicationtobebootstrappedwithAngular2'sjust-in-time(JIT)compiler.
Tip
Fornow,thedetailsofwhyyouarespecifyingjust-in-timecompilationaren'timportant.It'senoughtoknowthatJITcompilationisasimplerversion(asopposedtoahead-of-timecompilation)inAngular2'sofferings.
index.html
Finally,youneedtobuildanHTMLfilethatiscapableofbundlingtogetherallthesecompiledfilesandkickingofftheapplicationinitialization.Beginwiththefollowing:
[index.html]
<html>
<head>
<title>Angular2MinimumViableApplication</title>
<scriptsrc="node_modules/zone.js/dist/zone.js">
</script>
<scriptsrc="node_modules/reflect-metadata/Reflect.js">
</script>
<scriptsrc="node_modules/systemjs/dist/system.src.js">
</script>
</head>
<body>
<app-root></app-root>
</body>
</html>
Mostofthissofarshouldbeexpected.ZoneJSandReflectareAngular2dependencies.Themoduleloaderyou'lluseisSystemJS.<app-root>istheelementthatAppComponentwillrenderinside.
ConfiguringSystemJS
Next,SystemJSneedstobeconfiguredtounderstandhowtoimportmodulefilesandhowtoconnectmodulesfrombeingimportedinsideothermodules.Inotherwords,itneedstobegivenafiletobeginwithandadirectoryofmappingsfordependenciesofthatmainfile.Thiscanbe
![Page 516: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/516.jpg)
accomplishedwithSystem.config()andSystem.import(),whicharemethodsexposedontheglobalSystemobject:
[index.html]
<html>
<head>
<title>Angular2MinimumViableApplication</title>
<scriptsrc="node_modules/zone.js/dist/zone.js">
</script>
<scriptsrc="node_modules/reflect-metadata/Reflect.js">
</script>
<scriptsrc="node_modules/systemjs/dist/system.src.js">
</script>
<script>
System.config({
paths:{
'ng:':'node_modules/@angular/'
},
map:{
'@angular/core':'ng:core/bundles/core.umd.js',
'@angular/common':'ng:common/bundles/common.umd.js',
'@angular/compiler':
'ng:compiler/bundles/compiler.umd.js',
'@angular/platform-browser':
'ng:platform-browser/bundles/platform-browser.umd.js',
'@angular/platform-browser-dynamic':
'ng:platform-browser-dynamic/bundles/platform-browser-
dynamic.umd.js',
'rxjs':'node_modules/rxjs'
},
packages:{
app:{
main:'./main.js'
},
rxjs:{
defaultExtension:'js'
}
}
});
System.import('app');
</script>
</head>
<body>
<app-root></app-root>
</body>
</html>
System.config()specifieshowSystemJSshouldhandlethefilespassedtoit.
Thepathspropertyspecifiesanaliastoshortenthepath'sinsidemap.Itactsasasimple
![Page 517: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/517.jpg)
findandreplacefunction,soanyfoundinstancesofng:arereplacedwithnode_modules/@angular/.ThemappropertyspecifieshowSystemJSshouldresolvethemoduleimportsthatyouhavenotexplicitlydefined.Here,thistakestheformoffivecoreAngularmodulesandtheRxJSlibrary.Thepackagespropertyspecifiesthetargetsthatwillbeimportedbythispropertyandthefilestheyneedtomapto.
Tip
Forexample,theapppropertywillbeusedwhenamoduleimportsapp,andinsideSystemJS,thiswillmaptomain.js.Similarly,whenamodulerequiresanRxJSmodule,suchasSubject,SystemJSwilltaketherxjs/Subjectimportpath,recognizethatdefaultExtensionisspecifiedasjs,mapthemoduletoitsfilerepresentationnode_modules/rxjs/Subject.js,andimportit.
![Page 518: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/518.jpg)
SeealsoComposingpackage.jsonforaminimumviableAngular2applicationdescribeshowallthepiecesworkforthecorenodeprojectfileConfiguringTypeScriptforaminimumviableAngular2applicationtalksabouthowtoconfigurecompilationtosupportanAngular2projectPerformingin-browsertranspilationwithSystemJSdemonstrateshowSystemJScanbeusedtoconnectuncompiledstaticfilestogetherMigratingtheminimumviableAngular2applicationtoWebpackbundlingdescribeshowtointegrateWebpackintoyourAngularapplicationbuildprocessIncorporatingshimsandpolyfillsintoWebpackgivesyouahandywayofmanagingAngular2polyfilldependenciesHTMLgenerationwithhtml-webpack-pluginshowsyouhowyoucanconfigureannpmpackagetoaddcompiledfilestoyourHTMLautomaticallySettingupanapplicationwithAngularCLIgivesadescriptionofhowtousetheCLI,whatitgivesyou,andwhattheseindividualpiecesdo
![Page 519: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/519.jpg)
MigratingtheminimumviableapplicationtoWebpackbundlingItisadvantageousformanyreasonstomakeitaseasyandquickaspossiblefortheclienttoloadandrunthecodesentfromyourserver.Oneoftheeasiestandmosteffectivewaysofdoingthisisbybundlinglotsofcodeintoasinglefile.Innearlyallcases,itishighlyefficientforthebrowsertoloadasinglefilethatcontainsallthedependenciesrequiredtobootstrapanapplication.
WebpackoffersmanyusefultoolsandamongthemistheterrificJSbundler.Thisrecipedemonstrateshowyouwillbeabletocombineyourentireapplication(includingnpmpackagedependencies)intoasingleJavaScriptfilethatthebrowserwillbeserved.
Note
Thecode,links,andaliveexampleofthisareavailableathttp://ngcookbook.herokuapp.com/3310/.
![Page 520: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/520.jpg)
GettingreadyYoushouldhavecompletedallthestepsgivenintheComposingconfigurationfilesforaminimumviableAngular2applicationandComposingapplicationfilesforaminimumviableAngular2applicationrecipes.npmstartshouldstartupthedevelopmentserver,anditshouldbevisibleatlocalhost:3000.
![Page 521: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/521.jpg)
Howtodoit...Beginbyremovingtheapplication'sdependencyonSystemJS.webpackisabletoresolvedependenciesandbundleallyourfilesintoasingleJSfile.Beginbyinstallingwebpackwiththeglobalflag:
npminstallwebpack-g
webpack.config.js
webpacklooksforawebpack.config.jsfileforinstructionsonhowtobehave.Createthisnow:
[webpack.config.js]
module.exports={
entry:"./app/main.js",
output:{
path:"./dist",
filename:"bundle.js"
}
};
Nothingexceptionallycomplicatedisgoingonhere.Thistellswebpacktoselectmain.jsasthetop-levelapplicationfile,resolveallitsdependenciestothefilesthatdefinethem,andbundlethemintoasinglebundle.jsinsideadist/directory.
Tip
Atthispoint,youcancheckthatthisisworkingbyinvokingwebpackfromthecommandline,whichwillrunthebundler.Youshouldseebundle.jsappearinsidedist/withallthemoduledependenciesinsideit.
Thisisagoodstart,butthisgeneratedfilestillisn'tbeingusedanywhere.Next,you'llmodifyindex.htmltousethefile:
[index.html]
<html>
<head>
<title>Angular2MinimumViableApplication</title>
<scriptsrc="node_modules/zone.js/dist/zone.js">
</script>
<scriptsrc="node_modules/reflect-metadata/Reflect.js">
</script>
<scriptsrc="dist/bundle.js">
</script>
</head>
<body>
![Page 522: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/522.jpg)
<app-root></app-root>
</body>
</html>
Probablynotwhatyouwereexpectingatall!Sincebundle.jsistheapplicationentrypointandSystemJSisnolongerneededtoresolveanymodules(becausewebpackisalreadydoingthisforyouwhenbundlingthefiles),youcanremovetheapplication'sdependencyonSystemJS.
Sincethisisthecase,youcanremovetheSystemdependencyfromyourpackage.jsonandaddthewebpackscriptsanddependency:
[package.json]
{
"name":"mva-bundling",
"scripts":{
"start":"tsc&&webpack&&concurrently'npmruntsc:w'
'npmrunwp:w''npmrunlite'",
"lite":"lite-server",
"postinstall":"npminstall-S@types/node@types/core-js",
"tsc":"tsc",
"tsc:w":"tsc-w",
"wp":"webpack",
"wp:w":"webpack--watch"
},
"dependencies":{
"@angular/common":"2.0.0",
"@angular/compiler":"2.0.0",
"@angular/core":"2.0.0",
"@angular/platform-browser":"2.0.0",
"@angular/platform-browser-dynamic":"2.0.0",
"core-js":"^2.4.1",
"reflect-metadata":"^0.1.3",
"rxjs":"5.0.0-beta.12",
"zone.js":"^0.6.23"
},
"devDependencies":{
"concurrently":"^2.2.0",
"lite-server":"^2.2.2",
"typescript":"^2.0.2",
"webpack":"^1.13.2"
}
}
WhetherornotwebpackandtypescriptbelongtodevDependencieshereisamatterofdisputeandislargelysubjecttohowyoumanageyourlocalenvironment.Ifyou'vealreadyinstalledthemwiththeglobalflag,thenyoudon'tneedtolistithereasadependency.Thisisbecausenpmwillsearchforgloballyinstalledpackagesandfindthemforyoutorunnpmscripts.Furthermore,listingitherewillinstalladuplicatewebpacklocaltothisproject,whichisobviouslyredundant.
![Page 523: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/523.jpg)
Forthepurposeofthisrecipe,itishelpfultohaveithere.Thisisbecauseyoucanensurethatasinglenpminstallonthecommandlinewillfetchallthepackagesyouneedoffthebat,andthiswillletyouspecifytheversionyouwantwithintheproject.
Now,whenyouexecutenpmstart,thefollowingoccurs:
TypeScriptdoesaninitialcompilationof.tsfilesinto.jsfiles.WebpackdoesaninitialbundlingofalltheJSfilesintoasinglebundle.jsinthedist/directory.Simultaneously,lite-serverisstarted,theTypeScriptcompilerwatcherisstarted,andtheWebpackwatcherisstarted.Upona.tsfilechange,TypeScriptwillcompileitintoa.jsfile,andWebpackwillpickupthatfilechangeandrebundleitintobundle.js.Thelite-serverwillseethatbundle.jsischangedandreloadthepage,soyoucanseethechangesbeingupdatedautomatically.
Tip
Withoutspecifyingtheconfigurationsmoreclosely,theTypeScript,Webpack,andthelite-serverfilewatchlistswillusetheirdefaultsettings,whichmaybetoobroadandthereforewouldwatchfilestheydonotcareabout.Ideally,TypeScriptwouldonlywatch.tsfiles(whichdoesthiswithyourtsconfig.json),Webpackwouldonlywatch.html,.js,and.cssfiles,andlite-serverwouldonlywatchthefilesitactuallyservestotheclient.
![Page 524: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/524.jpg)
SeealsoIncorporatingshimsandpolyfillsintoWebpackgivesyouahandywayofmanagingAngular2polyfilldependenciesHTMLgenerationwithhtml-webpack-pluginshowsyouhowyoucanconfigureannpmpackagetoaddcompiledfilestoyourHTMLautomatically
![Page 525: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/525.jpg)
IncorporatingshimsandpolyfillsintoWebpackSofar,thishasbeenamuchcleanerimplementation,butyoustillhavethetwodanglingshimsinsidetheindex.htmlfile.You'vepareddownindex.htmlsuchthatitisnowrequestingonlyahandfulofJSfilesinsteadofeachmoduletargetindividually,butyoucangoevenfurtherandbundlealltheJSfilesintoasinglefile.
Thechallengeinthisisthatbrowsershimsaren'tdeliveredviamodules;inotherwords,therearen'tanyotherfilesthatwillimportthesetousethem.Theyjustassumetheiruseisavailable.Therefore,thestandardWebpackbundlingwon'tpickupthesetargetsandincludetheminthebundledfile.
Note
Thecode,links,andaliveexampleofthisareavailableathttp://ngcookbook.herokuapp.com/7479/.
![Page 526: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/526.jpg)
GettingreadyYoushouldcompletetheMigratingtheminimumviableapplicationtoWebpackbundlingrecipefirst,whichwillgiveyouallthesourcefilesneededforthisrecipe.
![Page 527: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/527.jpg)
Howtodoit...Thereareanumberofwaystogoaboutdoingthis,includingsomethatinvolvetheadditionofWebpackplugins,butthere'sanextremelysimplewayaswell:justaddtheimportsmanually.
Createanewpolyfills.ts:
[src/polyfills.ts]
import"reflect-metadata";
import"zone.js";
Importthismodulefrommain.ts:
[src/main.ts]
import'./polyfills';
import{platformBrowserDynamic}
from'@angular/platform-browser-dynamic';
import{AppModule}from'./app/app.module';
platformBrowserDynamic().bootstrapModule(AppModule);
Finally,cleanupindex.html:
[index.html]
<html>
<head>
<title>Angular2MinimumViableApplication</title>
<body>
<app-root></app-root>
<scriptsrc="dist/bundle.js"></script>
</body>
</html>
Now,Webpackshouldbeabletoresolvetheshimimports,andalltheneededfileswillbeincludedinsidebundle.js.
![Page 528: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/528.jpg)
Howitworks...TheonlyreasonthatthepolyfillsarenotdiscoveredbyWebpackisbecausetheyarenotrequiredanywhereintheapplication.Rather,anywheretheyareusedleadstotheassumptionthattheexposedtargets,suchasZone,havepreviouslybeenmadeavailable.Therefore,itiseasyforyoutosimplyimportthemattheverytopofyourapplication,whichhasawell-definedpointinthecode.WiththisWebpack,youwillbeabletodiscovertheexistenceofpolyfillsandincorporatethemintothegeneratedbundle.
![Page 529: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/529.jpg)
SeealsoMigratingtheminimumviableAngular2applicationtoWebpackbundlingdescribeshowtointegrateWebpackintoyourAngularapplicationbuildprocessHTMLgenerationwithhtml-webpack-pluginshowsyouhowyoucanconfigureannpmpackagetoaddcompiledfilestoyourHTMLautomatically
![Page 530: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/530.jpg)
HTMLgenerationwithhtml-webpack-pluginIdeally,youwouldliketobeabletohaveWebpackmanagethebundledfileanditsinjectionintothetemplate.Bydefault,Webpackisunabletodothis,asitisonlyconcernedwiththefilesrelatedtoscripting.Fortunately,WebpackoffersanextremelypopularpluginthatallowsyoutoexpandthescopeofWebpack'sfileconcerns.
Note
Thecode,links,andaliveexampleofthisareavailableathttp://ngcookbook.herokuapp.com/7185/.
![Page 531: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/531.jpg)
GettingreadyInstallthepluginandaddittodevDependenciesofpackage.jsonwiththefollowing:
npminstallhtml-webpack-plugin--save-dev
![Page 532: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/532.jpg)
Howtodoit...First,you'llneedtoincorporatethepluginintopackage.jsonifitisn'talready:
[package.json]
{
"name":"mva-bundling",
"scripts":{
"start":"tsc&&webpack&&concurrently'npmruntsc:w'
'npmrunwp:w''npmrunlite'",
"lite":"lite-server",
"postinstall":"npminstall-S@types/node@types/core-js",
"tsc":"tsc",
"tsc:w":"tsc-w",
"wp":"webpack",
"wp:w":"webpack--watch"
},
"dependencies":{
"@angular/common":"2.0.0",
"@angular/compiler":"2.0.0",
"@angular/core":"2.0.0",
"@angular/platform-browser":"2.0.0",
"@angular/platform-browser-dynamic":"2.0.0",
"core-js":"^2.4.1",
"reflect-metadata":"^0.1.3",
"rxjs":"5.0.0-beta.12",
"zone.js":"^0.6.23"
},
"devDependencies":{
"concurrently":"^2.2.0",
"html-webpack-plugin":"^2.22.0",
"imports-loader":"^0.6.5",
"lite-server":"^2.2.2",
"typescript":"^2.0.2",
"webpack":"^1.13.2"
}
}
Oncethismoduleisinstalled,defineitsoperationinsidetheWebpackconfig:
[webpack.config.js]
varHtmlWebpackPlugin=require('html-webpack-plugin');
module.exports={
entry:"./src/main.js",
output:{
path:"./dist",
filename:"bundle.js"
},
plugins:[newHtmlWebpackPlugin({
![Page 533: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/533.jpg)
template:'./src/index.html'
})]
};
ThisspecifiestheoutputHTMLfilethatwillservetheentireapplication.SincethepluginwillautomaticallygeneratetheHTMLfileforyou,you'llneedtomodifytheexistingonethatisdesignatedasthetemplate:
[src/index.html]
<html>
<head>
<title>Angular2MinimumViableApplication</title>
</head>
<body>
<app-root></app-root>
</body>
</html>
Finally,becauseindex.htmlisnowservedoutofthedist/directory,you'llneedtoconfigurethedevelopmentservertoservefilesoutofthere.Sincelite-serverisjustawrapperforBrowserSync,youcanspecifybaseDirinsideabs-config.jsonfile,whichyoushouldcreatenow:
[bs-config.json]
{
"server":{"baseDir":"./dist"}
}
![Page 534: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/534.jpg)
Howitworks...Webpackisverymuchawareofthebundlethatitiscreating,andsoitmakessensethatyouwouldbeabletomaintainareferencetothisbundle(orbundles)anddirectlypipethosepathsintoanindex.htmlfile.ThepluginwillappendthescriptsattheendofthebodytoensuretheentireinitialDOMispresent.
![Page 535: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/535.jpg)
SeealsoMigratingtheminimumviableAngular2applicationtoWebpackbundlingdescribeshowtointegrateWebpackintoyourAngularapplicationbuildprocessIncorporatingshimsandpolyfillsintoWebpackgivesyouahandywayofmanagingAngular2polyfilldependencies
![Page 536: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/536.jpg)
SettingupanapplicationwithAngularCLIIntandemwiththeAngular2framework,theAngularteamalsosupportsabuildtoolthatcancreate,build,andrunanAngular2applicationrightoutofthebox.What'smore,itincludesageneratorthatcancreatestyle-guide-compliantfilesanddirectoriesforvariousapplicationpiecesfromthecommandline.
Note
Thecode,links,andaliveexampleofthisareavailableathttp://ngcookbook.herokuapp.com/4068/.
![Page 537: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/537.jpg)
GettingreadyAngular'sCLIisannpmmodule.You'llneedtohaveNode.jsinstalledonyoursystem—v7.0.0orlaterworksasasuitablerecentreleasethat'scompatiblewiththeAngularCLI.
Tip
Thereisanotheroptionyouhave:manageyourNodeenvironmentswithnvm,theNodeversionmanager.ThisgivesyouatransparentwrapperthatcanseparatelymanageenvironmentswiththeNodeversionaswellastheinstallednpmpackagesinthatenvironment.Ifyou'veeverdealtwithmessinessinvolvingsudonpminstall-g,youwillbedelightedbythistool.
OnceNodeisinstalled(andifyouusenvm,you'veselectedwhichenvironmenttouse),installtheAngularCLI:
npminstall-gangular-cli
![Page 538: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/538.jpg)
Howtodoit...AngularCLIcomesreadytogenerateafullyworkingAngular2application.TocreateanapplicationnamedPublisherApp,invokethefollowingcommand:
ngnewpublisher
TheAngularCLIwilldutifullyassembleallthefilesneededforaminimalAngular2TypeScriptapplication,initializeaGitrepository,andinstallalltherequirednpmdependencies.Thecreatedfilelistshouldlookasfollows:
createREADME.md
createsrc/app/app.component.css
createsrc/app/app.component.html
createsrc/app/app.component.spec.ts
createsrc/app/app.component.ts
createsrc/app/app.module.ts
createsrc/app/index.ts
createsrc/app/shared/index.ts
createsrc/environments/environment.prod.ts
createsrc/environments/environment.ts
createsrc/favicon.ico
createsrc/index.html
createsrc/main.ts
createsrc/polyfills.ts
createsrc/styles.css
createsrc/test.ts
createsrc/tsconfig.json
createsrc/typings.d.ts
createangular-cli.json
createe2e/app.e2e-spec.ts
createe2e/app.po.ts
createe2e/tsconfig.json
create.gitignore
createkarma.conf.js
createpackage.json
createprotractor.conf.js
createtslint.json
Usecdpublishertomoveintotheapplication'sdirectory,whichwillallowyoutoinvokealltheproject-specificAngularCLIcommands.
Runningtheapplicationlocally
Torunthisapplication,startuptheserver:
ngserve
Thedefaultapplicationpagewillbeavailableonlocalhost:4200.
![Page 539: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/539.jpg)
Testingtheapplication
Toruntheapplication'sunittests,usethis:
ngtest
Toruntheapplication'send-to-endtests,usethis:
nge2e
![Page 540: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/540.jpg)
Howitworks...Let'sroughlygothroughwhateachofthesefilesoffertoyou:
Projectconfigurationfilesangular-cli.jsonistheconfigurationfilespecifyinghowtheAngularCLIshouldbundleandmanageyourapplication'sfilesanddirectories.package.jsonisthenpmpackageconfigurationfile.Insideit,you'llfindscriptsandcommand-linetargetsthattheAngularCLIcommandswilltieinto.
TypeScriptconfigurationfilestslint.jsonspecifiestheconfigurationforthetslintnpmpackage.TheAngularCLIcreatesforyoualintcommandfor.tsfileswithnpmrunlint.src/tsconfig.jsonispartoftheTypeScriptspecification;itinformsthecompilerthatthisistherootoftheTypeScriptproject.Itscontentsdefinehowthecompilationshouldoccur,anditspresenceenablesthetsccommandtousethisdirectoryastherootcompilationdirectory.e2e/tsconfig.jsonistheend-to-endTypeScriptcompilerconfigurationfile.src/typings.d.tsisthespecificationfileforthetypingsnpmmodule.ItallowsyoutodescribehowexternalmodulesshouldbewrappedandincorporatedintotheTypeScriptcompiler.Thistypings.d.tsfilespecifiestheSystemnamespaceforSystemJS.
Testconfigurationfileskarma.conf.jsistheconfigurationfileforKarma,thetestrunnerfortheprojectprotractor.conf.jsistheconfigurationfileforProtractor,theend-to-endtestframeworkfortheprojectsrc/test.tsdescribestotheKarmaconfigurationhowtostartupthetestrunnerandwheretofindthetestfilesthroughouttheapplication
Coreapplicationfilessrc/index.htmlistherootapplicationfilethatisservedtoruntheentiresingle-pageapplication.CompiledJSandotherstaticassetswillbeautomaticallyaddedtothisfilebythebuildscript.src/main.tsisthetop-levelTypeScriptfilethatservestobootstrapyourapplicationwithitsAppModuledefinition.src/polyfills.tsisjustafilethatkeepsthelonglistofimportedpolyfillmodulesoutofmain.ts.src/styles.cssistheglobalapplicationstylefile.
Environmentfilessrc/environments/environment.tsisthedefaultenvironmentconfigurationfile.Specifyingdifferentenvironmentswhenbuildingandtestingyourapplicationwilloverride
![Page 541: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/541.jpg)
these.src/environments/environment.prod.tsistheprodenvironmentconfiguration,whichcanbeselectedfromthecommandlinewith--prod.
AppComponentfiles
EveryAngular2applicationhasatop-levelcomponent,andAngularCLIcallsthisAppComponent.
src/app/app.component.tsisthecoreTypeScriptcomponentclassdefinition.Thisiswhereallofthelogicthatcontrolsthiscomponentshouldgo.src/app/app.component.htmlandsrc/app/app.component.cssarethetemplatingandstylingfilesspecifictoAppComponent.RecallthatstylingspecifiedinComponentMetadataisencapsulatedonlytothiscomponent.src/app/app.module.tsistheNgModuledefinitionforAppComponent.src/app/index.tsisthefilethatinformstheTypeScriptcompilerwhichmodulesareavailableinsidethisdirectory.Anymodulesthatareexportedinthisdirectoryandusedelsewhereintheapplicationmustbespecifiedhere.
AppComponenttestfilessrc/app/app.component.spec.tsaretheunittestsforAppComponente2e/app.e2e-spec.tsaretheend-to-endtestsforAppComponente2e/app.po.tsisthepageobjectdefinitionforuseinAppComponentend-to-endtesting
![Page 542: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/542.jpg)
There'smore...Whenlookingattheentireprojectcodebase,thebulkofthefilesbreakdownintofourcategories:
Filesthataresenttothebrowser:Thisincludesyouruncompiled/unminifiedapplicationfilesandalsothecompiled/minifiedfiles.Whendevelopingyourapplication,youwanttobeabletotestyourapplicationlocallywithuncompiledfiles.Youalsowanttobeabletoshipyourapplicationtoproductionwithcompiledandminifiedfiles,whichoptimizesbrowserperformance.Theuncompiled/unminifiedfilesarecollectedbythebuildscripts,tobecombinedintothecompiled/minifiedfiles.Filesusedfortesting:Thetestfilesthemselvesareusuallysprinkledthroughoutyourapplicationandarenotcompiled.Thiscategoryalsoincludesconfigurationfilesandtestscriptsthatcontrolwhatactuallyhappenswhenyourunthetestsandwherethetestrunnerscanfindthetestfilesinyourprojectdirectory.Filesthatcontrolyourdevelopmentenvironment:Dependingonyoursetup,yoursingle-pageapplicationmayrunbyitself(withnobackendcodebase),oritmaybebuiltalongsideasubstantialbackendcodebasethatexposesAPIsandotherserver-sidebehavior.Quickstartrepositoriesorapplicationgenerators(suchasAngularCLI)usuallyprovideyouwithaminimalHTTPservertogetyouoffthegroundandserveyourstaticassetstothebrowser.Howexactlyyourunyourdevelopmentenvironmentwillvary,butthefilesinthiscategorymanagehowyourapplicationwillworkbothlocallyandinproduction.Filesthatcompileyourapplication:Thefilesyoueditinyourcodeeditorofchoicearenottheonesthatreachthebrowserinaproductionapplication.Buildscriptsareusuallysetuptocombineallyourfilesintothesmallestandfewestfilespossible.Frequently,thiswillmeanasinglecompiledJSandcompiledCSSfiledeliveredtothebrowser.Thesefileswillminifyyourcodebase,compileTypeScriptintovanillaJavaScript,selectenvironmentfilesandothercontext-specificfiles,andorganizefileincludesandotherfilesandmoduledependenciessothatyourapplicationworkswhenit'scompiled.Usually,thefilestheycreatewillbedumpedintoadistdirectory,whichwillcontainfilesthatareservedtothebrowserinproduction.
![Page 543: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/543.jpg)
SeealsoComposingpackage.jsonforaminimumviableAngular2applicationdescribeshowallthepiecesworkforthecorenodeprojectfileConfiguringTypeScriptforaminimumviableAngular2applicationtalksabouthowtoconfigurecompilationtosupportanAngular2projectPerformingin-browsertranspilationwithSystemJSdemonstrateshowSystemJScanbeusedtoconnectuncompiledstaticfilestogetherComposingapplicationfilesforaminimumviableAngular2applicationwalksyouthroughhowtocreateanextremelysimpleAngular2appfromscratchIncorporatingshimsandpolyfillsintoWebpackgivesyouahandywayofmanagingAngular2polyfilldependencies
![Page 544: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/544.jpg)
Chapter9.Angular2TestingThischapterwillcoverthefollowingrecipes:
CreatingaminimumviableunittestsuitewithKarma,Jasmine,andTypeScriptWritingaminimumviableunittestsuiteforasimplecomponentWritingaminimumviableend-to-endtestsuiteforasimpleapplicationUnittestingasynchronousserviceUnittestingacomponentwithaservicedependencyusingstubsUnittestingacomponentwithaservicedependencyusingspies
![Page 545: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/545.jpg)
IntroductionWritingtestsislikebrushingyourteeth.Youcangetawaywithskippingitforawhile,butit'llcatchupwithyoueventually.
Theworldoftestingisawashwithconflictingideologies,platitudes,andgrandstanding.What'smore,thereisadizzyingarrayoftoolsavailablethatallowyoutowriteandrunyourtestsindifferentways,automateyourtests,oranalyzeyourtestcoverageorcorrectness.Ontopofthat,eachdeveloper'sutilityandstyleoftestingisunique;someonehackingawayatapre-seedstartupwillnothavethesamerequirementsasadeveloperthatispartofalargeteaminsideaFortune500company.
ThegoalofthischapteristowalkyouthroughtheavailabletestingutilitiesthattheAngular2frameworkcomeswithoutofthebox,aswellassomestrategiesfordeployingtheseutilities.TherecipeswillfocusonunittestsratherthanE2Etests,asanoverwhelmingmajorityofrobusttestsuiteswillbeunittests.
![Page 546: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/546.jpg)
CreatingaminimumviableunittestsuitewithKarma,Jasmine,andTypeScriptBeforeyoujumpintotheintricaciesoftestinganAngular2application,it'simportanttofirstexaminethesupportinginfrastructurethatwillmakerunningthesetestspossible.ThebulkofofficialAngularresourcesoffertestsontopofKarmaandJasmine,andthere'snoreasontorocktheboatonthisone,asthesearebothfinetestingtools.Thatsaid,it'sawholenewworldwithTypeScriptinvolved,andusingthemintestswillrequiresomeconsiderations.
Thisrecipewilldemonstratehowtoputtogetheraverysimpleunittestsuite.ItwilluseKarmaandJasmineasthetestinfrastructure,TypeScriptandWebpackforcompilationandmodulesupport,andPhantomJSasthetestbrowser.Forthoseunfamiliarwiththesetools,here'sabitaboutthem:
Karmaisaunittestrunner.YourunteststhroughKarmaonthecommandline.Ithastheabilitytostartupatestserverthatunderstandshowtofindtestfilesandservethemtothetestbrowser.Jasmineisatestframework.Whenyouusekeywordssuchas"it"and"describe,"rememberthattheyarepartofJasmineunittests.ItintegrateswithKarmaandunderstandshowtoexposeandrunthetestsyou'vewritten.PhantomJSisaheadlesswebkitbrowser.(HeadlessmeansitrunsasaprocessthatdoesnothaveavisibleuserinterfacebutstillconstructsaDOMandhasaJSruntime.)Unittestsrequireabrowsertorun,astheJavaScriptunittestsaredesignedtoexecuteinsideabrowserruntime.Karmasupportsalargenumberofbrowserpluginstorunthetestson,includingstandardbrowserssuchasChromeandFirefox.Ifyouweretoincorporatethesebrowserplugins,Karmawouldstartupaninstanceofthebrowserandrunthetestsinsideit.Forthepurposeofcreatingaminimumviableunittestsuite,youarefinedoingthetestinginsideaheadlessbrowser,whichwillcleanlyreportitsresultstothecommandline.Ifyouwanttorunyourtestsinsideanactualbrowser,Karmawillexposetheserverataspecifiedport,whichyoucanaccessdirectly,forexample,visitinghttp://localhost:9876inthedesiredtestbrowser.
Note
Thecode,links,andaliveexamplerelatedtothisrecipeareavailableathttp://ngcookbook.herokuapp.com/3998/.
![Page 547: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/547.jpg)
GettingreadyStartoutwithapackage.jsonfile:
[package.json]
{}
Note
ThisstillneedstobeavalidJSONfile,asnpmneedstobeabletoparseitandaddtoit.
![Page 548: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/548.jpg)
Howtodoit...Startoffbycreatingthefilethatwillbetested.YouintendtouseTypeScript,sogoaheadanduseitssyntaxhere:
[src/article.ts]
exportclassArticle{
title:string=
"LabMiceStrikeforImprovedWorkingConditions,Benefits"
}
Writingaunittest
WiththeArticleclassdefined,youcannowimportitintoanewtestfile,article.spec.ts,anduseit.
Note
Jasminetestfiles,byconvention,aresuffixedwith.spec.ts.TestfilesgeneratedbytheAngularCLIwillexistalongsidethefiletheytest,butbynomeansisthismandatory.YoucandefineyourconventioninsideyourKarmaconfigurationlateron.
StartoffbyimportingtheArticleclassandcreateanemptyJasminetestsuiteusingdescribe:
[src/article.spec.ts]
import{Article}from'./article';
describe('Articleunittests',()=>{
});
Note
describedefinesaspecsuite,whichincludesastringtitlecalledArticleunittests,andananonymousfunction,whichcontainsthesuite.Aspecsuitecanbenestedinsideanotherspecsuite.
Insideadescribesuitefunction,youcandefinebeforeEachandafterEach,whicharefunctionsthatexecutebeforeandaftereachunittestisdefinedinsidethesuite.Therefore,itispossibletodefinenestedsetuplogicforunittestsusingnesteddescribeblocks.
Insidethespecsuitefunction,writetheunittestthatisusingit:
[src/article.spec.ts]
import{Article}from'./article';
![Page 549: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/549.jpg)
describe('Articleunittests',()=>{
it('Hascorrecttitle',()=>{
leta=newArticle();
expect(a.title)
.toBe("LabMiceStrikeforImprovedWorkingConditions,
Benefits");
});
});
NotethatboththecodeandthetestarewritteninTypeScript.
ConfiguringKarmaandJasmine
First,installKarma,theKarmaCLI,Jasmine,andtheKarmaJasmineplugin:
npminstallkarmajasmine-corekarma-jasmine--save-dev
npminstallkarma-cli-g
Alternately,ifyouwanttosaveafewkeystrokes,thefollowingisequivalent:
npmi-Dkarmajasmine-corekarma-jasmine
npmikarma-cli-g
Karmareadsitsconfigurationoutofakarma.conf.jsfile,socreatethatnow:
[karma.conf.js]
module.exports=function(config){
config.set({
})
}
KarmaneedstoknowhowtofindthetestfilesandalsohowtouseJasmine:
[karma.conf.js]
module.exports=function(config){
config.set({
frameworks:[
'jasmine'
],
files:[
'src/*.spec.js'
],
plugins:[
'karma-jasmine',
]
})
}
ConfiguringPhantomJS
![Page 550: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/550.jpg)
PhantomJSallowsyoutodirecttestsentirelyfromthecommandline,butKarmaneedstounderstandhowtousePhantomJS.InstallthePhantomJSplugin:
npminstallkarma-phantomjs-launcher--save-dev
Next,incorporatethispluginintotheKarmaconfig:
[karma.conf.js]
module.exports=function(config){
config.set({
browsers:[
'PhantomJS'
],
frameworks:[
'jasmine'
],
files:[
'src/*.spec.js'
],
plugins:[
'karma-jasmine',
'karma-phantomjs-launcher'
]
})
}
KarmanowknowsithastorunthetestsinPhantomJS.
CompilingfilesandtestswithTypeScript
Ifyou'repayingattentionclosely,you'llnotethattheKarmaconfigisreferencingtestfilesthatdonotexist.Sinceyou'reusingTypeScript,youmustcreatethesefiles.InstallTypeScriptandtheJasminetypedefinitions:
npminstalltypescript@types/jasmine--save-dev
Addscriptdefinitionstoyourpackage.json:
[package.json]
{
"scripts":{
"tsc":"tsc",
"tsc:w":"tsc-w"
},
"devDependencies":{
"@types/jasmine":"^2.5.35",
"jasmine-core":"^2.5.2",
"karma":"^1.3.0",
"karma-cli":"^1.0.1",
"karma-jasmine":"^1.0.2",
![Page 551: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/551.jpg)
"karma-phantomjs-launcher":"^1.0.2",
"typescript":"^2.0.3"
}
}
Createatsconfig.jsonfile.Sinceyou'refinewiththecompiledfilesresidinginthesamedirectory,asimpleonewilldo:
[tsconfig.json]
{
"compilerOptions":{
"target":"es5",
"module":"commonjs",
"moduleResolution":"node"
}
}
Tip
Youwouldprobablynotdoitthiswayforaproductionapplication,butforaminimumviablesetup,thiswilldoinapinch.Aproductionapplicationwouldmostlikelyputcompiledfilesintoanentirelydifferentdirectory,frequentlynameddist/.
IncorporatingWebpackintoKarma
Ofcourse,you'llneedawayofresolvingmoduledefinitionsforcodeandtests.Karmaisn'tcapableofdoingthisonitsown,soyou'llneedsomethingtodothis.Webpackisperfectlysuitableforsuchatask,andKarmahasaterrificpluginthatallowsyoutopreprocessyourtestfilesbeforetheyreachthebrowser.
InstallWebpackanditsKarmaplugin:
npminstallwebpackkarma-webpack--save-dev
ModifytheKarmaconfigtospecifyWebpackasthepreprocessor.Thisallowsyourmoduledefinitionstoberesolvedproperly:
[karma.conf.js]
module.exports=function(config){
config.set({
browsers:[
'PhantomJS'
],
frameworks:[
'jasmine'
],
files:[
'src/*.spec.js'
![Page 552: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/552.jpg)
],
plugins:[
'karma-webpack',
'karma-jasmine',
'karma-phantomjs-launcher'
],
preprocessors:{
'src/*.spec.js':['webpack']
}
})
}
Writingthetestscript
YoucankickofftheKarmaserverwiththefollowing:
karmastartkarma.conf.js
Thiswillinitializethetestserverandrunthetests,watchingforchangesandrerunningthetests.However,thissidestepsthefactthattheTypeScriptfilesrequirecompilationinthefilesthatKarmaiswatching.TheTypeScriptcompileralsohasafilewatcherthatwillrecompileonthefly.Youwouldlikebothofthesetorecompilewheneveryousavechangestoasourcecodefile,soitmakessensetorunthemsimultaneously.Theconcurrentlypackageissuitableforthistask.
Note
concurrentlynotonlyallowsyoutorunmultiplecommandsatonce,butalsotokillthemallatonce.Withoutit,akillsignalfromthecommandlinewouldonlytargetwhicheverprocesswasrunmostrecently,ignoringtheprocessthatisrunninginthebackground.
Installconcurrentlywiththefollowing:
npminstallconcurrently--save-dev
Finally,buildyourtestscripttorunKarmaandtheTypeScriptcompilersimultaneously:
[package.json]
{
"scripts":{
"test":"concurrently'npmruntsc:w''karmastart
karma.conf.js'",
"tsc":"tsc",
"tsc:w":"tsc-w"
},
"devDependencies":{
"@types/jasmine":"^2.5.35",
"concurrently":"^3.1.0",
"jasmine-core":"^2.5.2",
"karma":"^1.3.0",
"karma-cli":"^1.0.1",
![Page 553: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/553.jpg)
"karma-jasmine":"^1.0.2",
"karma-phantomjs-launcher":"^1.0.2",
"karma-webpack":"^1.8.0",
"typescript":"^2.0.3",
"webpack":"^1.13.2"
}
}
Withthis,youshouldbeabletorunyourtests:
npmtest
Ifeverythingisdonecorrectly,theKarmaservershouldbootupandrunthetests,outputtingthefollowing:
PhantomJS2.1.1(Linux0.0.0):Executed1of1SUCCESS(0.038secs/0.001
secs)
![Page 554: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/554.jpg)
Howitworks...KarmaandJasmineworktogethertodelivertestfilestothetestbrowser.TypeScriptandWebpackaretaskedwithconvertingyourTypeScriptfilesintoaJavaScriptformatthatwillbeusablebythetestbrowser.
![Page 555: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/555.jpg)
There'smore...AninterestingconsiderationofthissetupishowexactlyTypeScriptishandled.
BoththecodeandtestfilesarewritteninTypeScript,whichallowsyoutousetheES6modulenotation,asopposedtosomemix-and-matchstrategy.However,thisleavesyouwithsomechoicestomakeonhowthetestsetupshouldwork.
Thetestsneedtobeabletousedifferentpiecesofyourapplicationinapiecemealfashion,asopposedtothestandardapplicationsetupwhereallthemodulesgetpulledtogetheratonce.ThisrecipehadTypeScriptindependentlycompilethe.tsfiles,anditthendirectedKarmatowatchtheresultant.jsfiles.Thisisperhapseasiertocomprehendbysomeonewhoiseasingintotests,butitmightnotbethemostefficientwaytogoaboutit.KarmaalsosupportsTypeScriptplugins,whichallowyoutopreprocessthefilesintoTypeScriptbeforehandingthemofftotheWebpackpreprocessor.
Karmasupportsthechainingofpreprocesssteps,whichwillbeusefulifyouwanttocompiletheTypeScriptontheflyaspartofpreprocessing.
![Page 556: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/556.jpg)
SeealsoWritingaminimumviableunittestsuiteforasimplecomponentshowsyouabasicexampleofunittestingAngular2componentsUnittestingasynchronousservicedemonstrateshowaninjectionismockedinunittestsUnittestingacomponentwithaservicedependencyusingStubsshowshowyoucancreateaservicemocktowriteunittestsandavoiddirectdependenciesUnittestingacomponentwithaservicedependencyusingSpiesshowshowyoucankeeptrackofservicemethodinvocationsinsideaunittest
![Page 557: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/557.jpg)
WritingaminimumviableunittestsuiteforasimplecomponentUnittestsarethebreadandbutterofyourapplicationtestingprocess.Theyexistasacompaniontoyoursourcecode,andmostofthetime,thebulkofyourapplicationtestswillbeunittests.Theyarelightweight,runquickly,areeasytoreadandreasonabout,andcangivecontextastohowthecodeshouldbeusedandhowitmightbehave.
SettingupKarma,Jasmine,TypeScript,andAngular2alongwithalltheconnectingconfigurationsbetweenthemisabitofanimposingtask;itwasdeemedtobeoutofthescopeofthischapter.It'snotaveryinterestingdiscussiontogetallofthemtoworktogether,especiallysincetherearealreadysomanyexampleprojectsthathaveputtogethertheirownsetupsforyou.It'sfarmoreinterestingtodivedirectlyintotheteststhemselvesandseehowtheycanactuallyinteractwithAngular2.
Note
Thecode,links,andaliveexamplerelatedtothisrecipeareavailableathttp://ngcookbook.herokuapp.com/3935/.
![Page 558: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/558.jpg)
GettingreadyThisrecipewillassumeyouareusingaworkingAngular2testingenvironment.TheoneprovidedintheapplicationgeneratedbytheAngularCLIisideal.Testscanberuninthisenvironmentwiththefollowingcommandinsidetheprojectdirectory:
ngtest
Beginwiththefollowingcomponent:
[src/app/article/article.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'app-article',
template:`
<h1>
{{title}}
</h1>
`
})
exportclassArticleComponent{
title:string='CaptainHookSuesOverSporkSnafu';
}
Yourgoalistoentirelyfleshoutarticle.component.spec.tstotestthisclass.
![Page 559: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/559.jpg)
Howtodoit...ThesimplestpossibletestyoucanthinkofistheonethatwillsimplycheckthatyouareabletoinstantiateaninstanceofArticleComponent.Beginwiththattest:
[src/app/article/article.component.spec.ts]
import{ArticleComponent}from'./article.component';
describe('Component:Article',()=>{
it('shouldcreateaninstance',()=>{
letcomponent=newArticleComponent();
expect(component).toBeTruthy();
});
});
Nothingtrickyisgoingonhere.SinceArticleComponentisjustaplainoldTypeScriptclass,nothingispreventingyoufromcreatinganinstanceandinspectingitinthememory.
However,forittoactuallybehavelikeanAngular2component,you'llneedsomeothertools.
UsingTestBedandasync
WhenyoutrytopuppetanAngular2environmentforthecomponentinatest,thereareanumberofconsiderationsyou'llneedtoaccountfor.First,Angular2unittestsheavilyrelyuponTestBed,whichcanbethoughtofasyourtestingmultitool.
ThedenominationofunittestswhendealingwithacomponentinvolvesComponentFixture.TestBed.createComponent()willcreateafixturewrappinganinstanceofthedesiredcomponent.
Tip
Theneedforfixturesiscenteredinhowunittestsaresupposedtowork.AnArticleComponentdoesnotmakesensewheninstantiatedasitwaswiththeinitialtestyouwrote.ThereisnoDOMelementtoattachto,norunningapplication,andsoon.Itdoesn'tmakesenseforthecomponentunitteststohaveanexplicitdependencyonthesethings.So,ComponentFixtureisAngular'swayoflettingyoutestonlytheconcernsofthecomponentasitwouldnormallyexist,withoutworryingaboutallthemessinessofitsinnatedependencies.
TheTestBedfixture'sasynchronousbehaviormandatesthatthetestlogicisexecutedinsideanasync()wrapper.
Tip
Theasync()wrappersimplyrunsthetestinsideitsownzone.Thisallowsthetestrunnertowait
![Page 560: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/560.jpg)
foralltheasynchronouscallsinsidethetesttocompletethembeforeendingthetest.
BeginbyimportingTestBedandasyncfromtheAngulartestingmoduleandputtogethertheskeletonfortwomoreunittests:
[src/app/article/article.component.spec.ts]
import{TestBed,async}from'@angular/core/testing';
import{ArticleComponent}from'./article.component';
describe('Component:Article',()=>{
it('shouldcreateaninstance',()=>{
letcomponent=newArticleComponent();
expect(component).toBeTruthy();
});
it('shouldhavecorrecttitle',async(()=>{
}));
it('shouldrendertitleinanh1tag',async(()=>{
}));
});
Nowthatyouhavetheskeletonsforthetwotestsyou'dliketowrite,it'stimetouseTestBedtodefinethetestmodule.Angular2componentsarepairedwithamoduledefinition,butwhenperformingunittests,you'llneedtousetheTestBedmodule'sdefinitionforthecomponenttoworkproperly.ThiscanbedonewithTestBed.configureTestModule(),andyou'llwanttoinvokethisbeforeeachtest.
Jasmine'sdescribeallowsyoutogroupbeforeEachandafterEachinsideit,anditisperfectforusehere:
[src/app/article/article.component.spec.ts]
import{TestBed,async}from'@angular/core/testing';
import{ArticleComponent}from'./article.component';
describe('Component:Article',()=>{
it('shouldcreateaninstance',()=>{
letcomponent=newArticleComponent();
expect(component).toBeTruthy();
});
describe('Async',()=>{
beforeEach(()=>{
TestBed.configureTestingModule({
declarations:[
ArticleComponent
],
});
});
![Page 561: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/561.jpg)
it('shouldhavecorrecttitle',async(()=>{
}));
it('shouldrendertitleinanh1tag',async(()=>{
}));
});
});
CreatingaComponentFixture
TestBedgivesyoutheabilitytocreateafixture,butyouhaveyettoactuallydoit.You'llneedafixtureforboththeasynctests,soitmakessensetodothisinbeforeEachtoo:
[src/app/article/article.component.spec.ts]
import{TestBed,async}from'@angular/core/testing';
import{ArticleComponent}from'./article.component';
describe('Component:Article',()=>{
letfixture;
it('shouldcreateaninstance',()=>{
letcomponent=newArticleComponent();
expect(component).toBeTruthy();
});
describe('Async',()=>{
beforeEach(()=>{
TestBed.configureTestingModule({
declarations:[
ArticleComponent
],
});
fixture=TestBed.createComponent(ArticleComponent);
}));
afterEach(()=>{
fixture=undefined;
});
it('shouldhavecorrecttitle',async(()=>{
}));
it('shouldrendertitleinanh1tag',async(()=>{
}));
});
});
Tip
Here,fixtureisassignedtoundefinedintheafterEachteardown.Thisistechnically
![Page 562: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/562.jpg)
superfluousforthepurposeofthesetests,butitisgoodtogetintothehabitofperformingarobustteardownofsharedvariablesinunittests.Thisisbecauseoneofthemostfrustratingthingstodebuginatestsuiteistestvariablebleed.Afterall,thesearejustfunctionsrunninginasequenceinabrowser.
Nowthatthefixtureisdefinedforeachtest,youcanuseitsmethodstoinspecttheinstantiatedcomponentindifferentways.
Forthefirsttest,you'dliketoinspecttheArticleComponentobjectitselffromwithinComponentFixture.ThisisexposedwiththecomponentInstanceproperty:
[src/app/article/article.component.spec.ts]
import{TestBed,async}from'@angular/core/testing';
import{ArticleComponent}from'./article.component';
describe('Component:Article',()=>{
letexpectedTitle='CaptainHookSuesOverSporkSnafu';
letfixture;
it('shouldcreateaninstance',()=>{
letcomponent=newArticleComponent();
expect(component).toBeTruthy();
});
describe('Async',()=>{
beforeEach(async(()=>{
TestBed.configureTestingModule({
declarations:[
ArticleComponent
],
});
fixture=TestBed.createComponent(ArticleComponent);
}));
afterEach(()=>{
fixture=undefined;
});
it('shouldhavecorrecttitle',async(()=>{
expect(fixture.componentInstance.title)
.toEqual(expectedTitle);
}));
it('shouldrendertitleinanh1tag',async(()=>{
}));
});
});
Forthesecondtest,youwantaccesstotheDOMthatthefixturehasattachedthecomponentinstanceto.TherootelementthatthecomponentistargetingisexposedwiththenativeElement
![Page 563: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/563.jpg)
property:
[src/app/article/article.component.spec.ts]
import{TestBed,async}from'@angular/core/testing';
import{ArticleComponent}from'./article.component';
describe('Component:Article',()=>{
letexpectedTitle='CaptainHookSuesOverSporkSnafu';
letfixture;
it('shouldcreateaninstance',()=>{
letcomponent=newArticleComponent();
expect(component).toBeTruthy();
});
describe('Async',()=>{
beforeEach(async(()=>{
TestBed.configureTestingModule({
declarations:[
ArticleComponent
],
});
fixture=TestBed.createComponent(ArticleComponent);
}));
afterEach(()=>{
fixture=undefined;
});
it('shouldhavecorrecttitle',async(()=>{
expect(fixture.componentInstance.title)
.toEqual(expectedTitle);
}));
it('shouldrendertitleinanh1tag',async(()=>{
expect(fixture.nativeElement.querySelector('h1')
.textContent).toContain(expectedTitle);
}));
});
});
Ifyourunthesetests,youwillnoticethatthelasttestwillfail.Thetestseesanemptystringinside<h1></h1>.Thisisbecauseyouarebindingavalueinthetemplatetoacomponentmember.Sincethefixturecontrolstheentireenvironmentsurroundingthecomponent,italsocontrolsthechangedetectionstrategy—which,here,istonotrununtilitistoldtodoso.YoucantriggeraroundofchangedetectionusingthedetectChanges()method:
[src/app/article/article.component.spec.ts]
import{TestBed,async}from'@angular/core/testing';
import{ArticleComponent}from'./article.component';
![Page 564: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/564.jpg)
describe('Component:Article',()=>{
letexpectedTitle='CaptainHookSuesOverSporkSnafu';
letfixture;
it('shouldcreateaninstance',()=>{
letcomponent=newArticleComponent();
expect(component).toBeTruthy();
});
describe('Async',()=>{
beforeEach(async(()=>{
TestBed.configureTestingModule({
declarations:[
ArticleComponent
],
});
fixture=TestBed.createComponent(ArticleComponent);
}));
afterEach(()=>{
fixture=undefined;
});
it('shouldhavecorrecttitle',async(()=>{
expect(fixture.componentInstance.title)
.toEqual(expectedTitle);
}));
it('shouldrendertitleinanh1tag',async(()=>{
fixture.detectChanges();
expect(fixture.nativeElement.querySelector('h1')
.textContent).toContain(expectedTitle);
}));
});
});
Withthis,youshouldseeKarmarunandpassallthreetests.
![Page 565: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/565.jpg)
Howitworks...Whenitcomestotestingcomponents,fixtureisyourfriend.Itgivesyoutheabilitytoinspectandmanipulatethecomponentinanenvironmentthatitwillbehavecomfortablyin.Youarethenabletomanipulatetheinstancesofinputmadetothecomponent,aswellasinspecttheiroutputandresultantbehavior.
Thisisthecoreofunittesting:the"thing"youaretesting—here,acomponentclass—shouldbetreatedasablackbox.Youcontrolwhatgoesintothebox,andyourtestsshouldmeasureanddefinewhattheyexpecttocomeoutofthebox.Ifthetestsaccountforallthepossiblecasesofinputandoutput,thenyouhaveachieved100percentunittestcoverageofthatthing.
![Page 566: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/566.jpg)
SeealsoCreatingaminimumviableunittestsuitewithKarma,Jasmine,andTypeScriptgivesyouagentleintroductiontounittestswithTypeScriptUnittestingasynchronousservicedemonstrateshowaninjectionismockedinunittestsUnittestingacomponentwithaservicedependencyusingStubsshowshowyoucancreateaservicemocktowriteunittestsandavoiddirectdependenciesUnittestingacomponentwithaservicedependencyusingSpiesshowshowyoucankeeptrackofservicemethodinvocationsinsideaunittest
![Page 567: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/567.jpg)
Writingaminimumviableend-to-endtestsuiteforasimpleapplicationEnd-to-endtesting(ore2eforshort)isontheotherendofthespectrumasfarasunittestingisconcerned.Theentireapplicationexistsasablackbox,andtheonlycontrolsatyourdisposal—forthesetests—areactionstheusermighttakeinsidethebrowser,suchasfiringclickeventsornavigatingtoapage.Similarly,thecorrectnessoftestsisonlyverifiedbyinspectingthestateofthebrowserandtheDOMitself.
Moreexplicitly,anend-to-endtestwill(insomeform)startupanactualinstanceofyourapplication(orasubsetofit),navigatetoitinanactualbrowser,dostufftoapage,andlooktoseewhathappensonthepage.It'sprettymuchascloseasyouaregoingtogettohavinganactualpersonsitdownanduseyourapplication.
Inthisrecipe,you'llputtogetheraverybasicend-to-endtestsuitesothatyoumightbetterunderstandtheconceptsinvolved.
Note
Thecode,links,andaliveexamplerelatedtothisrecipeareavailableathttp://ngcookbook.herokuapp.com/8985/.
![Page 568: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/568.jpg)
GettingreadyYou'llbeginwiththecodefilescreatedintheminimumviableapplicationrecipefromChapter8,ApplicationOrganizationandManagement.Themostimportantfilesthatyou'llbeeditinghereareAppComponentandpackage.json:
[package.json]
{
"scripts":{
"start":"tsc&&concurrently'npmruntsc:w''npmrunlite'",
"lite":"lite-server",
"postinstall":"npminstall-s@types/node@types/core-js",
"tsc":"tsc",
"tsc:w":"tsc-w"
},
"dependencies":{
"@angular/common":"2.1.0",
"@angular/compiler":"2.1.0",
"@angular/core":"2.1.0",
"@angular/platform-browser":"2.1.0",
"@angular/platform-browser-dynamic":"2.1.0",
"core-js":"^2.4.1",
"reflect-metadata":"^0.1.3",
"rxjs":"5.0.0-beta.12",
"systemjs":"0.19.27",
"zone.js":"^0.6.23"
},
"devDependencies":{
"concurrently":"^2.2.0",
"lite-server":"^2.2.2",
"typescript":"^2.0.2"
}
}
[app/app.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'app-root',
template:'<h1>AppComponenttemplate!</h1>'
})
exportclassAppComponent{}
![Page 569: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/569.jpg)
Howtodoit...TheAngularteammaintainstheProtractorproject,whichbymanyaccountsisthebestwaytogoaboutperformingend-to-endtestsonyourapplications,atleastinitially.Itcomeswithalargenumberofutilitiesoutoftheboxtomanipulatethebrowserwhenwritingyourtests,andexplicitintegrationswithAngular2,soit'saterrificplacetostart.
GettingProtractorupandrunning
ProtractorreliesonSeleniumtoautomatethebrowser.ThespecificsofSeleniumaren'tespeciallyimportantforthepurposeofcreatingaminimumviablee2etestsuite,butyouwillneedtoinstallaJavaruntime:
sudoapt-getinstallopenjdk-8-jre
Tip
IrunUbuntu,sotheOpenJDKJavaRuntimeEnvironmentV8issuitableformypurposes.Yourdevelopmentsetupmaydiffer.RuntimesfordifferentoperatingsystemscanbefoundonOracle'swebsite.
Protractoritselfcanbeinstalledfromnpm,butitshouldbeglobal.You'llbeusingitwithJasmine,soinstallitanditsTypeScripttypingsaswell:
npminstalljasmine-core@types/jasmine--save-dev
npminstallprotractor-g
Tip
Youmayneedtofiddlewiththisconfiguration.Sometimes,itmayworkifyouinstallprotractorlocallyratherthanglobally.Errorsinvolvingwebdriver-managerarepartoftheprotractorpackage,sotheywillmostlikelybeinvolvedwhereyourprotractorpackageinstallationisaswell.
Itshouldcomeasnosurprisethatprotractorisconfiguredwithafile,socreateitnow:
[protractor.conf.js]
exports.config={
specs:[
'./e2e/**/*.e2e-spec.ts'
],
capabilities:{
'browserName':'chrome'
},
baseUrl:'http://localhost:3000/',
framework:'jasmine',
}
![Page 570: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/570.jpg)
Noneofthesesettingsshouldsurpriseyou:
Thee2etestfilesaregoingtoliveinane2e/directoryandwillbesuffixedwith.e2e-spec.ts
ProtractorisgoingtospinupaChromeinstancethatitwillpuppeteerwithSeleniumTheserveryou'regoingtospinupwillexistatlocalhost:3000,andalltheURLsinsidetheProtractortestswillberelativetothisTheProtractortestswillbewrittenwiththeJasminesyntax
Forsimplicity,theserveryouarestartingupfortheend-to-endtestswillbethesamelite-serveryou'vebeenusingallalong.Whenitstartsup,lite-serverwillopenupabrowserwindowofitsown,whichwillprovetobeabitannoyinghere.SinceitisathinwrapperforBrowserSync,youcanconfigureittonotdothisbysimplydirectingitnottodosoinaconfigfilethatisonlyusedwhenrunninge2etests.
Createthisfilenowinsidethetestdirectory:
[e2e/bs-config.json]
{
"open":false
}
Note
Thelite-serverwrapperwon'tfindthisautomatically,butyou'lldirectittothefileinamoment.
MakingProtractorcompatiblewithJasmineandTypeScript
First,createatsconfig.jsonfileinsidethetestdirectory:
[e2e/tsconfig.json]
{
"compilerOptions":{
"target":"es5",
"module":"commonjs",
"moduleResolution":"node"
}
}
Next,createtheactuale2etestfileskeleton:
[e2e/app.e2e-spec.ts]
describe('AppE2ETestSuite',()=>{
it('shouldhavethecorrecth1text',()=>{
});
});
![Page 571: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/571.jpg)
ThisusesthestandardJasminesyntaxtodeclareaspecsuiteandanemptytestwithinit.
Beforefleshingoutthetest,youneedtoensurethatProtractorcanactuallyusethisfile.Installthets-nodepluginsothatProtractorcanperformthecompilationandusethesefilesine2etests:
npminstallts-node--save-dev
Next,instructProtractortousethistocompilethetestsourcefilesintoausableformat.Thiscanbedoneinitsconfigfile:
[protractor.conf.js]
exports.config={
specs:[
'./e2e/**/*.e2e-spec.ts'
],
capabilities:{
'browserName':'chrome'
},
baseUrl:'http://localhost:3000/',
framework:'jasmine',
beforeLaunch:function(){
require('ts-node').register({
project:'e2e'
});
}
}
Withallthis,you'releftwithaworkingbutemptyend-to-endtest.
Buildingapageobject
AnexcellentconventionthatIhighlyrecommendusingisthepageobject.
Note
Theideabehindthisisthatallofthelogicsurroundingtheinteractionwiththepagecanbeextractedintoitsownpageobjectclass,andtheactualtestbehaviorcanusethisabstractedpageobjectinsidetheclass.ThisallowstheteststobewrittenindependentlyoftheDOMstructureorroutingdefinitions,whichmakesforsuperiortestmaintenance.What'smore,itmakesyourteststotallyindependentofProtractor,whichmakesiteasiershouldyouwanttochangeyourend-to-endtestrunner.
Forthissimpleend-to-endtest,you'llwanttospecifyhowtoarriveatthispageandhowtoinspectittogetwhatyouwant.Definethepageobjectasfollowswithtwomembermethods:
[e2e/app.po.ts]
import{browser,element,by}from'protractor';
![Page 572: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/572.jpg)
exportclassAppPage{
navigate(){
browser.get('/');
}
getHeaderText(){
returnelement(by.css('app-rooth1')).getText();
}
}
navigate()instructsSeleniumtotherootpath(which,asyoumayrecall,isbasedonlocalhost:3000),andgetHeaderText()inspectsaDOMelementforitstextcontents.
Note
Notethatbrowser,element,andbyareallutilitiesimportedfromtheprotractormodule.Moreonthislaterintherecipe.
Writingthee2etest
Withalloftheinfrastructureinplace,youcannoweasilywriteyourend-to-endtest.You'llwanttoinstantiateanewpageobjectforeachtest:
[e2e/app.e2e-spec.ts]
import{AppPage}from'./app.po';
describe('AppE2ETestSuite',()=>{
letpage:AppPage;
beforeEach(()=>{
page=newAppPage();
});
it('shouldhavethecorrecth1text',()=>{
page.navigate();
expect(page.getHeaderText())
.toEqual('AppComponenttemplate!');
});
});
Scriptingthee2etests
Finally,you'llwanttogiveyourselftheabilitytoeasilyruntheend-to-endtestsuite.Seleniumisoftenbeingupdated,soitbehovesyoutoexplicitlyupdateitbeforeyourunthetests:
[package.json]
{
"scripts":{
![Page 573: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/573.jpg)
"pree2e":"webdriver-managerupdate&&tsc",
"e2e":"concurrently'npmrunlite---c=e2e/bs-config.json'
'protractorprotractor.conf.js'",
"start":"tsc&&concurrently'npmruntsc:w''npmrunlite'",
"lite":"lite-server",
"postinstall":"npminstall-s@types/node@types/core-js",
"tsc":"tsc",
"tsc:w":"tsc-w"
},
"dependencies":{
...
},
"devDependencies":{
...
}
}
Finally,Angular2needstointegratewithProtractorandbeabletotellitwhenthepageisreadytobeinteractedwith.ThisrequiresonemoreadditiontotheProtractorconfiguration:
[protractor.conf.js]
exports.config={
specs:[
'./e2e/**/*.e2e-spec.ts'
],
capabilities:{
'browserName':'chrome'
},
baseUrl:'http://localhost:3000/',
framework:'jasmine',
useAllAngular2AppRoots:true,
beforeLaunch:function(){
require('ts-node').register({
project:'e2e'
});
}
}
That'sall!Youshouldnowbeabletoruntheend-to-endtestsuitebyinvokingitwiththecorrespondingnpmscript:
npmrune2e
Thiswillstartupalite-serverinstance(withoutstartingupitsdefaultbrowser),andprotractorwillrunthetestsandexit.
![Page 574: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/574.jpg)
Howitworks...Atthetopoftheapp.po.tspageobjectfile,youimportedthreetargetsfromProtractor:browser,element,andby.Here'sabitaboutthesetargets:
browserisaprotractorglobalobjectthatallowsyoutoperformbrowser-levelactions,suchasvisitingURLs,waitingforeventstooccur,andtakingscreenshots.elementisaglobalfunctionthattakesaLocatorandreturnsanElementFinder.ElementFinderisthepointofcontacttointeractwiththematchingDOMelement,ifitexists.byisaglobalobjectthatexposesseveralLocatorfactories.Here,theby.css()locatorfactoryperformsananalogueofdocument.querySelector().
Tip
TheentireProtractorAPIcanbefoundathttp://www.protractortest.org/#/api.
Thereasonforwritingtheteststhiswaymayfeelstrangetoyou.Afterall,it'sarealbrowserrunningarealapplication,soitmightmakesensetoreachforDOMmethodsandthelike.
ThereasonforusingtheProtractorAPIinsteadissimple:thetestcodeyouarewritingisnotbeingexecutedinsidethebrowserruntime.Instead,ProtractorishandingofftheseinstructionstoSelenium,whichinturnwillexecutetheminsidethebrowserandreturntheresults.Thus,thetestcodeyouwritecanonlyindirectlyinterfacewiththebrowserandtheDOM.
![Page 575: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/575.jpg)
There'smore...Thepurposeofthisrecipewastoassembleaverysimpleend-to-endtestsuitesothatyoucangetafeelofwhatgoesonbehindthescenesinsomeform.Whiletheteststhemselveswillappearmoreorlessastheydohere,regardlessofthetestinfrastructuretheyarerunningon,theinfrastructureitselfisfarfrombeingoptimal;anumberofchangesandadditionscouldbemadetomakeitmorerobust.
Whenrunningunittests,itisoftenusefulfortheunitteststodetectthechangesinfilesandrunthemagainimmediately.Alargepartofthisisbecauseunittestsshouldbeverylightweight.Anydependenciesontherestoftheapplicationaremockedorabstractedawaysothataminimalamountofcodecanberuntoprepareyourunittestenvironment.Thus,thereislittlecosttorunningasuiteofunittestsinasequence.
End-to-endtests,ontheotherhand,behaveintheoppositeway.Theydoindeedrequiretheentireapplicationtobeconstructedandrun,whichcanbecomputationallyexpensive.Pagenavigations,resettingtheentireapplication,initializingandclearingauthentication,andotheroperationsthatmightcommonlybeperformedinanend-to-endtestcantakealongtime.Therefore,itdoesn'tmakeasmuchsenseheretoruntheend-to-endtestswithafilewatcherobservingforchangesmadetothetests.
![Page 576: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/576.jpg)
SeealsoCreatingaminimumviableunittestsuitewithKarma,Jasmine,andTypeScriptgivesyouagentleintroductiontounittestswithTypeScript
![Page 577: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/577.jpg)
UnittestingasynchronousserviceAngular2servicetypesareessentiallyclassesdesignatedforinjectability.Theyareeasytotestsinceyouhaveagreatdealofcontroloverhowandwheretheyareprovided,andconsequently,howmanyinstancesyou'llbeabletocreate.Therefore,testsforserviceswillexistlargelyastheywouldforanynormalTypeScriptclass.
It'llbebetterifyouarefamiliarwiththecontentofthefirstfewrecipesofthischapterbeforeyouproceedfurther.
Note
Thecode,links,andaliveexamplerelatedtothisrecipeareavailableathttp://ngcookbook.herokuapp.com/3107.
![Page 578: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/578.jpg)
GettingreadySupposeyouwanttobuilda"magiceightball"service.Beginwiththefollowingcode,withaddedcommentsforclarity:
[src/app/magic-eight-ball.service.ts]
import{Injectable}from'@angular/core';
@Injectable()
exportclassMagicEightBallService{
privatevalues:Array<string>;
privatelastIndex:number;
constructor(){
//Initializethevaluesarray
//Musthaveatleasttwoentries
this.values=[
'Askagainlater',
'Outlookgood',
'Mostlikely',
'Don'tcountonit'
];
//Initializewithanyvalidindex
this.lastIndex=this.getIndex();
}
privategetIndex():number{
//Returnarandomindexforthis.values
returnMath.floor(Math.random()*this.values.length);
}
reveal():string{
//Generateanewindex
letnewIdx=this.getIndex();
.
//Checkiftheindexwasthesameoneusedlasttime
if(newIdx===this.lastIndex){
//Ifso,shiftupone(wrappingaround)inthearray
//Thisisstillrandombehavior
newIdx=(++newIdx)%this.values.length;
}
//Savetheindexthatyouarenowusing
this.lastIndex=newIdx;
//Accessthestringandreturnit
returnthis.values[newIdx];
}
}
![Page 579: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/579.jpg)
Thereareseveralthingstonoteabouthowthisservicebehaves:
ThisservicehasseveralprivatemembersbutonlyonepublicmembermethodTheserviceisrandomlyselectedfromanarrayTheserviceshouldn'treturnthesamevaluetwiceinarow
Thewayyourunittestsarewrittenshouldaccountfortheseaswellascompletelytestthebehaviorofthisservice.
![Page 580: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/580.jpg)
Howtodoit...Beginbycreatingtheframeworkofyourtestfile:
[src/app/magic-eight-ball.service.spec.ts]
import{TestBed}from'@angular/core/testing';
import{MagicEightBallService}from
'./magic-eight-ball.service';
describe('Service:MagicEightBall',()=>{
beforeEach(()=>{
TestBed.configureTestingModule({
providers:[
MagicEightBallService
]
});
});
});
Sofar,noneofthisshouldsurpriseyou.MagicEightBallServiceisaninjectable;itneedstobeprovidedinsideamoduledeclaration,whichisdonehere.However,toactuallyuseitinsideaunittest,youneedtoperformaformalinjectionsincethisiswhatwouldberequiredtoaccessitfrominsideacomponent.Thiscanbeaccomplishedwithinject:
[src/app/magic-eight-ball.service.spec.ts]
import{TestBed,inject}from'@angular/core/testing';
import{MagicEightBallService}from
'./magic-eight-ball.service';
describe('Service:MagicEightBall',()=>{
beforeEach(()=>{
TestBed.configureTestingModule({
providers:[
MagicEightBallService
]
});
});
it('shouldbeabletobeinjected',inject([MagicEightBallService],
(magicEightBallService:MagicEightBallService)=>{
expect(magicEightBallService).toBeTruthy();
})
);
});
Offtoagoodstart,butthisdoesn'tactuallytestanythingaboutwhattheserviceisdoing.Next,writeatestthatensuresthatastringofnon-zerolengthisbeingreturned:
[src/app/magic-eight-ball.service.spec.ts]
![Page 581: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/581.jpg)
import{TestBed,inject}from'@angular/core/testing';
import{MagicEightBallService}from
'./magic-eight-ball.service';
describe('Service:MagicEightBall',()=>{
beforeEach(()=>{
TestBed.configureTestingModule({
providers:[
MagicEightBallService
]
});
});
it('shouldbeabletobeinjected',inject([MagicEightBallService],
(magicEightBallService:MagicEightBallService)=>{
expect(magicEightBallService).toBeTruthy();
})
);
it('shouldreturnastringwithnonzerolength',
inject([MagicEightBallService],
(magicEightBallService:MagicEightBallService)=>{
letresult=magicEightBallService.reveal();
expect(result).toEqual(jasmine.any(String));
expect(result.length).toBeGreaterThan(0);
})
);
});
Finally,youshouldwriteatesttoensurethatthetwovaluesreturnedarenotthesame.Sincethismethodisrandom,youcanrunituntilyouareblueinthefaceandstillnotbetotallysure.However,checkingthis50timesinarowisafinewaytobefairlycertain:
[src/app/magic-eight-ball.service.spec.ts]
import{TestBed,inject}from'@angular/core/testing';
import{MagicEightBallService}from
'./magic-eight-ball.service';
describe('Service:MagicEightBall',()=>{
beforeEach(()=>{
TestBed.configureTestingModule({
providers:[
MagicEightBallService
]
});
});
it('shouldbeabletobeinjected',inject([MagicEightBallService],
(magicEightBallService:MagicEightBallService)=>{
expect(magicEightBallService).toBeTruthy();
![Page 582: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/582.jpg)
})
);
it('shouldreturnastringwithnonzerolength',
inject([MagicEightBallService],
(magicEightBallService:MagicEightBallService)=>{
letresult=magicEightBallService.reveal();
expect(result).toEqual(jasmine.any(String));
expect(result.length).toBeGreaterThan(0);
})
);
it('shouldnotreturnthesamevaluetwiceinarow',
inject([MagicEightBallService],
(magicEightBallService:MagicEightBallService)=>{
letlast;
for(leti=0;i<50;++i){
letnext=magicEightBallService.reveal();
expect(next).not.toEqual(last);
last=next;
}
})
);
});
Terrific!Allthesetestshavepassed;you'vedoneagoodjobbuildingsomeincrementalanddescriptivecodecoverageforyourservice.
![Page 583: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/583.jpg)
Howitworks...Theinjecttestfunctionperformsdependencyinjectionforyoueachtimeitisinvoked,usingthearrayofinjectableclassespassedasthefirstargument.Thearrowfunctionthatispassedasitssecondargumentwillbehaveinessentiallythesamewayasacomponentconstructor,whereyouareabletousethemagicEightBallServiceparameterasaninstanceoftheservice.
Tip
Oneimportantdifferencefromhowitisinjectedcomparedtoacomponentconstructoristhatinsideacomponentconstructor,youwouldbeabletousethis.magicEightBallServicerightaway.Withrespecttoinjectionintounittests,itdoesnotautomaticallyattachtothis.
![Page 584: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/584.jpg)
There'smore...Importantconsiderationsforunittestingarewhattestsshouldbewrittenandhowtheyshouldproceed.Respectingtheboundariesofpublicandprivatemembersisessential.Sincethesetestsarewritteninawaythatonlyutilizesthepublicmembersoftheservice,theauthorisfreetogoaboutchanging,extending,orrefactoringtheinternalsoftheservicewithoutworryingaboutbreakingorneedingtoupdatethetests.Awell-designedclasswillbefullytestablefromitspublicinterface.
Tip
Thisnotionbringsupaninterestingphilosophicalpointregardingunittesting.Youshouldbeabletodescribethebehaviorofawell-formedserviceasafunctionofitspublicmembers.Similarly,awell-formedserviceshouldthenberelativelyeasytowriteunittests,giventhattheformerstatementistrue.
Ifitisthenthecasethatyoufindyourunittestsaredifficulttowrite—forexample,youareneedingtoreachintoaprivatememberoftheservicetotestitproperly—thenconsiderthenotionthatyourservicemightnotbeaswelldesignedasitcouldbe.
Inshort,ifit'shardtotest,thenyoumighthavewrittenaclassinaweirdway.
Testingwithoutinjection
Anobservantdeveloperwillnoteherethattheserviceyouaretestingdoesn'thaveanymeaningfuldependenceoninjection.Injectingitintovariousplacesintheapplicationsurelyprovidesitwithaconsistentway,buttheservicedefinitioniswhollyunawareofthisfact.Afterall,instantiationisinstantiation,andthisservicedoesn'tappeartobemorethananinjectableclass.Therefore,itiscertainlypossibletonotbotherinjectingtheserviceatallandmerelyinstantiatingitusingthenewkeyword:
[src/app/magic-eight-ball.service.spec.ts]
import{MagicEightBallService}from
'./magic-eight-ball.service';
describe('Service:MagicEightBall',()=>{
letmagicEightBallService;
beforeEach(()=>{
magicEightBallService=newMagicEightBallService();
});
it('shouldbeabletobeinjected',()=>{
expect(magicEightBallService).toBeTruthy();
});
![Page 585: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/585.jpg)
it('shouldreturnastringwithnonzerolength',()=>{
letresult=magicEightBallService.reveal();
expect(result).toEqual(jasmine.any(String));
expect(result.length).toBeGreaterThan(0);
});
it('shouldnotreturnthesamevaluetwiceinarow',()=>{
letlast;
for(leti=0;i<50;++i){
letnext=magicEightBallService.reveal();
expect(next).not.toEqual(last);
last=next;
}
});
});
Ofcourse,thisrequiresthatyoukeeptrackofwhethertheservicecaresaboutwhetherornotithasbeeninjectedanywhere.
![Page 586: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/586.jpg)
SeealsoUnittestingacomponentwithaservicedependencyusingstubsshowshowyoucancreateaservicemocktowriteunittestsandavoiddirectdependenciesUnittestingacomponentwithaservicedependencyusingspiesshowshowyoucankeeptrackofservicemethodinvocationsinsideaunittest
![Page 587: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/587.jpg)
UnittestingacomponentwithaservicedependencyusingstubsStandalonecomponenttestingiseasy,butyouwillrarelyneedtowritemeaningfultestsforacomponentthatexistsinisolation.Moreoftenthannot,thecomponentwillhaveoneormanydependencies,andwritinggoodunittestsisthedifferencebetweendelightanddespair.
Note
Thecode,links,andaliveexamplerelatedtothisrecipeareavailableathttp://ngcookbook.herokuapp.com/6651/.
![Page 588: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/588.jpg)
GettingreadySupposeyoualreadyhavetheservicefromtheUnittestingasynchronousservicerecipe.Inaddition,youhaveacomponent,whichmakesuseofthisservice:
[src/app/magic-eight-ball/magic-eight-ball.component.ts]
import{Component}from'@angular/core';
import{MagicEightBallService}from
'../magic-eight-ball.service';
@Component({
selector:'app-magic-eight-ball',
template:`
<button(click)="update()">Clickme!</button>
<h1>{{result}}</h1>
`
})
exportclassMagicEightBallComponent{
result:string='';
constructor(privatemagicEightBallService_:MagicEightBallService){}
update(){
this.result=this.magicEightBallService_.reveal();
}
}
Yourobjectiveistowriteasuiteofunittestsforthiscomponentwithoutsettinganexplicitdependencyontheservice.
![Page 589: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/589.jpg)
Howtodoit...Beginwithaskeletonofyourtestfile:
[src/app/magic-eight-ball/magic-eight-ball.component.spec.ts]
import{TestBed,async}from'@angular/core/testing';
import{MagicEightBallComponent}from
'./magic-eight-ball.component';
import{MagicEightBallService}from
'../magic-eight-ball.service';
describe('Component:MagicEightBall',()=>{
beforeEach(async(()=>{
}));
afterEach(()=>{
});
it('shouldbeginwithnotext',async(()=>{
}));
it('shouldshowtextafterclick',async(()=>{
}));
});
You'llfirstwanttoconfigurethetestmodulesothatitproperlyprovidestheseimportedtargetsinthetest:
[src/app/magic-eight-ball/magic-eight-ball.component.spec.ts]
import{TestBed,async}from'@angular/core/testing';
import{MagicEightBallComponent}from
'./magic-eight-ball.component';
import{MagicEightBallService}from
'../magic-eight-ball.service';
describe('Component:MagicEightBall',()=>{
letfixture;
beforeEach(async(()=>{
TestBed.configureTestingModule({
declarations:[
MagicEightBallComponent
],
providers:[
MagicEightBallService
]
});
fixture=TestBed.createComponent(MagicEightBallComponent);
}));
![Page 590: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/590.jpg)
afterEach(()=>{
fixture=undefined;
});
it('shouldbeginwithnotext',async(()=>{
}));
it('shouldshowtextafterclick',async(()=>{
}));
});
Stubbingaservicedependency
Injectingtheactualserviceworksjustfine,butthisisn'twhatyouwanttodo.Youdon'twanttoactuallyinjectaninstanceofMagicEightBallServiceintothecomponent,asthatwouldsetadependencyontheserviceandmaketheunittestmorecomplicatedthanitneedstobe.However,MagicEightBallComponentneedstoimportsomethingthatresemblesaMagicEightBallService.Anexcellentsolutionhereistocreateaservicestubandinjectitinitsplace:
[src/app/magic-eight-ball/magic-eight-ball.component.spec.ts]
import{TestBed,async}from'@angular/core/testing';
import{MagicEightBallComponent}from
'./magic-eight-ball.component';
import{MagicEightBallService}from
'../magic-eight-ball.service';
describe('Component:MagicEightBall',()=>{
letfixture;
letmagicEightBallResponse='Answerunclear';
letmagicEightBallServiceStub={
reveal:()=>magicEightBallResponse
};
beforeEach(async(()=>{
TestBed.configureTestingModule({
declarations:[
MagicEightBallComponent
],
providers:[
{
provide:MagicEightBallService,
useValue:magicEightBallServiceStub
}
]
});
fixture=TestBed.createComponent(MagicEightBallComponent);
}));
afterEach(()=>{
fixture=undefined;
![Page 591: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/591.jpg)
});
it('shouldbeginwithnotext',async(()=>{
}));
it('shouldshowtextafterclick',async(()=>{
}));
});
Acomponentcan'ttellthedifferencebetweentheactualserviceanditsmock,soitwillbehavenormallyinthetestconditionsyou'vesetup.
Next,youshouldwritethepreclicktestbycheckingthatthefixture'snativeElementcontainsnotext:
[src/app/magic-eight-ball/magic-eight-ball.component.spec.ts]
import{TestBed,async}from'@angular/core/testing';
import{MagicEightBallComponent}from
'./magic-eight-ball.component';
import{MagicEightBallService}from
'../magic-eight-ball.service';
describe('Component:MagicEightBall',()=>{
letfixture;
letgetHeaderEl=()=>
fixture.nativeElement.querySelector('h1');
letmagicEightBallResponse='Answerunclear';
letmagicEightBallServiceStub={
reveal:()=>magicEightBallResponse
};
beforeEach(async(()=>{
TestBed.configureTestingModule({
declarations:[
MagicEightBallComponent
],
providers:[
{
provide:MagicEightBallService,
useValue:magicEightBallServiceStub
}
]
});
fixture=TestBed.createComponent(MagicEightBallComponent);
}));
afterEach(()=>{
fixture=undefined;
});
it('shouldbeginwithnotext',async(()=>{
![Page 592: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/592.jpg)
fixture.detectChanges();
expect(getHeaderEl().textContent).toEqual('');
}));
it('shouldshowtextafterclick',async(()=>{
}));
});
Triggeringeventsinsidethecomponentfixture
Forthesecondtest,youshouldtriggeraclickonthebutton,instructthefixturetoperformchangedetection,andtheninspecttheDOMtoseethatthetextwasproperlyinserted.Sinceyouhavedefinedthetextthatthestubwillreturn,youcanjustcompareitdirectlywiththat:
[src/app/magic-eight-ball/magic-eight-ball.component.spec.ts]
import{TestBed,async}from'@angular/core/testing';
import{MagicEightBallComponent}from
'./magic-eight-ball.component';
import{MagicEightBallService}from
'../magic-eight-ball.service';
import{By}from'@angular/platform-browser';
describe('Component:MagicEightBall',()=>{
letfixture;
letgetHeaderEl=()=>
fixture.nativeElement.querySelector('h1');
letmagicEightBallResponse='Answerunclear';
letmagicEightBallServiceStub={
reveal:()=>magicEightBallResponse
};
...
it('shouldbeginwithnotext',async(()=>{
expect(getHeaderEl().textContent).toEqual('');
}));
it('shouldshowtextafterclick',async(()=>{
fixture.debugElement.query(By.css('button'))
.triggerEventHandler('click',null);
fixture.detectChanges();
expect(getHeaderEl().textContent)
.toEqual(magicEightBallResponse);
}));
});
You'llnotethatthisneedstoimportandusetheBy.csspredicate,whichisrequiredtoperformDebugElementinspections.
![Page 593: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/593.jpg)
Howitworks...Asdemonstratedinthedependencyinjectionchapter,providingastubtothecomponentisnodifferentthanprovidingaregularvaluetothecoreapplication.
Thestubhereisasinglefunctionthatreturnsastaticvalue.Thereisnoconceptofrandomlyselectingfromtheservice'sarrayofstrings,andtheredoesn'tneedtobe.Theunittestsfortheserviceitselfensurethatitisbehavingproperly.Instead,theonlyvalueprovidedbytheservicehereistheinformationitpassesbacktothecomponentforinterpolationbackintothetemplate.
![Page 594: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/594.jpg)
SeealsoWritingaminimumviableunittestsuiteforasimplecomponentshowsyouabasicexampleofunittestingAngular2componentsUnittestingasynchronousservicedemonstrateshowinjectionismockedinunittestsUnittestingacomponentwithaservicedependencyusingspiesshowshowyoucankeeptrackofservicemethodinvocationsinsideaunittest
![Page 595: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/595.jpg)
UnittestingacomponentwithaservicedependencyusingspiesTheabilitytostuboutservicesisuseful,butitcanbelimitinginanumberofways.Itcanalsobetedious,asthestubsyoucreatemustremainuptodatewiththepublicinterfaceoftheservice.Anotherexcellenttoolatyourdisposalwhenwritingunittestsisthespy.
Aspyallowsyoutoselectafunctionormethod.Italsohelpsyoucollectinformationaboutifandhowitwasinvokedaswellashowitwillbehaveonceitisinvoked.Itissimilarinconcepttoastubbutallowsyoutohaveamuchmorerobustunittest.
Note
Thecode,links,andaliveexamplerelatedtothisrecipeareavailableathttp://ngcookbook.herokuapp.com/3444/.
![Page 596: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/596.jpg)
GettingreadyBeginwiththecomponenttestsyouwroteinthelastrecipe:
[src/app/magic-eight-ball/magic-eight-ball.component.spec.ts]
import{TestBed,async}from'@angular/core/testing';
import{MagicEightBallComponent}from
'./magic-eight-ball.component';
import{MagicEightBallService}from
'../magic-eight-ball.service';
import{By}from'@angular/platform-browser';
describe('Component:MagicEightBall',()=>{
letfixture;
letgetHeaderEl=()=>fixture.nativeElement.querySelector('h1');
letmagicEightBallResponse='Answerunclear';
letmagicEightBallServiceStub={
reveal:()=>magicEightBallResponse
};
beforeEach(async(()=>{
TestBed.configureTestingModule({
declarations:[
MagicEightBallComponent
],
providers:[
{
provide:MagicEightBallService,
useValue:magicEightBallServiceStub
}
]
});
fixture=TestBed.createComponent(MagicEightBallComponent);
}));
afterEach(()=>{
fixture=undefined;
});
it('shouldbeginwithnotext',async(()=>{
fixture.detectChanges();
expect(getHeaderEl().textContent).toEqual('');
}));
it('shouldshowtextafterclick',async(()=>{
fixture.debugElement.query(By.css('button'))
.triggerEventHandler('click',null);
fixture.detectChanges();
expect(getHeaderEl().textContent)
.toEqual(magicEightBallResponse);
}));
![Page 597: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/597.jpg)
});
![Page 598: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/598.jpg)
Howtodoit...Insteadofusingastub,configurethetestmoduletoprovidetheactualservice:
[src/app/magic-eight-ball/magic-eight-ball.component.spec.ts]
import{TestBed,async}from'@angular/core/testing';
import{MagicEightBallComponent}from
'./magic-eight-ball.component';
import{MagicEightBallService}from
'../magic-eight-ball.service';
import{By}from'@angular/platform-browser';
describe('Component:MagicEightBall',()=>{
letfixture;
letgetHeaderEl=()=>fixture.nativeElement.querySelector('h1');
letmagicEightBallResponse='Answerunclear';
beforeEach(async(()=>{
TestBed.configureTestingModule({
declarations:[
MagicEightBallComponent
],
providers:[
MagicEightBallService
]
});
fixture=TestBed.createComponent(MagicEightBallComponent);
}));
afterEach(()=>{
fixture=undefined;
});
it('shouldbeginwithnotext',async(()=>{
fixture.detectChanges();
expect(getHeaderEl().textContent).toEqual('');
}));
it('shouldshowtextafterclick',async(()=>{
fixture.debugElement.query(By.css('button'))
.triggerEventHandler('click',null);
fixture.detectChanges();
expect(getHeaderEl().textContent)
.toEqual(magicEightBallResponse);
}));
});
Settingaspyontheinjectedservice
Yourgoalistouseamethodspytointerceptcallstoreveal()ontheservice.Theproblemhere,however,isthattheserviceisbeinginjectedintothecomponent;therefore,youdon'thavea
![Page 599: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/599.jpg)
directabilitytogetareferencetotheserviceinstanceandsetaspyonit.Fortunately,thecomponentfixtureprovidesthisforyou:
[src/app/magic-eight-ball/magic-eight-ball.component.spec.ts]
import{TestBed,async}from'@angular/core/testing';
import{MagicEightBallComponent}from
'./magic-eight-ball.component';
import{MagicEightBallService}from
'../magic-eight-ball.service';
import{By}from'@angular/platform-browser';
describe('Component:MagicEightBall',()=>{
letfixture;
letgetHeaderEl=()=>fixture.nativeElement.querySelector('h1');
letmagicEightBallResponse='Answerunclear';
letmagicEightBallService;
beforeEach(async(()=>{
TestBed.configureTestingModule({
declarations:[
MagicEightBallComponent
],
providers:[
MagicEightBallService
]
});
fixture=TestBed.createComponent(MagicEightBallComponent);
magicEightBallService=fixture.debugElement.injector
.get(MagicEightBallService);
}));
afterEach(()=>{
fixture=undefined;
magicEightBallService=undefined;
});
...
});
Next,setaspyontheserviceinstanceusingspyOn().Configurethespytointerceptthemethodcallandreturnthestaticvalueinstead:
[src/app/magic-eight-ball/magic-eight-ball.component.spec.ts]
import{TestBed,async}from'@angular/core/testing';
import{MagicEightBallComponent}from
'./magic-eight-ball.component';
import{MagicEightBallService}from
'../magic-eight-ball.service';
import{By}from'@angular/platform-browser';
describe('Component:MagicEightBall',()=>{
![Page 600: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/600.jpg)
letfixture;
letgetHeaderEl=()=>fixture.nativeElement.querySelector('h1');
letmagicEightBallResponse='Answerunclear';
letmagicEightBallService;
letrevealSpy;
beforeEach(async(()=>{
TestBed.configureTestingModule({
declarations:[
MagicEightBallComponent
],
providers:[
MagicEightBallService
]
});
fixture=TestBed.createComponent(MagicEightBallComponent);
magicEightBallService=fixture.debugElement.injector
.get(MagicEightBallService);
revealSpy=spyOn(magicEightBallService,'reveal')
.and.returnValue(magicEightBallResponse);
}));
afterEach(()=>{
fixture=undefined;
magicEightBallService=undefined;
revealSpy=undefined;
});
...
});
Withthisspy,youarenowcapableofseeinghowtherestoftheapplicationinteractswiththiscapturedmethod.Addanewtest,andcheckthatthemethodiscalledonceandreturnsthepropervalueafteraclick(thisalsopullstheclickingactionintoitsowntesthelper):
[src/app/magic-eight-ball/magic-eight-ball.component.spec.ts]
import{TestBed,async}from'@angular/core/testing';
import{MagicEightBallComponent}from
'./magic-eight-ball.component';
import{MagicEightBallService}from
'../magic-eight-ball.service';
import{By}from'@angular/platform-browser';
describe('Component:MagicEightBall',()=>{
letfixture;
letgetHeaderEl=()=>fixture.nativeElement.querySelector('h1');
letmagicEightBallResponse='Answerunclear';
letmagicEightBallService;
letrevealSpy;
letclickButton=()=>{
fixture.debugElement.query(By.css('button'))
.triggerEventHandler('click',null);
![Page 601: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/601.jpg)
};
beforeEach(async(()=>{
TestBed.configureTestingModule({
declarations:[
MagicEightBallComponent
],
providers:[
MagicEightBallService
]
});
fixture=TestBed.createComponent(MagicEightBallComponent);
magicEightBallService=fixture.debugElement.injector
.get(MagicEightBallService);
revealSpy=spyOn(magicEightBallService,'reveal')
.and.returnValue(magicEightBallResponse);
}));
afterEach(()=>{
fixture=undefined;
magicEightBallService=undefined;
revealSpy=undefined;
});
it('shouldbeginwithnotext',async(()=>{
fixture.detectChanges();
expect(getHeaderEl().textContent).toEqual('');
}));
it('shouldcallrevealafteraclick',async(()=>{
clickButton();
expect(revealSpy.calls.count()).toBe(1);
expect(revealSpy.calls.mostRecent().returnValue)
.toBe(magicEightBallResponse);
}));
it('shouldshowtextafterclick',async(()=>{
clickButton();
fixture.detectChanges();
expect(getHeaderEl().textContent)
.toEqual(magicEightBallResponse);
}));
});
Note
NotethatdetectChanges()isonlyrequiredtoresolvethedatabinding,nottoexecuteeventhandlers.
![Page 602: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/602.jpg)
Howitworks...Jasminespiesactasmethodinterceptorsandarecapableofinspectingeverythingaboutthegivenmethodinvocation.Itcantrackifandwhenamethodwascalled,whatargumentsitwascalledwith,howmanytimesitwascalled,howitshouldbehave,andsoon.Thisisextremelyusefulwhentryingtoremovedependenciesfromcomponentunittests,asyoucanmockoutthepublicinterfaceoftheserviceusingspies.
![Page 603: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/603.jpg)
There'smore...Spiesarenotbeholdentoreplacethemethodoutright.Here,itisusefultobeabletopreventtheexecutionfromreachingtheinternalsoftheservice,butitisnotdifficulttoimaginecaseswhereyouwouldonlywanttopassivelyobservetheinvocationofacertainmethodandallowtheexecutiontocontinuenormally.
Forsuchapurpose,insteadofusing.and.returnValue(),Jasmineallowsyoutouse.and.callThrough(),whichwillallowtheexecutiontoproceeduninterrupted.
![Page 604: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/604.jpg)
SeealsoWritingaminimumviableunittestsuiteforasimplecomponentshowsyouabasicexampleofunittestingAngular2componentsUnittestingasynchronousservicedemonstrateshowinjectionismockedinunittestsUnittestingacomponentwithaservicedependencyusingstubsshowshowyoucancreateaservicemocktowriteunittestsandavoiddirectdependencies
![Page 605: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/605.jpg)
Chapter10.PerformanceandAdvancedConceptsThischapterwillcoverthefollowingrecipes:
UnderstandingandproperlyutilizingenableProdModewithpureandimpurepipesWorkingwithzonesoutsideAngularListeningforNgZoneeventsExecutionoutsidetheAngularzoneConfiguringcomponentstouseexplicitchangedetectionwithOnPushConfiguringViewEncapsulationformaximumefficiencyConfiguringtheAngular2RendererServicetousewebworkersConfiguringapplicationstouseahead-of-timecompilationConfiguringanapplicationtouselazyloading
![Page 606: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/606.jpg)
IntroductionAngular2wasatotalrebuildforanumberofreasons,butoneofthebiggestoneswascertainlyefficiency.Theframeworkthatemergedfromtheashesisasleekandelegantone,butnotwithoutitscomplexities.
Thischapterservestoexploresomeofthenewfeaturesthatitbuildsuponandhowtomosteffectivelyemploythemtostreamlineyourapplication.
![Page 607: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/607.jpg)
UnderstandingandproperlyutilizingenableProdModewithpureandimpurepipesAngular2'schangedetectionprocessisanelegantbutficklebeastthatischallengingtounderstandatfirst.Whileitoffershugeefficiencygainsoverthe1.xframework,thegainscancomeatacost.ThedevelopmentmodeofAngularisactivatedbydefault,whichwillalertyouwhenyourcodeisindangerofbehavinginawaythatdefeatsthechangedetectionefficiencygains.Inthisrecipe,you'llimplementafeaturethatviolatesAngular'schangedetectionschema,correctit,andsafelyuseenableProdMode.
Note
Thecode,links,andaliveexamplerelatedtothisrecipeareavailableathttp://ngcookbook.herokuapp.com/0623/.
![Page 608: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/608.jpg)
GettingreadyBeginwithasimplecomponent:
[src/app/app.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'app-root',
template:`
<input#t>
<button(click)="update(t.value)">Save</button>
<h1>{{title}}</h1>
`
})
exportclassAppComponent{
title:string='';
update(newTitle:string){
this.title=newTitle;
}
}
Whenthebuttoninthiscomponentisclicked,itgrabsthevalueoftheinputandinterpolatesittotheheadertag.Asis,thisimplementationisperfectlysuitable.
![Page 609: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/609.jpg)
Howtodoit...TodemonstratetherelevanceofenableProdMode,you'llneedtointroduceabehaviorthatenableProdModewouldmask.Morespecifically,thismeansapieceofyourapplicationwillbehavedifferentlywhentwosequentialpassesofchangedetectionarerun.
Note
Thereareasignificantnumberofwaystoimplementthis,butforthepurposeofthisrecipe,you'llimplementanonsensicalpipethatchangeseverytimeit'sused.
Generatingaconsistencyerror
Createanonsensicalpipe,namelyAddRandomPipe:
[src/app/add-random.pipe.ts]
import{Pipe,PipeTransform}from'@angular/core';
@Pipe({
name:'addRandomPipe'
})
exportclassAddRandomPipeimplementsPipeTransform{
transform(value:string):string{
returnvalue+Math.random();
}
}
Next,takethispipeandintroduceittoyourcomponent:
[src/app/app.component.ts]
import{Component}from'@angular/core';
import{AddRandomPipe}from'./add-random.pipe';
@Component({
selector:'app-root',
template:`
<input#t>
<button(click)="update(t.value)">Save</button>
<h1>{{title|addRandomPipe}}</h1>
`
})
exportclassAppComponent{
title:string='';
update(newTitle:string){
this.title=newTitle;
}
}
![Page 610: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/610.jpg)
Thiswon'tcreateanerroryet,though.
Note
Angularwillindeedrunthechangedetectionprocesstwice,soitmightseemmysteriousthatitdoesn'tcreateanerror.Eventhoughthepipewillgenerateanewoutputeverytimeitstransformmethodisinvoked,Angularissmartenoughtorecognizethattheinputoftheinterpolationaren'tchanging,andtherefore,reevaluatingthepipeisunnecessary.Thisiscalleda"pure"pipe,whichistheAngulardefault.
InordertogetAngulartoevaluatethepipeduringeachchangedetectioncycle,specifythepipeas"impure":
[src/app/add-random.pipe.ts]
import{Pipe,PipeTransform}from'@angular/core';
@Pipe({
name:'addRandomPipe',
pure:false
})
exportclassAddRandomPipeimplementsPipeTransform{
transform(value:string):string{
returnvalue+Math.random();
}
}
Nowthefunbegins.Whenyouruntheapplication,youshouldseesomethingsimilartothefollowingerrormessage:
EXCEPTION:Errorin./AppComponentclassAppComponent-inlinetemplate:3:8
causedby:Expressionhaschangedafteritwaschecked.Previousvalue:
'0.0495151713435904'.Currentvalue:'0.9266277919907477'.
Introducingchangedetectioncompliance
Theerrorisbeingthrownassoonastheapplicationstartsup,beforeyou'reevengivenachancetopressthebutton.ThismeansthatAngularisdetectingthebindingmismatchasthecomponentisbeinginstantiatedandrenderedforthefirsttime.
Note
You'vecreatedapipethatintentionallychangeseachtime,soyourgoalistoinstructAngulartonotbothercheckingthepipeoutputtwiceagainstitselfuponcomponentinstantiation.
Atthispoint,you'vemanagedtogetAngulartothrowaconsistencyerror,whichitdoesbecauseit'srunningchangedetectioncheckstwiceandgettingdifferentresults.SwitchingonenableProdModeatthispointwouldstoptheerrorsinceAngularwouldthenonlyrunchange
![Page 611: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/611.jpg)
detectiononceandnotbothertocheckforconsistency.ThisisbecauseittrustsyoutohaveverifiedcompliancebeforeusingenableProdMode.TurningonenableProdModetomaskconsistencyerrormessagesisabitlikecominghometofindyourhouseisonfireandthendecidingtogoonavacation.
Angularallowsyoutocontrolthisbyspecifyingthechangedetectionstrategyforthecomponent.Thedefaultistoalwaysperformachangedetectioncheck,butthiscanbeoverriddenwiththeOnPushconfiguration:
[src/app/app.component.ts]
import{Component,ChangeDetectionStrategy}
from'@angular/core';
import{AddRandomPipe}from'./add-random.pipe';
@Component({
selector:'app-root',
template:`
<input#t>
<button(click)="update(t.value)">Save</button>
<h1>{{title|addRandomPipe}}</h1>
`,
changeDetection:ChangeDetectionStrategy.OnPush
})
exportclassAppComponent{
title:string='';
update(newTitle:string){
this.title=newTitle;
}
}
Nowwhenthecomponentinstanceisinitialized,youshouldnolongerseetheconsistencyerror.
SwitchingonenableProdMode
SinceyourapplicationisnowcompliantwithAngular'schangedetectionmechanism,you'refreetouseenableProdMode.Thisletsyourapplicationrunchangedetectiononceeachtime.Thisisbecauseitassumestheapplicationwillarriveinastablestate.
Inyourapplication'sbootstrappingfile,invokeenableProdModebeforeyoustartbootstrapping:
[src/main.ts]
import{platformBrowserDynamic}
from'@angular/platform-browser-dynamic';
import{enableProdMode}from'@angular/core';
import{AppModule}from'./app/';
enableProdMode();
![Page 612: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/612.jpg)
platformBrowserDynamic().bootstrapModule(AppModule);
![Page 613: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/613.jpg)
Howitworks...enableProdModewillconfigureyourapplicationinanumberofways,includingsilencingerrorandinformationalerrormessages,amongothers;however,noneofthesewaysisasvisibleasthesuppressionofthesecondarychangedetectionprocess.
![Page 614: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/614.jpg)
There'smore...Thereareotherwaystomitigatethisconsistencyproblem.Forexample,supposeyouwanttogenerateapipethatwillappendarandomnumbertotheinput.Itdoesn'tnecessarilyneedtobeadifferentrandomnumbereverysingletime;rather,youcanhaveoneforeachuniqueinputwithinacertainperiodoftime.Suchasituationcouldallowyoutohaveapipethatutilizessomesortofcachingstrategy.Ifthepipecachesresultsforaperiodoftimelongerthanchangedetectiontakestocomplete(whichisnotverylong),thenalteringthechangedetectionstrategyofthecomponentisnotnecessary.Thisisbecausemultiplepipeinvocationswillyieldanidenticalresponse.Forexample,refertothefollowingcode:
[src/app/add-random.pipe.ts]
import{Pipe,PipeTransform}from'@angular/core';
@Pipe({
name:'addRandomPipe',
pure:false
})
exportclassAddRandomPipeimplementsPipeTransform{
cache:Object={};
transform(input:string):string{
letvalue=this.cache[input];
if(!value||value.expire<Date.now()){
value={
text:input+Math.random(),
//Expiresinonesecond
expire:Date.now()+1000
}
this.cache[input]=value;
}
returnvalue.text;
}
}
Withthis,youcansafelystrikechangeDetection:ChangeDetectionStrategy.OnPushfromComponentMetadataofyourAppComponentandyouwillnotseeanyconsistencyerrors.
![Page 615: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/615.jpg)
SeealsoConfiguringtheAngular2rendererservicetousewebworkersguidesyouthroughtheprocessofsettingupyourapplicationtorenderonawebworkerConfiguringapplicationstouseahead-of-timecompilationguidesyouthroughhowtocompileanapplicationduringthebuild
![Page 616: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/616.jpg)
WorkingwithzonesoutsideAngularWorkingwithzonesentirelyinsideoftheAngularframeworkconcealswhattheyarereallydoingbehindthescenes.Itwouldbeadisservicetoyou,thereader,tojustglossovertheunderlyingmechanism.Inthisrecipe,you'lltakethevanillazone.jsimplementationoutsideofAngularandmodifyitabitinordertoseehowAngularcanmakeuseofit.
TherewillbenoAngularusedinthisrecipe,onlyzone.jsinsideasimpleHTMLfile.Furthermore,thisrecipewillbewritteninplainES5JavaScriptforsimplicity.
Note
Thecode,links,andaliveexamplerelatedtothisrecipeareavailableathttp://ngcookbook.herokuapp.com/0591/.
![Page 617: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/617.jpg)
GettingreadyBeginwiththefollowingsimpleHTMLfile:
[index.html]
<buttonid="button">Clickme</button>
<buttonid="add">Addlistener</button>
<buttonid="remove">Removelistener</button>
<script>
varbutton=document.getElementById('button');
varadd=document.getElementById('add');
varremove=document.getElementById('remove');
varclickCallback=function(){
console.log('click!');
};
setInterval(function(){
console.log('setinterval!');
},1000);
add.addEventListener('click',function(){
button.addEventListener('click',clickCallback);
});
remove.addEventListener('click',function(){
button.removeEventListener('click',clickCallback);
});
</script>
Eachofthesecallbackshaslogstatementsinsidethemsoyoucanseewhentheyareinvoked:
setIntervalcallsitsassociatedlistenereverysecondClickingonClickmecallsitslistenerifitisattachedClickingonAddlistenerattachesaclicklistenertothebuttonClickingonRemovelistenerremovestheclicklistener
Note
There'snothingspecialgoingonhere,becauseallofthesearedefaultbrowserAPIs.Themagicofzones,asyouwillsee,isthatthezonebehaviorcanbeintroducedaroundthiswithoutmodifyinganycode.
![Page 618: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/618.jpg)
Howtodoit...First,addthezone.jsscripttothetopofthefile:
[index.html]
<scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/zone.js/0.6.26/zone.js">
</script>
<buttonid="button">Clickme</button>
<buttonid="add">Addlistener</button>
<buttonid="remove">Removelistener</button>
...
Note
There'snospecialsetupneededforzone.js,butitneedstobeaddedbeforeyousetlistenersordoanythingthatcouldhaveasynchronousimplications.Angularneedsthisdependencytobeaddedbeforeitisinitialized.
AddingthisscriptintroducesZonetotheglobalnamespace.zone.jshasalreadycreatedaglobalzoneforyou.Thiscanbeaccessedwiththefollowing:
Zone.current
Forkingazone
Theglobalzoneisn'tdoinganythinginterestingyet.Tocustomizeazone,you'llneedtocreateyourownbyforkingtheonewehaveandrunningrelevantcodeinsideit.Dothisasfollows:
[index.html]
<script
src="https://cdnjs.cloudflare.com/ajax/libs/zone.js/0.6.26/zone.js">
</script>
<buttonid="button">Clickme</button>
<buttonid="add">Addlistener</button>
<buttonid="remove">Removelistener</button>
<script>
varbutton=document.getElementById('button');
varadd=document.getElementById('add');
varremove=document.getElementById('remove');
Zone.current.fork({}).run(function(){
varclickCallback=function(){
console.log('click!');
};
![Page 619: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/619.jpg)
setInterval(function(){
console.log('setinterval!');
},1000);
add.addEventListener('click',function(){
button.addEventListener('click',clickCallback);
});
remove.addEventListener('click',function(){
button.removeEventListener('click',clickCallback);
});
});
</script>
Behaviorally,thisdoesn'tchangeanythingfromtheperspectiveoftheconsole.fork()takesanemptyZoneSpecobjectliteral,whichyouwillmodifynext.
OverridingzoneeventswithZoneSpec
Whenapieceofcodeisruninazone,youareabletoattachbehaviorsatimportantpointsintheasynchronousbehaviorflow.Here,you'lloverridefourzoneevents:
scheduleTask
invokeTask
hasTask
cancelTask
You'llbeginwithscheduleTask.DefineanoverridemethodinsideZoneSpec.Overridesusetheeventnamesprefixedwithon:
[index.html]
<script
src="https://cdnjs.cloudflare.com/ajax/libs/zone.js/0.6.26/zone.js">
</script>
<buttonid="button">Clickme</button>
<buttonid="add">Addlistener</button>
<buttonid="remove">Removelistener</button>
<script>
varbutton=document.getElementById('button');
varadd=document.getElementById('add');
varremove=document.getElementById('remove');
Zone.current.fork({
onScheduleTask:function(zoneDelegate,zone,targetZone,task){
console.log('schedule');
zoneDelegate
.scheduleTask(targetZone,task);
}
![Page 620: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/620.jpg)
}).run(function(){
varclickCallback=function(){
console.log('click!');
};
setInterval(function(){
console.log('setinterval!');
},1000);
add.addEventListener('click',function(){
button.addEventListener('click',clickCallback);
});
remove.addEventListener('click',function(){
button.removeEventListener('click',clickCallback);
});
});
</script>
Withthisaddition,youshouldseethatthezonerecognizesthatthreetasksarebeingscheduled.Thisshouldmakesense,asyouaredeclaringthreeinstancesthatcangenerateasynchronousactions:setInterval,addEventListener,andremoveEventListener.IfyouclickonAddlistener,you'llseeitschedulethefourthtaskaswell.
Note
zoneDelegate.scheduleTask()isrequiredbecauseyouareactuallyoverwritingwhatthezoneisusingforthathandler.Ifyoudon'tperformthisaction,theschedulerhandlerwillexitbeforeactuallyschedulingthetask.
Theoppositeofschedulingataskiscancelingit,sooverridethateventhandlernext:
[index.html]
Zone.current.fork({
onScheduleTask:function(zoneDelegate,zone,targetZone,task){
console.log('schedule');
zoneDelegate
.scheduleTask(targetZone,task);
},
onCancelTask:function(zoneDelegate,zone,targetZone,task){
console.log('cancel');
zoneDelegate
.cancelTask(targetZone,task);
}
}).run(function(){
...
});
Acanceleventoccurswhenascheduledtaskisdestroyed,inthisexample,whenremoveEventListener()isinvoked.IfyouclickonAddlistenerandthenRemovelistener,
![Page 621: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/621.jpg)
you'llseeacanceleventoccur.
Theschedulingoftasksisvisibleduringthestartup,buteachtimeabuttonisclickedorasetIntervalhandlerisexecuted,youdon'tseeanythingbeinglogged.Thisisbecauseschedulingatask,whichoccursduringregistration,isdistinctfrominvokingatask,whichiswhentheasynchronouseventactuallyoccurs.
Todemonstratethis,addaninvokeTaskoverride:
[index.html]
Zone.current.fork({
onScheduleTask:function(zoneDelegate,zone,targetZone,task){
console.log('schedule');
zoneDelegate
.scheduleTask(targetZone,task);
},
onCancelTask:function(zoneDelegate,zone,targetZone,task){
console.log('cancel');
zoneDelegate
.cancelTask(targetZone,task);
},
onInvokeTask:function(zoneDelegate,zone,targetZone,task,
applyThis,applyArgs){
console.log('invoke');
zoneDelegate
.invokeTask(targetZone,task,applyThis,applyArgs);
}
}).run(function(){
...
});
Withthisaddition,youshouldnowbeabletoseeaconsolelogeachtimeataskisinvoked—forabuttonclickorasetIntervalcallback.
Sofar,you'vebeenabletoseewhenthezonehastasksscheduledandinvoked,butnow,dothereverseofthistodetectwhenallthetaskshavebeencompleted.ThiscanbeaccomplishedwithhasTask,whichcanalsobeoverridden:
[index.html]
Zone.current.fork({
onScheduleTask:function(zoneDelegate,zone,targetZone,task){
console.log('schedule');
zoneDelegate
.scheduleTask(targetZone,task);
},
onCancelTask:function(zoneDelegate,zone,targetZone,task){
console.log('cancel');
zoneDelegate
![Page 622: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/622.jpg)
.cancelTask(targetZone,task);
},
onInvokeTask:function(zoneDelegate,zone,targetZone,task,
applyThis,applyArgs){
console.log('invoke');
zoneDelegate
.invokeTask(targetZone,task,applyThis,applyArgs);
},
onHasTask:function(zoneDelegate,zone,targetZone,isEmpty){
console.log('has');
zoneDelegate.hasTask(targetZone,isEmpty);
}
}).run(function(){
...
});
TheisEmptyparameterofonHasTaskhasthreeproperties:eventTask,macroTask,andmicroTask.ThesethreepropertiesmaptoBooleansdescribingwhethertheassociatedqueueshaveanytasksinsidethem.
Withthesefourcallbacks,youhavesuccessfullyinterceptedfourimportantpointsinthecomponentlifecycle:
Whenatask"generator"isscheduled,whichmaygeneratetasksfrombrowserortimereventsWhenatask"generator"iscanceledWhenataskisactuallyinvokedHowtodeterminewhetheranytasksarescheduledandofwhattype
![Page 623: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/623.jpg)
Howitworks...Theconceptthatformsthecoreofzonesistheinterceptionofasynchronoustasks.Moredirectly,youwanttheabilitytoknowwhenasynchronoustasksarebeingcreated,howmanythereare,andwhenthey'redone.
zone.jsaccomplishesthisbyshimmingalltherelevantbrowsermethodsthatareresponsibleforsettingupasynchronoustasks.Infact,allthemethodsusedinthismethod—setInterval,addEventListener,andremoveEventListener—areallshimmedsothatthezonetheyareruninsideisawareofanyasynchronoustaskstheymightaddtothequeue.
![Page 624: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/624.jpg)
There'smore...TobegintorelatethistoAngular,you'llneedtotakeastepbacktoexaminethezoneecosystem.
Understandingzone.run()
You'llnoticeinthisexamplethatinvokeisprintedforeachasynchronousaction,evenforthosethatwereregisteredinsideanotherasynchronousaction.Thisisthepowerofzones.Anythingdoneinsidethezone.run()blockwillcascadewithinthesamezone.Thisway,thezonecankeeptrackofanunbrokensegmentofasynchronousbehaviorwithoutanoceanofboilerplatecode.
Microtasksandmacrotasks
Thisactuallyhasnothingtodowithzone.jsatallbutratherwithhowthebrowsereventloopworks.Alltheeventsgeneratedbyyouinthisexample—clicks,timerevents,andsoon—aremacrotasks.Thatis,thebrowserrespectstheirhandlersasasynchronous,blockingsegmentofcode.Thecodethatexecutesaroundthesetasks—thezone.jscallbacks,forexample—aremicrotasks.Theyaredistinctfrommacrotasksbutarestillsynchronouslyexecutedaspartoftheentire"turn"forthatmacrotask.
Note
Amacrotaskmaygeneratemoremicrotasksforitselfwithinitsownturn.
Oncethemicrotaskandmacrotaskqueuesareempty,thezonecanbeconsideredtobestable,sincethereisnoasynchronousbehaviortobeanticipated.ForAngular,thissoundslikeagreattimetoupdatetheUI.
Infact,thisisexactlywhatAngularisdoingbehindthescenes.Angularviewsthebrowserthroughthetask-centricgogglesofzone.jsandusesthisclevertooltodecidewhentogoaboutrendering.
![Page 625: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/625.jpg)
SeealsoListeningforNgZoneeventsgivesabasicunderstandingofhowAngularisusingzonesExecutionoutsidetheAngularzoneshowsyouhowtoperformlong-runningoperationswithoutincurringazoneoverheadConfiguringcomponentstouseexplicitchangedetectionwithOnPushdescribeshowtomanuallycontrolAngular'schangedetectionprocess
![Page 626: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/626.jpg)
ListeningforNgZoneeventsWiththeintroductionofAngular2comestheconceptofzones.Beforeyoubeginthisrecipe,IstronglyrecommendedyoutobeginbyworkingthroughtheWorkingwithzonesoutsideAngularrecipe.
![Page 627: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/627.jpg)
zone.jszone.jsisalibrarythatAngular2directlydependsupon.ItallowsAngulartobebuiltuponazonethatallowstheframeworktointimatelymanageitsexecutioncontext.
Moreplainly,thismeansthatAngularcantellwhenasynchronousthingsarehappeningthatitmightcareabout.Ifthissoundsabitlikehow$scope.apply()wasrelevantinAngular1.x,youarethinkingintherightway.
![Page 628: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/628.jpg)
NgZoneAngular2'sintegrationwithzonestakestheformoftheNgZoneservice,whichactsasasortofwrapperfortheactualAngularzones.ThisserviceexposesausefulAPIthatyoucantapinto.
Note
Thecode,links,andaliveexamplerelatedtothisrecipeareavailableathttp://ngcookbook.herokuapp.com/8676/.
![Page 629: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/629.jpg)
GettingreadyAllthatisneededforthisrecipeisacomponentintowhichtheNgZoneservicecanbeinjected:
[src/app/app.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'app-root',
template:``
})
exportclassAppComponent{
constructor(){
}
}
![Page 630: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/630.jpg)
Howtodoit...BeginbyinjectingtheNgZoneserviceintoyourcomponent,whichismadeavailableinsidethecoreAngularmodule:
[src/app/app.component.ts]
import{Component,NgZone}from'@angular/core';
@Component({
selector:'app-root',
template:``
})
exportclassAppComponent{
constructor(privatezone:NgZone){
}
}
TheNgZoneserviceexposesanumberofEventEmittersthatyoucanattachto.Sincezonesarecapableoftrackingasynchronousactivitywithinit,theNgZoneserviceexposestwoEventEmittersthatunderstandwhenanasynchronousactivitybecomesenqueuedanddequeuedasmicrotasks.
TheonUnstableEventEmitterletsyouknowwhenoneormoremicrotasksareenqueued;onStableisfiredwhenthethemicrotaskqueueisemptyandAngulardoesnotplantoenqueueanymore.
Addhandlerstoeach:
[src/app/app.component.ts]
import{Component,NgZone}from'@angular/core';
@Component({
selector:'app-root',
template:``
})
exportclassAppComponent{
constructor(privatezone:NgZone){
zone.onStable.subscribe(()=>console.log('stable'));
zone.onUnstable.subscribe(()=>console.log('unstable'));
}
}
Terrific!However,thelogoutputofthisisquiteboring.Atapplicationstartup,you'llseethattheapplicationisreportedasstable,butnothingfurther.
Demonstratingthezonelifecycle
![Page 631: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/631.jpg)
IfyouunderstandhowAngularuseszones,thelackofloggingshouldn'tsurpriseyou.There'snothingtogenerateasynchronoustasksinthiszone.Goaheadandaddsomebycreatingabuttonwithahandlerthatsetsatimeoutlogstatement:
[src/app/app.component.ts]
import{Component,NgZone}from'@angular/core';
@Component({
selector:'app-root',
template:`<button(click)="foo()">foo</button>`
})
exportclassAppComponent{
constructor(privatezone:NgZone){
zone.onStable.subscribe(()=>console.log('stable'));
zone.onUnstable.subscribe(()=>console.log('unstable'));
}
foo(){
setTimeout(()=>console.log('timeouthandler'),1000);
}
}
Nowwitheachclick,youshouldseeanunstable-stablepair,followedbyanunstable-timeouthandler-stablesetonesecondlater.Thismeansyou'vesuccessfullytiedintoAngular'szoneemitters.
![Page 632: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/632.jpg)
Howitworks...Inordertoobviatethenecessityofa$scope.apply()construct,Angularneedstheabilitytointelligentlydecidewhenitshouldchecktoseewhetherthestateoftheapplicationhaschanged.
Inanasynchronoussetting,suchasabrowserenvironment,thisseemslikeamessytaskatfirst.Somethingliketimedeventsandinputeventsare,bytheirverynature,difficulttokeeptrackof.Forexample,refertothefollowingcode:
element.addEventListener('click',_=>{
//dosomestuff
setInterval(_=>{
//dosomestuff
},1000);
});
Thiscodeiscapableofchangingthemodelintwodifferentplaces,bothofwhichareasynchronous.Codesuchasthisiswrittenallthetimeandinsomanydifferentplaces;it'sdifficulttoimagineawayofkeepingtrackofsuchcodewithoutsprinklingsomethinglike$scope.$apply()allovertheplace.
Theutilityofzone.js
Thebigideaofzonesistogiveyoutheabilitytograsphowandwhenthebrowserisperformingasynchronousactionsthatyoucareabout.
NgZoneiswrappingtheunderlyingzoneAPIforyouinsteadofexposingEventEmitterstothevariouspartsofthelifecycle,butthisshouldn'tconfuseyouonebit.Forthisexample,thelogoutputisdemonstratingthefollowing:
1. Theapplicationinitializesandexaminestheapplicationzone.Therearenotasksscheduled,soAngularemitsastableevent.
2. Youclickonthebutton.3. Thisgeneratesaclickevent,whichinturngeneratesataskinsidethezonetoexecutethe
clickhandler.Angularseesthisandemitsanunstableevent.4. Theclickhandlerisexecuted,schedulingataskin1second.5. Theclickhandleriscompleted,andtheapplicationonceagainhasnopendingtasks.Angular
emitsastableevent.6. OnesecondelapsesandthebrowsertimeraddsthesetTimeouthandlertothetaskqueue.
Sincethisisshimmedbythezone,Angularseesthisoccurandemitsanunstableevent.7. ThesetTimeouthandlerisexecuted.8. ThesetTimeouthandleriscompleted,andtheapplicationonceagainhasnopendingtasks.
Angularemitsastableevent.
![Page 633: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/633.jpg)
SeealsoWorkingwithzonesoutsideAngularisanexcellentintroductiontohowzonesworkinthebrowserExecutionoutsidetheAngularzoneshowsyouhowtoperformlong-runningoperationswithoutincurringazoneoverheadConfiguringcomponentstouseexplicitchangedetectionwithOnPushdescribeshowtomanuallycontrolAngular'schangedetectionprocess
![Page 634: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/634.jpg)
ExecutionoutsidetheAngularzoneTheutilityofzone.jsisterrific,sinceitworksautomatically,butaseasonedsoftwareengineerknowsthisoftencomesataprice.Thisisespeciallytruewhentheconceptofdatabindingcomesintoplay.
Inthisrecipe,you'lllearnhowtoexecuteoutsidetheAngularzoneandwhatbenefitsthisaffordsyou.
Note
Thecode,links,andaliveexamplerelatedtothisrecipeareavailableathttp://ngcookbook.herokuapp.com/3362/.
![Page 635: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/635.jpg)
Howtodoit...Tocompareexecutionindifferentcontexts,createtwobuttonsthatrunthesamecodeindifferentzonecontexts.Thebuttonsshouldcountto100withsetTimeoutincrements.Usetheperformanceglobaltomeasurethetimeittakes:
[src/app/app.component.ts]
import{Component,NgZone}from'@angular/core';
@Component({
selector:'app-root',
template:`
<button(click)="runInsideAngularZone()">
RuninsideAngularzone
</button>
<button(click)="runOutsideAngularZone()">
RunoutsideAngularzone
</button>
`
})
exportclassAppComponent{
progress:number=0;
startTime:number=0;
constructor(privatezone:NgZone){}
runInsideAngularZone(){
this.start();
this.step(()=>this.finish('InsideAngularzone'));
}
runOutsideAngularZone(){
this.start();
this.step(()=>this.finish('OutsideAngularzone'));
}
start(){
this.progress=0;
this.startTime=performance.now();
}
finish(location:string){
this.zone.run(()=>{
console.log(location);
console.log('Took'+
(performance.now()-this.startTime)+'ms');
});
}
step(doneCallback:()=>void){
if(++this.progress<100){
![Page 636: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/636.jpg)
setTimeout(()=>{
this.step(doneCallback);
},10);
}else{
doneCallback();
}
}
}
Atthispoint,thetwobuttonswillbehaveidentically,asbothofthemarebeingexecutedinsidetheAngularzone.
InordertoexecuteoutsidetheAngularzone,you'llneedtousetherunOutsideAngular()methodexposedbyNgZone:
runInsideAngularZone(){
this.start();
this.step(()=>this.finish('InsideAngularzone'));
}
runOutsideAngularZone(){
this.start();
this.zone.runOutsideAngular(()=>{
this.step(()=>this.finish('OutsideAngularzone'));
});
}
Atthispoint,youcanrunbothofthemagainsidebysideandverifythattheystilltake(roughly)thesameamountoftimetoexecute.Thisshouldnotsurpriseyou,astheyarestillperformingthesametask.Theinclusionofzone.jsmeansthatthebrowserAPIsareshimmedoutsideAngular,soevenrunningthisoutsidetheAngularzonemeansitisstillrunninginsideazone.
Inordertoseeaperformancedifference,you'llneedtointroducesomebindinginsidethetemplate:
[src/app/app.component.ts]
import{Component,NgZone}from'@angular/core';
@Component({
selector:'app-root',
template:`
<h3>Progress:{{progress}}%</h3>
<button(click)="runInsideAngularZone()">
RuninsideAngularzone
</button>
<button(click)="runOutsideAngularZone()">
RunoutsideAngularzone
</button>
`
})
![Page 637: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/637.jpg)
exportclassAppComponent{
...
}
Nowyoushouldbegintoseeasubstantivedifferencebetweentheruntimesofeachbutton.Thisshowsthatwhentheoverheadofbindingsiscausingtheapplicationtoslowdown,runOutsideAngular()hasthepotentialtoyieldsurprisinglysubstantiveperformanceoptimizations.
![Page 638: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/638.jpg)
Howitworks...WhenyouexaminetheNgZonesource,you'llfindthatthe"outer"zoneismerelythetopmostbrowserzone.AngularforksthiszoneuponinitializationandbuildsuponittoyieldtheniceNgZoneservicewrapper.
However,becausezonesdonotdiscriminateintherealmofasynchronouscallbacksanddatabinding,eachinvocationofthesetTimeouthandlerinsidetheAngularzoneisrecognizedasaneventthathasimplicationsonthetemplaterenderingprocess.Ineveryinvocation,Angularseesanupdatetothebounddatafollowinganasynchronoustask,andproceedstorerendertheview.Whenthisisdone100times,itaddsuptoseveralhundredextramillisecondsofexecution.
WhenthisisrunoutsidetheAngularzone,AngularisnolongerawareofthesetTimeouttasksthatarebeingexecutedandsodoesnotrequirearerender.Upontheveryfinaliterationthough,invokeNgZone.run();thiswillcausetheexecutiontorejointheAngularzone.Angularseesthetaskandthemodifieddataandupdatesthebindingsaccordingly;thistimethough,thisisdoneonlyonce.
![Page 639: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/639.jpg)
There'smore...Inthisrecipe,thefinish()methodinvokestherun()methodforboththeAngularzoneandthenon-Angularzone.Whenthezonethatthisisinvokeduponisalreadythecontextualzoneinwhichthetaskisbeingexecuted,usingrun()becomesredundantandiseffectivelyano-op.
![Page 640: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/640.jpg)
SeealsoWorkingwithzonesoutsideAngularisanexcellentintroductiontohowzonesworkinthebrowserListeningforNgZoneeventsgivesabasicunderstandingofhowAngularisusingzonesConfiguringcomponentstouseexplicitchangedetectionwithOnPushdescribeshowtomanuallycontrolAngular'schangedetectionprocess
![Page 641: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/641.jpg)
ConfiguringcomponentstouseexplicitchangedetectionwithOnPushTheconventionofAngular'sdataflow,inwhichdataflowsdownwardthroughthecomponenttreeandeventsfloatupwards,engenderssomeinterestingpossibilities.OneoftheseinvolvescontrollingwhenAngularshouldperformchangedetectionatagivennodeinthecomponenttree.
Note
Thecode,links,andaliveexamplerelatedtothisrecipeareavailableathttp://ngcookbook.herokuapp.com/4909/.
![Page 642: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/642.jpg)
GettingreadyBeginwiththefollowingsimpleapplication:
[app/root.component.ts]
import{Component}from'@angular/core';
import{Subject}from'rxjs/Subject';
import{Observable}from'rxjs/Observable';
@Component({
selector:'root',
template:`
<button(click)="shareSubject.next($event)">Share!</button>
<article[shares]="shareEmitter"></article>
`
})
exportclassRootComponent{
shareSubject:Subject<Event>=newSubject();
shareEmitter:Observable<Event>=
this.shareSubject.asObservable();
}
[app/article.component.ts]
import{Component,Input,ngOnInit}from'@angular/core';
import{Observable}from'rxjs/Observable';
@Component({
selector:'article',
template:`
<h1>{{title}}</h1>
<p>Shares:{{count}}</p>
`
})
exportclassArticleComponentimplementsOnInit{
@Input()shares:Observable<Event>;
count:number=0;
title:string=
'InsuranceFraudGrowsInWakeofApplePieHubbub';
ngOnInit(){
this.shares.subscribe((e:Event)=>++this.count);
}
}
Thisverysimpleapplicationisjustusinganobservabletopassshareeventsdown,fromaparentcomponenttoachildcomponent.Thechildcomponentkeepstrackoftheclickeventcountandinterpolatesthiscounttothepage.
Yourobjectiveistomodifythissetupsothatthechildcomponentonlydetectsachangewhenaneventisemittedfromtheobservable.
![Page 643: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/643.jpg)
Howtodoit...Outofthebox,Angularalreadyhasaveryrobustwayofdetectingchange:zones.Wheneverthereisaneventinsideazone,Angularrecognizesthatthiseventhasthepotentialtomodifytheapplicationinameaningfulway.Itthenperformschangedetectionfromthetopofthecomponenttreedowntothebottom,checkingwhetheranythingneedstobeupdatedinthechangedmodel.Sincedataonlyflowsdownward,thisisalreadyanextremelyefficientwayofhandlingit.
However,youmightliketoexertsomecontrolinthissituationsinceyoumaybeabletohand-optimizewhenchangedetectionshouldoccurinsideacomponent.Mostlikelyyouwillveryeasilybeabletotellwhenacomponentmayormaynotchange,basedonwhatishappeningaroundit.
ConfiguringtheChangeDetectionStrategy
Angularoffersyoutheoptionofchanginghowthechangedetectionschemeworks.IfyouwouldpreferthatAngularrefrainfromlisteningtozoneeventstokickoffaroundofchangedetection,youcaninsteadconfigureacomponenttoonlyperformchangedetectionwhenaninputischanged.ThiscanbedonebyconfiguringthecomponenttousetheOnPushstrategyinsteadofthedefault.
Surely,thiswilloccurlessoftenthanthefirehoseofbrowserevents.Ifthecomponentwillonlychangewhenaninputischanged,thenthiswillsaveAngularthetroubleofdoinganiterationofchangedetectiononthatcomponent.
ModifyArticleComponenttoinsteaduseOnPush:
[app/article.component.ts]
import{Component,Input,ngOnInit,ChangeDetectionStrategy}
from'@angular/core';
import{Observable}from'rxjs/Observable';
@Component({
selector:'article',
template:`
<h1>{{title}}</h1>
<p>Shares:{{count}}</p>
`,
changeDetection:ChangeDetectionStrategy.OnPush
})
exportclassArticleComponentimplementsOnInit{
@Input()shares:Observable<Event>;
count:number=0;
title:string=
'InsuranceFraudGrowsInWakeofApplePieHubbub';
![Page 644: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/644.jpg)
ngOnInit(){
this.shares.subscribe((e:Event)=>++this.count);
}
}
Thissuccessfullychangesthestrategy,butthereisonesignificantproblemnow:thecountwillnolongerbeupdated.Thishappens,ofcourse,becausetheinputtothiscomponentisnotbeingchanged.
ThecountonlyupdatedbeforebecauseAngularwasseeingclickeventsonthebutton,whichcausedchangedetectiononArticleComponent.Now,Angularhasbeeninstructedtoignoretheseclickevents,eventhoughthecountinsidethechildcomponentisstillbeingupdatedfromtheobservablehandlers.
Requestingexplicitchangedetection
Youareabletoinjectareferencetothecomponent'schangedetector.Withthis,itexposesamethodthatallowsyoutoforcearoundofchangedetectionwheneveryoulike.Sincethemodelisbeingupdatedinsideanobservablehandler,thisseemslikeafineplacetotriggerthechangedetectionprocess:
[app/article.component.ts]
import{Component,Input,ngOnInit,ChangeDetectionStrategy,
ChangeDetectorRef}from'@angular/core';
import{Observable}from'rxjs/Observable';
@Component({
selector:'article',
template:`
<h1>{{title}}</h1>
<p>Shares:{{count}}</p>
`,
changeDetection:ChangeDetectionStrategy.OnPush
})
exportclassArticleComponentimplementsOnInit{
@Input()shares:Observable<Event>;
count:number=0;
title:string=
'InsuranceFraudGrowsInWakeofApplePieHubbub';
constructor(privatechangeDetectorRef_:ChangeDetectorRef){}
ngOnInit(){
this.shares.subscribe((e:Event)=>{
++this.count;
this.changeDetectorRef_.markForCheck();
});
}
}
![Page 645: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/645.jpg)
Nowyouwillseethecountgettingupdatedonceagain.
![Page 646: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/646.jpg)
Howitworks...UsingOnPushessentiallyconvertsacomponenttooperatelikeablackbox.Iftheinputdoesn'tchange,thenAngularassumesthatthestateinsidethecomponentwillremainconstant,andthereforethereisnoneedtoproceedfurtherwithregardtodetectingachange.
Sincechangedetectionalwaysflowsfromtoptobottominthecomponenttree,therequestforchangedetectionusesmarkForCheck(),marksthecomponent'schangedetectortorunonlywhenAngularreachesthatcomponentinsidethetree.
![Page 647: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/647.jpg)
There'smore...Thisisausefulpatternifyou'relookingtosqueezeadditionalperformancefromyourapplication,butinsomecases,doingthiscandevelopintoananti-pattern.TheneedtoexplicitlydefinewhenAngularshouldperformchangedetectioncanbecometediousasyourcomponentcodegrowsinsize.TherecanpotentiallybebugsthatarisefrommissingoneplacewheremarkForCheck()shouldhavebeeninvokedbutwasnot.Angular'schangedetectionstrategyisalreadyquiteperformantandrobust,sousethisconfigurationwisely.
![Page 648: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/648.jpg)
SeealsoWorkingwithzonesoutsideAngularisanexcellentintroductiontohowzonesworkinthebrowserListeningforNgZoneeventsgivesabasicunderstandingofhowAngularisusingzonesExecutionoutsidetheAngularzoneshowsyouhowtoperformlong-runningoperationswithoutincurringazoneoverheadConfiguringViewEncapsulationformaximumefficiencyshowshowyoucanconfigurecomponentstoutilizetheshadowDOM
![Page 649: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/649.jpg)
ConfiguringViewEncapsulationformaximumefficiencyAlthoughitmaysoundclichéd,Angular2wasbuiltforthebrowsersoftomorrow.Youcanpointtowhythisisthecaseinalargenumberofways,butthereisonewaywherethisisextremelytrue:componentencapsulation.
TheidealcomponentmodelforAngular2istheoneinwhichcomponentsareentirelysandboxed,saveforthefewpiecesthatareexternallyvisibleandmodifiable.Inthisrespect,itdoesabang-upjob,buteventhemostmodernbrowserslimititsabilitytostriveforsuchefficacy.ThisisespeciallytrueintherealmofCSSstyling.
SeveralfeaturesofAngular'scomponentstylingareespeciallyimportant:
YouareabletowritestylesthatareguaranteedtobeonlyapplicabletoacomponentYoucanexplicitlyspecifystylesthatshouldbeinheriteddownwardthroughthecomponenttreeYoucanspecifyanencapsulationstrategyonapiecewisebasis
Thereareanumberofinterestingwaystoaccomplishanefficientcomponentstylingschema.
Note
Thecode,links,andaliveexamplerelatedtothisrecipeareavailableathttp://ngcookbook.herokuapp.com/1463/.
![Page 650: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/650.jpg)
GettingreadyBeginwithasimpleapplicationcomponent:
[src/app/app.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'app-root',
template:`
<h1>
{{title}}
</h1>
`,
styles:[`
h1{
color:red;
}
`]
})
exportclassAppComponent{
title='appworks!';
}
![Page 651: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/651.jpg)
Howtodoit...AngularwilldefaulttonotusingShadowDOMforcomponents,asthemajorityofbrowsersdonotsupportittoasatisfactorylevel.Thenextbestthing,whichitwilldefaultto,istoemulatethisstylingencapsulationbynestingyourselectors.Theprecedingcomponentiseffectivelytheequivalentofthefollowing:
[src/app/app.component.ts]
import{Component,ViewEncapsulation}from'@angular/core';
@Component({
selector:'app-root',
template:`
<h1>
{{title}}
</h1>
`,
styles:[`
h1{
color:red;
}
`],
encapsulation:ViewEncapsulation.Emulated
})
exportclassAppComponent{
title='appworks!';
}
Emulatedstylingencapsulation
HowexactlydoesAngularperformthisemulation?Lookattherenderedapplicationtofindout.Yourcomponentwillappearsomethinglikethefollowing:
<app-root_nghost-mqf-1="">
<h1_ngcontent-mqf-1="">
appworks!
</h1>
</app-root>
Intheheadofthedocument:
<style>
h1[_ngcontent-mqf-1]{
color:red;
}
</style>
Thepictureshouldbeabitclearernow.Angularisassigningthiscomponentclass(notaninstance)auniqueID,andthestylesdefinedfortheglobaldocumentwillonlybeapplicableto
![Page 652: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/652.jpg)
tagsthathavethematchingattribute.
Nostylingencapsulation
Ifthisencapsulationisn'tnecessary,youarefreetouseencapsulation:ViewEncapsulation.None.AngularwillhappilyskiptheuniqueIDassignmentstepforyou,givingyouavanilla:
<app-root>
<h1>
appworks!
</h1>
</app-root>
Intheheadofthedocument:
<style>
h1{
color:red;
}
</style>
Nativestylingencapsulation
Thebest,mostfuturistic,andleastsupportedmethodofgoingaboutthisistouseShadowDOMinstancestogoalongwitheachcomponentinstance.Thiscanbeaccomplishedusingencapsulation:ViewEncapsulation.Native.Now,yourcomponentwillrender:
<app-root>
#shadow-root
<style>
h1{
color:red;
}
</style>
<h1>
appworks!
</h1>
</app-root>
![Page 653: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/653.jpg)
Howitworks...Angularissmartenoughtorecognizewhereitneedstoputyourstylesandhowtomodifythemtomakethemworkforyourcomponentconfiguration:
ForNoneandEmulated,stylesgointothedocumentheadForNative,stylesgoinlinewiththerenderedcomponentForNoneandNative,nostylemodificationsareneededForEmulated,stylesarerestrictedbyattributeselectors
![Page 654: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/654.jpg)
There'smore...AnimportantconsiderationofViewEncapsulationchoicesisCSSperformance.ItiswellknownandentirelyintuitivethatCSSstylingismoreefficientwhenithastotraverseasmallerpartoftheDOMandhastomatchusingasimplerselector.
Emulatingcomponentencapsulationaddsanattributeselectortoeachandeverystylethatisdefinedforthatcomponent.Atscale,itisn'thardtoseehowthiscandegradeperformance.ShadowDOMelegantlysolvesthisproblembyofferingunmodifiedstylesinsidearestrictedpieceofDOM.Itsstylescannotescapebutcanbeapplieddownwardtoothercomponents.Furthermore,ShadowDOMcomponentscanbenestedandstrategicallyapplied.
![Page 655: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/655.jpg)
SeealsoUnderstandingandproperlyutilizingenableProdModewithpureandimpurepipesdescribeshowtotakethetrainingwheelsoffyourapplicationConfiguringcomponentstouseexplicitchangedetectionwithOnPushdescribeshowtomanuallycontrolAngular'schangedetectionprocess
![Page 656: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/656.jpg)
ConfiguringtheAngular2RenderertousewebworkersOneofthemostcompellingintroductionsinthenewrenditionoftheAngularframeworkisthetotalabstractionoftherenderingexecution.ThisstemsfromoneofthecoreideasofAngular:youshouldbeabletoseamlesslysubstituteoutanybehaviormoduleandreplaceitwithanother.This,ofcourse,meansthatAngularcannothaveanydependencybleedoutsideofthemodules.
OneplacethatAngularputsemphasisonbeingconfigurableisthelocationwherecodeexecutiontakesplace.Thisismanifestedinanumberofways,andthisrecipewillfocusonAngular'sabilitytoperformrenderingexecutionatalocationotherthaninsidethemainbrowser'sJavaScriptruntime.
Note
Thecode,links,andaliveexamplerelatedtothisrecipeareavailableathttp://ngcookbook.herokuapp.com/1859/.
![Page 657: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/657.jpg)
GettingreadyBeginwithsomesimpleapplicationelementsthatdonotyetformafullapplication:
[index.html]
<!DOCTYPEhtml>
<html>
<head>
<scriptsrc="zone.js"></script>
<scriptsrc="reflect-metadata.js"></script>
<scriptsrc="system.src.js"></script>
<scriptsrc="system-config.js"></script>
</head>
<body>
<article></article>
<script>
System.import('system-config.js')
.then(function(){
System.import('main.ts');
});
</script>
</body>
</html>
[app/article.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'article',
template:`
<h2>{{title}}</h2>
`
})
exportclassArticleComponent{
title:string=
'SurveyIndicatesPlasticFunnelBestWaytoDrinkRareWine';
}
NotethatthisrecipeusesSystemJStohandlemodulesandTypeScripttranspilation,butthisismerelytokeepthedemonstrationsimple.AproperlycompiledapplicationcanuseregularJavaScripttoaccomplishthesamefeat,withnodependencyonSystemJS.
Thisrecipewillstartoffbyassumingyouhaveabasicknowledgeofwhatwebworkersareandhowtheywork.Thereisadiscussionoftheirpropertieslaterinthisrecipe.
![Page 658: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/658.jpg)
Howtodoit...TheSystemJSstartupconfigurationkicksofftheapplicationfrommain.ts,soyou'llbeginthere.Insteadofbootstrappingtheapplicationinthisfileasyounormallywould,you'lluseanimportedAngularhelpertoinitializethewebworkerinstance:
[main.ts]
import{bootstrapWorkerUi}from"@angular/platform-webworker";
bootstrapWorkerUi(window.location.href+"loader.js");
Concernyourselfwiththeprecisedetailsofwhatthisisdoinglater,butthehigh-levelideaisthatthisiscreatingawebworkerinstancethatisintegratedwiththeAngularRenderer.
Note
RecallthatinitializingawebworkerrequiresapathtoitsstartupJSfile,andarelativepathinsidethisfilemightnotalwayswork;therefore,you'reusingthewindowlocationtoprovideanabsoluteURL.Thenecessityofthismaydifferbasedonyourdevelopmentsetup.
Sincethisfilereferencesawebworkerinitializationfilecalledloader.js,writeitnext.WorkerscannotbegivenaTypeScriptfile:
[loader.js]
importScripts(
"system.js",
"zone.js",
"reflect-metadata.js",
"./system-config.js");
System.import("./web-worker-main.ts");
Thisfilefirstimportsthesamefilesthatarelistedinsidethe<head>tagofindex.html.ThisshouldmakesensesincethewebworkerwillneedtoperformsomeofthedutiesofAngularbutwithoutdirectaccesstoanythingthatexistsinsidethemainJavaScriptruntime.Forexample,sincethiswebworkerwillberenderingacomponent,itneedstobeabletounderstandthe@Component({})notation,whichcannotbedonewithoutthereflect-metadataextension.
Justlikethemainapplication,thewebworkeralsohasaninitializationfile.Thistakestheformofweb-worker-main.ts:
[web-worker-main.ts]
import{AppModule}from'./app/app.module';
import{platformWorkerAppDynamic}
from'@angular/platform-webworker-dynamic';
![Page 659: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/659.jpg)
platformWorkerAppDynamic().bootstrapModule(AppModule);
Comparedtoanormalmain.tsfile,thisfileshouldlookdelightfullyfamiliar.Angularprovidesyouwithatotallyseparateplatformmodule,butonethataffordsyouanidenticalAPI,whichyouareusingheretobootstraptheapplicationfromtheyet-to-be-definedAppModule.Definethisnext:
[app/app.module.ts]
import{NgModule}from'@angular/core';
import{WorkerAppModule}from'@angular/platform-webworker';
import{AppModule}from'./app.module';
import{ArticleComponent}from'./article.component';
@NgModule({
imports:[
WorkerAppModule
],
bootstrap:[
ArticleComponent
],
declarations:[
ArticleComponent
]
})
exportclassAppModule{}
SimilartohowyouwouldnormallyuseBrowserModulewithinaconventionaltop-levelappmodule,Angularprovidesaweb-worker-flavoredWorkerAppModulethathandlesallthenecessaryintegration.
![Page 660: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/660.jpg)
Howitworks...Makenomistakeaboutwhatishappeninghere:yourapplicationisnowrunningontwoseparateJavaScriptthreads.
WebworkersarebasicallyjustreallydumbJavaScriptexecutionbuckets.Wheninitialized,theyaregivenaninitialpieceofJavaScripttorun,whichinthisexampletooktheformofloader.js/.Theyhavenounderstandingofwhatisgoingoninthemainbrowserruntime.Theycan'tinteractwiththeDOM,andyouareonlyabletocommunicatewiththemviaPostMessages.AngularbuildsanelegantabstractionontopofPostMessagestocreateabusinterface,anditisthisinterfacethatisusedtojointhetworuntimestogether.
IfyoulookintothePostMessagespecification,youwillnoticethatallofthedatapassedasthemessagemustbeserializedintoastring.HowthencanthisrenderingconfigurationpossiblyworkwiththeDOMinthemainbrowser,handlingeventsanddisplayingtheHTMLandthewebworkerperformingtherenderingonaDOMitcannottouch?
Theanswerissimple:Angularserializeseverything.Whentheinitialrenderingoccurs,oraneventinthebrowseroccurs,Angulargrabseverythingitneedstoknowaboutthecurrentstate,wrapsitupintoaserializedstring,andshipsitofftothewebworkerrendererontheproperchannel.Thewebworkerrendererunderstandswhat'sbeingpassedtoit.AlthoughitcannotaccessthemainDOM,itiscertainlyabletoconstructHTMLelements,understandhowtheserializedeventspassedtoitwillaffectthem,andperformtherendering.
Whenthetimecomestotellthebrowserwhattoactuallyrender,itwillinturnserializetherenderedcomponentandsenditbacktothemainbrowserruntime,whichwillunpackthestringandinsertitintotheDOM.
TotheAngularframework,becauseitisabstractedfromallthebrowserdependenciesthatmightgetinthewayofthis,everythingseemsnormal.Eventscomein,they'rehandled,andarendererservicetellsitwhattoputintotheDOM.EverythingthathappensinbetweenisunimportanttotheAngularframework,whichdoesn'tcarethateverythinghappenedinatotallyseparateJavaScriptruntime.
Note
Notethattheelementsyoubeginwithhavenothingunusualaboutthemtoallowwebworkercompatibility.ThisunderscorestheeleganceofAngular'swebworkerabstraction.
![Page 661: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/661.jpg)
There'smore...Aswebworkersaremorefullysupportedandutilized,patternssuchasthesewillmostlikelybecomemoreandmorecommon,anditisextremelyprescientoftheAngularteamtosupportthisbehavior.Thereare,however,someconsiderations.
Optimizingforperformancegains
Oneoftheprimarybenefitsofusingwebworkersisthatyounowhaveaccesstoanexecutioncontextthatisnotblockedbyanythingrunningonthebrowserexecutioncontext.Forperformancedrags,suchasreflowandblockingofeventloophandlers,thewebworkerwillcontinuewiththeexecutionwithoutacareforwhatishappeningelsewhere.
Therefore,gettingaperformancebenefitbecomesaproblemofoptimization.CommunicationbetweenthetwoJavaScriptthreadsrequiresserializationandtransmissionofevents,whichisobviouslynotasfastashandlingtheminthesamethread.However,inanespeciallycomplicatedapplication,renderingcanquicklybecomeoneofthemostexpensivethingsyourapplicationwilldo.Therefore,youmayneedtoexperimentandmakeajudgmentcallforyourapplication,asnotallapplicationswillseeperformancegainsfromusingwebworkers—onlythosewhererenderingbecomesprohibitivelyexpensive.
Compatibilityconsiderations
Webworkershaveextremelygoodsupport,butthereisstillaverysignificantnumberofbrowsersthatdonotsupportthem.Ifyourapplicationneedstoworkuniversally,webworkersarenotrecommended;sincetheywillnotgracefullydegrade,yourapplicationwillmerelyfail.
![Page 662: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/662.jpg)
SeealsoConfiguringtheAngular2rendererservicetousewebworkersguidesyouthroughthesettingupofyourapplicationtorenderonawebworkerConfiguringapplicationstouseahead-of-timecompilationguidesyouthroughhowtocompileanapplicationduringthebuildConfiguringanapplicationtouselazyloadingshowshowyoucandelayservingchunksofapplicationcodeuntilneeded
![Page 663: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/663.jpg)
Configuringapplicationstouseahead-of-timecompilationAngular2introducestheconceptofahead-of-timecompilation(AOT).Thisisanalternateconfigurationinwhichyoucanrunyourapplicationstomovesomeprocessingtimefrominsidethebrowser(referredtoasjust-in-timecompilationorJIT)towhenyoucompileyourapplicationontheserver.
Note
Thecode,links,andaliveexamplerelatedtothisrecipeareavailableathttp://ngcookbook.herokuapp.com/9253/.
![Page 664: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/664.jpg)
GettingreadyAOTcompilationisapplication-agnostic,soyoushouldbeabletoaddthistoanyexistingAngular2applicationwithminimalmodification.
Forthepurposesofthisexample,supposeyouhaveanexistingAppModuleinsideapp/app.module.ts.Youneedn'tconcernyourselfwithitscontentsinceitisirrelevantforthepurposeofAOT.
![Page 665: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/665.jpg)
Howtodoit...UsingAOTmeansyouwillcompileandbootstrapyourapplicationdifferently.Dependingonhowitisconfigured,youwillprobablywanttousesiblingfileswith"aot"addedtothename.Bearinmindthatthisisonlyfororganizationalpurposes;Angularisnotconcernedwithwhatyounameyourfiles.
InstallingAOTdependencies
Angularrequiresanewcompilationtoolngc,whichisincludedinthe@angular/compiler-clipackage.ItwillalsomakeuseofplatformBrowsertobootstrap,whichisprovidedinsidethe@angular/platform-browserpackage.Installbothoftheseandsavethedependencytopackage.json:
npminstall@angular/compiler-cli@angular/platform-server--save
Configuringngc
ngcwillessentiallyperformasupersetofdutiesofthetsccompilationtoolyouareaccustomedtousing.Itiswisetoprovideitwithaseparateconfigfile,whichcanbenamedtsconfig-aot.json(butyouarenotbeholdentothisname).
Thefileshouldappearidenticaltoyourexistingtsconfig.jsonfilebutwiththefollowingimportantmodifications:
[tsconfig-aot.json]
{
"compilerOptions":{
(lotsofsettingsherealready,onlychange'module')
"module":"es2015",
},
"files":[
"app/app.module.ts",
"main.ts"
],
"angularCompilerOptions":{
"genDir":"aot",
"skipMetadataEmit":true
}
}
AligningcomponentdefinitionswithAOTrequirements
AOTcompilationrequiresyourapplicationtobeorganizedinafewspecificways.Nothingshouldbreakanexistingapplication,butthey'reimportanttodoandtakenoteof.Ifyou'vebeen
![Page 666: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/666.jpg)
followingAngularbestpractices,theyshouldbeabreeze.
First,componentdefinitionsmusthaveamoduleIdspecified.YouwillfindthatcomponentsgeneratedwiththeAngularCLIalreadyhavethisincluded.Ifnot,themoduleIdshouldalwaysbemodule.id,asshownhere:
@Component({
moduleId:module.id,
(lotsofotherstuff)
})
SincethemoduleisundefinedwhencompilinginAOT,youcanprovideadummyvalueintheroottemplate:
<script>window.module='aot';</script>
Tip
AOTdoesn'tneedthemoduleID,butitallowsyoutohaveacompilationwithouterrors.NotethatthesestepsinvolvingmoduleIdmaybechangedoreliminatedentirelyinfutureversionsofAngular,astheyonlyexisttoallowyoutohavecompatibilitybetweenbothJITandAOTcompilations.
Next,you'llneedtochangethepathofthetemplates,bothCSSandHTML,toberelativetothecomponentdefinitionfile,notapplication-root-relative.IfyouareusingtheconventiongivenbytheAngularCLIorthestyleguide,youareprobablyalreadydoingthis.
Compilingwithngc
ngcisn'tavailabledirectlyonthecommandline;you'llneedtorunthebinaryfiledirectly.Additionally,sinceinthisexampleit'snotusingthetsconfig.jsonnamingconvention,you'llneedtopointthebinarytothelocationofthealternateconfigfile.Runthefollowingcommandfromtherootofyourapplicationtoexecutethecompilation:
node_modules/.bin/ngc-ptsconfig-aot.json
ThiscommandwilloutputacollectionofNgFactoryfileswith.ngfactory.tsinsidetheaotdirectory,asyouspecifiedearlierintheangularCompilerOptionssectionoftsconfig-aot.json.
Thecompilationisdone,butyourapplicationdoesn'tyetknowhowtousetheseNgFactoryinstances.
BootstrappingwithAOT
YourapplicationwillnowstartoffwithAppModuleNgFactoryinsteadofAppModule.Bootstrap
![Page 667: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/667.jpg)
itusingplatformBrowser:
[main.ts]
import{platformBrowser}from'@angular/platform-browser';
import{AppModuleNgFactory}from'aot/app/app.module.ngfactory';
platformBrowser().bootstrapModuleFactory(AppModuleNgFactory);
Withthis,you'llbeabletorunyourapplicationnormallybutthistimeusingprecompiledfiles.
![Page 668: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/668.jpg)
Howitworks...CompilinginAngularisacomplexsubject,butthereareseveralmainpointsrelevanttoswitchinganapplicationtoanAOTbuildprocess:
TheAngularcompilerlivesinsidethe@angular/compilermodule,whichisquitelarge.UsingAOTmeansthismodulenolongerneedstobedeliveredtothebrowser,whichyieldssubstantialbandwidthsavings.TheAngularcompilationprocessinvolvestakingthetemplatestringsinsideyourcomponents,whichexistasdefinedbytemplateortemplateUrl,andconvertingthemintoNgFactoryinstances.TheseinstancesspecifyhowAngularunderstandshowyoudefinedthetemplate.TheconversiontoFactoryinstanceshappensbothinJITandAOTcompilation;switchingtoAOTsimplymeansyouaredoingthisprocessingontheserverinsteadoftheclientandservingNgFactorydefinitionsdirectlyinsteadofuncompiledtemplatestrings.AOTwillobviouslyslowdownyourbuildtimesincemorecomputationisrequiredeachtimethecodebaseneedsupdating.SinceyoucantrustthatAngularwillbeabletocorrectlyinterpretNgFactorydefinitionsthattheAOTcompilationgenerates,itisbesttodoJITcompilationwhendevelopingtheapplicationandAOTcompilationwhenbuildinganddeployingproductionapplications.
![Page 669: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/669.jpg)
There'smore...AOTiscool,butitmightnotbeforeverybody.ItwillverylikelyreducetheloadtimeandinitializationtimeofyourAngularapplication,butyourbuildprocessisconsiderablymoreinvolvednow.What'smore,ifyouwanttouseJITindevelopmentbutAOTinproduction,younowhavetomaintaintwoversionsofthreedifferentfiles:index.html,main.ts,andtsconfig.json.Perhapsthisadditionalbuildcomplexityoverheadisworthit,butitshouldcertainlybeajudgmentcallbasedonyourdevelopmentsituation.
GoingfurtherwithTreeShaking
AngularalsosupportsaseparatestepofoptimizationcalledTreeShaking.Thisusesaseparatenpmlibrarycalledrollup.Essentially,thislibraryreadsinyourentireapplication(asJSfiles),figuresoutwhatmodulesarenotbeingused,andcutsthemoutofthecompiledcodebaseandthereforeofthepayloadthatisdeliveredtothebrowser.Thisisanalogoustoshakingatreetomakethedeadbranchesfallout,hence"treeshaking."
Note
Thees2015moduleconfigurationspecifiedearlierintsconfig-aot.jsonwasforsupportingtherolluplibrary,asitisarequirementforcompatibility.
Ifyourapplicationcodebaseiswell-maintained,youwillmostlikelyseelimitedbenefitsoftreeshakingsinceunusedimportsandthelikewillbecaughtwhencoding.What'smore,onecouldmaketheargumentthattreeshakingmightbeananti-patternsinceitsubtlyencouragesaliberaluseofmoduleinclusionbythedeveloperwiththeknowledgethattreeshakingwilldothedirtyworkingofcuttingoutanyunusedmodules.Thismaythenleadtoaclutteredcodebase.
UsingtreeshakingcanbeausefultoolwhenitcomestoAngular2applications,butitsusefulnessisinmanywaysevaporatedbykeepingacodebasetidy.
![Page 670: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/670.jpg)
SeealsoUnderstandingandproperlyutilizingenableProdModewithpureandimpurepipesdescribeshowtotakethetrainingwheelsoffyourapplicationConfiguringtheAngular2rendererservicetousewebworkersguidesyouthroughtheprocessofsettingupyourapplicationtorenderonawebworkerConfiguringanapplicationtouselazyloadingshowshowyoucandelayservingchunksofapplicationcodeuntilneeded
![Page 671: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/671.jpg)
ConfiguringanapplicationtouselazyloadingLazyloadedapplicationsarethosethatdefertheretrievalofrelevantresourcesuntiltheyareactuallynecessary.Onceapplicationsbegintoscale,thiscanyieldmeaningfulgainsinperformance,andAngular2supportslazyloadingrightoutofthebox.
Note
Thecode,links,andaliveexamplerelatedtothisrecipeareavailableathttp://ngcookbook.herokuapp.com/0279/.
![Page 672: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/672.jpg)
GettingreadySupposeyoubeginwiththefollowingsimpleapplication:
[app/root.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'root',
template:`
<h1>Rootcomponent</h1>
<router-outlet></router-outlet>
`
})
exportclassRootComponent{}
[app/link.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'app-link',
template:`
<arouterLink="/article">article</a>
`
})
exportclassLinkComponent{}
[app/article.component.ts]
import{Component}from'@angular/core';
@Component({
selector:'article',
template:`
<h1>{{title}}</h1>
`
})
exportclassArticleComponent{
title:string=
'Baboon'sStockPicksCrushTop-PerformingHedgeFund';
}
[app/app.module.ts]
import{NgModule}from'@angular/core';
import{BrowserModule}from'@angular/platform-browser';
import{RouterModule,Routes}from'@angular/router';
import{RootComponent}from'./root.component';
import{ArticleComponent}from'./article.component';
import{LinkComponent}from'./link.component';
constappRoutes:Routes=[
{
![Page 673: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/673.jpg)
path:'article',
component:ArticleComponent
},
{
path:'**',
component:LinkComponent
}
];
@NgModule({
imports:[
BrowserModule,
RouterModule.forRoot(appRoutes)
],
declarations:[
ArticleComponent,
LinkComponent,
RootComponent
],
bootstrap:[
RootComponent
]
})
exportclassAppModule{}
Thisapplicationhasonlytworoutes:thedefaultroute,whichdisplaysalinktothearticlepage,andthearticleroute,whichdisplayArticleComponent.Yourobjectiveistodefertheloadingoftheresourcesrequiredbythearticlerouteuntilitisactuallyvisited.
![Page 674: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/674.jpg)
Howtodoit...LazyloadingmeanstheinitialapplicationmodulethatisloadedcannothaveanydependenciesonthemodulethatyouwishtolazilyloadsincenoneofthatcodewillbepresentbeforethenewURLisvisited.First,movetheArticleComponentreferencetoitsownmodule:
[app/article.module.ts]
import{NgModule}from'@angular/core';
import{ArticleComponent}from'./article.component';
@NgModule({
declarations:[
ArticleComponent
],
exports:[
ArticleComponent
]
})
exportclassArticleModule{}
Removingalldependenciesmeansmovingtherelevantroutedefinitionstothismoduleaswell:
[app/article.module.ts]
import{NgModule}from'@angular/core';
import{ArticleComponent}from'./article.component';
import{Routes,RouterModule}from'@angular/router';
constarticleRoutes:Routes=[
{
path:'',
component:ArticleComponent
}
];
@NgModule({
imports:[
RouterModule.forChild(articleRoutes)
],
declarations:[
ArticleComponent
],
exports:[
ArticleComponent
]
})
exportclassArticleModule{}
Next,removeallthesemodulereferencesfromAppModule.Inaddition,modifytheroutedefinition,sothatinsteadofspecifyingacomponenttorender,itsimplyreferencesapathtothelazilyloadedmodule,aswellasthenameofthemoduleusingaspecial#syntax:
![Page 675: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/675.jpg)
[app/app.module.ts]
import{NgModule}from'@angular/core';
import{BrowserModule}from'@angular/platform-browser';
import{RouterModule,Routes}from'@angular/router';
import{RootComponent}from'./root.component';
import{LinkComponent}from'./link.component';
constappRoutes:Routes=[
{
path:'article',
loadChildren:'./app/article.module#ArticleModule'
},
{
path:'**',
component:LinkComponent
}
];
@NgModule({
imports:[
BrowserModule,
RouterModule.forRoot(appRoutes)
],
declarations:[
LinkComponent,
RootComponent
],
bootstrap:[
RootComponent
]
})
exportclassAppModule{}
Thisisallthat'srequiredtosetuplazyloading.Yourapplicationshouldbehaveidenticallytowhenyoubeganthisrecipe.
![Page 676: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/676.jpg)
Howitworks...Toverifythatitisinfactperformingalazyload,starttheapplicationandkeepaneyeontheNetworktabofyourbrowser'sdeveloperconsole.
WhenyouclickonthearticlerouterLink,youshouldseearticle.module.tsandarticle.component.tsrequestsgooutbeforetherenderingoccurs.ThismeansAngularisonlyfetchingtherequiredfilesonceyouactuallyvisittheroute.TheloadChildrenroutepropertytellsAngularthatwhenitvisitsthisroute,ifithasn'tloadedthemodulealready,itshouldusetherelativepathyoumayhaveprovidedtofetchthemodule.Oncethemodulefileisretrieved,Angularisabletoparseitandknowwhichotherfilesitneedstorequesttoloadallthemodule'sdependencies.
![Page 677: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/677.jpg)
There'smore...You'llnotethatthisintroducesabitofadditionallatencytoyourapplicationsinceAngularwaitstoloadtheresourcesrightwhenitactuallyneedsthem.What'smore,inthisexample,ithastoactuallyperformtwoadditionalroundtripstotheserverwhenitvisitsthearticleURL:onetorequestthemodulefileandonetorequestthemodule'sdependencies.
Inaproductionenvironment,thislatencymightbeunacceptable.Aworkaroundmightbetocompilethelazilyloadedpayloadintoasinglefilethatcanbefetchedwithonerequest.Dependingonhowyourapplicationisbuilt,yourmileagemayvary.
Accountingforsharedmodules
Thelazilyloadedmoduleistotallyseparatedfromyourmainapplicationmodule,andthisincludesinjectables.Ifaserviceisprovidedtothetop-levelapplicationmodule,youwillfindthatitwillcreatetwoseparateinstancesofthatserviceforeachplaceitisprovided—certainlyunexpectedbehavior,giventhatanapplicationloadednormallywillonlycreateoneinstanceifitisonlyprovidedonce.
ThesolutionistopiggybackontheforRootmethodthatAngularusestosimultaneouslyprovideandconfigureservices.Morerelevanttothisrecipe,itallowsyoutotechnicallyprovideaserviceatmultiplelocations;however,Angularwillknowhowtoignoreduplicatesofthis,provideditisdoneinsideforRoot().
First,definetheAuthServicethatyouwishtocreateonlyasingleinstanceof:
[app/auth.service.ts]
import{Injectable}from'@angular/core';
@Injectable()
exportclassAuthService{
constructor(){
console.log('instantiatedAuthService');
}
}
Thisincludesalogstatementsoyoucanseethatonlyoneinstantiationoccurs.
Next,createanNgModulewrapperspeciallyforthisservice:
[app/auth.module.ts]
import{NgModule,ModuleWithProviders}from"@angular/core";
import{AuthService}from"./auth.service";
@NgModule({})
![Page 678: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/678.jpg)
exportclassAuthModule{
staticforRoot():ModuleWithProviders{
return{
ngModule:AuthModule,
providers:[
AuthService
]
};
}
}
SincethisutilizestheforRoot()strategyasdetailedintheprecedingcode,you'refreetoimportthismodulebothinsidetheapplicationmoduleaswellasthelazilyloadedmodule:
[app/app.module.ts]
import{NgModule}from'@angular/core';
import{BrowserModule}from'@angular/platform-browser';
import{RouterModule,Routes}from'@angular/router';
import{RootComponent}from'./root.component';
import{LinkComponent}from'./link.component';
import{AuthModule}from'./auth.module';
constappRoutes:Routes=[
{
path:'article',
loadChildren:'./app/article.module#ArticleModule'
},
{
path:'**',
component:LinkComponent
}
];
@NgModule({
imports:[
BrowserModule,
RouterModule.forRoot(appRoutes),
AuthModule.forRoot()
],
declarations:[
LinkComponent,
RootComponent
],
bootstrap:[
RootComponent
]
})
exportclassAppModule{}
You'lladdittothelazilyloadedmoduletoo,butdon'tinvokeforRoot().Thismethodisreservedforonlytherootapplicationmodule:
![Page 679: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/679.jpg)
[app/article.module.ts]
import{NgModule}from'@angular/core';
import{ArticleComponent}from'./article.component';
import{Routes,RouterModule}from'@angular/router';
import{AuthModule}from'./auth.module';
constarticleRoutes:Routes=[
{
path:'',
component:ArticleComponent
}
];
@NgModule({
imports:[
RouterModule.forChild(articleRoutes),
AuthModule
],
declarations:[
ArticleComponent
],
exports:[
ArticleComponent
]
})
exportclassArticleModule{}
Finally,injecttheserviceintoRootComponentandArticleComponentanduselogstatementstoseethatitdoesindeedreachboththecomponents:
[app/root.component.ts]
import{Component}from'@angular/core';
import{AuthService}from'./auth.service';
@Component({
selector:'root',
template:`
<h1>Rootcomponent</h1>
<router-outlet></router-outlet>
`
})
exportclassRootComponent{
constructor(privateauthService_:AuthService){
console.log(authService_);
}
}
[app/article.component.ts]
import{Component}from'@angular/core';
import{AuthService}from'./auth.service';
![Page 680: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/680.jpg)
@Component({
selector:'article',
template:`
<h1>{{title}}</h1>
`
})
exportclassArticleComponent{
title:string=
'Baboon'sStockPicksCrushTop-PerformingHedgeFund';
constructor(privateauthService_:AuthService){
console.log(authService_);
}
}
Youshouldseeasingleserviceinstantiationandsuccessfulinjectionintoboththecomponents.
![Page 681: Angular 2 Cookbook - Programmer BooksDowngrading Angular 2 components to Angular 1 directives with downgradeComponent Getting ready How to do it... How it works... See also Downgrade](https://reader030.vdocument.in/reader030/viewer/2022041020/5ecee08a306e27216e5336e9/html5/thumbnails/681.jpg)
SeealsoConfiguringtheAngular2rendererservicetousewebworkersguidesyouthroughtheprocessofsettingupyourapplicationtorenderonawebworkerConfiguringapplicationstouseahead-of-timecompilationguidesyouthroughhowtocompileanapplicationduringthebuild