325
www.allitebooks.com

Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

  • Upload
    tranbao

  • View
    251

  • Download
    4

Embed Size (px)

Citation preview

Page 1: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

www.allitebooks.com

Page 2: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

www.allitebooks.com

Page 3: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

LearningAngularJSKenWilliamson

www.allitebooks.com

Page 4: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

www.allitebooks.com

Page 5: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

LearningAngularJS

byKenWilliamson

Copyright©2015KenWilliamson.Allrightsreserved.

PrintedintheUnitedStatesofAmerica.

PublishedbyO’ReillyMedia,Inc.,1005GravensteinHighwayNorth,Sebastopol,CA95472.

O’Reillybooksmaybepurchasedforeducational,business,orsalespromotionaluse.Onlineeditionsarealsoavailableformosttitles(http://safaribooksonline.com).Formoreinformation,contactourcorporate/institutionalsalesdepartment:[email protected].

Editor:MegFoley

ProductionEditor:NicoleShelby

Copyeditor:RachelHead

Proofreader:RachelMonaghan

Indexer:WordCo.IndexingServices

InteriorDesigner:DavidFutato

CoverDesigner:EllieVolckhausen

Illustrator:RebeccaDemarest

March2015:FirstEdition

www.allitebooks.com

Page 6: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

RevisionHistoryfortheFirstEdition2015-03-10:FirstRelease

Seehttp://oreilly.com/catalog/errata.csp?isbn=9781491916759forreleasedetails.

Whilethepublisherandtheauthorhaveusedgoodfaitheffortstoensurethattheinformationandinstructionscontainedinthisworkareaccurate,thepublisherandtheauthordisclaimallresponsibilityforerrorsoromissions,includingwithoutlimitationresponsibilityfordamagesresultingfromtheuseoforrelianceonthiswork.Useoftheinformationandinstructionscontainedinthisworkisatyourownrisk.Ifanycodesamplesorothertechnologythisworkcontainsordescribesissubjecttoopensourcelicensesortheintellectualpropertyrightsofothers,itisyourresponsibilitytoensurethatyourusethereofcomplieswithsuchlicensesand/orrights.

978-1-491-91675-9

[LSI]

www.allitebooks.com

Page 7: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

IwouldliketothankmysonChrisforallhishelpandforbeingasoundingboard.Thanks,Chris.

www.allitebooks.com

Page 8: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

www.allitebooks.com

Page 9: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

Preface

Theworldofsoftwaredevelopmenthaschangeddrasticallyoverthelastfewdecades.Manysoftwaremethodologiesandconceptsthatwereconsidered“cuttingedge”20orsoyearsagoarenowcommonpracticeinthefieldofsoftwaredevelopment,andhavebeenforyears.OneexampleistheWorldWideWebandtheuseofwebbrowserstodeliversoftwaretousers.In1993,theconceptofdeliveringsoftwareovertheInternetthatcouldthenruninawebbrowseronanymachinerunningonanyoperatingsystemwasconsideredbleedingedge.Butasanycomputeruserknows,thatpracticehasbeencommonplaceforyearsnow.

WhenJavaScriptclient-sidewebapplicationframeworkslikeAngularJS,Backbone.js,andEmber.jsfirstappeared,theywereconsideredtoocuttingedgeformostserioussoftwareprojects.Astheymatured,however,softwarearchitectsanddeveloperssawgreatpotentialintheseframeworks.ApplicationsbuiltwithJavaScriptclient-sideframeworksexistandrunentirelyontheuser’shardware,muchlikeconventionalthick-clientapplications.Applicationswrittenusingtheseframeworksaremuchfasterthanconventionalwebapplicationsandprovideamuchbetteruserexperience.

Overthelastcoupleofyears,JavaScriptclient-sideframeworkshavemadegreatstridesinfunctionalityandreliability,andtheyarenowheavilyusedtobuildmobileHTML5applications.Butmobileapplicationsareonlythestartingpoint.Theseframeworksnowhavethepotentialtoradicallychangethewaywebuildmodernwebapplicationsoftware.OfalltheJavaScriptframeworksavailable,AngularJS,backedbyGoogle,istheonethatshinesthebrightest.

AngularJShasmanyadvantagesoverotherJavaScriptclient-sideframeworks.AngularJSusestheMVCdesignpatternandembracesthatpatterncompletely.Themodel,view,andcontrollerareallclearlydefinedinAngularJSandservetogreatlysimplifythedevelopmentprocess.WithAngularJS,developerscanbuildapplicationsthathaveaclearseparationbetweentheirfunctionallayers.

OneofthegreatestadvantagesofAngularJSoverotherJavaScriptclient-sideframeworksistheuniquewayinwhichitletsdevelopersinteractwithRESTfulwebservices.AngularJS’sresourceobjectletsdevelopersinteractwithRESTserviceslikestandardobjects.ThecomplexityofRESTservicescanbegreatlysimplifiedusingthisapproach:withonlyafewlinesofcode,youcancreateanAngularJSservicethatinteractswithmultiplebackendRESTservices.Thoseservicescanthenbeusedthroughoutyourapplication,reducingthetotalnumberoflinesofcode.

Infact,oneofthebiggestadvantagesofAngularJSoverotherclient-sideframeworksisitsconceptofservices.AngularJSserviceshelptogreatlysimplifyanapplicationbycompartmentalizingclient-sidelogicintosingleunitsofcode.Thosesingleunits,called

www.allitebooks.com

Page 10: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

services,canthenbeusedrepeatedlythroughoutanapplication.AngularJSservicesproveespeciallypowerfulwhenyou’rebuildinglargeenterpriseapplicationswithmanylinesofcodeandmuchcomplexity.ComplexlogiccanbewrittenonlyonceinsideanAngularJSserviceandthenusedwhereverneeded.ThatalonemakesAngularJSthebestchoiceforyournextJavaScriptproject.

Thankstothisuseofservicesanditsall-inclusivedesign,AngularJShelpsdeveloperswritelesscode,therebygreatlyreducingapplicationcomplexity.ThesimplicityofAngularJSmakesiteasytolearnandeasytouse.AnytimespentlearningAngularJSistimewellspent.AnytimespentdevelopingAngularJSapplicationsistimespentturningacutting-edgetechnologyintoacommonplacetechnology.InthisbookIstrivetohelpyoudoboth,encouragingdesignconceptsandpracticesthatwillhelpyoubuildbetterAngularJSapplications.

www.allitebooks.com

Page 11: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

WhyIWroteThisBookIconstantlyseedevelopmentteamsavoidusingAngularJSbecauseofitsperceivedsteeplearningcurve.ThosesameteamsoftenchooseotherJavaScriptframeworksbecausetheyinitiallyseemeasiertolearn.ButAngularJSisnothardtolearnatall.ItisactuallymucheasiertolearnthanotherJavaScriptframeworks,ifthelearningprocessisapproachedcorrectly.Likemanyothers,IstruggledtolearnAngularJSinthebeginning.ThisbookwaswrittentohelpdevelopersavoidtheearlystrugglesassociatedwithlearningAngularJSandgetstartedbuildingAngularJSapplicationsandwebsitesveryquickly.

Page 12: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

WhatThisBookCoversThisbookcoverseverythingyouneedtoknowtobuildfullyfunctionalAngularJSapplications.ThebookstartsoffwiththebasicsofAngularJS.YouwilllearnaboutAngularJScomponentsinearlychapters.Aschaptersprogress,youwillgethands-onexperiencebuildingworkingAngularJSprojects.

Neartheendofthebook,youwillwritetheAngularJSpartofaworkingMEANstackblogapplicationanddeploytheapplicationtothecloud.MEANstandsforMongoDB,ExpressJS,AngularJS,andNode.js.ManyindustryexpertsbelievetheMEANstackwillbeadominantwebdevelopmentplatformincomingyears.

Afterreadingthisbook,youwillhavetheknowledgetostartbuildinghigh-qualityAngularJSapplicationsandwebsites.YouwillalsogainaclearunderstandingofthedesignconceptsassociatedwithAngularJSapplications,andofsecurityasitrelatestoAngularJSapplications.

Page 13: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

WhoShouldReadThisBookThisbookisintendedforanyonewhohasaninterestinlearningtodevelopAngularJSapplicationsorwebsitesquickly.ItwillalsobehelpfultoanyoneinterestedinlearninghowAngularJSisusedinaMEANstackapplication.ThereaderwillgainnotonlyaconceptualunderstandingofAngularJS,buthands-onexperienceaswell.AnyonereadingthisbookshouldhavesomeknowledgeofJavaScript,softwaredesignconcepts,andsoftwaredesignpatterns.Apriorknowledgeofwebdevelopmentwillalsobehelpful.

Page 14: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

TheChaptersinThisBookThisbookstartsoffwiththeverybasicsofAngularJSandassumesthereaderhasnopriorknowledgeoftheframework.Thechaptersarearrangedasfollows:

Chapter1,IntroductiontoAngularJS,startsoffwithabasicintroductiontoAngularJS.IthelpsthereaderunderstandhowAngularJSdiffersfromotherframeworks.

Chapter2,TheIDEandAngularJSProjects,coverssettingupadevelopmentenvironmentandbuildingAngularJSprojects.ItalsocovershowtosetupatestenvironmentforAngularJS.

Chapter3,MVCandAngularJS,comparesAngularJStotraditionalwebMVCframeworks.ItshowswhyAngularJSisabetterframeworkforbuildingmodernwebapplicationsandwebsites.

Chapter4,AngularJSControllers,isadiscussionofAngularJScontrollers.Thereaderwillbuildpartofaworkingapplicationandimplementcontrollertestingneartheendofthechapter.

Chapter5,AngularJSViewsandBootstrap,coversAngularJSviewsbuiltwithTwitterBootstrap.Thereaderwillcontinueworkingonafunctionalapplicationandimplementtesting.

Chapter6,AngularJSandRESTServices,coversAngularJSservicesastheyrelatetoRESTservices.ThereaderwillcontinueworkingontheapplicationandconnectittopublicRESTserviceswrittenforthisbook.

Chapter7,AngularJSModels,coversAngularJSmodelsandhowtheyrelatetocontrollersandviews.Thereaderwillcontinueworkingontheapplicationstartedearlier.

Chapter8,ServicesandBusinessLogic,coversnon-RESTAngularJSservices.Inthischapterthereaderwillbuildpartofthesecuritylayerusedlaterinthebook.

Chapter9,AngularJSDirectives,coversthebasicsofbuildingandtestingAngularJSdirectives.

Chapter10,AngularJSSecurity,showsthereaderhowtousethesecuritylayerintroducedinChapter8tosecuretheAngularJSapplicationstartedearlier.

Chapter11,MEANCloudandMobile,showsthereaderhowtousetheAngularJSapplicationdevelopedinpreviouschaptersinaMEANstackapplicationandinamobileapplication.

Chapter12,AngularJSandSEO,coverssearchengineoptimizationasitrelatestoAngularJSapplicationsandwebsites.

Page 15: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

ConventionsUsedinThisBookThefollowingtypographicalconventionsareusedinthisbook:

Italic

Indicatesnewterms,URLs,emailaddresses,filenames,andfileextensions.Constantwidth

Usedforprogramlistings,aswellaswithinparagraphstorefertoprogramelementssuchasvariableorfunctionnames,databases,datatypes,environmentvariables,statements,andkeywords.

Constantwidthbold

Showscommandsorothertextthatshouldbetypedliterallybytheuser.Constantwidthitalic

Showstextthatshouldbereplacedwithuser-suppliedvaluesorbyvaluesdeterminedbycontext.

NOTEThiselementsignifiesageneralnote.

WARNINGThiselementsignifiesawarningorcaution.

Page 16: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

UsingCodeExamplesSupplementalmaterial(codeexamples,exercises,etc.)isavailablefordownloadathttps://github.com/KenWilliamson.

Thisbookisheretohelpyougetyourjobdone.Ingeneral,ifexamplecodeisofferedwiththisbook,youmayuseitinyourprogramsanddocumentation.Youdonotneedtocontactusforpermissionunlessyou’rereproducingasignificantportionofthecode.Forexample,writingaprogramthatusesseveralchunksofcodefromthisbookdoesnotrequirepermission.SellingordistributingaCD-ROMofexamplesfromO’Reillybooksdoesrequirepermission.Answeringaquestionbycitingthisbookandquotingexamplecodedoesnotrequirepermission.Incorporatingasignificantamountofexamplecodefromthisbookintoyourproduct’sdocumentationdoesrequirepermission.

Weappreciate,butdonotrequire,attribution.Anattributionusuallyincludesthetitle,author,publisher,andISBN.Forexample:“LearningAngularJSbyKenWilliamson(O’Reilly).Copyright2015KenWilliamson,978-1-491-91675-9.”

Ifyoufeelyouruseofcodeexamplesfallsoutsidefairuseorthepermissiongivenabove,[email protected].

Page 17: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

Safari®BooksOnlineSafariBooksOnlineisanon-demanddigitallibrarythatdeliversexpertcontentinbothbookandvideoformfromtheworld’sleadingauthorsintechnologyandbusiness.

Technologyprofessionals,softwaredevelopers,webdesigners,andbusinessandcreativeprofessionalsuseSafariBooksOnlineastheirprimaryresourceforresearch,problemsolving,learning,andcertificationtraining.

SafariBooksOnlineoffersarangeofplansandpricingforenterprise,government,education,andindividuals.

Membershaveaccesstothousandsofbooks,trainingvideos,andprepublicationmanuscriptsinonefullysearchabledatabasefrompublisherslikeO’ReillyMedia,PrenticeHallProfessional,Addison-WesleyProfessional,MicrosoftPress,Sams,Que,PeachpitPress,FocalPress,CiscoPress,JohnWiley&Sons,Syngress,MorganKaufmann,IBMRedbooks,Packt,AdobePress,FTPress,Apress,Manning,NewRiders,McGraw-Hill,Jones&Bartlett,CourseTechnology,andhundredsmore.FormoreinformationaboutSafariBooksOnline,pleasevisitusonline.

Page 18: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

HowtoContactUsPleaseaddresscommentsandquestionsconcerningthisbooktothepublisher:

O’ReillyMedia,Inc.

1005GravensteinHighwayNorth

Sebastopol,CA95472

800-998-9938(intheUnitedStatesorCanada)

707-829-0515(internationalorlocal)

707-829-0104(fax)

Wehaveawebpageforthisbook,wherewelisterrata,examples,andanyadditionalinformation.Youcanaccessthispageathttp://bit.ly/learning-angularjs.

Tocommentorasktechnicalquestionsaboutthisbook,[email protected].

Formoreinformationaboutourbooks,courses,conferences,andnews,seeourwebsiteathttp://www.oreilly.com.

FindusonFacebook:http://facebook.com/oreilly

FollowusonTwitter:http://twitter.com/oreillymedia

WatchusonYouTube:http://www.youtube.com/oreillymedia

Page 19: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development
Page 20: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

Chapter1.IntroductiontoAngularJS

Google’sAngularJSisanall-inclusiveJavaScriptmodel-view-controller(MVC)frameworkthatmakesitveryeasytoquicklybuildapplicationsthatrunwellonanydesktopormobileplatform.Inaveryshortperiodoftime,AngularJShasmovedfrombeinganunknownopensourceofferingtooneofthebestknownandmostwidelyusedJavaScriptclient-sideframeworksoffered.AngularJS1.3andgreatercombinedwithjQueryandTwitterBootstrapgiveyoueverythingyouneedtorapidlybuildHTML5JavaScriptapplicationfrontendsthatuseRESTwebservicesforthebackendprocesses.ThisbookwillshowyouhowtouseallthreefrontendcomponentstoharnessthepowerofRESTservicesonthebackendandquicklybuildpowerfulmobileanddesktopapplications.

www.allitebooks.com

Page 21: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

JavaScriptClient-SideFrameworksJavaScriptclient-sideapplicationsrunontheuser’sdeviceorPC,andthereforeshifttheworkloadtotheuser’shardwareandawayfromtheserver.Untilfairlyrecently,server-sidewebMVCframeworkslikeStruts,SpringMVC,andASP.NETweretheframeworksofchoiceformostweb-basedsoftwaredevelopmentprojects.JavaScriptclient-sideframeworks,however,aresustainablemodelsthatoffermanyadvantagesoverconventionalwebframeworks,suchassimplicity,rapiddevelopment,speedofoperation,testability,andtheabilitytopackagetheentireapplicationanddeployittoallmobiledevicesandtheWebwithrelativeease.Youcanbuildyourapplicationonetimeanddeployandrunitanywhere,onanyplatform,withnomodifications.That’spowerful.

AngularJSmakesthatprocessevenfasterandeasier.Ithelpsyoubuildfrontendapplicationsindaysratherthanmonthsandhascompletesupportforunittestingtohelpreducequalityassurance(QA)time.AngularJShasarichsetofuserdocumentationandgreatcommunitysupporttohelpanswerquestionsduringyourdevelopmentprocess.ModelsandviewsinAngularJSaremuchsimplerthanwhatyoufindinmostJavaScriptclient-sideframeworks.Controllers,oftenmissinginotherJavaScriptclient-sideframeworks,arekeyfunctionalcomponentsinAngularJS.

Figure1-1showsadiagramofanAngularJSapplicationandallrelatedMVCcomponents.OncetheAngularJSapplicationislaunched,themodel,view,controller,andallHTMLdocumentsareloadedontheuser’smobileordesktopdeviceandrunentirelyontheuser’shardware.Asyoucansee,callsaremadetothebackendRESTservices,whereallbusinesslogicandbusinessprocessesarelocated.ThebackendRESTservicescanbelocatedonaprivatewebserverorinthecloud(whichismostoftenthecase).CloudRESTservicescanscalefromahandfulofuserstomillionsofuserswithrelativeease.

Figure1-1.DiagramofanAngularJSMVCapplication

Page 22: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

Single-PageApplicationsAngularJSismostoftenusedtobuildapplicationsthatconformtothesingle-pageapplication(SPA)concept.SPAsareapplicationsthathaveoneentrypointHTMLpage;alltheapplicationcontentisdynamicallyaddedtoandremovedfromthatonepage.YoucanseetheentrypointofourSPAintheindex.htmlcodethatfollows.Thetag<divng-view></div>iswherealldynamiccontentisinsertedintoindex.html:

<!--chapter1/index.html-->

<!DOCTYPEhtml>

<htmllang="en"ng-app="helloWorldApp">

<head>

<title>AngularJSHelloWorld</title>

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

<metahttp-equiv="Content-Type"content="text/html;charset=UTF-8">

<scriptsrc="js/libs/angular.min.js"></script>

<scriptsrc="js/libs/angular-route.min.js"></script>

<scriptsrc="js/libs/angular-resource.min.js"></script>

<scriptsrc="js/libs/angular-cookies.min.js"></script>

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

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

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

</head>

<body>

<divng-view></div>

</body>

</html>

Astheuserclicksonlinksintheapplication,existingcontentattachedtothetagisremovedandnewdynamiccontentisthenattachedtothesametag.Ratherthantheuserwaitingforanewpagetoload,newcontentisdynamicallydisplayedinafractionofthetimethatitwouldtaketoloadanewHTMLwebpage.

TIPYoucandownloadthesourcefortheChapter1“Hello,World”applicationfromGitHub.

Page 23: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

BootstrappingtheApplicationBootstrappingAngularJSistheprocessofloadingAngularJSwhenanapplicationfirststarts.LoadingtheAngularJSlibrariesinapagewillstartthebootstrapprocess.Theindex.htmlfileisanalyzed,andtheparserlooksfortheng-apptag.Theline<htmllang="en"ng-app="helloWorldApp"></html>showshowng-appisdefined.ThefollowingcodeshowstheJavaScriptthatisfiredbythatlineintheindex.htmlfile.Asyoucansee,app.jsiswheretheAngularJSapplicationhelloWorldAppisdefinedasanAngularJSmodule,andthisistheentrypointintotheapplication.ThevariablehelloWorldAppinthisfilecouldbenamedanything.Iwill,however,callithelloWorldAppforthesakeofuniformity:

/*chapter1/app.jsexcerpt*/

'usestrict';

/*AppModule*/

varhelloWorldApp=angular.module('helloWorldApp',[

'ngRoute',

'helloWorldControllers'

]);

Page 24: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

DependencyInjectionDependencyinjection(DI)isadesignpatternwheredependenciesaredefinedinanapplicationaspartoftheconfiguration.Dependencyinjectionhelpsyouavoidhavingtomanuallycreateapplicationdependencies.AngularJSusesdependencyinjectiontoloadmoduledependencieswhenanapplicationfirststarts.Theapp.jscodeintheprevioussectionshowshowAngularJSdependenciesaredefined.

Asyoucansee,twodependenciesaredefinedasneededbythehelloWorldAppapplicationatstartup.Thedependenciesaredefinedinanarrayinthemoduledefinition.ThefirstdependencyistheAngularJSngRoutemodule,whichprovidesroutingtotheapplication.Theseconddependencyisourcontrollermodule,helloWorldControllers.Wewillcovercontrollersindepthlater,butfornowjustunderstandthatcontrollersareneededbyourapplicationsatstartuptime.

Dependencyinjectionisnotanewconcept.Itwasintroducedover10yearsagoandhasbeenusedconsistentlyinvariousapplicationframeworks;DIwasatthecoreofthepopularSpringframeworkwritteninJava.Oneofitsmainadvantagesisthatitreducestheneedforboilerplatecode,writingofwhichwouldnormallybeatime-consumingprocessforadevelopmentteam.

Dependencyinjectionalsohelpstomakeanapplicationmoretestable.ThatisoneofthemainadvantagesofusingAngularJStobuildJavaScriptapplications.AngularJSapplicationsaremucheasiertotestthanapplicationswrittenwithmostJavaScriptframeworks.Infact,thereisatestframeworkthathasbeenspecificallywrittentomaketestingAngularJSapplicationseasy.Wewilltalkmoreabouttestingattheendofthischapter.

Page 25: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

AngularJSRoutesAngularJSroutesaredefinedthroughthe$routeProviderAPI.RoutesaredependentonthengRoutemodule,andthat’swhyitisarequirementwhentheapplicationstarts.Thefollowingcodefromapp.jsshowshowwedefineroutesinanAngularJSapplication.Tworoutesaredefined—thefirstis/andthesecondis/show:

/*chapter1/app.jsexcerpt*/

helloWorldApp.config(['$routeProvider','$locationProvider',

function($routeProvider,$locationProvider){

$routeProvider.

when('/',{

templateUrl:'partials/main.html',

controller:'MainCtrl'}).

when('/show',{

templateUrl:'partials/show.html',

controller:'ShowCtrl'

});

ThetwodefinedroutesmapdirectlytoURLsdefinedintheapplication.Ifauserclicksonalinkintheapplicationspecifiedaswww.someDomainName/show,the/showroutewillbefollowedandthecontentassociatedwiththatURLwillbedisplayed.Iftheuserclicksonalinkspecifiedaswww.someDomainName/,the/routewillbefollowedandthatcontentwillbedisplayed.

Page 26: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

HTML5ModeThecompleteapp.jsfileisshownnext.Thelastlineinapp.js($locationProvider.html5Mode(false).hashPrefix('!');)usesthelocationProviderservice.ThislineofcodeturnsofftheHTML5modeandturnsonthehashbangmodeofAngularJS.IfyouweretoturnonHTML5modeinsteadbypassingtrue,theapplicationwouldusetheHTML5HistoryAPI.HTML5modealsogivestheapplicationprettyURLslike/someAppName/blogPost/5insteadofthestandardAngularJSURLslike/someAppName/#!/blogPost/5thatusethe#!,knownasthehashbang.

/*chapter1/app.jscompletefile*/

'usestrict';

/*AppModule*/

varhelloWorldApp=angular.module('helloWorldApp',[

'ngRoute',

'helloWorldControllers'

]);

helloWorldApp.config(['$routeProvider','$locationProvider',

function($routeProvider,$locationProvider){

$routeProvider.

when('/',{

templateUrl:'partials/main.html',

controller:'MainCtrl'

}).when('/show',{

templateUrl:'partials/show.html',

controller:'ShowCtrl'

});

$locationProvider.html5Mode(false).hashPrefix('!');

}]);

HTML5modecanprovideprettyURLs,butitdoesrequireconfigurationchangesonthewebserverinmostcases.Thechangesaredifferentforeachindividualwebserver,andcandifferfordifferentserverinstallationsaswell.HTML5modealsohandlesURLchangesinadifferentway,byusingtheHTMLHistoryAPIfornavigation.

UsingHTML5modeisjustaconfigurationchangeinAngularJS,andwewon’tcovertheneededserverchangesinthisbookasourfocusisonAngularJS.TheAngularJSsitehasdocumentationonthechangesneededforallmodernwebserverswhenHTML5modeisenabled.Usingthismodehassomebenefits,butwewillstickwithhashbangmodeinourchapterexercises.

Hashbangmodeisusedtosupportconventionalsearchenginesthatdon’thavetheabilitytoexecuteJavaScriptonAjaxsiteslikethosebuiltwithAngularJS.WhenaconventionalsearchenginesearchesasitebuiltwithAngularJSthatuseshashbangs,thesearchenginereplacesthe#!with?_escaped_fragment_=.ConventionalsearchenginesexpecttheservertohaveHTMLsnapshotsatthelocationwhere_escaped_fragment_=isconfiguredtopoint.HTMLsnapshotsaremerelycopiesoftheHTMLrenderedversionofthewebsiteorapplication.

Page 27: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

ModernSearchEnginesFortunately,modernsearchengineshavetheabilitytoexecuteJavaScript,asannouncedbyGoogleinanewsreleaseonMay23,2014.HashbangmodealsoallowsAngularJSapplicationstostoreAjaxrequestedpagesinthebrowser’shistory.Thatprocessoftensimplifiesbrowserbookmarks.

Page 28: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

AngularJSTemplatesAngularJSpartials,alsocalledtemplates,arecodesectionsthatcontainHTMLcodethatareboundtothe<divng-view></div></div>tagshownintheindex.htmlfileearlierinthischapter.Ifyoulookbackatthecompleteapp.jsfile,youcanseethatdifferenttemplateUrlvaluesaredefinedforeachroute.

Themain.htmlandshow.htmlfileslistednextshowthetwodefinedpartials(templates).ThetemplatescontainjustHTMLcode,withnothingspecialatthistime.Later,wewilluseAngularJS’sbuilt-intemplatelanguagetodisplaydynamicdatainourtemplates:

<!--chapter1/main.html-->

<div>HelloWorld</div>

<!--chapter1/show.html-->

<div>ShowTheWorld</div>

Astheuserclicksonthedifferentlinks,thevalueassignedto<divng-view>isreplacedwiththecontentoftheassociatedtemplatefiles.Thevalueofcontrollerdefinedforeachroutereferencesthecontrollercomponent(oftheMVCpattern)thatisdefinedforeachparticularroute.

ThenextsectionsprovideabriefoverviewofeachAngularJSMVCcomponentandhowitisused,togiveyouabetterunderstandingofhowAngularJSworks.UnlikemostJavaScriptclient-sideframeworks,AngularJSprovidesthemodel,view,andcontrollercomponentsforuseinallapplications.ThatoftenhelpsdevelopersfamiliarwithdesignpatternstoquicklygraspAngularJSconcepts.

Page 29: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

AngularJSViews(MVC)ManyJavaScriptclient-sideframeworksrequireyoutoactuallydefinetheviewclassesinJavaScript,andtheycancontainanywherefromafewtohundredsoflinesofcode.SuchisnotthecasewithAngularJS.AngularJSpullsinallthetemplatesdefinedforanapplicationandbuildstheviewsinthedocumentobjectmodel(DOM)foryou.Therefore,theonlyworkyouneedtodotobuildtheviewsistocreatethetemplates.

BuildingviewsinAngularJSisasimpleprocessthatusesmostlyHTMLandCSS.ThesimplicityofAngularJSviewsisahugetime-saverwhenyou’rebuildingAngularJSapplications.WewillcovercreatingtemplatesinmoredetailinChapter5.

Page 30: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

AngularJSModels(MVC)ManyJavaScriptclient-sideframeworksalsorequireyoutocreateJavaScriptmodelclasses.ThatisalsonotthecasewithAngularJS.AngularJShasa$scopeobjectthatisusedtostoretheapplicationmodel.ScopesareattachedtotheDOM.Thewaytoaccessthemodelisbyusingdatapropertiesassignedtothe$scopeobject.

TheAngularJS$scopehelpstosimplifyJavaScriptapplicationsconsiderably.OtherJavaScriptframeworksoftenencourageplacinglargeamountsofbusinesslogicinsidethemodelclassesfortheparticularframework.Unfortunately,thatpracticeoftenleadstoduplicatedbusinesslogic.Inalargeproject,thatcanleadtothousandsoflinesofuselesscode.WewilltalkmoreaboutmodelsinChapter7.

www.allitebooks.com

Page 31: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

AngularJSControllers(MVC)AngularJScontrollersarethetapethatholdsthemodelsandviewstogether.Thecontrolleriswhereyoushouldplaceallbusinesslogicspecifictoaparticularviewwhenit’snotpossibletoplacethelogicinsideaRESTservice.BusinesslogicshouldalmostalwaysbeplacedinbackendRESTserviceswheneverpossible;thishelpstosimplifyAngularJSapplications.

Whenbusinesslogicplacedinsideanapplicationisusedbymultiplecontrollers,itshouldbeplacedinAngularJSnon-RESTservicesinstead.Thoseservicescanthenbeinjectedintoanycontrollerthatneedsaccesstothelogic.Wewillcovernon-RESTservicesinChapter8ingreatdetail.

Page 32: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

ControllerBusinessLogicThefollowingcodeshowsthecontentsofthecontrollers.jsfile.AtthestartofthefilewedefinethehelloWorldControllermodule.Wethendefinetwonewcontrollers,MainCtrlandShowCtrl,andattachthemtothehelloWorldControllermodule.BusinesslogicspecifictotheMainCtrlcontrollerisdefinedinsidethatcontroller.Likewise,businesslogicspecifictotheShowCtrlcontrollerisdefinedinsidetheShowCtrlcontroller.Noticethat$scopeisinjectedintobothcontrollers.The$scopethatisinjectedintoeachcontrollerisspecifictothatcontrollerandnotvisibletoothercontrollers:

/*chapter1/controllers.js*/

'usestrict';

/*Controllers*/

varhelloWorldControllers=

angular.module('helloWorldControllers',[]);

helloWorldControllers.controller('MainCtrl',['$scope',

functionMainCtrl($scope){

$scope.message="HelloWorld";

}]);

helloWorldControllers.controller('ShowCtrl',['$scope',

functionShowCtrl($scope){

$scope.message="ShowTheWorld";

}]);

Asyoucansee,wearenowusingthemodeltopopulatethemessagesthatgetdisplayedinthetemplates.Thefollowingcodeshowsthemodifiedtemplatesthatusethenewlycreatedmodelvalues.Theline$scope.message="HelloWorld"intheMainCtrlcontrollerisusedtocreateapropertynamedmessagethatisaddedtothescope(whichholdsthemodelattributes).Wethenusethedoublecurlybracesmarkup({{}})insidethemain.htmltemplatetogainaccesstoanddisplaythevalueassignedto$scope.message:

<!--chapter1/main.html-->

<div>{{message}}</div>

UsingdoublecurlybracesisAngularJS’swayofdisplayingscopepropertiesintheview.Thedoublecurlybracessyntaxisactuallypartofthebuilt-inAngularJStemplatelanguage.

Likewise,weusethevalueassignedtothemessagepropertywiththeline$scope.message="ShowTheWorld"intheShowCtrlcontrollertopopulatethemessagedisplayedintheshow.htmltemplate.Weusethedoublecurlybracesmarkupinsidetheshow.htmltemplateasbeforetogainaccesstoanddisplaythemodelproperty:

<!--chapter1/show.html-->

<div>{{message}}</div>

Page 33: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

IntegratingAngularJSwithOtherFrameworksAngularJScanbeintegratedintoexistingapplicationsthatuseotherframeworks.ThosemaybeotherJavaScriptclient-sideframeworks,orwebframeworkslikeSpringMVCorCakePHP.YoucouldtakeanapplicationwritteninJavaandaddsomenewclient-sidefunctionalityveryeasilyusingAngularJS,cuttingdevelopmenttimeconsiderably.

AddinganewAngularJSshoppingcarttoanexistingJavaapplicationwouldbeagoodexampletoconsider.TheexistingJavaapplicationcouldbewrittenwiththeSpringframeworkanduseSpringMVCasthewebframework.AddingashoppingcartbuiltwithJavausingSpringMVCcouldbeatime-consumingprocess.That,however,wouldnotbethecasewithAngularJS.

YoucouldquicklybuildashoppingcartwithAngularJSandbeupandrunninginafewhours,easilyintegratingthecartintotheexistingJavaapplication.Notonlywouldyoubeabletobuildthecartfaster,butyoucouldquicklyaddunittestingtoincreasecoverageandreducetheapplication’sdefects.AngularJSwasdesignedtobetestablefromtheverybeginning;thatisoneofthekeyfeaturesofAngularJSandamajorreasonforselectingitoverotherJavaScriptclient-sideframeworks.WewilltalkabouttestingAngularJSapplicationsinthenextsection.

Page 34: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

TestingAngularJSApplicationsInrecentyearscontinuousintegration(CI)buildtoolssuchasTravisCI,Jenkins,andothershaveriseninpopularityandusage.CItoolshavetheabilitytoruntestscriptsduringabuildprocessandgiveimmediatefeedbackbywayoftestresults.CItoolshelptoautomatetheprocessoftestingsoftwareandcanoftenalertdevelopersofsoftwaredefectsassoonastheyoccur.

TherearetwotypesofAngularJSteststhatintegratewellwithCItools.Thefirsttypeoftestingisunittesting.Mostdevelopersarefamiliarwithunittesting;theycanoftenidentifysoftwaredefectsearlyinthedevelopmentprocessbytestingsmallunitsofcode.Thesecondtypeoftestingisend-to-end(E2E)testing.E2Etestinghelpstoidentifysoftwaredefectsbytestinghowsoftwarecomponentsconnectandinteract.

TherearemanytestingtoolsusedforunittestingAngularJSapplications.TwoofthemostpopularareKarmaandJSTestDriver.Karma,however,isquicklybecomingthetopchoiceforAngularJSdevelopmentteams.ThemostpopularE2Etesttoolforend-to-endtestingofAngularJSapplicationsisanewtoolcalledProtractor.BothtoolsintegratewellwithCIbuildtools.

LargeAngularJSdevelopmentteamswillfindtestingAngularJSapplicationswithcontinuousintegrationtoolstobeahugetime-saver.OftenafailedCItestisthefirstindicationofadefectforlargeteams.SmallteamswillalsoseemanyadvantagestoCI-basedtesting.AngularJSdevelopersshouldalwaysdevelopbothunittestsandend-to-endtestswheneverpossible.

Throughoutthisbook,wewillcoverbothunittestingandend-to-endtesting.WewillusebothKarmaandJsTestDriveforunittesting,andwewilluseProtractorforE2Etesting.

Page 35: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

ConclusionWewillcovermodels,views,andcontrollersingreatdetailinlaterchapters,usingthosecomponentstobuildworkingapplicationsthatshowthepowerofAngularJS.WewillshowhowallthreecomponentsworktogethertosimplifythejobofbuildingJavaScriptclient-sideapplications.Wewillalsocoverbuildingbothunittestsandend-to-endtestsforAngularJSapplications.

Chapter2willfocusonhelpingyousetupadevelopmentenvironmentforHTML5.WewillalsodownloadthelatestversionsofAngularJS,jQuery,andTwitterBootstrapandaddthosetooursampleproject.

Page 36: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development
Page 37: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

Chapter2.TheIDEandAngularJSProjects

ManyJavaScripteditorsareusedbyAngularJSdevelopers.Usinganintegrateddevelopmentenvironment(IDE)withagoodJavaScripteditorisahugetime-saverandspeedsupthedevelopmentprocessconsiderably.IDEswithgoodJavaScripttoolsusuallyhavegoodHTML5andCSS3toolsaswell,whichhelpstoincreaseadeveloper’sproductivitysubstantially.WewillharnessthepowerofanIDEinthisbook.

Page 38: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

TheIDEWewillbeusingNetBeansasourintegrateddevelopmentenvironment.Youcan,however,useanyIDEoreditorthatyouprefer.MostofthischapterwillbegenericandwillworkfinewithanymodernIDE.Togetstarted,dothefollowing:

1. DownloadandinstallthelatestversionofNetBeansfromtheNetBeanswebsite(ordownloadanotherIDEofyourchoice).

2. DownloadthelatestversionsofthefollowingAngularJSfiles:a. angular.min.js(mainlibs)

b. angular-route.min.js(routinglibs)

c. angular-cookies.min.js(cookielibs)

d. angular-resource.min.js(RESTservicelibs)

3. DownloadthelatestversionofjQuery.

4. DownloadthelatestversionofTwitterBootstrap.

StartNetBeansandcreateanewHTML5project,asshowninFigure2-1.NametheprojectAngularJsHelloWorld_chapter2.

Page 39: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

Figure2-1.CreatingyournewHTML5project

Nowdothefollowing:

1. CreatethedirectorystructureshowninFigure2-2underSiteRoot.

2. CopytheAngularJS,jQuery,andBootstrapfilesintothelibsfolder.

3. Right-clickthejsfolderandcreatethefollowing.jsfiles:a. app.js(wheretheapplicationisdefined)

b. controllers.js(wherecontrollersaredefined)

c. services.js(whereservicesaredefined)

d. main.htmlunderthepartialsfolder

e. show.htmlunderthepartialsfolder

f. index.htmlundertheSiteRootfolder

Page 40: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

Figure2-2.Creatingthedirectorystructure

www.allitebooks.com

Page 41: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

EditingtheHTMLCodeNowwemustedittheindex.htmlfiletocreatebootstrappingfortheapplicationandtousethelibrariesand.jsfilesjustadded.Edityournewlycreatedindex.htmlfiletomatchthecodethatfollows.Theseareallthechangesthatweneedtomaketothisfilefornow.Next,wewilledittheapp.jsandcontrollers.jsfiles:

<!--chapter2/index.html-->

<!DOCTYPEhtml>

<htmllang="en"ng-app="helloWorldApp">

<head>

<title>AngularJSHelloWorld</title>

<metaname="viewport"content="width=device-width,

initial-scale=1.0">

<metahttp-equiv="Content-Type"content="text/html;charset=UTF-8">

<scriptsrc="js/libs/jquery-1.10.2.min.js"></script>

<scriptsrc="js/libs/angular.min.js"></script>

<scriptsrc="js/libs/angular-route.min.js"></script>

<scriptsrc="js/libs/angular-resource.min.js"></script>

<scriptsrc="js/libs/angular-cookies.min.js"></script>

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

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

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

</head>

<body>

<divng-view></div>

</body>

</html>

Page 42: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

EditingtheJavaScriptCodeUpdateyournewlycreatedapp.jsfilewiththecodeshownhere.Asyoucansee,itisthesamecodewecoveredinChapter1:

/*chapter2/app.js*/

'usestrict';

/*AppModule*/

varhelloWorldApp=angular.module('helloWorldApp',[

'ngRoute',

'helloWorldControllers'

]);

helloWorldApp.config(['$routeProvider','$locationProvider',

function($routeProvider,$locationProvider){

$routeProvider.

when('/',{

templateUrl:'partials/main.html',

controller:'MainCtrl'

}).when('/show',{

templateUrl:'partials/show.html',

controller:'ShowCtrl'

});

$locationProvider.html5Mode(false).hashPrefix('!');

}]);

Likewise,updateyournewlycreatedcontrollers.jsfilewiththecodeshownnext.ThisisalsothesamecodecoveredinChapter1forthecontroller:

/*chapter2/controllers.js*/

'usestrict';

/*Controllers*/

varhelloWorldControllers=

angular.module('helloWorldControllers',[]);

helloWorldControllers.controller('MainCtrl',

['$scope','$location','$http',

functionMainCtrl($scope,$location,$http){

$scope.message="HelloWorld";

}]);

helloWorldControllers.controller('ShowCtrl',

['$scope','$location','$http',

functionShowCtrl($scope,$location,$http){

$scope.message="ShowTheWorld";

}]);

Page 43: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

CreatingtheTemplatesNowallthatisleftistocreatethetemplates(partials).Dothefollowing:

<!--chapter2/main.html-->

<div>{{message}}</div>

1. Editthenewmain.htmlandaddthecodeshownhere:

2. Editshow.htmlandaddthecodeshownhere:

<!--chapter2/show.html-->

<div>{{message}}</div>

Page 44: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

RunningtheApplicationsThatconcludesthecodechangesneededintheChapter2projectfornow.Right-clickonthenewHTML5projectandselect“Run.”AttheURLhttp://localhost:8383/AngularJsHelloWorld_chapter2/index.html#!/,youshouldseethewords“HelloWorld”inthetop-leftcornerofthebrowser.

NowchangetheURLtohttp://localhost:8383/AngularJsHelloWorld_chapter2/index.html#!/show,andyoushouldseethewords“ShowTheWorld”inthetop-leftcornerofthebrowser.Ifyougetthecorrectresults,yourprojectisconfiguredcorrectly.Ifyougetadifferentresult,gobackthroughthischapterandverifythatyoucompletedallthesteps.

Ifyoucontinuetohaveproblems,downloadtheChapter2sourcefromGitHubandtrytorunthatcode.

Page 45: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

TestingAngularJSApplicationsintheIDEAsImentionedinthepreviouschapter,therearetwotypesofteststhatareusedfortestingAngularJSapplications.Thefirsttypeoftestistheunittest.Unittestingisusuallythefirstplacewhereissueswiththecodearefound,throughtestingsmallunitsofcode.Thesecondtypeoftestisend-to-end(E2E)testing.E2Etestinghelpstoidentifysoftwaredefectsbytestinghowcomponentsconnectandinteracttogetherasawhole.

NetBeanscaneasilyworkwithbothJsTestDriverandKarmaforunittestingAngularJSapplications.KarmaisquicklybecomingthemostpopularchoiceforAngularJSdevelopmentteams,sowewillfocusmoreonKarmainlaterchapters.ProtractoristhemostpopulartestframeworkforE2EtestingofAngularJSapplications.Currently,mostdevelopmentenvironmentsdon’thavebuilt-insupportforProtractor.Protractorisanewtestingframework,anditmaytakeawhilebeforemostIDEsandeditorssupportit.NetBeanscurrentlyhasnosupportforProtractor.

BothKarmaandProtractorrunonNode.js.Node.jsisanopensourcecross-platformframeworkbuiltontheGoogleV8JavaScriptengine.WewilluseNode.jslaterinthisbook,whenwefocusonbuildingMEANstackapplications.InstallingKarmaandProtractorisarelativelyeasyprocessthatusestheNode.jspackagemanager(npm)fortheinstallationprocess.

Node.js-basedprojectsuseaJSONfilenamedpackage.jsonastheprojectconfigurationfile.Thefollowingisastandardpackage.jsonfileusedinaNetBeansproject.Ifyoulookatthedependenciessectionofthefile,youwillseethatweactuallydefineKarmaasadependencyoftheapplication.ThatisbecauseKarmaisusuallyinstalledlocallyattheprojectlevelforeachindividualproject:

{"chapter":2,"name":"package.json"},

{

"name":"UlboraCmsMean",

"version":"2.0.0",

"description":"UlboraCms",

"keywords":["UlboraCMS","Node.js","Ken",

"Williamson","micbutton.com"],

"author":{

"name":"KenWilliamson",

"email":"[email protected]",

"url":"http://www.drivensolutions.com/"

},

"homepage":"http://www.ulboracms.org",

"repository":{

"type":"git",

"url":"https://github.com/Ulbora/ulboracms"

},

"engines":{

"node":">=0.6.0",

"npm":">=1.0.0"

},

"dependencies":{

"express":"~3.4.4",

"mongoose":"*",

"atob":"*",

"btoa":"*",

Page 46: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

"node-rest-client":"*",

"consolidate":"*",

"ejs":"*",

"handlebars":"*",

"nodemailer":"*",

"karma":"*"

},

"bundleDependencies":[],

"private":true,

"main":"./server.js",

"bugs":{

"url":"null"

}

}

AfilesimilartothisonewillbeusedlaterinthebookwhenwebuildtheMEANstackblogapplication.NetBeans,usingaNode.jsplugin,cangeneratethepackage.jsonfileforyou.Thegeneratedfilewillneedtobemodifiedtoincludethespecificsofyourparticularproject.

TIPYoucanalsousenpminittogeneratethepackage.jsonfile.Aftertypingnpminitatthecommandprompt,youwillbepresentedwithafewquestions.Yourresponseswillthenbeusedtocreateadefaultpackage.jsonfile.

Page 47: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

JsTestRunnerNetBeanshasbuilt-insupportforJsTestRunner.TheJsTestRunnerconfigurationfilecanbegeneratedandrequiresfewchangestogetunittestingrunningonyourlocalenvironment.

UnlikeKarma,JsTestRunnerisnotbasedonNode.js.ThefollowingisastandardJsTestRunnerconfigurationfilecreatedbyNetBeansforanAngularJSproject.NoticeinthefirstlinethatthetestserverURLandportarespecified:

/*chapter2/jsTestDriver.conf*/

server:http://localhost:42442

load:

-test/lib/jasmine/jasmine.js

-test/lib/jasmine-jstd-adapter/JasmineAdapter.js

-public_html/js/libs/angular.min.js

-public_html/js/libs/angular-mocks.js

-public_html/js/libs/angular-cookies.min.js

-public_html/js/libs/angular-resource.min.js

-public_html/js/libs/angular-route.min.js

-public_html/js/*.js

-test/unit/*.js

exclude:

Thelocationsofthetestlibraryfilesarespecifiedunderload.WealsospecifythelocationsofeachunittestscriptthatshouldberunbyJsTestDriver.Testfilenamesusuallyendwith“Spec.”ThefollowingcodeshowsatestspecificationfileusedtotestAngularJScontrollers.Wewillcovertestspecificationinlaterchapters,whenwerunourfirstunittests:

/*chapter2/controllerSpec.js*/

/*Jasminespecsforcontrollersgohere*/

describe('HelloWorld',function(){

beforeEach(module('helloWorldApp'));

describe('MainCtrl',function(){

varscope,ctrl;

beforeEach(inject(function($rootScope,$controller){

scope=$rootScope.$new();

ctrl=$controller('MainCtrl',{$scope:scope});

}));

it('shouldcreateinitialedmessage',function(){

expect(scope.message).toEqual("HelloWorld");

});

});

describe('ShowCtrl',function(){

varscope,ctrl;

beforeEach(inject(function($rootScope,$controller){

scope=$rootScope.$new();

ctrl=$controller('ShowCtrl',{$scope:scope});

}));

Page 48: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

it('shouldcreateinitialedmessage',function(){

expect(scope.message).toEqual("ShowTheWorld");

});

});

describe('CustomerCtrl',function(){

varscope,ctrl;

beforeEach(inject(function($rootScope,$controller){

scope=$rootScope.$new();

ctrl=$controller('CustomerCtrl',{$scope:scope});

}));

it('shouldcreateinitialedmessage',function(){

expect(scope.customerName).toEqual("Bob'sBurgers");

});

});

});

CurrentlyoneofthebigdisadvantagesoftestingJavaScriptapplicationsisthelackoftoolsthatgeneratetestscriptsbasedontheactualsourcefilesthatneedtobetested.ThosetoolshaveexistedintheJavaworldforyears,buttheyarestillrelativelynonexistentintherealmofJavaScript.So,afilelikethisoneneedstobecreatedbyhandtounittesteachAngularJScontroller.

Page 49: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

KarmaTestRunnerAsImentionedearlier,KarmaisatestrunnerbasedonNode.js.TheKarmateamrecommendsinstallingKarmalocallyattheprojectlevel.So,wewilladdKarmainthepackage.jsonfileofeachofourprojects,thenusethefollowingcommandtopulldownandinstallKarmaonaper-projectbasis:

npminstall

Whenyourunthiscommand,npmreadsthepackage.jsonfileandinstallsthepackagesdefinedinthedependenciessectionofthefile.Afteryourunthecommand,Karmawillbelocatedunderthenode_modulesfolderwithinyourprojectfolder.AnyotherNode.jsdependenciesdefinedinthepackage.jsonfilewillalsobelocatedunderthenode_modulesfolder.

Karmarequiresaconfigurationfilenamedkarma.conf.jsthatspecifieshowitshouldrununittests.YoucanuseNetBeanstogeneratethekarma.conf.jsfile.ThefollowingcodeshowsaKarmaconfigurationfilegeneratedbyNetBeans.Youcanseetherearesectionsofthefiletospecifythelocationsoflibraryfiles,testscripts,andbrowserplugins:

/*chapter2/karma.conf.js*/

/*

*Tochangethislicenseheader,chooseLicenseHeadersin

*ProjectProperties.

*Tochangethistemplatefile,chooseTools->Templates

*andopenthetemplateintheeditor.

*/

module.exports=function(config){

config.set({

basePath:'../',

files:[

],

exclude:[

],

autoWatch:true,

frameworks:[

],

browsers:[

],

plugins:[

]

});

};

WewillcoverKarmainmoredetailwhenwerunourfirstunittestusingKarma,inChapter4.

Page 50: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

ProtractorMostdevelopmentenvironmentsdonotyethavebuilt-insupportforProtractor.ProtractorisaNode.js-basedframework,justlikeKarma.TheinstallationprocessismuchliketheprocessforKarma.ProtractorisbuiltontopofWebDriverJS.TheProtractorteamrecommendsinstallingProtractorgloballyonyoursystem.

ToinstallProtractoronyourdevelopmentmachine,issuethefollowingcommand.Noticethe-gflaginthecommandline—thattellsnpmtoinstallProtractorgloballyforallprojectsandapplicationstouse:

npminstall-gprotractor

SinceProtractorisbuiltonWebDriverJS,wemustalsoconfigureWebDriverJSforourtestenvironment.RunthiscommandtoupdateWebDriverJSwithallthelatestbinaries:

webdriver-managerupdate

Oncethatcommandexecutessuccessfully,runthefollowingcommandtostarttheSeleniumServerthatWebDriverJSusestorunProtractortestscripts:

webdriver-managerstart

Protractorneedsaconfigurationfilethattellsithowtoruntestscripts.Herearethecontentsoftheconf.jsfileusedtoconfigureProtractor:

/*chapter2/conf.jsProtractorconfigurationfile*/

exports.config={

seleniumAddress:'http://localhost:4444/wd/hub',

specs:['blog-spec.js']

};

OnceProtractorisinstalledandconfiguredonyoursystem,allthatisleftistocreatethetestscripts(testspecifications)andrunthescripts.Here’sasamplescriptforaProtractortest:

/*chapter2/blog-spec.js*/

describe('MEANBlog',function(){

it('testtheMEANBlog',function(){

browser.get('http://localhost:8080');

element(by.model('blogList')).

sendKeys('thisisablogpost');

element(by.css('[value="add"]')).click();

varblogList=element.all(by.repeater('bloginblogs'));

expect(blogList.count()).toEqual(3);

expect(blogList.get(2).getText()).

www.allitebooks.com

Page 51: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

toEqual('thisisablogpost');

});

});

TorunProtractor,issuethefollowingcommand.Onceyourunthecommand,thebrowserwindowshouldopenanddisplaythetestresults:

protractorconf.js

BothKarmaandProtractorcanbeintegratedwithcontinuousintegration(CI)buildsystemslikeTravisCIandJenkins,asImentionedinChapter1.ManyopensourceprojectsandenterprisedevelopmentteamsaremovingtowardCIbuildsystems.BuildingKarmaandProtractortestingintoyourAngularJSprojectisavitalpartofthesoftwaredevelopmentprocess.Timespentwritingtestscriptswillultimatelybeworththeeffortinthelongrun.

WewillcoverbothKarmaandProtractortestingingreatdetailinlaterchapters.AtthattimewewillinstallandconfigurebothKarmaandProtractor.SincebothrunonNode.js,youwillalsoneedtoinstallthatandtheNode.jspackagemanager(npm)onyoursystemtopowerthetestplatforms.

Page 52: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

ConclusionInthischapter,wecoveredhowtosetupadevelopmentenvironmentforAngularJSandbuiltandranaprojectwithAngularJS.WealsocoveredhowtoinstallatestenvironmentwithbothJsTestDriverandKarmaforunittestingourAngularJSprojects.Finally,welookedathowtoinstallandconfigureProtractorfordoingend-to-endtestingofAngularJSprojects.Withtheknowledgegainedfromthischapter,wearereadytostartworkingwithmorecomplexprojects.

WearenowreadytomoveontoChapter3,wherewewillcoverMVCasitappliestoAngularJSinmoredetail.

Page 53: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development
Page 54: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

Chapter3.MVCandAngularJS

AngularJSpresentsanewandpowerfulwaytodevelopwebapplicationsandwebsites—ithasthepowerandfunctionalityofconventionalwebframeworks,butwithmanyadvantages.AngularJSprovidesawaytobuildwebappsandsiteswithouttheoverheadnormallyassociatedwithwebframeworks.

Conventionalwebframeworksoftentolerateserver-sidepagescriptingusingPHP,ActiveServerPages(ASP),andJavaServerPages(JSP).Whileserver-sidepagescriptingworkssufficientlywellontheserverside,itdoesposemanymaintenanceissuesfordevelopers.Butthatisnotthebiggestissuewithconventionalwebframeworks.Conventionalwebframeworkstendtorunslowerandbesluggishonmobiledevices.Andmobileusershaveamuchlowertoleranceforsystemdelaysandslowpageloadsthandesktopusers.

WemustcompareconventionalwebframeworkstoAngularJStounderstandtheadvantagesthatAngularJSpresents.ThenextsectionwillgiveyouaclearunderstandingoftheadvantagesofAngularJSoverframeworksthatyoumayhaveusedinthepast.Withthatunderstanding,wewillbesettostartbuildingmoremaintainableapplicationsinabetterway.

Page 55: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

TheOldWayWebMVCframeworkssuchasApacheStruts,SpringMVC,andtheZendFrameworkdominatedthewebdevelopmentframeworkspaceformorethan15years.Thosesameframeworksstilldominatethespaceeventoday.Therearesomecaseswherewebframeworksdopresentabetterapplicationdesignthanmoremodernclient-sideframeworks,butthosecaseshavediminishedconsiderablyoverthelastcoupleofyears.

WebMVCframeworksresideentirelyontheserver.Allfunctionssuchasdatabaseaccess,businesslogic,displaylogic,andUIactivitieshappenontheserver,usingservermemoryandresources.WebMVCframeworksoftenusevariouspagescriptingtechniquessuchasASP,JSP,andPHPtocontrolpresentationlogic,andinsomecasesbusinesslogicisalsoplacedinsidethepages.

Figure3-1showsadiagramofaconventionalwebMVCframework.Fromthediagram,youcanseethattheapplicationorwebsiterunsonthebackendserver,andonlythewebbrowserrunsontheuser’shardware.AlthoughthedesigninFigure3-1isoldtechnology,itisstillinheavyusetoday.

Figure3-1.ConventionalwebMVCframework

WebapplicationsandsitesbuiltwithRubyonRails,theZendFramework,SpringMVC,CakePHP,andotherwebframeworksarebasedonthisdesign.Althoughthedesignworkswellinmanysituations,itdoeshaveseveralflaws.

Onesuchdesignflawisrelatedtomobileapplicationsandmobilewebsites.WhilewebpagesassociatedwithwebframeworkscanbedesignedwithHTML5andCSS3andbemaderesponsiveandlookgoodonmobiledevices,theapplicationorwebsiteisdependentonthewebservertomakethedifferentpagesavailabletothemobiledevice.Inaddition,thewebpagesmustruninthemobiledevice’swebbrowser.

Theapplicationorsitedeveloperhasverylittlecontroloverthemobiledevice’sweb

Page 56: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

browser.AusermustfindthesiteorapplicationandenteritsURLintothebrowser’saddressbarinordertoviewthewebpageortoruntheapplication.Mobileusers,however,oftenfindthatprocesstootime-consuming.

Whilemobilesitesandapplicationsdistributedasweb-baseddesignshavetheadvantageofsavingdevelopmenthoursandmoney,theydoposeaprobleminmanysituations.Often,mobiledevelopersneedtobuildcustomdeviceapplicationsandhavethoseapplicationsdistributedviathevariousonlinestores.Notonlydoesacustomapplicationofferahigherlevelofcustomerservice,butitalsoservesasamarketingtool.Asthenumberofmobiledevicesinuseincreases,thedemandforcustommobileapplicationswillalsoincrease.

Consider,forexample,adoctor’sofficethatneedstoallowpatientstomakeappointmentsfromtheirmobiledevices.Suchanapplicationwouldneedtobefastandhavealmostnodelaywhenpatientsarenavigatingfrompagetopage.Theapplicationwouldalsoneedtolookgoodonanydevice.Auserwithasmallsmartphoneshouldhavethesameuserexperienceasauserwitha10-inchtablet.

AnapplicationdeveloperorarchitectattemptingamobiledesignbasedonthesystemdesignshowninFigure3-1reallyonlyhastwochoicestoconsider.

Page 57: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

ChoiceOneThefirstoptionistobuildacustommobileapplicationasa“wrapper”aroundtheconventionalsiteshowninFigure3-1.Figure3-2showsanAndroidapplicationdesignedasawrapperapplication.Asyoucansee,theAndroidapplicationconsistssolelyofanAndroidWebViewcomponentthatisconfiguredtopointtothewebapplicationURL.

Figure3-2.AnAndroidwrapperaroundatraditionalwebapplication

TheWebViewcomponentservesasabrowsercontrolinsidetheAndroidapplication.Thedevelopercancustom-configuretheWebViewcomponentfortheneedsoftheparticularmobileapplication.Allapplicationoperationsstill,however,runonthebackendserver(thewebserver),andthespeedandresponsivenessoftheAndroidapplicationarestillhighlydependentonthatserverandthequalityoftheuser’sInternetconnection.

ThefollowingcodeshowsasegmentofanAndroidmainActivity.AnewAndroidWebViewobjectisfirstinstantiated.JavaScriptisthenenabledforthenewinstance.Finally,theURLofthewebsiteisloadedintothenewinstancewiththeloadUrlmethod:

/*chapter3excerptfromanAndroidWebViewshownloadinga

conventionalwebsite*/

WebViewwebview=newWebView(this);

webview.getSettings().setJavaScriptEnabled(true);

finalActivityactivity=this;

webview.setWebViewClient(newWebViewClient(){

Page 58: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

webview.loadUrl("http://www.google.com");

TheWebViewinstanceshownhereisjustacontrolforthedevice’sinternalwebbrowser.TheAndroiddevice’sbrowseriscompletelydependentonthewebsiteforfunctionality.Ifthewebsitethatislinkedtogoesdownorthenetworkconnectionislost,theuser’sbrowserwillhangandcompletelystopworking.Thatfunctionalityisveryfrustratingformobiledeviceusers.Itis,however,acommonconfigurationformobileapplications.

Page 59: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

ChoiceTwoThesecondoptionwouldrequirethedevelopertowriteanativeorHTML5mobileapplicationthatcalledwebservicesonthebackendforbusinessfunctions.ThisapproachwouldrequireaddingRESTwebservicestotheexistingwebapplicationtomakeuseofexistingbusinesslogic.Optiontwois,ineffect,acompleterewriteoftheapplication.AddingRESTservicestotheexistingwebapplicationwouldnotbeatrivialmatter.Optiontwowould,however,offerthebestapplicationdesignandwouldprovidethebestuserexperience.

ThedesignshowninFigure3-1isn’tdirectlytransferabletomobiledevices.Fifteenyearsago,whenmobiledeviceswerenotinheavyuse,thatdesignwasacommonchoiceforapplicationdevelopersandarchitects,andposedfewproblems.Mobiledevicesalesreachedanall-timehighin2014,however,andmostanalystspredictthattrendwillonlyincreaseinthecomingyears.

Mobileisthefutureofeverything.Aswirelesssystemsimproveandevolve,mobiledeviceswillevolvetooandplayamajorroleinallourdailyactivities.Amobiledevicewillalertyouwhenyourtableisreadyatyourfavoriterestaurant.Thatsamedevicewillreplaceyourdebitcardorcreditcardwhenit’stimetopaythebillandtiptheserver.

So,developersmustplanforthefuturenow.It’stimetostopbuildingsoftwarebasedonanoldandoutdatedtechnology.That’swhereJavaScriptclient-sideframeworkscomeintoplay,andthat’swhereAngularJSshinesthebrightestofalltheJavaScriptframeworksavailable.AngularJSisasolidfoundationforbuildingscalableapplicationsthatrunwellondesktopsandabroadarrayofmobiledevices,withfewifanymodificationsneededforeachplatform.

Page 60: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

ANewandBetterWayAngularJSisaJavaScriptMVCframeworkthatcutsdevelopmenttimeforbothwebapplicationsandmobileapplicationsthatrunonmultipledeviceplatforms.Figure3-3showsadiagramofanAngularJSapplicationthatusesbusinesslogicthat’sexposedthroughRESTwebservices.TheRESTservicescanrunanywhereandbewritteninanyprogramminglanguage.TwopopularframeworksusedtobuildRESTservicesaretheSpringframework,writteninJava,andExpressJSforNode.js.

Figure3-3.AngularJSapplicationdesign

IfyoulookcloselyatFigure3-3,youcanseethattheentireAngularJSapplicationrunsontheuser’shardware,intheuser’swebbrowser.Thatmaybeadesktopbrowserorthebrowserofamobiledevice.Withthisdesignweshiftthedisplaylogicfromtheservertotheuser’shardware,resultinginamuchbetteruserexperience.Theapplicationrunsfasterandismuchmoreresponsive—morelikeathick-clientornativeapplicationthanabrowser-basedapplication.

AngularJSapplicationsharnessthepoweroftheuser’shardware.Theapproachthat’stakenfreestheserverorserverstohandlenothingbutbusinesslogicanddataaccess.UsingRESTservicesthatsendandreceiveJSONhelpstogreatlysimplifyAngularJSapplications:JSONisadata-interchangeformatforRESTservicesthatiseasytoreadandunderstand.

Figure3-4showsthesameAngularJSapplicationdeployedaspartofanAndroidapplication.TheJavaScript,CSS3,andHTML5codeisallthesameregardlessofwheretheapplicationisdeployed.Iftheapplicationwasdesignedfromamobile-firstperspective,itshouldlookgreatandrunwellonanyplatform.

www.allitebooks.com

Page 61: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

Figure3-4.TheAngularJSapplicationdeployedasanAndroidapp

NotonlydoesthedesigninFigure3-4produceabetteruserexperience,butitalsocutsdevelopmenttimesignificantly.AndaswiththedesigninFigure3-3,theapplicationrunsentirelyontheuser’shardware,shiftingtheloadfromtheservertotheuser’sdevice.

Page 62: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

TestingConsiderationsWecoveredsomeofthebasicsoftestingAngularJSapplicationsbackinChapter2.TheabilitytoeffectivelyandeasilytestAngularJSapplicationsisoneofthestrongestmotivatorsforusingtheframework.NotonlyareAngularJSapplicationsfastertowrite,buttheyarealsomuchfasterandeasiertotestthanconventionalwebframework–basedapplications.Hereiswhy.

TestscriptsforAngularJS,knownastestspecifications,arealwayswritteninJavaScript.Therearenocomplextestframeworkstoinstalllikeyoufindwithtraditionalwebframeworks.Onemorething:JavaScripttestsrunfasterthantestswrittenforconventionalwebframeworks.Thatisveryimportantwhenacontinuousintegrationsystemisused.

Testexecutionspeedsmaynotseemlikeaseriousconcernatfirst.ButconsiderthecontinuousintegrationplatformslikeTravisCIandJenkinsthatwediscussedbackinChapter2.Ifyouhadasmallshopwithfiveorsixdevelopers,testscriptexecutionspeedswouldn’tusuallybeaconcern.Ifyouhadalargeenterpriseshop,however,withafewhundreddevelopersallrunningCIbuildsatthesametime,thenconcernswouldchangequickly.

Thetwomostpopulartestframeworksusedforclient-sideJavaScriptandAngularJS,KarmaandProtractor,runontheNode.jsframework.ApplicationsandtestscriptsthatrunonNode.jsrunextremelyfast.ThatisoneofthemajoradvantagesofusingNode.js.ContinuousintegrationsystemsalsouseNode.jsforJavaScriptbuildsandtoruntestscripts.Itiseasytosee,then,whyJavaScripttestingisfasterinaCIenvironment.

Page 63: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

ResponsiveDesignConsiderationsAnotherconsiderationwhenyouarecomparingtraditionalwebframeworkstoAngularJSishowwellresponsivedesignisaccomplished.Astrongresponsivedesignlooksgoodbothonadesktopandonallmobiledevicesthatusethesoftware.Whileyoucanbuildresponsiveapplicationswithtraditionalwebframeworks,it’snotoftendone.Unfortunately,manywebapplicationdevelopersoftentargetdesktopsandmaybetabletsandignorethevarioussmallerdevicesthatusetheirwebsites.

Take,forexample,theCSS3codeshownnext.Thecodeistakenfromaserver-sideapplicationwrittenwithCakePHP,awebMVCframework:

/*chapter3server-sidecss3*/

/*notbuiltformobile*/

.page-container{

float:left;

margin:3%000;

padding:0000;

width:100%;

}

img{

max-width:50%;

}

.partner-form{

float:left;

width:50%;

margin:00025%;

padding:1%5%1%5%;

border-radius:7px;

-moz-border-radius:7px;/*Firefox3.6andearlier*/

border:#230fbasolid1px;

}

.new-article-upload-wrapper{

float:left;

width:30%;

margin:00035%;

padding:1%;

border-radius:7px;

-moz-border-radius:7px;/*Firefox3.6andearlier*/

border:#230fbasolid1px;

}

.login-title{

float:left;

width:100%;

margin:6%01%0;

text-align:center;

font-size:18pt;

font-weight:bold;

}

.config-form-wrapper{

float:left;

width:60%;

padding:0000;

margin:00020%;

border-radius:7px;

-moz-border-radius:7px;/*Firefox3.6andearlier*/

border:#230fbasolid1px;

}

.comment-form-wrapper{

float:left;

width:60%;

padding:0000;

margin:00020%;

border-radius:7px;

-moz-border-radius:7px;/*Firefox3.6andearlier*/

border:#230fbasolid1px;

Page 64: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

}

.summary-cell{

height:300px;

}

.summary-cell-data{

height:250px;

font-size:12pt;

}

Anapplicationstyledwiththiscodewouldlookfineonadesktop,andmaybeatablet.Therewouldbemajorstylingissueswithasmallmobiledevice,however.AmobilewrapperapplicationliketheoneImentionedearlierthatwrappedawebsitethatusedthiscodewouldbeatagreatdisadvantage.Youcouldnevermaketheapplicationlookgoodonasmallphone.

ThecodethatfollowsistakenfromamobileapplicationbuiltwithAngularJS.Noticethemediaquerylineslike@mediascreenand(min-width:1200px)thatwrappartsoftheCSS3.MediaqueriesletdevelopersstyleAngularJSapplicationstospecificscreensizes:

/*chapter3mobilecss3*/

/*builtformobile*/

@mediascreenand(min-width:1200px){

.page-container{

margin:3%000;

padding:0000;

width:100%;

}

img{

max-width:50%;

}

.partner-form{

width:50%;

margin:00025%;

padding:1%5%1%5%;

border-radius:7px;

-moz-border-radius:7px;/*Firefox3.6andearlier*/

}

.new-article-upload-wrapper{

width:30%;

margin:00035%;

padding:1%;

border-radius:7px;

-moz-border-radius:7px;/*Firefox3.6andearlier*/

}

.login-title{

width:100%;

margin:6%01%0;

font-size:18pt;

}

.config-form-wrapper{

width:60%;

padding:0000;

margin:00020%;

border-radius:7px;

-moz-border-radius:7px;/*Firefox3.6andearlier*/

}

.comment-form-wrapper{

width:60%;

padding:0000;

margin:00020%;

border-radius:7px;

-moz-border-radius:7px;/*Firefox3.6andearlier*/

}

.summary-cell{

height:300px;

Page 65: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

}

.summary-cell-data{

height:250px;

font-size:12pt;

}

}

@mediascreenand(max-width:1200px){

.page-container{

margin:5%000;

padding:0000;

width:100%;

}

img{

max-width:60%;

}

.nav-ds{

margin:0000;

}

.nav-dsli{

width:11%;

}

.nav-dslia{

margin:0000;

padding:4%04%0;

}

.partner-form{

width:50%;

margin:00025%;

padding:1%5%1%5%;

border-radius:7px;

-moz-border-radius:7px;/*Firefox3.6andearlier*/

}

.new-article-upload-wrapper{

margin:00030%;

padding:1%;

border-radius:7px;

-moz-border-radius:7px;/*Firefox3.6andearlier*/

}

.login-title{

width:100%;

margin:6%01%0;

font-size:18pt;

}

.config-form-wrapper{

width:60%;

padding:0000;

margin:00020%;

border-radius:7px;

-moz-border-radius:7px;/*Firefox3.6andearlier*/

}

.comment-form-wrapper{

width:60%;

padding:0000;

margin:00020%;

border-radius:7px;

-moz-border-radius:7px;/*Firefox3.6andearlier*/

}

.summary-cell{

height:300px;

}

.summary-cell-data{

height:250px;

font-size:12pt;

}

}

@mediascreenand(max-width:800px){

.page-container{

margin:7%000;

padding:0000;

width:100%;

}

img{

max-width:70%;

}

.nav-ds{

Page 66: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

margin:0000;

}

.nav-dsli{

width:11%;

}

.nav-dslia{

margin:0000;

padding:4%04%0;

}

.partner-form{

width:60%;

margin:00020%;

padding:1%5%1%5%;

border-radius:7px;

-moz-border-radius:7px;/*Firefox3.6andearlier*/

}

.new-article-upload-wrapper{

width:50%;

margin:00025%;

padding:1%;

border-radius:7px;

-moz-border-radius:7px;/*Firefox3.6andearlier*/

}

.login-title{

width:100%;

margin:6%01%0;

font-size:16pt;

}

.config-form-wrapper{

width:80%;

padding:0000;

margin:00010%;

border-radius:7px;

-moz-border-radius:7px;/*Firefox3.6andearlier*/

}

.comment-form-wrapper{

width:80%;

padding:0000;

margin:00010%;

border-radius:7px;

}

.summary-cell{

height:300px;

}

.summary-cell-data{

height:250px;

font-size:10pt;

}

}

@mediascreenand(max-width:450px){

.page-container{

margin:12%000;

padding:0000;

width:100%;

}

img{

max-width:100%;

}

.nav-ds{

margin:0000;

}

.nav-dsli{

width:15%;

}

.nav-dslia{

margin:0000;

padding:4%04%0;

}

.partner-form{

width:100%;

margin:0000%;

padding:1%5%1%5%;

border-radius:7px;

Page 67: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

-moz-border-radius:7px;/*Firefox3.6andearlier*/

}

.new-article-upload-wrapper{

width:100%;

margin:0000%;

padding:1%;

border-radius:7px;

-moz-border-radius:7px;/*Firefox3.6andearlier*/

}

.login-title{

width:100%;

margin:6%01%0;

font-size:14pt;

}

.config-form-wrapper{

width:100%;

padding:0000;

margin:0000;

border-radius:7px;

-moz-border-radius:7px;/*Firefox3.6andearlier*/

}

.comment-form-wrapper{

float:left;

width:100%;

padding:0000;

margin:0000;

border-radius:7px;

-moz-border-radius:7px;/*Firefox3.6andearlier*/

}

.summary-cell{

height:150px;

}

.summary-cell-data{

height:120px;

font-size:6pt;

}

}

IfthewebapplicationshownpreviouslyhadbeenwrittenwithAngularJS,itwouldhavebeenasimpletasktoconverttheAngularJSapplicationintoamobileapplication.ThedevelopmentteamcouldthenhavefixedtheCSS3issuesandbeendone.TheapplicationwrittenwithCakePHPhadtobecompletelyrewritten,however.

Page 68: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

ConclusionInthischapterwecomparedAngularJSapplicationstoapplicationsbuiltwithconventionalserver-sidewebframeworks.Weidentifiedmanyoftheshortcomingsofconventionalserver-sideframeworks,especiallyastheyrelatetomobileapplications,andgainedanunderstandingoftheseriouslimitationstheyposeonhowdevelopersbuildmobileapplications.

WealsolookedatthemanyadvantagesofbuildingapplicationswithAngularJS,suchasshorterdevelopmenttimesandincreasedapplicationspeedandtestability.WesawhowAngularJSgreatlysimplifiestheprocessofbuildingresponsivemobileapplications,thenlookedatareal-worldsituationwhereasimpleissuelikepoorlywrittenCSSposedaseriousproblemforamobiledevelopmentteamworkingwithanapplicationbuiltusingaconventionalserver-sidewebframework.

Theinformationpresentedinthischapterisagreatfoundationforthematerialcoveredinthefollowingchapters.WewillnowtakeourunderstandingoftheadvantagesofAngularJStothenextlevel,exploringhowAngularJShelpstosimplifytheprocessofinteractingwithbackendsystemsusingRESTservices.

AlthoughthisisnotabookonRESTservices,wewillcoverthebasicsofRESTservicesinChapter6,lookingindetailathowAngularJSconnectstotheseservicesandhowtointerfacewithJSONpayloads.Chapter7willprovideyouwithinformationonpublicRESTserviceendpointswrittenespeciallyforthisbookthatyoucanusetocompletethechapterexercises.

TheRESTservicesthatyouwilluseinChapter7arebuiltwithExpressJS,runonNode.js,anduseJSONasthedata-interchangeformat.Theservicesusedinthatandotherchaptersaredeployedtothecloudandopentoanyoneusingthisbookasalearningtool.Beforewegetintoallofthat,however,we’regoingtotakealookatAngularJScontrollers.

Page 69: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development
Page 70: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

Chapter4.AngularJSControllers

AngularJScontrollersareatthecenterofAngularJSapplicationsandareprobablythemostimportantcomponenttounderstand.ControllersarenotalwaysclearlydefinedinsomeJavaScriptclient-sideframeworks,andthattendstoconfusedeveloperswhohaveexperiencewithMVCframeworks.ThatisnotthecasewithAngularJS.AngularJSclearlydefinescontrollers,andcontrollersareatthecenterofAngularJSapplications.

AlmosteverythingthathappensinanAngularJSapplicationpassesthroughacontrolleratsomepoint.Dependencyinjectionisusedtoaddtheneededdependencies,asshowninthefollowingexamplefile,whichillustrateshowtocreateanewcontroller:

/*chapter4/controllers.js-anewcontroller*/

varaddonsControllers=

angular.module('addonsControllers',[]);

addonsControllers.controller('AddonsCtrl',

['$scope','checkCreds','$location','AddonsList','$http','getToken',

functionAddonsCtrl($scope,checkCreds,$location,AddonsList,

$http,getToken){

if(checkCreds()!==true){

$location.path('/loginForm');

}

$http.defaults.headers.common['Authorization']=

'Basic'+getToken();

AddonsList.getList({},

functionsuccess(response){

console.log("Success:"+

JSON.stringify(response));

$scope.addonsList=response;

},

functionerror(errorResponse){

console.log("Error:"+

JSON.stringify(errorResponse));

}

);

$scope.addonsActiveClass="active";

}]);

Inthiscode,wefirstcreateanewmodulenamedaddonsControllerbymakingacalltothemodulemethodofangular.Onthesecondline,wecreateanewcontrollernamedAddonsCtrlbycallingthecontrollermethodoftheaddonsControllersmodule.Doingthatattachesthenewcontrollertothatmodule.Allcontrollerscreatedinthecontrollers.jsfilewillbeaddedtotheaddonsControllersmodule.

Alsonoticethelineconsole.log("Success:"+JSON.stringify(response)).MostmodernbrowsershaveaccompanyingdevelopertoolsthatgivedeveloperseasyaccesstotheJavaScriptconsole.ThislineusestheJSON.stringifymethodtologtheJSONthat’sreturnedfromthewebservicetotheJavaScriptconsole.DeveloperscaneasilyusetheJavaScriptconsoletotroubleshootRESTserviceissuesbyviewingtheJSONloggedinthesuccesscallbackfunction,orintheerrorcallbackfunctionifaservicecallfails.

www.allitebooks.com

Page 71: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

MostdevelopertoolsandsomeIDEs,likeNetBeans,alsoincludeJavaScriptdebuggersthatallowdeveloperstoplacebreakpointsinboththesuccessanderrorcallbackfunctions.Doingsoallowsthedevelopertotakeafine-grainedapproachtotroubleshootingRESTservices.Quiteoften,thedevelopercanresolveotherwisecomplexRESTserviceissuesveryquicklybyusingaJavaScriptdebugger.

Thefollowingcodeisanexcerptofthepreviousfile.Itshowshowweusedependencyinjectiontoadddependenciestothenewcontroller.Thiscodeshows$scope,checkCreds,$location,AddonsList,$http,andgetTokensasdependenciesforthenewcontroller.Wehavealreadycoveredthe$scopebriefly.Fornowit’snotimportantwhattheotherdependenciesactuallyrepresent;youonlyneedtounderstandtheyarerequiredbythenewcontroller:

/*chapter4/controllers.jsexcerpt*/

/*usingdependencyinjection*/

['$scope','checkCreds','$location','AddonsList','$http','getToken',

functionAddonsCtrl($scope,checkCreds,$location,AddonsList,

$http,getToken){

}

Thiscontrollerplaysamajorroleintheapplicationinwhichitwasdefined.Controllersreallyhavetwoprimaryresponsibilitiesinanapplication.Wewilltakealookatthoseresponsibilitiesinmoredetailinthenextsection.

Page 72: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

InitializingtheModelwithControllersAngularJScontrollershavetwoprimarydutiesinanapplication.First,controllersshouldbeusedtoinitializethemodelscopeproperties.WhenacontrolleriscreatedandattachedtotheDOM,achildscopeiscreated.Thechildscopeholdsamodelusedspecificallyforthecontrollertowhichitisattached.Youcanaccessthechildscopebyusingthe$scopeobject.

CreateacopyoftheChapter2projectandnameitAngularJsHelloWorld_chapter4.Wewillusethisnewprojectfortherestofthischapter.YoucanalsodownloadtheprojectfromtheGitHubprojectsite.

Modelpropertiescanbeaddedtothescope,andonceaddedtheyareavailableinsidetheviewtemplates.Thecontrollercodeshownhereillustrateshowtoaddtwopropertiestothescope.Afteraddingthecustomernameandcustomernumbertothescope,bothareavailabletotheviewandcanbeaccessedwithdoublecurlybraces:

/*chapter4/controllers.jsexcerpt*/

helloWorldControllers.controller('CustomerCtrl',['$scope',

functionCustomerCtrl($scope){

$scope.customerName="Bob'sBurgers";

$scope.customerNumber="44522";

}]);

Nowaddthenewcontroller,CustomerCtrl,toyourproject’scontrollers.jsfile.Wewillmakeseveraladditionstothecontrollers.jsfileinthischapter.

Thefollowingviewtemplatecodeshowshowtoaccessthenewlyaddedmodelpropertiesinsidetheviewtemplate.Allpropertiesthatneedtobeaccessedfromtheviewshouldbeaddedtothe$scopeobject:

<!--chapter4/partials/customer.html-->

<div><b>CustomerName:</b>{{customerName}}</div>

<div><b>CustomerNumber:</b>{{customerNumber}}</div>

NowaddanewHTMLfileunderthepartialsfolderandnameitcustomer.html.Replacethegeneratedcodewiththecodejustshown.

Page 73: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

AddingBehaviorwithControllersThesecondprimaryuseforcontrollersisaddingbehaviortothe$scopeobject.Weaddbehaviorbyaddingmethodstothescope,asshowninthefollowingcontrollercode.Here,weattachachangeCustomermethodto$scopesothatitcanbeinvokedfrominsidetheview.Bydoingthis,weareaddingbehaviorthatallowsustochangethecustomernameandcustomernumber:

/*chapter4/controllers.jsexcerpt*/

helloWorldControllers.controller('CustomerCtrl',['$scope',

functionCustomerCtrl($scope){

$scope.customerName="Bob'sBurgers";

$scope.customerNumber=44522;

//addmethodtoscope

$scope.changeCustomer=function(){

$scope.customerName=$scope.cName;

$scope.customerNumber=$scope.cNumber;

};

}]);

AddthechangeCustomermethodshownheretotheCustomerCtrlcontrollerdefinedinyourcontrollers.jsfile.

Thefollowingcodeshowsthecustomer.htmlfileandthechangesneededintheviewtomakeuseofthenewbehaviorthatwasjustadded.Weaddtwonewpropertiestothemodelbyusingng-model="cName"andng-model="cNumber".Weuseng-click="changeCustomer();"toinvokethenewchangeCustomermethodthatisattachedtothescope:

<!--chapter4/partials/customer.html-->

<div><b>CustomerName:</b>{{customerName}}</div>

<div><b>CustomerNumber:</b>{{customerNumber}}</div>

<form>

<div>

<inputtype="text"ng-model="cName"required/>

</div>

<div>

<inputtype="number"ng-model="cNumber"required/>

</div>

<div>

<buttonng-click="changeCustomer();">ChangeCustomer</button>

</div>

</form>

Modifythecustomer.htmlfiletoincludethenewformdefinedhere.

OncethechangeCustomermethodisinvoked,thenewpropertiesareattachedto$scopeandavailabletothecontroller.Asyoucansee,wesimplyassignthetwonewproperties

Page 74: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

boundtothemodelbacktotheoriginaltwoproperties,customerNameandcustomerNumber,insidethechangeCustomermethod.Bothng-modelandng-clickareAngularJSdirectives.WewillcoverdirectivesindetailinChapter9.

Page 75: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

ControllerBusinessLogicControllersareusedasjustdemonstratedtoaddbusinesslogictoanapplication.Businesslogicaddedinthecontroller,however,shouldbespecifictotheviewassociatedwiththatonecontrollerandusedtosupportsomedisplaylogicfunctionalityofthatoneview.Anybusinesslogicthatcanbepushedofftheclient-sideapplicationshouldbeimplementedasaRESTserviceandnotactuallyinsidetheAngularJSapplication.

Thereisonecaveattothisconcept,however:RESTservicesmusthavearesponsetimeoftwo(2)secondsorless.Long-runningserviceswillonlycausedelaysintheUIandmakeforabaduserexperience.Meetingthetwo-seconds-or-lessrulerequireshavingRESTservicesthatareproperlydesignedandrunningonabackendsystemthatscaleswelltoloaddemandchanges.Thereareotherconcernsrelatedtomobileapplications,butwewillcoverthoseinChapter7andChapter8.

Businesslogicthatcan’tbeplacedinRESTservicesbutneedstobeavailabletomultiplecontrollersshouldnotbeplacedinthecontrollerbutshouldinsteadbeplacedinAngularJSnon-RESTservices.InChapter8,wewillcoverbusinesslogicservicesinmoredetail.Businesslogicthatisplacedinthecontrollershouldbesimplelogicthatrelatesonlytothecontrollerinwhichitisdefined.PlacingtoomuchbusinesslogicinsideanAngularJSapplicationwouldbeabaddesigndecision,however.

Page 76: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

PresentationLogicandFormattingDataPresentationlogicshouldnotbeplacedinsidethecontrollerbutinsteadshouldbeplacedintheview.AngularJShasmanyfeaturesforDOMmanipulationthathelpyouavoidplacingpresentationlogicinthecontrollers.Thecontrollerisalsonottheplacewhereyoushouldformatdata.AngularJShasfeaturesespeciallydesignedforformattingdata,andthat’swheredataformattingshouldtakeplace.Someofthosefeatureswillbecoveredindetailinthenextchapter.

Page 77: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

FormSubmissionNowwewilllookathowformsubmissionsarehandledinAngularJSusingcontrollers.ThefollowingcodeforthenewCustomer.htmlfileshowstheviewforanewform.CreateanewHTMLfileunderthepartialsfolderandreplacethegeneratedcodewiththecodelistedhere:

<!--chapter4/partials/newCustomer.html-->

<formng-submit="submit()"ng-controller="AddCustomerCtrl">

<div>

<inputtype="text"ng-model="cName"required/>

</div>

<div>

<inputtype="text"ng-model="cCity"required/>

</div>

<div>

<buttontype="submit">AddCustomer</button>

</div>

</form>

Asyoucansee,weusestandardHTMLfortheformwithnothingreallyspecialexceptthedirectives.Thedirectiveng-submitbindsthemethodnamedsubmit,definedintheAddCustomerCtrlcontroller,totheformforformsubmission.Theng-modeldirectivebindsthetwoinputelementstoscopeproperties.

Twoormorecontrollerscanbeappliedtothesameelement,andwecanusecontrollerastoidentifyeachindividualcontroller.Thefollowingcodeshowshowcontrollerasisused.YoucanseethataddCustidentifiestheAddCustomerCtrlcontroller.WeuseaddCusttoaccessthepropertiesandmethodsofthecontroller,asshown:

<!--chapter4/partials/newCustomer.html(withcontrolleras)-->

<formng-submit="addCust.submit()"

ng-controller="AddCustomerCtrlasaddCust">

<div>

<inputtype="text"ng-model="addCust.cName"required/>

</div>

<div>

<inputtype="text"ng-model="addCust.cCity"required/>

</div>

<div>

<buttonid="f1"type="submit">AddCustomer</button>

</div>

</form>

ThefollowingcodeshowstheAddCustomerCtrlcontrollerandhowweuseittohandlethesubmittedformdata.HereweusethepathmethodontheAngularJSservice$locationtochangethepathaftertheformissubmitted.Thenewpathishttp://localhost:8383/AngularJsHelloWorld_chapter4/index.html#!/addedCustomer/name/city

Page 78: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

Addthiscodetothecontrollers.jsfile:

/*chapter4/controllers.js*/

helloWorldControllers.controller('AddCustomerCtrl',

['$scope','$location',

functionAddCustomerCtrl($scope,$location){

$scope.submit=function(){

$location.path('/addedCustomer/'+$scope.cName+"/"+$scope.cCity);

};

}]);

That’sallthatisneededtohandletheformsubstitutionprocess.Wewillnowlookathowwegetaccesstothesubmittedvaluesinsideanothercontroller.

Page 79: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

UsingSubmittedFormDataTheapp.jsfileshownnextincludesthenewroutedefinitions.Modifytheapp.jsfileintheChapter3projectandaddthenewroutes.Makesureyourfilelookslikethefileshownhere:

/*chapter4/app.js*/

/*AppModule*/

varhelloWorldApp=angular.module('helloWorldApp',[

'ngRoute',

'helloWorldControllers'

]);

helloWorldApp.config(['$routeProvider','$locationProvider',

function($routeProvider,$locationProvider){

$routeProvider.

when('/',{

templateUrl:'partials/main.html',

controller:'MainCtrl'

}).when('/show',{

templateUrl:'partials/show.html',

controller:'ShowCtrl'

}).when('/customer',{

templateUrl:'partials/customer.html',

controller:'CustomerCtrl'

}).when('/addCustomer',{

templateUrl:'partials/newCustomer.html',

controller:'AddCustomerCtrl'

}).when('/addedCustomer/:customer/:city',{

templateUrl:'partials/addedCustomer.html',

controller:'AddedCustomerCtrl'

});

$locationProvider.html5Mode(false).hashPrefix('!');

}]);

Youcanseetherearetwopathparameters,customerandcity,fortheaddedCustomerroute.Thevaluesarepassedasargumentstoanewcontroller,AddedCustomerCtrl,showninthefollowingexcerpt.Weusethe$routeParamsserviceinthenewcontrollertogetaccesstothevaluespassedaspathparameterargumentsintheURL.Byusing$routeParams.customerwegetaccesstothecustomername,and$routeParams.citygetsusaccesstothecity:

/*chapter4/controllers.jsexcerpt*/

helloWorldControllers.controller('AddedCustomerCtrl',

['$scope','$routeParams',

functionAddedCustomerCtrl($scope,$routeParams){

$scope.customerName=$routeParams.customer;

$scope.customerCity=$routeParams.city;

}]);

Addthenewcontroller,AddedCustomerCtrl,toyourcontrollers.jsfilenow.

ThecodeforournewaddedCustomertemplateisshownnext.Onceagain,weuseAngularJSdoublecurlybracestogetaccesstoanddisplayboththecustomerNameandcustomerCitypropertiesintheview:

Page 80: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

<!--chapter4/addedCustomer.html-->

<div><b>CustomerName:</b>{{customerName}}</div>

<div><b>CustomerCity:</b>{{customerCity}}</div>

Toaddthetemplatetotheproject,createanewHTMLfileinthepartialsfolderandnameitaddedCustomer.html.Replacethegeneratedcodewiththecodejustshown.NotehowsimpleitistosubmitformswithAngularJS.SimplicityisoneofthefactorsthatmakesAngularJSagreatchoiceforanyJavaScriptclient-sideapplicationproject.

Page 81: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

JSTestDriverTherestofthischapterwillcoversettingupatestenvironmentandtestingAngularJScontrollers.NetBeanshasagreattestingenvironmentforbothJSTestDriverandKarma.WewillfocusfirstonsettingupJSTestDriverforunittesting.WewillthentakealookatKarmaforunittesting.Tobegin,dothefollowing:

1. DownloadtheJSTestDriverJAR.

2. IntheServicestab,right-click“JSTestDriver”andclick“Configure”(seeFigure4-1).

3. SelectthelocationoftheJSTestDriverJARjustdownloadedandchoosethebrowserofyourchoice(seeFigure4-2).

4. Right-clicktheprojectnode,thenclick“New”→“Other”→“UnitTests.”

5. Select“jsTestDriverConfigurationFile”andclick“Next.”

6. Makesurethefileisplacedintheconfigsubfolder,asshowninFigure4-3.

7. Makesurethecheckboxfor“DownloadandsetupJasmine”ischecked.

8. Click“Finish.”

9. Right-clicktheprojectnode,clickProperties,andselect“JavaScriptTesting.”

10. Select“jsTestDriver”fromthedrop-downbox.

Page 82: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

Figure4-1.Right-click“JSTestDriver”intheServicestab

Figure4-2.Selectyourbrowser(s)

Page 83: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

Figure4-3.Makesurethefileiscreatedintheconfigsubfolder

ThefollowingcodeshowstheJSTestDriverconfigurationfile.Insidethefile,wespecifytheserverURLthatisusedbyJSTestDriver.Wealsospecifytheneededlibraryfilesintheloadsectionofthefile,alongwiththelocationsofourJavaScriptfilesandtestscripts:

/*chapter4/jsTestdriver.conf*/

server:http://localhost:42442

load:

-test/lib/jasmine/jasmine.js

-test/lib/jasmine-jstd-adapter/JasmineAdapter.js

-public_html/js/libs/angular.min.js

-public_html/js/libs/angular-mocks.js

-public_html/js/libs/angular-cookies.min.js

-public_html/js/libs/angular-resource.min.js

-public_html/js/libs/angular-route.min.js

-public_html/js/*.js

-test/unit/*.js

exclude:

Noticewe’veaddedangular-mocks.jstothelistofrequiredAngularJSlibraryfiles.ThatfileisneededforunittestingAngularJSapplications.So,beforecontinuing,addtheangular-mocks.jsfiletothejs/libsfolder.

Page 84: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

CreatingTestScriptsNext,createanewJavaScriptfileintheunitsubfolderofthenewlycreatedUnitTestfolder,asshowninFigure4-4.NamethenewfilecontrollerSpec.js.

Figure4-4.CreatethecontrollerSpec.jsfileintheunitsubfolder

ThecontentsofthecontrollerSpec.jsfileareshownnext.OurtestscriptfilenamewillendwithSpec.ThefilespecifiesastandardsetofunittestscommonlyusedtotestAngularJScontrollers.Noticethatwehaveatestforeachofourcontrollersdefinedinthecontrollers.jsfile:

/*chapter4/controllerSpec.js*/

/*Jasminespecsforcontrollersgohere*/

describe('HelloWorld',function(){

beforeEach(module('helloWorldApp'));

describe('MainCtrl',function(){

varscope,ctrl;

beforeEach(inject(function($rootScope,$controller){

scope=$rootScope.$new();

ctrl=$controller('MainCtrl',{$scope:scope});

}));

it('shouldcreateinitialedmessage',function(){

expect(scope.message).toEqual("HelloWorld");

});

Page 85: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

});

describe('ShowCtrl',function(){

varscope,ctrl;

beforeEach(inject(function($rootScope,$controller){

scope=$rootScope.$new();

ctrl=$controller('ShowCtrl',{$scope:scope});

}));

it('shouldcreateinitialedmessage',function(){

expect(scope.message).toEqual("ShowTheWorld");

});

});

describe('CustomerCtrl',function(){

varscope,ctrl;

beforeEach(inject(function($rootScope,$controller){

scope=$rootScope.$new();

ctrl=$controller('CustomerCtrl',{$scope:scope});

}));

it('shouldcreateinitialedmessage',function(){

expect(scope.customerName).toEqual("Bob'sBurgers");

});

});

});

ThistestscriptusesJasmineasthebehavior-drivendevelopmentframeworkfortestingourcode.WewilluseJasmineforallourtestscriptsinthisbook.

Hereisthecompletecontrollers.jsfile:

/*chapter4/controllers.js*/

'usestrict';

/*Controllers*/

varhelloWorldControllers=

angular.module('helloWorldControllers',[]);

helloWorldControllers.controller('MainCtrl',['$scope',

functionMainCtrl($scope){

$scope.message="HelloWorld";

}]);

helloWorldControllers.controller('ShowCtrl',['$scope',

functionShowCtrl($scope){

$scope.message="ShowTheWorld";

}]);

helloWorldControllers.controller('CustomerCtrl',['$scope',

functionCustomerCtrl($scope){

$scope.customerName="Bob'sBurgers";

$scope.customerNumber=44522;

$scope.changeCustomer=function(){

$scope.customerName=$scope.cName;

$scope.customerNumber=$scope.cNumber;

};

}]);

helloWorldControllers.controller('AddCustomerCtrl',

['$scope','$location',

functionAddCustomerCtrl($scope,$location){

$scope.submit=function(){

$location.path('/addedCustomer/'+$scope.cName+"/"+$scope.cCity);

};

}]);

helloWorldControllers.controller('AddedCustomerCtrl',

['$scope','$routeParams',

Page 86: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

functionAddedCustomerCtrl($scope,$routeParams){

$scope.customerName=$routeParams.customer;

$scope.customerCity=$routeParams.city;

}]);

TIPTosavetime,youcandownloadtheChapter4codefromGitHub.ForacompleteguidetoJavaScripttestinginNetBeans,seethedocumentationatontheNetBeanswebsite.

Page 87: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

TestingwithJSTestDriverNowtoactuallytestthecontrollerswe’vedefined,justright-clicktheprojectnodeandselect“Test”fromthemenu.Ifyourprojectisconfiguredcorrectly,youshouldseeasuccessmessageforallthreecontrollersthatweretested.Ifyouhaveanyissueswiththetestresults,gobackovertheconfigurationfilesandvalidatethatallyourfilesmatchthoselistedinthischapter.Ifyoucontinuetohaveproblems,downloadandrunthesourcecodefromtheprojectsite.

Page 88: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

TestingwithKarmaKarmaisanewandfunwaytounittestAngularJSapplications.WewilluseKarmaheretotestthecontrollersthatwetestedearlier.

Page 89: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

InstallingKarmaKarmarunsonNode.js,asmentionedinChapter2,sofirstyoumustinstallNode.jsifit’snotalreadyinstalled.Refertonodejs.orgforinstallationdetailsforyourparticularoperatingsystem.You’llalsoneedtoinstalltheNode.jspackagemanager(npm)onyoursystem.npmisacommand-linetoolusedtoaddtheneededNode.jsmodulestoaproject.

Now,intherootoftheChapter4project,createaJSONfilenamedpackage.jsonandaddthefollowingcontent.Thepackage.jsonfileisusedasaconfigurationfileforNode.js:

{

"name":"package.json",

"devDependencies":{

"karma":"*",

"karma-chrome-launcher":"*",

"karma-firefox-launcher":"*",

"karma-jasmine":"*",

"karma-junit-reporter":"*",

"karma-coverage":"*"

}

}

Openacommand-linewindowonyoursystem,andnavigatetotherootoftheChapter4project.Youshouldseethepackage.jsonfilewhenyoulistoutthefilesinthefolder.

TypethiscommandtoactuallyinstalltheNode.jsdependenciesdefinedinthepackage.jsonfile:

npminstall

NowinstalltheKarmacommand-lineinterface(karma-cli)bytypingthefollowingcommand:

npminstall-gkarma-cli

WARNINGMakesuretorecordthelocationwherekarma-cliwasinstalled.Youwillneedthelocationlaterinthischapter.

Thiscommandinstallsthecommand-linetoolgloballyonyoursystem.

AlltheNode.jsdependenciesspecifiedinthepackage.jsonfilewillbeinstalledunderthenode_modulesfolderinsidetheprojectrootfolder.Ifyoulistoutthefilesandfolders,youshouldseethenewfolder.Youwon’tbeabletoseethenewfolderinsideNetBeans,however.

Page 90: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

KarmaConfigurationNext,createanewKarmaconfigurationfilenamedkarma.conf.jsinsidetheprojecttestfolder.Dothefollowing:

1. Right-clicktheprojectinNetBeans.

2. Select“New”→“Other”→“UnitTests.”

3. CreateanewKarmaconfigurationfileinsidethetestfolder.

Editthenewkarma.conf.jsfileandaddthefollowingcode:

/*chapter4/karma.conf.js*/

module.exports=function(config){

config.set({

basePath:'../',

files:[

"public_html/js/libs/angular.min.js",

"public_html/js/libs/angular-mocks.js",

"public_html/js/libs/angular-route.min.js",

"public_html/js/*.js",

"test/**/*Spec.js"

],

exclude:[

],

autoWatch:true,

frameworks:[

"jasmine"

],

browsers:[

"Chrome",

"Firefox"

],

plugins:[

"karma-junit-reporter",

"karma-chrome-launcher",

"karma-firefox-launcher",

"karma-jasmine"

]

});

};

NowdothefollowingtosetKarmaasthetestframework:

1. Right-clicktheproject.

2. Select“Properties.”

3. Select“JavaScriptTesting”fromthelistofcategories.

4. Select“Karma”asthetestingprovider.

5. Selectthelocationofthekarma-clitoolinstalledearlier.

6. Selectthelocationofthekarma.conf.jsfilejustcreated.

7. Select“OK.”

Page 91: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

RunningKarmaUnitTestsNowtoactuallyruntheunittests(usingthetestspecificationwrittenearlier)underKarma,right-clicktheprojectandselect“Test”fromthemenu.Karmawillstart.YoushouldseebothChromeandFirefoxbrowserwindowsopen.TheNetBeanstestresultswindowshouldopenanddisplaythreepassedtestsforChromeandthreepassedtestsforFirefox.

Ifyougetanyerrormessagesorfailedtests,gobackoverthissectionandverifythatyoucompletedalltheconfigurationsandinstallations.YoucanalsodownloadtheChapter4codefromtheGitHubprojectsite.

Page 92: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

End-to-EndTestingwithProtractorProtractorisanewtestframeworkforrunningend-to-end(E2E)tests.Protractorletsyourunteststhatexercisetheapplicationasauserwould.WithProtractorE2Etesting,youcantestvariouspages,navigatethrougheachpagefromwithinthetestscript,andfindanypotentialdefects.Protractoralsointegrateswithmostcontinuousintegrationbuildsystems.

Page 93: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

InstallingProtractorLikeKarma,ProtractorisaNode.js-basedtestframework.TheProtractorteamrecommendsinstallingProtractorglobally.Todoso,openacommand-linewindowandtypethecommand:

npminstall-gprotractor

ProtractorreliesonWebDriverJS,sowewillalsousethiscommandtoupdateWebDriverJSwiththelatestlibraries:

webdriver-managerupdate

Page 94: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

ConfiguringProtractorNext,wewillcreateaProtractorconfigurationfileforourproject.CreateanewJavaScriptfilenamedconf.jsunderthetestfolderoftheChapter4project.Enterthecodeshownhereinthenewfile:

/*chapter4/conf.js*/

exports.config={

seleniumAddress:'http://localhost:4444/wd/hub',

specs:['e2e/Hw-spec.js']

};

Page 95: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

CreatingProtractorTestSpecificationsNowweneedtocreateaProtractortestspecification.Dothefollowing:

1. Createanewfolderunderthetestfolderoftheprojectandnameite2e.

2. CreateanewJavaScriptfileinsidethenewe2efolderandnameitHw-spec.js.

NowcopythecodeshownhereintothenewHw-spec.jsfile:

/*chapter4/Hw-spec.jsProtractortestspecification*/

describe("HelloWorldTest",function(){

it("shouldtestthemainpage",function(){

browser.get(

"http://localhost:8383/AngularJsHelloWorld_chapter4/");

expect(browser.getTitle()).toEqual("AngularJSHelloWorld");

varmsg=element(by.binding("message")).getText();

expect(msg).toEqual("HelloWorld");

browser.get(

"http://localhost:8383/AngularJsHelloWorld_chapter4/#!/show");

expect(browser.getTitle()).toEqual("AngularJSHelloWorld");

varmsg=element(by.binding("message")).getText();

expect(msg).toEqual("ShowTheWorld");

browser.get(

"http://localhost:8383/AngularJsHelloWorld_chapter4/#!/

addCustomer");

element(by.model("cName")).sendKeys("tester");

element(by.model("cCity")).sendKeys("Atlanta");

element(by.id("f1")).click();

browser.get(

"http://localhost:8383/

AngularJsHelloWorld_chapter4/#!/addedCustomer/tester/Atlanta");

varmsg=element(by.binding("customerName")).getText();

expect(msg).toEqual("CustomerName:tester");

varmsg=element(by.binding("customerCity")).getText();

expect(msg).toEqual("CustomerCity:Atlanta");

});

});

Page 96: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

StartingtheSeleniumServerWebDriverJSrunsontheSeleniumServer.TostarttheSeleniumServerthatrunsProtractortests(usingthewebdriver-managertool),openanewcommandwindowandenterthefollowingcommand:

webdriver-managerstart

Page 97: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

RunningProtractorNowthattheSeleniumServerisrunning,wecanrunourProtractortests.Openanewcommandwindow,navigatetotherootoftheChapter4project,andtypethiscommand:

protractortest/conf.js

Youshouldseeabrowserwindowopen.YoushouldthenseethetestscriptnavigatethroughthepagesoftheChapter4application.Ifyouwatchthebrowserwindowclosely,youwillseethescriptentervaluesintheformthataddsanewcustomer.WhentheProtractorscripthasfinished,thebrowserwindowwillclose.

YoushouldseeresultslikethefollowinginthecommandwindowwhentheProtractorscriptcompletes.Thenumberofsecondsthatittakesthescripttofinishwillvarydependingonyourparticularsystem:

Finishedin3.368seconds

1test,6assertions,0failures

NOTEFormoreinformationontestingwithProtractor,seetheprojectsiteonGitHub.Protractorhasacompletesetofdocumentationtohelpyougetstarted.

Page 98: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

ConclusionUnittestingAngularJScontrollersallowsustovalidatethebasicfunctionalityofeachcontroller.Fornow,ourtestsareverysimple.TestingacontrollerthatretrievesdatafromaRESTservice,forexample,wouldbeamorecomplextask.

End-to-endtestingisabitmoreinvolved,andcanbedesignedtocompletelyexercisetheentireapplication.Fornow,ourE2Etestsarealsosimple.E2EtestshelptoidentifysoftwaredefectsearlyinthedevelopmentprocesswhenusedwithCIbuildsystems.

We’llbedoingmoretestinginthenextchapter,wherewefocusonAngularJSviews.

Page 99: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development
Page 100: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

Chapter5.AngularJSViewsandBootstrap

WewillnowstartanewAngularJSblogprojectthatusespublicRESTservicescreatedespeciallyforthisbook.Wewillworkontheblogprojectfortherestofthisbook.YoucanalsodownloadtheprojectcodefromGitHub.Wewillstartoffbybuildingtheviewsandthecontrollersforthoseviews.

TwitterBootstrapisafreecollectionofHTMLandCSStemplates.WewillbuildtheAngularJSviewswiththehelpofTwitterBootstraptohelpcutdevelopmenttime.Oncewehavetheviewsandcontrollersinplaceandunderstandtheiroperation,wewillfocusonthemodelandRESTservices(inthenexttwochapters).

Page 101: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

AngularJSTemplatesAngularJSviewsaredefinedbybuildingtemplates(partials).ViewsinAngularJSarecomposedofHTMLcodewithdirectivesadded,suchastheng-modeldirectiveshownpreviously.AngularJSbuildstheviewsdynamicallyatruntimebymergingthetemplateswiththepropertiespassedtothetemplatesinthe$scopeobject.TheendresultispureHTMLcodeboundtotheng-viewdirective,asexplainedbackinChapter1.Wewillcovertheng-viewdirectiveagaininthischapterasareview.

Page 102: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

CreatingtheBlogProjectStartanewHTML5projectinNetBeansandcallitAngularJsBlog.SetupthefolderstructureasshowninFigure5-1.MovethedownloadedAngularJS,jQuery,andBootstraplibraryfilestothejs/libsfolder,asshown.

Figure5-1.Blogprojectfolderstructure

We’llbeginwiththecodefortheindex.htmlfile.Asyoucansee,weloadtheneededlibraryfileswiththe<script>taginthe<head>sectionofthepage.Thetag<divng-view></div>iswherealldynamiccontentisinserted.Astheuserclicksonlinksintheapplication,existingcontentattachedtothetagisremovedandnewdynamiccontentisthenattachedtothatsametag:

<!--chapter5/index.html-->

<!DOCTYPEhtml>

<htmllang="en"ng-app="blogApp">

<head>

<title>AngularJSBlog</title>

Page 103: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

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

<metahttp-equiv="Content-Type"content="text/html;charset=UTF-8">

<scriptsrc="js/libs/jquery-1.10.2.min.js"></script>

<scriptsrc="js/libs/angular.min.js"></script>

<scriptsrc="js/libs/angular-route.min.js"></script>

<scriptsrc="js/libs/angular-resource.min.js"></script>

<scriptsrc="js/libs/angular-cookies.min.js"></script>

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

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

</head>

<body>

<divng-view></div>

</body>

</html>

Page 104: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

AddingaNewBlogControllerNextwewillsetupthecontrollersforournewblogapplication.ThefollowingcodedefinestheblogControllersmoduleandtheBlogCtrlcontrollerforthatmodule.WewilldefinemorecontrollersontheblogControllersmoduleasweworkontheblogapplication.Fornow,thecontrollers.jsfileisrelativelysmall:

/*chapter5/controllers.js*/

'usestrict';

/*Controllers*/

varblogControllers=

angular.module('blogControllers',[]);

blogControllers.controller('BlogCtrl',['$scope',

functionBlogCtrl($scope){

$scope.blogArticle=

"ThisisablogpostaboutAngularJS.

Wewillcoverhowtobuildablogandhowtoadd

commentstotheblogpost.";

}]);

Nextisthecodefortheapp.jsfilethatstartsthebootingprocessfortheblogapplication.Thisiswherewedefinetherouteforthemainpageoftheblog.Asyoucansee,wedefinengRouteandblogControllersasdependenciesoftheapplicationatstartuptime,usinginlinearrayannotations.ThetwodependenciesareinjectedintotheapplicationusingDIandareavailablethroughouttheapplicationwhenweneedthem.AnycontrollersattachedtotheblogControllersmoduleareaccessibletotheblogAppmodule(theAngularJSapplication):

/*chapter5/app.js*/

'usestrict';

/*AppModule*/

varblogApp=angular.module('blogApp',[

'ngRoute',

'blogControllers'

]);

blogApp.config(['$routeProvider','$locationProvider',

function($routeProvider,$locationProvider){

$routeProvider.

when('/',{

templateUrl:'partials/main.html',

controller:'BlogCtrl'

});

$locationProvider.html5Mode(false).hashPrefix('!');

}]);

Theroutesaredefinedintheapplicationconfigurationblock.Fornow,wewillonlydefinethemainpageoftheblog.WedefineBlogCtrlasthecontrollerand'partials/main.html'asthetemplateusedforthemainroute.Wewilladdmoreroutesasweneedthem.

Page 105: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

AddingaNewBlogTemplateNowwewilladdasimpletemplatefileandtestruntheapplicationbeforeaddingcodetothetemplate.Right-clicktheNetBeansprojectfolderandaddanewHTMLpagenamedmain.htmlinthepartialsfolder.ReplacethegeneratedHTMLcodewiththecodeshownhere:

<!--chapter5/main.html-->

{{blogArticle}}

Right-clicktheprojectfolderandselect“Run”fromthemenu.Ifyousetuptheprojectcorrectly,thebrowsershouldopenwiththefollowingtextdisplayed:“ThisisablogpostaboutAngularJS.Wewillcoverhowtobuildablogandhowtoaddcommentstotheblogpost.”Thistellsusourapplicationisproperlyconfigured.NowwewilluseTwitterBootstrapandHTMLtobuildamenuandmainpageforourblog.

Page 106: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

TwitterBootstrapYoushouldhavealreadyaddedbootstrap.min.jstotheproject.IfyourunintoJavaScripterrorsrelatedtoTwitterBootstrap,youcaneasilyreplacethebootstrap.min.jsfilewiththenonminifiedbootstrap.jsfiledistributedbyTwitter.UsingthenonminifiedversionofthefileallowsthedevelopertoplacebreakpointsintheBootstrapJavaScriptfileanddebuganyrelatedissues.WewillonlycoverthebasicsofTwitterBootstraphere.FormoredocumentationandtutorialsonBootstrap,seetheprojectsite.

First,weneedtoaddthreemorefoldersandsomeadditionalTwitterBootstrapfilestotheproject.WewilladdalltheBootstrapfileshere,althoughmuchofBootstrapisnotactuallyusedinthisproject.Dothefollowing:

1. AddasubfoldernamedcssundertheSiteRootfolder.

2. AddasubfoldernamedfontsundertheSiteRootfolder.

3. Addasubfoldernamedlib-cssundertheSiteRootfolder.

4. Copythebootstrap-theme.min.cssandbootstrap.min.cssfilesintothelib-cssfolder.

5. Copythefollowingfilestothefontsfolder:a. glyphicons-halflings-regular.eot

b. glyphicons-halflings-regular.svg

c. glyphicons-halflings-regular.ttf

d. glyphicons-halflings-regular.woff

6. Addthetwolinesofcodeshownnexttotheindex.htmlfile.ThesetwolinesareallthatweneedtomakeuseofTwitterBootstrap:

<!--chapter5/index.htmlexcerpt-->

<linkrel="stylesheet"href="lib-css/bootstrap.min.css"media="screen"/>

<scriptsrc="js/libs/bootstrap.min.js"></script>

Hereisthecompletedindex.htmlfile:

<!--chapter5/index.htmlcompletefile-->

<!DOCTYPEhtml>

<htmllang="en"ng-app="blogApp">

<head>

<title>Blog</title>

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

<metahttp-equiv="Content-Type"content="text/html;charset=UTF-8">

<linkrel="stylesheet"href="lib-css/bootstrap.min.css"media="screen"/>

<scriptsrc="js/libs/bootstrap.min.js"></script>

Page 107: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

<scriptsrc="js/libs/jquery-1.10.2.min.js"></script>

<scriptsrc="js/libs/angular.min.js"></script>

<scriptsrc="js/libs/angular-route.min.js"></script>

<scriptsrc="js/libs/angular-resource.min.js"></script>

<scriptsrc="js/libs/angular-cookies.min.js"></script>

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

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

</head>

<body>

<divng-view></div>

</body>

</html>

Figure5-2showstheprojectfilestructure.Makesureyourprojectissetupasshown.TheaddedCSSfilesandfontswillgiveusaccesstomanytime-savingfeaturesofTwitterBootstrap.WewillnowaddaBootstrapmenutoourproject.

Figure5-2.Thecompletedfilestructurefortheblogproject

Page 108: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

AddingaBootstrapMenuThefollowingarethecontentsofthemenu.htmlfile.MostofthecodeshownisclearlyexplainedontheBootstrapprojectsite.Thestylesaddedtothemenuherearedefinedinthebootstrap.min.cssfileaddedintheprevioussection.IfyouhavequestionsonBootstrapmenus,pleaserefertotheBootstrapprojectdocumentationforafullerexplanation.Yourmenu.htmlfileshouldlooklikethis:

<!--chapter5/menu.html-->

<navclass="navbarnavbar-inversenavbar-fixed-top"role="navigation">

<!--Brandandtogglegetgroupedforbettermobiledisplay-->

<divclass="container">

<divclass="navbar-header">

<buttontype="button"class="navbar-toggle"data-toggle="collapse"

data-target=".navbar-collapse">

<spanclass="sr-only">Togglenavigation</span>

<spanclass="icon-bar"></span>

<spanclass="icon-bar"></span>

<spanclass="icon-bar"></span>

</button>

<aclass="navbar-brand"style="{{brandColor}}"href="#!/">AngularBlog</a>

</div>

<!--Collectthenavlinks,forms,andothercontentfortoggling-->

<divclass="collapsenavbar-collapse">

<ulclass="navnavbar-nav">

<liclass="{{aboutActiveClass}}"><ahref="#!about">About</a></li>

<liclass="">

<ahref="https://github.com/KenWilliamson">DownloadProjectCode</a></li>

</ul>

</div><!--/.navbar-collapse-->

</div>

</nav>

Here’showtoaddthemenu.htmlfileinsidethemain.htmlfile:

<!--chapter5/main.html-->

<divng-includesrc="'partials/menu.html'"></div>

{{blogArticle}}

Thefirstlineshowstheneededadditiontomain.html.Asyousee,weusetheng-includedirectivetoincludethemenutemplateinsidethemaintemplate.Thisapproachallowsustokeepthemenucompletelyseparatefromtheothertemplates.Usingthisapproachmakesthecodebaseeasytomaintainandunderstand.WewillnowfocusonusingotherBootstrapstylestoenhanceourblog.

Page 109: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

AddingMockBlogDataWewillmodifytheBlogCtrlcontrollerandsetalistofblogpostsasascopepropertynamedblogList.Themodifiedcontrollers.jscodeisshownhere.TheJSONlistrepresentsthedatathatwilleventuallyberetrievedfromaRESTservice.Fornow,however,wewilljusthardcodetheJSONintothecontrollerasmockdata.TherearemoreadvancedwaystoaddmockdatatoanAngularJSapplication,butthatisbeyondthescopeofthisbook.Let’stakealookatthecontrollersfile:

/*chapter5/controllers.js*/

'usestrict';

/*Controllers*/

varblogControllers=

angular.module('blogControllers',[]);

blogControllers.controller('BlogCtrl',['$scope',

functionBlogCtrl($scope){

$scope.blogList=[

{

"_id":1,

"date":1400623623107,

"introText":"ThisisablogpostaboutAngularJS.

Wewillcoverhowtobuild",

"blogText":"ThisisablogpostaboutAngularJS.

Wewillcoverhowtobuildablogandhowtoadd

commentstotheblogpost."

},

{

"_id":2,

"date":1400267723107,

"introText":"Inthisblogpostwewilllearnhowto

buildapplicationsbasedonREST",

"blogText":"Inthisblogpostwewilllearnhowto

buildapplicationsbasedonRESTwebservicesthat

containmostofthebusinesslogicneededforthe

application."

}

];

}]);

Asyoucansee,thereisnopresentationlogicinthiscode,andnodataformattingisdoneinthecontroller.Thedate,forinstance,issenttotheviewasalongvaluethatisastandardrepresentationofadateinmostprogramminglanguages.Tryingtoformatthedateinthecontrollerwouldbeanincorrectdesignthatshouldn’tbeused.AngularJShasmanyfeaturesthatmakeformattingandpresentingdataeasy;we’lllookatsomeofthesenext.

Page 110: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

UsingCSS3toStylethePageNowwewilladdsomeCSS3tostyleourpages.Dothefollowing:

1. Right-clicktheprojectnodeandcreateanewCSSfilenamedstyle.css.

2. PlacethefollowingcodeintothenewCSSfile:

/*chapter5/styles.css*/

body{

font-family:arial;

font-size:12pt;

color:#2a6496;

}

.post-wrapper{

float:left;

width:100%;

margin:5%000;

padding:0000;

}

.blog-post-label{

float:left;

width:100%;

margin:10%000;

padding:0000;

text-align:center;

font-weight:bold;

font-size:16pt;

}

.blog-post-outer{

float:left;

width:60%;

margin:2%02%20%;

padding:1%;

background:#e0e0e0;

border-radius:6px;

-moz-border-radius:6px;/*Firefox3.6andearlier*/

border:darkgreensolid1px;

}

.blog-intro-text{

float:left;

width:100%;

margin:0000;

padding:0000;

text-align:center;

}

.blog-read-more{

float:left;

width:100%;

margin:2%000;

padding:0000;

text-align:center;

}

Nowmodifytheindex.htmlfile,addingthelineshownheretoloadthenewlycreatedCSSfile:

<!--chapter5/index.htmlexcerpt-->

<linkrel="stylesheet"href="css/styles.css"media="screen"/>

Page 111: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

Thecompleteindex.htmlfileisshownhere.Makesureyourversionofthefilematchesthisone:

<!--chapter5/index.htmlcompletefile-->

<!DOCTYPEhtml>

<htmllang="en"ng-app="blogApp">

<head>

<title>Blog</title>

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

<metahttp-equiv="Content-Type"content="text/html;charset=UTF-8">

<linkrel="stylesheet"href="lib-css/bootstrap.min.css"media="screen"/>

<scriptsrc="js/libs/jquery-1.10.2.min.js"></script>

<scriptsrc="js/libs/bootstrap.min.js"></script>

<scriptsrc="js/libs/angular.min.js"></script>

<scriptsrc="js/libs/angular-route.min.js"></script>

<scriptsrc="js/libs/angular-resource.min.js"></script>

<scriptsrc="js/libs/angular-cookies.min.js"></script>

<linkrel="stylesheet"href="css/styles.css"media="screen"/>

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

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

</head>

<body>

<divng-view></div>

</body>

</html>

Page 112: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

AddingStylesandPresentationLogicYoumustmodifythemain.htmltemplatetomakeuseofthenewstylesandtoaddproperpresentationlogicfordisplayingblogpostsandformattingdata.Modifyyourmain.htmltomatchthecodeshownhere.Thesecondline,<divid="container"class="container">,setsupaBootstrapcontainerandisstandardpracticewithTwitterBootstrap:

<!--chapter5/main.html-->

<divng-includesrc="'partials/menu.html'"></div>

<divid="container"class="container">

<divclass="blog-post-label">BlogPosts</div>

<divclass="post-wrapper">

<divng-repeat="blogPostinblogList">

<divclass="blog-post-outer">

<divclass="blog-intro-text">

Posted:{{blogPost.date|date:'MM/dd/yyyy@h:mma'}}

</div>

<divclass="blog-intro-text">

{{blogPost.introText}}

</div>

<divclass="blog-read-more">

<ahref="#!blogPost/{{blogPost._id}}">ReadMore</a>

</div>

</div>

</div>

</div>

</div>

TheBootstrapcontainerhandlesmuchofthepagestylingforvariousscreensizestomakethepageresponsiveforanyscreensizeonanydevice.InsidethecontainerweusetheCSSthatwasaddedinthestyles.cssfile.Wewon’tfocusmuchonthecustomCSS,becauseitisnotspecifictoAngularJSandiscoveredinmanyotherbooksonCascadingStyleSheets.

Wewill,however,takealookattheAngularJSdirectivesthatallowustobuildthepresentationlogicintheviewandhandleformatting.Theline<divng-repeat="blogPostinblogList">isveryimportanttounderstandingAngularJSviews.Thedirectiveng-repeatworkslikeaforloop,iteratingoverthelistofblogpostsinthescopepropertyblogList.

EachiterationthroughthelistgivesaccesstoeachiteminthelistthroughthevariableblogPost.Weusetheline{{blogPost.introText}}todisplaytheintrotext(thevalueoftheintroTextpropertyoftheblogPostvariable).

AnotherlinethatisveryimportantistheHTMLtemplatebinding{{blogPost.date|date:'MM/dd/yyyy@h:mma'}},whichallowsustoformatthedateintheview,whereitshouldbeformatted.AsIstatedpreviously,therearemanyfeaturesofAngularJSforformattingdata,andthisisjustone.Asyoucansee,thetemplatecodeissimpleandeasy

Page 113: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

tounderstand.

Wewillnowaddacontroller,route,andviewtodisplaytheindividualblogpostwhenauserclicksonthe“ViewMore”link.Ifyoulookclosely,youcanseethatthelinkpassesblogPost.idasapathparameterargumenttoanewroute,/blogPost.Wewillnowaddtheneededcodetoviewablogpost.

Page 114: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

ViewingtheBlogPostToaddtheextrafunctionality,firstappendthisCSScodetotheendofthestyles.cssfile:

/*chapter5/styles.cssexcerpt*/

.blog-entry-wrapper{

float:left;

width:100%;

margin:1%000;

padding:0000;

}

.blog-entry-outer{

float:left;

width:60%;

margin:2%02%20%;

padding:1%;

background:#e0e0e0;

border-radius:6px;

-moz-border-radius:6px;/*Firefox3.6andearlier*/

border:darkgreensolid1px;

}

.blog-comment-wrapper{

float:left;

width:50%;anHTML5project

margin:2%02%25%;

padding:1%;

border-radius:6px;

-moz-border-radius:6px;/*Firefox3.6andearlier*/

border:darkgreensolid1px;

}

.blog-entry-comments{

float:left;

width:96%;

margin:2%02%2%;

padding:1%;

background:#f5e79e;

border-radius:6px;

-moz-border-radius:6px;/*Firefox3.6andearlier*/

border:darkgreensolid1px;

}

.blog-comment-label{

float:left;

width:100%;

margin:1%000;

padding:0000;

text-align:center;

font-weight:bold;

font-size:16pt;

}

Thenaddthiscodetothebottomofthecontrollers.jsfile:

/*chapter5/controllers.jsexcerpt*/

blogControllers.controller('BlogViewCtrl',

['$scope','$routeParams',

functionBlogViewCtrl($scope,$routeParams){

varblogId=$routeParams.id;

varblog1={

"_id":1,

"date":1400623623107,

"introText":"ThisisablogpostaboutAngularJS.

Wewillcoverhowtobuild",

"blogText":"ThisisablogpostaboutAngularJS.

Page 115: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

Wewillcoverhowtobuildablogandhowtoadd

commentstotheblogpost.",

"comments":[

{

"commentText":"Verygoodpost.Iloveit."

},

{

"commentText":"Whencanwelearnservices."

}

]

};

varblog2={

"_id":2,

"date":1400267723107,

"introText":"Inthisblogpostwewilllearnhowto

buildapplicationsbasedonREST",

"blogText":"Inthisblogpostwewilllearnhowto

buildapplicationsbasedonRESTwebservicesthat

containmostofthebusinesslogicneededfortheapplication.",

"comments":[

{

"commentText":"RESTisgreat.Iwanttoknowmore."

},

{

"commentText":"WillweuseNode.jsforRESTservices?."

}

]

};

if(blogId==='1'){

$scope.blogEntry=blog1;

}elseif(blogId==='2'){

$scope.blogEntry=blog2;

}

}]);

Next,addanewtemplatefilenamedblogPost.htmlinthepartialsfolderandreplacethegeneratedcodewiththecodeshownhere:

<!--chapter5/blogPost.html-->

<divng-includesrc="'partials/menu.html'"></div>

<divid="container"class="container">

<divclass="blog-post-label">BlogEntry</div>

<divclass="blog-entry-wrapper">

<divclass="blog-intro-text">

Posted:{{blogEntry.date|date:'MM/dd/yyyy@h:mma'}}

</div>

<divclass="blog-entry-outer">

{{blogEntry.blogText}}

</div>

<divclass="blog-comment-wrapper">

<divclass="blog-comment-label">BlogComments</div>

<divclass="blog-entry-comments"ng-repeat="commentin

blogEntry.comments">

{{comment.commentText}}

</div>

</div>

</div>

</div>

Page 116: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

Andaddthiscodetotherouteprovidersectionofapp.js:

/*chapter5/app.jsexcerpt*/

.when('/blogPost/:id',{

templateUrl:'partials/blogPost.html',

controller:'BlogViewCtrl'

Thecompleteroutedefinitionisshownhere:

/*chapter5/app.jsexcerpt-completeroute*/

blogApp.config(['$routeProvider','$locationProvider',

function($routeProvider,$locationProvider){

$routeProvider.

when('/',{

templateUrl:'partials/main.html',

controller:'BlogCtrl'

}).when('/blogPost/:id',{

templateUrl:'partials/blogPost.html',

controller:'BlogViewCtrl'

});

$locationProvider.html5Mode(false).hashPrefix('!');

}]);

Asyoucansee,theeffortrequiredtoaddanewpagewasminimal.Ifyoulookattheroutedefinition,you’llseetheidpassedasapathparameterargument.Lookatthenewcontrollerandyoucanseehowwehandletheidparameter.SincewedonotyethaveRESTservicesinplace,wehardcodedtheJSONforthetwoblogpostsintothecontroller.

Onceweretrievethepassedidfrom$routeParams,weusethattodeterminewhichblogentrytosetasascopeproperty.Noticethatweneveractuallysetascopepropertyuntilweknowwhichblogentrygetssenttotheview.Noticealsothatblog1andblog2aredefinedaslocalvariables.Onlythevariablesneededinthepagearesetasscopeproperties.

WARNINGYoushouldneveraddpropertiestothescopethatarenotneededintheview.

Page 117: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

RunningtheBlogApplicationNowlet’sruntheprojecttotestourwork.Right-clicktheprojectnodeandselect“Run”fromthemenu.Ifyoumadeallthechangescorrectly,youshouldseethescreenshowninFigure5-3.Ifyougetadifferentresult,gobackoverthechangesinthischapterandverifythatyoumadealltheneededmodifications.

Figure5-3.Successfulresultfromrunningtheproject

Ifyouhaveproblemsthatyoucan’tresolve,downloadtheprojectcodefromGitHubandrunthatcode.Oncetheprojectisrunning,clickthe“ReadMore”linkonthefirstblogpost.YoushouldthenseethescreenshowninFigure5-4.Clickthe“ReadMore”linkonthesecondblogpost,andyoushouldseeasimilarpage.

Page 118: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

Figure5-4.Viewingthecommentsonthefirstblogpost

Page 119: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

TestingwithKarmaWewilluseKarmanowtotestourview.FromtherootoftheChapter5project,createaJSONfilenamedpackage.jsonandaddthefollowingcontents.Thepackage.jsonfileisusedasaconfigurationfileforNode.js,asmentionedinChapter4:

{

"name":"package.json",

"devDependencies":{

"karma":"*",

"karma-chrome-launcher":"*",

"karma-firefox-launcher":"*",

"karma-jasmine":"*",

"karma-junit-reporter":"*",

"karma-coverage":"*"

}

}

Openacommand-linewindowonyoursystem,andnavigatetotherootoftheChapter5project.Youshouldseethepackage.jsonfilewhenyoulistoutthefilesinthefolder.NowtypethefollowingcommandtoinstalltheNode.jsdependenciesdefinedinthepackage.jsonfile.ThisisthesameprocessdescribedinChapter4:

npminstall

Page 120: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

KarmaConfigurationNowwewillcreateanewKarmaconfigurationfilenamedkarma.conf.jsinsidetheproject’stestfolder,aswedidinChapter4.Dothefollowing:

1. Right-clicktheprojectinNetBeans.

2. Select“New”→“Other”→“UnitTests.”

3. CreateanewKarmaconfigurationfileinsidethetestfolder.

Editthenewkarma.conf.jsfileandaddthecodeshownhere:

/*chapter5/karma.conf.js*/

module.exports=function(config){

config.set({

basePath:'../',

files:[

"public_html/js/libs/angular.min.js",

"public_html/js/libs/angular-mocks.js",

"public_html/js/libs/angular-route.min.js",

"public_html/js/*.js",

"test/**/*Spec.js"

],

exclude:[

],

autoWatch:true,

frameworks:[

"jasmine"

],

browsers:[

"Chrome",

"Firefox"

],

plugins:[

"karma-junit-reporter",

"karma-chrome-launcher",

"karma-firefox-launcher",

"karma-jasmine"

]

});

};

NowdothefollowingtoconfigureKarmaasthetestframework:

1. Right-clicktheproject.

2. Select“Properties.”

3. Select“JavaScriptTesting”fromthelistofcategories.

4. Select“Karma”asthetestingprovider.

5. Selectthelocationofthekarma-clitoolinstalledinChapter4.

6. Selectthelocationofthekarma.conf.jsfilejustcreated,andselect“OK.”

Page 121: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

KarmaTestSpecificationsNowweneedtoaddnewtestspecificationsfortheChapter5project.Dothefollowing:

1. Createanewfoldernamedunitunderthetestfolderoftheproject.

2. CreateanewJavaScriptfilenamedcontrollerSpec.jsundertheunitfolder.

3. Enterthecodeshownhereinthenewfile:

/*chapter5/controllerSpec.js*/

describe('AngularJSBlogApplication',function(){

beforeEach(module('blogApp'));

describe('BlogCtrl',function(){

varscope,ctrl;

beforeEach(inject(function($rootScope,$controller){

scope=$rootScope.$new();

ctrl=$controller('BlogCtrl',{$scope:scope});

}));

it('shouldcreateshowblogentrycount',function(){

console.log("blogList:"+scope.blogList.length);

expect(scope.blogList.length).toEqual(2);

});

});

describe('BlogViewCtrl',function(){

varscope,ctrl,$httpBackend;

beforeEach(inject(function(_$httpBackend_,

$routeParams,$rootScope,$controller){

$httpBackend=_$httpBackend_;

$httpBackend.expectGET('blogPost').respond({_id:'1'});

$routeParams.id='1';

scope=$rootScope.$new();

ctrl=$controller('BlogViewCtrl',{$scope:scope});

}));

it('shouldshowblogentryid',function(){

expect(scope.blogEntry._id).toEqual(1);

});

});

});

Page 122: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

KarmaTestingThenewtestspecificationwillunittestbothcontrollers.Right-clicktheprojectandselect“Test”fromthemenu.Karmawillstart.YoushouldseebothChromeandFirefoxbrowserwindowsopen.TheNetBeanstestresultswindowshouldopenanddisplaytwopassedtestsforChromeandtwopassedtestsforFirefox.

Ifyougetanyerrormessagesorfailedtests,gobackoverthissectionandverifythatyoucompletedalltheconfigurationsandinstallations.YoucanalsodownloadtheChapter5codefromtheGitHubprojectsite.

Page 123: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

End-to-EndTestingNext,weneedtocreateaProtractorconfigurationfilefortheproject.CreateanewJavaScriptfilenamedconf.jsunderthetestfolderoftheChapter5project.Enterthecodeshownhereinthenewfile:

/*chapter5/conf.jsProtractorconfigurationfile*/

exports.config={

seleniumAddress:'http://localhost:4444/wd/hub',

specs:['e2e/blog-spec.js']

};

Page 124: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

ProtractorTestSpecificationNowweneedtocreateaProtractortestspecification.Dothefollowing:

1. Createanewfolderunderthetestfolderoftheprojectandnameite2e.

2. CreateanewJavaScriptfileinsidethenewe2efolderandnameitblog-spec.js.

Thencopythecodeshownnextintothenewblog-spec.jsfile.

WARNINGMakesurethelinesbrowser.get("http://localhost:8383/AngularJsBlog/");matchtheURLthatyouuseonyoursystemtocalltheblogapplication.TheURLcanbedifferentfordifferentdevelopmentenvironmentsandcandependonhowyounamedyourproject.

/*chapter5/blog-spec.js*/

describe("BlogApplicationTest",function(){

it("shouldtestthemainblogpage",function(){

browser.get("http://localhost:8383/AngularJsBlog/");

expect(browser.getTitle()).toEqual("AngularJSBlog");

//getsthebloglist

varblogList=element.all(by.repeater('blogPostinblogList'));

//teststhesizeoftheblogList

expect(blogList.count()).toEqual(2);

browser.get(

"http://localhost:8383/AngularJsBlog/#!/blogPost/1");

expect(browser.getTitle()).toEqual("AngularJSBlog");

//getsthecommentlist

varcommentList=element.all(

by.repeater('commentinblogEntry.comments'));

//checksthesizeofthecommentList

expect(commentList.count()).toEqual(2);

});

});

Page 125: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

ProtractorTestingStartanewcommandwindowandenterthefollowingcommandtostartthetestserver:

webdriver-managerstart

OpenanewcommandwindowandnavigatetotherootoftheChapter5project.Typethecommand:

protractortest/conf.js

Youshouldseeabrowserwindowopen.Youshouldthenseethetestscriptnavigatethroughthepagesoftheblogapplication.WhentheProtractorscripthasfinished,thebrowserwindowwillclose.

YoushouldseeresultslikethefollowinginthecommandwindowwhentheProtractorscriptcompletes.Thenumberofsecondsthatittakesthescripttofinishwillvarydependingonyourparticularsystem:

Finishedin1.377seconds

1test,4assertions,0failures

Page 126: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

ConclusionInthischapterwebuiltourviewusingTwitterBootstrap.WealsomadeourapplicationresponsivetodifferentscreensizesusingCSS3.WeconfiguredbothKarmaandProtractorforourblogproject,andranbothunitandend-to-endtests.

WewillnowcoverRESTservicesandhowtheyareusedinAngularJS.Thenwewillmoveontothemodel.

Page 127: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development
Page 128: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

Chapter6.AngularJSandRESTServices

Intheneweraofmobileeverywhere,thebusinesslogicforAngularJSapplicationsshouldalwaysbeplacedinRESTserviceswheneverpossible.AngularJSapplicationsshouldbekeptcleanandsimple.Why?AsAngularJSevolvesoverthenextfewyears,itisverypossiblethatmostAngularJSapplicationswillberewritten.

ThismeansthatanybusinesslogicplacedinsideanAngularJSapplicationwillneedtoberewrittenaswell—aseriousconsiderationforapplicationscontaininglargeamountsofbusinesslogic.RESTservices,ontheotherhand,maybearoundforyearstocome.Aswebservicestechnologiesevolve,manyRESTservicesmayundergoupgradesandmodifications,butacompleteservicerewriteisunlikelyinmostcases.Thebestplaceforbusinesslogicistheplacethatwillundergotheleastamountofchangeandbeavailabletoalltypesofapplications,nowandinthefuture.

Page 129: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

RESTServicesREST(REpresentationalStateTransfer)servicesallowfora“separationofconcerns.”RESTservicesarenotconcernedwiththeuserinterfaceoruserstate,andclientsthatuseRESTservicesarenotconcernedwithdatastorageorbusinesslogic.ClientscanbedevelopedindependentlyoftheRESTservices,aswehaveshowninpreviouschapters,usingmockdata.RESTservicescanlikewisebedevelopedindependentlyoftheclient,withnoconcernforclientspecificsoreventhetypesofclientsusingtheservices.RESTservicesshouldperforminthesamewayforallclients.

RESTservicesshouldbestateless.ARESTserviceshouldneverholddatainasessionvariable.AllinformationneededforaRESTservicecallshouldbecontainedintherequestandheaderpassedfromtheclienttotheservice.Anystateshouldbeheldintheclientandnotintheservice.TherearemanywaystoholdstateinanAngularJSapplication,includinglocalstorage,cookies,orcachestorage.

ARESTwebserviceissaidtobeRESTfulwhenitadherestothefollowingconstrants:

It’sURL-based(e.g.,http://www.micbutton.com/rs/blogPost).

ItusesanInternetmediatypesuchasJSONfordatainterchange.

ItusesstandardHTTPmethods(GET,PUT,POST,DELETE).

HTTPmethodshaveaparticularpurposewhenusedwithRESTservices.ThefollowingisthestandardwaythatHTTPmethodsshouldbeusedwithRESTservices:

1. POSTshouldbeusedto:a. Createanewresources.

b. Retrievealistofresourceswhenalargeamountofrequestdataisrequiredtobepassedtotheservice.

2. PUTshouldbeusedtoupdatearesource.

3. GETshouldbeusedtoretrievearesourceoralistofresources.

4. DELETEshouldbeusedtodeletearesource.

Forexample,thefollowingwouldbetheproperuseofHTTPmethods:

1. POST:http://www.micbutton.com/rs/blogPosttocreateanewblogpost

2. PUT:http://www.micbutton.com/rs/blogPosttoupdateablogpost

3. GET:http://www.micbutton.com/rs/blogPost/50togettheblogpostwithidequalto50

4. DELETE:http://www.micbutton.com/rs/blogPost/50todeletetheblogpostwith

Page 130: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

idequalto50

Page 131: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

AngularJSandRESTServicesAngularJSRESTservicecallsareasynchronousAjaxcallsbasedonthe$qservice’spromiseanddeferredAPIs.Wewillnotcoverpromises,deferredobjects,orAjaxinthisbook.IfyoudonotunderstandhowAjaxisusedtomakeasynchronouscalls,nowwouldbeagoodtimetoresearchthesetopics.MakingasynchronousAjaxRESTservicecallsisnotspecifictoAngularJSoranyotherclient-sideJavaScriptframework.ManylibrariesprovideAjaxfunctionality,includingjQuery,Dojo,andothers.

Page 132: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

WaystoCreateAngularJSServicesTherearethreewaystocreateandregisterservicesinAngularJS.Theyareasfollows:

Usingtheservicefunction

Usingtheproviderfunction

Usingthefactoryfunction

Here’showtocreateaservicewiththeservicefunction(wewillnotusethismethodtocreateservicesinthisbook):

/*chapter6/servicefunction*/

varblogServices=angular.module('blogServices',['ngResource']);

blogServices.service('BlogPost',[…]

Youcanalsocreateserviceswiththeproviderfunction,asshownhere:

/*chapter6/providerfunction*/

varblogServices=angular.module('blogServices',['ngResource']);

blogServices.provider('BlogPost',[…]

ThethirdwaytocreateservicesinAngularJSiswiththefactoryfunction.Thisisthemostcommonlyusedmethod,andthemethodwewillusetocreateAngularJSservicesthroughoutthisbook:

/*chapter6/factoryfunction*/

varblogServices=angular.module('blogServices',['ngResource']);

blogServices.factory('BlogPost',[…]

WewillnowlookathowtoconnecttoRESTservicesinAngularJS,althoughwewillnotactuallyimplementtheservicecodeinourblogapplicationuntilChapter7.WeneedtogetagoodtheoreticalunderstandingofAngularJSservicesbeforewestartcoding.Oncewehavethatunderstanding,wewillbesetforChapter7.

Page 133: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

WaystoCommunicatewithRESTServicesTherearecurrentlytwowaystocommunicatewithRESTservicesusingAngularJS:

The$httpservice

Thisserviceprovideslow-levelinteractionwithRESTservicesusingthebrowser’sXMLHttpRequestobject.

The$resourceobject

Thisobjectprovidesahigh-levelapproachtointeractingwithRESTservices,simplifyingtheprocessconsiderably.

Wewillfocusmostlyonusingthe$resourceobjectforcommunicatingwithRESTservicesandleavethe$httpservicediscussiontootherbooks(althoughwewillusethe$httpserviceinlaterchaptersforhandlingBasicAuthenticationheaders).Allourprojectcodeusesthe$resourceobject.

ThefollowingcodeshowshowtodefineanAngularJSservicethatcanbeusedtointeractwiththeBlogPostRESTservice.NoticethatwepasstheRESTserviceURLtothe$resourceobject.ThemethodsdefinedmatchtheRESTservicesthataredefinedonthatparticularURL.OncetheBlogPostserviceisdefined,itcanbeusedlikeastandardJavaScriptobjecttoaccessthedifferentRESTservicesdefinedonthisURL:

/*chapter6/services.js*/

'usestrict';

/*Services*/

varblogServices=

angular.module('blogServices',['ngResource']);

blogServices.factory('BlogPost',['$resource',

function($resource){

return$resource("http://www.micbutton.com/rs/blogPost",{},{

get:{method:'GET',cache:false,isArray:false},

save:{method:'POST',cache:false,isArray:false},

update:{method:'PUT',cache:false,isArray:false},

delete:{method:'DELETE',cache:false,isArray:false}

});

}]);

Usingthe$resourceobjectisbyfartheeasiestwaytocallRESTservices.Asyoucanseefromthisexample,theAngularJSservicecodeisstraightforwardandreallyfairlyuncomplicated.Evenwhenmanyservicesaredefined,theservices.jsfileisrelativelysimple.

TheAngularJS$httpservicementionedearlierisanotherwaytocallRESTservices.However,usingthe$httpservicewouldrequiremanymorelinesofcoderelatedtoRESTservicecallsthanweneedusingthe$resourceobject.Wedousethe$httpserviceinseveralplacesintheblogapplication,though,suchastosendaBasicAuthentication

Page 134: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

headertoRESTservices.Wewillcoverthatinlaterchapters.

Page 135: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

UpdatingtheProjectforRESTBeforewecanuseourservice,thenewservices.jsfilemustbeloadedatruntimeandthenewservicesmodule,blogServices,mustbespecifiedasadependencyoftheapplicationatstartuptime.Hereisthelinethatshouldbeaddedtotheindex.htmlfiletoloadtheservices.jsfile:

/*chapter6/index.htmlexcerpt*/

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

Andhereisthecompleteindex.htmlfile,withthisaddition:

<!--chapter6/index.htmlcompletefile-->

<!DOCTYPEhtml>

<htmllang="en"ng-app="blogApp">

<head>

<title>AngularJSBlog</title>

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

<metahttp-equiv="Content-Type"content="text/html;charset=UTF-8">

<linkrel="stylesheet"href="lib-css/bootstrap.min.css"media="screen"/>

<linkrel="stylesheet"href="css/styles.css"media="screen"/>

<scriptsrc="js/libs/jquery-1.10.2.min.js"></script>

<scriptsrc="js/libs/bootstrap.min.js"></script>

<scriptsrc="js/libs/angular.min.js"></script>

<scriptsrc="js/libs/angular-route.min.js"></script>

<scriptsrc="js/libs/angular-resource.min.js"></script>

<scriptsrc="js/libs/angular-cookies.min.js"></script>

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

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

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

</head>

<body>

<divng-view></div>

</body>

</html>

ThefollowingcodeshowshowweuseinlineannotationstoaddthenewBlogServicesmoduleasadependencyoftheapplicationatstartuptime.Oncethenewmoduleisaddedhere,theservicesdefinedonthemodulecanbeusedbyanycontrollerintheapplication:

/*chapter6/app.js*/

'usestrict';

/*AppModule*/

varblogApp=angular.module('blogApp',[

'ngRoute',

'blogControllers',

'blogServices'

]);

Page 136: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

blogApp.config(['$routeProvider','$locationProvider',

function($routeProvider,$locationProvider){

$routeProvider.

when('/',{

templateUrl:'partials/main.html',

controller:'BlogCtrl'

}).when('/blogPost/:id',{

templateUrl:'partials/blogPost.html',

controller:'BlogViewCtrl'

});

$locationProvider.html5Mode(false).hashPrefix('!');

}]);

Page 137: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

RESTServicesandControllersNowlet’slookathowtousetheBlogPostserviceinsidetheBlogViewCtrlcontroller.Firstwemustdefinetheserviceasarequirementofthecontroller,asshownhere.Wethenmakeacalltothegetmethodandpasstheidasanargument.Wealsodefinetwocallbackfunctions,successanderror(ifyoudonotunderstandJavaScriptcallbackfunctions,nowwouldbeagoodtimetostopandresearchhowtheywork):

/*chapter6/controllers.jsexcerpt*/

blogControllers.controller('BlogViewCtrl',

['$scope','$routeParams','BlogPost',

functionBlogViewCtrl($scope,$routeParams,BlogPost){

varblogId=$routeParams.id;

BlogPost.get({id:blogId},

functionsuccess(response){

console.log("Success:"+JSON.stringify(response));

$scope.blogEntry=response;

},

functionerror(errorResponse){

console.log("Error:"+JSON.stringify(errorResponse));

}

);

}]);

WhenacallismadetotheBlogViewCtrlcontroller,theidisretrievedfrom$routeParams.AcallisthenmadetothegetmethodoftheBlogPostservice,passingtheidasanargument.Atthatpoint,thecalltothecontrollercompletes.

Theoreticallywedon’tknowwhentheRESTservicecallwillreturnresults,butwhenitdoes,eitherthesuccesscallbackfunctionortheerrorcallbackfunctionwillbecalled.IftheRESTservicecallfails,thecodeinsidetheerrorcallbackfunctionshouldhandletheerrorcondition.IftheRESTservicecallissuccessful,thecodeinsidethesuccesscallbackfunctionhandlesthesuccessfunctionality.

Page 138: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

TheJSONResponseNowlet’stakealookattheJSONresponseobjectreturneduponsuccess.IftheRESTservicecallissuccessful,wesettheJSONreturnedasthevalueofascopepropertynamedblogEntry.Thepropertyisatthatpointboundtotheview,andAngularJSupdatestheviewwiththenewvaluesthatwereretrievedfromtheRESTservicecall.IftheRESTservicecallfails,thescreenisnotupdated,butwelogtheerrortotheconsoletohelpdiagnosethefailure.TheJSONresponseobjectreturnedfromasuccessfulcalllookslikethis:

{"chapter:6,"JSON":"response"}

{

"_id":1,

"date":1400623623107,

"introText":"ThisisablogpostaboutAngularJS.

Wewillcoverhowtobuild",

"blogText":"ThisisablogpostaboutAngularJS.

Wewillcoverhowtobuildablogandhowtoadd

commentstotheblogpost.",

"comments":[

{

"commentText":"Verygoodpost.Iloveit."

},

{

"commentText":"Whencanwelearnservices."

}

]

}

Page 139: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

ListServicesIfwewantedalistofblogposts,wecoulddefinethefollowingRESTservice:GET:http://www.micbutton.com/rs/blogList.Let’stakealookathowwewoulddefinethatserviceintheservices.jsfile.NoticethatwespecifyisArray:true.Thisdefinestheserviceasreturningalistandnotanindividualresource:

/*chapter6/services.jsexcerpt*/

blogServices.factory('BlogList',['$resource',

function($resource){

return

$resource

("http://nodeblog-micbuttoncloud.rhcloud.com/NodeBlog/blogList",

{},{

get:{method:'GET',cache:false,isArray:true}

});

}]);

FollowingisthecontrollercodeusedtoaccesstheBlogListservice.Weinjecttheserviceintothecontrolleraswedidearlier,andlikebefore,wepasssuccessanderrorcallbackfunctionstotheservicecall.TheresponsefromasuccessfulservicecallisassignedtotheblogListpropertyofthescopeandpassedtotheview:

/*chapter6/controllers.jsexcerpt*/

blogControllers.controller('BlogCtrl',['$scope','BlogList',

functionBlogCtrl($scope,BlogList){

BlogList.get({},

functionsuccess(response){

console.log("Success:"+JSON.stringify(response));

$scope.blogList=response;

},

functionerror(errorResponse){

console.log("Error:"+JSON.stringify(errorResponse));

}

);

}]);

WeaccesstheJSONinsidetheviewbyusingtheblogListscopeproperty,asshownhere.ThisisthesametechniqueweusedinChapter5.Weusetheng-repeatdirectivetoiterateoverthelistasbefore:

<!--chapter6/main.htmlexcerpt-->

<divng-repeat="blogPostinblogList">

<divclass="blog-post-outer">

<divclass="blog-intro-text">

Posted:{{blogPost.date|date:'MM/dd/yyyy@h:mma'}}</div>

<divclass="blog-intro-text">{{blogPost.introText}}</div>

<divclass="blog-read-more">

<ahref="#!blogPost/{{blogPost._id}}">ReadMore</a>

</div>

Page 140: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

TestingServiceswithKarmaThebestwaytotestAngularJSservicesiswithKarma.WeusedKarmaasoneofourtestframeworksinpreviouschapters.Unittestingaserviceletsusvalidatethattheunitofcodethatisusedtobuildtheserviceisworkingcorrectly.UnittestinganAngularJSservicethatconnectstoaRESTserviceisapotentialcauseoferrors,however.

RESTservicecallsareasynchronous,sotherecanbeadelaybeforetheservicecallresultsareavailabletothepartoftheapplicationthatinitiatedtheRESTcall.ConsideringthataRESTserviceisnotactuallypartoftheunitofcodethatwewouldbetestingwithaunittest,weshouldn’tbetooconcernedaboutRESTcallswhenunittesting.

Karma,asImentionedbefore,shouldbetheunittestframeworkforourblogapplication.ThefollowingcodeshowshowwemodifyanormalKarmaconfigurationfiletoallowustotestcodewheretheAngularJS$resourceobjectisused.Noticetheline"public_html/js/libs/angular-resource.min.js".Withthatline,wetellKarmatousetheAngularJSangular-resource.min.jsfile.Thatfileisneededonlywhenwe’reworkingwithcodethatcallsRESTservices:

/*chapter6/karma.conf.js*/

module.exports=function(config){

config.set({

basePath:'../',

files:[

"public_html/js/libs/angular.min.js",

"public_html/js/libs/angular-mocks.js",

"public_html/js/libs/angular-route.min.js",

"public_html/js/libs/angular-resource.min.js",

"public_html/js/*.js",

"test/**/*Spec.js"

],

exclude:[

],

autoWatch:true,

frameworks:[

"jasmine"

],

browsers:[

"Chrome",

"Firefox"

],

plugins:[

"karma-junit-reporter",

"karma-chrome-launcher",

"karma-firefox-launcher",

"karma-jasmine"

]

});

};

Page 141: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

KarmaServiceSpecificationsInordertotestAngularJSservices,weneedtoaddatestspecificationspecificallyfortheblogapplicationservices.ThefollowingcodeshowsaservicesSpec.jsfile.Thetestspecificationhasunittestingfortwoservices.ThefirstunittestisfortheBlogListservice,andthesecondtestisfortheBlogPostservice:

/*chapter6/servicesSpec.js*/

describe('AngularJSBlogServiceTesting',function(){

describe('testBlogList',function(){

var$rootScope;

varblogList;

beforeEach(module('blogServices'));

beforeEach(inject(function($injector){

$rootScope=$injector.get('$rootScope');

blogList=$injector.get('BlogList');

}));

it('shouldtestBlogListservice',function(){

expect(blogList).toBeDefined();

});

});

describe('testBlogPost',function(){

var$rootScope;

varblogPost;

beforeEach(module('blogServices'));

beforeEach(inject(function($injector){

$rootScope=$injector.get('$rootScope');

blogPost=$injector.get('BlogPost');

}));

it('shouldtestBlogPostservice',function(){

expect(blogPost).toBeDefined();

});

});

});

Noticeinthiscodethatweuse$injectortoinjectthetwoservicesdirectlyintothetestscripts.AsImentionedearlier,wearenottestingtheRESTservicesthemselves;weareonlytestingtheAngularJSservicesthatconnecttoRESTservices.ThetestsshouldsucceedeveniftheRESTservicesaredownforsomereason.

Page 142: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

End-to-EndTestingEnd-to-endtestingdonewithProtractorisamuchbetterwaytotestthefunctionalityofRESTservicesandtheapplicationsassociatedwiththem.Mostmodernsoftwaredevelopmentteamsusesometypeofcontinuousintegration(CI)buildsystem.MostCIsystemscanbeconfiguredtorunend-to-endtestsusingProtractor.

ProtractorE2Etestingcanevenbeconfiguredtoruntestsagainstproductionenvironments.Moreoften,however,E2EtestingiswrittentorunagainstservicesrunningonQAservers.E2Etestingisagoodwaytotestanapplicationthesamewayauserwouldusetheapplication.

Page 143: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

ProtractorConfigurationThefollowingisaconfigurationfileforProtractor.Aspecificationfilenamedblog-spec.jsisreferencedfromtheconfigurationfile:

/*chapter6/conf.jsProtractorconfigurationfile*/

exports.config={

seleniumAddress:'http://localhost:4444/wd/hub',

specs:['e2e/blog-spec.js']

};

Page 144: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

ProtractorTestSpecificationLet’stakealookatthecontentsoftheblog-spec.jsfile.Youcanseethatthebrowser.get(URL)callcanbemadeagainstanyaccessibleURL.TheURLcouldpointtoalocaldevelopmentbox,aQAserver,oraproductionserver.RESTservicescanbethoroughlytestedwithaProtractortestscript:

/*chapter6/blog-spec.jsProtractortestspecification*/

describe("BlogApplicationTest",function(){

it("shouldtestthemainblogpage",function(){

browser.get(

"http://localhost:8383/AngularJsBlogChapter6/");

expect(browser.getTitle()).toEqual("AngularJSBlog");

//getsthebloglist

varblogList=

element.all(by.repeater('blogPostinblogList'));

//teststhesizeoftheblogList

expect(blogList.count()).toEqual(1);

browser.get(

"http://localhost:8383/AngularJsBlogChapter6

/#!/blogPost/5394e59c4f50850000e6b7ea");

expect(browser.getTitle()).toEqual("AngularJSBlog");

//getsthecommentlist

varcommentList=

element.all(by.repeater('commentinblogEntry.comments'));

//checksthesizeofthecommentList

expect(commentList.count()).toEqual(2);

});

})

Page 145: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

ConclusionThisconcludesourdiscussionofRESTservicebasics.Throughouttherestofthisbookwe’llbeworkingwithliveRESTservices.Asweproceed,youwillgainabetterunderstandingofRESTserviceconcepts.WewillnowstartworkingwithactualRESTservicescreatedespeciallyforthisbook.

Page 146: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development
Page 147: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

Chapter7.AngularJSModels

AngularJSmodelsareheldinthe$scopeobject.InAngularJS,$scopeisusedtogainaccesstothemodelrelatedtoaparticularcontroller.$rootScopeisaparentscopethatcanbeusedtosaveandaccessmodelpropertiesthatspanmultiplecontrollers.Theuseof$rootScopeishighlydiscouragedinmostdesigns,however.Thereisonlyone$rootScopeinanapplication.$scopeisachildscopeof$rootScope.

AproperlydesignedAngularJSapplicationwillhavelittleornousefor$rootScopetostoremodelproperties.Inthischapterwewillfocusonlyon$scope,usedtostorethemodelretrievedfromRESTservices.

Page 148: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

PublicRESTServicesTheRESTservicesusedforthischapterareavailableathttp://nodeblog-micbuttoncloud.rhcloud.com/NodeBlog.TheservicesareopentothepublicandwritteninJavaScriptusingNode.js,ExpressJS,andMongoDB.InChapter11,youwilldeploythesameRESTserviceswithyourAngularJSblogapplicationasaMEANstack(MongoDB,ExpressJS,AngularJS,andNode.js)application.YouwillthendeploytheMEANstacktothecloudusingafreeRedHatOpenShiftaccount.

ThefollowingexcerptshowshowAngularJSservicesaccesstheRESTservicesusedforthischapter.TheRESTservicesreturnthesameJSONthatwaspreviouslyhardcodedinthecontrollers:

/*chapter7/services.jsexcerpt*/

$resource(

"http://nodeblog-micbuttoncloud.rhcloud.com/NodeBlog/blog/:id"

...

$resource(

"http://nodeblog-micbuttoncloud.rhcloud.com/NodeBlog/blogList"

...

Thecompletemodifiedservices.jsfileisshownhere:

/*chapter7/services.jscompletefile*/

'usestrict';

/*Services*/

varblogServices=

angular.module('blogServices',['ngResource']);

blogServices.factory('BlogPost',['$resource',

function($resource){

return$resource(

"http://nodeblog-micbuttoncloud.rhcloud.com/NodeBlog/blog/:id",

{},{

get:{method:'GET',cache:false,isArray:false},

save:{method:'POST',cache:false,isArray:false},

update:{method:'PUT',cache:false,isArray:false},

delete:{method:'DELETE',cache:false,isArray:false}

});

}]);

blogServices.factory('BlogList',['$resource',

function($resource){

return$resource(

"http://nodeblog-micbuttoncloud.rhcloud.com/NodeBlog/blogList",

{},{

get:{method:'GET',cache:false,isArray:true}

});

}]);

Page 149: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

ChangestotheControllersShownnextisthecontrollers.jsfile.Thechangesmadeheregreatlysimplifythecontrollers.Theservicesneededforeachindividualcontrollerareinjectedandmadeaccessibleforthatparticularcontrollertouse.TheblogIDispassedasapathparameterargumenttotheBlogPostservice.Apathparameterisusedbecausewedefined/id:attheendoftheBlogPostserviceURLintheservices.jsfile.Ifweremovedthe/:idfromtheendoftheserviceURL,AngularJSwouldpassthevalueasaqueryparameterargumentinstead.Theupdatedfilelookslikethis:

/*chapter7/controllers.js*/

'usestrict';

/*Controllers*/

varblogControllers=

angular.module('blogControllers',[]);

blogControllers.controller('BlogCtrl',

['$scope','BlogList',

functionBlogCtrl($scope,BlogList){

$scope.blogList=[];

BlogList.get({},

functionsuccess(response){

console.log("Success:"+

JSON.stringify(response));

$scope.blogList=response;

},

functionerror(errorResponse){

console.log("Error:"+

JSON.stringify(errorResponse));

}

);

}]);

blogControllers.controller('BlogViewCtrl',['$scope',

'$routeParams','BlogPost',

functionBlogViewCtrl($scope,$routeParams,BlogPost){

varblogId=$routeParams.id;

$scope.blg=1;

BlogPost.get({id:blogId},

functionsuccess(response){

console.log("Success:"+

JSON.stringify(response));

$scope.blogEntry=response;

},

functionerror(errorResponse){

console.log("Error:"+

JSON.stringify(errorResponse));

}

);

}]);

Page 150: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

ModelPropertiesOnceyou’veaddedtheJSONreturnedfromtheRESTservicetothemodelbyassigningittoascopeproperty,thatJSONismadeavailabletotheview.Allscopepropertiesareaccessedfrominsidetheview,asdescribedinpreviouschapters.Therearenochangesthatneedtobemadeintheview.

IfyouhaveusedotherJavaScriptclient-sideframeworks,bynowyoushouldseethesimplicityofAngularJSmodels.WithAngularJS,therearenomodelclassesthatneedtobedefined;youdon’tneedtowritemodelAjaxcodeorcreatemodelobjectsthathavetobeboundtotheviews.Allyouhavetodoisassignmodelpropertiestothescope.TheAngularJSframeworkhandlestherest.

AngularJSmodelsgreatlysimplifythecreationofJavaScriptapplications.Youcancutwhatpotentiallycouldbethousandsoflinesofmodel-relatedcodedowntoonlyafewlines.Bycuttinglinesofcodeyoualsocutvaluabledevelopmenttime,andpotentiallythenumberofdevelopersneededonaproject.Thesimplicityofthemodelcodealsomakesapplicationseasiertomaintainorenhance,onceagaincuttingcostsbycuttingdevelopmenttime.

Page 151: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

BlogApplicationPublicServicesNowwewillmaketheneededchangestoenableourblogapplicationtousethepublicRESTservicesdiscussedinthepreviouschapter.First,wemustaddtheservices.jsfiletoourproject.

Right-clicktheprojectandaddanewJavaScriptfilenamedservices.jsunderthejsfolder,asshowninFigure7-1.

Figure7-1.Addingtheservices.jsfile

Addthiscodetothenewlycreatedfile:

/*chapter7/services.js*/

'usestrict';

/*Services*/

varblogServices=

angular.module('blogServices',['ngResource']);

blogServices.factory('BlogPost',['$resource',

function($resource){

return$resource(

Page 152: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

"http://nodeblog-micbuttoncloud.rhcloud.com/NodeBlog/blog/:id",

{},{

get:{method:'GET',cache:false,isArray:false},

save:{method:'POST',cache:false,isArray:false},

update:{method:'PUT',cache:false,isArray:false},

delete:{method:'DELETE',cache:false,isArray:false}

});

}]);

blogServices.factory('BlogList',['$resource',

function($resource){

return$resource(

"http://nodeblog-micbuttoncloud.rhcloud.com/NodeBlog/blogList",

{},{

get:{method:'GET',cache:false,isArray:true}

});

}]);

Nowaddthenewservices.jsfiletotheindex.htmlfile’s<head>section,asshownhere,sothefilecanbeloadedbyourAngularJSapplication:

<!--chapter7/index.htmlexcerpt-->

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

Page 153: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

ModifyingtheHTMLThecompleteindex.htmlfileisshownhereforconvenience:

<!--chapter7/index.html-->

<!DOCTYPEhtml>

<htmllang="en"ng-app="blogApp">

<head>

<title>AngularJSBlog</title>

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

<metahttp-equiv="Content-Type"content="text/html;charset=UTF-8">

<linkrel="stylesheet"href="lib-css/bootstrap.min.css"media="screen"/>

<linkrel="stylesheet"href="css/styles.css"media="screen"/>

<scriptsrc="js/libs/jquery-1.10.2.min.js"></script>

<scriptsrc="js/libs/bootstrap.min.js"></script>

<scriptsrc="js/libs/angular.min.js"></script>

<scriptsrc="js/libs/angular-route.min.js"></script>

<scriptsrc="js/libs/angular-resource.min.js"></script>

<scriptsrc="js/libs/angular-cookies.min.js"></script>

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

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

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

</head>

<body>

<divng-view></div>

</body>

</html>

Page 154: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

ModifyingApp.jsThenewlycreatedservicesmodulemustbeaddedtotheapplicationbeforeitcanbeused.WeaddthenewblogServicesmoduleasadependencyoftheapplicationatstartuptimeusinginlinearrayannotations,asshownhere.Nowthenewservicescanbeinjectedandusedincontrollerswheneverneeded.WecannowreplacethehardcodedJSONusedasmockdatainpreviouschapters:

/*chapter7/app.js*/

'usestrict';

/*AppModule*/

varblogApp=angular.module('blogApp',[

'ngRoute',

'blogControllers',

'blogServices'

]);

blogApp.config(['$routeProvider','$locationProvider',

function($routeProvider,$locationProvider){

$routeProvider.

when('/',{

templateUrl:'partials/main.html',

controller:'BlogCtrl'

}).when('/blogPost/:id',{

templateUrl:'partials/blogPost.html',

controller:'BlogViewCtrl'

});

$locationProvider.html5Mode(false).hashPrefix('!');

}]);

Page 155: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

ModifyingtheControllersNowlet’sseehowtousethenewservicesinourcontrollers.Replacethepreviouscodeincontrollers.jswiththecodeshownnext.Thecodeshowshowweinjecttheservicesintoeachcontroller.Wepopulatethescopepropertiesinsidethesuccesscallbackfunction,asexplainedinpreviouschapters.

Asexplainedearlier,thesuccesscallbackfunctionisonlycalledwhentheRESTservicecallreturnssuccessfully.Atthatpoint,wecansafelypopulatethescopeproperties.ThescopepropertiesarethenboundtotheviewbytheAngularJSframework:

/*chapter7/controllers.js*/

'usestrict';

/*Controllers*/

varblogControllers=

angular.module('blogControllers',[]);

blogControllers.controller('BlogCtrl',

['$scope','BlogList',

functionBlogCtrl($scope,BlogList){

BlogList.get({},

functionsuccess(response){

console.log("Success:"+JSON.stringify(response));

$scope.blogList=response;

},

functionerror(errorResponse){

console.log("Error:"+JSON.stringify(errorResponse));

});

}]);

blogControllers.controller('BlogViewCtrl',

['$scope','$routeParams','BlogPost',

functionBlogViewCtrl($scope,$routeParams,BlogPost){

varblogId=$routeParams.id;

BlogPost.get({id:blogId},

functionsuccess(response){

console.log("Success:"+JSON.stringify(response));

$scope.blogEntry=response;

},

functionerror(errorResponse){

console.log("Error:"+JSON.stringify(errorResponse));

});

}]);

Wealsomadesomechangestothecontrollers.jsfiletomaketestingeasier.TestingAngularJScontrollerscanbemorecomplexwhenRESTservicesareinvolved.Asmentionedpreviously,wedon’tknowwhenRESTserviceswillreturnresults,becausetheyareasynchronouscalls.

AsynchronousRESTservicecallswillalwayscausecontrollerunitteststofail.UnittestsofcontrollersthatdependonRESTserviceswillfinishexecutionbeforetheRESTserviceseverreturnresults,soanyscopepropertiesusedbycontrollerunittestswillbemissingwhenthetestscriptexecutesifthosepropertiesarereturnedfromaRESTservicecall.

TherearewaystoaddadelayandmakeunittestscriptswaitontheRESTserviceresults,buttheyaddanunneededlevelofcomplexitytothetestscripts.Unittesting,afterall,

Page 156: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

shouldbeatestofaunitofcodeandnotanend-to-endtest.ProtractorE2EtestsareabetterwaytotestRESTservices.

Lookatthecodethatfollows.TheBlogListserviceisinjectedintotheBlogCtrlcontroller.WemakeanasynchronouscalltothegetmethodoftheBlogListservicebypassingtwocallbackfunctionstothecall.Thesuccesscallbackfunctionreturnsasuccessfulserviceresponseobject,andtheerrorcallbackfunctionreturnsanyerrorsiftheservicecallfails:

/*chapter7/controllers.jsexcerpt*/

blogControllers.controller('BlogCtrl',['$scope','BlogList',

functionBlogCtrl($scope,BlogList){

$scope.blogList=[];

BlogList.get({},

functionsuccess(response){

console.log("Success:"+JSON.stringify(response));

$scope.blogList=response;

},

functionerror(errorResponse){

console.log("Error:"+JSON.stringify(errorResponse));

}

);

}]);

ItmaytakeasecondormorefortheRESTservicetoreturnresults.OncetheRESTservicedoesreturnresults,thesuccesscallbackfunctionwillbecalled.Unfortunately,theunittestscriptwillhavefinishedexecutionlongbefore.Weremedythisissuebymakingachangetothecontroller.

Noticetheassignment$scope.blogList=[];intheprecedingcode.Theassignmenthasnoimpactonthefunctionalityofthecontroller,butithasamajorimpactontheunittestscriptassociatedwiththeBlogCtrlcontroller.TheassignmentinitializesthescopeblogListpropertywithanemptyarray.

ThefollowingcodeshowshowtheemptyarrayisusedtotesttheblogCtrlcontroller.Noticethelineofcodechecksthatthearraylengthisequalto0:

/*chapter7/controllerSpec.jsexcerpt*/

expect(scope.blogList.length).toEqual(0);

Wecanthenrestassuredthatthecontrollerisworkingsuccessfullyfroma“unitofcode”perspective.YouwillseelaterhowtomakesuretheRESTserviceworkedasexpected.

Page 157: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

RunningtheApplicationInNetBeans,right-clickandrunyourAngularJSblogapplication.Youshouldseethesamedatadisplayedonthescreenthatwastherewhenthedatawashardcoded.IfyouareusingChromeasyourbrowser,youcanturnon“DeveloperTools”andclickthe“Network”menubuttontoseetheRESTservicecallsthataremadeasyouclickvariouslinksintheapplication.YoucanalsoclicktheHeaders,Preview,Response,andTimingtabsinDeveloperToolstoseespecificinformationabouteachservicecall.

UsingChromeDeveloperToolsisalsoagreatwaytotroubleshootissueswithAngularJSRESTservicecallsifyouhaveproblems.There’sagreatJavaScriptdebuggerthatcanbeusedtodebugRESTservicecallsandotherJavaScriptissues.

IfyouarenotfamiliarwithChromeDeveloperTools,seetheGoogleChromesiteformoreinformation.InadditiontotheChromedebugger,NetBeansalsohasadebuggerbuiltinfordebuggingJavaScriptapplications.FormoreinformationondebuggingJavaScriptinNetBeans,takealookattheNetBeanswebsite.

Page 158: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

TestingServiceswithKarmaThebestwaytotestAngularJSservicesiswithKarma.WeusedKarmaasoneofourtestframeworksinpreviouschapters.Youshouldhavealreadycreatedthepackage.jsonfilefortheblogprojectbackinChapter5.Thefileisshownagainhereforreference:

/*chapter7/package.json*/

{

"name":"package.json",

"devDependencies":{

"karma":"*",

"karma-chrome-launcher":"*",

"karma-firefox-launcher":"*",

"karma-jasmine":"*",

"karma-junit-reporter":"*",

"karma-coverage":"*"

}

}

WealsocreatedtheKarmaconfigurationfilefortheblogprojectbackinChapter5,butweneedtomakeasmallchangetothat:weneedtoaddtheAngularJSangular-resource.min.jsfiletothekarma.conf.jsfiletotestourservices.Theangular-resource.min.jsfileisusedbyboththeBlogListandBlogPostservices.Themodifiedkarma.conf.jsfilelookslikethis:

/*chapter7/karma.conf.js*/

module.exports=function(config){

config.set({

basePath:'../',

files:[

"public_html/js/libs/angular.min.js",

"public_html/js/libs/angular-mocks.js",

"public_html/js/libs/angular-route.min.js",

"public_html/js/libs/angular-resource.min.js",

"public_html/js/*.js",

"test/**/*Spec.js"

],

exclude:[

],

autoWatch:true,

frameworks:[

"jasmine"

],

browsers:[

"Chrome",

"Firefox"

],

plugins:[

"karma-junit-reporter",

"karma-chrome-launcher",

"karma-firefox-launcher",

"karma-jasmine"

]

});

};

Page 159: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

KarmaServiceSpecificationsNowweneedtoaddnewservicetestspecificationsfortheblogproject.Dothefollowing:

1. CreateanewJavaScriptfilenamedservicesSpec.jsundertheunitfolder.

2. Enterthefollowingcodeinthenewfile:

/*chapter7/servicesSpec.js*/

/*Jasminespecsforcontrollers*/

describe('AngularJSBlogServiceTesting',function(){

describe('testBlogList',function(){

var$rootScope;

varblogList;

beforeEach(module('blogServices'));

beforeEach(inject(function($injector){

$rootScope=$injector.get('$rootScope');

blogList=$injector.get('BlogList');

}));

it('shouldtestBlogListservice',function(){

expect(blogList).toBeDefined();

});

});

describe('testBlogPost',function(){

var$rootScope;

varblogPost;

beforeEach(module('blogServices'));

beforeEach(inject(function($injector){

$rootScope=$injector.get('$rootScope');

blogPost=$injector.get('BlogPost');

}));

it('shouldtestBlogPostservice',function(){

expect(blogPost).toBeDefined();

});

});

});

ItisimportanttopointoutherethatourtestspecificationsfortheblogservicesdonotdependonthepresenceandfunctionalityoftheassociatedRESTservicesthatgetcalledbythoseservices.KarmaunittestsshouldtestthattheAngularJSservicescanbeinjected.Ifthetestsaresuccessful,thatprovesthattheservicesareconstructedproperly.Ourunittestingofservicesdoesnot,however,provethattheRESTservicesareworking.

AsImentionedbefore,Karmaunittestsoftenruninsidesomecontinuousintegration(CI)framework.CIsystemsareoftenconfiguredtotriggertherunningofunittestseverytimeachangeispushedtothesourcerepository.TheexistenceandaccessibilityofRESTservicescan’talwaysbeguaranteedwhenyou’reunittestinginsideaCI.

Unittestsshouldn’tdependontheexistenceofRESTservicesorothernetwork-relateddevices.Unittestingshouldtesttheindividualunitsofcodeandnottrytodoend-to-endtesting.WewilltestthefunctionalityofourRESTserviceswhenwedoE2EtestingwithProtractor.AnyproblemsrelatedtothecallingofRESTserviceswillshowasfailuresinProtractor.

Page 160: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

KarmaTestingThenewtestspecificationswillunittestthenewservices.ThecontrollerswillalsobetestedbecausewestillhavethecontrollerSpec.jsfileinoursystem.OurKarmaconfigurationfilelooksforalltestfilesthatendinSpec.js.

Right-clicktheprojectandselect“Test”fromthemenu.Karmawillstart.YoushouldseebothChromeandFirefoxbrowserwindowsopen.TheNetBeanstestresultswindowshouldopenanddisplayfourpassedtestsforChromeandfourpassedtestsforFirefox.

Ifyougetanyerrormessagesorfailedtests,gobackoverthissectionandverifythatyoucompletedalltheconfigurationsandinstallations.YoucanalsodownloadtheChapter7codefromtheGitHubprojectsite.

Page 161: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

End-to-EndTestingWealreadycreatedaProtractorconfigurationfilefortheblogapplicationinChapter5.TheProtractorconfigurationfileisshownhereforreference:

/*chapter7/conf.jsProtractorconfigurationfile*/

exports.config={

seleniumAddress:'http://localhost:4444/wd/hub',

specs:['e2e/blog-spec.js']

};

Page 162: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

ProtractorTestSpecificationNowweneedtochangetheProtractortestspecificationscreatedearlier.ThenewProtractortestsneedtointeractwiththeRESTservicesthatweuseinthischapter.

Copythecodeshownhereintotheblog-spec.jsfile.Makesurethelineslikebrowser.get("http://localhost:8383/AngularJsBlog/");matchtheURLthatyouuseonyoursystemtocalltheblogapplication.TheURLcanbedifferentfordifferentdevelopmentenvironmentsandcandependonhowyounamedyourproject:

/*chapter7/blog-spec.jsProtractortestspecification*/

describe("BlogApplicationTest",function(){

it("shouldtestthemainblogpage",function(){

browser.get(

"http://localhost:8383/AngularJsBlogChapter7/");

expect(browser.getTitle()).toEqual("AngularJSBlog");

//getsthebloglist

varblogList=

element.all(by.repeater('blogPostinblogList'));

//teststhesizeoftheblogList

expect(blogList.count()).toEqual(1);

browser.get(

"http://localhost:8383/AngularJsBlogChapter7

/#!/blogPost/5394e59c4f50850000e6b7ea");

expect(browser.getTitle()).toEqual("AngularJSBlog");

//getsthecommentlist

varcommentList=

element.all(by.repeater('commentinblogEntry.comments'));

//checksthesizeofthecommentList

expect(commentList.count()).toEqual(2);

});

});

Page 163: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

ProtractorTestingStartanewcommandwindowandenterthefollowingcommandtostartthetestserver:

webdriver-managerstart

OpenanewcommandwindowandnavigatetotherootoftheChapter5project.Typethecommand:

protractortest/conf.js

Youshouldseeabrowserwindowopen.Youshouldthenseethetestscriptnavigatethroughthepagesoftheblogapplication.WhentheProtractorscripthasfinished,thebrowserwindowwillclose.

YoushouldseeresultslikethefollowinginthecommandwindowwhentheProtractorscriptcompletes.Thenumberofsecondsthatittakesthescripttofinishwillvarydependingonyourparticularsystem:

Finishedin1.52seconds

1test,4assertions,0failures

Page 164: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

ConclusionThisconcludesourdiscussionofAngularJSmodels.WeaddedcodetomakeourblogapplicationworkwithRESTservicesrunninginthecloud,andwewroteunitteststotestthenewservicesthatweadded.WethenusedProtractortodoend-to-endtestingthatvalidatedthefunctionalityofourRESTservicesandtheAngularJSservicesassociatedwiththoseRESTservices.

WewilltalkaboutmodelsagaininChapter11,whenwedeployourapplicationtothecloudasaMEANstackapplication.Next,wewilladdsomenon-RESTservicestohandlebusinesslogicandseethepowerofAngularJSinaction.

Page 165: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development
Page 166: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

Chapter8.ServicesandBusinessLogic

NotallAngularJSservicesconnecttoRESTservices.Servicescanalsocontainbusinesslogicthatisusedbymultiplecontrollers.AsImentionedbefore,ifthebusinesslogiccanbemovedtoaRESTservice,thatiswhereitshouldbedefined.DefiningbusinesslogicinRESTservicesassuresthatthesamelogicwillbereadilyavailabletoallclient-sideapplications.

Often,however,itisnotpossibletomoveallbusinesslogictoRESTservices.Oftenthatsamebusinesslogicisneededacrossmultiplecontrollers.ThatiswhereAngularJSnon-RESTservicescomeinhandyonceagain.InthischapterwewilllookatseveralexamplesofwhereAngularJSnon-RESTservicesareuseful.

Take,forexample,asituationwhereauserneedstoauthenticateacrossmultipleRESTservices.OnewaytodothatisbyusingBasicAuthentication,wheretheuser’susernameandpasswordarepassedtoaserviceasatokenintheHTTPSheaderduringaservicecall.Thetokenisintheformof“username:password”andencodedwithbase64.

Asweknow,aRESTserviceshouldn’tholdstate,andholdingauser’scredentialsinasessionvariableontheserverisaserioussecurityconcern.UsingasessionvariabletoholdauthenticationstateontheserversideisusuallynotacceptableinmostRESTservicedesigns.AngularJSservicesaregreatforhandlingsuchsituations.

Page 167: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

HandlingUserAuthenticationFirst,weneedawaytovalidateauser’scredentialsoverHTTPS.ThefollowingcodeshowsaRESTserviceusedtoauthenticateauser:

/*chapter8/loginRESTserviceURLfromservices.js*/

POST:https://www.micbutton.com/user/login

HereistheJSONrequestfortheRESTservice:

{

"username":"ken",

"password":"password"

}

AndhereistheJSONresponsefortheRESTservice:

{

"authenticated":true

}

Thisparticularservicecallwouldnormallybeopentoanyuserandthereforewouldnotrequireauthentication.Allowingalluserstoaccessthisserviceuninhibitedmeansanyusercantrytovalidateagainsttheservice.Ifthereisapossibilityofabuse,theservicecouldbesecuredatthenetworklevel,orachallengeandresponsesystemcouldbeusedtodiscourageunwantedusers.

Onceausermakesacalltotheloginserviceandtheuser’scredentialsarevalidated,itisthejoboftheAngularJSapplicationtotemporarilystorethosecredentials.ItisalsothejoboftheAngularJSapplicationtodirecttheusertoaloginpagewhentheuserhasnotauthenticated.AngularJSnon-RESTservicesplayamajorroleinthisprocess.

Page 168: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

UsingBasicAuthenticationIftheRESTservicesaredesignedproperlytorequireauthenticationonallservicesthatcontainprivatedata,theAngularJSapplicationuserwillneverhaveaccesstoprivatedatawithoutprovidingthepropercredentials.Oncetheuserprovidesvalidusercredentials,theAngularJSapplicationcanstorethosecredentialsinacookieorsomeothertemporarystorage.Cookiesareagoodplacetostoreusercredentialsbecauseallmodernbrowsersstorecookiesmappedtoaparticularwebdomain.Cookieaccessisthengrantedonlytotheapplicationthatactuallycreatedthecookieonthatparticulardomain.OtherJavaScriptapplicationsrunninginthebrowseronlyhaveaccesstocookiestheycreate,whichareassociatedwiththeirrespectivedomains.

Page 169: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

CreatingAngularJSServicesAsImentionedinChapter6,therearethreewaystocreateservicesinAngularJS.Aservicecanbecreatedwiththeservicefunction,asshownhere:

/*chapter8/servicefunction*/

varblogServices=angular.module('blogServices',

['ngResource']);blogServices.service('BlogPost',[…]

orwiththeproviderfunction:

/*chapter8/providerfunction*/

varblogServices=angular.module('blogServices',

['ngResource']);blogServices.provider('BlogPost',[…]

ThethirdwaytocreateservicesinAngularJSiswiththefactoryfunction.ThisisthemethodwewillusetocreateAngularJSservicesinthischapterandthroughoutthisbook,becauseitisthemostcommonlyusedmethod.Thefollowingcodeshowshowtocreateaservicewiththefactoryfunction:

/*chapter8/factoryfunction*/

varblogServices=angular.module('blogServices',

['ngResource']);blogServices.factory('BlogPost',[…]

Page 170: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

HoldingUserCredentialsNowlet’stakealookatanAngularJSbusinesslogicservicedesignedtosavetheuser’scredentialsoncetheuserhasauthenticated.TheservicemakesuseofAngularJScookies,whichwecanincludeinanapplicationbyincludingtheangular-cookies.min.jslibraryfile.Theservicehastwoparametersdefined:theusername(un)andpassword(pw).

ThetwovaluesassignedtotheserviceareusedtobuildthetokenthatissentintheHTTPSheaderofeachRESTservicecall.TheAngularJSservicethenstoresthetokenandtheusernameascookiesforuselater:

/*chapter8/non-RESTbusinessservicetosetusercredentials*/

blogBusinessServices.factory('setCreds',

['$cookies',function($cookies){

returnfunction(un,pw){

vartoken=un.concat(":",pw);

$cookies.blogCreds=token;

$cookies.blogUsername=un;

};

}]);

Here’swhatacalltothesetCredsbusinesslogicservicetosaveanauthenticateduser’scredentialslookslike:

/*chapter8/controllers.jsexcerpt*/

setCreds($scope.username,$scope.password);

Page 171: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

CheckingUserCredentialsNowlet’slookatabusinesslogicservicethatchecksthestatusofauser’scredentials.Iftheservicereturnsfalse,theAngularJSapplicationshouldredirecttheusertotheloginpage.Itisalsoimportanttoremembertosavetheuser’scredentialsbymakingacalltosetCredsanytimetheuser’spasswordischanged:

/*chapter8/non-RESTbusinesslogicservicetocheckcredentials*/

blogBusinessServices.factory('checkCreds',

['$cookies',function($cookies){

returnfunction(){

varreturnVal=false;

varblogCreds=$cookies.blogCreds;

if(blogCreds!==undefined&&blogCreds!==""){

returnVal=true;

}

returnreturnVal;

};

}]);

TheservicesimplylooksfortheexistenceoftheblogCredscookieandreturnstrueifthecookieexists.IfasubsequentservicecallfailswiththesavedcredentialsandreturnsanHTTP401errorcode,itisthejoboftheAngularJSapplicationtodeletethesavedcookiesandredirecttheusertotheloginpage.ThefollowingcodeshowsacalltothecheckCredsservice:

/*chapter8/controllers.jsexcerpt*/

if(checkCreds()){

//dosomethingtocontinue

}

Page 172: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

DeletingUserCredentialsOurnextservicedeletestheuser’scredentialsoncetheuser’ssessionhasended,orwhentheuser’scredentialsfailedtoauthenticateduringaRESTservicecall.OncetheblogCredscookieisremoved,theAngularJSapplicationshouldredirecttheusertotheloginpage:

/*chapter8/non-RESTbusinesslogicservicetodeletecredentials*/

blogBusinessServices.factory('deleteCreds',

['$cookies',function($cookies){

returnfunction(){

$cookies.blogCreds="";

$cookies.blogUsername="";

};

}]);

Here’swhatacalltothedeleteCredsservicelookslike:

/*chapter8/controllers.jsexcerpt*/

deleteCreds();

Page 173: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

RetrievingUserCredentialsThefollowingcodeshowsabusinesslogicservicethatretrievestheuser’stokenfromtheblogCredscookie.AtokenpassedtoaRESTserviceintheHTTPSheadermustbeencodedwithbase64.Thebusinessserviceencodesthetokeninbase64andthenreturnsthatencodedtoken:

/*chapter8/non-RESTbusinesslogicservicetoretrievecredentials*/

blogBusinessServices.factory('getToken',

['$cookies',function($cookies){

returnfunction(){

varreturnVal="";

varblogCreds=$cookies.blogCreds;

if(blogCreds!==undefined&&blogCreds!==""){

returnVal=btoa(blogCreds);

}

returnreturnVal;

};

}]);

ThefollowingcodeshowshowthetokenreturnedfromtheserviceisusedtobuildtheBasicAuthenticationheaderwhenwe’recallingaRESTservice.ThislineshouldbedefinedbeforeeveryRESTservicecallthatrequiresauthentication.ThecallmakesuseoftheAngularJS$httpservice:

/*chapter8/controllers.jsexcerpt*/

$http.defaults.headers.common['Authorization']='Basic'+getToken();

ThefollowingcodeshowshowtousethegetTokenservicetoauthenticatetotheBlogservicewhenwearesavingablogpost:

/*chapter8/controllers.jsexcerpt*/

blogControllers.controller('NewBlogCtrl',

['$scope','checkCreds','$location','$http','getToken',

functionNewBlogCtrl($scope,checkCreds,$location,$http,getToken){

$http.defaults.headers.common['Authorization']='Basic'+getToken();

Blog.save({},

functionsuccess(response){

console.log("Success:"+JSON.stringify(response));

$scope.status=response;

},

functionerror(errorResponse){

console.log("Error:"+JSON.stringify(errorResponse));

}

);

}]);

Onefinalbusinesslogicservicethatwouldbeusefulisshownnext.Theserviceretrievestheuser’susernamefromtheblogUsernamecookie.Theusernameisthenreturnedforuseinmultipleplacesthroughouttheapplication.UsingthegetUsernameservicesimplifiesstoringandaccessingtheuser’susername:

/*chapter8/non-RESTbusinesslogicservicetoretrieveusername*/

Page 174: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

blogBusinessServices.factory('getUsername',

['$cookies',function($cookies){

returnfunction(){

varreturnVal="";

varblogUsername=$cookies.blogUsername;

if(blogUsername!==undefined&&blogUsername!==""){

returnVal=blogUsername;

}

returnreturnVal;

};

}]);

ItshouldbeobviousbynowthatAngularJSservicesareveryvaluabletohaveinanapplication.AnytimeAngularJSbusinesslogicneedstobeusedbymultiplecontrollers,thatlogicshouldbedefinedinservices.

Wewillnowaddeverythingthatwehavecoveredinthischapterintoonefile,calledbusinessServices.js,andaddtheservicesinthatfiletoourblogproject.InChapter10wewilladdaloginscreenandsecuritytoourblogapplication.Withsecurityinplace,wewillthendeployourapplicationtothecloudinChapter11.Beforewedeployourblogapplicationtothecloud,however,wewilladdnewscreensinChapter11toallowausertosubmitnewblogpostsandcomments.

Page 175: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

BlogApplicationBusinessLogicNow,toaddthenewbusinessservices,right-clicktheprojectnodeandaddanewJavaScriptfilenamedbusinessServices.jsunderthejsfolder.Hereisthecodethatshouldbeplacedinthenewlycreatedservicesfile.NoticethatwehavemadeAngularJScookiesavailablebyinjectingngCookies.AngularJScookiesareprovidedbyangular-cookies.min.js,whichwealreadyaddedtotheprojectearlier:

/*chapter8/businessServices.js*/

'usestrict';

/*businesslogicservicesonly*/

varblogBusinessServices=

angular.module('blogBusinessServices',['ngCookies']);

blogBusinessServices.factory('checkCreds',

['$cookies',function($cookies){

returnfunction(){

varreturnVal=false;

varblogCreds=$cookies.blogCreds;

if(blogCreds!==undefined&&blogCreds!==""){

returnVal=true;

}

returnreturnVal;

};

}]);

blogBusinessServices.factory('getToken',

['$cookies',function($cookies){

returnfunction(){

varreturnVal="";

varblogCreds=$cookies.blogCreds;

if(blogCreds!==undefined&&blogCreds!==""){

returnVal=btoa(blogCreds);

}

returnreturnVal;

};

}]);

blogBusinessServices.factory('getUsername',

['$cookies',function($cookies){

returnfunction(){

varreturnVal="";

varblogUsername=$cookies.blogUsername;

if(blogUsername!==undefined&&blogUsername!==""){

returnVal=blogUsername;

}

returnreturnVal;

};

}]);

blogBusinessServices.factory('setCreds',

['$cookies',function($cookies){

returnfunction(un,pw){

vartoken=un.concat(":",pw);

$cookies.blogCreds=token;

$cookies.blogUsername=un;

};

}]);

blogBusinessServices.factory('deleteCreds',

['$cookies',function($cookies){

returnfunction(){

$cookies.blogCreds="";

$cookies.blogUsername="";

};

}]);

Page 176: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

UsingtheBusinessLogicNowtoloadthenewbusinesslogicservices,wemustaddthebusinessServices.jsfiletothe<head>sectionofindex.html,asshownhere:

<!--chapter8/index.htmlexcerpt-->

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

Thecompleteindex.htmlfileisshownhereforconvenience:

<!--chapter8/index.html-->

<!DOCTYPEhtml>

<htmllang="en"ng-app="blogApp">

<head>

<title>AngularJSBlog</title>

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

<metahttp-equiv="Content-Type"content="text/html;charset=UTF-8">

<linkrel="stylesheet"href="lib-css/bootstrap.min.css"media="screen"/>

<linkrel="stylesheet"href="css/styles.css"media="screen"/>

<scriptsrc="js/libs/jquery-1.10.2.min.js"></script>

<scriptsrc="js/libs/bootstrap.min.js"></script>

<scriptsrc="js/libs/angular.min.js"></script>

<scriptsrc="js/libs/angular-route.min.js"></script>

<scriptsrc="js/libs/angular-resource.min.js"></script>

<scriptsrc="js/libs/angular-cookies.min.js"></script>

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

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

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

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

</head>

<body>

<divng-view></div>

</body>

</html>

WemustalsoaddthenewblogBusinessServicesmoduleasadependencyoftheapplicationatstartuptime.Wedothisusinginlinearrayannotations:

/*chapter8/app.js*/

'usestrict';

/*AppModule*/

varblogApp=angular.module('blogApp',[

'ngRoute',

'blogControllers',

'blogServices',

'blogBusinessServices'

]);

blogApp.config(['$routeProvider','$locationProvider',

function($routeProvider,$locationProvider){

$routeProvider.

when('/',{

templateUrl:'partials/main.html',

controller:'BlogCtrl'

}).when('/blogPost/:id',{

templateUrl:'partials/blogPost.html',

controller:'BlogViewCtrl'

});

$locationProvider.html5Mode(false).hashPrefix('!');

Page 177: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

}]);

Page 178: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

TestingServiceswithKarmaUnittestingservicesishowwefinddefectsearlyinthedevelopmentprocess.Infact,unittestsforeachindividualserviceshouldbewrittenwhentheserviceiswritten.Althoughourservicesinthischapterarenotoverlycomplicated,unittestingisstillveryimportant.WewillcontinuetouseKarmaforunittestinginthischapter.

Page 179: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

KarmaConfigurationWealreadyhaveaKarmaconfigurationfileforourblogproject,butweneedtomakeamodificationtothefiletoaccommodateAngularJScookiesinourKarmaunittestscripts.SincetheservicesinthischapterrelyonAngularJScookies,weneedtomakethekarma.conf.jsfileawareoftheangular-cookies.min.jsfileinourproject.

Thelineinthekarma.conf.jsfilethatmakesKarmaawareofAngularJScookiesisshownhere:

/*chapter8/karma.conf.jsexcerpt*/

files:[

...

"public_html/js/libs/angular-cookies.min.js",

...

],

Thecompletekarma.conf.jsfileisshownhere.Maketheneededchangetothekarma.conf.jsfileinyourblogproject,andthenwewilllookathowwetestournewbusinessservices:

/*chapter8/karma.conf.jscompletefile*/

module.exports=function(config){

config.set({

basePath:'../',

files:[

"public_html/js/libs/angular.min.js",

"public_html/js/libs/angular-mocks.js",

"public_html/js/libs/angular-route.min.js",

"public_html/js/libs/angular-resource.min.js",

"public_html/js/libs/angular-cookies.min.js",

"public_html/js/*.js",

"test/**/*Spec.js"

],

exclude:[

],

autoWatch:true,

frameworks:[

"jasmine"

],

browsers:[

"Chrome",

"Firefox"

],

plugins:[

"karma-junit-reporter",

"karma-chrome-launcher",

"karma-firefox-launcher",

"karma-jasmine"

]

});

};

Page 180: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

KarmaTestSpecificationsNowweneedtoaddunittestspecificationsforeachofthefivebusinesslogicservicesthatweaddedearlierinthechapter.Wewilltalkbrieflyabouteachindividualunittesttogainafullunderstandingofthetestspecifications.

FirstwewilltakealookattheunittestforthesetCredsservice.Ifyouremember,thesetCredsservicetakestwoparameters,theusernameandpassword.Wewilltesttheoperationoftheservicethoroughlyintheunitteststhatfollow,butfornowourunittestwillonlycheckthatthesetCredsservicecanbeinjected:

/*chapter8/businessServicesSpec.jsexcerpt-setCredsservice*/

describe('testsetCreds',function(){

var$rootScope;

varsetCreds;

beforeEach(module('blogBusinessServices'));

beforeEach(inject(function($injector){

$rootScope=$injector.get('$rootScope');

setCreds=$injector.get('setCreds');

setCreds("test","test");

}));

it('shouldtestsetCredsserviceexist',function(){

expect(setCreds).toBeDefined();

});

});

NextwewilllookattheunittestforthecheckCredsservice.TheunittestscriptusesboththesetCredsserviceandthecheckCredsservice.RecallthatthecheckCredsserviceusesAngularJScookies.Whencookiesarecreatedfromaunittestscript,thecookiescreatedexistonlyforthedurationofthetestscript.Whentheunittestscriptends,sodothecookies.OurcheckCredsunittestlookslikethis:

/*chapter8/businessServicesSpec.jsexcerpt-checkCredsservice*/

describe('testcheckCreds',function(){

var$rootScope;

varcheckCreds;

varsetCreds;

beforeEach(module('blogBusinessServices'));

beforeEach(inject(function($injector){

$rootScope=$injector.get('$rootScope');

checkCreds=$injector.get('checkCreds');

setCreds=$injector.get('setCreds');

setCreds("test","test");

}));

it('shouldtestsetCredsserviceexist',function(){

expect(checkCreds()).toEqual(true);

});

});

ThetestscriptfirstmakesacalltothesetCredsservice,passingausernameof“test”andapasswordof“test”asparameters.Thosevaluesarestoredinacookievalidonlyforthistestscriptrun.WethenvalidatethatthecheckCredsservicereturnstrue,indicatingthatboththesetCredsandcheckCredsservicecallsweresuccessful.Wecannowrestassured

Page 181: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

thatbothservicesareworkingasexpected.

NowwewilltakealookattheunittestforthegetTokenservice.Justasbefore,wemakeacalltothesetCredsserviceandpassausernameof“test”andapasswordof“test”totheservice.WethenmakeacalltothegetTokenservice.Thereturnedvaluefromtheserviceisabase64-encodedstringthatiscomposedoftheusernameandthepassword.Wewillonlyvalidatethatavalueisreturned,withthetoBeDefinedmethod:

/*chapter8/businessServicesSpec.jsexcerpt-getTokenservice*/

describe('testgetToken',function(){

var$rootScope;

vargetToken;

varsetCreds;

beforeEach(module('blogBusinessServices'));

beforeEach(inject(function($injector){

$rootScope=$injector.get('$rootScope');

getToken=$injector.get('getToken');

setCreds=$injector.get('setCreds');

setCreds("test","test");

}));

it('shouldtestsetCredsserviceexist',function(){

expect(getToken()).toBeDefined();

});

});

WhenwetestthegetUsernameservice,wecanactuallyvalidatethevaluesetfortheusername.ThefollowingcodeshowstheunittestforthegetUsernameservice.Justasbefore,wemakeacalltothesetCredsserviceandpassausernameof“test”andapasswordof“test.”WethenmakeacalltothegetUsernameserviceandvalidatethatitreturns“test”astheusername:

/*chapter8/businessServicesSpec.jsexcerpt-getUsernameservice*/

describe('testgetUsername',function(){

var$rootScope;

vargetUsername;

varsetCreds;

beforeEach(module('blogBusinessServices'));

beforeEach(inject(function($injector){

$rootScope=$injector.get('$rootScope');

getUsername=$injector.get('getUsername');

setCreds=$injector.get('setCreds');

setCreds("test","test");

}));

it('shouldtestsetCredsserviceexist',function(){

expect(getUsername()).toEqual("test");

});

});

Thelastunittestisshownnext.ItisatestofthedeleteCredsservice.InthistestscriptwemakeacalltothesetCredsservice,thenwecallthedeleteCredsservicetoremovethecredentialsthatwejustadded.WethencallthecheckCredsservicetovalidatethatnocredentialsarestoredbycheckingforareturnedvalueoffalse:

/*chapter8/businessServicesSpec.jsexcerpt-deleteCredsservice*/

Page 182: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

describe('testdeleteCreds',function(){

var$rootScope;

vardeleteCreds;

varsetCreds;

varcheckCreds;

beforeEach(module('blogBusinessServices'));

beforeEach(inject(function($injector){

$rootScope=$injector.get('$rootScope');

deleteCreds=$injector.get('deleteCreds');

setCreds=$injector.get('setCreds');

checkCreds=$injector.get('checkCreds');

setCreds("test","test");

deleteCreds();

}));

it('shouldtestsetCredsserviceexist',function(){

expect(checkCreds()).toEqual(false);

});

});

FollowingisthecompletebusinessServicesSpec.jsfile.Right-clicktheunitfolderunderthetestfolder,createanewJavaScriptfilenamedbusinessServicesSpec.js,andenterthecodeshownhere:

/*chapter8/businessServicesSpec.jscompletefile*/

describe('AngularJSBlogBusinessServiceTesting',function(){

describe('testsetCreds',function(){

var$rootScope;

varsetCreds;

beforeEach(module('blogBusinessServices'));

beforeEach(inject(function($injector){

$rootScope=$injector.get('$rootScope');

setCreds=$injector.get('setCreds');

setCreds("test","test");

}));

it('shouldtestsetCredsserviceexist',function(){

expect(setCreds).toBeDefined();

});

});

describe('testcheckCreds',function(){

var$rootScope;

varcheckCreds;

varsetCreds;

beforeEach(module('blogBusinessServices'));

beforeEach(inject(function($injector){

$rootScope=$injector.get('$rootScope');

checkCreds=$injector.get('checkCreds');

setCreds=$injector.get('setCreds');

setCreds("test","test");

}));

it('shouldtestsetCredsserviceexist',function()

expect(checkCreds()).toEqual(true);

});

});

describe('testgetToken',function(){

var$rootScope;

vargetToken;

varsetCreds;

beforeEach(module('blogBusinessServices'));

beforeEach(inject(function($injector){

$rootScope=$injector.get('$rootScope');

getToken=$injector.get('getToken');

setCreds=$injector.get('setCreds');

setCreds("test","test");

}));

it('shouldtestsetCredsserviceexist',function()

expect(getToken()).toBeDefined();

});

});

Page 183: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

describe('testgetUsername',function(){

var$rootScope;

vargetUsername;

varsetCreds;

beforeEach(module('blogBusinessServices'));

beforeEach(inject(function($injector){

$rootScope=$injector.get('$rootScope');

getUsername=$injector.get('getUsername');

setCreds=$injector.get('setCreds');

setCreds("test","test");

}));

it('shouldtestsetCredsserviceexist',function(){

expect(getUsername()).toEqual("test");

});

});

describe('testdeleteCreds',function(){

var$rootScope;

vardeleteCreds;

varsetCreds;

varcheckCreds;

beforeEach(module('blogBusinessServices'));

beforeEach(inject(function($injector){

$rootScope=$injector.get('$rootScope');

deleteCreds=$injector.get('deleteCreds');

setCreds=$injector.get('setCreds');

checkCreds=$injector.get('checkCreds');

setCreds("test","test");

deleteCreds();

}));

it('shouldtestsetCredsserviceexist',function(){

expect(checkCreds()).toEqual(false);

});

});

});

Page 184: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

KarmaTestingTheprecedingtestspecificationswilltestallthenewbusinesslogicservicesaddedinthischapter.ThecontrollertestspecificationandtheRESTservicetestspecificationunittestswillalsorunwhenKarmastarts.

Right-clicktheprojectandselect“Test”fromthemenu.Karmawillstart.YoushouldseebothChromeandFirefoxbrowserwindowsopen.TheNetBeanstestresultswindowshouldopenanddisplayninepassedtestsforChromeandninepassedtestsforFirefox.

Ifyougetanyerrormessagesorfailedtests,gobackoverthissectionandverifythatyoucompletedalltheconfigurationsandinstallations.YoucanalsodownloadtheChapter8codefromtheGitHubprojectsite.

Page 185: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

End-to-EndTestingWehaven’tyetaddedthebusinesslogicservicescreatedinthischaptertoourcontrollers,soweshouldseenochangeintheend-to-endtesting.WewillvalidatethatnoadverseissueswereintroducedinthischapterwithProtractor.

Page 186: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

ProtractorConfiguration

WealreadycreatedaProtractorconfigurationfilefortheblogapplicationinChapter5.TheProtractorconfigurationfileisshownhereforreference:

/*chapter8/conf.jsProtractorconfigurationfile*/

exports.config={

seleniumAddress:'http://localhost:4444/wd/hub',

specs:['e2e/blog-spec.js']

};

Page 187: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

ProtractorTestSpecification

NochangesarerequiredtotheProtractortestspecification,shownhereforreference:

/*chapter8/blog-spec.jsProtractortestspecification*/

describe("BlogApplicationTest",function(){

it("shouldtestthemainblogpage",function(){

browser.get(

"http://localhost:8383/AngularJsBlog/");

expect(browser.getTitle()).toEqual("AngularJSBlog");

//getsthebloglist

varblogList=

element.all(by.repeater('blogPostinblogList'));

//teststhesizeoftheblogList

expect(blogList.count()).toEqual(1);

browser.get(

"http://localhost:8383/AngularJsBlog/

#!/blogPost/5394e59c4f50850000e6b7ea");

expect(browser.getTitle()).toEqual("AngularJSBlog");

//getsthecommentlist

varcommentList=

element.all(by.repeater('commentinblogEntry.comments'));

//checksthesizeofthecommentList

expect(commentList.count()).toEqual(2);

});

});

Page 188: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

ProtractorTesting

Startanewcommandwindowandenterthiscommandtostartthetestserver:

webdriver-managerstart

OpenanewcommandwindowandnavigatetotherootoftheChapter5project.Typethecommand:

protractortest/conf.js

Youshouldseeabrowserwindowopen.Youshouldthenseethetestscriptnavigatethroughthepagesoftheblogapplication.WhentheProtractorscripthasfinished,thebrowserwindowwillclose.

YoushouldseeresultslikethefollowinginthecommandwindowwhentheProtractorscriptcompletes.Thenumberofsecondsthatittakesthescripttofinishwillvarydependingonyourparticularsystem:

Finishedin1.768seconds

1test,4assertions,0failures

Page 189: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

ConclusionThechangestoourblogapplicationmadeinthischaptergiveuseverythingweneedtoenableustoworkwithRESTserviceauthentication.Asmentionedbefore,ourAngularJSapplicationdoesn’tactuallyhandleauthentication,butinsteadholdsthestatusofauthentication.

Thebusinesslogicservicesthatweaddedinthischaptergreatlysimplifytheprocessoftrackingauthenticationacrossmultiplecontrollers.WewilltalkmoreaboutsecurityinChapter10.WewillnowmoveontoAngularJSdirectives.

Page 190: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development
Page 191: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

Chapter9.AngularJSDirectives

Fromauser’sperspective,directivesarenothingmorethancustomHTMLtagsthatareaddedtoapplicationtemplates.Directivescanbesimple,ortheycanbeverycomplex.DirectivesareusedbytheAngularJSHTMLcompilertoenhancethefunctionalityoftheassociatedtemplate.SomeexamplesofAngularJSdirectivesarengModel,ngView,andngRepeat.

Page 192: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

TheHTMLCompilerLet’stalkbrieflyabouttheAngularJSHTMLcompiler.TheuseofthewordcompilerinrelationtoAngularJSisoftenconfusingforexperienceddevelopersnewtotheframework.Experienceddevelopersdon’tnormallyassociatecompilerswithHTML.Thewordcompiler,however,takesonawholenewmeaninginthecontextofAngularJS.

CompilingHTMLinAngularJSissimplytheprocessofsearchingthroughtheDOMtreetoidentifyHTMLelementsassociatedwithdirectives.Thecompilerthenbuildsthetemplateandassignseventstotheassociatedelementsinthetemplate.This,however,isagreatlysimplifieddescriptionoftheAngularJSHTMLcompilerandthecompilerprocesses.Ifyouwouldliketoknowmoreaboutthecompiler,takealookattheAngularJSwebsitedocumentation,whichcoverstheHTMLcompileringreatdetail.

Page 193: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

WhatAreDirectives?DirectivesareveryvaluableinAngularJSandarewhatsetsAngularJSapartfrommostJavaScriptclient-sideframeworks.Thankstodirectives,wecanavoidcreatingmodelclasseswithhundredsoflinesofcode.Thankstodirectives,wehaveasimplifiedmodelandviewinAngularJSthatallowsdeveloperstoquicklycreatepowerfulJavaScriptapplications.

AlthoughbuildingcustomdirectivesinAngularJSisabitmorecomplextolearnthanotherareasoftheframework,Iwilltrytosimplifythelearningprocessbyshowingyouhowtocreateafairlysimpledirective.TherearecompletebooksthatcovertheAngularJSdirectivedesignprocess,soifyouhaveadesiretolearnaboutAngularJSdirectivesingreatdetail,abookthatcoversonlydirectiveswouldbeagoodstartingpointafteryoufinishthischapter.

Page 194: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

BuildingCustomDirectivesIfyourememberbackinChapter5,webuiltamenuforourblogapplicationandused<divng-includesrc="'partials/menu.html'"></div>toincludethatmenuintoeachtemplate.Themenuwasdefinedinthemenu.htmlfileasHTML.Whilethatapproachworkswellandisacommonwaytoaddanapplicationmenu,thereisanotherwaytoaddamenuthatisabitmoreelegant.

Ournewmenuapproachwillinvolvebuildingacustomdirectivetohandletheinclusionofamenuintoourtemplates.Firstwemustaddanewdirectivesfiletoourblogproject.Wethendefinethenewdirectiveandinjectthedirectiveintoourapplication.Oncethatisdone,wecanreplace<divng-includesrc="'partials/menu.html'"></div>withatagthatusesourcustomdirective.

Openyoureditor,right-clicktheapplicationnode,andcreateanewJavaScriptfilenameddirectives.jsunderthejsfolder.Thecodetoplaceinthefileisshownnext.Wewillwalkthroughthecode,andI’llexplainhowthedirectiveactuallyworks.Wewillthenconfigureourblogapplicationtousethenewdirectiveandseeitinaction:

/*chapter9/directives.js*/

'usestrict';

/*Directives*/

varblogDirectives=

angular.module('blogDirectives',[]);

blogDirectives.directive('blgMenu',function(){

return{

restrict:'A',

templateUrl:'partials/menu.html',

link:function(scope,el,attrs){

scope.label=attrs.menuTitle;

}

};

});

FirstwemustcreateanewmodulenamedblogDirectives.Wewillthencreateanewdirectiveonthatmodule.WepassboththedirectivenameandacallbackfunctiontothedirectivescallontheblogDirectivesmodule.

Page 195: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

NamingConventionsforDirectivesTakenoticeofthecamelcasedirectivenameblgMenu.SinceHTMLiscase-insensitive,werefertothenewdirectiveinsideanHTMLtemplatefileasblg-menu.TheAngularJSHTMLcompilerthennormalizesthedirectivenameintoitscamelcaseequivalent,blgMenu.

Alsotakenoticeoftheblgprefixonthenewdirectivename.Alldirectivenamesusedintemplatesmustbeunique.DirectivenamescannotmatchanyexistingHTMLtagname,oranyfutureHTMLtagname.CustomdirectivesalsocannotusethengprefixalreadyusedbyAngularJSdirectives.

So,wemustuseauniquedirectivenamethatwon’tconflictwithcurrentorfutureHTMLnamesorwithAngularJSdirectivenames.Thebestwaytodothatistouseauniquenameprefixforcustomdirectives.Wewilluseblgforourprefixbecauseitisunlikelytocauseaproblemnoworinthefuture.

Page 196: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

TheRestrictOptionAlsotakenoticeofthelinerestrict:'A'inourdirective.Thatisknownastherestrictoption.TherestrictoptionishowAngularJStriggersthedirectiveinsideatemplate.Thevalueof"A"causesthedirectivetobetriggeredontheattributename.Thefollowingtableshowsallthepossiblevaluesfortherestrictoption.Thedefaultvaluefortherestrictoptionis'A'.

Table9-1.Restrictoption

Value UsageinAngularJS

'A' Onlymatchtheattributename(<divblg-menu></div>)(default)

'E' Onlymatchtheelementname(<blg-menu></blg-menu>)

'C' Onlymatchtheclassname(<divclass="blg-menu"></div>)

'M' Onlymatchthecommentname(<!--directive:blg-menu-->)

Page 197: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

TheTemplateURLAlsonoticetheattributeassignmenttemplateUrl:'partials/menu.html'.ThetemplateUrlattributetellstheAngularJSHTMLcompilertoreplacethedirectiveblg-menuinsideatemplatewithHTMLcontentlocatedinsideaseparatefile.Theblg-menuattributewillbereplacedwiththecontentofouroriginalmenutemplatefile(partials/menu.html).

Thereisonesmallchangethatneedstobemadeinthemenutemplatefiletoallowustopassthesitetitletothedirectiveasanargument.Iwillshowthatchangeshortly.Passingthetitleasanargumentisnotrequiredorevenneeded,butIshowitheretohelpexplainhowdirectiveswork.

Page 198: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

TemplateAttributesThefollowingcodeshowshowwepassmenu-titleasanargumenttoournewdirective.Allvaluesarepassedtothemethodnamedlinkasaparameternamedattrs.Wegainaccesstothetitlevaluebyassigningthevalueofattrs.menuTitletoascopeproperty:

/*chapter9/directives.jsexcerpt*/

link:function(scope,el,attrs){

scope.label=attrs.menuTitle;

}

Thescopeispassedasanargumenttothelinkmethodandisaccessibleinsidethemethod,asseenbytheassignmentofthemenuTitleattribute.Directivesareusedinsideatemplateasshownnext,inthemain.htmltemplate.blg-menuisthenameofthedirective,andmenu-titleisthenamepassedtothedirectiveasthetitleattributeofthenewdirective.TheAngularJSHTMLcompileralsonormalizestheattributenameintoitscamelcaseform,soitbecomesmenuTitleinsidethetemplate(asshownbeforeinthetemplatecodefromdirectives.js):

<!--chapter9/main.htmlexcerpt-->

<divblg-menumenu-title="AngularJSBlog"></div>

Page 199: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

AddingtheCustomDirectiveNowwemustconfigureourblogapplicationtousethenewlycreatedcustomdirective.Toloadthenewdirectivesfile,weneedtoaddonelineintheindex.htmlfile:

<!--chapter9/index.htmlexcerpt-->

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

Thecompleteindex.htmlfileisshownhereforconvenience:

<!--chapter9/index.html-->

<!DOCTYPEhtml>

<htmllang="en"ng-app="blogApp">

<head>

<title>AngularJSBlog</title>

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

<metahttp-equiv="Content-Type"content="text/html;charset=UTF-8">

<linkrel="stylesheet"href="lib-css/bootstrap.min.css"media="screen"/>

<linkrel="stylesheet"href="css/styles.css"media="screen"/>

<scriptsrc="js/libs/jquery-1.10.2.min.js"></script>

<scriptsrc="js/libs/bootstrap.min.js"></script>

<scriptsrc="js/libs/angular.min.js"></script>

<scriptsrc="js/libs/angular-route.min.js"></script>

<scriptsrc="js/libs/angular-resource.min.js"></script>

<scriptsrc="js/libs/angular-cookies.min.js"></script>

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

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

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

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

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

</head>

<body>

<divng-view></div>

</body>

</html>

Wealsoneedtomakeachangetotheapp.jsfile.WeaddthenewblogDirectivesmoduleasadependencyoftheapplicationatstartuptime,usinginlinearrayannotations:

/*chapter9/app.js*/

'usestrict';

/*AppModule*/

varblogApp=angular.module('blogApp',[

'ngRoute',

'blogControllers',

'blogServices',

'blogBusinessServices',

'blogDirectives'

]);

blogApp.config(['$routeProvider','$locationProvider',

function($routeProvider,$locationProvider){

$routeProvider.

when('/',{

templateUrl:'partials/main.html',

controller:'BlogCtrl'

}).when('/blogPost/:id',{

templateUrl:'partials/blogPost.html',

controller:'BlogViewCtrl'

Page 200: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

});

$locationProvider.html5Mode(false).hashPrefix('!');

}]);

Nowwemustmodifyourtemplatefilestousethenewlycreatedcustomdirective.Inthemain.htmltemplatefile,wereplacetheline<divng-includesrc="'partials/menu.html'"></div>withthelineshownhere:

<!--chapter9/main.htmlexcerpt-->

<divblg-menumenu-title="AngularJSBlog"></div>

Thecompletemain.htmlfileisshownhereforconvenience:

<!--chapter9/main.html-->

<divblg-menumenu-title="AngularJSBlog"></div>

<divid="container"class="container">

<divclass="blog-post-label">BlogPosts</div>

<divclass="post-wrapper">

<divng-repeat="blogPostinblogList">

<divclass="blog-post-outer">

<divclass="blog-intro-text">

Posted:{{blogPost.date|date:'MM/dd/yyyy@h:mma'}}

</div>

<divclass="blog-intro-text">

{{blogPost.introText}}

</div>

<divclass="blog-read-more">

<ahref="#!blogPost/{{blogPost._id}}">ReadMore</a>

</div>

</div>

</div>

</div>

</div>

WemakethesamechangetotheblogPost.htmltemplate,asshownhere:

<!--chapter9/blogPost.html-->

<divblg-menumenu-title="AngularJSBlog"></div>

<divid="container"class="container">

<divclass="blog-post-label">BlogEntry</div>

<divclass="blog-entry-wrapper">

<divclass="blog-intro-text">

Posted:{{blogEntry.date|date:'MM/dd/yyyy@h:mma'}}

</div>

<divclass="blog-entry-outer">

{{blogEntry.blogText}}

</div>

<divclass="blog-comment-wrapper">

<divclass="blog-comment-label">BlogComments</div>

<divclass="blog-entry-comments"ng-repeat="commentinblogEntry.comments">

{{comment.commentText}}

</div>

</div>

Page 201: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

</div>

</div>

Page 202: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

PassingtheTitleAttributeFinally,wemustmakeonelastchangetothemenu.htmltemplatefiletomakeuseofthetitlevaluepassedtothedirectiveinthemenu-titleattribute.Replacethehardcodedtitlewith{{label}},asshownhere:

<!--chapter9/menu.html-->

<navclass="navbarnavbar-inversenavbar-fixed-top"role="navigation">

<!--Brandandtogglegetgroupedforbettermobiledisplay-->

<divclass="container">

<divclass="navbar-header">

<buttontype="button"

class="navbar-toggle"data-

toggle="collapse"

data-target=".navbar-collapse">

<spanclass="sr-only">Togglenavigation</span>

<spanclass="icon-bar"></span>

<spanclass="icon-bar"></span>

<spanclass="icon-bar"></span>

</button>

<aclass="navbar-brand"style="{{brandColor}}"href="#!/">{{label}}</a>

</div>

<!--Collectthenavlinks,forms,andothercontentfortoggling-->

<divclass="collapsenavbar-collapse">

<ulclass="navnavbar-nav">

<liclass="{{aboutActiveClass}}"><ahref="#!about">About</a></li>

<liclass="">

<ahref="https://github.com/KenWilliamson">DownloadProjectCode</a>

</li>

</ul>

</div><!--/.navbar-collapse-->

</div>

</nav>

Withthischangemade,wecanruntheapplicationandtestournewmenu.

Page 203: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

RunningtheBlogApplicationNowwewillrunourblogprojecttocheckthatallchangesweremadesuccessfully.Saveallyourchangesandright-clicktheprojectnode.Select“Run”fromthemenu,andtheapplicationshouldlaunch.Ifallchangesweremadecorrectly,youshouldseethemenubaracrossthetopofthepagejustasbefore.

Turnondevelopertoolsforyourbrowserandcheckforanyerrors.Ifyouhaveanyproblems,gooverwhatwecoveredandvalidatethatallthechangesweremadecorrectly.Ifyouhaveissuesthatcan’tberesolved,downloadthecodeforChapter9fromtheprojectsite.Runthedownloadedprojecttoseethechangesmadeinthischapter,andcompareittoyourcodetofindandfixanyissues.

Page 204: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

TestingDirectiveswithKarmaWritingatestspecificationforadirectivethatusesanexternalHTMLtemplatefileisabitmorecomplicatedthanwritingmosttestspecifications.ThetestscriptwillfailwhenittriestoloadthetemplatefileusingHTTPfromtheserver.IfyouweretousehardcodedHTMLforthemenuinsidethedirective,everythingwouldworkfine.NotsowithexternalHTMLtemplates,however.

OnewayaroundtheproblemistouseapreprocessorthatconvertsourHTMLtemplatefileintoaJavaScriptstringandthengeneratesanAngularJSmodulefromthatstring.Thepreprocessedmoduleisthenloadedintothe$templateCacheandmadeavailabletoKarma.Thatwaywecanusethecachedversionofourtemplatefileandourdirectiveworksasexpected.

Onewaytohandlethepreprocessingistousethekarma-ng-html2js-preprocessorKarmaplugin.Althoughthepluginisabittrickytoconfigureproperly,itquicklysolvestheexternaltemplateproblem.Payparticularattentiontothewaythepluginisconfigured.IfyouareusinganIDEotherthanNetBeans,youmayneedtolookfordocumentationspecifictoyourIDE.

Page 205: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

KarmaConfigurationFirst,weneedtoeditthepackage.jsonfileusedtoconfigureNode.jsdependencies.Hereistheneededchange:

/*chapter9/package.jsonexcerpt*/

"karma-ng-html2js-preprocessor":"~0.1"

Thecompletepackage.jsonfileisshownnext.Theaddedlinemakesthekarma-ng-html2js-preprocessorpluginaNode.jsdependency.ThemoduleisthenaccessibletoKarma.Edittheexistingblogprojectpackage.jsonfileandaddtherequiredlineasshown:

{

"name":"package.json",

"devDependencies":{

"karma":"*",

"karma-chrome-launcher":"*",

"karma-firefox-launcher":"*",

"karma-jasmine":"*",

"karma-junit-reporter":"*",

"karma-coverage":"*",

"karma-ng-html2js-preprocessor":"~0.1"

}

}

Afterwechangethepackage.jsonfile,weneedtousenpmtoinstalltheplugin.

OpenanewcommandwindowandnavigatetotherootoftheChapter9project.Youshouldseethepackage.jsonfilewhenyoulistoutthefilesinthefolder.

Nowtypethefollowingcommandtoinstallthekarma-ng-html2js-preprocessorplugindefinedinthepackage.jsonfile:

npminstall

Weneedtomakeseveralchangestothekarma.conf.jsfilethatwecreatedearlier.Thechangesareconfigurationchangesforthenewpluginjustinstalled;theyaresubtlebutimportant.

First,noticeinthefollowingcodethatwe’veaddedanewlineinthefilessection.Thenewline,'public_html/partials/*.html',tellsthepluginwheretofindthetemplatefileusedinourdirective:

/*chapter9/karma.conf.jsexcerpt*/

files:[

"public_html/js/libs/angular.min.js",

"public_html/js/libs/angular-mocks.js",

"public_html/js/libs/angular-route.min.js",

"public_html/js/libs/angular-resource.min.js",

"public_html/js/libs/angular-cookies.min.js",

"public_html/js/*.js",

"public_html/partials/*.html",

"test/**/*Spec.js"

]

Page 206: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

Wemustalsoaddapreprocessorssectiontothefile.TheentryinthissectionmapsthelocationofthetemplatefilestothenewKarmaplugin:

/*chapter9/karma.conf.jsexcerpt*/

preprocessors:{

'public_html/partials/*.html':['ng-html2js']

}

Next,weneedtoaddthenewplugintothelistofKarmaplugins,asshownhere—thelastlinetellsKarmathatthispluginwillbeused:

/*chapter9/karma.conf.jsexcerpt*/

plugins:[

"karma-junit-reporter",

"karma-chrome-launcher",

"karma-firefox-launcher",

"karma-jasmine",

"karma-ng-html2js-preprocessor"

]

Thereisonemorechangethatweneedtomaketothekarma.conf.jsfile.Weneedtotellthenewplugintostrip"public_html/"fromthepathtothetemplatefiles:

/*chapter9/karma.conf.jsexcerpt*/

ngHtml2JsPreprocessor:{

stripPrefix:'public_html/'

}

Followingisthecompletemodifiedkarma.conf.jsfile.Openthekarma.conf.jsfileintheblogprojectandmaketheneededchanges:

/*chapter9/karma.conf.jscompletefile*/

module.exports=function(config){

config.set({

basePath:'../',

files:[

"public_html/js/libs/angular.min.js",

"public_html/js/libs/angular-mocks.js",

"public_html/js/libs/angular-route.min.js",

"public_html/js/libs/angular-resource.min.js",

"public_html/js/libs/angular-cookies.min.js",

"public_html/js/*.js",

"public_html/partials/*.html",

"test/**/*Spec.js"

],

preprocessors:{

'public_html/partials/*.html':['ng-html2js']

},

exclude:[

],

autoWatch:true,

frameworks:[

"jasmine"

],

browsers:[

"Chrome",

"Firefox"

],

plugins:[

"karma-junit-reporter",

Page 207: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

"karma-chrome-launcher",

"karma-firefox-launcher",

"karma-jasmine",

"karma-ng-html2js-preprocessor"

],

ngHtml2JsPreprocessor:{

stripPrefix:'public_html/'

}

});

};

Page 208: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

KarmaTestSpecificationNowweneedtoaddanewtestspecificationtotheblogproject.Dothefollowing:

1. Right-clicktheunitfolderunderthetestfolderandaddanewJavaScriptfilenameddirectivesSpec.jstotheproject.

2. CopythiscodeintothenewdirectivesSpec.jsfile:

/*chapter9/directivesSpec.js*/

describe('AngularJSBlogApplication',function(){

beforeEach(module('blogDirectives'));

describe('UnittestofMenuDirective',function(){

varrootScope,compile;

//TheexternaltemplatefilereferencedbytemplateUrl

beforeEach(module('partials/menu.html'));

beforeEach(inject(function(_$compile_,_$rootScope_){

compile=_$compile_;

rootScope=_$rootScope_;

}));

it('Replacesthemenuattributewiththemenu',function(){

varelm=angular.element(

"<divblg-menumenu-title=\"AngularJSBlog\"></div>");

varmenu=compile(elm)(rootScope);

rootScope.$digest();

expect(menu.html()).toContain("AngularJSBlog");

});

});

});

Thiscodediffersabitfromthetestspecificationsthatwehaveseensofar.RememberthatdirectivesneedtobecompiledbytheHTMLcompiler.Thetestspecificationaccountsforthatneed.

First,noticeinthelineshownherethatweloadtheAngularJSmodulethatrepresentsthetemplateHTMLfilethatisneededbythedirective.RememberthatthetemplateHTMLfilewasconvertedtoaJavaScriptstring,andthenthatstringwasusedbytheKarmapreprocessorplugintogenerateanAngularJSmodule:

/*chapter9/directivesSpec.jsexcerpt*/

//TheexternaltemplatefilereferencedbytemplateUrl

beforeEach(module('partials/menu.html'));

AlsonoticethatwenowinjecttheHTMLcompilerwith_$compile_.WealsoinjecttherootScopewith_$rootScope_:

/*chapter9/directivesSpec.jsexcerpt*/

Page 209: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

beforeEach(inject(function(_$compile_,_$rootScope_){

compile=_$compile_;

rootScope=_$rootScope_;

}));

Recallthatwhenweincludedournewdirectiveinsidethemain.htmltemplate,weusedtheline<divblg-menumenu-title="AngularJSBlog"\></div>toincludethenewdirective-basedmenuintothepage.Thefollowingcodeshowsthatsamelinegettingpassedtotheangular.elementmethod:

/*chapter9/directivesSpec.jsexcerpt*/

varelm=angular.

element("<divblg-menumenu-title=\"AngularJSBlog\"></div>");

varmenu=compile(elm)(rootScope);

rootScope.$digest();

Theresultingelmvariableisthenpassedtothecompileralongwiththerootscopereference,asshownhere.Thenwecall$digest,andthattellsAngularJStoupdatebindingsandfireanywatches.

Finally,weevaluatetheHTMLbycallingthemenu.htmlmethodandlookingforthetitlethatwepassedtothedirectivewithmenu-title="AngularJSBlog".

Page 210: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

KarmaTestingNow,withallthechangesmadetotheblogproject,wearereadytotestournewdirective.

Right-clicktheprojectandselect“Test”fromthemenu.Karmawillstart.YoushouldseebothChromeandFirefoxbrowserwindowsopen.TheNetBeanstestresultswindowshouldopenanddisplay10passedtestsforChromeand10passedtestsforFirefox.

Ifyougetanyerrormessagesorfailedtests,gobackoverthissectionandverifythatyoucompletedalltheconfigurationsandinstallations.YoucanalsodownloadtheChapter9codefromtheGitHubprojectsite.

Page 211: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

End-to-EndTestingWewillmakeonesmallchangetoallowustotestthenewdirective-basedmenuduringend-to-endtesting.ThemodificationwillinvolveourProtractortestscriptclickingthemainmenulinkafternavigatingtoablogentry.

Page 212: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

ProtractorConfigurationWealreadycreatedaProtractorconfigurationfilefortheblogapplicationinChapter5.TheProtractorconfigurationfileisshownhereforreference:

/*chapter9/conf.jsProtractorconfigurationfile*/

exports.config={

seleniumAddress:'http://localhost:4444/wd/hub',

specs:['e2e/blog-spec.js']

};

Page 213: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

ProtractorTestSpecificationWewillmakeasmallchangetothetestspecification,shownnext.Noticethelastlineinthefile.Thelineusesthenavbar-brandCSSclasstolookupthelinktothemainpage.Thescriptthenclicksthelinkandnavigatesbacktothemainpage.Thetestvalidatesthatthenewmenuisworkingcorrectly:

/*chapter9/blog-spec.js*/

describe("BlogApplicationTest",function(){

it("shouldtestthemainblogpage",function(){

browser.get("http://localhost:8383/AngularJsBlogChapter9/");

expect(browser.getTitle()).toEqual("AngularJSBlog");

//getsthebloglist

varblogList=element.all(by.repeater('blogPostinblogList'));

//teststhesizeoftheblogList

expect(blogList.count()).toEqual(1);

browser.get(

"http://localhost:8383/AngularJsBlogChapter9/

#!/blogPost/5394e59c4f50850000e6b7ea");

expect(browser.getTitle()).toEqual("AngularJSBlog");

//getsthecommentlist

varcommentList=

element.all(by.repeater('commentinblogEntry.comments'));

//checksthesizeofthecommentList

expect(commentList.count()).toEqual(2);

element(by.css('.navbar-brand')).click();

});

});

Page 214: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

ProtractorTestingWiththosechangesmade,wearereadytostarttheend-to-endtesting.

Startanewcommandwindowandenterthiscommandtostartthetestserver:

webdriver-managerstart

OpenanewcommandwindowandnavigatetotherootoftheChapter9project.Typethecommand:

protractortest/conf.js

Youshouldseeabrowserwindowopen.Youshouldthenseethetestscriptnavigatethroughthepagesoftheblogapplication.WhentheProtractorscripthasfinished,thebrowserwindowwillclose.

YoushouldseeresultslikethefollowinginthecommandwindowwhentheProtractorscriptcompletes.Thenumberofsecondsthatittakesthescripttofinishwillvarydependingonyourparticularsystem:

Finishedin1.91seconds

1test,4assertions,0failures

Page 215: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

ConclusionInthischapteryoulearnedhowtocreateacustomAngularJSdirective.YoualsolearnedhowtowritetestspecificationsforAngularJSdirectives.Wemadealltheneededchangestoourblogapplicationtoaddanewdirective-basedmenutoourblog.

Onceyourblogapplicationisrunningcorrectly,wecanmoveon.Thisconcludesourdiscussionofdirectives;we’llstartaddingsecurityfeaturestoourblogapplicationinthenextchapter.

Page 216: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development
Page 217: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

Chapter10.AngularJSSecurity

YoumightwonderwhywearecoveringsecurityinabookonAngularJS.Well,quitesimply,securityisoneofthemostimportantandmostchallengingtasksfacedbyanAngularJSdeveloper.It’snotthatthedeveloperisactuallyresponsibleforimplementingthesecuritylayer—thatisnotthecaseatall—butitisveryimportantforanAngularJSdevelopertounderstandtherolethatAngularJSplaysintheoverallsecuritymodelofanapplicationorwebsite.

Youshouldneverattempttoimplementanindependentclient-sidesecuritylayerinanAngularJSapplication,oranyotherJavaScriptapplicationforthatmatter.Securityshouldalwaysbeimplementedonthebackendserviceswherethedataresides.Thatistheonlysafeplacetoimplementasecuritylayer.

RemembertheuserhasfullaccesstotheJavaScriptrunninginthebrowser.AsIsaidbefore,ourAngularJSapplicationrunsintheuser’sbrowserontheuser’shardware.TheusercansavetheJavaScriptlocallyandeasilymakemodificationscircumventinganysecuritylayerimplementedbyanunsuspectingJavaScriptdeveloper.

Withthatinmind,thereareseveralrulesthatAngularJSdevelopersandbackenddevelopersneedtoremember.AlthoughactuallyimplementingthesecuritylayerisnotusuallythejobofanAngularJSdeveloper,itisoftenacollaborativeeffortforalldevelopersinvolvedinaproject.Thefollowingrulesshouldalwaysbeconsidered:

1. AlwaysuseSSLtocommunicatewithRESTservicesthatcontainprivatedata(HTTPS).

2. AlwaysusesometypeofauthenticationoneachRESTservicecallthatcontainsprivatedata(BasicAuthentication,forexample).

3. NeverholdRESTserviceauthenticationstatusinasessionvariableontheserver.Doingthatopensyourserver-sideapplicationuptocross-originattacksandotherserioussecurityconcerns.

4. NeverimplementaCross-OriginResourceSharing(CORS)layerthatreturns*asthelistofalloweddomains.Forexample,(Access-Control-Allow-Origin:*)wouldallowalldomainstomakecross-origincallstotheRESTservicesonthesite.Doingthatcircumventsthebrowser’sCORSsecurityimplementationcompletely.

5. AlwaysmakesurethatanyJavaScriptthatmaygetinjectedinsideaJSONpropertydoesnotgetexecutedontheserverside.ThisdesignflawisatthecoreoftheNoSQLinjectionattack,whereJavaScriptfunctionsareinjectedintheJSONrequestofaserviceandunknowinglyexecutedbytheserver,inordertobreachthesecurityofaNoSQLdatabase.

Page 218: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

Alwaysrememberthatanysecurity-relatedJavaScriptcodecanbeviewedandmodifiedbytheuser.Whilemostmodernbrowsersdoofferbuilt-insecurity,JavaScriptdevelopersshouldneverrelyonthebrowserforsecurity.Theresponsibilityforsecurityrestsentirelyontheshouldersofthebackendservicedevelopers.Withthatsaid,IwillshowsometechniquesfordevelopingAngularJSapplicationsthatworkwellwithasecuritylayerimplementedproperlyinthebackendservices.

Page 219: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

AuthenticationWewillstartourdiscussionofsecuritybybuildingaloginscreenandtheassociatedcontrollerandserviceforourblogapplication.Wewillsendtheuser’scredentialstoaloginRESTserviceforvalidation.WewillalsomakeuseofthebusinesslogicservicesthatwedevelopedbackinChapter8.

Wedon’tactuallyuseHTTPSforourblogapplicationbecauseit’snotaproductionapplication.Butinaproductionenvironment,SSLshouldalwaysbeusedtoprotectprivatedataandtheuser’scredentialswhencallingaloginRESTservice.AdditionalsecuritystepscouldevenbetakenintheRESTservicestolimitaccesstoaparticularmachineoraparticularIPaddress.Wewillnot,however,beconcernedwiththatlevelofsecurityinthisbook.

Page 220: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

AddingaLoginServiceWewillstartoffbyaddinganAngularJSloginservice.Openyoureditorandaddthefollowingcodetothebottomofyourproject’sservices.jsfile.ThenewAngularJSloginservicemapstoaloginRESTserviceonourbackendserver.ThecodeismuchlikethatoftheotherAngularJSserviceswe’vesetupsofar.Ithasonemethod,login,thatmapstoaPOSTmethodontheRESTservice:

/*chapter10/services.jsexcerpt*/

blogServices.factory('Login',['$resource',

function($resource){

return

$resource(

"http://nodeblog-micbuttoncloud.rhcloud.com/NodeBlog/login",

{},{

login:{method:'POST',cache:false,isArray:false}

});

}]);

Page 221: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

AddingaLoginControllerNowweneedtoaddalogincontroller.Openyoureditorandaddthecodeshownnexttothebottomofthecontrollers.jsfile.NoticethatweinjectthenewLoginserviceandthesetCredsbusinesslogicservicethatwedevelopedbackinChapter8.Wealsoinjectthe$locationservicetoallowustoredirecttheuseronceauthenticated.Thenewcontrollerhasasubmitmethodthatisattachedtothescope.Attachingthemethodtothescopeallowsustocallthemethodfrominsidethelogintemplate.WebuildtheJSONrequestthatgetspassedtotheserviceinthevariablenamedpostData,usingthescopepropertiessubmittedbytheform:

/*chapter10/controllers.jsexcerpt*/

blogControllers.controller('LoginCtrl',

['$scope','$location','Login','setCreds',

functionLoginCtrl($scope,$location,Login,setCreds){

$scope.submit=function(){

$scope.sub=true;

varpostData={

"username":$scope.username,

"password":$scope.password

};

Login.login({},postData,

functionsuccess(response){

console.log("Success:"+JSON.stringify(response));

if(response.authenticated){

setCreds($scope.username,$scope.password)

$location.path('/');

}else{

$scope.error="LoginFailed"

}

},

functionerror(errorResponse){

console.log("Error:"+JSON.stringify(errorResponse));

}

);

};

}]);

Wealsoaddascopepropertynamederror.Thispropertyispopulatedanytimetheuserfailstoauthenticate,displayinga“LoginFailed”message.Wewillseehowtheerrorispresentedlaterinthechapter.Oncetheuserauthenticates,wemakeacalltotheAngularJSbusinesslogicservicesetCredsandpasstheuser’susernameandpasswordtobesavedinacookie.Wethenredirecttheusertothemainapplicationlink.

Page 222: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

SecurityModificationstoOtherControllersWemustalsomakeminormodificationstotheothertwocontrollersinourblogproject.Openyoureditorandreplacethetwocontrollersaddedearlierwiththecodeshownnext.Noticewenowinjectthe$locationserviceandthecheckCredsbusinessservicethatweaddedbackinChapter8.ThecheckCredsserviceworksbycheckingtheuser’scredentialsatthetopofthecontroller.Iftheuserhasnotauthenticated,acallismadetothepathmethodonthe$locationservicetoredirecttheusertotheloginpage(wewillcoverthenewloginpathshortly):

/*chapter10/controllers.jsexcerpt*/

blogControllers.controller('BlogCtrl',

['$scope','BlogList','$location','checkCreds',

functionBlogCtrl($scope,BlogList,$location,checkCreds){

if(!checkCreds()){

$location.path('/login');

}

BlogList.get({},

functionsuccess(response){

console.log("Success:"+JSON.stringify(response));

$scope.blogList=response;

},

functionerror(errorResponse){

console.log("Error:"+JSON.stringify(errorResponse));

}

);

}]);

blogControllers.controller('BlogViewCtrl',

['$scope','$routeParams','BlogPost','$location','checkCreds',

functionBlogViewCtrl($scope,$routeParams,BlogPost,

$location,checkCreds){

if(!checkCreds()){

$location.path('/login');

}

varblogId=$routeParams.id;

BlogPost.get({id:blogId},

functionsuccess(response){

console.log("Success:"+JSON.stringify(response));

$scope.blogEntry=response;

},

functionerror(errorResponse){

console.log("Error:"+JSON.stringify(errorResponse));

}

);

}]);

Page 223: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

AddingaLogoutControllerWehaveonemorechangetomaketothecontrollers.jsfile:weneedtoaddanewcontrollertologtheuseroutofthesystemandresethiscredentials.Addthecodeshownheretothebottomofthecontrollers.jsfile.Onceagain,wemakeuseoftheAngularJSbusinesslogicserviceswrittenbackinChapter8byaddingacalltothedeleteCredsservice.Theservicecallremovestheuser’scredentials,andthenweredirecttheusertotheloginpage:

/*chapter10/controllers.jsexcerpt*/

blogControllers.controller('LogoutCtrl',

['$location','deleteCreds',

functionLogoutCtrl($location,deleteCreds){

deleteCreds();

$location.path('/login');

}]);

Theentirecontrollers.jsfileisshownheretohelpmakethechangesclearer:

/*chapter10/controllers.js*/

'usestrict';

/*Controllers*/

varblogControllers=

angular.module('blogControllers',[]);

blogControllers.controller('BlogCtrl',

['$scope','BlogList','$location','checkCreds',

functionBlogCtrl($scope,BlogList,$location,checkCreds){

if(!checkCreds()){

$location.path('/login');

}

BlogList.get({},

functionsuccess(response){

console.log("Success:"+JSON.stringify(response));

$scope.blogList=response;

},

functionerror(errorResponse){

console.log("Error:"+JSON.stringify(errorResponse));

});

}]);

blogControllers.controller('BlogViewCtrl',

['$scope','$routeParams','BlogPost','$location','checkCreds',

functionBlogViewCtrl($scope,$routeParams,BlogPost,

$location,checkCreds){

if(!checkCreds()){

$location.path('/login');

}

varblogId=$routeParams.id;

BlogPost.get({id:blogId},

functionsuccess(response){

console.log("Success:"+JSON.stringify(response));

$scope.blogEntry=response;

},

functionerror(errorResponse){

console.log("Error:"+JSON.stringify(errorResponse));

});

}]);

blogControllers.controller('LoginCtrl',

Page 224: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

['$scope','$location','Login','setCreds',

functionLoginCtrl($scope,$location,Login,setCreds){

$scope.submit=function(){

$scope.sub=true;

varpostData={

"username":$scope.username,

"password":$scope.password

};

Login.login({},postData,

functionsuccess(response){

console.log("Success:"+JSON.stringify(response));

if(response.authenticated){

setCreds($scope.username,$scope.password)

$location.path('/');

}else{

$scope.error="LoginFailed"

}

},

functionerror(errorResponse){

console.log("Error:"+JSON.stringify(errorResponse));

});

};

}]);

blogControllers.controller('LogoutCtrl',

['$location','deleteCreds',

functionLogoutCtrl($location,deleteCreds){

deleteCreds();

$location.path('/login');

}]);

Next,wewilladdedanewlogintemplateandtheassociatedCSS.Wewillthenaddtwonewpathstothe$routeProvidersectionoftheapp.jsfile.

Page 225: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

AddingaLoginTemplateRight-clicktheprojectnodeandaddanewHTMLfiletothepartialsfolder.Namethenewfilelogin.html.Replacethecontentofthenewlycreatedfilewiththecodeshownhere.Noticethatweusetheng-submitdirectivetoconnectthesubmitmethodinourLoginCtrltotheformforformsubmission:

<!--chapter10/login.html-->

<divclass="blog-login-wrapper">

<formclass=""ng-submit="submit()"ng-controller="LoginCtrl">

<divclass="blog-login-error">{{error}}</div>

<divclass="blog-login-label">

<labelfor="username">Username:</label></div>

<divclass="blog-login-element">

<inputtype="text"ng-model="username"name="username"

placeholder="username"required/></div>

<divclass="blog-login-label">

<labelfor="password">Password:</label></div>

<divclass="blog-login-element">

<inputtype="password"ng-model="password"name="password"

placeholder="password"required/></div>

<divclass="blog-login-button">

<buttontype="submit"class="form-button">Signin</button></div>

</form>

</div>

NowopentheCSSfilestyles.cssinyoureditorandaddthefollowingcodetothebottomofthefile.NoticethatweuseCSS3mediaquerieslike@mediascreenand(min-width:1200px)tomakeourlogintemplateberesponsiveandlookgoodonanymobileordesktopplatform:

/*chapter10/styles.css*/

.blog-login-wrapper{

float:left;

background:#e0e0e0;

border-radius:6px;

-moz-border-radius:6px;/*Firefox3.6andearlier*/

border:darkgreensolid1px;

}

@mediascreenand(min-width:1200px){

.blog-login-wrapper{

width:40%;

margin:10%0030%;

padding:1%;

background:#e0e0e0;

border-radius:6px;

-moz-border-radius:6px;/*Firefox3.6andearlier*/

border:darkgreensolid1px;

}

}

@mediascreenand(max-width:1200px){

.blog-login-wrapper{

width:40%;

margin:10%0030%;

padding:1%;

background:#e0e0e0;

border-radius:6px;

-moz-border-radius:6px;/*Firefox3.6andearlier*/

border:darkgreensolid1px;

}

Page 226: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

}

@mediascreenand(max-width:600px){

.blog-login-wrapper{

width:80%;

margin:10%0010%;

padding:1%;

background:#e0e0e0;

border-radius:6px;

-moz-border-radius:6px;/*Firefox3.6andearlier*/

border:darkgreensolid1px;

}

}

.blog-login-label{

float:left;

width:70%;

margin:00015%;

padding:1%000;

text-align:center;

}

.blog-login-element{

float:left;

width:70%;

margin:00015%;

padding:1%000;

text-align:center;

}

.blog-login-button{

float:left;

width:100%;

margin:0000;

padding:5%000;

text-align:center;

}

.blog-login-error{

float:left;

width:100%;

margin:0000;

padding:0000;

text-align:center;

color:red;

}

Page 227: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

AddingNewRoutesNowweneedtoaddthetwonewroutestoourrouteproviderintheapp.jsfile.Thefollowingcodeshowsthechangesneededforthisfile.Asyoucansee,thetwonewroutesmakeuseofthetwonewcontrollersandthenewtemplatefile:

/*chapter10/app.js*/

'usestrict';

/*AppModule*/

varblogApp=angular.module('blogApp',[

'ngRoute',

'blogControllers',

'blogServices',

'blogBusinessServices',

'blogDirectives'

]);

blogApp.config(['$routeProvider','$locationProvider',

function($routeProvider,$locationProvider){

$routeProvider.

when('/',{

templateUrl:'partials/main.html',

controller:'BlogCtrl'

}).when('/blogPost/:id',{

templateUrl:'partials/blogPost.html',

controller:'BlogViewCtrl'

}).when('/login',{

templateUrl:'partials/login.html',

controller:'LoginCtrl'

}).when('/logOut',{

templateUrl:'partials/login.html',

controller:'LogoutCtrl'

});

$locationProvider.html5Mode(false).hashPrefix('!');

}]);

Page 228: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

AddingaLogoutLinkFinally,weneedtomakeonemorechangetoourblogapplication:weneedtomodifythemenu.htmlfileandaddthenew“Logout”menulink.Hereisthelineyou’llneedtoaddtothemenu.htmlfile.Thenewlogoutlinkmapstothelogoutroutethatwejustadded:

<!--chapter10/menu.htmlexcerpt-->

<li><aid="lo"href="#!logOut">Logout</a></li>

Thecompletemenu.htmlfileisshownhereforconvenience:

<!--chapter10/menu.htmlcompletefile-->

<navclass="navbarnavbar-inversenavbar-fixed-top"role="navigation">

<!--Brandandtogglegetgroupedforbettermobiledisplay-->

<divclass="container">

<divclass="navbar-header">

<buttontype="button"class="navbar-toggle"data-toggle="collapse"

data-target=".navbar-collapse">

<spanclass="sr-only">Togglenavigation</span>

<spanclass="icon-bar"></span>

<spanclass="icon-bar"></span>

<spanclass="icon-bar"></span>

</button>

<aclass="navbar-brand"style="{{brandColor}}"href="#!/">{{label}}</a>

</div>

<!--Collectthenavlinks,forms,andothercontentfortoggling-->

<divclass="collapsenavbar-collapse">

<ulclass="navnavbar-nav">

<liclass="{{aboutActiveClass}}"><ahref="#!about">About</a></li>

<liclass="">

<ahref="https://github.com/KenWilliamson">DownloadProjectCode</a>

</li>

<li><aid="lo"href="#!logOut">Logout</a></li>

</ul>

</div><!--/.navbar-collapse-->

</div>

</nav>

Onceyouhavemadeallthechangesoutlinedinthischapter,yourblogapplicationshouldhavealltheneededsecurityadditionsthatwerespecified.Totestthechangesthatweremade,wewillruntheprojectandcheckforerrors.

Page 229: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

RunningtheBlogApplicationRight-clicktheprojectnodeandselect“Run”fromthemenu.YourprojectshouldrunandyoushouldseethescreeninFigure10-1.Ifyoudonotseetheloginscreen,checkthatallthechangesoutlinedinthischapterwereperformedcorrectly.Turnondevelopertoolsforyourbrowserandlookforerrors,asdescribedinpreviouschapters.

Figure10-1.Theloginscreen

Page 230: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

LoggingInOnceyourprojectisrunning,dothefollowing:

1. Enter“node”astheusername.

2. Enter“password”asthepassword.

3. Clickthe“Signin”button.

Youshouldnowseethesameblogscreensthatyoubuiltinthepreviouschapters.Theapplicationshouldfunctionjustasbeforewithnochanges.Navigatethroughtheapplicationtovalidatethateverythingworkscorrectly.

Ifyouweretoenterincorrectusercredentials,youwouldseetheerrormessagedescribedearlier(“LoginFailed”)displayedinred.Noticethenewmenuitem“Logout”attherightendofthemenubar.Click“Logout”andyoursessionshouldend.Youshouldthenbetakenbacktotheloginscreen.Iftheloginandlogoutprocessworkcorrectly,yoursecuritychangeswereimplementedsuccessfully.

Page 231: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

TestingwithKarmaWe’veaddedanewAngularJSserviceandtwonewcontrollerstoourblogapplication.Wenowneedtotesttheapplicationtomakecertaintherearenodefectsinourcode.Wealsoneedtovalidatethatallpreviousunittestsarestillpassing.

Wewillstartoffbywritingatestspecificationforthenewservice.Wewillthenwritetwonewtestspecificationsforthetwonewcontrollers.Onceourunittestingiscomplete,wewillmakechangestoourend-to-endtesting.

Page 232: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

KarmaConfigurationWealreadyhaveanup-to-dateKarmaconfigurationfileforourblogproject.Thereshouldbenochangestothefileatthispoint.Thecompletekarma.conf.jsfileisshownhereforreference:

/*chapter10/karma.conf.js*/

module.exports=function(config){

config.set({

basePath:'../',

files:[

"public_html/js/libs/angular.min.js",

"public_html/js/libs/angular-mocks.js",

"public_html/js/libs/angular-route.min.js",

"public_html/js/libs/angular-resource.min.js",

"public_html/js/libs/angular-cookies.min.js",

"public_html/js/*.js",

"public_html/partials/*.html",

"test/**/*Spec.js"

],

preprocessors:{

'public_html/partials/*.html':['ng-html2js']

},

exclude:[

],

autoWatch:true,

frameworks:[

"jasmine"

],

browsers:[

"Chrome",

"Firefox"

],

plugins:[

"karma-junit-reporter",

"karma-chrome-launcher",

"karma-firefox-launcher",

"karma-jasmine",

"karma-ng-html2js-preprocessor"

],

ngHtml2JsPreprocessor:{

stripPrefix:'public_html/'

});

};

Page 233: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

KarmaTestSpecificationsWeneedtoaddunittestspecificationsforthenewLoginserviceandthetwonewcontrollers.ThefollowingcodeshowsthenewtestspecificationfortheLoginservice.TheservicereliesonaRESTservice,sowewillonlytesttomakesurewecaninjecttheservice.WewillactuallytesttheserviceinteractionwiththeRESTserviceduringend-to-endtesting.Ifthereareanyissues,wewillfindthemthere.Addthistestspecificationtotheproject’sservicesSpec.jsfile:

/*chapter10/servicesSpec.jsexcerpt*/

describe('testLogin',function(){

var$rootScope;

varlogin;

beforeEach(module('blogServices'));

beforeEach(inject(function($injector){

$rootScope=$injector.get('$rootScope');

login=$injector.get('Login');

}));

it('shouldtestLoginservice',function(){

expect(login).toBeDefined();

});

});

ThecompleteservicesSpec.jsfileisshownhere:

/*chapter10/servicesSpec.jscompletefile*/

describe('AngularJSBlogServiceTesting',function(){

describe('testBlogList',function(){

var$rootScope;

varblogList;

beforeEach(module('blogServices'));

beforeEach(inject(function($injector){

$rootScope=$injector.get('$rootScope');

blogList=$injector.get('BlogList');

}));

it('shouldtestBlogListservice',function(){

expect(blogList).toBeDefined();

});

});

describe('testBlogPost',function(){

var$rootScope;

varblogPost;

beforeEach(module('blogServices'));

beforeEach(inject(function($injector){

$rootScope=$injector.get('$rootScope');

blogPost=$injector.get('BlogPost');

}));

it('shouldtestBlogPostservice',function(){

expect(blogPost).toBeDefined();

});

});

describe('testLogin',function(){

var$rootScope;

varlogin;

beforeEach(module('blogServices'));

beforeEach(inject(function($injector){

$rootScope=$injector.get('$rootScope');

login=$injector.get('Login');

}));

it('shouldtestLoginservice',function(){

Page 234: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

expect(login).toBeDefined();

});

});

});

Nowweneedtestspecificationsforthetwonewcontrollers.FirstweshowthetestspecificationfortheLoginCtrlcontroller.Wefirstgetareferencetothecontrollerandthencallthesubmitmethodattachedtothescope.Weuseascopepropertytovalidatethatthemethodcallwassuccessful:

/*chapter10/controllerSpec.jsexcerpt*/

describe('LoginCtrl',function(){

varscope,ctrl;

beforeEach(inject(function($rootScope,$controller){

scope=$rootScope.$new();

ctrl=$controller('LoginCtrl',{$scope:scope});

scope.submit();

}));

it('shouldshowsubmitsuccess',function(){

console.log("LoginCtrl:"+scope.sub);

expect(scope.sub).toEqual(true);

});

});

NextisthetestspecificationfortheLogoutCtrlcontroller.Inthiscase,wejustvalidatethatwecangetareferencetothecontroller.Wewillvalidatethatthecontrolleractuallyhandleslogoutcorrectlywhenwedoend-to-endtesting:

/*chapter10/controllerSpec.jsexcerpt*/

describe('LogoutCtrl',function(){

varscope,ctrl;

beforeEach(inject(function($rootScope,$controller){

scope=$rootScope.$new();

ctrl=$controller('LogoutCtrl',{$scope:scope});

}));

it('shouldcreateLogoutCtrlcontroller',function(){

console.log("LogoutCtrl:"+ctrl);

expect(ctrl).toBeDefined();

//expect(scope.blogList).toBeUndefined();

});

});

ThecompletecontrollerSpec.jsfileisshownnext.Makethechangestoyourfileintheblogapplicationandvalidatethatitmatchestheversionshownhere:

/*chapter10/controllerSpec.jscompletefile*/

describe('AngularJSBlogApplication',function(){

beforeEach(module('blogApp'));

//beforeEach(module('blogServices'));

describe('BlogCtrl',function(){

varscope,ctrl;

beforeEach(inject(function($rootScope,$controller){

scope=$rootScope.$new();

ctrl=$controller('BlogCtrl',{$scope:scope});

}));

it('shouldcreateshowblogentrycount',function(){

Page 235: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

console.log("blogList:"+scope.blogList);

expect(scope.blogList.length).toEqual(0);

//expect(scope.blogList).toBeUndefined();

});

});

describe('BlogViewCtrl',function(){

varscope,ctrl,$httpBackend;

beforeEach(inject(function(_$httpBackend_,$routeParams,

$rootScope,$controller){

$httpBackend=_$httpBackend_;

$httpBackend.expectGET('blogPost').respond({_id:'1'});

$routeParams.id='1';

scope=$rootScope.$new();

ctrl=$controller('BlogViewCtrl',{$scope:scope});

}));

it('shouldshowblogentryid',function(){

//expect(scope.blogEntry._id).toEqual(1);

//expect(scope.blogList).toBeUndefined();

expect(scope.blg).toEqual(1);

});

});

describe('LoginCtrl',function(){

varscope,ctrl;

beforeEach(inject(function($rootScope,$controller){

scope=$rootScope.$new();

ctrl=$controller('LoginCtrl',{$scope:scope});

scope.submit();

}));

it('shouldshowsubmitsuccess',function(){

console.log("LoginCtrl:"+scope.sub);

expect(scope.sub).toEqual(true);

//expect(scope.blogList).toBeUndefined();

});

});

describe('LogoutCtrl',function(){

varscope,ctrl;

beforeEach(inject(function($rootScope,$controller){

scope=$rootScope.$new();

ctrl=$controller('LogoutCtrl',{$scope:scope});

}));

it('shouldcreateLogoutCtrlcontroller',function(){

console.log("LogoutCtrl:"+ctrl);

expect(ctrl).toBeDefined();

//expect(scope.blogList).toBeUndefined();

});

});

});

Page 236: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

KarmaTestingThetestspecificationsjustaddedwilltestthenewserviceandthetwonewcontrollers.Wewillalsotestalltheexistingcontrollers,theexistingservices,andtheexistingdirectivewhenKarmaruns.

Right-clicktheprojectandselect“Test”fromthemenu.Karmawillstart.YoushouldseebothChromeandFirefoxbrowserwindowsopen.TheNetBeanstestresultswindowshouldopenanddisplayatotalof26passedtestcases.

Ifyougetanyerrormessagesorfailedtests,gobackoverthissectionandverifythatyoucompletedalltheconfigurationsandinstallations.YoucanalsodownloadtheChapter10codefromtheGitHubprojectsite.

Page 237: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

End-to-EndTestingWewillmakeseveralchangestotheend-to-endtestspecificationsforourblogapplicationhere.Wewillneedtologintotheblogapplicationwiththescript.Then,onceloggedin,wewillnavigatethroughtheblogasbeforetoverifythatallpreviousE2Efunctionalitystillworks.Wewillthenneedtologoutwiththetestscripttotestthelogoutfunctionality.

Page 238: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

ProtractorConfigurationWealreadycreatedaProtractorconfigurationfilefortheblogapplicationinChapter5.TheProtractorconfigurationfileisshownhereforreference:

/*chapter10/conf.jsProtractorconfigurationfile*/

exports.config={

seleniumAddress:'http://localhost:4444/wd/hub',

specs:['e2e/blog-spec.js']

};

Page 239: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

ProtractorTestSpecificationTheblog-spec.jsfileshownherecontainsseveralchanges.Firstnoticethatthescriptneedstocompletetheloginformbypopulatingtheusernameandpasswordfields.ThenitlooksuptheloginformbuttonbytheCSSclassname,andclicksthebutton:

/*chapter10/blog-spec.jsProtractortestspecification*/

describe("BlogApplicationTest",function(){

it("shouldtestthemainblogpage",function(){

browser.get("http://localhost:8383/AngularJsBlog/");

//logsintotheblogapplication

element(by.model("username")).sendKeys("node");

element(by.model("password")).sendKeys("password");

element(by.css('.form-button')).click();

expect(browser.getTitle()).toEqual("AngularJSBlog");

//getsthebloglist

varblogList=

element.all(by.repeater('blogPostinblogList'));

//teststhesizeoftheblogList

expect(blogList.count()).toEqual(1);

browser.get(

"http://localhost:8383/AngularJsBlog/#!/

blogPost/5394e59c4f50850000e6b7ea");

expect(browser.getTitle()).toEqual("AngularJSBlog");

//getsthecommentlist

varcommentList=

element.all(by.repeater('commentinblogEntry.comments'));

//checksthesizeofthecommentList

expect(commentList.count()).toEqual(2);

element(by.css('.navbar-brand')).click();

//logsoutoftheblogapplication

element(by.id('lo')).click();

expect(browser.getTitle()).toEqual("AngularJSBlog");

});

});

Oncethescripthassuccessfullyloggedintotheapplication,itnavigatesthroughtheapplicationasbefore.Then,attheendofthetestscript,itlooksupthelogoutlinkbyid.Itthenclicksthelink,loggingoutoftheapplication.

Theend-to-endtestspecificationvalidatesthattheloginprocessworks.ItalsovalidatesallthepreviousfunctionalitytestedinChapter9.Thenitvalidatesthatthelogoutprocessworkscorrectly.

Page 240: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

ProtractorTestingNow,withthosechangesadded,wearereadytostarttheend-to-endtesting.

Startanewcommandwindowandenterthefollowingcommandtostartthetestserver:

webdriver-managerstart

OpenanewcommandwindowandnavigatetotherootoftheChapter10project.Typethecommand:

protractortest/conf.js

Youshouldseeabrowserwindowopen.Youshouldthenseethetestscriptlogintotheblogapplicationandnavigatethroughthepagesoftheapplication,andfinallylogoutoftheapplication.WhentheProtractorscripthasfinished,thebrowserwindowwillclose.

YoushouldseeresultslikethefollowinginthecommandwindowwhentheProtractorscriptcompletes.Thenumberofsecondsthatittakesthescripttofinishwillvarydependingonyourparticularsystem:

Finishedin3.285seconds

1test,5assertions,0failures

Page 241: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

OneLastPointonSecurityIwanttoemphasizeonelastthingaboutimplementingsecurityinaJavaScriptapplication.AnysecuritythatyouimplementinJavaScriptcanbecircumventedbytheuser,asIexplainedatthestartofthechapter.TheloginscreenandsecuritythatweimplementedinthischapterarecompletelydependentontheloginRESTservice.

Theloginscreenisusedjustasawaytogatherandstoretheuser’scredentialsinasafeplacetemporarilyandtocontroltheauthenticationprocessforeachRESTservicethatcontainsprivatedata.Theuser’scredentialsareremovedaftereachsessionandhavetobeenteredagainateachlogin,unlesstheuserchoosestosavetheircredentials.

Page 242: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

ConclusionInthenextchapteryouwillseehowtheuser’scredentialsareusedtogainaccesstoprivateRESTservicesthataddnewblogpostsandcomments.YouwillfirstdeploytheRESTservicesandtheAngularJSapplicationtogetherinaMEANstackdeploymenttoyourlocalmachinetoseethewholeprocessinaction.Oncetheapplicationisupandrunningonyourlocalmachine,youwillbeabletousethedevelopertoolsinChrometoviewtheRESTservicelogsatruntime:you’llbeabletoviewtheURL,request,andresponseofeachservicecall.

Youwillalsoseeanyerrorsthatoccur.OnceyouhavetestedtheMEANstackonyourlocalmachine,youwilldeploytheprojecttothecloudusingGit,whichisadistributedversioncontrolandsourcecodemanagement(SCM)systeminitiallydevelopedbyLinusTorvalds.

Page 243: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development
Page 244: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

Chapter11.MEANCloudandMobile

ThischapterwillcoverboththeclouddeploymentofourblogapplicationandashortdiscussiononbuildingamobileHTML5versionofourapplication.TheclouddeploymentwillbetoafreeaccountonRedHat’sOpenShiftplatform.Themobilediscussionwillcoverthestepsneededtobuildamobileversionoftheblogapplicationthatwillrunonanymobiledeviceandcanbedistributedthroughtherespectivemobileapplicationstores.ThemobileversionwillusethesameRESTservicesthatweuseforthecloudversionofourblogapplication.

Page 245: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

LocalDeploymentBeforewedeployourblogapplicationtothecloud,wewillsetupalocalprojectinNetBeansthatwewilllaterusetodeployourblogtoOpenShift.Wecanalsorunandtestourblogapplicationlocallybeforepushingittothecloud.AllthecodeforthischapterhasalreadybeenwrittenandcanbedownloadedfromGitHub.WewillwalkthroughthecodeanddiscussthechangesthathavebeenmadetoourAngularJSapplicationtoallowforadeploymenttothecloud.

OurclouddeploymentusesNode.jsastheserverplatform,ExpressJSasthewebapplicationframework,andMongoDBasthedatabase.WewilldiscusshowAngularJSintegrateswithallthreeofthesetoformaMEAN(MongoDB,ExpressJS,AngularJS,andNode.js)stackdeployment.WewillprimarilyfocusontherolethatAngularJSplaysinaMEANstackapplication.

WewillnotcovertheNode.jscodeingreatdetail.AlthoughtheNode.jsserver-sidecodeisJavaScript,itcanoftenbequitecomplex.Ifyouhaveserver-sideexperience,feelfreetoexperimentwiththeservercode.BookswrittenspecificallyontheMEANstackwillcovertheNode.jsandExpressJScodeofMEANstackapplicationsinmuchgreaterdepththanwewillhere.

Page 246: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

InstallingNode.js,npm,andMongoDBBeforeyoucanrunthenewMEANblogapplicationlocally,youmustinstallNode.js,MongoDB,andnpm(theNode.jspackagemanager)onyourlocalsystem.Theinstallationsaredifferentforeachoperatingsystem,butyoucanfindmoreinformationaboutNode.jsatnodejs.organdyoucanfindinformationaboutMongoDBathttp://www.mongodb.org.IfyouareusingoneoftheLinuxdistributions,youcanusuallyinstallandconfigurebothNode.jsandMongoDBthroughtheOSpackagemanagementsystem.Beforewecontinue,installandconfigureNode.js,npm,andMongoDBifyouhaven’tdonesoalready.

Page 247: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

InstallingtheNetBeansNode.jsPluginNowwewillinstallaNode.jspluginforNetBeanstosimplifyourinteractionwithNode.js.Dothefollowing:

1. FollowthedirectionsonTimBoudreau’sblog.

2. Downloadandinstalltheplugin.

3. Configurethepluginasspecified.

OnceyouhavetheNode.jspluginforNetBeansinstalledandconfigured,downloadthesourceforthischapterfromGitHub.Unzipthefilesomewhereonyourlocaldrive.InNetBeans,click“File”andselect“OpenProject”fromthemenu,thennavigatetotheprojectsourcethatyoujustdownloadedandopentheNode.jsproject.YoushouldseetheNodeBlogproject,asshowninFigure11-1.

Figure11-1.TheNodeBlogprojectinNetBeans

Page 248: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

TheMEANApplicationWe’lluseMongoDBasourserver-sidedatabase.MongoDBisaNoSQLdatabasethatisfastandeasytouse.WithMongoDB,thereisnoconcernaboutwritingSQLqueries;wejustusetheMongoDBAPItointeractwiththedatabase.We’llactuallysimplifyourinteractionwithMongoDBevenmorebyusingMongoose.js,anobjectdatamodeling(ODM)librarythatallowsustointeractwithMongoDBusingJSONviaagreatlysimplifiedAPIinterface.

OurMEANstackusesRESTservicesbuiltwithExpressJS.ExpressJSisawebframeworkthatislightweightandeasytouse.RESTservicesbuiltonExpressJScanbeusedexclusivelyinourapplicationorexposedtotheoutsideworldforusebyexternalapplications.

MEANstackapplicationsrunonNode.js,whichrunsonGoogle’sV8JavaScriptengine.Node.jsisaverypowerfulplatformfordevelopingserver-sidesoftwareapplicationsinJavaScript.AngularJSsitsontopoftheotherthreepiecesoftheMEANstackandisusedtobuildJavaScriptapplicationsthatinteractdirectlywiththeRESTservicesbuiltwithExpressJS.

Page 249: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

Node.jsPublicFolderYouwillnoticeourAngularJSblogcodeisnowlocatedunderthepublicfolderintheMEANproject.PlacingtheAngularJScodeinthepublicfolderiscommonpracticewhenyou’rebuildingMEANapplications.Openthepublicfolderandyoushouldseethesamecodethatwedevelopedinthepreviouschapters.

Page 250: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

MEANServicesSeveralchangeswereneededtoourservices.jsfile,asshowninthefollowingcode.NoticethatwechangedtheURLforeachservicefromhttp://nodeblog-micbuttoncloud.rhcloud.com/NodeBlog/to./NodeBlog/.Thatsmallchangemakesourapplicationtransportabletoanycloudplatform.Withoutmakingthatchange,wewouldneedtoconfiguretheserviceURLseverytimewemovedtheapplicationtoanewcloudplatform:

/*chapter11/services.js*/

'usestrict';

/*Services*/

varblogServices=

angular.module('blogServices',['ngResource']);

blogServices.factory('BlogPost',['$resource',

function($resource){

return$resource("./NodeBlog/blog/:id",{},{

get:{method:'GET',cache:false,isArray:false},

save:{method:'POST',cache:false,isArray:false},

update:{method:'PUT',cache:false,isArray:false},

delete:{method:'DELETE',cache:false,isArray:false}

});

}]);

blogServices.factory('BlogList',['$resource',

function($resource){

return$resource("./NodeBlog/blogList",{},{

get:{method:'GET',cache:false,isArray:true}

});

}]);

blogServices.factory('Login',['$resource',

function($resource){

return$resource("./NodeBlog/login",{},{

login:{method:'POST',cache:false,isArray:false}

});

}]);

blogServices.factory('BlogPostComments',['$resource',

function($resource){

return$resource("./NodeBlog/comment/:id",{},{

save:{method:'POST',cache:false,isArray:false}

});

}]);

Wealsomadechangestotheapplicationtoallowtheusertocreatenewblogpostsandtoaddcommentstoposts.Oneofthosechangeswastothisfileaswell:noticethatweaddedanewBlogPostCommentsserviceatthebottomofthefile.Therewerealsochangesmadetootherfilesintheapplication.Wewillfirstdiscussthechangestocontrollers.js.

Page 251: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

MEANBlogControllersFollowingisthenewcontrollers.jsfile,whichwe’vemodifiedtogiveustheabilitytoaddnewblogpostsandcomments.NoticefirstthechangesthatweremadetotheBlogViewCtrlcontroller.We’veinjectedseveralnewservicesintothecontroller,includingtheBlogPostCommentsservicejustshown.We’vealsoaddedanewsubmitmethodtothecontrollerthathandlestheprocessofaddinganewcommenttoablogpost.ThenewsubmitmethodmakesacalltothesavemethodontheBlogPostCommentsservice:

/*chapter11/controllers.js*/

'usestrict';

/*Controllers*/

varblogControllers=

angular.module('blogControllers',[]);

blogControllers.controller('BlogCtrl',

['$scope','BlogList','$location','checkCreds',

functionBlogCtrl($scope,BlogList,$location,checkCreds){

if(!checkCreds()){

$location.path('/login');

}

$scope.brandColor="color:white;";

$scope.blogList=[];

BlogList.get({},

functionsuccess(response){

console.log("Success:"+JSON.stringify(response));

$scope.blogList=response;

},

functionerror(errorResponse){

console.log("Error:"+JSON.stringify(errorResponse));

});

}]);

blogControllers.controller('BlogViewCtrl',

['$scope','$routeParams','BlogPost','BlogPostComments',

'$location','checkCreds','$http','getToken','$route',

functionBlogViewCtrl($scope,$routeParams,BlogPost,

BlogPostComments,$location,checkCreds,$http,getToken,

$route){

if(!checkCreds()){

$location.path('/login');

}

varblogId=$routeParams.id;

$scope.blg=1;

BlogPost.get({id:blogId},

functionsuccess(response){

console.log("Success:"+JSON.stringify(response));

$scope.blogEntry=response;

$scope.blogId=response._id;

},

functionerror(errorResponse){

console.log("Error:"+JSON.stringify(errorResponse));

}

);

$scope.submit=function(){

$scope.sub=true;

$http.defaults.headers.common['Authorization']='Basic'+

getToken();

varpostData={

"commentText":$scope.commentText,

"blog":$scope.blogId

};

Page 252: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

BlogPostComments.save({},postData,

functionsuccess(response){

console.log("Success:"+JSON.stringify(response));

$location.path('/blogPost/'+$scope.blogId);

$route.reload();

},

functionerror(errorResponse){

console.log("Error:"+JSON.stringify(errorResponse));

});

};

}]);

blogControllers.controller('LoginCtrl',['$scope',

'$location','Login','setCreds',

functionLoginCtrl($scope,$location,Login,setCreds){

$scope.submit=function(){

$scope.sub=true;

varpostData={

"username":$scope.username,

"password":$scope.password

};

Login.login({},postData,

functionsuccess(response){

console.log("Success:"+JSON.stringify(response));

if(response.authenticated){

setCreds($scope.username,$scope.password)

$location.path('/');

}else{

$scope.error="LoginFailed"

}

},

functionerror(errorResponse){

console.log("Error:"+JSON.stringify(errorResponse));

});

};

}]);

blogControllers.controller('LogoutCtrl',['$location','deleteCreds',

functionLogoutCtrl($location,deleteCreds){

deleteCreds();

$location.path('/login');

}]);

blogControllers.controller('NewBlogPostCtrl',

['$scope','BlogPost','$location','checkCreds','$http','getToken',

functionNewBlogPostCtrl($scope,BlogPost,$location,checkCreds,

$http,getToken){

if(!checkCreds()){

$location.path('/login');

}

$scope.languageList=[

{

"id":1,

"name":"English"

},

{

"id":2,

"name":"Spanish"

}

];

$scope.languageId=1;

$scope.newActiveClass="active";

$scope.submit=function(){

$scope.sub=true;

$http.defaults.headers.common['Authorization']='Basic'+

getToken();

varpostData={

"introText":$scope.introText,

"blogText":$scope.blogText,

"languageId":$scope.languageId

};

BlogPost.save({},postData,

functionsuccess(response){

Page 253: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

console.log("Success:"+JSON.stringify(response));

$location.path('/');

},

functionerror(errorResponse){

console.log("Error:"+JSON.stringify(errorResponse));

});

};

}]);

blogControllers.controller('AboutBlogCtrl',['$scope',

'$location','checkCreds',

functionAboutBlogCtrl($scope,$location,checkCreds){

if(!checkCreds()){

$location.path('/login');

}

$scope.aboutActiveClass="active";

}]);

TheRESTservicelinkedtotheBlogPostCommentsservicerequiresBasicAuthentication.IfyoulookatthefirstlineofthenewsubmitmethodaddedtotheBlogViewCtrlcontroller($http.defaults.headers.common['Authorization']='Basic'+getToken();),youwillseehowRESTserviceBasicAuthenticationishandledinAngularJS.Thecodeonthatlinemakesuseofthe$httpservicetoaddaBasicAuthenticationheadertotheRESTservicecall.

WeusethegetTokenAngularJSbusinesslogicservicedevelopedinChapter8toaddthebase64tokentotheheader,asdescribedinthatchapter.Onceanewcommentisaddedsuccessfully,wethenmakeacalltothepathmethodonthe$locationservice($location.path('/blogPost/'+$scope.blogId);)andacalltothereloadmethodonthe$routeservice($route.reload();).Makingthosetwocallsrefreshestheblogpostpagetoshowthenewlyaddedcomment.

WealsoaddedanewcontrollernamedNewBlogPostCtrl.ThenewcontrollerhasasubmitmethodthatmakesacalltotheBlogPostserviceusedpreviously.ThesavemethodiscalledontheBlogPostservice,andtheRESTservicemappedtothesavemethodrequiresBasicAuthentication,asdescribedpreviously.Theimplementationforauthenticationisthesame.

Page 254: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

MEANBlogTemplatesThenewcontrolleralsohasanewlanguageListJSONarraythatisusedtopopulateanewHTML<select>elementinthetemplateusedfornewblogposts.Thelanguagefieldisnotactuallyusedbyourblogapplicationbutisincludedtoshowhowtopopulatea<select>elementinanAngularJSview.Wepreselectthe<select>elementwith“English”bysettingthelanguageIdscopeproperty($scope.languageId=1;).

Therewerenoothersignificantchangesmadetothecontrollers.jsfile.Wewillnowtalkaboutthenewtemplateaddedtoallowuserstoaddnewblogposts.Wewillalsocoverchangesmadetotheblogposttemplateneededforaddingcommentstoblogentries.

Page 255: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

AddingCommentsThefollowingcodeshowsthemodificationsneededtotheexistingblogposttemplate.Youwillnoticethatwe’veaddedanewformforsubmittingnewcomments.ThenewformismappedtothenewsubmitmethodoftheBlogEntryCtrlcontroller.AlsonoticethatweholdtheblogIDinahiddenelementandpassthatIDbacktothecontrollerwhentheusersubmitstheform.TheblogIDispassedtotheRESTservicethataddsnewcomments:

<!--chapter11/blogPost.html-->

<divblg-menumenu-title="AngularJSMEANBlog"></div>

<divid="container"class="container">

<divclass="blog-post-label">BlogEntry</div>

<divclass="blog-entry-wrapper">

<divclass="blog-intro-text">

Posted:{{blogEntry.date|date:'MM/dd/yyyy@h:mma'}}

</div>

<divclass="blog-entry-outer">

{{blogEntry.blogText}}

</div>

<divclass="blog-comment-wrapper">

<divclass="blog-comment-label">BlogComments</div>

<divclass="blog-entry-comments"ng-repeat="commentinblogEntry.comments">

{{comment.commentText}}

</div>

</div>

<divclass="blog-comment-entry-wrapper">

<formclass=""ng-submit="submit()"ng-controller="BlogViewCtrl">

<inputtype="hidden"ng-model="blogId"/>

<divclass="blog-post-entry-label">

<labelfor="commentText">NewComment:</label>

</div>

<divclass="blog-post-entry-element">

<textareaclass="blog-post-textarea"type="text"

ng-model="commentText"name="commentText"placeholder="Comment"required/>

</div>

<divclass="blog-post-button">

<buttontype="submit"class="form-button">Submit</button>

</div>

</form>

</div>

</div>

</div>

Page 256: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

AddingBlogEntriesThefollowingcodeshowsthenewtemplateusedtoaddnewblogposts.ThetemplatemapsformsubmissiontothesubmitmethodoftheNewBlogPostCtrlcontrollerusingtheng-submitdirective,asbefore:

<!--chapter11/newPost.html-->

<divblg-menumenu-title="AngularJSMEANBlog"></div>

<divid="container"class="container">

<divclass="blog-post-label">NewBlogPosts</div>

<divclass="blog-post-wrapper">

<formclass=""ng-submit="submit()"ng-controller="NewBlogPostCtrl">

<divclass="blog-post-entry-label">

<labelfor="introText">IntroText:</label></div>

<divclass="blog-post-entry-element">

<textareaclass="blog-post-textarea"type="text"

ng-model="introText"name="introText"placeholder="IntroText"required/></div>

<divclass="blog-post-entry-label">

<labelfor="blogText">BlogText:</label></div>

<divclass="blog-post-entry-element">

<textareaclass="blog-post-textarea"type="text"

ng-model="blogText"name="blogText"placeholder="BlogText"required/></div>

<divclass="blog-post-entry-label">

<labelfor="blogText">Language:</label></div>

<divclass="blog-post-entry-element">

<selectclass="form-select-element-left"ng-model="languageId"

ng-options="lan.idaslan.nameforlaninlanguageList"

name="languageId"required>

</select>

</div>

<divclass="blog-post-button"><buttontype="submit"

class="form-button">Submit</button></div>

</form>

</div>

</div>

Thefollowingcodeshowsthechangeneededtothemenu.htmlfile:we’veaddedalinkinthemenutothenewblogpostcreationview.Thenewpathconfigurationisalsoshown:

<!--chapter11/menu.html-->

<navclass="navbarnavbar-inversenavbar-fixed-top"role="navigation">

<!--Brandandtogglegetgroupedforbettermobiledisplay-->

<divclass="container">

<divclass="navbar-header">

<buttontype="button"class="navbar-toggle"data-toggle="collapse"

data-target=".navbar-collapse">

<spanclass="sr-only">Togglenavigation</span>

<spanclass="icon-bar"></span>

<spanclass="icon-bar"></span>

<spanclass="icon-bar"></span>

</button>

<aclass="navbar-brand"style="{{brandColor}}"href="#!/">{{label}}</a>

</div>

<!--Collectthenavlinks,forms,andothercontentfortoggling-->

<divclass="collapsenavbar-collapse">

<ulclass="navnavbar-nav">

<liclass="{{aboutActiveClass}}"><ahref="#!about">About</a></li>

Page 257: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

<liclass="{{newActiveClass}}"><ahref="#!newBlogPost">New</a></li>

<liclass="">

<ahref="https://github.com/KenWilliamson">DownloadProjectCode</a>

</li>

<li><ahref="#!logOut">Logout</a></li>

</ul>

</div><!--/.navbar-collapse-->

</div>

</nav>

Page 258: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

AddingNewRoutesThefollowingcodeshowsthechangesneededfortheapp.jsfile.Thenewrouteusedtoaddanewblogpostisshown.Theroutewasaddedtothe$routeProviderasbefore:

/*chapter11/app.jsexcerpt*/

.when('/newBlogPost',{

templateUrl:'partials/newPost.html',

controller:'NewBlogPostCtrl'

})

Thecompleteapp.jsfileisshownhereforconvenience:

/*chapter11/app.jscompletefile*/

'usestrict';

/*AppModule*/

varblogApp=angular.module('blogApp',[

'ngRoute',

'blogControllers',

'blogServices',

'blogBusinessServices',

'blogDirectives'

]);

blogApp.config(['$routeProvider','$locationProvider',

function($routeProvider,$locationProvider){

$routeProvider.

when('/',{

templateUrl:'partials/main.html',

controller:'BlogCtrl'

}).when('/blogPost/:id',{

templateUrl:'partials/blogPost.html',

controller:'BlogViewCtrl'

}).when('/newBlogPost',{

templateUrl:'partials/newPost.html',

controller:'NewBlogPostCtrl'

}).when('/about',{

templateUrl:'partials/about.html',

controller:'AboutBlogCtrl'

}).when('/login',{

templateUrl:'partials/login.html',

controller:'LoginCtrl'

}).when('/logOut',{

templateUrl:'partials/login.html',

controller:'LogoutCtrl'

});

$locationProvider.html5Mode(false).hashPrefix('!');

}]);

Page 259: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

AddingNode.jsDependenciesNoothersignificantchangesweremadetotheblogapplication.Wewillnowruntheapplicationlocallybeforedeployingtothecloud.

Thereisonesmallcommand-linetaskthatneedstobeperformedbeforeyoucanruntheblogapplicationlocally.ThisisstandardpracticewhenworkingwithNode.js.Dothefollowing:

1. OpenacommandwindowandnavigatetothelocationonyourdrivewhereyouunzippedtheNodeBlogproject.

2. Youshouldseethepackage.jsonfileatthatlocation.

3. Inthecommandwindow,dothefollowing:a. Typenpminstall.

b. PressEnter.

Thiscommandusesnpmtoinstallalltheblogapplicationdependencies.Iftheinstallationwassuccessful,youshouldseealltherequiredNode.jspackagesinstalledinthecurrentdirectoryunderanewfoldernamednode_modules.

Whenyourunthenpminstallcommand,npmreadsthepackage.jsonfileandinstallsalltherequiredpackagesthataredefinedinthatfile.Iftherewereerrorsandthenewfolderdidn’tgetcreated,thereisaproblemwiththeNode.jsinstallationonyourmachine.OnceyouhavetherequiredNode.jspackagesinstalledinyourproject,youarereadytoruntheproject.

Page 260: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

RunningtheBlogApplicationLocallyRight-clicktheNodeBlogprojectandselect“Run”fromthemenu.YoushouldseeasmallindicatoratthebottomrightofNetBeans,asshowninFigure11-2.Ifyousee“Running,”yourprojectandNode.jsareinstalledcorrectly.Openabrowserandnavigatetohttp://localhost:8080,andyoushouldseetheloginscreenasbefore.

Figure11-2.RunningtheNodeBlogproject

Loginwiththefollowingcredentials:

username=“node”

password=“password”

Theapplicationshouldperformjustasitdidbefore.Ifyouhaveanyissuesrunningtheapplicationlocally,resolvethoseissuesbeforeyoucontinue.Oncetheapplicationrunslocallyonyourmachine,continueontothenextsection.

Page 261: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

TestingwithKarmaWe’veaddedanewBlogPostCommentsservicetotheservices.jsfile,andmadechangestothecontrollers.jsfile.Inordertovalidatethateverythingisworkingcorrectly,weneedtoupdatethetestspecificationsaswell.Ifyoulookatthetestspecificationsforcontrollersandservicesinthedownloadedcodeforthischapter,youwillseetheneededchangesandadditions.

FirstIwillshowhowtoconfigureKarmainaMEANstackenvironment.ThenwewilllookatthetestspecificationforthenewBlogPostCommentsserviceandthechangestothetestspecificationsforcontrollers.

Page 262: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

KarmaConfigurationTheKarmaconfigurationfilewasmodifiedfromthefileweusedinChapter10.NowtheAngularJSapplicationislocatedunderthepublicfolderoftheMEANblogapplication.InChapter10,thepublic_htmlfolderwasusedinstead.TheKarmaconfigurationfilewasmodifiedtoaccountforthatchange.ThefullKarmaconfigurationfileisshownhere:

/*chapter11/karma.conf.jsKarmaconfigurationfile*/

module.exports=function(config){

config.set({

basePath:'../',

files:[

"public/js/libs/angular.min.js",

"public/js/libs/angular-mocks.js",

"public/js/libs/angular-route.min.js",

"public/js/libs/angular-resource.min.js",

"public/js/libs/angular-cookies.min.js",

"public/js/*.js",

"public/partials/*.html",

"test/**/*Spec.js"

],

preprocessors:{

'public/partials/*.html':['ng-html2js']

},

exclude:[

],

autoWatch:true,

frameworks:[

"jasmine"

],

browsers:[

"Chrome",

"Firefox"

],

plugins:[

"karma-junit-reporter",

"karma-chrome-launcher",

"karma-firefox-launcher",

"karma-jasmine",

"karma-ng-html2js-preprocessor"

],

ngHtml2JsPreprocessor:{

stripPrefix:'public/'

}

});

};

ThereisoneotherthingtonoteifyouareusingNetBeans:aNode.jsprojectinNetBeansdoesnothavebuilt-insupportforKarma.Thatisnotreallyaproblem;wejustneedtolaunchKarmafromthecommandlineinstead.Wewillcoverthatinthenextsection.

Now,beforewestartunittesting,weneedtoinstallalltheNode.jsdependenciesdefinedintheproject’spackage.jsonfile.Dothefollowing:

1. NavigatetothelocationwhereyouunzippedtheMEANblogproject.

2. Navigatetothelocationofthepackage.jsonfile.

3. Typethefollowingcommandtoinstallalldependencies:

npminstall

Page 263: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

Theinstallprocesswillrunforseveralminutes.Whenallpackagesareinstalled,youwillbereadytomoveontothenextsection.

Page 264: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

KarmaTestSpecificationsThetestspecificationforthenewBlogPostCommentsserviceisshownnext.Wewillonlyverifythatwecaninjecttheserviceatthispoint.WewillcompletelychecktheservicewhenwedoE2Etesting:

/*chapter11/servicesSpec.jsexcerpt*/

describe('testBlogPostComments',function(){

var$rootScope;

varcomment;

beforeEach(module('blogServices'));

beforeEach(inject(function($injector){

$rootScope=$injector.get('$rootScope');

comment=$injector.get('BlogPostComments');

}));

it('shouldtestBlogPostCommentsservice',function(){

expect(comment).toBeDefined();

});

});

ThenewtestspecificationfortheNewBlogPostCtrlcontrollerisshownnext.Noticethatwemakeacalltothesubmitmethodthatisattachedtothecontroller’sscope.Wethenvalidatethatthecalltothesubmitmethodwassuccessful:

/*chapter11/controllerSpec.jsexcerpt*/

describe('NewBlogPostCtrl',function(){

varscope,ctrl;

beforeEach(inject(function($rootScope,$controller){

scope=$rootScope.$new();

ctrl=$controller('NewBlogPostCtrl',{$scope:scope});

scope.submit();

}));

it('shouldshowsubmitsuccessofNewBlogPostCtrl',

function(){

console.log("NewBlogPostCtrl:"+scope.sub);

expect(scope.sub).toEqual(true);

});

});

NextupisthetestspecificationfortheAboutBlogCtrlcontroller.WevalidatethefunctionalityofthecontrollerbycheckingthevalueassignedtotheaboutActiveClassvariable:

/*chapter11/controllerSpec.jsexcerpt*/

describe('AboutBlogCtrl',function(){

varscope,ctrl;

beforeEach(inject(function($rootScope,$controller){

scope=$rootScope.$new();

ctrl=$controller('AboutBlogCtrl',{$scope:scope});

}));

it('shouldcreateAboutBlogCtrlcontroller',function(){

console.log("AboutBlogCtrl:"+ctrl);

expect(scope.aboutActiveClass).toEqual("active");

});

});

WealsomadeachangetothetestspecificationfortheBlogViewCtrlcontroller,asshownhere.Wenowneedtovalidateacalltothenewsubmitmethodattachedtothescopeof

Page 265: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

thatcontroller:

/*chapter11/controllerSpec.jsexcerpt*/

describe('BlogViewCtrl',function(){

varscope,ctrl,$httpBackend;

beforeEach(inject(function(_$httpBackend_,

$routeParams,$rootScope,$controller){

$httpBackend=_$httpBackend_;

$httpBackend.expectGET('blogPost').respond({_id:'1'});

$routeParams.id='1';

scope=$rootScope.$new();

ctrl=$controller('BlogViewCtrl',{$scope:scope});

scope.submit();

}));

it('shouldshowblogentryid',function(){

expect(scope.blg).toEqual(1);

expect(scope.sub).toEqual(true);

});

});

ThecompleteservicesSpec.jsandcontrollerSpec.jsfilesareshownnextforreference:

/*chapter11/servicesSpec.jscompletefile*/

describe('AngularJSBlogServiceTesting',function(){

describe('testBlogList',function(){

var$rootScope;

varblogList;

beforeEach(module('blogServices'));

beforeEach(inject(function($injector){

$rootScope=$injector.get('$rootScope');

blogList=$injector.get('BlogList');

}));

it('shouldtestBlogListservice',function(){

expect(blogList).toBeDefined();

});

});

describe('testBlogPost',function(){

var$rootScope;

varblogPost;

beforeEach(module('blogServices'));

beforeEach(inject(function($injector){

$rootScope=$injector.get('$rootScope');

blogPost=$injector.get('BlogPost');

}));

it('shouldtestBlogPostservice',function(){

expect(blogPost).toBeDefined();

});

});

describe('testLogin',function(){

var$rootScope;

varlogin;

beforeEach(module('blogServices'));

beforeEach(inject(function($injector){

$rootScope=$injector.get('$rootScope');

login=$injector.get('Login');

}));

it('shouldtestLoginservice',function(){

expect(login).toBeDefined();

Page 266: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

});

});

describe('testBlogPostComments',function(){

var$rootScope;

varcomment;

beforeEach(module('blogServices'));

beforeEach(inject(function($injector){

$rootScope=$injector.get('$rootScope');

comment=$injector.get('BlogPostComments');

}));

it('shouldtestBlogPostCommentsservice',function(){

expect(comment).toBeDefined();

});

});

});

/*chapter11/controllerSpec.jscompletefile*/

describe('AngularJSBlogApplication',function(){

beforeEach(module('blogApp'));

//beforeEach(module('blogServices'));

describe('BlogCtrl',function(){

varscope,ctrl;

beforeEach(inject(function($rootScope,$controller){

scope=$rootScope.$new();

ctrl=$controller('BlogCtrl',{$scope:scope});

}));

it('shouldcreateshowblogentrycount',function(){

console.log("blogList:"+scope.blogList);

expect(scope.blogList.length).toEqual(0);

//expect(scope.blogList).toBeUndefined();

});

});

describe('BlogViewCtrl',function(){

varscope,ctrl,$httpBackend;

beforeEach(inject(function(_$httpBackend_,$routeParams,

$rootScope,$controller){

$httpBackend=_$httpBackend_;

$httpBackend.expectGET('blogPost').respond({_id:'1'});

$routeParams.id='1';

scope=$rootScope.$new();

ctrl=$controller('BlogViewCtrl',{$scope:scope});

scope.submit();

}));

it('shouldshowblogentryid',function(){

//expect(scope.blogEntry._id).toEqual(1);

//expect(scope.blogList).toBeUndefined();

expect(scope.blg).toEqual(1);

expect(scope.sub).toEqual(true);

});

});

describe('LoginCtrl',function(){

varscope,ctrl;

beforeEach(inject(function($rootScope,$controller){

scope=$rootScope.$new();

ctrl=$controller('LoginCtrl',{$scope:scope});

scope.submit();

}));

it('shouldshowsubmitsuccess',function(){

console.log("LoginCtrl:"+scope.sub);

expect(scope.sub).toEqual(true);

//expect(scope.blogList).toBeUndefined();

});

});

describe('LogoutCtrl',function(){

varscope,ctrl;

Page 267: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

beforeEach(inject(function($rootScope,$controller){

scope=$rootScope.$new();

ctrl=$controller('LogoutCtrl',{$scope:scope});

}));

it('shouldcreateLogoutCtrlcontroller',function(){

console.log("LogoutCtrl:"+ctrl);

expect(ctrl).toBeDefined();

//expect(scope.blogList).toBeUndefined();

});

});

describe('NewBlogPostCtrl',function(){

varscope,ctrl;

beforeEach(inject(function($rootScope,$controller){

scope=$rootScope.$new();

ctrl=$controller('NewBlogPostCtrl',{$scope:scope});

scope.submit();

}));

it('shouldshowsubmitsuccessofNewBlogPostCtrl',

function(){

console.log("NewBlogPostCtrl:"+scope.sub);

expect(scope.sub).toEqual(true);

//expect(scope.blogList).toBeUndefined();

});

});

describe('AboutBlogCtrl',function(){

varscope,ctrl;

beforeEach(inject(function($rootScope,$controller){

scope=$rootScope.$new();

ctrl=$controller('AboutBlogCtrl',{$scope:scope});

}));

it('shouldcreateAboutBlogCtrlcontroller',function(){

console.log("AboutBlogCtrl:"+ctrl);

expect(scope.aboutActiveClass).toEqual("active");

//expect(scope.blogList).toBeUndefined();

});

});

});

Page 268: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

KarmaTestingNowweneedtolaunchKarmaandverifythatalltestsweresuccessful.WeneedtousethecommandlinetolaunchKarma,asmentionedearlier.Dothefollowing:

1. Openacommandwindow.

2. NavigatetothelocationoftheMEANblogproject.

3. Navigateinsidetheprojecttowherethetestfolderandthepackage.jsonfilearelocated.

4. TypethiscommandtolaunchKarma:

karmastarttest/karma.conf.js

YoushouldseeaChromeandaFirefoxbrowserwindowopen.Youshouldthenseetextlikethefollowingdisplayedinthecommandwindow,indicatingsuccess:

Chrome38.0.2125(Linux):Executed16of16SUCCESS(0.17secs)

Firefox33.0.0(Ubuntu):Executed16of16SUCCESS(0.157secs)

TOTAL:32SUCCESS

Page 269: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

End-to-EndTestingTheMEANblogapplicationrequiresachangetotheURLintheE2Etestspecifications.AsinChapter10,thescriptwillneedtologintotheblogapplication.Then,onceloggedin,itwillnavigatethroughtheblogasbeforetoverifythatallpreviousE2Efunctionalitystillworks.Itwillthenneedtologouttotestthelogoutfunctionalityaswell.

Page 270: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

ProtractorConfigurationWealreadycreatedaProtractorconfigurationfilefortheblogapplicationinChapter5,andwe’vejustmovedthatfileintotheMEANapplication.TheProtractorconfigurationfileisshownhereforreference:

/*chapter11/conf.jsProtractorconfigurationfile*/

exports.config={

seleniumAddress:'http://localhost:4444/wd/hub',

specs:['e2e/blog-spec.js']

};

Page 271: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

ProtractorTestSpecificationThemodifiedProtractortestspecificationisshownnext.NoticethenewURL,asmentionedpreviously:

/*chapter11/blog-spec.jsProtractortestspecification*/

describe("BlogApplicationTest",function(){

it("shouldtestthemainblogpage",function(){

browser.get("http://localhost:8080/#!/");

//logsintotheblogapplication

element(by.model("username")).sendKeys("node");

element(by.model("password")).sendKeys("password");

element(by.css('.form-button')).click();

expect(browser.getTitle()).toEqual("AngularJSBlog");

//getsthebloglist

varblogList=element.all(by.repeater('blogPostinblogList'));

//testthesizeoftheblogList

expect(blogList.count()).toEqual(3);

browser.get("http://localhost:8080/#!/blogPost/5387bafe185e4e972996adff");

expect(browser.getTitle()).toEqual("AngularJSBlog");

//getsthecommentlist

varcommentList=element.all(by.repeater('commentinblogEntry.comments'));

//checksthesizeofthecommentList

expect(commentList.count()).toEqual(2);

element(by.css('.navbar-brand')).click();

//logoutoftheblogapplication

element(by.id('lo')).click();

expect(browser.getTitle()).toEqual("AngularJSBlog");

});

});

Page 272: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

ProtractorTestingWearenowreadytostarttheend-to-endtesting.Startanewcommandwindowandenterthefollowingcommandtostartthetestserver:

webdriver-managerstart

OpenanewcommandwindowandnavigatetotherootoftheChapter11project.Typethecommand:

protractortest/conf.js

Youshouldseeabrowserwindowopen.Youshouldthenseethetestscriptlogintotheblogapplicationandnavigatethroughthepagesoftheapplication,asinChapter10.Thescriptshouldthenlogoutoftheapplication.WhentheProtractorscripthasfinished,thebrowserwindowwillclose.

YoushouldseeresultslikethefollowinginthecommandwindowwhentheProtractorscriptcompletes.Thenumberofsecondsthatittakesthescripttofinishwillvarydependingonyourparticularsystem:

Finishedin2.644seconds

1test,5assertions,0failures

Wearenowreadytocontinuewithourdeploymenttothecloud.

Page 273: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

MEANDeploymenttotheCloudNowwewilldeployourblogapplicationtoOpenShiftusingGit.NetBeanscomeswithabuilt-inversionofGitthatisveryeasytoconfigureandusewhenyou’redeployingtoOpenShift.FirstyoumustopenafreeOpenShiftaccount,whichgivesyouthreefreegears(cloudserverinstances)thatcanrunNode.js.Dothefollowing:

1. Gotohttps://www.openshift.com/app/account/newandcreateanewaccount.

2. Clickthe“AddApplication”buttonandcreateanewNode.js0.10application(saveacopyofthepageforreferencelater).

3. AddaMongoDBcartridgetotheapplication(saveacopyofthepageforreferencelater).

4. FollowtheOpenShiftdocumentationandsetupGitonyourdevelopmentenvironment.You’llneedapublicSSHkeytouseGitontheOpenShiftsystem.

5. OnceGitisconfigured,clonetheapplicationwithGittoalocationonyourdriveseparatefromthelocationwhereyouunzippedtheNodeBlogdownload.

6. OpenthenewOpenShiftproject,andcopythefollowingfilesfromtheNodeBlogprojecttothenewOpenShiftproject,replacingtheexistingversions:a. package.json

b. server.js

7. CopythepublicfolderfromtheNodeBlogprojecttothenewOpenShiftproject.

8. CopythedbfolderfromtheNodeBlogprojecttothenewOpenShiftproject.

Nowweneedtotestthecloudversionoftheapplicationlocally.OpenacommandwindowandnavigatetothefolderwhereyouplacedthenewOpenShiftproject.Makesureyouseethepackage.jsonfile,andenternpminstallinthecommandwindowasyoudidearlier.Nowright-clicktheOpenShiftprojectandselect“Run”fromthemenu.Ifyouseetherunningindicatorasshownbefore,theapplicationisworkingproperly.

Now,usingtheGitcredentialsthatyousetupearlierforyourOpenShiftapplication,doaGitremotepushinNetBeansandtheapplicationwillbedeployedtoOpenShift.Ifyouseeanyerrors,usetheOpenShiftdocumentationtoresolvetheerrorcondition.Mostproblemsareusuallyrelatedtocredentialsandcanberesolvedeasily.

Page 274: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

TestingtheCloudBlogOncetheapplicationisdeployedtothecloud,openabrowserandnavigatetotheOpenShift-suppliedlinkforyourapplication.Ifyoudidn’tkeepacopyoftheapplicationpage,logintoyourOpenShiftaccountandclicktheapplicationthatyoujustcreated.Thelinktotheapplicationwillbeshownonthedetailspages.

Onceyounavigatetothelinkforyourapplication,youshouldseetheloginscreenasbefore.Ifyouseetheloginscreen,yourapplicationwassuccessfullydeployedtothecloud.Logintoyourblogapplicationandaddanewblogpost.Addacommenttothepost.Yourblogapplicationshoulddisplaythenewpostandthecomment.Ifyouwouldliketoviewtheapplicationlogfiles,followtheOpenShiftdocumentationrelatedtoviewinglogfilesformorehelp.

Thisconcludesourdiscussiononclouddeployment.Next,we’lltakeabrieflookathowtoturnyourblogapplicationintoamobileHTML5application.

Page 275: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

MobileVersionTheAngularJSblogapplicationhasallthatweneedtobuildamobileversionforanymobileplatform.OurbusinesslogicisintheRESTservices,andallmodernmobiledevicescanaccessRESTservices.Weusedaresponsivedesign,sotheapplicationshouldlookgoodonanymobiledevice.AllmodernmobiledevicesalsohavewebbrowsersandnativebrowsercontrolssuchastheAndroidWebViewthatcanlaunchinternalHTMLpages.

Theprocessforbuildingamobileblogapplicationisstraightforwardforanymobiledevice.Theprocessinvolvesthefollowingsteps:

1. Createanewmobileprojectfortheparticularmobiledeviceofchoice.

2. FollowtheCordovadocumentationandaddCordovatoyourmobileproject.

3. CopytheentirecontentsoftheChapter10project(AngularJsBlogChapter10)tothefolderinthemobileprojectspecifiedbyCordovaasadestinationforHTMLfilesforyourparticularmobileplatform.

4. FollowtheCordovadocumentationandconfigureyourmobileapplicationtolaunchtheindex.htmlfilecopiedfromtheChapter10project.

5. OncethemobileprojectisconfiguredaccordingtotheCordovaspecifications,runtheprojectonanemulatororamobiledevice.

Theapplicationshouldrunandlookthesameasthewebversion.TherearenoAngularJS-specificchangesthatweneedtomaketotheprojectcode.IfyouareinterestedinbuildingAngularJS-basedmobileapplications,feelfreetotakethecodefromChapter10andbuildaCordova-basedHTML5mobileapplicationforyourplatformofchoice.TheCordovawebsitehasdocumentationforallmodernplatformstohelpyougetstartedwithyourproject.

Page 276: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

ConclusionInthischapterwemadeafewmodificationsanddeployedourblogapplicationtothecloud.Werantheapplicationlocally,andalsoranthecloud-deployedapplication.WealsotookaquicklookathoweasyitistobuildmobileapplicationswithAngularJS.Wewillnowfocusonhowtogetyourapplicationfoundbysearchengines.

Page 277: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development
Page 278: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

Chapter12.AngularJSandSEO

Youmightwonderwhywearecoveringsearchengineoptimization(SEO)inanAngularJSbook.Theanswerissimple.

Currently,AngularJSandmostJavaScriptclient-sideframeworksareusedmostlyforwebapplications.Often,SEOisreallynotthatimportantwherewebapplicationsareconcerned.AsAngularJSgainsinpopularity,however,itcouldverywellbecomeamajorplayerintheworldofwebsitedesign.AngularJScouldpotentiallyreplaceclient-sidecodethatiscurrentlywritteninJava,PHP,Ruby,andPython.

Thatisnottosaythatthoselanguageswillbecompletelyreplaced—theywon’t.Java,PHP,Ruby,andPythonwillcontinuetobeasimportantaseverintheworldofsoftwaredevelopment,butinadifferentway.ThoselanguagesandtheirassociatedframeworkswilltakeontheroleofprovidingthebackendRESTservicesneededforAngularJSandotherJavaScriptclient-sideframeworks.WhenyouconsiderthatcompletewebsitescouldsoonbewrittenwithAngularJS,it’sclearthatSEOshouldthenbecomeamajorconcernforAngularJSdevelopers.ThischapterwillhelpyoutobetterunderstandAngularJSandSEO.

Itisalwaysbesttofocusmoreonbuildingagreatwebapplicationorwebsite,andlessonthespecificsofsearchengines.Gooddesignandperformancearealwaysbyfarthemostimportantconsiderationsforanewsoftwareproject.Althoughsearchengineoptimizationisimportant,focusingtoomuchonSEOduringthedesignandimplementationphaseofaprojectcanultimatelycostyouvaluabledevelopmenthours.

Eventually,however,youdohavetofocusongettingyourapplicationorsitefoundbyallthemajorsearchengines.InthisfinalchapterwewilllookatsomeofthewaystogetyournewAngularJSsoftwarefound.ManyofthepracticespresentedherearerecommendedbyGoogle.

Page 279: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

OldVersusNewAngularJSSEOInthepast,usersofwebsitesbuiltwithAngularJShadtofollowaratherarchaicprocessinwhichpagesnapshotsweremadeforanentiresite,andthewebsitecouldthenforwardsearchenginestothesnapshotssothattheywouldseetheprerenderedversionofthesiteratherthantheactualJavaScriptversionofthesite.Sinceconventionalsearchenginesdidn’thavetheabilitytoexecuteJavaScript,pagesbuiltwithAngularJSwererenderedtooldersearchenginesasablankwhitepagewithnocontent.

However,inanewsreleaseonMay23,2014,GoogleconfirmedthatitnowhasthecapabilitytoindexJavaScriptwebsitesandapplications.Thatis,theGooglebothasundergoneupgradestomakeitpossibletoindexsitesandapplicationsthatuseGoogle’sAngularJSandotherJavaScriptframeworks.ForGoogle,thattime-consumingandoftenexpensiveprocessofSEOforAngularJSisnolongernecessary.AlthoughthestateofothersearchenginesandtheirabilitytoexecuteJavaScriptisunknownatthistime,theywillundoubtedlyfollowGoogle’sleadveryquickly,beingforcedtofollowsuitorgetleftbehind.

Therearealsoseveralcompaniesthatspecializeinhelpingclientswiththewebsiteprerenderingprocess.Eventhoughsearchenginesarechanging,manyofthesecompanieswilldoubtlesscontinueofferingprerenderingservicesforseveralyears,ifyoufeeltheneedforthoseservices.

Page 280: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

GettingFoundbySearchEnginesWithallthatsaid,therearestillsomewaystoincreaseyourchancesofgettingabetterrankingwithGoogleandothersearchengines.WewillcovertheSEOtasksthatareabsolutelynecessary:

1. SignupforaGoogleWebmasterToolsaccount,addyoursitetotheaccount,andfollowGoogle’sadvice.

2. Buildasitemap.xmlfileforyoursite.

3. Addmicroformattagstoyoursite.

4. MakesureyourJavaScriptiscleanandeasyforsearchenginestoexecute.

5. AvoidcallingRESTservicesthattakelongerthantwosecondstoreturnresults.

Page 281: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

GoogleWebmasterToolsOneofthefirstthingsthatyoushoulddoforSEOistogetaGoogleWebmasterToolsaccount.OnceyouaddyoursiteandstarttofollowGoogle’sadvice,youwillseeimmediateimprovementsinyourrankingandthenumberofpagesofyoursitethatareindexedbyGoogle.TheadvicegivenbyGoogleappliestoothersearchenginesaswell.Don’texpecttoseeSEOimprovementsdrasticallyincreaseyourranking,however;SEOisanongoingandtime-consumingprocessthatcantakemonthsorevenyearstorendersignificantresults.

Page 282: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

AddingaSitemapAccordingtoGoogle,asitemapfileisveryimportanttoSEO.Google’sWebmasterToolswillhelpyouwiththeprocessofbuildingasitemapanduploadingittoGoogle.Usingasitemapspeedsuptheprocessofgettingyoursiteindexedbymakingsearchenginesawareofthepagesandlinksonyoursite.Youshouldkeepthesitemapup-to-date,withanynewpagesadded.Makesuretoremoveanypagesfromthesitemapthatnolongerexistonthesite.

Page 283: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

MicroformatTagsAnotherthingthatimprovesSEOistheuseofmicroformattags(tag-basednavigation).Theuseoftag-basednavigationstartedonblogsitesbuthasspreadconsiderablyoverthelastfewyears;itisnowusedonbusinesswebsitesaswell.

Tag-basednavigationusestheformatshownheretoindicatetosearchenginesthatthepagecontentcontainstherelatedkeywords.Asyoucansee,thehrefattributecontainsalinktoapageonthesite,andtherelattributetellssearchenginesthatthepagecontainsthereferencedkeywords:

<!--chapter12/tag-basednavigation-->

<p>Tags:<ahref="http://www.ulboracms.org/#!/"rel="tag">UlboraCMS</a>,

<ahref="http://www.ulboracms.org/#!/"rel="tag">JavaCMS</a>,

<ahref="http://www.ulboracms.org/#!/"rel="tag">RESTservice</a>,

<ahref="http://www.ulboracms.org/#!/"rel="tag">JSONREST</a>,

<ahref="http://

www.ulboracms.org/#!/article/26"rel="tag">RESTwebservices</a></p>

Tag-basednavigationissupportedbyallmajorsearchengines.

Page 284: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

BuildingCleanClientCodeOneofthebestwaystoimproveSEOistocreateacleanandefficientAngularJSapplication.UnnecessaryJavaScriptshouldalwaysbeavoided.JavaScriptmethodsshouldexecutequickly,withnounnecessaryprocessesrunninginthebackground.

Searchenginestakepagespeedintoconsiderationwhenrankingsites.Pagesthatcontainlong-runningJavaScriptfunctionsmaygetdroppedbyGoogleandothersearchenginesandnotgetindexed.Onceapagegetsdroppedbyasearchengine,itcantakealongtimetogetthatpageindexedagain.

Page 285: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

BuildingFastRESTServicesOnelastthingthatcandirectlyaffectpagespeedandSEOisthespeedoftheRESTwebservicesusedtopopulatepagecontent.PagesthatrelyonslowRESTservicescansufferasaresult.RESTservicesshouldreturnresultsintwosecondsorless.

ServicesthatreturnresultsinunderasecondarebestforSEOandsiteperformance.AlthoughRESTservicedesignisbeyondthescopeofthisparticularbook,IwanttoemphasizehowimportantwebservicedesignistoSEOwhenwebpagesrelyonthoseservicesforcontent.WhenyoursitedependsonRESTservices,alwaysmakesurethoseservicesperformwellandaddnounnecessarydelaytoyoursiteorapplication.Alwaysinsistonpeak-performingservices.

Page 286: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

ConclusionThatbringsustotheendofthischapterandtheendofthebook.I’vedonemybesttopresentAngularJSinawaythatwillmakeiteasytounderstandforbothbeginnersandexperienceddevelopersalike.TheconceptofusingJavaScriptclient-sideframeworkstobuildcompletefrontendapplicationsandwebsitesisrelativelynew,andoftenreferredtoas“cuttingedge”bymany.TherecentGoogleannouncementrelatedtoJavaScriptandSEOmentionedearlieratteststothat.

Butthingsthatareconsideredcuttingedgetodaywillbecommonplaceinafewyears.IbelieveAngularJSwillbeattheforefrontofapplicationdevelopmentincomingyears,andiswellworththetimespentlearningtheframework.Thisbookisonlyastartingpoint,however.NowyoumustgooutanddevelopgreatapplicationswithAngularJS,andhavefunbuildingthoseapplicationstoo!Remember,thebestAngularJSapplicationisawell-designedAngularJSapplication.Alwaysbuildthebestapplicationsthatyoupossiblycan.It’sworththeeffortintheend.

Page 287: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development
Page 288: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

References

AngularJS

Bootstrap

jQuery

WikipediaentryforMVC

WikipediaentryforREST

WikipediaentryforWebservice

UlboraCMS

UlboraCMSatSourceForge

WikipediaentryforSPA

WikipediaentryforRWD

Page 289: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development
Page 290: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

Index

Symbols

$locationservice,AddingaLoginController

$rootScopeobject,AngularJSModels

$scopeobject,AngularJSModels(MVC)

addingbehaviorto,InitializingtheModelwithControllers

attachingmethodsto,AddingaLoginController

modelsin,AngularJSModels

<select>element(HTML),MEANBlogTemplates

{{}}(doublecurlybraces),ControllerBusinessLogic

A

ActiveServerPages(ASP),MVCandAngularJS

Ajax

RESTservicesand,AngularJSandRESTServices

sites,HTML5Mode

AngularJS

asclient-sideframework,JavaScriptClient-SideFrameworks

asMVCframework,ANewandBetterWay

bootstrappingwith,BootstrappingtheApplication

businesslogicin,ControllerBusinessLogic

controllers,AngularJSControllers(MVC)

dependencyinjection,DependencyInjection

Page 291: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

directives,AngularJSDirectives-Conclusion

downloadingfilesfor,TheIDE

HTMLcompiler,TheHTMLCompiler

HTML5and,HTML5Mode

integratingwithotherframeworks,IntegratingAngularJSwithOtherFrameworks

modelclasses,AngularJSModels(MVC)

routes,AngularJSRoutes

searchenginesand,ModernSearchEngines

SEOfor,AngularJSandSEO-Conclusion

services,non-REST,CreatingAngularJSServices

single-pageapplicationsin,Single-PageApplications

templates,AngularJSTemplates

testing,TestingAngularJSApplications

viewclassesin,AngularJSViews(MVC)

ApacheCordova,MobileVersion

applications

addingservicemodulesto,ModifyingApp.js

runninginIDEs,RunningtheApplications,RunningtheBlogApplication

runningwithmodels,RunningtheApplication

testinginIDEs,TestingAngularJSApplicationsintheIDE-Protractor

transportable,MEANServices

usingRESTservicesin,BlogApplicationPublicServices

Page 292: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

ASP.NETframework,JavaScriptClient-SideFrameworks

authentication,HandlingUserAuthentication-RetrievingUserCredentials

B

BasicAuthentication,UsingBasicAuthentication,MEANBlogControllers

bootstrapping,BootstrappingtheApplication

HTMLcodeand,EditingtheHTMLCode

businesslogic,ServicesandBusinessLogic-Conclusion

addingtoprojects,BlogApplicationBusinessLogic-TestingServiceswithKarma

controller,ControllerBusinessLogic

incontrollers,ControllerBusinessLogic

RESTservicesand,RESTServices

userauthentication,HandlingUserAuthentication-RetrievingUserCredentials

using,UsingtheBusinessLogic

C

CakePHPframework,TheOldWay

integratingwithAngularJS,IntegratingAngularJSwithOtherFrameworks

callbackfunctions,RESTServicesandControllers

cascadingstylesheets,AddingStylesandPresentationLogic

ChromeDeveloperTools,RunningtheApplication

clientcode,BuildingCleanClientCode

client-sideframeworks,JavaScriptClient-SideFrameworks

integratingAngularJSwith,IntegratingAngularJSwithOtherFrameworks

modelclasses,AngularJSModels(MVC)

viewclassesin,AngularJSViews(MVC)

Page 293: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

client-sidesecurity,AngularJSSecurity

clouddeployment,MEANDeploymenttotheCloud

makingappstransportable,MEANServices

continuousintegration(CI),TestingAngularJSApplications

end-to-endtestingand,End-to-EndTesting

controlleras,FormSubmission

controllers,AngularJSControllers(MVC),AngularJSControllers-Conclusion

behavior,addingwith,AddingBehaviorwithControllers-AddingBehaviorwithControllers

businesslogicin,ControllerBusinessLogic,ControllerBusinessLogic

editingJavaScriptcodefor,EditingtheJavaScriptCode

end-to-endtestingof,End-to-EndTestingwithProtractor-RunningProtractor

formdata,using,UsingSubmittedFormData

formsubmissionsand,FormSubmission-FormSubmission

initializingmodelswith,InitializingtheModelwithControllers

JSTestDriverand,JSTestDriver-TestingwithJSTestDriver

Karma,testingwith,TestingwithKarma-RunningKarmaUnitTests

login,adding,AddingaLoginService

logout,AddingaLogoutController-AddingaLogoutController

MEAN,MEANBlogControllers-MEANBlogControllers

modelsand,ChangestotheControllers,ModifyingtheControllers-ModifyingtheControllers

multiple,forsingleelements,FormSubmission

presentationlogicand,PresentationLogicandFormattingData

Page 294: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

projects,addingto,AddingaNewBlogController

Protractor,testingwith,End-to-EndTestingwithProtractor-RunningProtractor

RESTservicesand,RESTServicesandControllers

securitymodificationsfor,SecurityModificationstoOtherControllers

templatesand,AngularJSTemplates

cookies,UsingBasicAuthentication

checking,CheckingUserCredentials

deleting,DeletingUserCredentials

holdingusercredentialsin,HoldingUserCredentials

retrievinginformationfrom,RetrievingUserCredentials

Cross-OriginResourceSharing(CORS)layer,AngularJSSecurity

CSS3

mediaqueriesin,AddingaLoginTemplate

stylingpageswith,UsingCSS3toStylethePage

D

data

formattingwithcontrollers,PresentationLogicandFormattingData,AddingMockBlogData

mock,addingtoprojects,AddingMockBlogData

storage,RESTservicesand,RESTServices

dates,formatting,AddingMockBlogData,AddingStylesandPresentationLogic

dependencyinjection(DI),DependencyInjection

npminstallcommandand,AddingNode.jsDependencies

OpenShiftand,MEANDeploymenttotheCloud

Page 295: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

servicesmoduleand,UpdatingtheProjectforREST

deployment,MEANCloudandMobile-Conclusion

cloud,MEANDeploymenttotheCloud

local,LocalDeployment-ProtractorTesting

directives,AngularJSDirectives-Conclusion

addingtoprojects,AddingtheCustomDirective-PassingtheTitleAttribute

buildingpresentationlogicwith,AddingStylesandPresentationLogic

custom,building,BuildingCustomDirectives

defined,WhatAreDirectives?

end-to-endtestingof,End-to-EndTesting

Karmaand,TestingDirectiveswithKarma-KarmaTesting

namingconventionsfor,NamingConventionsforDirectives

ng-click,AddingBehaviorwithControllers,AddingBehaviorwithControllers

ng-include,BuildingCustomDirectives

ng-model,AddingBehaviorwithControllers,AddingBehaviorwithControllers,AngularJSTemplates

ng-repeat,AddingStylesandPresentationLogic,ListServices

ng-submit,FormSubmission,AddingaLoginTemplate,AddingBlogEntries

ng-view,AngularJSTemplates

passingtitleattribute,PassingtheTitleAttribute

Protractorand,End-to-EndTesting

restrictoption,TheRestrictOption

templateattributesfor,TemplateAttributes

Page 296: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

templateUrlattribute,TheTemplateURL

unittestingfor,TestingDirectiveswithKarma-KarmaTesting

viewsand,AddingStylesandPresentationLogic

E

end-to-endtesting(E2E),TestingAngularJSApplications,Protractor

businesslogic,End-to-EndTesting

MEANstackdeployment,End-to-EndTesting-ProtractorTesting

models,End-to-EndTesting

non-RESTservices,End-to-EndTesting

ofdirectives,End-to-EndTesting

ofsecurity,End-to-EndTesting

RESTservices,End-to-EndTesting

ExpressJS,TheMEANApplication

buildingRESTserviceswith,ANewandBetterWay

F

factoryfunction,WaystoCreateAngularJSServices,CreatingAngularJSServices

failedRESTservicecalls,TheJSONResponse

Firefox,RunningKarmaUnitTests

forms,FormSubmission-UsingSubmittedFormData

data,using,UsingSubmittedFormData

submissionsfrom,FormSubmission-FormSubmission

frameworks

ASP.NET,JavaScriptClient-SideFrameworks

CakePHP,IntegratingAngularJSwithOtherFrameworks,TheOldWay

Page 297: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

client-side,JavaScriptClient-SideFrameworks,AngularJSViews(MVC),AngularJSModels(MVC)

Jasmine,CreatingTestScripts

MVC,AngularJSViews(MVC),MVCandAngularJS-Conclusion

server-sidewebMVC,JavaScriptClient-SideFrameworks

SpringMVC,JavaScriptClient-SideFrameworks,DependencyInjection,IntegratingAngularJSwithOtherFrameworks,TheOldWay,ANewandBetterWay

Struts,JavaScriptClient-SideFrameworks,TheOldWay

web,TheOldWay-ChoiceTwo

webMVC,JavaScriptClient-SideFrameworks

Zend,TheOldWay

G

Git,Conclusion,MEANDeploymenttotheCloud

Google,OldVersusNewAngularJSSEO

GoogleChrome,RunningKarmaUnitTests

GoogleWebmasterTools,GoogleWebmasterTools

H

hashbangmode,HTML5Mode,HTML5Mode

HTMLcompiler,TheHTMLCompiler

HTML5,HTML5Mode

editing,EditingtheHTMLCode

HistoryAPI,HTML5Mode

mobileapplicationsfor,MobileVersion

Page 298: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

mode,turningoff,HTML5Mode

modifyingtousemodels,ModifyingtheHTML

HTTPmethods,RESTServices

HTTPS,Authentication

I

IDE,TheIDEandAngularJSProjects-Conclusion

HTML,editing,EditingtheHTMLCode

JavaScript,editing,EditingtheJavaScriptCode

NetBeans,TheIDE

runningapplicationsin,RunningtheApplications

templates,creating,CreatingtheTemplates

testingapplicationsin,TestingAngularJSApplicationsintheIDE-TestingAngularJSApplicationsintheIDE

inputelements(fromforms),UsingSubmittedFormData

J

Jasmineframework,CreatingTestScripts

Java,AngularJSandSEO

JavaServerPages(JSP),MVCandAngularJS

JavaScript

console,accessing,AngularJSControllers

editing,EditingtheJavaScriptCode

JenkinsCIsystem,Protractor

testingand,TestingConsiderations

JQuery,IntroductiontoAngularJS

Page 299: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

downloading,TheIDE

JSTestDriver,TestingAngularJSApplications,JSTestDriver-TestingwithJSTestDriver

testscripts,creating,CreatingTestScripts

testingwith,TestingwithJSTestDriver

JSON,RESTresponseobjectsas,TheJSONResponse

JsTestRunner,JsTestRunner

K

Karma,TestingAngularJSApplications,KarmaTestRunner

businesslogicand,KarmaConfiguration-KarmaTesting

configuring,KarmaConfiguration,KarmaConfiguration

configuringforMEANstackdeployment,KarmaConfiguration

directivesand,TestingDirectiveswithKarma-KarmaTesting

installing,InstallingKarma

MEANstackdeployment,testing,TestingwithKarma-KarmaTesting

models,testing,TestingServiceswithKarma-KarmaTesting

non-RESTservicesand,KarmaConfiguration-KarmaTesting

RESTservices,testingwith,TestingServiceswithKarma-KarmaServiceSpecifications

security,testingwith,TestingwithKarma-KarmaTesting

servicespecificationsfor,KarmaServiceSpecifications

testingconsiderationsfor,TestingConsiderations

unittests,running,RunningKarmaUnitTests

karma-ng-html2js-preprocessorKarmaplugin,TestingDirectiveswithKarma

Page 300: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

L

listservices,ListServices

lists,returning,ListServices

locationProviderservice,HTML5Mode

loginservices,AddingaLoginService

controllersand,AddingaLoginService

logincontrollersand,AddingaLoginController

non-REST,AddingaLoginService

security,AddingaLoginController,AddingaLoginTemplate

templates,AddingaLoginTemplate

userauthentication,AddingaLoginService,AddingaLoginTemplate

M

MEAN(MongoDB,ExpressJS,AngularJS,andNode.js)stackdeployment,MEANCloudandMobile-Conclusion

changingserviceURLfor,MEANServices

clouddeployment,MEANDeploymenttotheCloud

controllers,MEANBlogControllers-MEANBlogControllers

end-to-endtestingof,End-to-EndTesting-ProtractorTesting

Karmaand,TestingwithKarma-KarmaTesting

mobileapps,MobileVersion

Node.jsdependencies,adding,AddingNode.jsDependencies

Protractorand,End-to-EndTesting-ProtractorTesting

runninglocally,RunningtheBlogApplicationLocally

services,MEANServices

Page 301: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

templates,MEANBlogTemplates

unittesting,TestingwithKarma-KarmaTesting

microformattags,MicroformatTags

mobileapps

aswrapperforserver-sideapplication,ChoiceOne

convertingwebapplicationsto,ChoiceTwo

devices,designingfor,ResponsiveDesignConsiderations-ResponsiveDesignConsiderations

MEANstackdeploymentsfor,MobileVersion

responsivedesignand,ResponsiveDesignConsiderations-ResponsiveDesignConsiderations

webMVCframeworksand,TheOldWay

models,AngularJSModels(MVC),AngularJSModels-Conclusion

addingtoapp,ModifyingApp.js

controllersand,AngularJSControllers(MVC),ChangestotheControllers,ModifyingtheControllers-ModifyingtheControllers

end-to-endtesting,End-to-EndTesting

initializingwithcontrollers,InitializingtheModelwithControllers

Karma,testingwith,TestingServiceswithKarma-KarmaTesting

propertiesof,ModelProperties

Protractor,testingwith,End-to-EndTesting

RESTservicesassourceof,PublicRESTServices

scopepropertiesand,ModelProperties

unittesting,TestingServiceswithKarma-KarmaTesting

Page 302: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

MongoDB,InstallingNode.js,npm,andMongoDB

interactingwith,TheMEANApplication

Mongoose.jsODMlibrary,TheMEANApplication

MVCframeworks,MVCandAngularJS-Conclusion

AngularJSas,ANewandBetterWay

controllers,AngularJSControllers(MVC)

modelclasses,AngularJSModels(MVC)

responsivedesignand,ResponsiveDesignConsiderations-ResponsiveDesignConsiderations

testingconsiderationsfor,TestingConsiderations

viewclasses,AngularJSViews(MVC)

web,TheOldWay-ChoiceTwo

N

NetBeans

configuring,TheIDE

installing,TheIDE

JSTestDriver,JSTestDriver-TestingwithJSTestDriver

JsTestRunnersupportin,JsTestRunner

Karmaand,KarmaConfiguration

Node.jsplugin,installing,InstallingtheNetBeansNode.jsPlugin

Protractorand,TestingAngularJSApplicationsintheIDE

ng-apptag,BootstrappingtheApplication

ng-clickdirective,AddingBehaviorwithControllers,AddingBehaviorwithControllers

Page 303: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

ng-includedirective,BuildingCustomDirectives

ng-modeldirective,AddingBehaviorwithControllers,AddingBehaviorwithControllers,AngularJSTemplates

ng-repeatdirective,ListServices

ng-submitdirective,FormSubmission,AddingBlogEntries

userauthenticationand,AddingaLoginTemplate

ng-viewdirective,AngularJSTemplates

dynamiccontentand,CreatingtheBlogProject

single-pageapplicationsin,Single-PageApplications,AngularJSTemplates

ngRoutemodule,DependencyInjection

Node.js,InstallingNode.js,npm,andMongoDB

dependencies,adding,AddingNode.jsDependencies

Karmaand,TestingAngularJSApplicationsintheIDE

Protractorand,TestingAngularJSApplicationsintheIDE

NoSQL,TheMEANApplication

injectionattack,AngularJSSecurity

npm(Node.jspackagemanager),InstallingNode.js,npm,andMongoDB

npminstallcommand,AddingNode.jsDependencies

O

OpenShift,MEANDeploymenttotheCloud

P

package.jsonfile,TestingAngularJSApplicationsintheIDE

performanceandSEO,BuildingCleanClientCode

PHP,MVCandAngularJS,AngularJSandSEO

Page 304: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

presentationlogic

addingtoprojects,AddingStylesandPresentationLogic

controllersand,PresentationLogicandFormattingData,AddingMockBlogData

directivesand,AddingStylesandPresentationLogic

projects,AngularJSViewsandBootstrap-Conclusion

application,running,RunningtheBlogApplication

controllers,adding,AddingaNewBlogController

creating,CreatingtheBlogProject

directives,adding,AddingtheCustomDirective-PassingtheTitleAttribute

end-to-endtesting,End-to-EndTesting

functionality,adding,ViewingtheBlogPost-ViewingtheBlogPost

Karma,testingwith,TestingwithKarma-ProtractorTesting

menus,adding,AddingaBootstrapMenu

mockdata,adding,AddingMockBlogData

non-RESTservices,adding,BlogApplicationBusinessLogic-TestingServiceswithKarma

presentationlogic,adding,AddingStylesandPresentationLogic

Protractor,testingwith,End-to-EndTesting

RESTservices,updatingfor,UpdatingtheProjectforREST

servicemodules,adding,ModifyingApp.js

styles,addingto,AddingStylesandPresentationLogic

stylingpagesin,UsingCSS3toStylethePage

templates,adding,AddingaNewBlogTemplate

Page 305: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

testing,TestingwithKarma-KarmaTesting

TwitterBootstrap,adding,TwitterBootstrap

unittesting,TestingwithKarma-KarmaTesting

Protractor,TestingAngularJSApplications,Protractor

businesslogicand,End-to-EndTesting

configuring,ConfiguringProtractor

directivesand,End-to-EndTesting

installing,InstallingProtractor

MEANstackdeploymentand,TestingwithKarma-KarmaTesting

models,testingwith,End-to-EndTesting

non-RESTservicesand,End-to-EndTesting

RESTservices,testingwith,End-to-EndTesting

running,RunningProtractor

securityand,End-to-EndTesting

SeleniumServerand,StartingtheSeleniumServer

testserver,starting,ProtractorTesting

testspecifications,creating,CreatingProtractorTestSpecifications,ProtractorTestSpecification

testingconsiderationsfor,TestingConsiderations

testingwith,ProtractorTesting

providerfunction,WaystoCreateAngularJSServices,CreatingAngularJSServices

publicSSHkeys,MEANDeploymenttotheCloud

Python,AngularJSandSEO

Page 306: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

R

responsivedesign,ResponsiveDesignConsiderations-ResponsiveDesignConsiderations

HTML5mobileapplicationsand,MobileVersion

RESTservices,AngularJSandRESTServices-Conclusion

AngularJSand,AngularJSandRESTServices

asobjects,WaystoCommunicatewithRESTServices

authenticatingacrossmultiple,ServicesandBusinessLogic

ChromeDeveloperToolsand,RunningtheApplication

communicatingwith,WaystoCommunicatewithRESTServices

controllersand,RESTServicesandControllers

creatingAngularJSservices,WaystoCreateAngularJSServices

end-to-endtestingof,End-to-EndTesting

failedcalls,TheJSONResponse

JavaScriptdebuggersand,AngularJSControllers

Karma,testingwith,TestingServiceswithKarma-KarmaServiceSpecifications,KarmaServiceSpecifications

lists,displayingwith,ListServices

lists,returning,ListServices

Protractor,testingwith,End-to-EndTesting

responseobjectsfrom,TheJSONResponse

responsetimesfor,ControllerBusinessLogic,BuildingFastRESTServices

SEOand,BuildingFastRESTServices

testingspecificationsfor,KarmaServiceSpecifications

Page 307: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

troubleshooting,RunningtheApplication

unittestsand,ModifyingtheControllers

restrictoption(directives),TheRestrictOption

routes,AngularJSRoutes

adding,forsecurity,AddingNewRoutes

templatesand,AngularJSTemplates

Ruby,AngularJSandSEO

RubyonRails,TheOldWay

S

scopeproperties,ModelProperties

displaying,ControllerBusinessLogic

errorhandlingwith,AddingaLoginController

passingvaluesto,TemplateAttributes

searchengineoptimization(SEO),AngularJSandSEO-Conclusion

clientcodeand,BuildingCleanClientCode

GoogleWebmasterTools,GoogleWebmasterTools

microformattags,MicroformatTags

performanceand,BuildingCleanClientCode

RESTservicesand,BuildingFastRESTServices

sitemaps,AddingaSitemap

tag-basednavigation,MicroformatTags

searchengines,ModernSearchEngines

security,AngularJSSecurity-Conclusion

end-to-endtestingfor,End-to-EndTesting

Page 308: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

Karma,testingwith,TestingwithKarma-KarmaTesting

logincontrollers,AddingaLoginController

logintemplate,AddingaLoginTemplate

logoutcontrollersand,AddingaLogoutController-AddingaLogoutController

logoutlinkand,AddingaLogoutLink

modificationsofcontrollers,SecurityModificationstoOtherControllers

non-RESTservicesand,ServicesandBusinessLogic

Protractorand,End-to-EndTesting

routes,adding,AddingNewRoutes

unittesting,TestingwithKarma-KarmaTesting

userauthenticationand,Authentication

SeleniumServer,StartingtheSeleniumServer

running,Protractor

SEOcompanies,OldVersusNewAngularJSSEO

server-sidewebMVCframeworks,JavaScriptClient-SideFrameworks

servicefunction,WaystoCreateAngularJSServices,CreatingAngularJSServices

servicesmodule,UpdatingtheProjectforREST

modifyingtomakeappstransportable,MEANServices

services,non-REST,ServicesandBusinessLogic-Conclusion

checkingdatawith,CheckingUserCredentials

cookies,readingwith,RetrievingUserCredentials

creating,CreatingAngularJSServices

deletingdatawith,DeletingUserCredentials

Page 309: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

holdingdatawith,HoldingUserCredentials

login,AddingaLoginService

MEAN,MEANServices

userauthentication,HandlingUserAuthentication-RetrievingUserCredentials

single-pageapplications,Single-PageApplications

sitemaps,AddingaSitemap

SpringMVCframework,JavaScriptClient-SideFrameworks,DependencyInjection,TheOldWay

buildingRESTserviceswith,ANewandBetterWay

integratingwithAngularJS,IntegratingAngularJSwithOtherFrameworks

SSL,AngularJSSecurity

Strutsframework,JavaScriptClient-SideFrameworks,TheOldWay

styles

addingtoprojects,AddingStylesandPresentationLogic

Bootstrapand,AddingStylesandPresentationLogic

successcallbackfunction,RESTServicesandControllers,ModifyingtheControllers

T

tag-basednavigation,MicroformatTags

templates,AngularJSTemplates

creating,CreatingtheTemplates

login,AddingaLoginTemplate

MEAN,MEANBlogTemplates

projects,addingto,AddingaNewBlogTemplate

viewsas,AngularJSTemplates

Page 310: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

templateUrlattribute(directives),TheTemplateURL

testscripts,creating,CreatingTestScripts

testspecifications,TestingConsiderations

testing,TestingAngularJSApplications

clouddeployments,TestingtheCloudBlog

considerationsforMVC,TestingConsiderations

end-to-end,Protractor

inIDE,TestingAngularJSApplicationsintheIDE-Protractor

Karma,KarmaTestRunner

libraryfiles,locationof,JsTestRunner

Protractor,Protractor

unit,KarmaTestRunner

withJSTestDriver,TestingwithJSTestDriver

withJsTestRunner,JsTestRunner

Torvalds,Linus,Conclusion

TravisCIsystem,Protractor

testingand,TestingConsiderations

TwitterBootstrap,IntroductiontoAngularJS,AngularJSViewsandBootstrap,TwitterBootstrap

downloading,TheIDE

menus,adding,AddingaBootstrapMenu

U

unittesting,TestingAngularJSApplications,KarmaTestRunner

asynchronouscalls,ModifyingtheControllers

Page 311: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

businesslogic,KarmaConfiguration-KarmaTesting

directives,TestingDirectiveswithKarma-KarmaTesting

MEANstackdeployment,TestingwithKarma-KarmaTesting

models,TestingServiceswithKarma-KarmaTesting

non-RESTservices,KarmaConfiguration-KarmaTesting

RESTservices,ModifyingtheControllers

security,testing,TestingwithKarma-KarmaTesting

withJSTestDriver,JSTestDriver-TestingwithJSTestDriver

userauthentication,HandlingUserAuthentication-RetrievingUserCredentials

basic,UsingBasicAuthentication

logincontrollers,AddingaLoginController

loginservices,AddingaLoginService

logintemplate,AddingaLoginTemplate

logoutcontrollersand,AddingaLogoutController-AddingaLogoutController

logoutlink,AddingaLogoutLink

securityand,AngularJSSecurity

testingwithKarma,KarmaTestSpecifications-KarmaTesting

unittesting,KarmaTestSpecifications-KarmaTesting

usercredentials

checking,CheckingUserCredentials

deleting,DeletingUserCredentials

holding,HoldingUserCredentials

retrieving,RetrievingUserCredentials

Page 312: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

V

V8JavaScriptengine(Google),TheMEANApplication

views

astemplates,AngularJSTemplates

controllersand,AngularJSControllers(MVC)

directivesand,AddingStylesandPresentationLogic

testingwithKarma,TestingwithKarma-KarmaTesting

W

webapplications

convertingtomobile,ChoiceTwo

wrappersfor,ChoiceOne

webbrowsers,securityand,AngularJSSecurity

webframeworks,TheOldWay-ChoiceTwo

webMVCframeworks,JavaScriptClient-SideFrameworks

webdriver-managertool,StartingtheSeleniumServer

WebDriverJS,Protractor

WebViewcomponent(Android),ChoiceOne

Z

ZendFramework,TheOldWay

Page 313: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development
Page 314: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

AbouttheAuthor

KenWilliamsonisasoftwareengineerandarchitectwithovertwentyyearsofexperienceinthetechnologyindustry.Ken’sfirstprogramminglanguagewasAssemblyusingthe6502chip.HemovedontoCandC++andeventuallytoJavaandJavaScript.Kenhasdesignedandwrittenmobile,desktop,andserversoftwareforsomeofthebiggestcompaniesintheworld.

KenholdsaBSinComputerSciencefromKennesawStateUniversity.HeisthefounderofseveralopensourceprojectsincludingUlboraCMS;hehasalsocontributedtomanyotheropensourceprojectsovertheyears.KenmakeshishomeinAtlanta,Georgiawithhiswife,Sherry.YoucanfindKenatwww.ken-williamson.com.

Page 315: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development
Page 316: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

Colophon

TheanimalsonthecoverofLearningAngularJSareFloridacricketfrogs(Acrisgryllusdorsalis),whicharesubspeciesoftheSoutherncricketfrog.TheycanbefoundallthroughoutFlorida,withtheexceptionoftheextremenorthwesternpanhandle.

Cricketfrogspreferafreshwaterenvironment,suchaspuddles,lakes,marshes,andstreams.Theyareeasilyrecognizedbythetriangularmarkontheirheadsandthetwodarkstripesontheirrear.

BreedingoccursfromAprilintothefall,withsmallclustersofeggsattachedtosubmergedplants.Malesadvertisetheirreadinesswithaloud,rapidcallofgick,gick,gick,whichhasbeendescribedbysomeasthesoundofmarblesclickingtogether.

AdultFloridacricketfrogsgrowtobeabout1.25incheslong,andvaryincolorfromdarkbrowntotanorgreen.Theyenjoyhealthypopulationgrowthandarenotconsideredthreatenedinanyway.

ManyoftheanimalsonO’Reillycoversareendangered;allofthemareimportanttotheworld.Tolearnmoreabouthowyoucanhelp,gotoanimals.oreilly.com.

ThecoverimageisfromLydekker’sRoyalNaturalHistory.ThecoverfontsareURWTypewriterandGuardianSans.ThetextfontisAdobeMinionPro;theheadingfontisAdobeMyriadCondensed;andthecodefontisDaltonMaag’sUbuntuMono.

Page 317: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

PrefaceWhyIWroteThisBook

WhatThisBookCovers

WhoShouldReadThisBook

TheChaptersinThisBook

ConventionsUsedinThisBook

UsingCodeExamples

Safari®BooksOnline

HowtoContactUs

1.IntroductiontoAngularJSJavaScriptClient-SideFrameworks

Single-PageApplications

BootstrappingtheApplication

DependencyInjection

AngularJSRoutes

HTML5Mode

ModernSearchEngines

AngularJSTemplates

AngularJSViews(MVC)

AngularJSModels(MVC)

AngularJSControllers(MVC)

ControllerBusinessLogic

IntegratingAngularJSwithOtherFrameworks

TestingAngularJSApplications

Conclusion

Page 318: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

2.TheIDEandAngularJSProjectsTheIDE

EditingtheHTMLCode

EditingtheJavaScriptCode

CreatingtheTemplates

RunningtheApplications

TestingAngularJSApplicationsintheIDE

JsTestRunner

KarmaTestRunner

Protractor

Conclusion

3.MVCandAngularJSTheOldWay

ChoiceOne

ChoiceTwo

ANewandBetterWay

TestingConsiderations

ResponsiveDesignConsiderations

Conclusion

4.AngularJSControllersInitializingtheModelwithControllers

AddingBehaviorwithControllers

ControllerBusinessLogic

PresentationLogicandFormattingData

FormSubmission

UsingSubmittedFormData

Page 319: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

JSTestDriverCreatingTestScripts

TestingwithJSTestDriver

TestingwithKarmaInstallingKarma

KarmaConfiguration

RunningKarmaUnitTests

End-to-EndTestingwithProtractorInstallingProtractor

ConfiguringProtractor

CreatingProtractorTestSpecifications

StartingtheSeleniumServer

RunningProtractor

Conclusion

5.AngularJSViewsandBootstrapAngularJSTemplates

CreatingtheBlogProject

AddingaNewBlogController

AddingaNewBlogTemplate

TwitterBootstrap

AddingaBootstrapMenu

AddingMockBlogData

UsingCSS3toStylethePage

AddingStylesandPresentationLogic

ViewingtheBlogPost

RunningtheBlogApplication

Page 320: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

TestingwithKarmaKarmaConfiguration

KarmaTestSpecifications

KarmaTesting

End-to-EndTestingProtractorTestSpecification

ProtractorTesting

Conclusion

6.AngularJSandRESTServicesRESTServices

AngularJSandRESTServices

WaystoCreateAngularJSServices

WaystoCommunicatewithRESTServices

UpdatingtheProjectforREST

RESTServicesandControllers

TheJSONResponse

ListServices

TestingServiceswithKarmaKarmaServiceSpecifications

End-to-EndTestingProtractorConfiguration

ProtractorTestSpecification

Conclusion

7.AngularJSModelsPublicRESTServices

ChangestotheControllers

ModelProperties

Page 321: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

BlogApplicationPublicServices

ModifyingtheHTML

ModifyingApp.js

ModifyingtheControllers

RunningtheApplication

TestingServiceswithKarmaKarmaServiceSpecifications

KarmaTesting

End-to-EndTestingProtractorTestSpecification

ProtractorTesting

Conclusion

8.ServicesandBusinessLogicHandlingUserAuthentication

UsingBasicAuthentication

CreatingAngularJSServices

HoldingUserCredentials

CheckingUserCredentials

DeletingUserCredentials

RetrievingUserCredentials

BlogApplicationBusinessLogic

UsingtheBusinessLogic

TestingServiceswithKarmaKarmaConfiguration

KarmaTestSpecifications

KarmaTesting

Page 322: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

End-to-EndTesting

ProtractorConfiguration

ProtractorTestSpecification

ProtractorTesting

Conclusion

9.AngularJSDirectivesTheHTMLCompiler

WhatAreDirectives?

BuildingCustomDirectives

NamingConventionsforDirectives

TheRestrictOption

TheTemplateURL

TemplateAttributes

AddingtheCustomDirective

PassingtheTitleAttribute

RunningtheBlogApplication

TestingDirectiveswithKarmaKarmaConfiguration

KarmaTestSpecification

KarmaTesting

End-to-EndTestingProtractorConfiguration

ProtractorTestSpecification

ProtractorTesting

Conclusion

10.AngularJSSecurity

Page 323: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

Authentication

AddingaLoginService

AddingaLoginController

SecurityModificationstoOtherControllers

AddingaLogoutController

AddingaLoginTemplate

AddingNewRoutes

AddingaLogoutLink

RunningtheBlogApplicationLoggingIn

TestingwithKarmaKarmaConfiguration

KarmaTestSpecifications

KarmaTesting

End-to-EndTestingProtractorConfiguration

ProtractorTestSpecification

ProtractorTesting

OneLastPointonSecurity

Conclusion

11.MEANCloudandMobileLocalDeployment

InstallingNode.js,npm,andMongoDB

InstallingtheNetBeansNode.jsPlugin

TheMEANApplication

Node.jsPublicFolder

Page 324: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

MEANServices

MEANBlogControllers

MEANBlogTemplates

AddingComments

AddingBlogEntries

AddingNewRoutes

AddingNode.jsDependencies

RunningtheBlogApplicationLocally

TestingwithKarmaKarmaConfiguration

KarmaTestSpecifications

KarmaTesting

End-to-EndTestingProtractorConfiguration

ProtractorTestSpecification

ProtractorTesting

MEANDeploymenttotheCloud

TestingtheCloudBlog

MobileVersion

Conclusion

12.AngularJSandSEOOldVersusNewAngularJSSEO

GettingFoundbySearchEngines

GoogleWebmasterTools

AddingaSitemap

MicroformatTags

Page 325: Learning AngularJS: A Guide to AngularJS Developmentpepa.holla.cz/wp...AngularJS-A-Guide-to-AngularJS-Development.pdf · Learning AngularJS: A Guide to AngularJS Development

BuildingCleanClientCode

BuildingFastRESTServices

Conclusion

References

Index