383

Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

  • Upload
    others

  • View
    1

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga
Page 2: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

LearningVue.js2

Page 3: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

TableofContents

LearningVue.js2CreditsAbouttheAuthorAcknowledgmentsAbouttheReviewerwww.PacktPub.com

Whysubscribe?DedicationPreface

WhatthisbookcoversWhatyouneedforthisbookWhothisbookisforConventionsReaderfeedbackCustomersupport

DownloadingtheexamplecodeErrataPiracyQuestions

1.GoingShoppingwithVue.jsBuzzwordsVue.jshistoryThemostimportantthingaboutVue.jsLet'sgoshopping!

ImplementingashoppinglistusingjQueryImplementingashoppinglistusingVue.jsAnalyzingdatabindingusingdevelopertoolsBringinguserinputtothedatawithtwo-waybindingRenderingthelistofitemsusingthev-fordirectiveCheckanduncheckshoppinglistitemsAddingnewshoppinglistitemsusingthev-ondirective

UsingVue.jsinanexistingprojectVue.js2.0!ProjectsusingVue.js

GrammarlyOptimizelyFilterBlendPushSilver

BookroadmapLet'smanagetime!

Togglethetitlebyusingcomputedproperties

Page 4: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Left-padtimevaluesusingcomputedpropertiesKeepstatewithstart,pause,andstopbuttons

ExerciseSummary

2.Fundamentals–InstallingandUsingMVVMarchitecturalpatternDefineProperty,getters,andsettersComparingwithotherframeworks

ReactAngularVue

Vue.jsfundamentalsReusablecomponentsVue.jsdirectivesPluginsinVue.jsExerciseApplicationstateandVuexvue-cliVuepluginsforIDEs

Installing,using,anddebuggingaVue.jsapplicationInstallingVue.js

StandaloneCDNBowerCSP-compliantnpmvue-cliDevbuild

DebuggingyourVueapplicationScaffoldingourapplications

ScaffoldingtheshoppinglistapplicationBootstrapingyourPomodoroapplication

ExerciseSummary

3.Components–UnderstandingandUsingRevisitingcomponentsBenefitsofusingcomponents

DeclaringtemplatesinHTMLHandlingdataandelpropertiesinsideacomponentScopeofthecomponentsComponentsinsideothercomponents

RewritingtheshoppinglistwithsimplecomponentsDefiningtemplatesforallthecomponentsDefiningandregisteringallthecomponents

Page 5: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

ExerciseSingle-filecomponents

PluginsforIDEsStyleandscopeHot-reloadingPreprocessors

HTMLpreprocessorsCSSpreprocessorsJavaScriptpreprocessors

Rewritingourshoppinglistapplicationwithsingle-filecomponentsAddItemComponentConfiguringItemComponentandItemsComponent

ExerciseRewritingthePomodoroapplicationwithsingle-filecomponentsReactivebindingofCSStransitionsSummary

4.Reactivity–BindingDatatoYourApplicationRevisitingdatabindingInterpolatingdata

AddingtitleofthePomodorostateExercise

UsingexpressionsandfiltersExpressionsFiltersExercise

RevisitingandapplyingdirectivesTwo-waybindingusingthev-modeldirectiveTwo-waybindingbetweencomponentsBindingattributesusingthev-binddirectiveConditionalrenderingusingv-ifandv-showdirectivesArrayiterationusingthev-fordirective

CreatingShoppingListComponentandmodifyingItemsComponentModifyingApp.vue

Eventlistenersusingthev-ondirectiveShorthandsExercise

KittensSummary

5.Vuex–ManagingStateinYourApplicationParent-childcomponents'communication,events,andbrainteaserWhydoweneedaglobalstatestore?WhatisVuex?Howdoesthestoreworkandwhatissospecialaboutit?Greetingswithstore

Page 6: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

StorestateandgettersMutationsActions

InstallingandusingtheVuexstoreinourapplicationsUsingtheVuexstoreintheshoppinglistapplicationUsingVuexstoreinthePomodoroapplication

Bringinglifetostart,pause,andstopbuttonsBindingPomodorominutesandsecondsCreatingthePomodorotimerChangingthekitten

Summary6.Plugins–BuildingYourHousewithYourOwnBricks

ThenatureofVuepluginsUsingthevue-resourcepluginintheshoppinglistapplication

CreatingasimpleserverInstallingvue-resource,creatingresources,anditsmethodsFetchingalltheshoppingliststheapplicationstartsUpdatingserverstatusonchangesCreatinganewshoppinglistDeletingexistingshoppinglistsExercise

CreatingandusingaplugininthePomodoroapplicationCreatingtheNoiseGeneratorpluginUsingtheplugininthePomodoroapplicationCreatingabuttontotogglethesoundExercise

Summary7.Testing–TimetoTestWhatWeHaveDoneSoFar!

Whyunittests?UnittestsforVueapplicationWritingunittestsfortheshoppinglistapplication

Testingactions,getters,andmutationsGoodtestcriteriaCodecoverageFakingserverresponsesandwritingasynchronoustestsTestingcomponents

WritingunittestsforourPomodoroapplicationWhatisend-to-endtesting?Nightwatchfore2eWritinge2etestsforthePomodoroapplicationSummary

8.Deploying–TimetoGoLive!Softwaredeployment

WhatisGitHub?

Page 7: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

WhatisTravis?WhatisHeroku?

MovingtheapplicationtotheGitHubrepositorySettingcontinuousintegrationwithTravisDeployingthePomodoroapplication

CheckinglogsPreparingtheapplicationtorunonHeroku

DeployingtheshoppinglistapplicationTryingHerokulocally

9.WhatIsNext?ThejourneysofarVue2.0Revisitingourapplications

ShoppinglistapplicationThePomodoroapplication

Whyisitjustthebeginning?Addingfeaturestoourapplications

ShoppinglistapplicationThePomodoroapplication

BeautifyingourapplicationsLogotypeIdentityanddesignAnimationsandtransitions

ExtendingourapplicationstootherdevicesSummary

10.SolutionstoExercisesExerciseforchapter1Exercisesforchapter2

EnhancingMathPluginCreatingaChromeapplicationofthePomodorotimer

Exercisesforchapter3Exercise1Exercise2

Summary

Page 8: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

LearningVue.js2

Page 9: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

LearningVue.js2Copyright©2016PacktPublishing

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

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

PacktPublishinghasendeavoredtoprovidetrademarkinformationaboutallofthecompaniesandproductsmentionedinthisbookbytheappropriateuseofcapitals.However,PacktPublishingcannotguaranteetheaccuracyofthisinformation.

Firstpublished:December2016

Productionreference:1071216

PublishedbyPacktPublishingLtd.

LiveryPlace

35LiveryStreet

Birmingham

B32PB,UK.

ISBN978-1-78646-994-6

www.packtpub.com

Page 10: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Credits

Author

OlgaFilipova

CopyEditor

SameenSiddiqui

Reviewer

Bogdan-AlinBâlc

ProjectCoordinator

SheejalShah

CommissioningEditor

WilsonD'souza

Proofreader

SafisEditing

AcquisitionEditor

ChaitanyaNair

Indexer

TejalDaruwaleSoni

ContentDevelopmentEditor

DivijKotian

ProductionCoordinator

MelwynD'sa

TechnicalEditor

PrajaktaMhatre

Page 11: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

AbouttheAuthorOlgaFilipovawasborninUkraine,inKyiv.Shegrewupinafamilyofphysicists,scientists,andprofessors.ShestudiedsystemanalysisattheNationalUniversityofUkraineKyivPolytechnicInstitute.Attheageof20,shemovedtoPortugalwhereshedidherbachelor'sandmaster'sdegreesincomputerscienceintheUniversityofCoimbra.Duringherstudies,sheparticipatedinresearchanddevelopmentofEuropeanprojectsandbecameanassistantteacherofoperatingsystemsandcomputergraphics.Afterobtaininghermaster'sdegree,shestartedtoworkatFeedzai.Atthattime,itwasasmallteamoffourwhodevelopedaproductfromscratch,andnowitisoneofthemostsuccessfulPortuguesestartups.Atsomepoint,hermainresponsibilitybecametodevelopalibrarywritteninJavaScriptwhosepurposewastobringdatafromtheenginetothewebinterface.ThismarkedOlga'smaindirectionintech—webdevelopment.Atthesametime,shecontinuedherteachingpracticeinacourseofprofessionalwebdevelopmentinthelocalprofessionaleducationcenterinCoimbra.

In2013,alongwithherbrotherandherhusband,shestartedaneducationalprojectbasedinUkraine.Thisproject'snameisEdEraandithasgrownfromasmallplatformofonlinecoursesintoabigplayerintheUkrainianeducationalsystem.Currently,EdEraispointinginantheinternationaldirectionandpreparinganawesomeonlinecourseaboutIT.Don'tmissit!

In2014,Olga,withherhusbandanddaughter,movedfromPortugaltoBerlin,whereshestartedworkingatMeetricsasafrontendengineer,andafterayearshebecametheleadofanamazingteamoffrontendsoftwaredevelopers.

OlgaishappilymarriedtoanawesomeguycalledRui,whoisalsoasoftwareengineer.RuistudiedwithOlgaattheUniversityofCoimbraandworkedwithheratFeedzai.OlgahasasmartandbeautifuldaughtercalledTaissa,afluffycatcalledPatusca,andtwofluffiestchinchillascalledBarabashkaandCheburashka.

Page 12: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

AcknowledgmentsIamgratefultoPacktPublishingforofferingmethepossibilitytowritethisbook.Youaregreatandsoisyourteam.ThankyouDivij,Chaitanya,Prajakta,andthewholePacktteamforbeingawesomeandsupportingmethroughallthisjourneyinsuchafriendlyandwarmway.

Qualityissomethingthatisdifficulttoachievewhenworkingonsomethingonyourown.Thankyou,Packtteam,you'vebeenawesome.AndabigspecialgratitudegoestoRomania,toBogdan,whothoroughlyreviewedthebook,ranallthecodesnippets,tests,andlint.Bogdan'sattentiontoeventhemosttinydetailsisastonishing.ThebookwasrewrittenafterBogdan'scomments,anditbecamesomuchcleaner.Thankyouverymuch,BogdanandAlex,fortherecommendation.

Time.Support.Love.Whenyouhavethesethreethingsyouarehappyandanychallengeintheworldcanscareyou.Whenyouhavethesethreethingsyouknowthatyouarecapableofeverything.Whenyouhavethesethreethingsyouhavepower.Butyoucanneverhavethesethingsalone.Thatiswhyyoumustbeeternallygratefultothosewhoprovidetime,support,andlovetoyou.

ThatiswhymybigthanksgoestomycompanywhereIamcurrentlyworking—Meetrics.Meetricsprovidedmewithtimetowritethebook.Theytrustedmeandallowedmetouseafractionofmyworkingtimeforwritingthebook.Thankyouverymuch!

Iwanttothanktoallmyfriendsandcolleagueswhosupportedmeduringthisjourney.EverytimeIcometoMeetricsmyteamasksmehowthebookisgoing.Everytimewe'regoingtoPortugalorUkraine,ourfriendsandfamilyask.EverydaymyfriendsfromBerlinaskmehowisitgoing.Thankyou,people,youareawesome!Thankyou,EdErateam,forbeingamazingandpostponingimportanttasksbecauseofmybook.

IwouldlovetoexpressgratitudetomyparentsforeducatingmewithsomuchlovethatIknowthatIamcapableofanything.IknowthatIwillnotfail.Thankyouforallyourloveandsupport.Thankyouforgivingmethisconfidenceinmyself.Iwanttothanktomylovelydaughter,whoseloveandhelpkeepsmegoingandcontinuingwhatI'mdoing,knowingthatallthisisnotfornothing.Iwanttothanktomybrotherforallthefunweshareevenandmostlywhilewe'reworking.

AndIwanttoaddressaspecialthankswithlovetomyhusband.AlongthiswritingjourneyRuihasgivenmetime,support,andlove.RuidideverythingathomesoIcouldhaveallthetimeforwriting.RuifelteveryslightchangeinmymoodandprovidedsupportduringallofthemsoIcouldfeelcomfortableagainandwrite.IfIwouldstayupthewholenightwritingandneededsomeonetobenearby,Ruiwouldstayupthewholenight.Foreverychapterinthebook,Ruiwasthefirstpersontoreviewthem.Thiswasinvaluablefeedback.RuigavemechaptersbackfullofcorrectionsandIfeltsad.Butthenhewouldsaysomethinglike:Ohmygod,Olga,thischapterisamazing!Iunderstoodeverything!Iamlookingforwardforthe

Page 13: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

nextchaptertoseewhat'snext!Whensomeonewhoyouloveverymuchtellsyouthis,youjustwanttomoveonandcontinueyouramazingwriting.Thankyouverymuchforthis!

Page 14: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

AbouttheReviewerBogdan-AlinBâlcisateamleadwithapassionforfrontendtechnologies.HehasworkedwithJavaScriptforthepast8years,fromtheemergenceofjQueryandAjaxtomodernfull-fledgedMVCframeworks.Whenhe'snotlookingintosomenewJSchallenge,hespendstimewithhisfriends,playinggamesandwatchingsports.

Page 15: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

www.PacktPub.comForsupportfilesanddownloadsrelatedtoyourbook,pleasevisitwww.PacktPub.com.

DidyouknowthatPacktofferseBookversionsofeverybookpublished,withPDFandePubfilesavailable?YoucanupgradetotheeBookversionatwww.PacktPub.comandasaprintbookcustomer,youareentitledtoadiscountontheeBookcopy.Getintouchwithusatservice@packtpub.comformoredetails.

Atwww.PacktPub.com,youcanalsoreadacollectionoffreetechnicalarticles,signupforarangeoffreenewslettersandreceiveexclusivediscountsandoffersonPacktbooksandeBooks.

https://www.packtpub.com/mapt

Getthemostin-demandsoftwareskillswithMapt.MaptgivesyoufullaccesstoallPacktbooksandvideocourses,aswellasindustry-leadingtoolstohelpyouplanyourpersonaldevelopmentandadvanceyourcareer.

Page 16: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Whysubscribe?FullysearchableacrosseverybookpublishedbyPacktCopyandpaste,print,andbookmarkcontentOndemandandaccessibleviaawebbrowser

Page 17: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

DedicationIdevotethisbooktomydaughter,Taissa.

Page 18: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

PrefaceThisbookisaboutVue.js.WewillstartourjourneytryingtounderstandwhatVue.jsis,howitcomparestootherframeworks,andwhatitallowsustodo.WewilllearndifferentaspectsofVue.jswhilebuildingsmallfunnyapplicationsontopofitandapplyingtheseaspectsinpractice.Intheend,wewilllookbacktoseewhat'vewelearnedandhavealookintothefuturetoseewhatwecanstilllearnanddo.So,youwilllearnthefollowing:

WhatisVue.jsandhowitworksReactivityanddatabindingwithVue.jsReusablecomponentswithVue.jsPluginsforVue.jsTestinganddeployingapplicationswritteninVue.js

AlltheexamplesinthisbookarebuiltontopoftherecentlyreleasedVue2.0version.Thebookalsocontainsreferencestothepreviousversionregardingdeprecatedorchangedaspectsoftheframework.

IamsureyouwillenjoytheprocessofbuildingapplicationsusingVue.jswiththisbook.

Page 19: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

WhatthisbookcoversChapter1,GoingShoppingwithVue.js,containsanintroductiontoVue.js,totheterminologyusedthroughthebook,andfirstbasicexamples.

Chapter2,Fundamentals–InstallingandUsing,explainsthebehindthescenesofVue.js,providestheoreticalinsightsintothearchitecturalpattern,touchesnearlyallthemainVue.jsconcepts,andbootstrapstheapplicationsthatwillbedevelopedthroughthebook.

Chapter3,Components–UnderstandingandUsing,goesdeepintocomponentsandexplainshowtorewriteapplicationsusingasimplecomponentsystemandsingle-filecomponents.

Chapter4,Reactivity–BindingDatatoYourApplication,containsadetailedexplanationsoftheusageofdatabindingmechanismsinVue.js.

Chapter5,Vuex–ManagingStateinYourApplication,containsdetailedintroductiontoVuex,astatemanagementsystemforVue.js,andexplainshowtouseitinyourapplicationinordertoachieveanice,maintainablearchitecture.

Chapter6,Plugins–BuildingYourHousewithYourOwnBricks,showshowtousepluginsinVueapplicationsandexplainshowtouseanexistingplugininanapplicationandexplainshowtobuildourownpluginandthenuseit.

Chapter7,Testing–TimetoTestWhatWeHaveDoneSoFar,containsanintroductiontothetestingtechniquesthatcanbeusedinVueapplicationstobringthemtotheneededlevelofquality.Wetackleitbyshowinghowtowriteunittestsandhowtodevelopend-to-endtestsfortheapplicationsinthebook.

Chapter8,Deploying–TimetoGoLive!,showshowtobringyourVueapplicationtotheworld,guaranteeingitsqualitywithcontinuousintegrationtools.ItexplainshowtoconnectaGitHubrepositorytotheTraviscontinuousintegrationsystemandtotheHerokuclouddeploymentplatform.

Chapter9,WhatIsNext,wrapsupeverythingthathasbeendonesofarandleavesthereaderwiththefollowupsteps.

Appendix,SolutionstoExercises,providessolutionstotheexercisesforfirstthreechapters.

Page 20: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

WhatyouneedforthisbookTherequirementsforthisbookarethefollowing:

ComputerwithanInternetconnectionTexteditor/IDENode.js

Page 21: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

WhothisbookisforThisbookisforwebdevelopersorforpeoplewhowanttobecomewebdevelopers.Whetheryouhavejuststartedtoworkwithwebtechnologiesoryouarealreadyaguruofframeworksandlanguagesinthevastoceanofwebtechnologies,thisbookmightshowyousomethingnewintheworldofreactivewebapplications.IfyouareaVuedeveloperandhaveusedVue1.0,thisbookmightbeausefulguideforyoutomigratetoVue2.0,sincealltheexamplesofthebookarebasedonVue2.0.EvenifyouarealreadyusingVue2.0,thisbookmightbeaniceexerciseofbuildinganapplicationfromscratch,applyingallVueandsoftwareengineeringconceptsandtakingittothedeploymentstage.

Atleastsometechnicalbackgroundisrequired.IfyoucanalreadywritecodeinJavaScript,itisahugeplus.

Page 22: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

ConventionsInthisbook,youwillfindanumberoftextstylesthatdistinguishbetweendifferentkindsofinformation.Herearesomeexamplesofthesestylesandanexplanationoftheirmeaning.

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

Ablockofcodeissetasfollows:

exportdefault{

components:{

ShoppingListComponent,

ShoppingListTitleComponent

},

computed:mapGetters({

shoppinglists:'getLists'

})

}

Whenwewishtodrawyourattentiontoaparticularpartofacodeblock,therelevantlinesoritemsaresetinbold:

exportdefault{

components:{

ShoppingListComponent,

ShoppingListTitleComponent

},

computed:mapGetters({

shoppinglists:'getLists'

}),

methods:mapActions(['populateShoppingLists']),

store,

mounted(){

this.populateShoppingLists()

}

}

Anycommand-lineinputoroutputiswrittenasfollows:

cdshopping-list

npminstallvue-resource--save-dev

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

Note

Warningsorimportantnotesappearinaboxlikethis.

Page 23: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Tip

Tipsandtricksappearlikethis.

Page 24: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

ReaderfeedbackFeedbackfromourreadersisalwayswelcome.Letusknowwhatyouthinkaboutthisbook-whatyoulikedordisliked.Readerfeedbackisimportantforusasithelpsusdeveloptitlesthatyouwillreallygetthemostoutof.Tosendusgeneralfeedback,[email protected],andmentionthebook'stitleinthesubjectofyourmessage.Ifthereisatopicthatyouhaveexpertiseinandyouareinterestedineitherwritingorcontributingtoabook,seeourauthorguideatwww.packtpub.com/authors.

Page 25: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

CustomersupportNowthatyouaretheproudownerofaPacktbook,wehaveanumberofthingstohelpyoutogetthemostfromyourpurchase.

Page 26: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

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

Youcandownloadthecodefilesbyfollowingthesesteps:

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

Oncethefileisdownloaded,pleasemakesurethatyouunziporextractthefolderusingthelatestversionof:

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

ThecodebundleforthebookisalsohostedonGitHubathttps://github.com/PacktPublishing/Learning-Vue.js-2.Wealsohaveothercodebundlesfromourrichcatalogofbooksandvideosavailableathttps://github.com/PacktPublishing/.Checkthemout!

Page 27: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

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

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

Page 28: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

PiracyPiracyofcopyrightedmaterialontheInternetisanongoingproblemacrossallmedia.AtPackt,wetaketheprotectionofourcopyrightandlicensesveryseriously.IfyoucomeacrossanyillegalcopiesofourworksinanyformontheInternet,pleaseprovideuswiththelocationaddressorwebsitenameimmediatelysothatwecanpursuearemedy.

Pleasecontactusatcopyright@packtpub.comwithalinktothesuspectedpiratedmaterial.

Weappreciateyourhelpinprotectingourauthorsandourabilitytobringyouvaluablecontent.

Page 29: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

QuestionsIfyouhaveaproblemwithanyaspectofthisbook,[email protected],andwewilldoourbesttoaddresstheproblem.

Page 30: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Chapter1.GoingShoppingwithVue.js"Vue.jsisaJavaScriptframeworkforbuildingastonishingwebapplications.Vue.jsisaJavaScriptlibraryforcreatingwebinterfaces.Vue.jsisatoolthatleveragestheuseofMVVMarchitecture."

SimplifiedJavaScriptJargonsuggeststhatVue.jsisaJavaScriptlibraryforcreatinguserinterfaces(Views)basedonunderlyingdatamodels(http://jargon.js.org/_glossary/VUEJS.md).

TheofficialVue.jswebsite(https://vuejs.org/)justsomemonthsagostatedthatVue.jswerereactivecomponentsformodernwebinterfaces.

NowitstatesthatVue.jsisaprogressiveJavaScriptframework:

Page 31: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

SowhatisVue.jsafterall?Framework?Tool?Library?Shoulditbeusedforbuildingfull-stackwebapplicationsorjustforaddingsomespecialfunctionality?ShouldIswitchfrommyfavoriteframeworktoit?Ifyes,why?CanIuseitalongsideothertoolsinmyproject?Whatadvantagesitmightbring?

Inthischapter,wewilltrytofindtheanswerstoallthesequestions.WewillslightlytouchVue.jsanduseitwithinsomesmallandsimpleexamples.

Morespecifically,wewilldothefollowing:

LearnwhatVue.jsis,itsimportantparts,anditshistoryLearnwhatprojectsuseVue.jsBuildasimpleshoppinglistusingVue.jsandcomparetheimplementationtothejQueryimplementationofthesameapplicationBuildasimplePomodorotimerusingVue.jsEnjoyasmallandsimpleexercise

Page 32: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

BuzzwordsTherewillbelotsofbuzzwords,abbreviations,andotherhipstercombinationsoflettersinthisbook.Pleasedonotbeafraidofthem.Icantellyoumorebut,forthemostpartofthingsyouneedtodousingVue.jsoranyotherframework,youdonotneedtoknowthemallbyheart!But,inanycase,letusleavethethesaurusheresothatyoubecomeconfusedwithterminologyatanypointofthebook,youcancomebackhereandhavealook:

Applicationstate:Thisisaglobalcentralizedstateoftheapplication.Thedatainthisstateisinitializedwhentheapplicationisstarted.Thisdatacanbeaccessedbyanyapplication'scomponent;however,itcannotbechangedeasilybythem.Eachitemofthestatehasanattachedmutationthatcanbedispatchedonspecialeventsoccurringinsidetheapplication'scomponents.Bootstrap:ThisisaprojectthatprovidesasetofstylesandJavaScripttoolsfordevelopingaresponsiveandniceapplicationwithouthavingtothinkalotaboutCSS.ContentDistributionNetwork(CDN):Thisisaspecialserverwhoseaimistodeliverdatatotheuserswithhighavailabilityandhighperformance.PeopleandcompanieswhodevelopframeworksliketodistributethemviaCDNsbecausetheyallowthemjusttopointouttheCDN'sURLintheinstallationinstructions.Vue.jsishostedinnpmcdn(https://npmcdn.com/),whichisareliableandglobalnetworkforthethingsthatarepublishedtothenpm.Components:ThesearethepiecesoftheapplicationwiththeirowndataandViewthatcanbereusedthroughtheapplication,actingasabrickfromwhichthehouseisbeingbuilt.CascadingStyleSheets(CSS):ThisisasetofstylestoapplytotheHTMLdocumenttomakeitniceandbeautiful.DeclarativeViews:ThesearetheViewsthatprovideawayofdirectdatabindingbetweenplainJavaScriptdatamodelsandtherepresentation.Directives:ThesearespecialHTMLelementsattributesinVue.jsthatallowdatabindingindifferentways.DocumentObjectModel(DOM):ThisisaconventionforrepresentingnodesinmarkuplanguagessuchasHTML,XML,andXHTML.ThenodesofthedocumentsareorganizedintoaDOMtree.WhensomeonesaysinteractingwithDOM,itisjusttheirfancywayofsayinginteractingwithHTMLelements.npm:ThisisapackagemanagerforJavaScriptandallowssearching,installing,andmanagingJavaScriptpackages.Markdown:Thisisahuman-friendlysyntaxthatallowswebwriterstowritetheirtextwithoutworryingaboutstylesandHTMLtags.Markdownfileshavea.mdextension.ModelViewViewModel(MVVM):ThisisanarchitecturalpatternwhosecentralpointisaViewModelthatactsasabridgebetweentheViewandthedatamodel,allowingthedataflowbetweenthem.ModelViewController(MVC):Thisisanarchitecturalpattern.ItallowsseparatingViewsfromModelsandfromthewaythatinformationflowsfromViewstoModels,andviceversa.

Page 33: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

One-waydatabinding:ThisisthetypeofdatabindingwherethechangesinthedatamodelareautomaticallypropagatedtotheViewlayer,butnotviceversa.Rapidprototyping:IntheWeb,thisisatechniqueofeasilyandrapidlybuildingthemockupsoftheuserinterface,includingsomebasicuserinteraction.Reactivity:IntheWeb,thisisactuallytheimmediatepropagationofanychangesofdatatotheViewlayer.Two-waydatabinding:ThisisthetypeofdatabindingwherethechangesinthedatamodelareautomaticallypropagatedtotheViewlayer,andthechangesthathappenintheViewlayerareimmediatelyreflectedinthedatamodel.Userinterface(UI):Thisisasetofvisualcomponentsthatallowtheusertocommunicatewiththeapplication.Vuex:ThisisanarchitectureforVueapplicationsandallowssimplemanagementoftheapplicationstate.

Page 34: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Vue.jshistoryWhen,EvanYou,Vue.jscreator(http://evanyou.me/),wasworkingatGoogleCreativeLabsononeoftheprojects,theyneededtofastprototypearatherbigUIinterface.WritingalotofrepeatedHTMLwasclearlytime-andresource-consuming,andthat'swhyEvanstartedlookingforsomealreadyexistingtoolforthispurpose.Tohissurprise,hediscoveredthattherewasnotool,library,orframeworkthatcouldfitexactlyintothepurposeofrapidprototyping!Atthattime,Angularwaswidelyused,React.jswasjuststarting,andframeworkssuchasBackbone.jswereusedforlarge-scaleapplicationswithMVCarchitecture.ForthekindofprojectthatneededsomethingreallyflexibleandlightweightjustforquickUIprototyping,neitherofthesecomplexframeworkswereadequate.

Whenyourealizethatsomethingcooldoesnotexistandyouareabletocreateit—justdoit!

Note

Vue.jswasbornasatoolforrapidprototyping.Nowitcanbeusedtobuildcomplexscalablereactivewebapplications.

ThatwaswhatEvandid.Thatishowhecametotheideaofcreatingalibrarythatwouldhelpinrapidprototypingbyofferinganeasyandflexiblewayofreactivedatabindingandreusablecomponents.

Likeeverygoodlibrary,Vue.jshasbeengrowingandevolving,thusprovidingmorefeaturesthanitwaspromisingfromthebeginning.Currently,itprovidesaneasywayofattachingandcreatingplugins,writingandusingmixins,andoveralladdingcustombehavior.Vuecanbeusedinsuchaflexiblewayandissononopinionatedoftheapplicationstructuringthatitdefinitelycanbeconsideredasaframeworkcapableofsupportingtheend-to-endbuildingofcomplexwebapplications.

Page 35: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

ThemostimportantthingaboutVue.jsVue.jsallowsyoutosimplybindyourdatamodelstotherepresentationlayer.Italsoallowsyoutoeasilyreusecomponentsthroughouttheapplication.

Youdon'tneedtocreatespecialmodelsorcollectionsandtoregistereventsobjectinthere.Youdon'tneedtofollowsomespecialsyntax.Youdon'tneedtoinstallanyofnever-endingdependencies.

YourmodelsareplainJavaScriptobjects.TheyarebeingboundtowhateveryouwantinyourViews(text,inputtext,classes,attributes,andsoon),anditjustworks.

Youcansimplyaddthevue.jsfileintoyourprojectanduseit.Alternatively,youcanusevue-cliwithWebpackandBrowserifyfamily,whichnotonlybootstrapsthewholeprojectbutalsosupportshotreloadingandprovidesdevelopertools.

YoucanseparatetheViewlayerfromstylesandJavaScriptlogicoryoucanputitalltogetherintothesameVuefileandbuildyourcomponents'structureandlogicinthesameplace.ThereispluginsupportforallmodernandcommonlyusedIDEs.

Youcanusewhateverpreprocessorsyouwant,andyoucanuseES2015.Youcanuseitalongsideyourfavoriteframeworkyouhavebeendevelopingin,oryoucanuseititself.Youcanuseitjusttoaddasmallfunctionality,oryoucanusethewholeVueecosystemtobuildcomplexapplications.

Ifyouwanttocheckhowitcomparestootherframeworks,suchasAngularorReact,thenpleasevisithttp://vuejs.org/guide/comparison.html.

IfyouwanttocheckoutalltheamazingthingsaboutVue.js,thenyouaremorethanwelcometovisithttps://github.com/vuejs/awesome-vue.

Page 36: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Let'sgoshopping!Idon'tknowhowbutIcanfeelthatyourweekendiscloseandthatyouarestartingtothinkaboutgoingshoppingtobuytheneededgroceriesforthenextweek.Unlessyouareageniuswhoisabletomaintainthewholelistinyourheadoryouareamodestpersonwhodoesnotneedsomuch,youprobablymakeashoppinglistbeforegoingshopping.Maybeyouevenusesomeappforthat.Now,Iaskyou:whynotuseyourownapp?Howdoyoufeelaboutcreatinganddesigningit?Let'sdothat!Let'screateourownshoppinglistapplication.Let'sstartbycreatingarapidprototypeforit.It'sareallyeasytask—buildaninteractiveprototypefortheshoppinglist.

Itshouldshowthelistandallowustoaddandremovetheitems.Actually,it'sverysimilartoaToDolist.Let'sstartdoingitusingclassicHTML+CSS+JS+jQueryapproach.WewillalsousetheBootstrapframework(http://getbootstrap.com/)tomakethingsalittlebitmorebeautifulwithouthavingtowriteextensiveCSScode.(Yes,becauseourbookisnotaboutCSSandbecausemakingthingswithBootstrapissocrazilyeasy!)

Page 37: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

ImplementingashoppinglistusingjQueryProbably,yourcodewillenduplookingassomethinglikethefollowing:

HereistheHTMLcode:

<divclass="container">

<h2>MyShoppingList</h2>

<divclass="input-group">

<inputplaceholder="addshoppinglistitem"

type="text"class="js-new-itemform-control">

<spanclass="input-group-btn">

<button@click="addItem"class="js-addbtnbtn-default"

type="button">Add!</button>

</span>

</div>

<ul>

<li>

<divclass="checkbox">

<label>

<inputclass="js-item"name="list"

type="checkbox">Carrot

</label>

</div>

</li>

<li>

<divclass="checkbox">

<label>

<inputclass="js-item"name="list"type="checkbox">Book

</label>

</div>

</li>

<liclass="removed">

<divclass="checkbox">

<label>

<inputclass="js-item"name="list"type="checkbox"

checked>Giftforaunt'sbirthday

</label>

</div>

</li>

</ul>

</div>

HereistheCSScode:

.container{

width:40%;

margin:20pxauto0pxauto;

}

.removed{

color:gray;

}

.removedlabel{

Page 38: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

text-decoration:line-through;

}

ulli{

list-style-type:none;

}

HereistheJavaScript/jQuerycode:

$(document).ready(function(){

/**

*Addbuttonclickhandler

*/

functiononAdd(){

var$ul,li,$li,$label,$div,value;

value=$('.js-new-item').val();

//validateagainstemptyvalues

if(value===''){

return;

}

$ul=$('ul');

$li=$('<li>').appendTo($ul);

$div=$('<div>')

.addClass('checkbox')

.appendTo($li);

$label=$('<label>').appendTo($div);

$('<input>')

.attr('type','checkbox')

.addClass('item')

.attr('name','list')

.click(toggleRemoved)

.appendTo($label);

$label

.append(value);

$('.js-new-item').val('');

}

/**

*Checkboxclickhandler-

*togglesclassremovedonliparentelement

*@paramev

*/

functiontoggleRemoved(ev){

var$el;

$el=$(ev.currentTarget);

$el.closest('li').toggleClass('removed');

}

$('.js-add').click(onAdd);

$('.js-item').click(toggleRemoved);

});

Tip

Page 39: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

DownloadingtheexamplecodeDetailedstepstodownloadthecodebundlearementionedinthePrefaceofthisbook.ThecodebundleforthebookisalsohostedonGitHubathttps://github.com/PacktPublishing/Learning-Vue.js-2.Wealsohaveothercodebundlesfromourrichcatalogofbooksandvideosavailableathttps://github.com/PacktPublishing/.Checkthemout!

Ifyouopenthepageinabrowser,youwillprobablyseesomethinglikethefollowing:

ShoppinglistimplementationusingtheHTML+CSS+jQueryapproach

PleasehavealookatJSFiddleathttps://jsfiddle.net/chudaol/u5pcnLw9/2/.

Asyoucansee,itisaverybasicpieceofHTMLcodethatcontainsanunorderedlistofelements,whereeachelementispresentedwithacheckboxandatext—aninputfortheusertextandtheAdd!button.EachtimetheAdd!buttonisclicked,thecontentofthetextinputistransformedintoalistentryandappendedtothelist.Whenthecheckboxofanyitemisclicked,thestateofanentryistoggledfromtotobuy(unchecked)tobought(checked).

Let'salsoaddafeaturethatallowsustochangethetitleofthelist(itmightbecomeusefulifweendupimplementingmultipleshoppinglistsintheapplication).

So,wewillendupwithsomeextramarkupandsomemorejQueryeventlistenersandhandlers:

<divclass="container">

<h2>MyShoppingList</h2>

<!--...-->

Page 40: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

<divclass="footer">

<hr/>

<em>Changethetitleofyourshoppinglisthere</em>

<inputclass="js-change-title"type="text"

value="MyShoppingList"/>

</div>

</div>

//Andjavascriptcode:

functiononChangeTitle(){

$('h2').text($('.js-change-title').val());

}

$('.js-change-title').keyup(onChangeTitle);

CheckJSFiddleathttps://jsfiddle.net/chudaol/47u38fvh/3/.

Page 41: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

ImplementingashoppinglistusingVue.jsThiswasaverysimpleexample.Let'strytoimplementitstep-by-stepusingVue.js.Thereareplentyofwaysofincludingvue.jsintoyourproject,butinthischapter,wewillincludeitjustbyaddingtheJavaScriptVuefilefromtheCDN:

<scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/vue/2.0.3/vue.js">

</script>

So,let'sstartbyrenderingalistofelements.

CreatetheHTMLfileandaddthefollowingmarkup:

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

<h2>{{title}}</h2>

<ul>

<li>{{items[0]}}</li>

<li>{{items[1]}}</li>

</ul>

</div>

NowaddthefollowingJavaScriptcode:

vardata={

items:['Bananas','Apples'],

title:'MyShoppingList'

};

newVue({

el:'#app',

data:data

});

Openitinthebrowser.Youwillseethatthelistisrendered:

Page 42: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

ShoppinglistimplementedusingVue.js

Let'sanalyzethisexample.TheVueapplicationcodestartswiththenewVuekeyword.Howdowebindthepieceofmarkuptotheapplicationdata?WepasstotheVueinstancetheDOMelementthatmustbeboundtoit.AnyothermarkupinthepagewillnotbeaffectedandwillnotrecognizeVue'smagic.

Asyoucansee,ourmarkupiswrappedintothe#appelementandispassedasafirstargumentinthemapofVueoptions.Thedataargumentcontainstheobjectsthatarebeingusedinsidethemarkupusingdoublecurlybrackets({{}}).Youwillprobablyfindthisannotationveryeasytounderstandifyouarefamiliarwithtemplatingpreprocessors(forexample,handlebars;formoreinformation,visithttp://handlebarsjs.com/).

Sowhat?—you'reprobablyexclaiming.Whatareyougoingtoteachme?Howtousetemplatingpreprocessors?Thankyouverymuch,butIwouldbebetteroffhavingsomebeersandwatchingfootball.

Stop,dearreader,don'tgo,justgrabyourbeerandlet'scontinueourexample.You'llseethatit'llbelotsoffun!

Page 43: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

AnalyzingdatabindingusingdevelopertoolsLet'sseedatabindinginaction.Openyourbrowser'sdevelopertools,findyourJavaScriptcode,andaddabreakpointatthestartofthescript.NowanalyzehowthedataobjectslookbeforeandaftertheVueapplicationisinitialized.Yousee,itchangedalot.Nowthedataobjectispreparedtothereactivedatabinding:

ThedataobjectbeforeandaftertheVueobjectinitialization

Nowifwechangethetitlepropertyofthedataobjectfromthedevelopertoolsconsole(wecandoitbecauseourdataisaglobalobject),itwillbereflectedautomaticallyinthetitleonthepage:

Page 44: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Databinding:changingobjectpropertiesaffectstheViewimmediately

Page 45: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Bringinguserinputtothedatawithtwo-waybindingSo,inourexample,wewereabletobringthedatafromtheplainJavaScriptdatamodeltothepage.Weprovideditasortofaflightfromtheapplicationcodetothepage.Don'tyouthinkthatitwouldbeniceifwecouldofferatwo-wayflighttoourdata?

Let'sseenowhowwecanachievetwo-waydatabindingandhowwecanchangethevalueofadatapropertyfromthepage.

CopytheHTMLmarkupforthetitle,changetheinputfromthefirstjQueryexample,andaddtheattributev-model="title"totheinputelement.

Tip

HaveyoualreadyheardaboutdirectivesinVue.js?Congratulations,you'vejustusedone!Actually,thev-modelattributeisadirectiveofVue.jsthatprovidestwo-waydatabinding.YoucanreadmoreaboutitattheofficialVuepage:http://vuejs.org/api/#v-model.

Now,theHTMLcodeforourshoppinglistapplicationcodelookslikethefollowing:

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

<h2>{{title}}</h2>

<ul>

<li>{{items[0]}}</li>

<li>{{items[1]}}</li>

</ul>

<divclass="footer">

<hr/>

<em>Changethetitleofyourshoppinglisthere</em>

<inputv-model="title"/>

</div>

</div>

Andthat'sit!

Refreshthepagenowandmodifytheinput.You'llseethetitleautomaticallybeingupdatedasyoutype:

Page 46: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Databinding:changingthetextboundtothemodel'spropertyaffectsthetextboundtothesamepropertyimmediately

So,everythingisnice;however,thisexamplejustgrabsthetwoitemelementsandrendersthemaslistitems.Wewantittorenderthelistofitemsindependentlyofthelistsize.

Page 47: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Renderingthelistofitemsusingthev-fordirectiveSo,weneedsomemechanismtoiteratethroughtheitemsarrayandtorendereachiteminour<ul>element.

Fortunately,Vue.jsprovidesuswithanicedirectiveforiteratingthroughiterativeJavaScriptdatastructures.Itiscalledv-for.Wewilluseitinthelistitem<li>element.Modifythemarkupofthelistsothatitlookslikethefollowing:

<ul>

<liv-for="iteminitems">{{item}}</li>

</ul>

Note

Youwilllearnothernicedirectivessuchasv-if,v-else,v-show,v-on,v-bind,andsooninthisbook,sokeepreading.

Refreshthepageandhavealook.Thepageremainsthesame.Now,trytopushanitemintothearrayofitemsfromthedevelopertoolsconsole.Trytopopthemaswell.Youwillnotbesurprisedtoseethattheitemsarraymanipulationsareimmediatelyreflectedonthepage:

Page 48: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Databinding:changinganarrayaffectslistsbasedonitimmediately

So,nowwehavealistofitemsthatisrenderedonapagewithjustonelineofthemarkup.However,westillneedtheseitemstohaveacheckboxthatallowsustocheckthealreadyboughtitemsoruncheckthemwhenneeded.

Page 49: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

CheckanduncheckshoppinglistitemsToachievethisbehavior,let'sslightlymodifyouritemsarraybychangingourstringitemsandtransformingthemintotheobjectswithtwoproperties,textandchecked(toreflectthestate),andlet'smodifythemarkuptoaddacheckboxtoeachitem.

SoourJavaScriptcodeforthedatadeclarationwilllooklikethefollowing:

vardata={

items:[{text:'Bananas',checked:true},

{text:'Apples',checked:false}],

title:'MyShoppingList',

newItem:''

};

Andourlistmarkupwilllooklikethis:

<ul>

<liv-for="iteminitems"v-bind:class="{'removed':

item.checked}">

<divclass="checkbox">

<label>

<inputtype="checkbox"v-model="item.checked">{{

item.text}}

</label>

</div>

</li>

</ul>

Refreshthepageandcheckthatthecheckedpropertyoftheitemscheckbox,andtheremovedclassofeachlistitem,<li>,isboundtothecheckedBooleanstateoftheitems.Playaroundandtrytoclickcheckboxestoseewhathappens.Isn'titnicethatjustwithtwodirectivesweareabletopropagatethestateoftheitemsandchangetheclassofthecorresponding<li>HTMLelement?

Page 50: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Addingnewshoppinglistitemsusingthev-ondirectiveSonowwejustneedasmalladditiontoourcodetobeabletoactuallyaddshoppinglistitems.Toachievethat,wewilladdonemoreobjecttoourdataandcallitnewItem.We'llalsoaddasmallmethodthatpushesnewitemtotheitemsarray.Andwe'llcallthismethodfromthemarkuppageusingthev:ondirectiveusedontheHTMLinputelementthatwillbeusedforthenewitemandonthebuttonusedtoclicktoaddanewitem.

SoourJavaScriptcodewilllooklikethefollowing:

vardata={

items:[{text:'Bananas',checked:true},

{text:'Apples',checked:false}],

title:'MyShoppingList',

newItem:''

};

newVue({

el:'#app',

data:data,

methods:{

addItem:function(){

vartext;

text=this.newItem.trim();

if(text){

this.items.push({

text:text,

checked:false

});

this.newItem='';

}

}

}

});

WeaddedanewpropertytothedataobjectcallednewItem.ThenweaddedanewsectioncalledmethodstoourVueinitializationoptionsobjectandaddedtheaddItemmethodtothissection.Allthedatapropertiesareaccessibleinthemethodssectionviathethiskeyword.Thus,inthismethod,wejustgetthis.newItemandpushitintothethis.itemsarray.Nowwemustbindthecalltothismethodtosomeuseraction.Asithasalreadybeenmentioned,wewillusethev-ondirectiveandapplyittotheenterkeyuponthenewiteminputandtotheAdd!buttonclick.

Soaddthefollowingmarkupbeforeourlistofitems:

<divclass="input-group">

<inputv-model="newItem"v-on:keyup.enter="addItem"

placeholder="addshoppinglistitem"type="text"class="form-

control">

<spanclass="input-group-btn">

<buttonv-on:click="addItem"class="btnbtn-default"

Page 51: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

type="button">Add!</button>

</span>

</div>

Note

Thev-ondirectiveattachesaneventlistenertotheelements.Theshortcutisthe@sign.So,insteadofwritingv-on:keyup="addItem",youcanwrite@keyup="addItem".Youcanreadmoreaboutthev-ondirectiveontheofficialdocumentationsiteathttp://vuejs.org/api/#v-on.

Let'sfinalize.Thewholecodenowlookslikethefollowing:

HereistheHTMLcode:

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

<h2>{{title}}</h2>

<divclass="input-group">

<inputv-model="newItem"@keyup.enter="addItem"

placeholder="addshoppinglistitem"type="text"

class="form-control">

<spanclass="input-group-btn">

<button@click="addItem"class="btnbtn-default"

type="button">Add!</button>

</span>

</div>

<ul>

<liv-for="iteminitems":class="{'removed':item.checked

}">

<divclass="checkbox">

<label>

<inputtype="checkbox"v-model="item.checked">{{

item.text}}

</label>

</div>

</li>

</ul>

<divclass="footerhidden">

<hr/>

<em>Changethetitleofyourshoppinglisthere</em>

<inputv-model="title"/>

</div>

</div>

HereistheJavaScriptcode:

vardata={

items:[{text:'Bananas',checked:true},

{text:'Apples',checked:false}],

title:'MyShoppingList',

newItem:''

};

newVue({

el:'#app',

Page 52: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

data:data,

methods:{

addItem:function(){

vartext;

text=this.newItem.trim();

if(text){

this.items.push({

text:text,

checked:false

});

this.newItem='';

}

}

}

});

Here'salinktoJSFiddle:https://jsfiddle.net/chudaol/vxfkxjzk/3/.

Page 53: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

UsingVue.jsinanexistingprojectIcanfeelnowthatyouhaveseenhoweasyistobindthepropertiesofthemodeltothepresentationlayerandyouarealreadystartingtothinkabouthowitcanbeusedinyourexistingproject.Butthenyouthink:hell,no,Ineedtoinstallsomethings,runnpminstall,changetheproject'sstructure,adddirectives,andchangethecode.

AndhereIcantellyou:no!Noinstalls,nonpms,justgrabthevue.jsfile,insertitintoyourHTMLpage,anduseit.That'sall,nostructurechanges,noarchitecturaldecisions,nodiscussions.Justuseit.IwillshowyouhowweuseditatEdEra(https://www.ed-era.com)toincludeasmall"checkyourself"functionalityattheendofaGitBookchapter.

EdEraisaUkraine-basedonlineeducationalprojectwhoseaimistotransformthewholeeducationalsystemintosomethingmodern,online,interactive,andfun.Actually,Iamaco-founderandthechieftechnicalofficerofthisyoungniceproject,beingresponsibleforthewholetechnicalpartofthething.So,inEdEra,wehavesomeonlinecoursesbuiltontopoftheopenEdXplatform(https://open.edx.org/)andsomeinteractiveeducationalbooksbuiltontopofthegreatGitBookframework(http://www.gitbook.org).Basically,GitBookisaplatformbasedontopoftheNode.jstechnologystack.ItallowssomeonewithbasicknowledgeofthemarkdownlanguageandbasicGitcommandstowritebooksandhostthemintheGitBookservers.EdEra'sbookscanbefoundathttp://ed-era.com/books(beware,theyareallinUkrainian).

Let'sseewhatwehavedoneinourbooksusingVue.js.

Atsomepoint,IdecidedtoincludeasmallquizattheendofthechapteraboutpersonalpronounsinthebookthatteachesEnglish.Thus,I'veincludedthevue.jsJavaScriptfile,editedthecorresponding.mdfile,andincludedthefollowingHTMLcode:

<divid="pronouns">

<p><strong>Checkyourself:)</strong></p>

<textareaclass="textarea"v-model="text"v-

on:keyup="checkText">

{{text}}

</textarea><iv-bind:class="{'correct':correct,

'incorrect':!correct}"></i>

</div>

ThenIaddedacustomJavaScriptfile,whereI'veincludedthefollowingcode:

$(document).ready(function(){

varinitialText,correctText;

initialText='MeissadbecauseheismorecleverthanI.';

correctText='Iamsadbecauseheismorecleverthanme.';

newVue({

el:'#pronouns',

Page 54: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

data:{

text:initialText,

correct:false

},

methods:{

checkText:function(){

vartext;

text=this.text.trim();

this.correct=text===correctText;

}

}

});

});

Note

YoucancheckthiscodeatthisGitHubpage:https://github.com/chudaol/ed-era-book-english.Here'sacodeofapagewritteninmarkdownwithinsertedHTML:https://github.com/chudaol/ed-era-book-english/blob/master/2/osobovi_zaimenniki.md.Andhere'saJavaScriptcode:https://github.com/chudaol/ed-era-book-english/blob/master/custom/js/quiz-vue.js.Youcanevenclonetherepositoryandtryitlocallyusinggitbook-cli(https://github.com/GitbookIO/gitbook/blob/master/docs/setup.md).

Let'shavealookatthiscode.Youhaveprobablyalreadydetectedthepartsthatyouhavealreadyseenandeventried:

Thedataobjectcontainstwoproperties:ThestringpropertytextTheBooleanpropertycorrect

ThecheckTextmethodjustgrabsthetextproperty,comparesitwiththecorrecttext,andassignsthevaluetothecorrectvalueThev-ondirectivecallsthecheckTextmethodonkeyupThev-binddirectivebindstheclasscorrecttothecorrectproperty

HereishowthecodelooksinmyIDE:

Page 55: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

UsingVueinamarkdown-drivenproject

Andnextiswhatitlookslikeinthebrowser:

Vue.jsinactioninsidetheGitBookpage

Page 56: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Vue.jsinactioninsidetheGitBookpage

Checkitoutathttp://english.ed-era.com/2/osobovi_zaimenniki.html.

Amazing,right?Prettysimple,prettyreactive!

Page 57: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Vue.js2.0!Atthetimeofwriting,Vue.js2.0wasannounced(https://vuejs.org/2016/04/27/announcing-2.0/).Checkthefollowinglinksinthisregard:

http://www.infoworld.com/article/3063615/javascript/vuejs-lead-our-javascript-framework-is-faster-than-react.htmlhttps://www.reddit.com/r/vuejs/comments/4gq2r1/announcing_vuejs_20/

ThesecondversionofVue.jshassomeconsiderabledifferencescomparingtoitspredecessor,startingfromthewaythatdatabindingisbeinghandledandmovingtoitsAPI.ItuseslightweightvirtualDOMimplementationforrendering,supportsserver-siderendering,andisfasterandleaner.

Atthetimeofwriting,Vue2.0wasinanearlyalphastage.Donotworry,though.AlltheexamplesthatwewillcoverinthisbookarebasedonthelateststableversionofVue2.0andarefullycompatiblewiththeboththeversions.

Page 58: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

ProjectsusingVue.jsProbably,atthistime,youarewonderingwhatprojectsareouttherethatarebuiltontopofVue.js,oruseitasapartoftheircodebase.Therearealotofniceopensource,experimental,andenterpriseprojectsusingit.Thecompleteandconstantlyupdatedlistoftheseprojectscanbefoundathttps://github.com/vuejs/awesome-vue#projects-using-vuejs.

Let'shavealookatsomeofthem.

Page 59: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

GrammarlyGrammarly(https://www.grammarly.com/)isaservicethathelpsyouwriteEnglishcorrectly.Ithasseveralapps,oneofthemisasimpleChromeextensionthatjustchecksanytextinputthatyoufillin.Anotheroneisanonlineeditorthatyoucanusetocheckbigchunksoftext.ThiseditorisbuiltusingVue.js!ThefollowingisascreenshotofthistextbeingeditedintheonlineeditorofGrammarly:

Grammarly:aprojectthatisbuiltontopofVue.js

Page 60: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

OptimizelyOptimizely(https://www.optimizely.com/)isaservicethathelpsyoutest,optimize,andpersonalizeyourwebsites.I'veusedthePacktsitetocreateanOptimizelyexperimentandtocheckoutVue.jsinactioninthisresource.Itlookslikethefollowing:

Optimizely:aprojectthatisbuiltontopofVue.js

Hoveringaroundwiththemousegivesusthepossibilityofopeningacontextmenuthatallowsdifferentmanipulationswiththepagedata,includingthesimplestone,textediting.Let'strythisone:

Page 61: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

UsingOptimizelyandwatchingVue.jsinaction

Thetextboxisopened.WhenItypeinit,thetextinthetitleisreactivelychanged.WesawandimplementeditusingVue.js:

UsingOptimizelyandwatchingVue.jsinaction

Page 62: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

FilterBlendFilterBlend(https://github.com/ilyashubin/FilterBlend)isanopensourceplaygroundfortheCSSbackground-blend-modeandfilterproperties.

Youcanloadyourimagesandcombineblendingwithfilters.

IfyouwanttogiveFilterBlendatry,youcaninstallitlocally:

1. Clonetherepository:

gitclonehttps://github.com/ilyashubin/FilterBlend.git

2. EntertheFilterBlenddirectory:

cdFilterBlend

3. Installthedependencies:

npminstall

4. Runtheproject:

gulp

Openyourbrowseronlocalhost:8000andplayaround.Youcanseethatonceyouchangesomethinginthemenuontheright,itisimmediatelypropagatedtotheimagesontheleftside.AllthisfunctionalityisachievedusingVue.js.CheckthecodeonGitHub.

Page 63: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

FilterBlend:aprojectbuiltontopofVue.js

Page 64: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

PushSilverPushSilver(https://pushsilver.com)isaniceandsimpleserviceforbusypeopletocreatesimpleinvoices.Itallowscreatinginvoices,sendingandresendingthemtotheclients,andkeepingtrackingofthem.Itwascreatedbyadeveloperdoingfreelanceconsultancyandbeingtiredofhavingtocreateinvoiceseachtimeforeachsmallproject.ThistoolworkswellanditwasbuiltusingVue.js:

PushSilver:invoicemanagingapplicationbuiltontopofVue.js

Page 65: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

PushSilver:invoicemanagingapplicationbuiltontopofVue.js

Page 66: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

BookroadmapThisbook,likemostpartoftechnicalbooks,isorganizedinsuchawaythatyoudonotneedtoreaditfrombeginningtoend.Youcanchoosethepartsthatinterestyouthemostandskiptherest.

Thisbookisorganizedasfollows:

Ifyouarereadingthis,there'snoneedtospecifywhatisgoingoninthefirstchapter.Chapter2,Fundamentals-InstallingandUsing,isprettytheoreticalandwillexplainwhat'sgoingonbehindthescenesofVue.jsanditsmainparts.So,ifyouarenotintotheoryandwanttoputyourhandsintocoding,youarefreetoskipthispart.Inthispart,wewillalsogothroughtheinstallationandsetupprocess.Fromthethirdtotheeighthchapter,we'llexplorethemainfeaturesofVue.jswhilebuildingtheapplication:

InChapter3,Components-UnderstandingandUsing,wewillintroduceVuecomponentsandapplythisknowledgetoourapplication.InChapter4,Reactivity-BindingDatatoYourApplication,wewilluseallthedatabindingmechanismsprovidedbyVue.InChapter5,Vuex-ManagingStateinYourApplication,wewillintroducetheVuexstatemanagementsystemandexplainhowtouseitinourapplications.InChapter6,Plugins-BuildingYourHousewithYourOwnBricks,wewilllearnhowtocreateandusepluginsforVueapplicationstoenrichtheirfunctionality.InChapter7,Testing-TimetoTestWhatWeHaveDonesoFar!,wewillcoverandexplorecustomdirectivesofVue.jsandcreatesomeinourapplication.InChapter8,Deploying-TimetoGoLive!,wewilllearnhowtotestanddeployJavaScriptapplicationwritteninVue.js.

InChapter9,WhatIsNext?,we'llsummarizewhatwe'velearnedandseewhatwecandonext.

Page 67: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Let'smanagetime!Atthispointoftime,Ialreadyknowthatyouareso,so,soenthusiasticwiththisbookthatyouwanttoreadittotheendwithoutstopping.Butthisisnotright.Weshouldmanageourtimeandgiveussometimetoworkandsometimetorest.Let'screateasmallapplicationthatimplementsaPomodorotechniquetimersothatitcanhelpusinourworkingtimemanagement.

Note

ThePomodorotechniqueisatimemanagementtechniquenamedafterthekitchentomatotimer(infact,PomodoromeanstomatoinItalian).Thistechniqueconsistsofbreakingdowntheworkingtimeintosmallintervalsseparatedbyshortbreaks.ReadmoreaboutthePomodorotechniqueontheofficialsite:http://pomodorotechnique.com/.

Thus,ourgoalisverysimple.Wejusthavetocreateaverysimpletimecounterthatwilldecrementuntilltheendoftheworkingintervalandthenrestartanddecrementtilltheendoftherestingtimeandsoon.

Let'sdothat!

WewillintroducetwoVuedatavariables,minuteandsecond,whichwillbedisplayedonourpage.Themainmethodoneachsecondwilldecrementsecond;itwilldecrementminutewhensecondbecomes0;andwhenbothminuteandsecondvariablescometo0,theapplicationshouldtogglebetweenworkingandrestinginterval:

OurJavaScriptcodewilllooklikethefollowing:

constPOMODORO_STATES={

WORK:'work',

REST:'rest'

};

constWORKING_TIME_LENGTH_IN_MINUTES=25;

constRESTING_TIME_LENGTH_IN_MINUTES=5;

newVue({

el:'#app',

data:{

minute:WORKING_TIME_LENGTH_IN_MINUTES,

second:0,

pomodoroState:POMODORO_STATES.WORK,

timestamp:0

},

methods:{

start:function(){

this._tick();

this.interval=setInterval(this._tick,1000);

},

_tick:function(){

Page 68: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

//ifsecondisnot0,justdecrementsecond

if(this.second!==0){

this.second--;

return;

}

//ifsecondis0andminuteisnot0,

//decrementminuteandsetsecondto59

if(this.minute!==0){

this.minute--;

this.second=59;

return;

}

//ifsecondis0andminuteis0,

//toggleworking/restingintervals

this.pomodoroState=this.pomodoroState===

POMODORO_STATES.WORK?POMODORO_STATES.REST:

POMODORO_STATES.WORK;

if(this.pomodoroState===POMODORO_STATES.WORK){

this.minute=WORKING_TIME_LENGTH_IN_MINUTES;

}else{

this.minute=RESTING_TIME_LENGTH_IN_MINUTES;

}

}

}

});

InourHTMLcode,let'screatetwoplaceholdersforminuteandsecond,andastartbuttonforourPomodorotimer:

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

<h2>

<span>Pomodoro</span>

<button@click="start()">

<iclass="glyphiconglyphicon-play"></i>

</button>

</h2>

<divclass="well">

<divclass="pomodoro-timer">

<span>{{minute}}</span>:<span>{{second}}</span>

</div>

</div>

</div>

Again,weareusingBootstrapforthestyling,soourPomodorotimerlookslikethefollowing:

Page 69: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

CountdowntimerbuiltwithVue.js

OurPomodoroisnice,butithassomeproblems:

Firstofall,wedon'tknowwhichstateisbeingtoggled.Wedon'tknowifweshouldworkorrest.Let'sintroduceatitlethatwillchangeeachtimethePomodorostateischanged.Anotherproblemisinconsistentdisplayofminutesandsecondsnumbers.Forexample,for24minutesand5seconds,wewouldliketosee24:05andnot24:5.Let'sfixitintroducingcomputedvaluesinourapplicationdataanddisplayingtheminsteadofnormalvalues.Yetanotherproblemisthatourstartbuttoncanbeclickedoverandoveragain,whichcreatesatimereachtimeit'sclicked.Trytoclickitseveraltimesandseehowcrazyyourtimergoes.Let'sfixitbyintroducingstart,pause,andstopbuttons,applyapplicationstatestothem,anddisablebuttonstothestateaccordingly.

Page 70: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

TogglethetitlebyusingcomputedpropertiesLet'sstartbyfixingthefirstproblembycreatingcomputedpropertytitleandusingitinourmarkup.

Note

Computedpropertiesarethepropertiesinsidethedataobjectthatallowustoavoidblowingupthetemplatewithsomeextralogic.Youcanfindmoreinformationaboutcomputedpropertiesontheofficialdocumentationsite:http://vuejs.org/guide/computed.html.

AddthecomputedsectionintheVueoptionsobjectandaddthepropertytitlethere:

data:{

//...

},

computed:{

title:function(){

returnthis.pomodoroState===POMODORO_STATES.WORK?'Work!':

'Rest!'

}

},

methods:{

//...

AndnowjustusethefollowingpropertyasitwasanormalVuedatapropertyinyourmarkup:

<h2>

<span>Pomodoro</span>

<!--!>

</h2>

<h3>{{title}}</h3>

<divclass="well">

Andvoilà!NowwehaveatitlethatchangeseachtimethePomodorostateisbeingtoggled:

Page 71: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Automaticchangeofthetitlebasedonthestateofthetimer

Nice,isn'tit?

Page 72: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Left-padtimevaluesusingcomputedpropertiesNowlet'sapplythesamelogicforleftpaddingourminuteandsecondnumbers.Let'saddtwocomputedproperties,minandsec,inourcomputedsectioninthedataoptionsandapplythesimplealgorithmtopadthenumberswith0ontheleft.Ofcourse,wecoulduseafamousleft-padproject(https://github.com/stevemao/left-pad),buttokeepthingssimpleandnottobreakthewholeInternet(http://www.theregister.co.uk/2016/03/23/npm_left_pad_chaos/),let'sapplyasimplelogicofourown:

computed:{

title:function(){

returnthis.pomodoroState===POMODORO_STATES.WORK?'Work!':

'Rest!'

},

min:function(){

if(this.minute<10){

return'0'+this.minute;

}

returnthis.minute;

},

sec:function(){

if(this.second<10){

return'0'+this.second;

}

returnthis.second;

}

}

Andlet'susethesepropertiesinsteadofminuteandsecondinourHTMLcode:

<divclass="pomodoro-timer">

<span>{{min}}</span>:<span>{{sec}}</span>

</div>

Refreshapageandcheckhowbeautifulournumbersarenow:

Page 73: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

LeftpaddingusingcomputedpropertiesinVue.js

Page 74: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Keepstatewithstart,pause,andstopbuttonsSo,tofixthethirdproblem,let'sintroducethreeapplicationstates,started,paused,andstopped,andlet'shavethreemethodsthatwouldallowustopermuteoverthesestates.Wealreadyhavethemethodthatstartstheapplication,sowejustaddthelogictheretochangethestatetostarted.Wealsoaddtwoadditionalmethods,pauseandstop,whichwouldpausethetimerandchangetothecorrespondingapplicationstate:

constPOMODORO_STATES={

WORK:'work',

REST:'rest'

};

constSTATES={

STARTED:'started',

STOPPED:'stopped',

PAUSED:'paused'

};

//<...>

newVue({

el:'#app',

data:{

state:STATES.STOPPED,

//<...>

},

//<...>

methods:{

start:function(){

this.state=STATES.STARTED;

this._tick();

this.interval=setInterval(this._tick,1000);

},

pause:function(){

this.state=STATES.PAUSED;

clearInterval(this.interval);

},

stop:function(){

this.state=STATES.STOPPED;

clearInterval(this.interval);

this.pomodoroState=POMODORO_STATES.WORK;

this.minute=WORKING_TIME_LENGTH_IN_MINUTES;

this.second=0;

},

//<...>

}

});

And,let'saddtwobuttonstoourHTMLcodeandaddtheclicklistenersthatcallthecorrespondingmethods:

<button:disabled="state==='started'"

@click="start()">

<iclass="glyphiconglyphicon-play"></i>

</button>

<button:disabled="state!=='started'"

Page 75: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

@click="pause()">

<iclass="glyphiconglyphicon-pause"></i>

</button>

<button:disabled="state!=='started'&&state!=='paused'"

@click="stop()">

<iclass="glyphiconglyphicon-stop"></i>

</button>

Nowourapplicationlooksniceandallowsustostart,pause,andstopthetimer:

Togglingstart,stop,andpausebuttonsaccordingtotheapplicationstate

CheckwhatthewholecodelookslikeinJSFiddleathttps://jsfiddle.net/chudaol/b6vmtzq1/1/.

Aftersomuchworkandsomanyofnewtermsandknowledge,youcertainlydeserveakitten!Ialsolovekittens,sohereyouhavearandomkittenfromtheawesomesitehttp://thecatapi.com/:

Page 76: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga
Page 77: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

ExerciseAttheendofthischapter,Iwouldliketoproposeasmallexercise.ThePomodorotimerthatwebuiltearlierinthechaptersis,withoutanydoubt,great,butitstilllackssomenicefeatures.Areallynicethingthatitcouldprovidewouldbeshowingrandomkittensfromhttp://thecatapi.com/duringrestingtime.Canyouimplementthis?Ofcourseyoucan!Butpleasedonotconfuserestingwithworkingtime!Iamalmostsurethatyourprojectmanagerwillnotlikeitmuchifyoustareatkittensinsteadofworking.

ThesolutiontothisexercisecanbefoundinAppendix,SolutionstoExercises.

Page 78: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

SummaryIamverygladthatyouhavereachedthispoint,thismeansthatyoualreadyknowwhatVue.jsis,andifsomeoneasksyouwhetheritisatool,alibrary,oraframework,youcertainlywillfindananswer.YoualsoknowhowtostartanapplicationusingVue.jsandyouknowhowtouseVue'sfeaturesinanalreadyexistingproject.YouplayedaroundwithsomereallyniceprojectsthatarewritteninVue.jsandyoustarteddevelopingsomeofyourown!Nowyoudonotjustgoshopping,nowyougoshoppingwithashoppinglistcreatedbyyouusingVue.js!Nowyoudon'tneedtostealyourtomatotimerfromthekitchentouseitasaPomodorotimerwhileworking;youcanuseyourowndigitalPomodorotimermadewithVue.js.And,lastbutnottheleast,nowyoucaninsertrandomkittensinyourJavaScriptapplicationalsousingVue.js.

Inthenextchapter,wewillcoverthebehindthescenesofVue,howandwhydoesitwork,andthearchitecturalpatternsituses.Eachoftheconceptswillbewrappedupwithanexampletodemonstrateit.Thenwewillbereadytoputourhandsdeepintothecodeandtoimproveourapplicationstakingthemtothestateofawesomeness.

Page 79: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Chapter2.Fundamentals–InstallingandUsingInthepreviouschapter,wegainedsomefamiliaritywithVue.js.Wewereabletouseitintwodifferentapplicationsthatwecreatedfromscratch.WelearnedhowtointegrateVue.jsintoanalreadyexistingproject.WewereabletoseeVue'sreactivedatabindinginaction.

Now,youareprobablyaskingyourself:howdoesitwork?WhatdoesitdotoachievethisbehavioroffastUIchangeswhenthedatamodelischanged?Probably,youdecidedtouseVue.jsinyourprojectandarenowwonderingwhetheritfollowssomearchitecturalpatternorparadigmsothatyoushouldadoptitinyourproject.Inthischapter,wewillexplorethekeyconceptsoftheVue.jsframeworktounderstandallitsbehindthescenesfeatures.Alsointhischapter,wewillanalyzeallthepossiblewaysofinstallingVue.js.Wewillalsocreateaskeletonforourapplications,whichwewilldevelopandenhancethroughthenextchapters.Wewillalsolearnwaysofdebuggingandtestingourapplications.

So,inthischapter,wearegoingtolearn:

WhattheMVVMarchitectureparadigmisandhowitappliestoVue.jsWhatdeclarativeViewsareHowVue.jsexploresdefinedproperties,getters,andsettersHowreactivityanddatabindingworksinVue.jsWhatdirtychecking,DOM,andvirtualDOMareThemaindifferencesbetweenVue.js1.0andVue.js2.0WhatreusablecomponentsareHowplugins,directives,customplugins,andcustomdirectivesworkinVue.jsHowtoinstall,start,run,anddebugaVueapplication

Page 80: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

MVVMarchitecturalpatternDoyourememberhowwewerecreatingtheVueinstanceinthefirstchapter?WewereinstantiatingitcallingnewVue({...}).Youalsorememberthatintheoptions,wewerepassingtheelementonthepagewherethisVueinstanceshouldbeboundandthedataobjectthatcontainedthepropertieswewantedtobindtoourView.ThedataobjectisourModelandtheDOMelementwheretheVueinstanceisboundisourView:

ClassicView-ModelrepresentationwheretheVueinstancebindsonetoanother

Inthemeantime,ourVueinstanceissomethingthathelpstobindourModeltotheViewandviceversa.OurapplicationthusfollowsModel-View-ViewModel(MVVM)pattern,wheretheVueinstanceisaViewModel:

ThesimplifieddiagramoftheModel-View-ViewModelpattern

OurModelcontainsdataandsomebusinesslogic,andourViewisresponsibleforitsrepresentation.ViewModelhandlesdatabinding,ensuringthatthedatachangedintheModelisimmediatelyaffectingtheViewlayerandviceversa.

OurViewsthusbecomecompletelydatadriven.ViewModelbecomesresponsibleforthecontrolofthedataflow,makingdatabindingfullydeclarativeforus.

Page 81: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

DefineProperty,getters,andsettersSo,whathappenswiththedataoncepassedtotheVueinstance?WhatarethesetransformationsthatVueappliestoitsothatitbecomessoautomaticallyboundtotheViewlayer?

Let'sanalyzewhatwouldwedoifwehad,let'ssay,astring,andeverytimeitchangeswewouldliketoapplysometransformationstosomeDOMelement.Howwouldweapplythestring-changinglistenerfunction?Towhatwouldweattachit?ThereisnosuchthingasvarstringVar='hello';stringVar.onChange(doSomething).

Sowewouldprobablywrapthestring'svaluesettingandgettinginsomesortoffunctionthatwoulddosomething,forexample,updatingtheDOMeachtimethestringwasupdated.Howwouldyouimplementit?Whileyou'rethinkingaboutit,I'llprepareaquickdemoofsomethinginteresting.

Openthedevelopertoolsonyourshoppinglistapplication.Let'scodealittlebit.Createanobjvariableandanothertextvariable:

varobj={};

vartext='';

Let'sstoretheDOMelementh2inavariable:

varh2=document.getElementsByTagName('h2')[0];

Ifweassigntexttotheobj.textproperty,howcanweachievethatineverychangeofthispropertytheinnerHTMLofh2wouldchangeaswell?

Let'susetheObject.definePropertymethod(https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty).

Thismethodallowsthecreationofgetterandsetterfunctions,thusspecifyingwhatmusthappenwhenthepropertyisaccessedorchanged:

Object.defineProperty(obj,'text',{

get:function(){

returntext;

},

set:function(newVal){

text=newVal;

h2.innerHTML=text;

}

});

Nowtrytochangetheobj.textpropertyfromtheconsole.Lookatthetitle:

Page 82: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Thesetmethodofobject.definePropertyiscalledeverytimethepropertychanges

ThisexactmechanismwasusedbyVue.js.OncethedataarepassedtotheVueinstance,allitspropertiesgothroughtheObject.definePropertymethod,whichassignsreactivegettersandsetterstothem.Foreachdirectiveexistingonapage,awatcherisadded,whichisbeingnotifiedwithinthesetmethod.Openthevue.jscodeintheconsoleandsearchforthelinethatsaysset:functionreactiveSetter(newVal).Addabreakpointandtrytochangethetitleoftheshoppinglistintheinput.Nowstepoveruntilyoureachthelastcallinthisfunctionthatsaysdep.notify():

Page 83: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Breakpointinsidethesetterfunctionthatcallsthewatchersnotifymethod

Stepintothefunction.Youwillseethatthisfunctionisiteratingthroughthewatchersofthepropertyandupdatesthem.Ifyoustepoverthiscall,youwillseethattheDOMisnotbeingupdated.Thisisbecausetheupdatesperformedonthesameeventlooparebeingputintothequeuethatisbeingflushedperiodically.

FindtherunBatcherQueuefunctionandputabreakpointinsideit.Trytochangethetitleagain.Asyoucansee,thisfunctioniteratesthroughallthewatchersthatarewaitinginsidethequeueandcallstherunmethodoneachofthem.Ifyoustepintothismethod,youwillseethatitcomparesthenewvaluewiththepreviousone:

if(value!==this.value||...

Itthenitcallsacallback'sexecution:

this.cb.call(this.vm,value,oldValue);

Ifyoustepintothiscallbackfunction,youwillseethatintheend,itwillfinallyupdatethe

Page 84: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

DOMvalue:

update:functionupdate(value){

this.el[this.attr]=_toString(value);

}

Isn'titsimple?

Note

InthisdebuggingVueversion1.0isused.

SothemechanismbehindtheVue.jsreactivedatabindingisverysimple.Watchersarebeingassignedtoallthedirectivesanddataproperties.Then,duringthesetmethodofObject.defineProperty,theWatchersarenotifiedand,inturn,theyupdatethecorrespondingDOMordata:

ThedataflowfromthedataobjecttotheDOM

DOMelementsthathavedirectiveshaveattachedlistenersthatlistentotheirupdatesandcallthecorrespondingdatapropertysetterthat,inturn,wakesupitsWatchers.

Page 85: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

ComparingwithotherframeworksWhenyoutryanewtool,youwanttoknowhowitcompareswithothertoolsorframeworks.YoucanfindadeepanalysisinthisregardontheofficialpageofVue.js:http://vuejs.org/guide/comparison.html.IwilljustpointtosometopicsthatIfindimportantregardingthemostlyusedframeworks.

Page 86: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

ReactReactandVuearereallysimilar.TheybothusevirtualDOM,havereusablecomponents,andareaboutreactivedata.Itisworthmentioning,however,thatVueonlyusesvirtualDOMstartingfromitssecondmajorversion.PriortoVue2.0,itusedrealDOM.TheVue2.0releasenotonlybecamemoreperformantthanVue1.0butitalsobecamemoreperformantthanReact(http://vuejs.org/guide/comparison.html#Performance-Profiles).

Themostsignificantdifferenceisprobablythewayyoucreateyourcomponentsinbothframeworks.YoumightalreadyknowthatinReact,everythingisJavaScript.Developingeverything,eventemplates,inJavaScript,canactuallybegood,soprogrammersarealwaysinthesamescopeandrenderingbecomesmoreflexible.

However,forsomedesignerswhowanttodorapidprototypingorfordeveloperswithnot-so-strongprogrammingskills,orforpeoplewhosimplydon'twanttolearnJSX,itmightbecomereallypainfultoworklikethis.InVuecomponents,youcanactuallyalsouseJSX,butyoucanstillfollowacommonwebdevelopmentstructure:writingCSSinsidethe<style>tags,writingHTMLcodeinsidethe<template>tags,andwritingthecomponent'slogicinJavaScriptinsidethe<script>tags.Compare,forexample,thetemplateinsidetherenderfunctioninReactandthetemplatethatyoucanwriteinsidetheVuecomponent.Inthisexample,Iwillshowhowtorenderthelistofitemsoftheshoppinglistthatwehaveseenbefore.SoinReact,youwillendupwiththeJSXcodesimilartothisone:

render(){

return(

<ul>

{items.map(item=>

<liclassName={item.checked&&'removed'}>

<divclassName='checkbox'>

<inputtype='checkbox'checked={item.checked}>

{item.text}

</div>

</li>

)}

</ul>

)

});

UsingVue,youwilljustwritethefollowingHTMLcodeinsidethetemplatetag:

<template>

<ul>

<liv-for="iteminitems":class="{'removed':item.checked}">

<divclass="checkbox">

<label>

<inputtype="checkbox"v-model="item.checked">{{item.text}}

</label>

</div>

</li>

</ul>

Page 87: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

</template>

I,personally,liketohavethesethingsseparated,thusIfinditnicethatVueoffersthispossibility.

AnothernicethingaboutVueisthatitallowstoscopestylewithinthecomponentsusingthescopedattributeattachedtothestyletag:

<stylescoped>

</style>

Withinthisstyle,incaseyouusepreprocessors,youstillhaveaccesstoallgloballydefinedvariablesandcancreateorredefinestylesthatwillbeonlyaccessiblebythiscomponent.

It'salsoworthtomentionthelearningcurveforbothframeworks.TobeabletostartdevelopingapplicationsusingReact,youwouldprobablyhavetolearnJSXandES2105syntax,sincemostexamplesinofficialReactdocumentationuseit.WithVue,youcanstartoutoftheblue.Justincludeitinthepage,likeyouwoulddowithjQuery,andyoucanalreadyuseVuemodelsanddatabindingusingprettysimpleandeasytounderstandsyntax,andanyJavaScriptversionyouliketouse.Afterthat,youcanscaleupinyourlearningandinyourapplicationsstyle.

Incaseyouwanttoperformadeeperanalysisofbothframeworks,havealookatthedocumentation,trytoelaboratesimilarexamples,andcheckwhatsuitsyourneedsmore.

Page 88: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

AngularThereisahugedifferencebetweenAngular1andAngular2.WeallknowthatthesecondversionofAngulariscompletelydifferentfromitspredecessor.Itoffersmoreperformance,theAPIisdifferent,andtheunderlyingimplementationhasbeenrewritten.

ThesetwoversionsaresodifferentthatinVueofficialdocumentation,youwillfindthecomparisonbetweenboththeAngularversionsasitwasbetweentwodifferentframeworks.However,thelearningcurveandthewayinwhicheachoftheframeworksforcesyoutostructuretheapplicationaretransversalforboththeAngularversions.ItturnsoutthatVueismuchlessopinionatedthanAngular1aswellasAngular2.JustcompareAngular'squickstartguideandVue'shelloworldapplicationsathttps://angular.io/docs/js/latest/quickstart.htmlandhttp://vuejs.org/guide/index.html#Hello-World.

"EvenwithoutTypeScript,Angular'sQuickstartguidestartsoutwithanappthatusesES2015JavaScript,NPMwith18dependencies,4files,andover3,000wordstoexplainitall-justtosayHelloWorld."

--http://vuejs.org/guide/comparison.html#Learning-Curve

IfyoustilluseAngular1,it'sworthtomentionthatthebigdifferencebetweenthisframeworkandVueisthatinthisversionofAngular,eachtimethescopechanged,re-evaluatedallthewatchers,thusperformingdirtychecking,hencereducingtheperformancewhentheamountofwatchersbecameconsiderablyhigh.Hence,inVue,whensomethinginthescopechanges,onlythisproperty'swatcherisbeingre-evaluated.Allothersaresittingidleandwaitingfortheirrespectivecalls.

Page 89: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

VueNo,itisnotatypo.ItisalsoworthcomparingVuewithVue.Vuehasalsorecentlylauncheditssecondversion,whichisfasterandcleanerthanitspredecessor.IfyoustilluseVue1.0,itisworthtoupgrade.Ifyoudon'tknowanythingaboutVueversions,itisworthtocheckhowitevolvedandwhatdoesthenewversionallow.ChecktheVueblogpostthatannouncedVue2.0inApril2016athttps://vuejs.org/2016/04/27/announcing-2.0/.

Page 90: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Vue.jsfundamentalsBeforeputtingourhandsintothecodeandstartingtoenhanceourapplicationswithcomponents,plugins,mixins,templates,andotherthings,let'soverviewthemainVuefeatures.Let'sanalyzewhatarereusablecomponentsandhowtheapplicationstatecanbemanaged,andalsotalkaboutplugins,filters,andmixins.Inthissection,wewillhavejustaslightoverviewofthesefeatures.Wewilllearnthemdeeplyinthenextchapters.

Page 91: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

ReusablecomponentsNowthatyouknownotonlywhatdatabindinginVue.jsisandhowtouseit,butalsohowitworks,itistimetointroduceanotherpowerfulVue.jsfeature.ComponentscreatedwithVue.jscanbeusedandreusedintheapplicationasbricksyoubuildyourhouseof.Eachcomponenthasitsownscopeofstylesandbindings,beingcompletelyisolatedfromtheothercomponents.

ThecomponentcreationsyntaxisverysimilartotheVueinstancecreationthatwealreadyknow,andyoushouldonlyuseVue.extendinsteadofjustVue:

varCustomComponent=Vue.extend({...})

CustomcomponentsinVue.js

Let's,forexample,trytodivideourshoppinglistcodeintocomponents.Asyouremember,ourshoppinglistconsistsessentiallyofthreeparts:thepartthatcontainstheshoppinglistitem,anotherpartthatcontainstheinputforaddingnewitems,andthethirdpartthatallowschangingthetitleoftheshoppinglist:

Page 92: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Threeessentialpartsoftheshoppinglistapplication

Let'schangethecodeoftheapplicationsothatitusesthreecomponents,oneforeachpart.

Ourcodewaslookinglikethefollowing:

vardata={

items:[{text:'Bananas',checked:true},

{text:'Apples',checked:false}],

title:'MyShoppingList',

newItem:''

};

newVue({

el:'#app',

data:data,

methods:{

addItem:function(){

vartext;

text=this.newItem.trim();

if(text){

this.items.push({

text:text,

checked:false

});

this.newItem='';

}

}

Page 93: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

}

});

Nowwewillcreatethreecomponents:ItemsComponent,ChangeTitleComponent,andAddItemComponent.Allofthemwillhavethedatapropertywiththedataobject.TheaddItemmethodwilljumpfromthemainVueinstancetoChangeTitleComponent.AllthenecessaryHTMLwillgofromourindex.htmlfiletoeachofthecomponents.Sointheend,ourmainscriptwilllooklikethefollowing:

vardata={

items:[{text:'Bananas',checked:true},

{text:'Apples',checked:false}],

title:'MyShoppingList',

newItem:''

};

/**

*Declaringcomponents

*/

varItemsComponent=Vue.extend({

data:function(){

returndata;

},

template:'<ul>'+

'<liv-for="iteminitems"

:class="{'removed':item.checked}">'+

'<divclass="checkbox">'+

'<label>'+

'<inputtype="checkbox"

v-model="item.checked">{{item.text}}'+

'</label>'+

'</div>'+

'</li>'+

'</ul>'

});

varChangeTitleComponent=Vue.extend({

data:function(){

returndata;

},

template:'<inputv-model="title"/>'

});

varAddItemComponent=Vue.extend({

data:function(){

returndata;

},

methods:{

addItem:function(){

vartext;

text=this.newItem.trim();

if(text){

this.items.push({

text:text,

checked:false

});

Page 94: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

this.newItem="";

}

}

},

template:

'<divclass="input-group">'+

'<inputv-model="newItem"@keyup.enter="addItem"

placeholder="addshoppinglistitem"type="text"

class="form-control">'+

'<spanclass="input-group-btn">'+

'<button@click="addItem"class="btnbtn-default"

type="button">Add!</button>'+

'</span>'+

'</div>'

});

/**

*Registeringcomponents

*/

Vue.component('items-component',ItemsComponent);

Vue.component('change-title-component',ChangeTitleComponent);

Vue.component('add-item-component',AddItemComponent);

/**

*InstantiatingaVueinstance

*/

newVue({

el:'#app',

data:data

});

HowdoweusethesecomponentsinsidetheView?Weshouldjustreplacethecorrespondingmarkupwiththetagoftheregisteredcomponent.Ourmarkuplookedlikethefollowing:

Page 95: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Theshoppinglistapplicationmarkupwithdefinedcomponents

So,thefirsthighlightedareawewillreplacewiththe<add-item-component></add-item-component>tag,thesecondonewiththe<items-component></items-component>tag,andthethirdonewiththe<change-title-component></change-title-component>tag.Thus,ourpreviouslyhugemarkupnowlookslikethefollowing:

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

<h2>{{title}}</h2>

<add-item-component></add-item-component>

<items-component></items-component>

<divclass="footer">

<hr/>

<em>Changethetitleofyourshoppinglisthere</em>

<change-title-component></change-title-component>

</div>

</div>

Wewillgodeeplyintocomponentsinthenextchapterandwilllearnanevennicerwayofstructuringthem.Staytuned!

Page 96: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Vue.jsdirectivesYouhavealreadylearnedinthepreviouschapterwhatdirectivesareandhowtheyareusedtoenhancetheapplication'sbehavior.

You'vealreadyusedsomedirectivesthatallowdatabindingindifferentwaystotheViewlayer(v-model,v-if,v-show,andsoon).Besidesthesedefaultdirectives,Vue.jsallowsyoutocreatecustomdirectives.CustomdirectivesprovideamechanismtoenablecustombehaviorofDOMtodatamapping.

Whenregisteringacustomdirective,youcanprovidethreefunctions:bind,update,andunbind.Insidethebindfunction,youcanattachaneventlistenertotheelementanddowhateverneedstobedonethere.Insidetheupdatefunctionthatreceivesoldandnewvaluesasparameters,youcandefineacustombehaviorofwhatshouldhappenwhendatachanges.Theunbindmethodprovidesallthecleaningoperationsneeded(forexample,detacheventlisteners).

Tip

InVue2.0,directiveshavesignificantlyreducedthescopeofresponsibility—nowtheyareonlyusedtoapplylow-leveldirectDOMmanipulations.Vue'schangingguidesuggeststopreferusingcomponentsovercustomdirectives(https://github.com/vuejs/vue/issues/2873).

Thus,thefullversionofthecustomdirectivewouldlooklikethefollowing:

Vue.directive('my-directive',{

bind:function(){

//dothepreparationworkonelementbinding

},

update:function(newValue,oldValue){

//dosomethingbasedontheupdatedvalue

},

unbind:function(){

//dotheclean-upwork

}

})

Thesimplifiedversion,incaseyoujustneedtodosomethingonthevalueupdate,canonlyhavetheupdatemethodthatcanbepasseddirectlyasthesecondparameterofthedirectivefunction:

Vue.directive('my-directive',function(el,binding){

//dosomethingwithbinding.value

})

Thetheoryisnice,butwithoutasmallexample,itturnsoutboring.Solet'shavealookataverysimpleexample,whichwillshowthesquareofthenumbereachtimeitsvalueisupdated.

Ourcustomdirectivewilllooklikethefollowing:

Page 97: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Vue.directive('square',function(el,binding){

el.innerHTML=Math.pow(binding.value,2);

});

Usethisdirectiveinyourtemplatefileusingthev-prefix:

<divv-square="item"></div>

InstantiatetheVueinstancewithiteminitsdataandtrytochangethevalueofitem.Youwillseethatthevalueinsidethedivelementwillimmediatelydisplaythesquarenumberofthechangedvalue.ThecompletecodeforthiscustomdirectivecanbefoundintheJSFiddleathttps://jsfiddle.net/chudaol/we07oxbd/.

Page 98: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

PluginsinVue.jsVue'scorefunctionality,aswehavealreadyanalyzed,providesdeclarativedatabindingandcomponentscomposing.Thiscorebehaviorisenhancedwithpluginsthatprovidearichsetoffunctionality.Thereareseveraltypesofplugins:

Pluginsthataddsomeglobalpropertyormethod(vue-element)Pluginsthataddsomeglobalassets(vue-touch)PluginsthataddVueinstancemethodsattachingthemtoVue'sprototypePluginsthatprovidesomeexternalfunctionalityorAPI(vue-router)

PluginsmustprovideaninstallmethodthathasaccesstotheglobalVueobjectthatcanenhanceandmodifyit.Inordertousethisplugin,Vueprovidestheusemethodthatreceivespluginsinstances(Vue.use(SomePlugin)).

Tip

YoucanalsowriteaVuepluginofyourowntoenablecustombehaviorforyourVueinstance.

Let'susethepreviouscustomdirectivesexampleandcreateaminimalisticpluginthatimplementsmathematicalsquareandsquarerootdirectives.CreateafilenamedVueMathPlugin.jsandaddthefollowingcode:

exportdefault{

install:function(Vue){

Vue.directive('square',function(el,binding){

el.innerHTML=Math.pow(binding.value,2);

});

Vue.directive('sqrt',function(el,binding){

el.innerHTML=Math.sqrt(binding.value);

});

}

};

Nowcreateafilecalledscript.js.Let'saddthemainscripttothisfile.Inthisscript,wewillimportbothVueandVueMathPlugin,andwillcallVue'susemethodinordertotellittousethepluginandcalltheplugin'sinstallmethod.Thenwe'lljustinitiateaVueinstanceaswealwaysdo:

importVuefrom'vue/dist/vue.js';

importVueMathPluginfrom'./VueMathPlugin.js';

Vue.use(VueMathPlugin);

newVue({

el:'#app',

data:{item:49}

});

Page 99: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Nowcreateanindex.htmlfilethatincludesthemain.jsfile(wewillbuilditwithBrowserifyandBabelify).Inthisfile,let'saddaninputusingthev-modeldirectivethatwillbeusedtochangethevalueoftheitem.Createtwospansusingv-squareandv-sqrtdirectivesaswell:

<body>

<divid="app">

<inputv-model="item"/>

<hr>

<div>Square:<spanv-square="item"></span></div>

<div>Root:<spanv-sqrt="item"></span></div>

</div>

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

</body>

Createapackage.jsonfiletoincludetheneededdependenciesforbuildingtheprojectandaddascriptforbuildingthemain.jsfile:

{

"name":"vue-custom-plugin",

"scripts":{

"build":"browserifyscript.js-omain.js-t

[babelify--presets[es2015]]"

},

"version":"0.0.1",

"devDependencies":{

"babel-preset-es2015":"^6.9.0",

"babelify":"^7.3.0",

"browserify":"^13.0.1",

"vue":"^2.0.3"

}

}

Nowinstallthedependenciesandbuildtheprojectfromthefollowingcommandline:

npminstall

npmrunbuild

Openindex.htmlinthebrowser.Trytochangethenumberintheinputbox.Bothsquareandsquarerootvalueschangeimmediately:

Page 100: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Thechangesinthedataareappliedimmediatelytothedirectivescreatedasapartofcustomplugin

Page 101: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

ExerciseEnhanceMathPluginwithtrigonometricalfunctions(sine,cosine,andtangent).

ApossiblesolutiontothisexercisecanbefoundintheAnnexes.

Page 102: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

ApplicationstateandVuexWhenanapplicationreachesaconsiderablesize,itmightbecomenecessaryforustomanagetheglobalapplicationstatesomehow.InspiredfromFlux(https://facebook.github.io/flux/),thereisaVuexmodulethatallowsustomanageandsharetheglobalapplicationstateamongVuecomponents.

Tip

Donotthinkabouttheapplicationstateassomethingcomplexanddifficulttounderstand.Infact,itisnomorethanjustdata.Eachcomponenthasitsowndata,and"applicationstate"isdatathatcanbeeasilysharedbetweenallthecomponents!

HowVuexstoremanagesapplicationsstateupdates

Liketheotherplugins,inordertobeabletouseandtoinstantiatetheVuexstore,youneedtoinstructVuetouseit:

importVuexfrom'vuex';

importVuefrom'vue';

Vue.use(Vuex);

varstore=newVuex.Store({

Page 103: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

state:{<...>},

mutations:{<...>}

});

Then,wheninitializingthemaincomponent,assignthestoreinstancetoit:

newVue({

components:components,

store:store

});

Nowthemainapplicationandallitscomponentsareawareaboutthestore,haveaccesstothedatainsideit,andareabletotriggeractionsonitatanytimeoftheapplication'slifecycle.Wewilldigdeeplyintotheapplicationstateinthenextchapters.

Page 104: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

vue-cliYes,Vuehasitsowncommand-lineinterface.ItallowsustoinitializeaVueapplicationwithwhateverconfigurationwewant.YoucaninitializeitwithWebpackboilerplate,withBrowserifyboilerplate,orjustwithasimpleboilerplatethatjustcreatesanHTMLfileandprepareseverythingforyoutostartworkingwithVue.js.

Installitwithnpm:

npminstall-gvue-cli

Thedifferentwaysofinitializinganapplicationareasfollows:

vueinitwebpack

vueinitwebpack-simple

vueinitbrowserify

vueinitbrowserify-simple

vueinitsimple

Toseethedifference,let'srunvueinitwiththesimpletemplateandwiththeWebpacktemplate,andlookatthedifferencesinthegeneratedstructure.Followingishowtheoutputdiffersfrombothcommands:

Theoutputfromthecommandsvueinitwebpackandvueinitsimple

Page 105: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Thefollowingishowtheapplicationstructurediffers:

Thedifferenceinstructureinapplicationscaffoldedwithvueinitsimpleandvueinitwebpack

Theindex.htmlfileinthesimpleconfigurationalreadycontainsVue.jsfromtheCDN,soifyoujustneedtodosomethingreallysimplesuchasquickprototyping,usethisone.

ButifyouareabouttostartacomplexSinglePageApplication(SPA)projectthatwillrequiretestingandhotreloadingduringdevelopment,usetheWebpackorBrowserifyconfiguration.

Page 106: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

VuepluginsforIDEsTherearepluginsforVuesyntaxhighlightingforsomemajorIDEs.Iwillleaveyouwiththelinkstothefanciestofthem:

IDE LinktotheVueplugin

Sublime https://github.com/vuejs/vue-syntax-highlight

Webstorm https://github.com/postalservice14/vuejs-plugin

Atom https://github.com/hedefalk/atom-vue

VisualStudioCode https://github.com/LiuJi-Jim/vscode-vue

vim https://github.com/posva/vim-vue

Brackets https://github.com/pandao/brackets-vue

Page 107: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Installing,using,anddebuggingaVue.jsapplicationInthissection,wewillanalyzeallthepossiblewaysofinstallingVue.js.Wewillalsocreateaskeletonforourapplicationsthatwewilldevelopandenhancethroughthenextchapters.Wewillalsolearnthewaysofdebuggingandtestingourapplications.

Page 108: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

InstallingVue.jsThereareanumberofwaystoinstallVue.js.Startingfromclassic,includingthedownloadedscriptintoHTMLwithinthe<script>tags,usingtoolslikebower,npm,orVue'scommand-lineinterface(vue-cli),tobootstrapthewholeapplication.

Let'shavealookatallthesemethodsandchooseourfavorite.Inalltheseexamples,wewilljustshowaheaderonapagesayingLearningVue.js.

Standalone

Downloadthevue.jsfile.Therearetwoversions,minifiedanddeveloperversion.Thedevelopmentversionisathttps://vuejs.org/js/vue.js.Theminifiedversionisathttps://vuejs.org/js/vue.min.js.

Tip

Ifyouaredeveloping,makesureyouusethedevelopmentnon-minifiedversionofVue.Youwilllovethenicetipsandwarningsontheconsole.

Thenjustincludevue.jsinthe<script>tags,asfollows:

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

Vueisregisteredintheglobalvariable.Youarereadytouseit.

Ourexamplewillthenlookassimpleasthefollowing:

<divid="app">

<h1>{{message}}</h1>

</div>

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

<script>

vardata={

message:'LearningVue.js'

};

newVue({

el:'#app',

data:data

});

</script>

CDN

Vue.jsisavailableinthefollowingCDNs:

jsdelivr:https://cdn.jsdelivr.net/vue/2.0.3/vue.jscdnjs:https://cdnjs.cloudflare.com/ajax/libs/vue/2.0.3/vue.jsunpkg:https://unpkg.com/[email protected]/dist/vue.js(recommended)

Page 109: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

JustputtheURLinsourceinthescripttagandyouarereadytouseVue!

<scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/vue/2.0.3/vue.js">

</script>

Tip

BewaretheCDNversionmightnotbesynchronizedwiththelatestavailableversionofVue.

Thus,theexamplewilllookexactlythesameasinthestandaloneversion,butinsteadofusingdownloadedfileinthe<script>tags,weareusingaCDNURL.

Bower

IfyouarealreadymanagingyourapplicationwithBoweranddon'twanttouseothertools,there'salsoaBowerdistributionofVue.Justcallbowerinstall:

#lateststablerelease

bowerinstallvue

Ourexamplewilllookexactlylikethetwopreviousexamples,butitwillincludethefilefromthebowerfolder:

<scriptsrc="bower_components/vue/dist/vue.js"></script>

CSP-compliant

ContentSecurityPolicy(CSP)isasecuritystandardthatprovidesasetofrulesthatmustbeobeyedbytheapplicationinordertopreventsecurityattacks.Ifyouaredevelopingapplicationsforbrowsers,youarelikelyfamiliarwiththispolicy!

FortheenvironmentsthatrequireCSP-compliantscripts,there'saspecialversionofVue.jsathttps://github.com/vuejs/vue/tree/csp/dist.

Let'sdoourexampleasaChromeapplicationtoseetheCSP-compliantVue.jsinaction!

Startbycreatingafolderforourapplicationexample.ThemostimportantthinginaChromeapplicationisthemanifest.jsonfile,whichdescribesyourapplication.Let'screateit.Itshouldlooklikethefollowing:

{

"manifest_version":2,

"name":"LearningVue.js",

"version":"1.0",

"minimum_chrome_version":"23",

"icons":{

"16":"icon_16.png",

"128":"icon_128.png"

},

"app":{

"background":{

Page 110: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

"scripts":["main.js"]

}

}

}

Thenextstepistocreateourmain.jsfile,whichwillbetheentrypointfortheChromeapplication.Thescriptshouldlistenfortheapplicationlaunchingandopenanewwindowwithgivensizes.Let'screateawindowof500x300sizeandopenitwithindex.html:

chrome.app.runtime.onLaunched.addListener(function(){

//Centerthewindowonthescreen.

varscreenWidth=screen.availWidth;

varscreenHeight=screen.availHeight;

varwidth=500;

varheight=300;

chrome.app.window.create("index.html",{

id:"learningVueID",

outerBounds:{

width:width,

height:height,

left:Math.round((screenWidth-width)/2),

top:Math.round((screenHeight-height)/2)

}

});

});

Atthispoint,theChrome-specificapplicationmagicisoverandnowweshalljustcreateourindex.htmlfilethatwilldothesamethingasinthepreviousexamples.Itwillincludethevue.jsfileandourscript,wherewewillinitializeourVueapplication:

<htmllang="en">

<head>

<metacharset="UTF-8">

<title>Vue.js-CSP-compliant</title>

</head>

<body>

<divid="app">

<h1>{{message}}</h1>

</div>

<scriptsrc="assets/vue.js"></script>

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

</body>

</html>

DownloadtheCSP-compliantversionofVue.jsandaddittotheassetsfolder.

Nowlet'screatetheapp.jsfileandaddthecodethatwealreadywroteaddedseveraltimes:

vardata={

message:"LearningVue.js"

};

newVue({

Page 111: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

el:"#app",

data:data

});

Addittotheassetsfolder.

Donotforgettocreatetwoiconsof16and128pixelsandcallthemicon_16.pngandicon_128.png,respectively.

Yourcodeandstructureintheendshouldlookmoreorlesslikethefollowing:

StructureandcodeforthesampleChromeapplicationusingvue.js

Andnowthemostimportantthing.Let'scheckifitworks!Itisvery,verysimple:

1. Gotochrome://extensions/urlinyourChromebrowser.2. ChecktheDevelopermodecheckbox.3. ClickonLoadunpackedextension...andcheckthefolderthatwe'vejustcreated.4. Yourappwillappearinthelist!Nowjustopenanewtab,clickonapps,andcheckthat

yourappisthere.Clickonit!

Page 112: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

SampleChromeapplicationusingvue.jsinthelistofChromeapps

Congratulations!YouhavejustcreatedaChromeapplication!

npm

Thenpminstallationmethodisrecommendedforlarge-scaleapplications.Justrunnpminstallvueasfollows:

#lateststablerelease

npminstallvue

#lateststableCSP-compliantrelease

npminstallvue@csp

Thenrequireit:

varVue=require("vue");

Or,forES2015lovers,runthefollowing:

importVuefrom"vue";

OurHTMLwilllookexactlylikeinthepreviousexamples:

<htmllang="en">

Page 113: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

<head>

<metacharset="UTF-8">

<title>Vue.js-NPMInstallation</title>

</head>

<body>

<divid="app">

<h1>{{message}}</h1>

</div>

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

</body>

</html>

Nowlet'screateascript.jsfilethatwilllookalmostexactlythesameasinastandaloneorCDNversion,withtheonlydifferencebeingthatitwillrequirevue.js:

varVue=require('vue/dist/vue.js');

vardata={

message:'LearningVue.js'

};

newVue({

el:'#app',

data:data

});

Let'sinstallVueandBrowserifyinordertobeabletocompileourscript.jsfileintothemain.jsfile:

npminstallvue--save-dev

npminstallbrowserify--save-dev

Inthepackage.jsonfile,addascriptforbuildaswellthatwillexecuteBrowserifyonscript.jstranspilingitintomain.js.Soourpackage.jsonfilewilllooklikethefollowing:

{

"name":"learningVue",

"scripts":{

"build":"browserifyscript.js-omain.js"

},

"version":"0.0.1",

"devDependencies":{

"browserify":"^13.0.1",

"vue":"^2.0.3"

}

}

Nowrunthefollowingcommand:

npmrunbuild

Andopenindex.htmlinthebrowser.

Ihaveafriendthatatthispointwouldsaysomethinglike:really?Somanysteps,installations,

Page 114: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

commands,explanations...Justtooutputsomeheader?I'mout!

Ifyouarealsothinkingthis,wait.Yes,thisistrue,nowwe'vedonesomethingreallysimpleinarathercomplexway,butifyoustaywithmeabitlonger,youwillseehowcomplexthingsbecomeeasytoimplementifweusethepropertools.Also,donotforgettocheckyourPomodorotimer,maybeit'stimetotakearest!

vue-cli

Aswehavealreadymentionedinthepreviouschapter,Vueprovidesitsowncommand-lineinterfacethatallowsbootstrappingsingle-pageapplicationsusingwhateverworkflowsyouwant.Itimmediatelyprovideshotreloadingandstructureforatest-drivenenvironment.Afterinstallingvue-cli,justrunvueinit<desiredboilerplate><project-name>andthenjustinstallandrun:

#installvue-cli

$npminstall-gvue-cli

#createanewproject

$vueinitwebpacklearn-vue

#installandrun

$cdlearn-vue

$npminstall

$npmrundev

Nowopenyourbrowseronlocalhost:8080.Youjustusedvue-clitoscaffoldyourapplication.Let'sadaptittoourexample.Openasourcefolder.Inthesrcfolder,youwillfindanApp.vuefile.DoyourememberwetalkedaboutVuecomponentsthatarelikebricksfromwhichyoubuildyourapplication?Doyourememberthatwewerecreatingandregisteringtheminsideourmainscriptfile,andImentionedthatwewilllearntobuildcomponentsinamoreelegantway?Congratulations,youarelookingatthecomponentbuiltinafancyway!

FindthelinethatsaysimportHellofrom'./components/Hello'.Thisisexactlyhowthecomponentsarebeingreusedinsideothercomponents.Havealookatthetemplateatthetopofthecomponentfile.Atsomepoint,itcontainsthe<hello></hello>tag.ThisisexactlywhereinourHTMLfilethehellocomponentwillappear.Havealookatthiscomponent;itisinthesrc/componentsfolder.Asyoucansee,itcontainsatemplatewith{{msg}}andascriptthatexportsdatawithdefinedmsg.Thisisexactlythesameasweweredoinginourpreviousexampleswithoutusingcomponents.Let'sslightlymodifythecodetomakeitthesameasinthepreviousexamples.IntheHello.vuefile,changemsginthedataobject:

<script>

exportdefault{

data(){

return{

msg:"LearningVue.js"

}

}

}

</script>

Page 115: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

IntheApp.vuecomponent,removeeverythingfromthetemplateexceptthehellotagsothatthetemplatelookslikethefollowing:

<template>

<divid="app">

<hello></hello>

</div>

</template>

Nowifyoureruntheapplication,youwillseeourexamplewithbeautifulstylesthatwedidn'ttouch:

Vueapplicationbootstrappedusingvue-cli

Tip

BesidesWebpackboilerplatetemplate,youcanusethefollowingconfigurationswithyourvue-cli:

webpack-simple:AsimpleWebpack+vue-loadersetupforquickprototypingbrowserify:Afull-featuredBrowserify+Vueifysetupwithhot-reload,linting,andunittesting

Page 116: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

browserify-simple:AsimpleBrowserify+Vueifysetupforquickprototypingsimple:ThesimplestpossibleVuesetupinasingleHTMLfile

Devbuild

Mydearreader,IcanseeyourshiningeyesandIcanreadyourmind.NowthatyouknowhowtoinstallanduseVue.jsandhowitworks,youdefinitelywanttoputyourhandsdeeplyintothecorecodeandcontribute!

Iunderstandyou.Forthis,youneedtousethedevelopmentversionofVue.js,whichyouhavetodownloadfromGitHubandcompileyourself.

Let'sbuildourexamplewiththisdevelopmentversionofVue.Createanewfolder,forexample,dev-build,andcopyallthefilesfromthenpmexampletothisfolder.

Donotforgettocopythenode_modulesfolder.YoushouldcdintoitanddownloadfilesfromGitHubtoit,andthenrunnpminstallandnpmrunbuild:

cd<APP-PATH>/node_modules

rm-rfvue

gitclonehttps://github.com/vuejs/vue.git

cdvue

npminstall

npmrunbuild

Nowbuildourexampleapplication:

cd<APP-PATH>

npmrunbuild

Openindex.htmlinthebrowser;youwillseetheusualLearningVue.jsheader.

Let'snowtrytochangesomethinginvue.jssource!Gotothenode_modules/vue/src/compiler/parserfolderandopenthetext-parser.jsfile.Findthelinethatsaysthefollowing:

constdefaultTagRE=/\{\{((?:.|\n)+?)\}\}/g

Actually,thisregularexpressiondefinesdefaultdelimitersusedintheHTMLtemplates.ThethingsinsidethesedelimitersarerecognizedasaVuedataorasaJavaScriptcode.Let'schangethem!Let'sreplace{and}withdoublepercentagesigns!Goonandeditthefile:

constdefaultTagRE=/\%\%((?:.|\n)+?)\%\%/g

NowrebuildbothVuesourceandourapplicationandrefreshthebrowser.Whatdoyousee?

Page 117: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

AfterchangingtheVuesourceandreplacingdelimiters,{{}}delimitersdonotworkanymore!

Themessageinside{{}}isnolongerrecognizedasdatathatwepassedtoVue.Infact,itisbeingrenderedaspartofHTML.

Nowgototheindex.htmlfileandreplaceourcurlybracketsdelimiterswithdoublepercentage,asfollows:

<divid="app">

<h1>%%message%%</h1>

</div>

Rebuildourapplicationandrefreshthebrowser!Whataboutnow?Youseehoweasyitistochangetheframework'scodeandtotryoutyourchanges.I'msureyouhaveplentyofideasabouthowtoimproveoraddsomefunctionalitytoVue.js.Sochangeit,rebuild,test,deploy!Happypullrequests!

Page 118: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

DebuggingyourVueapplicationYoucandebugyourVueapplicationthesamewayyoudebuganyotherwebapplication.Useyourdevelopertools(firebug),breakpoints,debuggerstatements,andsoon.IfyouwanttodivedeepinsidetheChromedebuggingtools,checkChrome'sdocumentationathttps://developer.chrome.com/devtools.

VuealsoprovidesVue.jsdevtools,soitgetseasiertodebugVueapplications.YoucandownloadandinstallitfromtheChromewebstoreathttps://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd.

Unfortunately,itdoesn'tworkwithlocallyopenedfiles,sousesomesimpleHTTPserverinordertoserveourexamplesasawebpage(forexample,https://www.npmjs.com/package/http-server).

Afterinstallingit,open,forexample,ourshoppinglistapplication.Opendevelopertools.YouwillseetheVuetabhasautomaticallyappeared:

Page 119: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Vuedevtools

Inourcase,weonlyhaveonecomponent—<Root>.Asyoucanimagine,oncewestartworkingwithcomponentsandhavinglotsofthem,theywillallappearintheleftpartoftheVuedevtoolspalette.Clickonthe<Root>componentandinspectit.You'llseeallthedataattachedtothiscomponent.Ifyoutrytochangesomething,forexample,addashoppinglistitem,checkoruncheckacheckbox,changethetitle,andsoon,allthesechangeswillbeimmediatelypropagatedtothedataintheVuedevtools.Youwillimmediatelyseethechangesontheright-handsideofit.Let'stry,forexample,toaddashoppinglistitem.Onceyoustarttyping,youseeontherighthownewItemchangesaccordingly:

ThechangesintheModelsareimmediatelypropagatedtotheVuedevtoolsdata

WhenwestartaddingmorecomponentsandintroducecomplexitytoourVueapplications,thedebuggingwillcertainlybecomemorefun!

Page 120: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

ScaffoldingourapplicationsDoyourememberthetwoapplicationsthatwestartedtoworkoninthefirstchapter,theshoppinglistapplicationandthePomodoroone?Inthissection,wewillscaffoldtheseapplicationsusingthevue-clitoolinorderforthemtobereadytocontainreusablecomponents,betested,andbedeployed.Oncewebootstraptheseapplications,wewillworkonthemuntiltheendofthisbook.Solet'sdoitcarefullyandwithlotsoflove!

Page 121: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

ScaffoldingtheshoppinglistapplicationWewillscaffoldtheshoppinglistapplicationusingvue-cliWebpackconfiguration.

Tip

Incaseyouhaveignoredallpreviouspracticalexercisesrelatedtovue-cli,donotforgettoinstallitbeforeproceedingtothenextsteps:npminstall-gvue-cli

Ifyoualreadyhavevue-cliinstalled,gotothedirectorywhereyouwanttobootstraptheapplicationandrunthefollowing:

vueinitwebpackshopping-list

Answeryestoallthequestions(justclickenter)andvoilà!Youhavetheapplicationbootstrapped:

Bootstrapingtheshoppinglistapplicationwithvue-cli

Switchtotheshoppinglistdirectoryandrunnpminstallandnpmrundev.Openyourbrowseratlocalhost:8080.YouwillseetheHelloWorldpageofthenewlycreatedVueapplication:

Page 122: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

TheHelloWorldviewofthenewlybootstrappedapplication

Let'scleanthebootstrappedcodesothattheapplicationgetsreadytobepopulatedwithourapplication-specificcode.GototheApp.vuefileandremoveeverything,leavingjustthetagsthatdefinetheapplicationstructure:

<template>withthemain<div>insideThe<script>tagThe<style>tag

So,intheend,yourApp.vuefilelookslikethefollowing:

<template>

<divid="app">

</div>

</template>

<script>

</script>

<style>

</style>

Page 123: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Havealookatthepageopenedinthebrowser.Funny,youhaven'tdoneanything,butthepagenowdoesn'tcontainthedefaultHelloWorld.Thepageisempty!Ithaschangedautomatically!

Tryaddingsomethinginsidethe<template>tags.Lookatthepage;itautomaticallyreloadsonceyouintroducechanges.Thisworksbecauseofthevue-hot-reloadpluginthatdetectschangesinyourVuecomponentsandautomaticallyrebuildstheprojectandreloadsthebrowserpage.TrytowritesomeJavaScriptcodeinsidethe<script>tagsthatdoesn'tcorrespondtolintstandards,forexample,usingnotDefinedVariable:

<script>

notDefinedVariable=5;

</script>

Thepageinthebrowserisnotrefreshed.Lookatyourshellconsole.Itshowsthelinterrorsand"refuses"tobuildyourapplication:

Eachtimetheapplicationischangedthelintrulesarechecked

Thishappens,thankstotheESLintplugin,whichchecksthecodeagainstthelintruleseachtimetheapplicationchanges.

Withthat,wecanbesurethatourcodewillfollowthebestqualitystandards.

Speakingofquality,weshouldalsoprepareourapplicationtobeabletorununittests.Luckilyforus,vue-cliwithWebpackhasalreadydoneitforus.Runnpmrununittorununittestsandnpmrune2etorunend-to-endnightwatchtests.End-to-endtestswillnotruninparallelwithyourrunningapplicationsincebothareusingthesameport.So,ifyouwanttoruntestsduringdevelopment,youshouldchangetheportintheconfig/index.jsconfigurationfileorsimplystoptheapplicationbetweenrunningtests.Afterrunningtests,youwillseetheend-to-endtestsfail.Thisisbecausetheyarecheckingfortheapplication'sspecificelementsthatwehaveremoved.Openthefiletest.jsfromthetest/e2e/specs/directoryandcleanalltheassertionsthatwedon'tneedanymore.Nowitshouldlooklikethefollowing:

module.exports={

Page 124: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

'defaulte2etests':function(browser){

browser

.url('http://localhost:8080')

.waitForElementVisible('#app',5000)

.end()

}

}

Rerunthetests.Nowtheyshouldbepassing.Fromnowon,aswewilladdcodetoourapplication,wewilladdunitandend-to-endtests.

Page 125: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

BootstrapingyourPomodoroapplicationForthePomodoroapplication,dothesameasfortheshoppinglistapplication.RunvueinitwebpackpomodoroandrepeatallthenecessarystepstoensurethatthestructureisreadytobepopulatedwiththePomodoroapplicationcode!

Page 126: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

ExerciseImplementourPomodoroapplicationasaChromeapp!YoujustneedtouseitwithaCSP-compliantversionofVue.jsandaddamanifest.jsonfile.

Page 127: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

SummaryInthischapter,wehaveanalyzedthebehind-the-scenesofVue.js.Youlearnedhowdatareactivityisachieved.YousawhowVue.jsleveragesObject.definePropertygettersandsetterstopropagatechangesinthedata.YousawanoverviewofthekeyVue.jsconcepts,suchasreusablecomponents,pluginssystem,andstatemanagementwithVuex.Wehavebootstrappedtheapplicationsthatwewilldevelopduringthenextchapters.

Inthenextchapter,wewillhaveadeeperlookintotheVue'scomponentssystem.Wewillusecomponentsinourapplications.

Page 128: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Chapter3.Components–UnderstandingandUsingInthepreviouschapter,youlearnedhowVue.jsworks.YousawbehindthescenesandevenmadeaslightdebugofthecoreVue.jscode.YoulearnedsomeofVue'skeyconcepts.YoualsolearnedandtrieddifferentwaysofinstallingVue.js.Wehavebootstrappedtheapplications;wewilldevelopandenhancefromthischapteron.Wehavealsoseenhowtodebugandtotestourapplications.

Inthefirstchapter,wetalkedaboutcomponentsandevencreatedsome.Inthischapter,wewillusecomponentsinourapplicationsandseesomeinterestingdirectivesinaction.Thatbeingsaid,inthischapter,wearegoingtodothefollowing:

RevisitthecomponentstopicandreviewwhatcomponentsareCreatecomponentsforourapplicationsLearnwhatsingle-filecomponentsareLearnhowtoachievereactiveCSStransitionswithspecialattributes

Page 129: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

RevisitingcomponentsAsyousurelyrememberfromthepreviouschapters,componentsarespecialpartsoftheVueapplicationthathavetheirownscopeofdataandmethods.Componentscanbeusedandreusedthroughouttheapplication.Inthepreviouschapter,youlearnedthatacomponentiscreatedbyusingtheVue.extend({...})methodandregisteredusingtheVue.component()syntax.So,inordertocreateanduseacomponent,wewouldwritethefollowingJavaScriptcode:

//creatingcomponent

varHelloComponent=Vue.extend({

template:'<h1>Hello</h1>'

});

//registeringcomponent

Vue.component('hello-component',HelloComponent);

//initializingtheVueapplication

newVue({

el:'#app'

});

Then,wewillusehello-componentinsidetheHTML:

<divid='app'>

<hello-component></hello-component>

</div>

Tip

BothinitializationandregistrationcanbewrittenasasingleVue.componentinvocationwithcorrespondingoptions:

Vue.component('hello-component',{template:'<h1>Hello</h1>'});

Page 130: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

BenefitsofusingcomponentsTherearesomethingsthatweneedtolearnbeforegoingdeepintothecomponentsandrewriteourapplicationsusingthem.Inthissection,wewillcoverthingssuchashandlingdataandelpropertiesinsideacomponent,componenttemplates,scope,andpreprocessors.

Page 131: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

DeclaringtemplatesinHTMLInourpreviousexample,wecreatedaVuecomponentwithatemplatewrittenasastring.It'sactuallyeasyandnicebecausewehaveeverythingweneedinsideourcomponent.NowimagineourcomponentwithamorecomplexHTMLstructure.WritingacomplexHTMLstringtemplateiserror-prone,ugly,andagainstbestpractices.

Tip

Bybestpractices,Imeancleanandmaintainablecode.ComplexHTMLwrittenasastringisanythingbutmaintainable.

VueallowsdeclaringtemplatesinsideanHTMLfilewithinaspecial<template>tag!

So,torewriteourexample,wewilldeclareanHTMLtagtemplatewiththecorrespondingmarkupinside:

<templateid="hello">

<h1>Hello</h1>

</template>

Andthen,insideourcomponent,insteadoftheHTMLstring,wewilljustusetheIDofthetemplate:

Vue.component('hello-component',{

template:'#hello'

});

Ourwholecodewilllooklikethefollowing:

<body>

<templateid="hello">

<h1>Hello</h1>

</template>

<divid="app">

<hello-component></hello-component>

</div>

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

<script>

Vue.component('hello-component',{

template:'#hello'

});

newVue({

el:'#app'

});

</script>

</body>

Page 132: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Intheprecedingexample,wehadonlyusedthetemplateattributeforthecomponent.Let'smoveonandseehowthedataandelattributesshouldbetreatedinsideacomponent.

Page 133: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

HandlingdataandelpropertiesinsideacomponentAsalreadymentioned,thecomponent'ssyntaxisthesameastheVueinstance'ssyntax,butitmustextendtheVueinsteadofcallingitdirectly.Withthispremise,itseemscorrecttocreateacomponentlikethefollowing:

varHelloComponent=Vue.extend({

el:'#hello',

data:{msg:'Hello'}

});

Butthiswouldleadtoascopeleak.EveryinstanceofHelloComponentwouldsharethesamedataandel.Andthisisnotexactlywhatwewant.ThatiswhyVueexplicitlydemandstodeclarethesepropertiesasfunctions:

varHelloComponent=Vue.component('hello-component',{

el:function(){

return'#hello';

},

data:function(){

return{

msg:'Hello'

}

}

});

Evenifyoumakeamistakeanddeclarethedataortheelpropertiesasanobjectoranelement,Vuewillkindlywarnyou:

Vue'swarningwhenusingdataasanobjectinsteadofafunctioninsideofaVuecomponent

Page 134: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

ScopeofthecomponentsAsalreadymentioned,allcomponentshavetheirownscopethatisinaccessiblebyothercomponents.Nevertheless,theglobalapplicationscopeisaccessiblebyalltheregisteredcomponents.Youcanseethecomponents'scopeaslocalandtheapplicationscopeasglobalscopes.It'sthesame.However,usingtheparent'sdatainsideacomponentisnotstraightforward.Youhavetoexplicitlyindicateinsideacomponentwhichparent'sdatapropertiesshouldbeaccessedusingthepropattributeandbindthemtothecomponentinstanceusingthev-bindsyntax.Let'sseehowitworksonourHelloComponentexample.

Let'sstartbydeclaringHelloComponentwithdatathatcontainstheattributemsg:

Vue.component('hello-component',{

data:function(){

return{

msg:'Hello'

}

}

});

Now,let'screateaVueinstancewithsomedatainsideit:

newVue({

el:'#app',

data:{

user:'hero'

}

});

InsideourHTML,let'screateatemplateandapplyittothecomponentusingthetemplate'sID:

//templatedeclaration

<templateid="hello">

<h1>{{msg}}{{user}}</h1>

</template>

//usingtemplateincomponent

Vue.component('hello-component',{

template:'#hello',

data:function(){

return{

msg:'Hello'

}

}

});

Inordertoseethecomponentonthepage,weshouldinvokeitinsidetheHTMLofourappcontainer:

<divid="app">

<hello-component></hello-component>

</div>

Page 135: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Ifyouopenthepageinthebrowser,youwillonlyseeHello;theuserdatapropertyisstillnotboundtothecomponent:

Theparent'sdatapropertyisnotyetboundtoourVuecomponent

InordertobindthedatafromtheparentVueapplication,wehavetodothefollowingtwothings:

IndicatethispropertyinsideofthepropattributeofacomponentBindittothehello-componentinvocation:

//callingparent'sdataattributesinthecomponent

Vue.component('hello-component',{

template:'#hello',

data:function(){

return{

msg:'Hello'

}

},

props:['user']

});

//bindingauserdatapropertytothecomponent

<divid="app">

<hello-componentv-bind:user="user"></hello-component>

</div>

Refreshthepageandyouwillseehowitnowpresentsyouwithagreeting:

Afterthecorrectbindingoftheparent'sdatapropertytothecomponent,everythingworksasexpected.

Tip

Actually,thev-bind:usersyntaxcanbeshortcutjustbyusingthefollowing:

Page 136: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

:user<hello-component:user="user"></hello-component>

Page 137: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

ComponentsinsideothercomponentsThebeautyofthecomponentsisthattheycanbeusedandreusedinsideothercomponentsasLegobricksandblocks!Let'sbuildanothercomponent;let'scallitgreetings,whichwillbecomposedoftwosub-components:theformaskingfortheuser'snameandourhellocomponent.

Inordertodothis,let'sdeclarethetemplatefortheformandouralreadyfamiliarhellotemplate:

<!--templatefortheform-->

<templateid="form">

<div>

<labelfor="name">What'syourname?</label>

<inputv-model="user"type="text"id="name">

</div>

</template>

//templateforsayinghello

<templateid="hello">

<h1>{{msg}}{{user}}</h1>

</template>

NowwewillregistertwoVuecomponentsbasedonthesetemplates:

//registerformcomponent

Vue.component('form-component',{

template:'#form',

props:['user']

});

//registerhellocomponent

Vue.component('hello-component',{

template:'#hello',

data:function(){

return{

msg:'Hello'

}

},

props:['user']

});

Finally,wewillcreateourgreetingstemplatethatwillusebothformandhellocomponents.Donotforgetthatwehavetobindtheuserpropertyonthecomponentsinvocation:

<templateid="greetings">

<div>

<form-component:user="user"></form-component>

<hello-component:user="user"></hello-component>

</div>

</template>

Atthispoint,wecancreateourgreetingscomponentandusethegreetingstemplateinsideit.

Page 138: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Let'sinitialize,whichdatafunctionwiththenameoftheuserinthiscomponent:

//creategreetingscomponentbasedonthegreetingstemplate

Vue.component('greetings-component',{

template:'#greetings',

data:function(){

return{

user:'hero'

}

}

});

Insideourmainapplicationcontainer,wewillnowinvokethegreetingscomponent:

<divid="app">

<greetings-component></greetings-component>

</div>

DonotforgettoinitializetheVueapplication:

newVue({

el:'#app'

});

Openthepageinthebrowser.Youshouldseesomethinglikethefollowing:

ThepagebuiltfromvariousVuecomponents

Trytochangethenameintheinput.Youareexpectingittochangealsointhegreetingsheaderbecauseweboundittoit.Butstrangely,itdoesn'tchange.Well,thisisactuallythenormalbehavior.Bydefault,allpropsfollowone-waydatabinding.Thismeansthatifthedatachangeswithintheparent'sscope,thesechangesarepropagatedtothechildcomponent,butnotviceversa.Itisdonethiswayinordertopreventchildrencomponentsfromaccidentallymutatingtheparentstate.Itis,however,possibletoforcechildrencomponentstocommunicatewiththeirparentsbyinvokingevents.ChecktheVuedocumentationathttps://vuejs.org/guide/components.html#Custom-Events.

Inourcase,wecanbindausermodeltoourforminputcomponentandemittheinputeventeverytimetheusertypesintheinputbox.Weachieveitbyusingthev-on:inputmodifier,justlikeitisdescribedinthissectionathttps://vuejs.org/guide/components.html#Form-Input-

Page 139: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Components-using-Custom-Events.

Thus,wehavetopassv-model:usertoform-component:

<form-componentv-model="user"></form-component>

Then,form-componentshouldacceptthevaluepropandemittheinputevent:

Vue.component('form-component',{

template:'#form',

props:['value'],

methods:{

onInput:function(event){

this.$emit('input',event.target.value)

}

}

});

Theinputboxinsidetheform-componenttemplateshouldbindthev-on:inputandtheonInputmethodtothev-on:inputmodifier:

<inputv-bind:value="value"type="text"id="name"v-on:input="onInput">

Tip

Actually,priortoVue2.0,thiskindoftwo-waysynchronizationbetweencomponentsandtheirparentswaspossiblebyexplicitlytellingthepropertybeingboundtosyncusingthe.syncmodifier:<form-component:user.sync="user"></form-component>

Refreshthepage.Nowyoucanchangethenameinsidetheinputanditisimmediatelypropagatedtotheparent'sscope,andthustootherchildrencomponentsthatrelyonthisproperty:

Bindingpropertieswiththe.syncmodifierallowstwo-waydatabindingbetweenparentandchildrencomponents

YoucanfindthecompletecodeforthisexampleintheJSFiddleathttps://jsfiddle.net/chudaol/1mzzo8yn/.

Tip

Page 140: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

BeforetheVue2.0release,therewasonemoredata-bindingmodifier,.once.Withthismodifier,thedatawouldbeboundonlyonce,andanyotherchangeswouldnotaffectthestateofcomponents.Comparethefollowing:

<form-component:user="user"></form-component>

<form-component:user.sync="user"></form-component>

<form-component:user.once="user"></form-component>

Page 141: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

RewritingtheshoppinglistwithsimplecomponentsNowthatwealreadyknowalotaboutcomponents,let'srewriteourshoppinglistapplicationusingthem.

Tip

Fortherewritingoftheapplication,wewillusethisversionoftheshoppinglistapplicationasabase:https://jsfiddle.net/chudaol/vxfkxjzk/3/.

Wehavealreadydoneitpreviously,whenwestartedtalkingaboutcomponents.Butatthattime,weusedstringtemplatesinsidethecomponents'options.Let'sdoitnowusingtemplatesaswehavejustlearnedtodo.Let'sjusthavealookattheinterfaceandidentifythecomponentsagain:

Ourshoppinglistapplicationwillhavefourcomponents

Thus,Isuggestthatourshoppinglistapplicationconsistsofthefollowingfourcomponents:

AddItemComponent:ThecomponentresponsibleforaddinganewitemtotheshoppinglistItemComponent:Thecomponentresponsiblefortherenderingofthenewitemintheshoppinglist

Page 142: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

ItemsComponent:ThecomponentresponsibleforrenderingandmanagingthelistofItemComponent

ChangeTitleComponent:Thecomponentresponsibleforchangingthetitleofthelist

Page 143: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

DefiningtemplatesforallthecomponentsLet'screatetemplatesforthesecomponentsassumingthatthecomponentsthemselvesarealreadydefinedandregistered.

Note

CamelCaseVSkebab-caseYouhaveprobablynoticedthatwhilewedeclarevariablesdescribingcomponentsinCamelCase(varHelloComponent=Vue.extend({...})),wenametheminkebab-case:Vue.component('hello-component',{...}).Wedothisbecauseofthecase-insensitiveHTMLattributenature.Thus,ourcomponentsfortheshoppinglistapplicationwillbecalledasfollows:

add-item-component

item-component

items-component

change-title-component

Havealookathowourmarkupwaspreviously(https://jsfiddle.net/chudaol/vxfkxjzk/3/).

Let'srewriteitusingtemplatesandcomponents'names.Inthispart,wewilljustworryaboutthepresentationlayer,leavingthedatabindingandactionshandlingforafutureimplementation.WejustcopyandpastetheHTMLpartoftheapplicationanddistributeitoverourcomponents.Ourfourtemplateswilllooksomethinglikethefollowing:

<!--addnewitemtemplate-->

<templateid="add-item-template">

<divclass="input-group">

<[email protected]="addItem"v-model="newItem"

placeholder="addshoppinglistitem"type="text"

class="form-control">

<spanclass="input-group-btn">

<button@click="addItem"class="btnbtn-default"

type="button">Add!</button>

</span>

</div>

</template>

<!--listitemtemplate-->

<templateid="item-template">

<li:class="{'removed':item.checked}">

<divclass="checkbox">

<label>

<inputtype="checkbox"v-model="item.checked">{{item.text}}

</label>

</div>

Page 144: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

</li>

</template>

<!--itemslisttemplate-->

<templateid="items-template">

<ul>

<item-componentv-for="iteminitems":item="item">

</item-component>

</ul>

</template>

<!--changetitletemplate-->

<templateid="change-title-template">

<div>

<em>Changethetitleofyourshoppinglisthere</em>

<inputv-bind:value="value"v-on:input="onInput"/>

</div>

</template>

Thus,ourmaincomponents'markupwillconsistofsomecomponents:

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

<h2>{{title}}</h2>

<add-item-component></add-item-component>

<items-component:items="items"></items-component>

<divclass="footer">

<hr/>

<change-title-componentv-model="title"</change-title-component>

</div>

</div>

Asyoucansee,themajorityofeachtemplateisaplaincopyandpasteofthecorrespondingHTMLcode.

However,therearesomesignificantdifferences.Thelistitemtemplate,forexample,isslightlychanged.Youhavealreadylearnedandusedthev-fordirectivepreviously.Inthepreviousexamples,weusedthisdirectivewithHTMLelementssuchas<li>.NowyouseethatitcanalsobeusedwithVuecustomcomponents.

Youmighthavealsonoticedasmalldifferenceinthechangetitletemplate.NowithasavalueboundtoitandemitstheonInputmethodboundtothev-on:inputmodifier.Asyouhavelearnedintheprevioussection,childrencomponentscannotdirectlyaffectdirectlyaparent'sdata,whichiswhywehavetousetheeventssystem.

Page 145: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

DefiningandregisteringallthecomponentsHavealookattheJavaScriptcodeinourpreviousshoppinglistapplication:https://jsfiddle.net/chudaol/c8LjyenL/.Let'saddthecodethatcreatesVuecomponents.WewillusetheIDsofalreadydefinedtemplatesfortheirtemplateattribute.Also,donotforgetaboutthepropsattributetopassthepropertiesfromtheparentapplication.Thus,weaddthefollowingcode:

//additemcomponent

Vue.component('add-item-component',{

template:'#add-item-template',

data:function(){

return{

newItem:''

}

}

});

//itemcomponent

Vue.component('item-component',{

template:'#item-template',

props:['item']

});

//itemscomponent

Vue.component('items-component',{

template:'#items-template',

props:['items']

});

//changetitlecomponent

Vue.component('change-title-component',{

template:'#change-title-template',

props:['value'],

methods:{

onInput:function(event){

this.$emit('input',event.target.value)

}

}

});

Asyoucansee,inpropsofeachcomponent,wehavepasseddifferentdataattributes—onlythosethatspecificallyconcernthecomponent.WehavealsomovedthenewItemattributetothedataattributeofadd-item-component.Inchange-title-component,wehaveaddedtheonInputmethodthatemitstheinputevent,sothetitleintheparentcomponentisaffectedbywhatevertheusertypesintheinputbox.

OpentheHTMLfileinthebrowser.Theinterfaceisexactlythesameasitwasearlier!ThecompletecodeofwhatwehavedoneinthissectioncanbefoundintheJSFiddleathttps://jsfiddle.net/chudaol/xkhum2ck/1/.

Page 146: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

ExerciseAlthoughourapplicationlooksexactlyasitwaslookingearlier,itsfunctionalitywaslost.Notonlydoesitnotadditems,butitalsoshowstheuglyerrorinthedevtoolsconsole.

Pleaseusetheeventsemittingsystemtobringtheaddingitemsfunctionalityback.

ApossiblesolutionforthisexercisecanbefoundintheAppendix,SolutionstoExercises.

Page 147: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Single-filecomponentsWeknowfromtheoldbestpracticesthatitisalwaysgoodtoseparateHTMLfromCSSandJavaScriptfiles.SomemodernframeworkssuchasReactarerelaxingandgraduallywipingoutthisrule.Nowadays,youwillnotbeshockedbylookingatthesmallfileorthecomponentthatcontainsitsownmarkup,style,andapplicationcodeinsideit.Actually,forsmallcomponents,weevenfinditmoreconvenienttohavesucharchitecture.Vuealsoallowsdefiningeverythingrelatedtothesamecomponentinthesamefile.Thiskindofcomponentisknownasasingle-filecomponent.

Note

Asingle-fileVuecomponentisafilewitha.vueextension.Theapplicationthatcontainssuchcomponentscanbebuiltusingthewebpackvueconfiguration.Toscaffoldanappwithsuchaconfiguration,theeasiestwayistousevue-cli(https://github.com/vuejs-templates/webpack).

AVuecomponentcanhaveuptothreesectionsinit:

<script>

<template>

<style>

Eachofthesesectionsisresponsibleforexactlywhatyouarethinking.Putintothe<template>tagwhatevertheHTMLtemplateshouldberesponsiblefor,putintothe<script>tagtheJavaScriptcoderesponsiblefortheVuecomponent,methods,data,props,andsoon.The<style>tagshallcontaintheCSSstyleforthegivencomponent.

Doyourememberourhello-component?HavealookatitintheJSFiddleathttps://jsfiddle.net/chudaol/mf82ts9a/2/.

Startbyscaffoldingtheappusingthewebpack-simpleconfigurationwithvue-cli:

npminstall-gvue-clivueinitwebpack-simplehello

TorewriteitasaVuecomponent,wecreateourHelloComponent.vuefileandaddthefollowingcode:

<template>

<h1>{{msg}}</h1>

</template>

<script>

exportdefault{

data(){

return{

msg:'Hello!'

}

Page 148: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

}

}

</script>

NotethatwedonotneedtospecifythetemplateinourJavaScriptcomponentdefinition.Beingasingle-filecomponent,itisimplicitthatthetemplatethatshouldbeusedistheonedefinedinthisfile.YoumightalsohavenoticedthatweuseES6styleinhere.Also,donotforgetthatthedataattributeshouldbeafunctionandnotanobject.

Inourmainscript,wehavetocreatetheVueappandinstructittouseHelloComponent:

importVuefrom'vue'

importHelloComponentfrom'./HelloComponent.vue'

newVue({

el:'#app',

components:{HelloComponent}

});

Ourindex.htmlmarkupwillnotchange.Itwillstillinvokehello-component:

<body>

<divid="app">

<hello-component></hello-component>

</div>

<scriptsrc="./dist/build.js"></script>

</body>

Nowwejustneedtoinstallnpmdependencies(ifyoustillhaven'tdoneso)andbuildtheapplication:

npminstall

npmrundev

Onceyoudoit,yourbrowserwillautomaticallyopenthelocalhost:8080page!

Checkthecompletecodeinthechapter3/hellofolder.

Youcanalsotest,modify,retest,andcheckthehellocomponentinthewebpackbinathttp://www.webpackbin.com/N1LbBIsLb.

Tip

WebpackbinisaniceservicetorunandtestapplicationsbuiltwithWebpack.Itisaverynicetooleventhoughit'sstillinbeta.Asit'sstillyoung,itstillhassomeminorissues.Forinstance,ifyoutrytodownloadthepackageoftheentireproject,itwillnotbuild.

Page 149: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

PluginsforIDEsVuecreatorsandcontributorsthoughtaboutdevelopersanddevelopedpluginsforalargesetofmodernIDEs.Youcanfindthemathttps://github.com/vuejs/awesome-vue#syntax-highlighting.IfyouarelikemeanduseWebStormIDEbyIntelliJ,followtheseinstructionstoinstalltheVuesupportplugin:

1. GotoPreferences|Plugins.2. ClickonBrowserepositories.3. Typevueinthesearchbox.4. SelectVue.jsandclickontheInstallbutton:

InstallingtheVuepluginforwebstormIDE

Page 150: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

StyleandscopeItisprettyobviousthatthetemplateandthescriptofthecomponentbelongonlytoit.However,thesamedoesnotapplytostyle.Try,forexample,toaddastyletagtoourhellocomponentandaddtheCSSruleforthe<h1>tagtohavetheredcolor:

<style>

h1{

color:red;

}

</style>

Now,whenthepageisrefreshed,itisquiteexpectedthatthecoloroftheHello!headerchangestored.Nowtrytoaddthe<h1>tagtothemainindex.htmlfile.Youmightbesurprised,butitwillalsobered:

<divid="app">

<h1>Thisisasinglefilecomponentdemo</h1>

<hello-component></hello-component>

</div>

Allthe<h1>tagshavethestylethatwedefinedinsideacomponent

Tomakethestylebeattachedonlytothescopeofthecomponent,weneedtoindicatetheattributescopedtothe<style>tag:

<stylescoped>

h1{

color:red;

}

</style>

Lookatthepageandyou'llseethatonlytheHello!textisred,theotherh1hasitsdefaultstyle.

Page 151: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Hot-reloadingYoumighthavenoticedthatnowInolongeraskyoutorefreshthepagebuttolookatthepage.Thisisbecausethepageisautomaticallyrefreshedoneachchangewhentheapplicationisbootstrappedusingvue-cliWebpackscaffoldingapproach.Themagichappensthankstothevue-hot-reloadAPIthatwatchestheapplication'sfilesandtellsthebrowsertoautomaticallyreloadeverytimesomethinghaschanged!Yay!

Page 152: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

PreprocessorsIfyouareintopreprocessors,youaremorethanwelcometousetheminyour.vuecomponents.Thisispossibleduetovue-loaderthatallowsusingWebpackloaders.

Note

Youcanfindmoreaboutvue-loadersandpreprocessorsinthetutorialathttp://vue-loader.vuejs.org/en/.

HTMLpreprocessors

Inordertobeabletouseapreprocessorinasingle-fileVuecomponents,justaddthelangattributetothe<template>tag!Donotforgettoinstallthecorrespondingnodemodule:

npminstalljade--save-dev

Usingjade,forexample,inourhellocomponent'stemplate,wouldbeaseasyasfollows:

<templatelang="jade">

h1{{msg}}

</template>

CSSpreprocessors

ThesamelogicappliestotheCSSpreprocessors.Let'sseehowtouse,forexample,asasspreprocessor:

<stylelang="sass"scoped>

$red:red;

h1{

color:$red;

}

</style>

Tip

Likeinthepreviousexample,donotforgettoinstallthecorrespondingloaderforthistowork:npminstallsass-loadernode-sass--save-dev

JavaScriptpreprocessors

ItisalsopossibletouseanyJavaScriptpreprocessors.Likeinthetwopreviousexamples,justusethelangattributetospecifythepreprocessortouse.Anddonotforgettoinstallitvianpm!

>npminstallcoffee-loadercoffee-script--save-dev

<scriptlang="coffee">

exports.default=data:->

{msg:'Hello!'}

</script>

Page 153: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Rewritingourshoppinglistapplicationwithsingle-filecomponentsNowthatwealreadyknowsomuchaboutcomponentsandhowtousethem,andalsoknownicetechniquestomakeourcodeeasiertowrite,let'sgetbacktoourshoppinglistandrewriteitassingle-filecomponent'sVueapplication.Tohaveaneasysetup,wecanusevue-cliwithWebpackconfiguration.Actually,we'vealreadydoneitinChapter2,Fundamentals-InstallingandUsing.So,justfindthisapplicationandbepreparedtostartworkingonit.Ifyoucannotfindit,youcaneasilycreateit:

#installvue-cliifyoustillhadn'tinstalledit

$npminstallvue-cli-g

#bootstraptheapplication

$vueinitwebpackshopping-list

$cdshopping-list

$npminstall

$npmrundev

Ensurethatyourindex.htmlfilelookslikethefollowing:

<!DOCTYPEhtml>

<html>

<head>

<metacharset="utf-8">

<title>shopping-list</title>

<linkrel="stylesheet"

href="https://maxcdn.bootstrapcdn.com/bootstrap/

3.3.6/css/bootstrap.min.css">

</head>

<body>

<app></app>

</body>

</html>

Andyourmain.jsfileshouldlooklikethefollowing:

importVuefrom'vue'

importAppfrom'./App'

newVue({

el:'app',

components:{App}

})

Wearenowreadytocreateourcomponentsandtopopulateourapplicationwiththem.Ofcourse,yourememberthatourshoppinglisthasessentiallyfourcomponents:

AddItemComponent:Thecomponentresponsibleforaddinganewitemtotheshoppinglist

Page 154: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

ItemComponent:ThecomponentresponsiblefortherenderingofthenewitemintheshoppinglistitemslistItemsComponent:ThecomponentresponsiblefortherenderingandmanagingthelistofItemComponent

ChangeTitleComponent:Thecomponentresponsibleforchangingthetitleofthelist

Let'screatealloftheminthecomponentsfolder.Tostartwith,justincludethreeemptysections(<template>,<script>,and<style>)ineachofthemandinvoketheminthecorrectplaceswithinthemainApp.vuecomponent.Pleaseputsomethingintothetemplatethatwillallowustovisiblyidentifythedifferentcomponentsonthepage.So,thecodeofallourfourcomponentswilllooklikethefollowing:

Thecodeforallfourcomponentsoftheshoppinglistapplication

NowopentheApp.vuecomponent.Thisisourmaincomponentthatwillassembleallthecomponentstogether.

Removeeverythingfromthe<template>,<script>,and<style>tags.Wewillnowstarttobuildourapplication.

Firstofall,wemustimportthecomponentsthatwillbeusedbyApp.vue(inthiscase,allofthem).

Tip

Donotforgetthat,asweareusingES2015inthisapplication,wecanuseimport/exportandalltheotherbeautifulES2015things.

Insidethe<script>tag,let'simportthecomponentsandexporttheobjectthatwillcontaintheimportedcomponentsanddatafunctionthatreturnstheshoppinglist'sitems:

<script>

importAddItemComponentfrom'./components/AddItemComponent'

importItemsComponentfrom'./components/ItemsComponent'

importChangeTitleComponentfrom'./components/ChangeTitleComponent'

Page 155: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

exportdefault{

components:{

AddItemComponent,

ItemsComponent,

ChangeTitleComponent

},

data(){

return{

items:[{text:'Bananas',checked:true},

{text:'Apples',checked:false}]

}

},

methods:{

addItem(text){

this.items.push({

text:text,

checked:false

})

}

}

}

</script>

Ourtemplatecanbasicallybethesameasthetemplatethatwehavebuiltintheshoppinglistapplicationusingsimplecomponents.Let'sjustremoveeverythingconcerningthemodelsanddatabindingfornow.First,insertthecomponentresponsibleforaddingitems,thenthecomponentcontainingalltheitems,andthen,inthefooter,thecomponentresponsibleforchangingthetitle.

Ourtemplatewillthenlooklikethefollowing:

<template>

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

<h2>{{title}}</h2>

<add-item-component></add-item-component>

<items-component></items-component>

<divclass="footer">

<hr/>

<change-title-component></change-title-component>

</div>

</div>

</template>

Youstillrememberthatthenamesofthecomponents'variablesareCamelCased,andwhentheyareusedinsidethetemplate,theyshouldbeinvokedusingkebab-case,right?Good,let'sseehowitlooksinthebrowser:

Page 156: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Shoppinglistapplicationbuiltofsingle-filecomponents

Doesn'tseemthatbeautiful,right?Let'sfilleachofthecomponentswiththeirtemplates.

Tip

WewillcontinueusingBootstrap'sCSSstyleforthisapplication.Includeitgloballyintheindex.htmlfile:<linkrel="stylesheet"href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">

Page 157: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

AddItemComponentOpenAddItemComponent.vue.Let'sfillits<template>.Itwilllooklikethefollowing:

<template>

<div>

<divclass="input-group">

<inputtype="text"class="inputform-control"

placeholder="addshoppinglistitem">

<spanclass="input-group-btn">

<buttonclass="btnbtn-default"type="button">Add!</button>

</span>

</div>

</div>

</template>

Ifyoulookatthepageinyourbrowser,youcanalreadyseethatitchangedandbecamemorerecognizableasourshoppinglistapplication.

Page 158: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

ConfiguringItemComponentandItemsComponentLet'snowmovetotheItemComponent.WewilljustcopyandpastetheHTMLfromthesimplecomponentexample:

//ItemComponent.vue

<template>

<li:class="{'removed':item.checked}">

<divclass="checkbox">

<label>

<inputtype="checkbox"v-model="item.checked">{{item.text}}

</label>

</div>

</li>

</template>

Let'salsoaddsomescopedstyleforthiscomponent.Thiscomponent'sspecificstyleisthestylethathastodowiththe<li>,<span>,andclass.removed.Let'scopyandpastethemintothiscomponent:

//ItemComponent.vue

<stylescoped>

.removed{

color:gray;

}

.removedspan{

text-decoration:line-through;

}

li{

list-style-type:none;

}

lispan{

margin-left:5px;

}

</style>

NowopenItemsComponents.Asyouremember,itisalistoftheItemComponentelements.Evenifyoudonotremember,Iguessthatthepluralcharacteristicsofthenameofthiscomponentsuggeststhis.InorderforittobeabletousetheItemComponent,itmustimportitandregisterinthecomponentsproperty.So,let'smodifythescriptfirst:

//ItemsComponent.vue

<script>

importItemComponentfrom'./ItemComponent'

exportdefault{

components:{

ItemComponent

}

}

</script>

Nowyoucanuseitem-componentin<template>!Doyoustillrememberhowtoiteratewith

Page 159: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

vue.js?Ofcourseyoudo!Thatiswhyyouareopeningthe<template>tagrightnowandwritingthefollowingcode:

//temsComponent.vue

<template>

<div>

<item-componentv-for="iteminitems":item="item">

</item-component>

</div>

</template>

Ifyoucheckthepagenow,you'llbesurprisedtoseethatthingsactuallydonotwork.Thewebconsoleisfulloferrors.Canyoufigureoutwhy?

Doyourememberthatwhenchildrencomponentswanttohaveaccesstotheparent'sdata,theymustdeclare"props"onthecomponentinitialization?Thisisexactlywhatwe'veforgottenaboutonthedeclarationofbothItemsComponentandItemComponent.

Firstofall,withinApp.vue,binditemstotheitems-componentinvocation:

//App.vue

<items-component:items="items"></items-component>

ThenaddthepropsattributetoItemsComponent:

//ItemsComponent.vue

<script>

importItemComponentfrom'./ItemComponent'

exportdefault{

components:{

ItemComponent

},

props:['items']

}

</script>

NowgobacktoItemComponentandaddthepropsproperty:

//temComponent.vue

<script>

exportdefault{

props:['item']

}

</script>

Checkthepagenow.Nowitindeedcontainsthelistofitemsandhasalookandfeelalmostthesameasithadwhenwefirstcreatedit.Checkthefullcodeforthissectioninthechapter3/shopping-listfolder.

Page 160: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

ExerciseFinishtheshoppinglistapplicationsothatithasthesamefunctionalityasbefore.

There'snotsomuchleftandI'msureyouwillbedonewithitinlessthanhalfanhour.ThepossiblesolutiontothisexercisecanbefoundintheAppendix,SolutionstoExercises.

Page 161: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

RewritingthePomodoroapplicationwithsingle-filecomponentsIhopeyoustillrememberandpossiblyevenusethePomodoroapplicationthatwedevelopedinthefirstchapterofthisbook.

Iwouldliketorevisititnowandtodothesameexercisewedidintheprevioussection—definethecomponentsoftheapplicationandrewriteitusingthesecomponents.

Let'shavealookatourPomodoroapplication.AndnowIamgoingtospoilyou:I'llincludeascreenshotthatalreadycontainsthekittensthatarebeingshownduringtherestingtimeusinghttp://thecatapi.com/api:

ThePomodoroapplicationinitsRest!state

Therearesomeeasilyidentifiablecomponents:

Page 162: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Thecomponentofthecontrols(start,pause,end),let'snameitControlsComponentThecomponentofthetimecountdown,CowntdownComponentThecomponentofthetitleofthecurrentstate(Work!/Rest!),StateTitleComponentThecomponentofthekittensrenderingthatdependsonthestate(workingorresting),KittensComponent(thisismyfavoriteone!)

Now,pleasestopstaringatthekittenandlet'sstartimplementingourPomodoroapplicationusingsingle-filecomponents!Somefirststepstoscaffoldtheapplicationareasfollows:

1. StartbyopeningthescaffoldedPomodoroapplicationfromthepreviouschapterorcreateanewapplicationbasedontheWebpacktemplate.

2. Runnpminstallandnpmrundevintheapplicationfolder.3. Ensurethatyourindex.htmllookslikethefollowing:

<!DOCTYPEhtml>

<html>

<head>

<metacharset="utf-8">

<title>pomodoro</title>

</head>

<body>

<app></app>

</body>

</html>

4. Ensurethatyourmain.jsfilelookslikethefollowing:

importVuefrom'vue'

importAppfrom'./App'

/*eslint-disableno-new*/

newVue({

el:'app',

components:{App}

})

5. Openyourbrowsertothepagelocalhost:8080.6. Then,likeinthepreviousexample,gotothecomponentsfolderandcreateallthe

necessary.vuecomponents.7. GotoApp.vue,andimportandregisterallthecreatedcomponents.8. Inthe<template>sectionofeachofthecomponents,putsomethingthatwilluniquely

identifyitsothatwecaneasilyrecognizeitwhencheckingthepage.

Youwillalmostcertainlycometothestructureandtheinitialcode,whichlookssomethinglikethefollowing:

Page 163: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

TheveryinitialstateofthePomodoroapplicationimplementedwithsingle-filecomponents

Now,let'sassumethatourcomponentsarereadytouseandlet'splacethemwheretheybelongintotheapplication'slayout,accordingly.

Iwilljustslightlyremindyouhowthewholeapplication'smarkuplookedearlier:

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

<h2>

<span>Pomodoro</span>

//LookslikeourControlsComponent

<button>

<iclass="glyphiconglyphicon-play"></i>

</button>

<button>

<iclass="glyphiconglyphicon-pause"></i>

</button>

<button>

<iclass="glyphiconglyphicon-stop"></i>

</button>

</h2>

//LookslikeourStateTitleComponent

<h3>{{title}}</h3>

//LookslikeourCountdownComponent

<divclass="well">

<divclass="pomodoro-timer">

<span>{{min}}</span>:<span>{{sec}}</span>

Page 164: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

</div>

</div>

//LookslikeourKittensComponent

<divclass="well">

<img:src="catImgSrc"/>

</div>

</div>

You'veprobablynoticedthatIremovedsomepartsthatareresponsiblefortheclassbindingsoractionshandlers.Donotworry.RememberScarlettO'HarainGonewiththeWind?Sheusedtosay,

"Ican'tthinkaboutthatrightnow.I'llthinkaboutthattomorrow."

(http://goo.gl/InYm8e).ScarlettO'Harawasawisewoman.BelikeScarlettO'Hara.Fornow,wewillfocusmerelyonthe<template>tagforourApp.vue.Everythingelsewillcomelaterandwewillthinkaboutitthen.NowwecanbasicallycopyandpastethisHTMLsnippetandreplacethesectionsthatweidentify,suchasthecomponentswiththeirkebab-casenames.So,thetemplateinApp.vuewilllooklikethefollowing:

//App.vue

<template>

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

<h2>

<span>Pomodoro</span>

<controls-component></controls-component>

</h2>

<state-title-component></state-title-component>

<countdown-component></countdown-component>

<kittens-component></kittens-component>

</div>

</template>

Abitsmaller,huh?Checkyourbrowserwithyourappopened.NotverybeautifulandforsurehasnothingtodowithourPomodoroapplication,but...itworks!

Page 165: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Pomodoroapplicationbootstrappedasasingle-filecomponentsapplication

Whatshouldwedonow?Copythecorrespondingmarkuptotheircomponent's<template>sections.Pleasedothistinycopyandpastebyyourself,letitbeasmallhomeexercise.However,ifyouwanttocheckyourself,takealookatthechapter3/pomodorofolder.That'sitfornow!Allthedatabindingsandinterestingstuffwillcomeinthenextchapter.Sodonotclosethebook.However,donotforgettotakesomePomodoropauses.

Page 166: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

ReactivebindingofCSStransitionsJustbeforethetransitiontothenextchapter,whichwilltalkalotaboutdifferenttypesofdatabinding,Iwouldliketogiveyoujustatinyflavorofsomethinginterestingthatispossibletobind.Iknowthatyoupayalotofattentiontothewords,mydearreader.So,you'vealreadyfoundthewordtransitiontwotimesuntilnow,andyouhaveprobablyguessedthatwecanactuallybindCSStransitionstothedatachanges.

So,imaginethatyouhaveanelementthatshouldonlybeshownifthedataattributeshowistrue.Thisiseasy,right?Youalreadyknowthev-ifdirective:

<divv-if="show">hello</div>

Thus,whenevertheshowattributeischanged,this<div>behavesaccordingly.Imaginethatonhiding/showing,youwouldliketoapplysomeCSStransition.WithVueyoucanusethespecialtransitionwrappercomponenttospecifythetransitiontouseondatachanging:

<transitionname="fade">

<divv-if="show"transition="my">hello</div>

</transition>

Afterthat,youjusthavetodefineCSSrulesforthefade-enter,fade-leave,fade-enter-active,andfade-leave-activeclasses.ChecktheofficialVuedocumentationpageregardingtheseclassesathttps://vuejs.org/v2/guide/transitions.html#Transition-Classes.

Let'sseehowitworksinourkittenscomponentexample.Let'sstartbyaddingthev-ifdirectivetothekittens-componentinsideApp.vue:

<template>

<...>

<kittens-componentv-if="kittens"></kittens-component>

<...>

</template>

Also,weshouldaddthedatafunctioninthe<script>tagofApp.vue(let'salsomakeitglobalsothatwecanmodifyitfromthedevtoolsconsole):

<script>

//...//

window.data={

kittens:true

};

exportdefault{

//.....//

data(){

returnwindow.data

}

}

</script>

Page 167: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Lookatthebrowser:everythingseemsunchanged.Openthedevtoolsconsoleandtypethefollowing:

data.kittens=false

You'llseethatthekittenscomponentwilldisappearfromthepage.Ifyoutypethefollowing,itwillappearagain:

data.kittens=true

Tip

Ihopeyouhaven'tforgottentoincludeBootstrap'sCSSinthemainindex.htmlfile.Withoutit,you'llseenoappearing/disappearingatallbecauseour<div>taghasnoinformationnoranyclassappliedtoit:<linkrel="stylesheet"href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">

However,wearetalkingabouttheCSStransitionsandnotaboutsimplyhiding/showingstuff.Nowlet'sapplytheCSSfadetransitiontoourkittenscomponent.Justaddawrappercomponenttransitionwithanameattributefade:

<template>

<...>

<transitionname="fade">

<kittens-componentv-if="kittens"></kittens-component>

</transition>

<...>

</template>

Nowifwedefinenicerulestothecorrectclasses,we'llseeaniceCSStransition.Let'sdoit.AddthefollowingCSSrulesinsidethe<style>tag:

<stylescoped>

.fade-enter-active,.fade-leave-active{

transition:opacity.5s

}

.fade-enter,.fade-leave-active{

opacity:0

}

</style>

Lookatthepageagain.Opentheconsoleandtypedata.kittens=falseanddata.kittens=trueagain.Nowyoucanseeanicefadetransitionhappeningoneachdatachange.Inthenextchapter,wewilltalkmoreabouttransitionsinVue.jsandapplythemtoourapplications.

Page 168: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

SummaryInthischapter,youlearnedaboutVuecomponentsandhowtousethem.Yousawhowtocreateandregisterthemusingaclassicapproach(applicationthatusesHTML,CSS,andJavaScript)andyoualsosawhoweasyitistocreateandmanipulatethemusingasingle-filecomponentsapproach.Thingstoretain:

WhilevariablesarecreatedusingCamelCasedformat,inordertobeabletousecomponentsinsidetemplates,youmustapplythecorrespondingkebab-casedformat,forexample,MyBeautifulComponent->my-beautiful-componentAttributesdataandelinsidethecomponentmustbefunctionsandnotobjects:{data:function(){}}

Ifyouwantthestyleofthecomponentnottoleaktotheglobalscope,addascopedattributetoit:<stylescoped></style>

Wehavealsorewrittenourapplicationsusingsingle-filecomponentsandtouchedonthedatabindingtotheCSStransitionsslightly.

Inthenextchapter,wewilldivedeeplyintoallthetypesofdatabinding,includingCSSandJavaScripttransitions.Wewillbringourapplicationsbacktolifeusingdatabinding.Lastbutnotleast,wewillseemorecats!

Page 169: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Chapter4.Reactivity–BindingDatatoYourApplicationInthepreviouschapter,youlearnedoneofthemostimportantconceptsofVue.js:components.Yousawhowtocreatecomponents,howtoregister,howtoinvoke,andhowtouseandreusethem.Youalsolearnedtheconceptofsingle-filecomponentsandevenusedthemintheshoppinglistandPomodoroapplications.

Inthischapter,wewillgodeeperintotheconceptofdatabinding.Wehavealreadytalkedaboutitearlier,soyouarealreadyfamiliarwithit.Wewillbinddatainallpossiblewaysinourcomponents.

Summingitup,inthischapter,wearegoingto:

RevisitthedatabindingsyntaxApplydatabindinginourapplicationsIterateoverthearrayofelementsandrendereachelementusingthesametemplatewithdifferentdataRevisitandapplytheshorthandsofdataandeventsbindinginourapplications

Page 170: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

RevisitingdatabindingWehavebeentalkingaboutdatabindingandreactivitystartingfromtheveryfirstchapter.So,youalreadyknowthatdatabindingisamechanismofpropagatingchangesfromthedatatothevisiblelayerandviceversa.Inthischapter,wewillcarefullyrevisitallthedifferentwaysofdatabindingandapplytheminourapplications.

Page 171: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

InterpolatingdataLet'simaginethefollowingpieceofHTMLcode:

<divid="hello"></div>

Also,imaginethefollowingJavaScriptobject:

vardata={

msg:'Hello'

};

Howcanwerenderthevaluesofdataentriesonthepage?HowcanweaccessthemsothatwecanusetheminsideourHTML?Actually,wehavebeendoingthisalotwithVue.jsduringthelasttwochapters.Thereisnoprobleminunderstandinganddoingitagainandagain.

"Repetitioestmaterstudiorum"

Ifyouarealreadyaprofessionalofdatainterpolation,justskipthissectionandproceedtotheexpressionsandfilters.

So,whatshouldwedotopopulatethe<div>withthevalueofmsg?Ifwegotheold-fashionedjQueryway,wewouldprobablydosomethinglikethefollowing:

$("#hello").text(data.msg);

Butthen,duringruntime,ifyouchangethevalueofmsgandifyouwantthischangetobepropagatedtotheDOM,youmustdoitmanually.Bysimplychangingthedata.msgvalue,nothingwillhappen.

Forexample,let'swritethefollowingcode:

vardata={

msg:'Hello'

};

$('#hello').text(data.msg);

data.msg='Bye';

Thenthetextthatwillappearinthe<div>will,ofcourse,beHello.CheckthisJSFiddleathttps://jsfiddle.net/chudaol/uevnd0e4/.

WithVue,thesimplestinterpolationisdonewith{{}}(handlebarsannotation).Inourexample,wewouldwritethefollowingHTMLcode:

<divid="hello">{{msg}}</div>

Thecontentofthe<div>thusbecomesboundtothemsgdata.Eachtimemsgchanges,thecontentofthedivchangesautomaticallyfollowingitscontent.Havealookatthejsfiddle

Page 172: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

exampleathttps://jsfiddle.net/chudaol/xuvqotmq/1/.data.msgisalsochangedaftertheVueinstantiation.Thevaluethatappearsonthescreenisthenewone!

Itisstillone-waybindinginterpolation.IfwechangethevalueintheDOM,nothingwillhappentodata.Still,ifweonlyneedthevaluesofthedatatoappearintheDOMandtobechangedaccordingly,itisaperfectandvalidapproach.

Atthismoment,itshouldbereallyclearthatifwewanttousethevaluesofthedataobjectinsidethetemplate,weshouldsurroundthemwith{{}}.

Let'saddthemissinginterpolationstoourPomodoroapplication.Pleasecheckthecurrentsituationinthechapter4/pomodorofolder.Ifyourunnpmrundevandhavealookattheopenedpage,youwillseethatthepagelookslikethefollowing:

MissinginterpolationsinourPomodoroapplication

Fromtheveryfirstglanceatthepage,weareabletoidentifywhatismissingthere.

Thepageismissingthetimer,thekittens,thetitleofthePomodorostate(theonethatdisplaysWork!orRest!),andthelogicthatshowsorhidesthekittens'placeholderaccordingtothePomodorostate.Let'sstartbyaddingthetitleofthePomodorostateandtheminutesandsecondsofthePomodorotimer.

Page 173: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

AddingtitleofthePomodorostateFirstofall,weshoulddecidewhatcomponentthiselementshouldbelongto.Havealookatourfourcomponents.ItismorethanobviousthatitshouldbelongtoStateTitleComponent.Ifyoulookatthefollowingcode,youwillseethatitactuallyalreadyinterpolatesthetitleinitstemplate:

//StateTitleComponent.vue

<template>

<h3>{{title}}</h3>

</template>

<stylescoped>

</style>

<script>

</script>

Good!Inthepreviouschapter,we'vealreadydonemostofthework.Nowwejusthavetoaddthedatathatmustbeinterpolated.Inthe<script>tagofthiscomponent,let'saddthedataobjectwiththetitleattributeinside.Fornow,let'shardcodeittooneofthepossiblevaluesandthendecidehowtochangeit.Whatdoyouprefer?Work!orRest!?IthinkIknowtheanswer,solet'saddthefollowingcodetoourscripttag:

//StateTitleComponent.vue

<script>

exportdefault{

data(){

return{

title:'LearningVue.js!'

}

}

}

</script>

Let'sleaveitlikethisfornow.Wewillcomebacktothislaterinthemethodsandeventhandlingsection.

Page 174: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

ExerciseInthesamewayinwhichweaddedthetitleofthePomodorostate,pleaseaddtheminutesandsecondstimercounterstotheCountDownComponent.Theycanbehardcodedfornow.

Page 175: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

UsingexpressionsandfiltersInthepreviousexample,wehaveusedsimplepropertykeysinsidethe{{}}interpolations.Actually,Vuesupportsalotmoreinsidethesenicecurlybrackets.Let'sseewhatitispossibletodothere.

Page 176: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

ExpressionsItmightsoundunexpected,butVuesupportsfullJavaScriptexpressionsinsidethedatabindingbrackets!Let'sgotoanyofthePomodoroapplicationcomponentsandaddanyJavaScriptexpressiontothetemplate.Youcandosomeexperimentsinthechapter4/pomodoro2folder.

Try,forexample,toopentheStateTitleComponent.vuefile.Let'saddsomeJavaScriptexpressioninterpolationtoitstemplate,forexample:

{{Math.pow(5,2)}}

Actually,youjustneedtouncommentthefollowinglines:

//StateTitleComponent.vue

<!--<p>-->

<!--{{Math.pow(5,2)}}-->

<!--</p>-->

Youwillseenumber25onthepage.Nice,isn'tit?Let'sreplacesomeofourdatabindingsinthePomodoroapplicationwithaJavaScriptexpression.Forexample,intheCountdownComponentcomponent'stemplate,twodirectives,eachforminandsec,canbereplacedbyoneexpression.Currentlyitlooksasfollows:

//CountdownComponent.vue

<template>

<divclass="well">

<divclass="pomodoro-timer">

<span>{{min}}</span>:<span>{{sec}}</span>

</div>

</div>

</template>

Wecanreplaceitwiththefollowingcode:

//CountdownComponent.vue

<template>

<divclass="well">

<divclass="pomodoro-timer">

<span>{{min+':'+sec}}</span>

</div>

</div>

</template>

Whereelsecanweaddsomeexpressions?Let'shavealookatStateTitleComponent.Atthismoment,weusethehardcodedtitle.Weknow,however,thatsomehowitshoulddependonthePomodorostate.Ifitisintheworkingstate,itshoulddisplayWork!,otherwiseitshoulddisplayRest!.Let'screatethisattributeandcallitisworking,andlet'sassignittothemainApp.vuecomponentbecauseitseemstobelongtotheglobalapplicationstate.ThenwewillreuseitinsidetheStateTitleComponentcomponent'spropsattribute.Thus,openApp.vue,and

Page 177: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

addtheBooleanpropertyisworkingandsetittotrue:

//App.vue

<...>

window.data={

kittens:true,

isworking:true

};

exportdefault{

<...>

data(){

returnwindow.data

}

}

Let'snowreusethispropertyinStateTitleComponent,addtwostringpropertiesforeachofthepossibletitles,and,finally,addtheexpressioninthetemplatethatwillconditionallyrenderonetitleoranotheraccordinglytothecurrentstate.Thus,thescriptofthecomponentwilllooklikethefollowing:

//StateTitleComponent.vue

<script>

exportdefault{

data(){

return{

workingtitle:'Work!',

restingtitle:'Rest!'

}

},

props:['isworking']

}

</script>

Nowwecanconditionallyrenderonetitleoranotherbasedontheisworkingproperty.Thus,thetemplateofStateTitleComponentwilllooklikethefollowing:

<template>

<div>

<h3>

{{isworking?workingtitle:restingtitle}}

</h3>

</div>

</template>

Lookattherefreshedpage.Strangely,itshowsRest!asthetitle.HowdidthishappeniftheisworkingpropertyissettotrueinApp.vue?WesimplyforgottobindthispropertyonthecomponentinvocationintheApp.vuetemplate!OpentheApp.vuecomponentandaddthefollowingcodeonthestate-title-componentinvocation:

<state-title-componentv-bind:isworking="isworking"></state-title-component>

Now,ifyoulookatthepage,thecorrecttitleappearsasWork!Ifyouopenthedevtools

Page 178: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

consoleandtypedata.isworking=false,youwillseethetitlechanging.

Iftheisworkingattributeisfalse,thetitleisRest!,asshowninthefollowingscreenshot:

Iftheisworkingattributeistrue,thetitleisWork!,asshowninthefollowingscreenshot:

Page 179: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga
Page 180: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

FiltersBesidesexpressionsinsidethecurlyinterpolationbrackets,itisalsopossibletousefiltersthatareappliedtotheresultoftheexpression.Filtersarejustfunctions.Theyarecreatedbyusandappliedbyusingthepipesymbol:|.Ifyoucreateafilterthatmakeslettersuppercaseandcallituppercase,inordertoapplyit,justuseitafterthepipesymbolinsidethemustacheinterpolation:

<h3>{{title|lowercase}}</h3>

Youcanchainasmanyfiltersasyouwant,forexample,ifyouhavefilterA,B,C,youcandosomethinglike{{key|A|B|C}}.FiltersarecreatedusingVue.filtersyntax.Let'screateourlowercasefilter:

//main.js

Vue.filter('lowercase',(key)=>{

returnkey.toLowerCase()

})

Let'sapplyittothePomodorotitleinthemainApp.vuecomponent.Inordertobeabletousethefilter,weshouldpassthe'Pomodoro'stringinsidethehandlebarsinterpolationnotation.WeshouldpassitasaJavaScriptstringexpressionandapplyafilterusingthepipesymbol:

<template>

<...>

<h2>

<span>{{'Pomodoro'|lowercase}}</span>

<controls-component></controls-component>

</h2>

<...>

</template>

Checkthepage;thePomodorotitlewillactuallyappearwritteninthelowercasesyntax.

Let'srevisitourCountdownTimercomponentandhavealookatthetimer.Fornow,thereareonlyhardcodedvalues,right?Butwhentheapplicationisfullyfunctional,thevalueswillcomefromsomecomputation.Therangeofvalueswillbefrom0to60.Itisokayifthetimershows20:40,butitisnotokayforfewerthantenvalues.Forexample,whenitisonly1minuteand5seconds,itwillbe1:5,whichisnotgood.Weareexpectingtoseesomethinglike01:05.So,weneedtheleftpadfilter!Let'screateit.

Gotothemain.jsfileandaddaleftpadfilteraftertheuppercasefilterdefinition:

//main.js

Vue.filter('leftpad',(value)=>{

if(value>=10){

returnvalue

}

return'0'+value

})

Page 181: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

OpentheCountdownComponentcomponentandlet'sagainsplitminandsectothedifferentinterpolationbracketsandaddfilterstoeachofthem:

//CountdownComponent.vue

<template>

<divclass="well">

<divclass="pomodoro-timer">

<span>{{min|leftpad}}:{{sec|leftpad}}</span>

</div>

</div>

</template>

Replaceminandsecindatawith1and5,respectively,andhavealook.Thenumbersappearwithapreceding"0"!

Page 182: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

ExerciseCreatetwofilters,uppercaseandaddspace,andapplythemtothetitlePomodoro:

TheuppercasefiltermustdoexactlywhatitsaysitdoesTheaddspacefiltermustaddaspaceontherightofthegivenstringvalue

DonotforgetthatPomodoroisnotakey,soinsidetheinterpolationbrackets,itshouldbetreatedasastring!Thetitlebeforeandafterthisexercisewouldlooksomethinglikethefollowing:

ThetitleofthePomodoroapplicationbeforeandafterapplyingfiltersuppercaseandaddspace

Checkyourself:havealookatthechapter4/pomodoro3folder.

Page 183: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

RevisitingandapplyingdirectivesIntheprevioussection,wesawhowtointerpolatetheapplication'sdataandhowtobindittothevisuallayer.Thoughthesyntaxisprettypowerfulandoffersahighpossibilityofdatamodification(usingfiltersandexpressions),ithassomelimitations.Try,forexample,toimplementthefollowingusing{{}}notation:

UsetheinterpolateddataintheuserinputandapplythechangestothecorrespondingdatawhentheusertypesintheinputBindaspecificelement'sattribute(forexample,src)tothedataRendersomeelementconditionallyIteratethroughanarrayandrendersomecomponentwiththeelementsofthearrayCreateeventlistenersontheelements

Let'stryatleastthefirstone.Open,forexample,theshoppinglistapplication(it'sinthechapter4/shopping-listfolder).CreateaninputelementintheApp.vuetemplateandsetitsvalueto{{title}}:

<template>

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

<h2>{{title}}</h2>

<inputtype="text"value="{{title}}">

<add-item-component></add-item-component>

<...>

</div>

</template>

Ohno!Errors,errorseverywhere.Interpolationinsideattributeshasbeenremoved,itsays.DoesitmeanthatpriortoVue2.0youcouldeasilyusetheinterpolationinsideattributes?Yes,andno.Youwouldnotgetanerrorifyou'duseinterpolationsinsideattributes,butchangingthetitleinsidetheinputwouldresultinnothing.InVue2.0,aswellasinthepriorversions,toachievethiskindofbehavior,wemustusedirectives.

Note

Directivesarespecialattributesoftheelementsthathaveav-prefix.Whyv-?BecauseVue!Directivesprovideatinysyntaxthatprovidesarichersetofpossibilitiesthansimpletextinterpolation.Theyhavethepowertoreactivelyapplysomespecialbehaviortothevisuallayeroneachdatachange.

Page 184: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Two-waybindingusingthev-modeldirectiveTwo-waybindingisatypeofbindingwherenotonlydatachangesarepropagatedtotheDOMlayer,butalsothechangesthatoccurtothebounddataintheDOMarepropagatedtothedata.TobindthedatainsuchawaytotheDOM,wecanusethev-modeldirective.

Iamsureyoustillrememberfromthefirstchapterthatthev-modeldirectiveisusedasfollows:

<inputtype="text"v-model="title">

Inthisway,thevalueofthetitlewillappearintheinput,andifyoutypesomethinginthisinput,thecorrespondingchangewillbeimmediatelyappliedtothedataandreflectedinallinterpolatedvaluesonthepage.

Justreplacethehandlebarsnotationwithv-modelandopenthepage.

Trytotypesomethingintheinput.Youwillseehowthetitleisimmediatelychanged!

Justremember,thisdirectivecanonlybeusedwiththefollowingelements:

<input>

<select>

<textarea>

Tryallofthemandthendeletethiscode.Ourmainpurposeistobeabletochangethetitleusingthechangetitlecomponent.

Page 185: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Two-waybindingbetweencomponentsRememberfromthepreviouschapterthattwo-waybindingbetweencomponentscannotbeeasilyachievedusingthev-modeldirective.Duetoarchitecturalreasons,Vuejustpreventschildrenfromeasilychangingtheparents'scope.

That'swhyweusedtheeventssysteminthepreviouschaptertobeabletochangethetitleoftheshoppinglistfromthechildcomponent.

Wewilldoitagaininthischapter.Justwaitcoupleofparagraphsuntilwereachthesectiononv-ondirectives.

Page 186: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Bindingattributesusingthev-binddirectiveThev-binddirectiveallowsustobindanelement'sattributeoracomponentpropertytoanexpression.Inordertoapplyittothespecificattribute,weuseacolondelimiter:

v-bind:attribute

Forexample:

v-bind:src="src"

v-bind:class="className"

Anyexpressioncanbewritteninsidethe"".Thedatapropertiescanbeusedaswell,justlikeinthepreviousexamples.Let'saddthekittenimagetoKittenComponentinourPomodoroapplicationusingthecatapiasthesource.OpenourPomodoroapplicationfromthechapter4/pomodoro3folder.

OpenKittenComponent,addcatimgsrctothecomponent'sdata,andbindittotheimagetemplateusingv-bindsyntaxwiththesrcattribute:

<template>

<divclass="well">

<imgv-bind:src="catImgSrc"/>

</div>

</template>

<stylescoped>

</style>

<script>

exportdefault{

data(){

return{

catimgsrc:"http://thecatapi.com/api/images/get?size=med"

}

}

}

</script>

Openthepage.Enjoythekitten!

Page 187: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

PomodoroKittenComponentwithappliedsourceattribute

Page 188: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Conditionalrenderingusingv-ifandv-showdirectivesIfyouhavepaidenoughattentionintheearliersections,andifIaskyoutoconditionallyrendersomething,youmightbeactuallyabletodoitusingJavaScriptexpressionsinsidetheinterpolationbrackets{{}}.

However,trytoconditionallyrendersomeelementorthewholecomponent.Itmightnotbeassimpleasapplyinganexpressioninsidethebrackets.

Thev-ifdirectiveallowstoconditionallyrenderthewholeelement,whichmightalsobeacomponentelementdependingonsomecondition.Theconditioncanbeanyexpressionanditcanusethedatapropertiesaswell.Forexample,wecandothefollowing:

<divv-if="1<5">hello</div>

Or:

<divv-if="Math.random()*10<6">hello</div>

Oreven:

<divv-if="newDate().getHours()>=16">BeerTime!</div>

Orusingthecomponent'sdata:

<template>

<div>

<h1v-if="!isadmin">BeerTime!</h1>

</div>

</template>

<script>

exportdefault{

data(){

return{

isadmin:false

}

}

}

</script>

Thev-showattributedoesthesamejob.Theonlydifferenceisthatv-ifwillorwillnotrendertheelementtotheconditionaccordingly,whereasthev-showattributewillalwaysrendertheelement,justapplyingdisplay:noneCSSpropertywhentheresultoftheconditionisfalse.Let'sseethedifference.Openthebeer-timeprojectinthechapter4/beer-timefolder.Runnpminstallandnpmrundev.OpentheApp.vuecomponentandplaywithtrue/falsevalues,andtrytoreplacev-ifwithv-show.Opendevtoolsandchecktheelementstab.

Let'sfirstcheckhowitlookswhenweswitchbetweentrueandfalseintheisadminpropertyvalueusingv-if.

Page 189: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Whentheconditionismet,everythingappearsasexpected;theelementisrenderedandappearsonthepage:

Conditionalrenderingusingthev-ifdirective.Conditionismet.

Whentheconditionisnotmet,theelementisnotrendered:

Page 190: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Conditionalrenderingusingthev-ifdirective.Conditionisnotmet.

Notethatwhentheconditionisnotfulfilled,thecorrespondingelementisnotrenderedatall!

Let'splaywiththeconditionresultvalueusingthev-showdirective.Whentheconditionismet,itappearsinexactlythesamewayasitwasinthepreviouscaseusingv-if:

Conditionalrenderingusingthev-showdirective.Conditionismet.

Nowlet'scheckwhatwillhappenwiththeelementusingthev-showdirectivewhentherightconditionisnotmet:

Page 191: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Conditionalrenderingusingthev-showdirective.Conditionisnotmet.

Inthiscase,everythingisthesamewhentheconditionismet,butwhentheconditionisnotfulfilled,theelementisrenderedaswellwiththedisplay:noneCSSproperty.

Howdoyoudecidewhichoneisbettertouse?Onthefirstrender,iftheconditionisnotmet,thev-ifdirectivewillnotrendertheelementatall,hencereducingthecomputationcostsontheinitialrendering.But,ifthepropertychangesfrequentlyduringruntime,thecostofrendering/removinganelementishigherthanjusttoapplythedisplay:noneproperty.Thus,usev-showwithfrequentlychangingpropertiesandv-ififtheconditionwillnotchangetoomuchduringruntime.

Let'scomebacktoourPomodoroapplication.KittensComponentshouldbeconditionallyrenderedwhenPomodoroisnotinitsworkingstate.So,openyourPomodoroapplicationcodeinthechapter4/pomodoro4folder.

Whatdoyouthinkshouldbeused?v-iforv-show?Let'sanalyze.Independentlyfromwhatweuse,shouldthiselementbevisibleontheinitialrender?Theanswerisno,becauseontheinitialrender,theuserstartsherworkingdayandstartsthePomodorotimer.Itmightbebettertousev-iftonothavethecostofinitialrenderingwhenthereisnoneed.But,let'sanalyzeanotherfactor—thefrequencyoftogglingthestatethatwillmakethekittenscomponentvisible/invisible.ThiswillhappenateachPomodorointerval,right?After15-20minutesofworkandthenafter5minutesofrestinterval,whichis,actually,notsofrequentandwillnotaffectthecostofrenderingthatmuch.Inthiscase,inmyopinion,itdoesn'tmatterwhichoneyouuse.Let'susev-show.OpentheApp.vuefileandapplythev-showdirectivetothe

Page 192: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

kittens-componentinvocation:

<template>

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

<...>

<transitionname="fade">

<kittens-componentv-show="!isworking"></kittens-component>

</transition>

</div>

</template>

Openthepageandtrytotogglethevalueofdata.isworkinginthedevtoolsconsole.Youwillseehowthekittenscontainerappearsanddisappears.

Page 193: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Arrayiterationusingthev-fordirectiveYouprobablyrememberthatarrayiterationisdoneusingthev-fordirectivewiththefollowingsyntax:

<divv-foriteminitems>

item

</div>

Orwithcomponents:

<componentv-foriteminitemsv-bind:componentitem="item"></component>

Foreachiteminthearray,thiswillrenderacomponentandbindthecomponent'sitempropertytothevalueoftheitem.Ofcourse,yourememberthatinsidethe""ofthebindingsyntaxyoucanusewhateverJavaScriptexpressionyouwant.So,justbecreative!

Tip

Donotforgetthatthepropertyweuseinthebindingsyntax(componentitem)shouldbepresentinthecomponent'sdata!

Havealook,forexample,atourshoppinglistapplication(Thechapter4/shopping-listfolder).Italreadyusesthev-forsyntaxinItemsComponenttorenderthelistofitems:

<template>

<ul>

<item-componentv-for="iteminitems":item="item"></item-component>

</ul>

</template>

ItemComponent,inturn,hastheitempropertydeclaredusingprops:

<script>

exportdefault{

props:['item']

}

</script>

Now,let'sdosomethinginterestingwithourshoppinglistapplication.Untilnowweweredealingonlywithoneshoppinglist.Imaginethatyouwanttohaveadifferentshoppinglistfordifferentkindofshopping.Forexample,youmighthavearegularshoppinglistforthenormalgroceriesshoppingday.Youmighthaveadifferentshoppinglistfortheholidays.Youmightalsowanttohaveadifferentshoppinglistwhenyoubuyanewhouse.Let'susethepowerofthereusabilityoftheVuecomponentsandtransformourshoppinglistapplicationintothelistofshoppinglists!WewilldisplaythemusingBootstrap'stabpanel;formoreinformation,refertohttp://getbootstrap.com/javascript/#tabs.

OpenyourshoppinglistapplicationintheIDE(thechapter4/shopping-listfolder).

Page 194: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Firstofall,weshouldaddBootstrap'sJavaScriptfileandjQuery,becausebootstrapreliesonitfordoingitsamazingmagic.Goonandjustaddthemmanuallytotheindex.htmlfile:

<body>

<...>

<scriptsrc="https://code.jquery.com/jquery-3.1.0.js"></script>

<script

src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js">

</script>

<...>

</body>

Now,let'sestablishastep-by-stepoverviewofwhatweshoulddoinordertotransformourapplicationintothelistofshoppinglists:

1. Firstofall,wemustcreateanewcomponent.Let'scallitShoppingListComponentandmovethecontentofourcurrentApp.vuetothere.

2. OurnewShoppingListComponentshouldcontainthepropsattributewithtitleanditemsthatitwillreceivefromApp.vue.

3. ItemsComponentshouldreceiveitemsfromthepropsattributeratherthanhavingithardcoded.

4. InAppcomponent'sdata,let'sdeclareandhardcode(fornow)anarrayofshoppinglists,eachoftheitemsshouldhaveatitle,anarrayofitems,andanID.

5. App.vueshouldimportShoppingListComponent,andinthetemplate,iterateovertheshoppinglistsarray,andforeachofthem,buildthehtml/jadestructureofthetabspanelforeachoftheshoppinglists.

Okay,then,let'sstart!

CreatingShoppingListComponentandmodifyingItemsComponent

Insidethecomponentsfolder,createanewShoppingListComponent.vue.CopyandpastetheApp.vuefile'scontentintothisnewfile.Donotforgettodeclarepropsthatwillcontaintitleanditemsandbinditemstotheitems-componentinvocationinsidethetemplate.Yourfinalcodeforthiscomponentshouldlooksomethinglikethefollowing:

//ShoppingListComponent.vue

<template>

<div>

<h2>{{title}}</h2>

<add-item-component></add-item-component>

<items-componentv-bind:items="items"></items-component>

<divclass="footer">

<hr/>

<change-title-component></change-title-component>

</div>

</div>

</template>

<script>

Page 195: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

importAddItemComponentfrom'./AddItemComponent'

importItemsComponentfrom'./ItemsComponent'

importChangeTitleComponentfrom'./ChangeTitleComponent'

exportdefault{

components:{

AddItemComponent,

ItemsComponent,

ChangeTitleComponent

}

props:['title','items']

}

</script>

<stylescoped>

.footer{

font-size:0.7em;

margin-top:20vh;

}

</style>

Notethatweremovedthestylingforthecontainerandthecontainer'sclassfromtheparentdiv.ThispartofthecodeshouldstayinApp.vuebecauseitdefinestheglobalapplication'scontainerstyling.Donotforgetaboutthepropsattributeandbindingpropstoitems-component!

OpenItemsComponent.vueandensurethatitcontainsthepropsattributewithitems:

<script>

<...>

exportdefault{

props:['items'],

<...>

}

</script>

ModifyingApp.vue

NowgotoApp.vue.Removeallthecodeinsidethe<script>and<template>tags.Inthescripttag,importShoppingListComponentandinvokeitinsidethecomponentsproperty:

//App.vue

<script>

importShoppingListComponentfrom'./components/ShoppingListComponent'

exportdefault{

components:{

ShoppingListComponent

}

}

</script>

Addadataattributeandcreateashoppinglistsarraythere.Addarbitrarydataforthisarray.Eachoftheobjectsofthearrayshouldhaveid,title,anditemsattributes.items,asyou

Page 196: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

remember,mustcontainthecheckedandtextproperties.Forexample,yourdatapropertymightlooklikethefollowing:

//App.vue

<script>

importShoppingListComponentfrom'./components/ShoppingListComponent'

exportdefault{

components:{

ShoppingListComponent

},

data(){

return{

shoppinglists:[

{

id:'groceries',

title:'Groceries',

items:[{text:'Bananas',checked:true},

{text:'Apples',checked:false}]

},

{

id:'clothes',

title:'Clothes',

items:[{text:'blackdress',checked:false},

{text:'allstars',checked:false}]

}

]

}

}

}

</script>

Bemorecreativethanme:addmorelists,moreitems,somethingniceandinteresting!

Let'snowcreateastructureforcomposingthebootstraptabpanelbasedoniterationovertheshoppinglist!Let'sstartbydefiningabasicstructureneededfortabstowork.Let'saddallthenecessaryclassesandjadestructurepretendingthatwehaveonlyoneelement.Let'salsowriteinCapsLockalltheunknownsthatwillbereusedfromourshoppinglistarray:

//App.vue

<template>

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

<ulclass="navnav-tabs"role="tablist">

<lirole="presentation">

<ahref="ID"aria-controls="ID"role="tab"data-toggle="tab">TITLE</a>

</li>

</ul>

<divclass="tab-content">

<divclass="tab-pane"role="tabpanel"id="ID">

SHOPPINGLISTCOMPONENT

</div>

</div>

</div>

</template>

Page 197: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Therearetwoelementswhereweneedtoiterateovertheshoppinglistsarray—the<li>tagthatcontainsan<a>attributeandthetab-panediv.Inthefirstcase,wemustbindtheIDofeachshoppinglisttothehrefandaria-controlsattributeandinterpolatethetitle.Inthesecondcase,weneedtobindtheidattributetotheidpropertyandrendertheshoppinglistitemandbindtheitemsarrayandtitletoit.Easy!Let'sgo.Startbyaddingthev-fordirectivetoeachoftheelements(tothe<li>andtothetab-panedivelement):

//App.vue

<template>

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

<ulclass="navnav-tabs"role="tablist">

<liv-for="listinshoppinglists"role="presentation">

<ahref="ID"aria-controls="ID"role="tab"data-

toggle="tab">TITLE</a>

</li>

</ul>

<divclass="tab-content">

<divv-for="listinshoppinglists"class="tab-pane"

role="tabpanel"

id="ID">

SHOPPINGLISTCOMPONENT

</div>

</div>

</div>

</template>

NowreplacethepartsinCapsLockwiththeproperbindings.Rememberthattothebindattribute,weusethev-bind:<corresponding_attribute>="expression"syntax.

Forthehrefattributeoftheanchorelement,wehavetodefineanexpressionthatappendstheIDselector#toid:v-bind:href="'#'+list.id".Thearia-controlsattributeshouldbeboundtothevalueoftheID.titlecanbeboundusingthesimple{{}}notationinterpolation.

Forshopping-list-component,wemustbindtitleanditemstothecorrespondingvaluesofthelistitem.DoyourememberthatwedefinedthetitleanditemspropertiesinsidethepropsoftheShoppingListComponent?Thebindings,thus,shouldlooklikev-bind:title=list.titleandv-bind:items=list.items.

Soaftertheproperbindingattribution,thetemplatewilllooklikethefollowing:

//App.vue

<template>

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

<ulclass="navnav-tabs"role="tablist">

<liv-for="listinshoppinglists"role="presentation">

<av-bind:href="'#'+list.id"v-bind:aria-controls="list.id"

role="tab"data-toggle="tab">{{list.title}}</a>

</li>

</ul>

<divclass="tab-content">

Page 198: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

<divv-for="listinshoppinglists"class="tab-pane"role="tabpanel"

v-bind:id="list.id">

<shopping-list-componentv-bind:

v-bind:items="list.items"></shopping-list-component>

</div>

</div>

</div>

</template>

We'realmostdone!Ifyouopenthepagenow,youwillseebothofthetitlesofthetabsappearingonthepage:

Tabtitlesasseenonthescreenafterthemodification

Ifyoustartclickingonthetabstitles,thecorrespondingtabpaneswillopen.Butthisisnotwhatwewereexpectingtosee,right?Whatwewereexpectingisforthefirsttabtobevisible(active)bydefault.Forthistohappen,weshouldaddtheactiveclasstothefirstliandtothefirsttab-panediv.Buthowcanwedoitifthecodeisthesameforallthetabsaslongasweareiteratingthroughthearray?

Fortunately,forus,Vueallowsustoprovidenotonlytheiterationiteminsidethev-forloop,butalsoindex,andthenreusethisindexvariableinsidetheexpressionsusedinthetemplates.Thus,wecanuseittoconditionallyrendertheactiveclassiftheindexis"0".Usingtheindexvariableinsidethev-forloopisaseasyasthefollowing:

v-for="(list,index)inshoppinglists"

Thesyntaxforclassbindingisthesameasforeverythingelse(classisalsoanattribute):

v-bind:class="active"

DoyourememberthatwecanwriteanyJavaScriptexpressioninsidethequotes?Inthiscase,wewanttowriteaconditionthatevaluatesthevalueofindex,andincaseitis"0",thevalueofclassisactive:

v-bind:class="index===0?'active':''"

Addtheindexvariabletothev-formodifiersandtheclassbindingstotheliandtothetab-paneelement,sothatthefinaltemplatecodelookslikefollowing:

<template>

Page 199: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

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

<ulclass="navnav-tabs"role="tablist">

<liv-bind:class="index===0?'active':

''"v-for="(list,index)inshoppinglists"role="presentation">

<av-bind:href="'#'+list.id"v-bind:aria-controls="list.id"

role="tab"data-toggle="tab">{{list.title}}</a>

</li>

</ul>

<divclass="tab-content">

<divv-bind:class="index===0?'active':''"

v-for="(list,index)inshoppinglists"class="tab-pane"

role="tabpanel"v-bind:id="list.id">

<shopping-list-componentv-bind:

v-bind:items="list.items"></shopping-list-component>

</div>

</div>

</div>

</template>

Lookatthepage.Nowyoushouldseenicetabsthatdisplaythecontentbydefault:

Thelookandfeeloftheshoppinglistapplicationafterthecorrectclassbinding

Thefinalshoppinglistapplicationcodeafterthesemodificationscanbefoundinthechapter4/shopping-list2folder.

Page 200: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Eventlistenersusingthev-ondirectiveItisveryeasytolistentotheeventsandcallcallbacksusingVue.js.Eventlisteningisalsodoneusingaspecialdirectivewithspecificmodifiersforeachoftheeventtypes.Thedirectiveisv-on.Themodifiersareappliedafterthecolon:

v-on:click="myMethod"

Ok,yousay,andwheredoIdeclarethismethod?Youwillprobablynotbelieveme,butallthecomponent'smethodsaredeclaredinsidethemethodsproperty!So,todeclarethemethodcalledmyMethod,youshoulddothefollowing:

<script>

exportdefault{

methods:{

myMethod(){

//dosomethingnice

}

}

}

</script>

Allthedataandpropsattributesareaccessibleinsidethemethodsusingthethiskeyword.

Let'saddamethodtoaddanewitemtotheitemsarray.Wehaveactuallydoneitalreadyinthepreviouschapter,whenwelearnedhowtopassdatabetweenparentandchildrencomponentsusingtheeventsemittingsystem.Wewilljustrecapthisparthere.

InordertobeabletoaddnewitemswithinAddItemComponenttotheshoppinglistthatbelongstoShoppingListComponent,weshoulddothefollowing:

EnsurethatAddItemComponenthasadatapropertycallednewItem.CreateanaddItemmethodinsidetheAddItemComponentthatpushesthenewItemandemitstheeventadd.ApplyaneventlistenertotheAdd!buttonusingthev-on:clickdirective.ThiseventlistenershouldcallthedefinedaddItemmethod.CreateanaddItemmethodinsidetheShoppingListComponentthatwillreceivethetextasaparameterandpushittotheitemsarray.Bindthev-ondirectivewithacustomaddmodifiertotheadd-item-componentinvocationinsidetheShoppingListComponent.ThislistenerwillcalltheaddItemmethoddefinedinthiscomponent.

Let'sgothen!Usetheshoppinglistapplicationfromthechapter4/shopping-list2folderandplaywithit.

StartbyopeningAddItemComponentandaddthemissingv-ondirectivetotheAdd!buttonandtheaddItemmethod:

Page 201: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

//AddItemComponent.vue

<template>

<divclass="input-group">

<inputtype="text"v-model="newItem"

placeholder="addshoppinglistitem"class="form-control">

<spanclass="input-group-btn">

<buttonv-on:click="addItem"class="btnbtn-default"

type="button">Add!</button>

</span>

</div>

</template>

<script>

exportdefault{

data(){

return{

newItem:''

}

},

methods:{

addItem(){

vartext

text=this.newItem.trim()

if(text){

this.$emit('add',this.newItem)

this.newItem=''

}

}

}

}

</script>

SwitchtoShoppingListComponentandbindthev-on:adddirectivetotheinvocationofadd-item-componentinsidethetemplatetag:

//ShoppingListComponent.vue

<template>

<div>

<h2>{{title}}</h2>

<add-item-componentv-on:add="addItem"></add-item-component>

<items-componentv-bind:items="items"></items-component>

<divclass="footer">

<hr/>

<change-title-component></change-title-component>

</div>

</div>

</template>

NowcreatetheaddItemmethodinsidetheShoppingListComponent.Itshouldreceivethetextandjustpushitintothethis.itemsarray:

//ShoppingListComponent.vue

<script>

importAddItemComponentfrom'./AddItemComponent'

Page 202: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

importItemsComponentfrom'./ItemsComponent'

importChangeTitleComponentfrom'./ChangeTitleComponent'

exportdefault{

components:{

AddItemComponent,

ItemsComponent,

ChangeTitleComponent

},

props:['title','items'],

methods:{

addItem(text){

this.items.push({

text:text,

checked:false

})

}

}

}

</script>

Openthepageandtrytoaddtheitemstothelistbytypingintheinputboxandclickingthebuttonafterward.Itworks!

Now,Iwouldliketoaskyoutoswitchyourrolefromtheapplication'sdevelopertoitsuser.Typethenewitemintheinputbox.Whatistheobvioususeractionaftertheitemhasbeenintroduced?Aren'tyoutryingtohittheEnterbutton?Ibetyouare!Whennothingishappening,itisalittlebitfrustrating,isn'tit?Don'tworry,myfriend,wejusthavetoaddonemoreeventlistenertotheinputboxandcallthesamemethodaswedidwiththeAdd!button.

Soundseasy,right?Whateventisfiredwhenwe'rehittingtheEnterbutton?Right,itisthekeyupevent.So,wejusthavetousethev-ondirectivewiththekeyupmethodafterthedelimitercolon:v-on:keyup.Theproblemisthatthiseventisfiredwhenanykeyboardbuttonishit,whichmeansthatwhilewe'retypingthenewshoppinglistitem,eachtimethenewletterisbeingintroduced,themethodwillbecalled.Thisisnotwhatwewant.Ofcourse,wecouldaddaconditioninsideouraddItemmethodthatwouldcheckfortheevent.codeattributeand,onlyincaseit's13(whichcorrespondstotheEnterkey),wewouldcalltherestofthemethod.Fortunately,forus,Vueprovidesamechanismtoprovidekeystrokemodifierstothismethodthatallowsustoonlycallamethodifacertainkeycodewashit.Itshouldbeimplementedusingthedot(.)modifier.Inourcase,itisasfollows:

v-on:keyup.enter

Let'saddittoourinputbox.GotoAddItemComponentandaddthev-on:keyup.enterdirectivetotheinputasfollows:

<template>

<divclass="input-group">

<inputtype="text"v-on:keyup.enter="addItem"v-model="newItem"

placeholder="addshoppinglistitem"class="form-control">

<spanclass="input-group-btn">

Page 203: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

<buttonv-on:click="addItem"class="btnbtn-default"

type="button">Add!</button>

</span>

</div>

</template>

OpenthepageandtrytoaddtheitemtotheshoppinglistusingtheEnterbutton.Itworks!

Let'sdothesamefortitlechanging.Theonlydifferenceisthattheaddingitems,weusedacustomaddeventandherewewillusethenativeinputevent.Wehavealreadydoneit.Wejusthavetoperformthefollowingsteps:

1. Bindthemodeltitleusingthev-modeldirectivetochange-title-componentinthetemplateoftheShoppingListComponent.

2. ExportvalueinthepropsattributeoftheChangeTitleComponent.3. CreateanonInputmethodinsidetheChangeTitleComponentthatwillemitthenative

inputmethodwiththevalueoftheeventtarget.4. BindvaluetoinputinsidetheChangeTitleComponentcomponent'stemplateandthev-

ondirectivewiththeonInputmodifier.

Thus,thechange-title-componentinvocationinsidetheShoppingListComponenttemplatewilllooklikethefollowing:

//ShoppingListComponent.vue

<change-title-componentv-model="title"></change-title-component>

ChangeTitleComponentwilllooklikethefollowing:

//ChangeTitleComponent.vue

<template>

<div>

<em>Changethetitleofyourshoppinglisthere</em>

<inputv-bind:value="value"v-on:input="onInput"/>

</div>

</template>

<script>

exportdefault{

props:['value'],

methods:{

onInput(event){

this.$emit('input',event.target.value)

}

}

}

</script>

Thefinalcodeforthispartcanbefoundinthechapter4/shopping-list3folder.

Page 204: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

ShorthandsOfcourse,itisnottimeconsumingtowritethev-bindorv-ondirectiveinthecodeeachtime.Developerstendtothinkthateachtimewedecreasetheamountofcode,wewin.Vue.jsallowsustowin!Justrememberthattheshorthandforthev-binddirectiveisacolon(:)andtheshorthandforthev-ondirectiveisthe@symbol.Thismeansthatthefollowingcodedoesthesamething:

v-bind:items="items":items="items"

v-bind:class='$index===0?"active":""'

:class='$index===0?"active":""'

v-on:keyup.enter="addItem"@keyup.enter="addItem"

Page 205: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

ExerciseRewriteallthev-bindandv-ondirectivesintheshoppinglistapplicationusingtheshortcutswejustlearned.

Checkyourselfbylookingatthechapter4/shopping-list4folder.

Page 206: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

KittensInthischapter,wehaven'ttouchedalotonourPomodoroapplicationwithitsnicekittens.Ipromiseyouthatwe'lldoalotofitinthenextchapter.Inthemeantime,Ihopethatthiskittenwillmakeyouhappy:

Kittenasking"What'snext?"

Page 207: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

SummaryInthischapter,wehadanextensiveoverviewofallthepossiblewaysofbindingdatatoourpresentationlayer.Youlearnedhowtosimplyinterpolatedatausinghandlebarsbrackets({{}}).YoualsolearnedhowtouseJavaScriptexpressionsandfiltersinsuchaninterpolation.Youlearnedandapplieddirectivessuchasv-bind,v-model,v-for,v-if,andv-show.

Wemodifiedourapplicationssothattheyusericherandmoreefficientdata-bindingsyntax.

Inthenextchapter,wewilltalkaboutVuex,thestatemanagementarchitectureinspiredbyFluxandReduxbutwithsimplifiedconcepts.

Wewillcreateglobalapplicationstatemanagementstoresforbothofourapplicationsandexploretheirpotentialbyworkingwithit.

Page 208: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Chapter5.Vuex–ManagingStateinYourApplicationInthepreviouschapter,youlearnedoneofthemostimportantconceptsofVue.js:databinding.Youlearnedandappliedalotofwaysofbindingdatatoourapplication.Youalsolearnedhowtousedirectives,howtolistentoevents,andhowtocreateandinvokemethods.Inthischapter,youwillseehowtomanagethedatathatrepresentsaglobalapplicationstate.WewilltalkaboutVuex,aspecialarchitectureforcentralizedstatesinVueapplications.Youwilllearnhowtocreateaglobaldatastoreandhowtoretrieveandchangeitinsidethecomponents.Wewilldefinewhatdataislocalandwhatshouldbeglobalinourapplications,andwewillusetheVuexstoretoworkwithaglobalstateinthem.

Summingitup,inthischapter,wearegoingto:

UnderstandthedifferencebetweenlocalandglobalapplicationstatesUnderstandwhatVuexisandhowitworksLearnhowtousedatafromtheglobalstoreLearnaboutstoregetters,mutations,andactionsInstallandusetheVuexstoreintheshoppinglistandPomodoroapplications

Page 209: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Parent-childcomponents'communication,events,andbrainteaserRememberourshoppinglistapplication?DoyourememberourChangeTitleComponentandhowweensuredthattypinginitsinputboxwouldaffectthetitleoftheshoppinglistthatbelongstotheparentcomponent?Yourememberthateachcomponenthasitsownscope,andthescopeoftheparentcomponentcannotbeaffectedbychildrencomponents.Thus,inordertobeabletopropagatethechangesfrominsidethechildrencomponentstotheparentcomponents,weusedevents.Puttingitverysimply,youcancallthe$emitmethodfromthechildcomponentwiththenameoftheeventbeingdispatchedandlistentothiseventwithinthev-ondirectiveontheparentcomponent.

Ifitisanativeevent,suchasinput,it'sevenmoresimple.Justbindtheneededattributetothechildcomponentasav-modelandthencallthe$emitmethodwiththenameoftheevent(forexample,input)fromthechildcomponent.

Actually,thisisexactlywhatwehavedonewithChangeTitleComponent.

Openthecodeinsidethechapter5/shopping-listfolderandcheckifI'mright.(Youmightalsowanttorunnpminstallandnpmrundevifyouwanttochecktheapplication'sbehaviorinyourbrowser.)

Weboundthetitleusingthev-modeldirectivetoChangeTitleComponentinsidetheShoppingListComponenttemplate:

//ShoppingListComponent.vue

<template>

<div>

<...>

<divclass="footer">

<hr/>

<change-title-componentv-model="title"></change-title-component>

</div>

</div>

</template>

Afterthat,wedeclarethevalueofthetitlemodelinsidethepropsattributeoftheChangeTitleComponentandemittheinputeventontheinputaction:

<template>

<div>

<em>Changethetitleofyourshoppinglisthere</em>

<input:value="value"@input="onInput"/>

</div>

</template>

<script>

exportdefault{

Page 210: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

props:['value'],

methods:{

onInput(event){

this.$emit('input',event.target.value)

}

}

}

</script>

Seemsprettystraightforward,right?

Ifwetrytochangethetitleintheinputbox,thetitleofourshoppinglistchangesaccordingly:

Afterestablishingevents-basedcommunicationbetweentheparentandchildcomponents,weareabletochangethetitle

Lookslikewewereactuallyabletoachieveourpurpose.However,ifyouopenyourdevtools,youwillseeanuglyerror:

[Vuewarn]:Avoidmutatingapropdirectlysincethevaluewillbeoverwritten

whenevertheparentcomponentrerenders.Instead,useadataorcomputed

propertybasedontheprop'svalue.Propbeingmutated:"title"

Ouch!Vueisactuallyright,wearemutatingthedatathatiscontainedinsidetheShoppingListcomponent'spropsattribute.Thisattributecomesfromthemainparentcomponent,App.vue,whichis,inturn,theparentofourShoppingListComponent.Andwealreadyknowthatwecannotmutatetheparent'sdatafromthechildcomponent.IfthetitlebelongeddirectlytotheShoppingListComponent,wewereallgood,butinthiscase,wearedefinitelydoingsomethingwrong.

Page 211: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Also,ifyouarepayingenoughattention,youprobablynoticedthatthere'sonemoreplacethatcontainsthesamepieceofdatathatdoesn'tchangedespiteoureffort.Lookatthetab'stitle.ItcontinuestodisplaythewordGroceries.Butwewantittochangeaswell!

Smallsidenote:I'veaddedanewcomponent,ShoppingListTitleComponent.Itrepresentsthetab'stitle.Doyouremembercomputedproperties?Notethatthiscomponentcontainsonethatjustprepends#totheIDimportedthroughthepropsattributetogenerateananchor:

<template>

<a:href="href":aria-controls="id"role="tab"data-toggle="tab">

{{title}}</a>

</template>

<script>

exportdefault{

props:['id','title'],

computed:{

href(){

return'#'+this.id

}

}

}

</script>

Theanchorthatdisplaysthetab'stitlecontainsanhrefbindingdirectivethatreliesonthiscomputedproperty.

So,backtothetitlechanging.WhatcanwedotochangethetitleofthiscomponentwhenthetitleinsidetheChangeTitleComponentchanges?IfwecouldpropagatetheeventtothemainApp.vuecomponent,wecouldactuallysolvebothproblems.Wheneverthedataintheparentcomponentchanges,itaffectsallthechildrencomponents.

So,weneedtosomehowmaketheeventflowfromChangeTitleComponentuntilthemainAppcomponent.Soundsdifficult,butactually,wejustneedtoregisterourcustomeventinbothChangeTitleComponentanditsparentandemitituntilitreachestheAppcomponent.TheAppcomponentshouldhandlethiseventbyapplyingthechangetothecorrespondingtitle.InorderforApp.vuetoknowexactlywhichshoppinglistisbeingchanged,itschildShoppingListComponentshouldalsopasstheIDoftheshoppinglistthatitrepresents.Forthistohappen,App.vueshouldpasstheidpropertytothecomponent,andtheshoppinglistcomponentshouldregisteritinitspropsattribute.

So,wewilldothefollowing:

1. BindtheidpropertytoShoppingListComponentonitscreationinsidetheAppcomponent'stemplate.

2. Bindpropertytitleinsteadofv-modeltothechange-title-componentfromwithintheShoppingListcomponent.

3. Attachthecustomevent(let'scallitchangeTitle)toinputinsidetheChangeTitleComponent.

4. TellShoppingListComponenttolistentothecustomchangeTitleeventcomingfromthe

Page 212: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

change-title-componentusingthev-ondirectiveandhandleitbyemittinganotherevent(itcanalsobecalledchangeTitle)thatshouldbecaughtbytheAppcomponent.

5. AttachlistenertothechangeTitleeventtotheshopping-list-componentinsideApp.vueandhandleitbyactuallychangingthetitleofthecorrespondingshoppinglist.

Let'sstartbymodifyingtheApp.vuefile'stemplateandbindingtheshoppinglist'sIDtoshopping-list-component:

//App.vue

<template>

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

<...>

<shopping-list-component:id="list.id":

:items="list.items"></shopping-list-component>

<...>

</div>

</template>

NowregistertheidattributeinsidetheShoppingListComponentcomponent'sprops:

//ShoppingListComponent.vue

<script>

<...>

exportdefault{

<...>

props:['id','title','items'],

<...>

}

</script>

Bindthetitledatapropertyinsteadofthev-modeldirectivetothechange-title-component:

//ShoppingListComponent.vue

<template>

<...>

<change-title-component:></change-title-component>

<...>

</template>

//ChangeTitleComponent.vue

<template>

<div>

<em>Changethetitleofyourshoppinglisthere</em>

<input:value="title"@input="onInput"/>

</div>

</template>

<script>

exportdefault{

props:['value','title'],

<...>

}

</script>

Page 213: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

EmitacustomeventinsteadofinputfromtheChangeTitleComponentandlistentothiseventinitsparentcomponent:

//ChangeTitleComponent.vue

<script>

exportdefault{

<...>

methods:{

onInput(event){

this.$emit('changeTitle',event.target.value)

}

}

}

</script>

//ShoppingListComponent.vue

<template>

<...>

<change-title-component:

v-on:changeTitle="onChangeTitle"></change-title-component>

<...>

</template>

CreatetheonChangeTitlemethodinShoppingListComponentthatwillemititsownchangeTitleevent.ListentothiseventintheApp.vuecomponentusingthev-ondirective.NotethattheonChangeTitlemethodoftheshoppinglistcomponentshouldsenditsIDinorderforApp.vuetoknowwhichshoppinglist'stitleisbeingchanged.So,theonChangeTitlemethodanditshandlingwilllookasfollows:

//ShoppingListComponent.vue

<script>

<...>

exportdefault{

<...>

methods:{

<...>

onChangeTitle(text){

this.$emit('changeTitle',this.id,text)

}

}

}

</script>

//App.vue

<template>

<...>

<shopping-list-component:id="list.id":

:items="list.items"v-on:changeTitle="onChangeTitle">

</shopping-list-component>

<...>

</template>

Finally,let'screateachangeTitlemethodinsideApp.vuethatwillfindashoppinglistinthe

Page 214: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

shoppinglistsarraybyitsIDandchangeitstitle:

<script>

<...>

import_from'underscore'

exportdefault{

<...>

methods:{

onChangeTitle(id,text){

_.findWhere(this.shoppinglists,{id:id}).title=text

}

}

}

</script>

Notethatwehaveusedtheunderscoreclass'sfindWheremethod(http://underscorejs.org/#findWhere)tomakeourtaskoffindingtheshoppinglistbyitsIDeasier.

And...wearedone!Checkthefinalcodeforthisteaserinthechapter5/shopping-list2folder.Checkthepageinthebrowser.Trytochangethetitleintheinputbox.Youwillseethatitchangeseverywhere!

Admitthatthiswasquitechallenging.Trytorepeatallthestepsbyyourself.Inthemeantime,letmeberandomandtellyoutwowords:globalandlocal.Thinkaboutit.

Page 215: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Whydoweneedaglobalstatestore?Asadeveloper,youarealreadyfamiliarwithglobalandlocalconcepts.Thereareglobalvariablesthatareaccessiblebyeachsectionoftheapplication,butmethodsalsohavetheirown(local)scopeandtheirscopeisnotaccessiblebyothermethods.

Acomponent-basedsystemalsohasitslocalandglobalstates.Eachcomponenthasitslocaldata,buttheapplicationhasaglobalapplicationstatethatcanbeaccessedbyanycomponentoftheapplication.Thechallengethatwehavemetinthepreviousparagraphwouldbeeasilysolvedifwehadsomekindofaglobalvariablesstorecontainingthetitlesoftheshoppinglistsandeachcomponentcouldaccessandmodifythem.Fortunatelyforus,Vue'screatorthoughtaboutusandcreatedVuexarchitecture.Thisarchitectureallowsustocreateaglobalapplicationstore—theplacewheretheglobalapplicationstatecanbestoredandmanaged!

Page 216: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

WhatisVuex?Aspreviouslymentioned,Vuexisanapplicationarchitectureforcentralizedstatemanagement.ItwasinspiredbyFluxandRedux,butitisalittlebiteasiertounderstandandtouse:

Vuexarchitecture;theimageistakenfromtheVuexGitHubpageathttps://github.com/vuejs/vuex

Lookinthemirror(donotforgettosmiletoyourself).Youseeaniceprettyhuman.However,there'sawholecomplexsysteminsideit.Whatdoyoudowhenyoufeelcold?Andhowdoyoufeelwhenit'shot?Howdoesitfeeltobehungry?Andveryhungry?Andhowdoesitfeeltotouchafluffycat?Thehumancanbeinvarioustypesofstates(happy,hungry,smiley,angry,andsoon).Thehumanalsohasalotofcomponents,suchashands,arms,legs,

Page 217: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

stomach,face,andsoon.Canyouimaginehowwoulditbeif,let'ssay,ahandwereabletodirectlyinfluenceyourstomach,makingyoufeelhungry,withoutyourawareness?

Thewayweworkisverysimilartothecentralizedstatemanagementsystem.Ourbraincontainsaninitialstateofthings(happy,nothungry,satisfied,andsoon).Italsoprovidesthemechanismthatallowspullingthestringsinitthatcanaffectthestate.Forexample,makeasmile,feelsatisfied,clapyourhands,andsoon.Ourhands,stomach,mouth,andothercomponentscannotdirectlyaffectthestate.Buttheycantellourbraintodispatchcertainchanges,andthesechanges,inturn,willaffectthestate.

Forexample,whenyouarehungry,youeat.Yourstomachatsomecertainpointtellsthebrainthatitisfull.Theactiondispatchesamutationofthestateofbeinghungrytobesatisfied.Yourcomponentmouthisboundtothisstateanditmakesitexpressthesmile.Thus,thecomponentsareboundtotheread-onlybrainstateandcandispatchbrainactionsthatwillalterthestate.Thecomponentsarenotawareofeachotherandcannotmodifyeachother'sstatedirectlyinanyway.Theyalsocanalsonotaffectdirectlythebrain'sinitialstate.Theycanonlycalltheactions.Actionsbelongtothebrain,andintheircallbacks,thestatecanbemodified.Thus,ourbrainisasinglesourceoftruth.

Tip

Singlesourceoftruthininformationsystemsisawayofdesigningthearchitectureoftheapplicationinsuchawaythateverydataelementisonlystoredonce.Thisdataisread-onlytopreventtheapplication'scomponentsfromcorruptingthestatethatisaccessedbyothercomponents.TheVuexstoreisdesignedinsuchawaythatitisnotpossibletochangeitsstatefromanycomponent.

Page 218: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Howdoesthestoreworkandwhatissospecialaboutit?TheVuexstorecontainsessentiallytwothings:stateandmutations.Stateisanobjectthatrepresentstheinitialstateoftheapplicationdata.Mutationsisalsoanobjectcontainingactionfunctionsthataffectthestate.VuexstoreisjustaplainJavaScriptfilethatexportsthesetwoobjectsandtellsVuetouseVuex(Vue.use(Vuex)).Thenitcanbeimportedintoanyothercomponent.IfyouimportitinthemainApp.vuefileandregisterthestoreontheVueapplicationinitialization,itispassedtothewholechildrenchainandcanbeaccessedthroughthethis.$storevariable.So,veryroughly,inaverysimplifiedway,wewouldcreateastore,importitinthemainapp,anduseitinacomponentinthefollowingway:

//CREATESTORE

//initializestate

conststate={

msg:'Hello!'

}

//initializemutations

constmutations={

changeMessage(state,msg){

state.msg=msg

}

}

//createstorewithdefinedstateandmutations

exportdefaultnewVuex.Store({

state:state

mutations:mutations

})

//CREATEVUEAPP

<script>

importstorefrom'./vuex/store'

exportdefault{

components:{

SomeComponent

},

store:store

}

</script>

//INSIDESomeComponent

<script>

exportdefault{

computed:{

msg(){

returnthis.$store.state.msg;

}

},

methods:{

changeMessage(){

this.$store.commit('changeMessage',newMsg);

Page 219: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

}

}

}

</script>

Theverylogicalquestionmightarise:whycreateaVuexstoreinsteadofjusthavingasharedJavaScriptfilethatimportssomestate?Youcan,ofcourse,dothat,butthenyoumustmakesurethatnoneofthecomponentscanmutatethestatedirectly.Beingabletochangethestoreattributesdirectlywould,ofcourse,bealoteasier,butthenitmightleadtoerrorsandinconsistencies.Vuexprovideacleanwayofimplicitlyprotectingthestore'sstateofdirectaccess.And,it'sreactive.Puttingallthisinstatements:

TheVuexstoreisreactive.Oncecomponentsretrieveastatefromit,theywillreactivelyupdatetheirviewseverytimethestatechanges.Componentsarenotabletodirectlymutatethestore'sstate.Instead,theyhavetodispatchmutationsdeclaredbythestore,whichallowseasytrackingofchanges.OurVuexstorethusbecomesasinglesourceoftruth.

Let'screateasimplegreetingsexampletoseeVuexinaction.

Page 220: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

GreetingswithstoreWewillcreateaverysimpleVueapplicationwithtwocomponents:oneofthemwillcontainthegreetingsmessageandtheotheronewillcontaininputthatwillallowustochangethismessage.Ourstorewillcontaintheinitialstatethatwillrepresenttheinitialgreetingandthemutationthatwillbeabletochangethemessage.Let'sstartbycreatingaVueapplication.Wewillusevue-cliandthewebpack-simpletemplate:

vueinitwebpack-simplesimple-store

Installthedependenciesandruntheapplicationasfollows:

cdsimple-storenpminstallnpmrundev

Theapplicationisstarted!Openthebrowserinlocalhost:8080.Actually,thegreetingisalreadythere.Let'snowaddthenecessarycomponents:

ShowGreetingsComponentwilljustdisplaythegreetingsmessageChangeGreetingsComponentwilldisplaytheinputfieldthatwillallowtochangethemessage

Inthesrcfolder,createacomponentssubfolder.StartbyaddingShowGreetingsComponent.vuetothisfolder.

Itwilllookassimpleasthefollowing:

<template>

<h1>{{msg}}</h1>

</template>

<script>

exportdefault{

props:['msg']

}

</script>

Afterthat,addChangeGreetingsComponent.vuetothisfolder.Ithastocontaintheinputwiththev-model='msg'directive:

<template>

<inputv-model='msg'>

</template>

<script>

exportdefault{

props:['msg']

}

</script>

NowopentheApp.vuefile,importthecomponents,andreplacethemarkupwiththesetwocomponents.Donotforgettobindmsgtobothofthem.So,yourApp.vueafterthemodificationswilllooklikethefollowing:

Page 221: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

<template>

<div>

<show-greetings-component:msg='msg'></show-greetings-component>

<change-greetings-component:msg='msg'></change-greetings-component>

<div>

</template>

<script>

importShowGreetingsComponentfrom'./components/ShowGreetingsComponent.vue'

importChangeGreetingsComponentfrom'./components/ChangeGreetingsComponent.vue'

exportdefault{

components:{ShowGreetingsComponent,ChangeGreetingsComponent},

data(){

return{

msg:'HelloVue!'

}

}

}

</script>

Openthebrowser.Youwillseetheinputboxwithourgreeting;however,typinginitwillnotchangethemessageinthetitle.Wewerealreadyexpectingthatbecauseweknowthatcomponentscannotdirectlyaffecteachother'sstate.Let'snowintroducethestore!Firstofall,wemustinstallvuex:

npminstallvuex--save

Createafoldernamedvuexinthesrcfolder.CreateaJavaScriptfilenamedstore.js.Thiswillbeourstatemanagemententry.Firstofall,importbothVueandVuexandtellVuethatwewanttouseVuexinthisapplication:

//store.js

importVuefrom'vue'

importVuexfrom'vuex'

Vue.use(Vuex)

Nowcreatetwoconstants,stateandmutations.Statewillcontainthemessagemsgwhilemutationswillexportthemethodthatwillallowustomodifymsg:

conststate={

msg:'HelloVue!'

}

constmutations={

changeMessage(state,msg){

state.msg=msg

}

}

NowinitializetheVuexstorewiththealreadycreatedstateandmutations:

exportdefaultnewVuex.Store({

state:state,

Page 222: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

mutations:mutations

})

Tip

AsweareusingES6,thenotation{state:state,mutations:mutations}canbereplacedwith,simply,{state,mutations}

Ourwholestore'scodewillthuslooklikethefollowing:

//store.js

importVuefrom'vue'

importVuexfrom'vuex'

Vue.use(Vuex)

conststate={

msg:'HelloVue!'

}

constmutations={

changeMessage(state,msg){

state.msg=msg

}

}

exportdefaultnewVuex.Store({

state,

mutations

})

WecannowimportthestoreinourApp.vue.Bydoingthis,wetellallthecomponentsthattheycanusetheglobalstore,andasaresult,wecanremovedatafromApp.vue.Also,wedonotneedtobinddatatothecomponentsanymore:

//App.vue

<template>

<div>

<show-greetings-component></show-greetings-component>

<change-greetings-component></change-greetings-component>

</div>

</template>

<script>

importShowGreetingsComponentfrom'./components/ShowGreetingsComponent.vue'

importChangeGreetingsComponentfrom'./components/ChangeGreetingsComponent.vue'

importstorefrom'./vuex/store'

exportdefault{

components:{ShowGreetingsComponent,ChangeGreetingsComponent},

store

}

</script>

Nowlet'sgobacktoourcomponentsandreusethedatafromthestore.Inordertobeabletoreusereactivedatafromthestore'sstate,weshouldusecomputedproperties.Vueissosmartthatitwilldoalltheworkforustoreactivelyupdatethesepropertieswheneverthestate

Page 223: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

changes.Andno,wedonotneedtoimportthestoreinsidethecomponents.Wehaveaccesstoitjustbyusingthethis.$storevariable.So,ourShowGreetingsComponentwilllooklikethefollowing:

//ShowGreetingsComponent.vue

<template>

<h1>{{msg}}</h1>

</template>

<style>

</style>

<script>

exportdefault{

computed:{

msg(){

returnthis.$store.state.msg

}

}

}

</script>

Followthesamelogictoreusethestore'smsgintheChangeGreetingsComponent.Nowwejusthavetodispatchthemutationoneachkeyupevent.Forthistohappen,wejustneedtocreateamethodthatwillcommitthecorrespondingstore'smutationandthatwewillcallfromtheinput'skeyuplistener:

//ChangeGreetingsComponent.vue

<template>

<inputv-model='msg'@keyup='changeMsg'>

</template>

<script>

exportdefault{

computed:{

msg(){

returnthis.$store.state.msg

}

},

methods:{

changeMsg(ev){

this.$store.commit('changeMessage',ev.target.value)

}

}

}

</script>

Openthepage.Trytochangethetitle.Etvoilà!Itworks!

Page 224: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

UsingtheVuexstoretocallthemutationsandchangethestore'sstatepropagatingitthroughthecomponents

Wedon'tneedtobindthev-modeldirectiveanymorebecauseallthechangeshappenduetothecallingstore'smutationmethod.Thus,themsgpropertycanbeboundasthevalue'sattributetotheinputbox:

<template>

<input:value='msg'@keyup='changeMsg'>

</template>

Checkthecodeforthissectioninthechapter5/simple-storefolder.Inthisexample,wehaveusedaverysimplifiedversionofthestore.However,complexSingle-PageApplications(SPAs)requireamorecomplexandmodularstructure.Wecanandshouldextractthestore'sgettersandactionsthatdispatchmutationstoseparatedfiles.Wecanalsogroupthesefilesaccordingtothecorrespondingdata'sresponsibilities.Inthenextsections,wewillseehowwecanachievesuchamodularstructurebyusinggettersandactions.

Page 225: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

StorestateandgettersItis,ofcourse,goodthatwecanreusethethis.$store.statekeywordinsidethecomponents.Butimaginethefollowingscenarios:

Inalarge-scaleappwheredifferentcomponentsaccessthestateofthestoreusing$this.store.state.somevalue,wedecidetochangethenameofsomevalue.Thismeansthatwehavetochangethenameofthevariableinsideeachandeverycomponentthatusesit!Wewanttouseacomputedvalueofstate.Forexample,let'ssaywewanttohaveacounter.Itsinitialstateis"0".Eachtimeweuseit,wewanttoincrementit.Thismeansthateachcomponenthastocontainafunctionthatreusesthestore'svalueandincrementsit,whichmeanshavingrepeatedcodeineachcomponent,whichisnotgoodatall!

Sorryforthenot-so-goodscenarios,guys!Fortunately,thereisanicewaynottofallintoanyofthem.Imaginethecentralizedgetterthataccessesthestore'sstateandprovidesagetterfunctiontoeachofthestate'sitems.Ifneeded,thisgettercanapplysomecomputationtothestate'sitem.Andifweneedtochangethenameofsomeoftheattributes,weonlychangeitinoneplace,inthisgetter.It'sratheragoodpracticeoraconventionthananarchitecturalmandatorysystem,butIstronglyrecommendtouseitevenifyouhaveonlyacoupleofstateitems.

Let'screatesuchagetterforoursimplegreetingsapplication.Justcreateagetters.jsfileinsidethevuexfolderandexportagetMessagefunctionthatwillreturnstate.msg:

//getters.js

exportdefault{

getMessage(state){

returnstate.msg

}

}

ThenitshouldbeimportedbythestoreandexportedinthenewVuexobject,sothestoreknowswhatitsgettersare:

//store.js

importVuefrom'vue'

importVuexfrom'vuex'

importgettersfrom'./getters'

Vue.use(Vuex)

conststate={

msg:'HelloVue!'

}

constmutations={

changeMessage(state,msg){

state.msg=msg

}

Page 226: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

}

exportdefaultnewVuex.Store({

state,mutations,getters

})

Andthen,inourcomponents,weusegettersinsteadofdirectlyaccessingthestore'sstate.Justreplaceyourcomputedpropertyinboththecomponentswiththefollowing:

computed:{

msg(){

returnthis.$store.getters.getMessage

}

},

Openthepage;everythingworkslikeacharm!

Stillthethis.$store.gettersnotationcontainssomanyletterstowrite.We,programmersarelazy,right?Vueisniceenoughtoprovideuswithaneasywaytosupportourlaziness.ItprovidesamapGettershelperthatdoesexactlyasitsnamesuggests—providesallthestore'sgetterstoourcomponents.Justimportitanduseitinyourcomputedpropertiesasfollows:

//ShowGreetingsComponent.vue

<template>

<h1>{{getMessage}}</h1>

</template>

<script>

import{mapGetters}from'vuex'

exportdefault{

computed:mapGetters(['getMessage'])

}

</script>

//ChangeGreetingsComponent.vue

<template>

<input:value='getMessage'@keyup='changeMsg'>

</template>

<script>

import{mapGetters}from'vuex'

exportdefault{

computed:mapGetters(['getMessage']),

methods:{

changeMsg(ev){

this.$store.commit('changeMessage',ev.target.value)

}

}

}

</script>

Notethatwe'vechangedthepropertyusedinsidethetemplatetohavethesamenameasthegetter'smethodname.However,itisalsopossibletomapthecorrespondinggettermethod's

Page 227: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

nametothepropertynamethatwewanttouseinourcomponent:

//ShowGreetingsComponent.vue

<template>

<h1>{{msg}}</h1>

</template>

<style>

</style>

<script>

import{mapGetters}from'vuex'

exportdefault{

computed:mapGetters({

msg:'getMessage'

})

}

</script>

//ChangeGreetingsComponent.vue

<template>

<input:value='msg'@keyup='changeMsg'>

</template>

<script>

import{mapGetters}from'vuex'

exportdefault{

computed:mapGetters({

msg:'getMessage'

}),

methods:{

changeMsg(ev){

this.$store.commit('changeMessage',ev.target.value)

}

}

}

</script>

So,wewereabletoextractthegetterforthemsgpropertytothecentralizedstore'sgettersfile.

Now,ifyoudecidetoaddsomecomputationtothemsgproperty,youonlyneedtodoitinoneplace.Justinoneplace!

Page 228: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Rickwasalwayschangingthecodeinallthecomponentsandjustdiscoveredthatitispossibletoonlychangeitinoneplace

Forexample,ifwewanttoreusetheuppercasedmessageinallthecomponents,wecanapplytheuppercasefunctioninsidethegetterasfollows:

//getters.js

exportdefault{

getMessage(state){

return(state.msg).toUpperCase()

}

}

Fromnowon,eachcomponentthatusesthegettertoretrievethestatewillhaveanuppercasedmessage:

TheShowTitleComponentuppercasedmessage.ThetoUpperCasefunctionisappliedinsidethegetters

Page 229: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Notealsohowsmoothlythemessageisbeingchangedtouppercaseinsidetheinputboxwhileyou'retypinginit!Checkthefinalcodeforthissectioninsidethechapter5/simple-store2folder.

Ifwedecidetochangethenameofthestate'sattribute,wewillonlychangeitinsidethegettersfunction.Forexample,ifwewanttochangethenameofmsgtomessage,wewilldoitinsideourstore:

conststate={

message:'HelloVue!'

}

constmutations={

changeMessage(state,msg){

state.message=msg

}

}

Andthen,wewillalsochangeitinsidethecorrespondinggetterfunction:

exportdefault{

getMessage(state){

return(state.message).toUpperCase()

}

}

That'sit!Therestoftheapplicationisleftcompletelyuntouched.Thisisthepowerofsucharchitecture.Insomeverycomplexapplications,wecanhavemorethanonegettersfilesthatexportstatefordifferentkindoftheapplication'sproperties.Modularityisapowerthatdrivesthemaintainability;useit!

Page 230: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

MutationsFromthepreviousexample,itshouldbeclearthatmutationsarenomorethansimpleeventhandlerfunctionsthataredefinedbyname.Mutationhandlerfunctionsreceiveastateasafirstargument.Otherargumentscanbeusedtopassdifferentparameterstothehandlerfunction:

constmutations={

changeMessage(state,msg){

state.message=msg

},

incrementCounter(state){

state.counter++;

}

}

Aparticularityofmutationsisthattheycannotbecalleddirectly.Tobeabletodispatchamutation,weshouldcallamethodcalledcommitwithanameofthecorrespondingmutationandparameters:

store.commit('changeMessage','newMessage')

store.commit('incrementCounter')

Tip

PriortoVue2.0,amethodtodispatchmutationwascalled"dispatch".Soyouwouldcallitasfollows:store.dispatch('changeMessage','newMessage')

Youcancreateasmanymutationsasyouwish.Theycanperformdifferentoperationsonsame-stateitems.Youcangoevenfurtheranddeclaremutationnamesasconstantsinaseparatedfile.Inthisway,youcaneasilyimportthemandusetheminsteadofstrings.So,forourexample,wewouldcreateafileinsidethevuexdirectoryandcallitmutation_types.js,andexportalltheconstantnamesthere:

//mutation_types.js

exportconstINCREMENT_COUNTER='INCREMENT_COUNTER'

exportconstCHANGE_MSG='CHANGE_MSG'

Then,inourstore,wewillimporttheseconstantsandreusethem:

//store.js

<...>

import{CHANGE_MSG,INCREMENT_COUNTER}from'./mutation_types'

<...>

constmutations={

[CHANGE_MSG](state,msg){

state.message=msg

},

[INCREMENT_COUNTER](state){

state.counter++

}

Page 231: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

}

Insidethecomponentsthatdispatchmutations,wewillimportthecorrespondingmutationtypeanddispatchitusingthevariablename:

this.$store.commit(CHANGE_MSG,ev.target.value)

Thiskindofstructuremakesalotofsenseinbigapplications.Again,youcangroupyourmutationtypesaccordingtothefunctionalitytheyprovidetotheapplicationandimportonlythosemutationsinthecomponentsthatareneededforthespecificcomponent.Thisis,again,aboutbestpractices,modularity,andmaintainability.

Page 232: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

ActionsWhenwedispatchamutation,webasicallyperformanaction.SayingthatwecommitaCHANGE_MSGmutationisthesameassayingthatweperformanactionofchangingthemessage.Forthesakeofbeautyandtotalextraction,likeweextractthestorestate'sitemsintogettersandmutationsnamesconstantstothemutation_typeswecanalsoextractthemutationstotheactions.

Note

Thus,actionisnomorethanjustafunctionthatdispatchesamutation!

functionchangeMessage(msg){store.commit(CHANGE_MSG,msg)}

Let'screateasimpleactionsfileforourchange,messageexample.Butbeforethat,let'screateonemoreitemforthestore'sinitialstate,counter,andinitializeitwitha"0"value.So,ourstorewilllooklikethefollowing:

//store.js

importVuefrom'vue'

importVuexfrom'vuex'

import{CHANGE_MSG,INCREMENT_COUNTER}from'./mutation_types'

Vue.use(Vuex)

conststate={

message:'HelloVue!',

counter:0

}

constmutations={

[CHANGE_MSG](state,msg){

state.message=msg

},

[INCREMENT_COUNTER](state){

state.counter++;

}

}

exportdefaultnewVuex.Store({

state,

mutations

})

Let'salsoaddacountergettertothegettersfile,soourgetters.jsfilelookslikethefollowing:

//getters.js

exportdefault{

getMessage(state){

return(state.message).toUpperCase()

},

Page 233: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

getCounter(state)

{

return(state.counter)

}

}

And,finally,let'susethecounter'sgetterinsideShowGreetingsComponenttoshowtheamountoftimesthemessagemsgwaschanged:

<template>

<div>

<h1>{{msg}}</h1>

<div>themessagewaschanged{{counter}}times</div>

</div>

</template>

<script>

import{mapGetters}from'vuex'

exportdefault{

computed:mapGetters({

msg:'getMessage',

counter:'getCounter'

})

}

</script>

Let'snowcreateactionsforboththemutations,forthecounterandforthechangemessage.Insideavuexfolder,createanactions.jsfileandexporttheactionsfunctions:

//actions.js

import{CHANGE_MSG,INCREMENT_COUNTER}from'./mutation_types'

exportconstchangeMessage=(store,msg)=>{

store.commit(CHANGE_MSG,msg)

}

exportconstincrementCounter=(store)=>{

store.commit(INCREMENT_COUNTER)

}

WecanandshoulduseES2015argumentsdestructuringandmakeourcodemoreelegant.Let'salsoexportalltheactionsinasingleexportdefaultstatement:

//actions.js

import{CHANGE_MSG,INCREMENT_COUNTER}from'./mutation_types'

exportdefault{

changeMessage({commit},msg){

commit(CHANGE_MSG,msg)

},

incrementCounter({commit}){

commit(INCREMENT_COUNTER)

}

}

Page 234: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Okay,nowwehaveniceandbeautifulactions.Let'susetheminourChangeGreetingsComponent!Tobeabletouseactionsinsidecomponents,weshouldfirstimportthemtoourstoreandthenexportinthenewVuexobject.Thenactionscanbedispatchedusingthethis.$store.dispatchmethodinsidethecomponents:

//ChangeGreetingsComponent.vue

<template>

<input:value="msg"@keyup="changeMsg">

</template>

<script>

import{mapGetters}from'vuex'

exportdefault{

computed:mapGetters({

msg:'getMessage'

}),

methods:{

changeMsg(ev){

this.$store.dispatch('changeMessage',ev.target.value)

}

}

}

</script>

Sowhat'sactuallythedifference?Wecontinuetowritethis.$storecode,theonlydifferenceisthatinsteadofcallingthecommitmethodwecalldispatch.DoyourememberhowwediscoveredmapGettershelper?Wasn'titnice?ActuallyVuealsoprovidesamapActionshelperthatallowsustoavoidwritingtheextensivethis.$store.dispatchsomethingmethod.JustimportmapActionsinthesamewayasweimportmapGettersanduseitinsidethecomponent'smethodsproperty:

//ChangeGreetingsComponent.vue

<template>

<input:value="msg"@keyup="changeMessage">

</template>

<script>

import{mapGetters}from'vuex'

import{mapActions}from'vuex'

exportdefault{

computed:mapGetters({

msg:'getMessage'

}),

methods:mapActions(['changeMessage','incrementCounter'])

}

</script>

Notethatwechangedthehandler'sfunctionforthekeyupevent,sowedon'thavetomaptheevents'namestothecorrespondingactions.However,justlikeinthecaseofmapGetters,wecanalsomapcustomevents'namestothecorrespondingactionsnames.

WeshouldalsochangethechangeMessageinvocationbecausewedon'textractanyevent's

Page 235: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

targetvalueinsidetheactionsnow;thus,weshoulddoitinsidetheinvocation:

//ChangeGreetingsComponent.vue

<template>

<input:value="msg"@keyup="changeMessage($event.target.value)">

</template>

Finally,let'sbindtheincrementCounteractiontotheuser'sinput.Let's,forexample,callthisactionfromourinputtemplateonthekeyup.enterevent:

<template>

<input:value="msg"@keyup="changeMessage"

@keyup.enter="incrementCounter">

</template>

Ifyouopenthepage,andtrytochangethetitleandhittheEnterbutton,youwillseethatthecounterwillbeincrementedeachtimeyouhitEnter:

Usingactionstoincrementthecounteronthepage

So,youseehoweasyitistomodularizeourapplicationbyusingactionsinsteadofdirectlyaccessingthestore.YouexportactionsinyourVuexstore,importthemapActionsinthecomponents,andcallthemfromtheeventhandlerdirectivesinthetemplates.

Doyourememberour"human"exampleinwhichwewerecomparingthepartsofthehumanbodytothecomponentsandthehumanbraintothestoreoftheapplicationstate?Imaginethatyouarerunning.Itisonlyoneactionbuthowmanychangesarebeingdispatchedandhowmanycomponentsarebeingaffectedbythosechanges?Whenyourun,yourheartrateincreases,yousweat,yourarmsmove,andyourfacesmilesbecauseyourealizehowniceitistorun!Whenyoueat,youalsosmilebecauseitisgoodtoeat.Youalsosmilewhenyouseeakitten.So,differentactionscandispatchmorethanonechange,andthesamechangecanbedispatchedbymorethanoneaction.

ThesamehappenswithourVuexstore,anditsmutationsandactions.Withinthesameaction,morethanonemutationcanbedispatched.Forinstance,wecoulddispatchourmutationforchangingamessageandincreasingthecounterwithinthesameaction.Let'screatethisactioninsideouraction.jsfile.Let'scallithandleMessageInputChangesandmakeitreceiveone

Page 236: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

argument:event.ItwilldispatchtheCHANGE_MSGmutationwithevent.target.value,andincaseevent.keyCodeisenter,itwilldispatchtheINCREMENT_COUNTERmutation:

//actions.js

handleMessageInputChanges({commit},event){

commit(CHANGE_MSG,event.target.value)

if(event.keyCode===13){

commit(INCREMENT_COUNTER)

}

}

Nowlet'simportthisactioninsideourChangeGreetingsComponentcomponent'smapActionsobjectandlet'suseitcallingitwiththe$eventparameter:

//ChangeGreetingsComponent.vue

<template>

<input:value="msg"@keyup="handleMessageInputChanges($event)"/>

</template>

<script>

import{mapGetters,mapActions}from'vuex'

exportdefault{

computed:mapGetters({

msg:'getMessage'

}),

methods:mapActions(['handleMessageInputChanges'])

}

</script>

Openthepage,andtrytochangethegreetingsmessageandincrementthecounterbyhittingtheEnterbutton.Itworks!

Thefinalcodeforthesimple-storeexamplecanbefoundinthechapter5/simple-store3folder.

Page 237: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

InstallingandusingtheVuexstoreinourapplicationsNowthatweknowwhatVuexis,howtocreateastore,dispatchmutations,andhowtousegettersandactions,wecaninstallthestoreinourapplicationsanduseittofinalizetheirdataflowandcommunicationchain.

Youcanfindtheapplicationstoworkoninthefollowingfolders:

Pomodoro:chapter5/pomodoroShoppinglist:chapter5/shopping-list2

Donotforgettorunnpminstallonbothapplications.

Startbyinstallingvuexanddefinethenecessarydirectoryandfilestructureinbothapplications.

Toinstallvuex,justrunthefollowing:

npminstallvuex--save

Afterinstallingvuex,createasubfoldervuexineachoftheapplication'ssrcfolders.Inthisfolder,createfourfiles:store.js,mutation_types.js,actions.js,andgetters.js.

Preparethestore.jsstructure:

//store.js

importVuefrom'vue'

importVuexfrom'vuex'

importgettersfrom'./getters'

importactionsfrom'./actions'

importmutationsfrom'./mutations'

Vue.use(Vuex)

conststate={

}

exportdefaultnewVuex.Store({

state,

mutations,

getters,

actions

})

ImportandusethestoreinthemainApp.vue:

//App.vue

Page 238: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

<script>

<...>

importstorefrom'./vuex/store'

exportdefault{

store,

<...>

}

</script>

Wewillnowdefinewhichistheglobalandwhichisthelocalstateineachoftheapplications,definewhatdataandbindingaremissing,dividethedata,andaddallthemissingstuffusingwhatwe'vejustlearned.

Page 239: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

UsingtheVuexstoreintheshoppinglistapplicationIhopeyoustillrememberthechallengewewerefacingatthebeginningofthischapter.WewouldliketoestablishcommunicationbetweenthecomponentsinsuchawaythatitwouldbeeasytochangethetitleoftheshoppinglistsfromtheChangeTitleComponentandpropagateittobothShoppingListTitleandShoppingListComponent.Let'sremovethehardcodedarrayofshoppinglistsfromApp.vueandcopyittothestore'sstate:

//store.js

<...>

conststate={

shoppinglists:[

{

id:'groceries',

title:'Groceries',

items:[{text:'Bananas',checked:true},

{text:'Apples',checked:false}]

},

{

id:'clothes',

title:'Clothes',

items:[{text:'blackdress',checked:false},

{text:'all-stars',checked:false}]

}

]

}

<...>

Let'sdefinegettersfortheshoppinglists:

//getters.js

exportdefault{

getLists:state=>state.shoppinglists

}

Now,importmapGettersinApp.vueandmaptheshoppinglistsvaluetothegetListsmethodsothatthe<script>taginsidetheApp.vuecomponentwilllooklikethefollowing:

//App.vue

<script>

importShoppingListComponentfrom'./components/ShoppingListComponent'

importShoppingListTitleComponentfrom

'./components/ShoppingListTitleComponent'

import_from'underscore'

importstorefrom'./vuex/store'

import{mapGetters}from'vuex'

exportdefault{

components:{

Page 240: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

ShoppingListComponent,

ShoppingListTitleComponent

},

computed:mapGetters({

shoppinglists:'getLists'

}),

methods:{

onChangeTitle(id,text){

_.findWhere(this.shoppinglists,{id:id}).title=text

}

},

store

}

</script>

Therestisleftuntouched!

Nowlet'sdefineamutationinsideourstorethatwillberesponsibleforchangingthetitle.Itisclearthatitshouldbeafunctionthatreceivesanewtitlestringasaparameter.However,thereissomedifficulty.Wedon'tknowwhichoftheshoppingliststitleshouldbechanged.Ifwecouldpassalist'sIDfromacomponenttothisfunction,wecouldactuallywriteapieceofcodethatwouldfindacorrectlistbyitsID.DidIjustsayifwecould?Ofcourse,wecan!Actually,ourShoppingListComponentalreadyreceivestheIDfromitsparentApp.vue.Let'sjustpassthisIDfromShoppingListComponenttoChangeTitleComponent.Inthisway,wewillbeabletoinvokethenecessaryactionfromthecomponentwherethetitleisactuallychanged,withouthavingtopropagatetheeventthroughtheparents'chain.

So,justbindtheIDtothechange-title-componentinsidetheShoppingListComponentcomponent'stemplate,asfollows:

//ShoppingListComponent.vue

<template>

<...>

<change-title-component::id="id"v-

on:changeTitle="onChangeTitle"></change-title-component>

<...>

</template>

DonotforgettoaddtheidattributetotheChangeTitleComponentcomponent'spropsattribute:

//ChangeTitleComponent.vue

<script>

exportdefault{

props:['title','id'],

<...>

}

</script>

Okay,nowourChangeTitleComponenthasaccesstobothtitleandidoftheshoppinglist.Let'saddthecorrespondingmutationtothestore.

Page 241: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

WecanstartbywritingafunctionthatfindsashoppinglistbyitsID.Forthis,Iwillusetheunderscoreclass's_.findWheremethod,justlikewedidintheApp.vuecomponent'schangeTitlemethod.

Importunderscoreinsidemutations.jsandaddthefindByIdfunctionasfollows:

//mutations.js

<...>

functionfindById(state,id){

return_.findWhere(state.shoppinglists,{id:id})

}

<...>

Let'snowaddthemutationandlet'scallit,forexample,CHANGE_TITLE.Thismutationwillreceivethedataobjectasaparametercontainingtitleandid,andassignthevalueofthereceivedtitletothetitleofthefoundshoppinglistitem.Firstofall,let'sdeclareaconstantCHANGE_TITLEinmutation_types.jsandreuseitinsteadofwritingthemutation'snameasastring:

//mutation_types.js

exportconstCHANGE_TITLE='CHANGE_TITLE'

//mutations.js

import_from'underscore'

import*astypesfrom'./mutation_types'

functionfindById(state,id){

return_.findWhere(state.shoppinglists,{id:id})

}

exportdefault{

[types.CHANGE_TITLE](state,data){

findById(state,data.id).title=data.title

}

}

Wearealmostfinished.Let'snowdefineachangeTitleactioninsidetheactions.jsfileandreuseitinourChangeTitleComponent.Opentheactions.jsfileandaddthefollowingcode:

//actions.js

import{CHANGE_TITLE}from'./mutation_types'

exportdefault{

changeTitle:({commit},data)=>{

commit(CHANGE_TITLE,data)

}

}

And,thefinaltouch.OpenChangeTitleComponent.vue,importthemapActionshelper,maptheonInputmethodtothechangeTitleaction,andcallitinsidetemplatewiththeobjectmappingtitletoevent.target.valueandIDtotheidparameter.So,thecodeofChangeTitleComponentwilllooklikethefollowing:

Page 242: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

//ChangeTitleComponent.vue

<template>

<div>

<em>Changethetitleofyourshoppinglisthere</em>

<input:value="title"@input="onInput({title:$event.target.value,

id:id})"/>

</div>

</template>

<script>

import{mapActions}from'vuex'

exportdefault{

props:['title','id'],

methods:mapActions({

onInput:'changeTitle'

})

}

</script>

Youcannowremovealltheevents-handlingcodefromtheShoppingListComponentandthemainAppcomponent.

Openthepageandtrytotypeintheinputbox!Thetitlewillchangeinalllocations:

Usingstore,mutations,andactions—allcomponentsupdatetheirstatewithouttheneedofeventshandlingmechanism

Thefinalcodefortheshoppinglistapplicationafterapplyingthestore'sfunctionscanbefoundinthechapter5/shopping-list3folder.

Page 243: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

UsingVuexstoreinthePomodoroapplicationFinally,wegotbacktoourPomodoro!Whenwasthelasttimeyoutooka5-minutebreak?Let'sbuildourPomodoroapplicationwiththeVuexarchitectureandthentakearestlookatkittens.Let'sstartwiththebaseinthechapter5/pomodorofolder,whereyoualreadyincludedthebasicstructureoftheVuexstore(ifnot,gotothestartoftheInstallingandusingVuexstoreinourapplicationssection).

Page 244: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Bringinglifetostart,pause,andstopbuttonsLet'sstartbyanalyzingwhatcanactuallybedonewithourPomodorotimer.Lookatthepage.Wehaveonlythreebuttons:start,pause,andstop.Thismeansthatourapplicationcanbeinoneofthesethreestates.Let'sdefineandexporttheminourstore.jsfile:

//store.js

<...>

conststate={

started:false,

paused:false,

stopped:false

}

<...>

Initially,allthesestatesaresettofalse,whichmakessensebecausetheapplicationisnotstarted,it'snotpausedand,ofcourse,itisnotstopped!

Let'snowdefinegettersforthesestates.Openthegetters.jsfileandaddthegetterfunctionsforallthreestates:

//getters.js

exportdefault{

isStarted:state=>state.started,

isPaused:state=>state.paused,

isStopped:state=>state.stopped

}

Whatshouldhappentoourcontrolbuttonsforeachofthedefinedstates:

Thestartbuttonshouldbecomedisabledwhentheapplicationisstarted.However,itshouldbeenabledagainwhentheapplicationispausedsothatwecanusethisbuttontoresumetheapplication.Thepausebuttoncanonlybeenabledwhentheapplicationisstarted(becausewecannotpausesomethingthathasnotbeenstartedyet).However,itshouldbedisablediftheapplicationispaused(becausewecannotpausesomethingthatisalreadypaused).Thestopbuttoncanonlybeenabledwhentheapplicationisstarted.

Let'stranslatethisintocodebyaddingthedisabledclasstoourcontrolbuttonsconditionally,dependingontheapplicationstates.

Tip

Onceweapplythedisabledclass,Bootstrapwilltakecareofthebuttons'behaviorforusbynotonlyapplyingspecialstylingbutalsodeactivatinginteractiveelements.

Inordertobeabletousethealreadydefinedgetters,wemustimportmapGettersintothe<script>tagofthecomponent.Afterthat,wemusttellthecomponentthatwewanttousethembyexportingthemwithinthecomputedpropertyobject:

Page 245: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

//ControlsComponent.vue

<script>

import{mapGetters}from'vuex'

exportdefault{

computed:mapGetters(['isStarted','isPaused','isStopped'])

}

</script>

Nowthesegetterscanbeusedinsidethetemplate.So,wewillapplythedisabledclasstothefollowing:

Thestartbuttonwhentheapplicationisstartedandnotpaused(isStarted&&!isPaused)Thepausebuttonwhentheapplicationisnotstartedorpaused(!isStarted||isPaused)Thestopbuttonwhentheapplicationisnotstarted(!isStarted)

Ourtemplatewillnowlooklikethefollowing:

//ControlsComponent.vue

<template>

<span>

<button:disabled='isStarted&&!isPaused'>

<iclass="glyphiconglyphicon-play"></i>

</button>

<button:disabled='!isStarted||isPaused'>

<iclass="glyphiconglyphicon-pause"></i>

</button>

<button:disabled='!isStarted'>

<iclass="glyphiconglyphicon-stop"></i>

</button>

</span>

</template>

Youseenowthatthepauseandstopbuttonslookdifferent!Ifyoumousehoveryourmouseoverthem,thecursorisnotchanged,whichmeansthattheyarereallydisabled!Let'sjustcreateastylefortheiconsthatareinsidethedisabledbuttonstohighlightthedisabledstateevenmore:

//ControlsComponent.vue

<stylescoped>

button:disabledi{

color:gray;

}

</style>

Okay,nowthatwehavebeautifuldisabledbuttons,let'sbringabitoflifeintothem!

Let'sthinkaboutwhatshouldactuallyhappentotheapplicationstateswhenwestart,pause,orstoptheapplication:

Whenwestarttheapplication,thestatestartedshouldbecometrueandbothpausedandstoppedstatesshouldforsurebecomefalse.

Page 246: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Whenwepausetheapplication,thestatepausedistrue,statestoppedisfalse,andstatestartedistruebecauseapausedapplicationcontinuestobestarted.Whenwestoptheapplication,thestatestoppedbecomestrueandboththepausedandstartedstatesbecomefalse.Let'stranslateallthisbehaviorintomutation_types,mutations,andactions!

Openmutation_types.jsandaddthreemutationtypesasfollows:

//mutation_types.js

exportconstSTART='START'

exportconstPAUSE='PAUSE'

exportconstSTOP='STOP'

Nowlet'sdefinemutations!Openthemutations.jsfileandaddthreemutationsforeachofthemutationtypes.So,wehavedecidedthatwhenwe:

Starttheapplication:Thestatestartedistrue,andstatespausedandstoppedarefalse.Pausetheapplication:Thestatestartedistrue,thestatepausedistrue,andstoppedarefalse.Stoptheapplication:Thestatestoppedistrue,andstatesstartedandpausedarefalse.

Nowlet'sputitintothecode.Importmutation_typestomutations.jsandwriteallthreenecessarymutationsasfollows:

//mutations.js

import*astypesfrom'./mutation_types'

exportdefault{

[types.START](state){

state.started=true

state.paused=false

state.stopped=false

},

[types.PAUSE](state){

state.paused=true

state.started=true

state.stopped=false

},

[types.STOP](state){

state.stopped=true

state.paused=false

state.started=false

}

}

Nowlet'sdefineouractions!Gototheactions.jsfile,importmutationtypes,andexportthreefunctions:

//actions.js

import*astypesfrom'./mutation_types'

Page 247: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

exportdefault{

start:({commit})=>{

commit(types.START)

},

pause:({commit})=>{

commit(types.PAUSE)

},

stop:({commit})=>{

commit(types.STOP)

}

}

ThefinaltouchtobringourbuttonstolifeistoimporttheseactionsintoControlsComponentandcallthemontheclickeventoneachbutton.Let'sdoit.DoyoustillrememberhowtocalltheactiononsomeeventappliedtotheHTMLelement?Ifwearetalkingabouttheclickevent,itisjustthefollowing:

@click='someAction'

So,inourControlsComponent.vue,weimportthemapActionsobject,mapittothecomponent'smethodsproperty,andapplyittothecorrespondingbutton'sclicks.That'sall!The<script>tagofControlsComponentwillthuslooklikethefollowing:

//ControlsComponent.vue

<script>

import{mapGetters,mapActions}from'vuex'

exportdefault{

computed:mapGetters(['isStarted','isPaused','isStopped']),

methods:mapActions(['start','stop','pause'])

}

</script>

Nowcallthesefunctionsinsidetheeventhandlerdirectiveswithinthetemplatesothatthe<template>tagoftheControlsComponentlookslikethefollowing:

//ControlsComponent.vue

<template>

<span>

<button:disabled='isStarted&&!isPaused'

@click="start">

<iclass="glyphiconglyphicon-play"></i>

</button>

<button:disabled='!isStarted||isPaused'

@click="pause">

<iclass="glyphiconglyphicon-pause"></i>

</button>

<button:disabled='!isStarted'@click="stop">

<iclass="glyphiconglyphicon-stop"></i>

</button>

</span>

</template>

Page 248: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Trytoclickthebuttons.Theydoexactlywhatweneedthemtodo.Nicework!Checkitoutinthechapter5/pomodoro2folder.However,wearenotdoneyet.WestillhavetomakeourPomodorotimerintoanactualtimerandnotjustsomepagethatallowsyoutoclicksomebuttonsandwatchthemchangingtheirstatesfromdisabledtoenabled.

Page 249: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

BindingPomodorominutesandsecondsIntheprevioussection,wewereabletodefinethreedifferentstatesofthePomodoroapplication:started,paused,andstopped.However,let'snotforgetaboutwhatthePomodoroapplicationshouldbeusedfor.Itmustcountdownsomegiventimeforworkandthenswitchtothebreakcountdowntimer,andthencomebacktowork,andsoon.

ThisleadsustorealizethatthereisonemoreveryimportantPomodoroapplication'sstate:thebinarystatethattogglesbetweenworkingandrestingperiodsoftime.Thisstatecannotbetoggledbybuttons;itshouldsomehowbemanagedbyourapplication'sinternallogic.

Let'sstartbydefiningtwostateproperties:oneforthecounterthatwillbedecreasedwiththetimeandtheotheronetodistinguishbetweentheworkingandnot-workingstates.Let'sassumethatwhenwestartourPomodoro,westartourworkingday,sotheworkingstateshouldbesettotrueandthecountdowncountershouldbesettotheamountoftimethatwedefineforourworkingPomodoroperiod.Forthesakeofmodularityandmaintainability,let'sdefinetheamountoftimeforworkandforrestinanexternalfile.Let'scallit,forexample,config.js.Createtheconfig.jsfileintheproject'srootdirectoryandaddthefollowingcontent:

//config.js

exportconstWORKING_TIME=20*60

exportconstRESTING_TIME=5*60

BytheseinitializationsImeanthatourPomodoroshouldcountdown20minutesfortheworkingPomodorointervaland5minutesforbreaks.Ofcourse,youarefreetodefineyourownvaluesthataremostsuitableforyou.Let'snowexportconfig.jsinourstoreandreusetheWORKING_TIMEvaluetoinitializeourcounter.Let'salsocreateapropertythattogglesbetweenwork/breakandcallitisWorking.Let'sinitializeittotrue.

So,ournewstatewilllooklikethefollowing:

//store.js

<...>

import{WORKING_TIME}from'../config'

conststate={

started:false,

paused:false,

stopped:false,

isWorking:true,

counter:WORKING_TIME

}

So,wehavethesetwonicenewproperties.Beforestartingtocreatemethods,actions,mutations,andotherthingsthatdecreasethecounterandtoggletheisWorkingproperty,let'sthinkofthevisualelementsthatrelyontheseproperties.

Page 250: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Wedon'thavesomanyelements,soit'seasytodefine.

TheisWorkingstateisaffectingthetitle:weshoulddisplayWork!whenit'stimetoworkandRest!whenit'stimetohavearest.TheisWorkingstateisalsoaffectingthekittenscomponentvisibility:itshouldbedisplayedonlywhenisWorkingisfalse.Thecounterpropertyaffectsminuteandsecond:eachtimeitdecreases,thesecondshouldalsodecreaseitsvalue,andevery60decreases,theminuteshouldalsodecreaseitsvalue.

Let'sdefinegettersfunctionsfortheisWorkingstateandfortheminuteandthesecond.Afterdefiningthesegetters,wecanreusetheminourcomponentsinsteadofusingthehardcodedvalues.Let'sstartbydefiningagetterfortheisWorkingproperty:

//getters.js

exportdefault{

isStarted:state=>state.started,

isPaused:state=>state.paused,

isStopped:state=>state.stopped,

isWorking:state=>state.isWorking

}

Let'sreusethisgetterinthecomponentsthatwereusinghardcodedisworkingdefinedintheApp.vuecomponent.OpenApp.vue,removeallthereferencestotheisworkinghardcodedvariable,importthemapGettersobject,andmaptheisworkingpropertytotheisWorkingmethodinsidethecomputedpropertyasfollows:

//App.vue

<script>

<...>

import{mapGetters}from'vuex'

exportdefault{

<...>

computed:mapGetters({

isworking:'isWorking'

}),

store

}

</script>

RepeatthesamestepsinStateTitleComponent.ImportmapGettersandreplacepropswithmappedcomputedproperty:

//StateTitleComponent.vue

<script>

import{mapGetters}from'vuex'

exportdefault{

data(){

return{

workingtitle:'Work!',

Page 251: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

restingtitle:'Rest!'

}

},

computed:mapGetters({

isworking:'isWorking'

})

}

</script>

Therestisleftuntouchedinboththecomponents!Insidethetemplates,theisworkingpropertyisused.Thispropertycontinuestoexist;it'sjustimportedfromthereactiveVuexstoreandnotfromthehardcodeddata!

Nowwemustdefinegettersforminutesandseconds.Thispartistrickierbecauseinthesegetters,wehavetoapplysomecomputationtothecounterstate'sproperty.Thisisnotdifficultatall.Ourcounterrepresentsatotalnumberofseconds.Thismeansthatwecaneasilyextractminutesbydividingthecounterby60androundingtothelowestinteger(Math.floor).Thesecondscanbeextractedbytakingtheremainderofthedivisionby60.Thus,wecanwriteourgettersforminutesandsecondsinthefollowingway:

//getters.js

exportdefault{

<...>

getMinutes:state=>Math.floor(state.counter/60),

getSeconds:state=>state.counter%60

}

That'sit!Let'snowreusethesegettersintheCountdownComponent.ImportmapGettersandmapitscorrespondingmethodstotheminandsecpropertiesinsidethecomputedproperty.Donotforgettoremovethehardcodeddata.OurscripttagoftheCountdownComponent.vuewillthuslooklikethefollowing:

//CountdownComponent.vue

<script>

import{mapGetters}from'vuex'

exportdefault{

computed:mapGetters({

min:'getMinutes',

sec:'getSeconds'

})

}

</script>

Therestisleftcompletelyuntouched!Thetemplatewasreferencingtheminandsecproperties,andtheycontinuetoexist.Thecodeasitwasuntilnowcanbefoundinthechapter5/pomodoro3folder.Lookatthepage;nowthedisplayedminutesandsecondscorrespondtotheamountofworkingtimewe'vedefinedinourconfigurationfile!Ifyouchangeit,itwillchangeaswell:

Page 252: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

ChangingtheconfigurationfortheamountofworkingtimewillimmediatelyaffectthePomodoroapplicationview

Page 253: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

CreatingthePomodorotimerOkay,noweverythingisreadytoactuallystarttocountdownourworkingtimesowecanfinallyhavesomerest!Let'sdefinetwoauxiliaryfunctions,togglePomodoroandtick.

ThefirstonewilljusttoggletheisWorkingproperty.Itwillalsoredefinethestate'scounter.WhenthestateisWorking,thecountershouldcorrespondtotheworkingtime,andwhenthestateisnotworking,thecountershouldcorrespondtotherestingtime.

Thetickfunctionwilljustdecreasethecounterandcheckifithasreached"0"value,andinthiscase,willtogglethePomodorostate.Therestisalreadybeingtakencareof.So,thetogglePomodorofunctionwilllooklikethefollowing:

//mutations.js

functiontogglePomodoro(state,toggle){

if(_.isBoolean(toggle)===false){

toggle=!state.isWorking

}

state.isWorking=toggle

state.counter=state.isWorking?WORKING_TIME:RESTING_TIME

}

Ah,anddonotforgettoimportWORKING_TIMEandRESTING_TIMEfromourconfig!Also,donotforgettoimportunderscoresinceweareusingitforthe_.isBooleancheck:

//mutations.js

import_from'underscore'

import{WORKING_TIME,RESTING_TIME}from'./config'

Then,thetickfunctionwilljustdecreasethecounterandcheckifithasreachedthe"0"value:

//mutations.js

functiontick(state){

if(state.counter===0){

togglePomodoro(state)

}

state.counter--

}

Fine!It'sstillnotenough.Weneedtosettheintervalthatwouldcallthetickfunctionforeachsecond.Whereshoulditbeset?Well,itismorethanclearthatitshouldbedonewhenwestartourPomodoro,intheSTARTmutation!

ButifwesettheintervalintheSTARTmutationanditcallsthetickfunctioneachsecond,howwillitbestoppedorpausedonhittingthepauseorstopbutton?That'swhythesetIntervalandclearIntervalJavaScriptfunctionsexistandthat'swhywehaveastorewherewecansavetheinitialstatefortheintervalvalue!Let'sstartbydefiningintervalasnullinthestore'sstate:

//store.js

Page 254: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

conststate={

<...>

interval:null

}

Now,inourSTARTmutation,let'saddthefollowingcodethatinitializestheinterval:

//mutations.js

exportdefault{

[types.START](state){

state.started=true

state.paused=false

state.stopped=false

state.interval=setInterval(()=>tick(state),1000)

},

<...>

}

Wejustsetanintervalthatwillcallthetickfunctioneachsecond.Inturn,thetickfunctionwilldecreasethecounter.Thevaluesthatrelyonthecounter'svalue—minuteandsecond—willchangeandreactivelypropagatethesechangestotheview.

Ifyouclickonthestartbuttonnow,youwillsetthecountdowninaction!Yay!It'salmostdone.WejustneedtoaddtheclearIntervalfunctiononthepauseandstopmutationmethods.Apartfromthis,onthestopmethod,let'scallthetogglePomodorofunctionwithtrue,whichwillresetthePomodorotimertotheworkingstate:

//mutations.js

exportdefault{

[types.START](state){

state.started=true

state.paused=false

state.stopped=false

state.interval=setInterval(()=>tick(state),1000)

},

[types.PAUSE](state){

state.paused=true

state.started=true

state.stopped=false

clearInterval(state.interval)

},

[types.STOP](state){

state.stopped=true

state.paused=false

state.started=false

togglePomodoro(state,true)

}

}

Page 255: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

ChangingthekittenIhopeyouworkedalotandyourrestingtimehasfinallycome!Ifnotandifyoucan'twaitforit,justchangetheWORKING_TIMEvalueintheconfig.jsfileforsomethingconsiderablysmallandwaitforit.IthinkIfinallydeservesomerest,soI'vebeenstaringatthisniceimageforsomeminutesalready:

Iamstaringattheimage,thecatisstaringatme

Wouldn'tyoulikethedisplayedimagetochangesometimes?Ofcourse,youwould!Inordertoachievethis,wemustjustappendsomethingtotheimagesourcesothatitchangeswithtimeanddeliversanon-cachedimagetous.

Tip

Oneofthebestpracticestodelivernon-cachedthingsistoappendthetimestamptotherequestedURL.

Page 256: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Wecould,forexample,haveanotherpropertyinourstore,let'ssay,timestamp,whichwouldbeupdatedwitheachcounterdecreaseanditsvaluewouldbeappendedtotheimage-sourceURL.Let'sdoit!Let'sstartbydefiningatimestamppropertyinourstore'sstateasfollows:

//store.js

conststate={

<...>

timestamp:0

}

Tellthetickfunctiontoupdatethisvalueoneachtick:

//mutations.js

functiontick(state){

<...>

state.timestamp=newDate().getTime()

}

Createthegetterforthisvalueingetters.jsanduseitinsidetheKittensComponentbyaccessingthethis.$store.getters.getTimestampmethodinsidethecomputedproperty:

//getters.js

exportdefault{

<...>

getTimestamp:state=>state.timestamp

}

//KittensComponent.vue

<script>

exportdefault{

computed:{

catimgsrc(){

return'http://thecatapi.com/api/images/get?size=med&ts='

+this.$store.getters.getTimestamp

}

}

}

</script>

Nowit'salittlebittoofast,right?Let'sdefineatimetoshoweachkitten.It'snotdifficultatall.Forexample,ifwedecidetoshoweachkittenfor3seconds,beforechangingthestateofthetimestampinsidethetickfunction,wejusthavetocheckifthecountervalueisdivisibleby3.Let'salsomaketheamountofsecondstoshowthekittenconfigurable.Addthefollowingtoconfig.js:

//config.js

exportconstWORKING_TIME=0.1*60

exportconstRESTING_TIME=5*60

exportconstKITTEN_TIME=5//eachkittenisvisiblefor5seconds

Nowimportittothemutations.jsfileanduseitinthetickfunctiontocheckifit'stimetochangethetimestamp'svalue:

Page 257: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

//mutations.js

import{WORKING_TIME,RESTING_TIME,KITTEN_TIME}from'./config'

<...>

functiontick(state){

<...>

if(state.counter%KITTEN_TIME===0){

state.timestamp=newDate().getTime()

}

}

Wearedone!Youcancheckthefinalcodeforthissectioninthechapter5/pomodoro4folder.Yes,I'vesettheworkingtimeto6secondssothatyoucanhaveabreakandenjoysomereallynicekittensfromthecatapi.com.

So,beforereadingthesummaryofthischapterandstartingthenextone,takeabreak!Justlikethiswonderfulspecies:

Wonderfulthinghavingitsbreak.Belikehim.Takeabreak.

Page 258: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

SummaryInthischapter,yousawhowtousetheeventshandlingandtriggeringmechanismtopropagatethecomponents'datachangestotheirparents.

Mostimportantly,youusedthepowerofVuexarchitecturetobeabletoestablishthedataflowbetweenthecomponents.Yousawhowthestoreiscreated,anditsmainparts,mutations,andstates.Youlearnedhowtostructuretheapplicationthatusesthestoresothatitbecomesmodularandmaintainable.Youalsolearnedhowtocreatethestore'sgettersandhowtodefineactionsthatdispatchthestorestate'smutations.Weappliedallthelearnedmechanismstoourapplicationsandsawthedataflowinaction.

Atthispoint,weareabletouseanydataexchangingmechanisminVueapplications,startingfromsimplelocaldatabindinginsidethecomponentsandgoingfurthertoglobalstatemanagement.Atthispoint,weknowallthebasestooperatedatainsideourVueapplication.We'realmostdone!

Inthenextchapter,wewillgodeepintothepluginssystemforVueapplications.Youwilllearnhowtouseexistingpluginsandcreateyourownplugintoenrichyourapplicationswithcustombehavior.

Page 259: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Chapter6.Plugins–BuildingYourHousewithYourOwnBricksInthepreviouschapter,youlearnedhowtomanagetheglobalapplicationstoreusingtheVuexarchitecture.Youlearnedalotofnewconceptsandappliedthem.Youalsolearnedhowtocreateastore,howtodefineitsstateandmutations,andhowtouseactionsandgetters.WebroughtourshoppinglistandPomodoroapplicationstolifeusingtheknowledgeacquiredduringthechapter.

Inthischapter,wewillrevisitVueplugins,seehowtheywork,andhowtheymustbecreated.Wewillusesomeexistingpluginsandcreateourown.

Summingitup,inthischapter,wearegoingtodothefollowing:

UnderstandthenatureofVuepluginsUsetheresourcepluginintheshoppinglistsapplicationCreateapluginthatproduceswhite,pink,andbrownnoisesandapplyittoourPomodoroapplication

Page 260: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

ThenatureofVuepluginsPluginsinVue.jsareusedforexactlythesamepurposeastheyareusedinanyotherscope:toaddsomenicefunctionalitythat,duetoitsnature,cannotbeachievedwiththecorefunctionalityofthesystem.PluginswrittenforVuecanprovidevariousfunctionalities,startingfromthedefinitionofsomeglobalVuemethodsoreventheinstancemethodsandmovingtowardprovidingsomenewdirectives,filters,ortransitions.

Inordertobeabletouseanexistingplugin,youmustfirstinstallit:

npminstallsome-plugin--save-dev

Andthen,tellVuetouseitinyourapplication:

varVue=require('vue')

varSomePlugin=require('some-plugin')

Vue.use(SomePlugin)

Wecanalsocreateourownplugins.Thisisalsoeasy.Yourpluginmustprovideaninstallmethodwhereyoudefineanyglobalorinstancemethods,orcustomdirectives:

MyPlugin.install=function(Vue,options){

//1.addglobalmethodorproperty

Vue.myGlobalMethod=...

//2.addaglobalasset

Vue.directive('my-directive',{})

//3.addaninstancemethod

Vue.prototype.$myMethod=...

}

Thenitcanbeusedjustlikeanyotherexistingplugin.Inthischapter,wewillusetheexistingresourcepluginforVue(https://github.com/vuejs/vue-resource)andcreateourownpluginthatgenerateswhite,pink,andbrownnoises.

Page 261: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Usingthevue-resourcepluginintheshoppinglistapplicationOpentheshoppinglistapplication(thechapter6/shopping-listfolder)andrunnpminstallandnpmrundev.It'sniceandclean,butitstillusesthehardcodedlistoftheshoppinglists.Itwouldbereallyniceifwewereabletoaddnewshoppinglists,deletethem,andstoretheinformationonupdatedshoppinglistssothatwhenwerestarttheapplication,thedisplayedinformationcorrespondstothelastwesawbeforerestarting.Inordertobeabletodothat,wewillusetheresourceplugin,whichallowsustoeasilycreateRESTresourcesandcallRESTmethodsonthem.Beforestarting,let'ssummarizeeverythingthatweneedtodoinordertoachievethis:

Firstofall,weneedtohaveasimpleserverthatcontainssomestoragefromwherewecanretrieveandwherewecanstoreourshoppinglists.Thisservermustprovidetheneededendpointsforallthisfunctionality.Aftercreatingourserverandallneededendpoints,weshouldinstallandusethevue-resourceplugintocreatearesourceandactionsthatwillcallthemethodsontheprovidedendpoints.Inordertoguaranteethedataintegrity,weshouldcallactionsthatupdateserver'sstateoneachshoppinglistsupdate.Ontheapplicationstart,weshouldfetchshoppinglistsfromtheserverandassignthemtoourstore'sstate.Weshouldalsoprovideamechanismtocreatenewshoppinglistsanddeletetheexistingones.

Doesn'tsoundtoodifficult,right?Let'sstartthen!

Page 262: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

CreatingasimpleserverForthesakeofsimplicity,wewilluseaverybasicandeasy-to-useHTTPserverthatstoresdatainsidearegularJSONfile.Itiscalledjson-serveranditishostedathttps://github.com/typicode/json-server.Installitintheshoppinglistapplication'sdirectory:

cdshopping-list

npminstall--save-devjson-server

Createaserverfolderwiththedb.jsonfileinsideitwiththefollowingcontent:

//shopping-list/server/db.json

{

"shoppinglists":[

]

}

Thiswillbeourdatabase.Let'saddthescriptentrytoourpackage.jsonfilesothatwecaneasilystartourserver:

"scripts":{

"dev":"nodebuild/dev-server.js",

"server":"node_modules/json-server/bin/index.js--watch

server/db.json",

<...>

},

Now,tostartaserver,justrunthefollowing:

cdshopping-list

npmrunserver

Openthebrowserpageathttp://localhost:3000/shoppinglists.Youwillseeanemptyarrayasaresult.Thisisbecauseourdatabaseisstillempty.Trytoinsertsomedatausingcurl:

curl-H"Content-Type:application/json"-d'{"title":"new","items":[]}'

http://localhost:3000/shoppinglists

Ifyourefreshthepagenow,youwillseeyournewinsertedvalue.

NowthatwehaveoursimpleRESTserverupandrunning,let'suseitinourshoppinglistapplicationwiththehelpofthevue-resourceplugin!

Page 263: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Installingvue-resource,creatingresources,anditsmethodsBeforegoingdeeperintotheusageofthevue-resourceplugin,checkoutitsdocumentationathttps://github.com/vuejs/vue-resource/blob/master/docs/resource.md.Basically,thedocumentationprovidesaneasywayofcreatingresourcesbasedonthegivenURL(inourcase,itwillbehttp://localhost:3000/shoppinglists).Aftertheresourceiscreated,wecancallget,delete,post,andupdatemethodsonit.

Installitintheproject'sfolder:

cdshopping-list

npminstallvue-resource--save-dev

Nowlet'screatetheentrypointforourAPI.Insideansrcfolderoftheshoppinglistapplication,createasubfolderandcallitapi.Createanindex.jsfileinsideit.Inthisfile,wewillimportthevue-resourcepluginandtellVuetouseit:

//api/index.js

importVuefrom'vue'

importVueResourcefrom'vue-resource'

Vue.use(VueResource)

Nice!NowwearereadytocreateShoppingListsResourceandattachsomemethodstoit.Tocreatearesourceusingthevue-resourceplugin,wejustcallaresourcemethodonVueandpasstheURLtoit:

constShoppingListsResource=Vue.resource('http://localhost:3000/'+

'shoppinglists{/id}')

TheShoppingListsResourceconstantnowexposesallthemethodsneededfortheimplementationofCRUD(Create,Read,Update,andDelete)operations.Itissoeasytousethatwecouldbasicallyexporttheresourceitself.Butlet'sexportnicemethodsforeachoftheCRUDoperations:

exportdefault{

fetchShoppingLists:()=>{

returnShoppingListsResource.get()

},

addNewShoppingList:(data)=>{

returnShoppingListsResource.save(data)

},

updateShoppingList:(data)=>{

returnShoppingListsResource.update({id:data.id},data)

},

deleteShoppingList:(id)=>{

returnShoppingListsResource.remove({id:id})

}

}

Page 264: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Thefullcodefortheapi/index.jsfilecanbeseeninthisgistathttps://gist.github.com/chudaol/d5176b88ba2c5799c0b7b0dd33ac0426.

That'sit!OurAPIisreadytobeusedandtopopulateourreactiveVuedata!

Page 265: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

FetchingalltheshoppingliststheapplicationstartsLet'sstartbycreatinganactionthatwillfetchandpopulatestore'sshoppinglistsstate.Afteritscreation,wecancallitonthemainApp.vuereadystate.

Defineaconstantinthemutation_types.jsfileasfollows:

//mutation_types.js

exportconstPOPULATE_SHOPPING_LISTS='POPULATE_SHOPPING_LISTS'

Nowcreateamutation.Thismutationwilljustreceiveanarrayofshoppinglistsandassignittotheshoppinglistsstate:

//mutations.js

exportdefault{

[types.CHANGE_TITLE](state,data){

findById(state,data.id).title=data.title

},

[types.POPULATE_SHOPPING_LISTS](state,lists){

state.shoppinglists=lists

}

}

Okthen!NowwejustneedanactionthatwillusetheAPI'sgetmethodanddispatchthepopulatingmutation.ImporttheAPIintheactions.jsfileandcreateacorrespondingactionmethod:

import{CHANGE_TITLE,POPULATE_SHOPPING_LISTS}from'./mutation_types'

importapifrom'../api'

exportdefault{

changeTitle:({commit},data)=>{

commit(CHANGE_TITLE,data)

},

populateShoppingLists:({commit})=>{

api.fetchShoppingLists().then(response=>{

commit(POPULATE_SHOPPING_LISTS,response.data)

})

}

}

Intheprecedinglinesofcode,weperformaverysimpletask—wecallthefetchShoppingListsAPI'smethodthat,inturn,callsthegetmethodoftheresource.ThismethodperformsanhttpGETcallandreturnsapromisethatisresolvedwhenthedataisbackfromtheserver.

Thisdataisthenusedtodispatchthepopulatingmutationwithit.Thismethodwillassignthisdatatothestore'sstateshoppinglistsproperty.Thispropertyisreactive;doyouremember?Thismeansthatalltheviewsthatrelyontheshoppinglistspropertygetterwillbeupdated.Let'snowusethisactioninthemainApp.vuecomponentonitsmountedstate.CheckmoreaboutmountedstatehookintheofficialVuedocumentationpageat

Page 266: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

https://vuejs.org/v2/api/#mounted.

OpentheApp.vuecomponent,importthemapActionsobject,mapthepopulateShoppingListsactioninsidethecomponent'smethodsproperty,andcallitinsidethemountedhandler.So,afterthechanges,thescripttagofApp.vuelookslikethefollowing:

<script>

importShoppingListComponentfrom'./components/ShoppingListComponent'

importShoppingListTitleComponentfrom

'./components/ShoppingListTitleComponent'

importstorefrom'./vuex/store'

import{mapGetters,mapActions}from'vuex'

exportdefault{

components:{

ShoppingListComponent,

ShoppingListTitleComponent

},

computed:mapGetters({

shoppinglists:'getLists'

}),

methods:mapActions(['populateShoppingLists']),

store,

mounted(){

this.populateShoppingLists()

}

}

</script>

Ifyouopenthepagenow,youwillseetheonlyshoppinglistthatwecreatedusingcurl,asshowninthefollowingscreenshot:

Thedisplayedshoppinglistsarebeingservedbyoursimpleserver!

Page 267: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Trytoinsertmoreitemsusingcurlorevendirectlymodifyingthedb.jsonfile.Refreshthepageandlookhowitworkslikeacharm!

Page 268: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

UpdatingserverstatusonchangesVerywell,nowwehaveourshoppinglistsbeingservedbyourRESTAPIandeverythingworksandlooksnice.Trytoaddsomeshoppinglistitemsorchangethetitlesoftheshoppinglistsandcheckoruncheckitems.Afteralltheseinteractions,refreshthepage.Whoops,thelistsareempty,nothinghappened.That'sabsolutelycorrect,wehaveanAPImethodforupdatingthegivenshoppinglistbutwedon'tcallitanywhere,soourserverisnotawareoftheappliedchanges.

Let'sstartbydefiningwhatcomponentsdosomethingwithourshoppinglistssothatthesechangesaresenttotheserver.Thefollowingthreethingscanhappentotheshoppinglistsandtheiritems:

ThetitleofthelistcanbechangedinChangeTitleComponentThenewitemcanbeaddedtotheshoppinglistinAddItemComponentTheitemoftheshoppinglistcanbecheckedoruncheckedinItemComponent

Wemustcreateanactionthatmustbetriggeredonallthesechanges.Withinthisaction,weshouldcalltheupdateAPI'smethod.Haveacloselookattheupdatemethodinsidetheapi/index.jsmodule;itmustreceivethewholeshoppinglistobjectasaparameter:

//api/index.js

updateShoppingList:(data)=>{

returnShoppingListsResource.update({id:data.id},data)

}

Let'screateanactionthatreceivesanidasaparameter,retrievestheshoppinglistbyitsID,andcallstheAPI'smethod.Beforedoingthis,createagetListByIdmethodinthegetters.jsfileandimportitintotheactions:

//getters.js

import_from'underscore'

exportdefault{

getLists:state=>state.shoppinglists,

getListById:(state,id)=>{

return_.findWhere(state.shoppinglists,{id:id})

}

}

//actions.js

importgettersfrom'./getters'

Nowwearereadytodefinetheactionforupdatingtheshoppinglist:

//actions.js

<...>

exportdefault{

<...>

updateList:(store,id)=>{

Page 269: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

letshoppingList=getters.getListById(store.state,id)

api.updateShoppingList(shoppingList)

}

}

Actually,wecannowdeletethefindByIdmethodfrommutations.jsandjustreusethisonefromgetters.js:

//mutations.js

import*astypesfrom'./mutation_types'

importgettersfrom'./getters'

exportdefault{

[types.CHANGE_TITLE](state,data){

getters.getListById(state,data.id).title=data.title

},

[types.POPULATE_SHOPPING_LISTS](state,lists){

state.shoppinglists=lists

}

}

Well,nowwehavedefinedtheactionthatcallstheupdateListmethodofourAPI.Nowwejusthavetocalltheactiononeachchangethathappensinsidethecomponents!

Let'sstartwithAddItemComponent.WemustdispatchtheupdateListactioninsidetheaddItemmethodusingthethis.$store.dispatchmethodwiththeaction'sname.However,there'sasmallproblem—wemustpassthelistitemIDtotheupdateListmethodandwedonothaveareferencetoitinsidethiscomponent.Butit'sactuallyaneasyfix.JustaddtheIDinsidethecomponent'spropsandbindittothecomponentonitsinvocationinsideShoppingListComponent.SoourAddItemComponentcomponent'sscripttaglookslikethefollowing:

//AddItemComponent.vue

<script>

exportdefault{

props:['id'],

data(){

return{

newItem:''

}

},

methods:{

addItem(){

vartext

text=this.newItem.trim()

if(text){

this.$emit('add',this.newItem)

this.newItem=''

this.$store.dispatch('updateList',this.id)

}

}

Page 270: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

}

}

</script>

And,insideShoppingListComponent,ontheadd-item-componentinvocation,bindtheIDtoit:

//ShoppingListComponent.vue

<template>

<...>

<add-item-component:id="id"@add="addItem"></add-item-component>

<...>

</template>

Now,ifyoutrytoadditemstotheshoppinglistsandrefreshthepage,thenewlyaddeditemsappearinthelist!

NowweshoulddothesameforChangeTitleComponent.OpentheChangeTitleComponent.vuefileandcheckthecode.Rightnow,itcallsthechangeTitleactiononinput:

//ChangeTitleComponent.vue

<template>

<div>

<em>Changethetitleofyourshoppinglisthere</em>

<input:value="title"@input="onInput({title:

$event.target.value,id:id})"/>

</div>

</template>

<script>

import{mapActions}from'vuex'

exportdefault{

props:['title','id'],

methods:mapActions({

onInput:'changeTitle'

})

}

</script>

Wecould,ofcourse,importtheupdateListactionandcallitrightaftercallingthechangeTitleaction.Butitmightbeeasiertodoitinsidetheactionitself.Youmayrememberthatinordertodispatchthestore'saction,weshouldcallthedispatchmethodappliedtothestorewiththeaction'snameasaparameter.SowecandoitinsidethechangeTitleaction.Justopentheaction.jsfile,findourchangeTitleaction,andaddthecalltoupdateList:

//actions.js

exportdefault{

changeTitle:(store,data)=>{

store.commit(CHANGE_TITLE,data)

store.dispatch('updateList',data.id)

},

<...>

Page 271: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

}

It'sdone!Openthepage,modifythetitlesofthepages,andrefreshthepage.Thetitlesshouldmaintaintheirmodifiedstate!

Thelastchangethatweneedtoguaranteetobepersistedisthechangeintheshoppinglist'sitemscheckedproperty.Let'slookatItemComponentanddecidewhereweshouldcalltheupdateListaction.

Let'sstartbyaddingtheIDinsidethepropsattribute,justlikewedidwithAddItemComponent:

//ItemComponent.vue

<script>

exportdefault{

props:['item','id']

}

</script>

Wemustalsobindtheidpropertytothecomponent'sinvocation,whichisdoneinsideItemsComponent:

//ItemsComponent.vue

<template>

<ul>

<item-componentv-for="iteminitems":item="item":id="id">

</item-component>

</ul>

</template>

<script>

importItemComponentfrom'./ItemComponent'

exportdefault{

components:{

ItemComponent

},

props:['items','id']

}

</script>

Thisalsomeansthatwemustbindtheidpropertytoitem-componentinsideShoppingListComponent:

//ShoppingListComponent.vue

<template>

<...>

<items-component:items="items":id="id"></items-component>

<...>

</template>

WeshouldalsoimportthemapActionsobjectinsideItemComponentandexporttheupdateListmethodinsidethemethodsproperty:

Page 272: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

//ItemComponent.vue

<script>

import{mapActions}from'vuex'

exportdefault{

props:['item','id'],

methods:mapActions(['updateList'])

}

</script>

Okaythen,everythingisboundtoeverything;nowwejusthavetofindtherightplaceinsideItemComponenttocalltheupdateListaction.

Andthisturnsouttobenotsuchaseasytask,becauseunlikeintheothercomponentswherewehadeventhandlersdealingwithchangesandcallingthecorrespondingfunctions,herewejusthaveclassandmodelbindingsattachedtothecheckboxelement.Luckilyforus,Vueprovidesawatchoptionthatallowsustoattachlistenerstoanyofthecomponent'sdataandbindthehandlerstothem.Inourcase,wewanttowatchtheitem.checkedpropertyandcalltheaction.So,justaddthewatchattributetothecomponentsoptionsasfollows:

//ItemComponent.vue

<script>

import{mapActions}from'vuex'

exportdefault{

props:['item','id'],

methods:mapActions(['updateList']),

watch:{

'item.checked':function(){

this.updateList(this.id)

}

}

}

</script>

And...wearedone!Trytoadditemstotheshoppinglists,check,uncheck,andcheckthemagain.Refreshthepage.Everythinglookslikeitwasbeforerefreshing!

Page 273: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

CreatinganewshoppinglistOkaythen,wearealreadyfetchingtheshoppinglistsfromtheserver;wealsostoreappliedchanges,sowearefine.Butwouldn'titalsobeniceifwecouldcreatetheshoppinglistsusingtheuserinterfaceofourapplicationinsteadofmodifyingthedb.jsonfileorusingcurlpostrequests?Ofcourse,itwouldbenice.And,ofcourse,wecandoitwithfewlinesofcode!

Let'sstartbyaddingtheactionthatcallsthecorrespondingAPImethod,asfollows:

//actions.js

exportdefault{

<...>

createShoppingList:({commit},shoppinglist)=>{

api.addNewShoppingList(shoppinglist)

}

}

Nowwehavetoprovideavisualmechanismforcallingthisaction.Forthat,wecancreateanextratabinthetablistwiththeplusbutton,whichwillcalltheactionwhenitisclicked.WewilldoitinsidetheApp.vuecomponent.WehavealreadyimportedthemapActionsobject.Let'sjustaddthecreateShoppingListmethodtotheexportedmethodsproperty:

//App.vue

<script>

importShoppingListComponentfrom'./components/ShoppingListComponent'

importShoppingListTitleComponentfrom

'./components/ShoppingListTitleComponent'

importstorefrom'./vuex/store'

import{mapGetters,mapActions}from'vuex'

exportdefault{

components:{

ShoppingListComponent,

ShoppingListTitleComponent

},

computed:mapGetters({

shoppinglists:'getLists'

}),

methods:mapActions(['populateShoppingLists',

'createShoppingList']),

store,

mounted(){

this.populateShoppingLists()

}

}

</script>

Atthismoment,ourApp.vuecomponenthasaccesstothecreateShoppingListactionandcancallitonaneventhandler.Thequestionis—withwhatdata?ThecreateShoppingListmethodiswaitingtoreceiveanobjectthatwillthenbesenttotheserver.Let'screateamethodthatwillgenerateanewlistwithahardcodedtitle,andwithinthismethod,calltheactionwiththis

Page 274: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

newobject.Butwhereshoulditputthismethod?ThemethodspropertyofthecomponentisalreadyoccupiedbytheinvocationofthemapActionshelper.Well,themapActionsmethodreturnsamapofmethods.Wecansimplyextendthismapwithourlocalmethod:

//App.vue

methods:_.extend({},

mapActions(['populateShoppingLists','createShoppingList']),

{

addShoppingList(){

letlist={

title:'NewShoppingList',

items:[]

}

this.createShoppingList(list)

}

}),

NowwejustneedtoaddabuttonandbindtheaddShoppingListmethodtoitsclickevent.Youcancreateyourownbuttonanywhereonthepage.Mybutton'scodelookslikethefollowing:

App.vue

<template>

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

<ulclass="navnav-tabs"role="tablist">

<li:class="index===0?'active':''"v-for="(list,index)in

shoppinglists"role="presentation">

<shopping-list-title-component:id="list.id"

:title="list.title"></shopping-list-title-component>

</li>

<li>

<ahref="#"@click="addShoppingList">

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

</a>

</li>

</ul>

<divclass="tab-content">

<div:class="index===0?'active':''"v-for="(list,index)in

shoppinglists"class="tab-pane"role="tabpanel":id="list.id">

<shopping-list-component:id="list.id":title="list.title"

:items="list.items"></shopping-list-component>

</div>

</div>

</div>

</template>

Lookatthepage;nowwehaveaniceplusbuttononthelasttab,whichclearlyindicatesthatthereisapossibilityofaddinganewshoppinglist,asshowninthefollowingscreenshot:

Page 275: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Nowwecanaddnewshoppinglistsusingthisniceplusbutton

Trytoclickonthebutton.Whoops,nothinghappens!However,ifwelookattheNetworkpanel,wecanseetherequestwasactuallyperformedandthatsucceeded:

Page 276: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Thecreationrequestwasperformedsuccessfully;however,nothingchangedonthepage

Actually,thismakesperfectsense.Weupdatedtheinformationontheserver,buttheclientsideisnotawareofthesechanges.Ifwecouldpopulateshoppinglistsafterthesuccessfulshoppinglistcreation,itwouldbenice,wouldn'tit?DidIsay"ifwecould"?Ofcoursewecan!Justgobacktoactions.jsandcallthepopulateShoppingListsactiononthepromise'sthencallbackusingthestore.dispatchmethod:

//actions.js

createShoppingList:(store,shoppinglist)=>{

api.addNewShoppingList(shoppinglist).then(()=>{

store.dispatch('populateShoppingLists')

})

}

Now,ifyouclickontheplusbutton,youwillimmediatelyseethenewlycreatedlistappearinginthetabpane,asshowninthefollowingscreenshot:

Newlyaddedshoppinglistafterrepopulatingourlists

Youcannowclickonthenewshoppinglist,changeitsname,additsitems,andcheckanduncheckthem.Whenyourefreshthepage,everythingisjustlikeitwasbeforetherefreshing.Amazingwork!

Page 277: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

DeletingexistingshoppinglistsWearealreadyabletocreateandupdateourshoppinglists.Nowwejustneedtobeabletodeletethem.Afterallthethingsthatwehavelearnedinthischapter,thiswillbetheeasiestpart.WeshouldaddtheactionthatwillcallthedeleteShoppingListmethodofourAPI,addtheremovebuttontoeachoftheshoppinglist,andcalltheactiononthebuttonclick.

Let'sstartbyaddingtheaction.Similarly,aswedidwiththecreationofshoppinglists,wewillcallthepopulatemethodrightafterremovingtheshoppinglist,soouractionwilllooklikethefollowing:

//action.js

deleteShoppingList:(store,id)=>{

api.deleteShoppingList(id).then(()=>{

store.dispatch('populateShoppingLists')

})

}

Nowlet'sthinkwhereweshouldaddtheremovebutton.Iwouldliketoseeitneartheshoppinglisttitleinthetabheader.ThisisthecomponentcalledShoppingListTitleComponent.OpenitandimportthemapActionshelper.Exportitinthemethodsproperty.So,thecodeinsidethescripttagofthiscomponentlookslikethefollowing:

//ShoppingListTitleComponent.vue

<script>

import{mapActions}from'vuex'

exportdefault{

props:['id','title'],

computed:{

href(){

return'#'+this.id

}

},

methods:mapActions(['deleteShoppingList'])

}

</script>

Nowlet'saddtheremovebuttonandbindthedeleteShoppingListmethodtoitsclickeventlistener.WeshouldpasstheIDtothismethod.Wecandoitdirectlyinsidethetemplate:

//ShoppingListTitleComponent.vue

<template>

<a:href="href":aria-controls="id"role="tab"data-toggle="tab">

{{title}}

<iclass="glyphiconglyphicon-remove"

@click="deleteShoppingList(id)"></i>

</a>

</template>

Page 278: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Ialsoaddedalittlebitofstylingtotheremoveiconsothatitlooksabitsmallerandabitmoreelegant:

<stylescoped>

i{

font-size:x-small;

padding-left:3px;

cursor:pointer;

}

</style>

That'sit!Openthepageandyou'llseeatinyxbuttonneareachshoppinglisttitle.Tryclickingonitandyouwillimmediatelyseethechanges,asshowninthefollowingscreenshot:

ShoppinglistswiththeremoveXbuttonthatallowsustodeleteunusedshoppinglists

Congratulations!Nowwehaveafullyfunctionalapplicationthatallowsustocreateshoppinglistsforanyoccasion,removethem,andmanagetheitemsoneachofthem!Goodwork!Thefinalcodeforthissectioncanbefoundinthechapter6/shopping-list2folder.

Page 279: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

ExerciseOurshoppinglistsareallverysimilartoeachother.Iwouldliketoproposeasmallstylingexerciseinwhichyoushouldattachcoloringtoyourlistsinordertomakethemdifferonefromanother.Itwillrequireyoutoaddonemorefieldforthebackgroundcolorontheshoppinglistcreationandtouseitinsidethecomponenttopaintyourlistswiththegivencolor.

Page 280: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

CreatingandusingaplugininthePomodoroapplicationNowthatweknowhowtouseexistingpluginswithourVueapplication,whynotcreateourownplugin?WealreadyhavealittlebitofanimationinourPomodoroapplication,andthescreenchangescompletelywhenthestateischangedfromtheworkingPomodorointervaltotherestinginterval.However,ifwearenotlookingatthetab,wehavenoideaifweshouldworkorrest.ItwouldbenicetoaddsomesoundstoourPomodoro!

Whenthinkingaboutsoundsinatimemanagementapplication,Iwouldliketothinkaboutthesoundthatisniceforworking.Everyoneofushasourownfavoriteplaylistforwork.Ofcourse,itdiffersaccordingtoeachperson'smusicalpreferences.That'swhyIdecidedtoaddsomeneutralsoundtoourapplicationduringtheworkingperiodoftime.Itwasprovenbysomestudiesthatdifferentnoises(white,pink,brown,andsoon)aregoodforthekindofworkwhereahighlevelofconcentrationisrequired.TheWikipediaentryaboutthesestudiescanbefoundathttps://en.wikipedia.org/wiki/Sound_masking.AndsomeQuoraexpertstalkingaboutthiscanbefoundathttp://bit.ly/2cmRVW2.

Inthissection,wewillusetheWebAudioAPI(https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API)tocreateapluginforVuethatgenerateswhite,pink,andbrownnoises.WewillprovideamechanismtoinstantiateonenoiseoranotherusingVuedirectivesandwewillalsoprovideglobalVuemethodsthatwillstartandpausethesesounds.Afterthat,wewillusethisplugintoswitchbetweenasilentstatewhilerestingandlookingatcatsandanoisystatewhileworking.Doesitsoundchallengingandinteresting?Ireallyhopeitdoes!Let'sstartthen!

Page 281: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

CreatingtheNoiseGeneratorpluginOurpluginwillbestoredinasingleJavaScriptfile.Itwillcontainthreemethods,oneforthegenerationofeachnoiseandprovideaVue.installmethodwherethedirectivesandneededVuemethodswillbedefined.Usethechapter6/pomodorofolderasastartingpoint.StartbycreatingapluginssubfolderinthesrcfolderandaddingtheVueNoiseGeneratorPlugin.jsfilethere.Nowlet'screatethefollowingthreemethods:

generateWhiteNoise

generatePinkNoise

generateBrownNoise

IwillnotreinventthewheelandwilljustcopyandpastethealreadyexistingcodethatIfoundontheInternet.Ofcourse,IwouldliketogivehugecredittothegreatresourcethatIfoundathttp://noisehack.com/generate-noise-web-audio-api/.Thatbeingsaid,ourpluginaftercopyingthecodeandorganizingitinthefunctionsshouldlooklikethefollowing:

//plugins/VueNoiseGenerator.js

import_from'underscore'

//Thankstothisgreattutorial:

//http://noisehack.com/generate-noise-web-audio-api/

varaudioContext,bufferSize,noise

audioContext=new(window.AudioContext||window.webkitAudioContext)()

functiongenerateWhiteNoise(){

varnoiseBuffer,output

bufferSize=2*audioContext.sampleRate

noiseBuffer=audioContext.createBuffer(1,bufferSize,

audioContext.sampleRate)

output=noiseBuffer.getChannelData(0)

_.times(bufferSize,i=>{

output[i]=Math.random()*2-1

})

noise=audioContext.createBufferSource()

noise.buffer=noiseBuffer

noise.loop=true

noise.start(0)

returnnoise

}

functiongeneratePinkNoise(){

bufferSize=4096

noise=(function(){

varb0,b1,b2,b3,b4,b5,b6,node

b0=b1=b2=b3=b4=b5=b6=0.0

node=audioContext.createScriptProcessor(bufferSize,1,1)

node.onaudioprocess=function(e){

varoutput

Page 282: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

output=e.outputBuffer.getChannelData(0)

_.times(bufferSize,i=>{

varwhite=Math.random()*2-1

b0=0.99886*b0+white*0.0555179

b1=0.99332*b1+white*0.0750759

b2=0.96900*b2+white*0.1538520

b3=0.86650*b3+white*0.3104856

b4=0.55000*b4+white*0.5329522

b5=-0.7616*b5-white*0.0168980

output[i]=b0+b1+b2+b3+b4+b5+b6+white*0.5362

output[i]*=0.11//(roughly)compensateforgain

b6=white*0.115926

})

}

returnnode

})()

returnnoise

}

functiongenerateBrownNoise(){

bufferSize=4096

noise=(function(){

varlastOut,node

lastOut=0.0

node=audioContext.createScriptProcessor(bufferSize,1,1)

node.onaudioprocess=function(e){

varoutput=e.outputBuffer.getChannelData(0)

_.times(bufferSize,i=>{

varwhite=Math.random()*2-1

output[i]=(lastOut+(0.02*white))/1.02

lastOut=output[i]

output[i]*=3.5//(roughly)compensateforgain

})

}

returnnode

})()

returnnoise

}

YoucantestallthesenoisesintheJSFiddleathttps://jsfiddle.net/chudaol/7tuewm5z/.

Okay,sowehaveallthethreenoisesimplemented.NowwemustexporttheinstallmethodthatwillbecalledbyVue.ThismethodreceivestheVueinstanceandcancreatedirectivesandmethodsonit.Let'screateadirectiveandcallitnoise.Thisdirectivecanhaveoneofthreevalues,white,pink,orbrown,andaccordingtothereceivedvaluewillinstantiatethenoisevariablebycallingthecorrespondingnoisecreationmethod.So,ourdirectivecreationwithinaninstallmethodwilllooklikethefollowing:

//plugins/VueNoiseGeneratorPlugin.js

Page 283: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

exportdefault{

install:function(Vue){

Vue.directive('noise',(value)=>{

varnoise

switch(value){

case'white':

noise=generateWhiteNoise()

break

case'pink':

noise=generatePinkNoise()

break

case'brown':

noise=generateBrownNoise()

break

default:

noise=generateWhiteNoise()

}

noise.connect(audioContext.destination)

audioContext.suspend()

})

}

}

Aftertheinstantiation,weconnectthenoisetothealreadyinstantiatedaudioContextandsuspenditbecausewedon'twantittostartproducingthenoiserightonthedirectivebinding.Wewantittobeinstantiatedonsomeevents(forexample,clickingonthestartbutton)andpausedonotherevents(forexample,whensomeoneclicksonthepausebutton).Forthat,let'sprovidemethodsforstarting,pausing,andstoppingouraudioContext.WewillputthesethreemethodsontheglobalVuepropertycallednoise.Wewillcallthesemethodsstart,pause,andstop.Withinthestartmethod,wewanttoresumeaudioContextandsuspenditonboththepauseandstopmethods.So,ourmethodswilllooklikethefollowing:

//plugins/VueNoiseGeneratorPlugin.js

exportdefault{

install:function(Vue){

Vue.directive('noise',(value)=>{

<...>

})

Vue.noise={

start(){

audioContext.resume()

},

pause(){

audioContext.suspend()

},

stop(){

audioContext.suspend()

}

}

}

}

That'sit!Ourpluginiscompletelyreadytobeused.It'snotperfect,ofcourse,becausewe

Page 284: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

onlyhaveoneaudioContext,whichisbeinginstantiatedonceandthenpopulatedbyoneofthechosennoises,meaningwewillnotbeabletousethenoisedirectivemorethanonceonthepage,butagain,thisisjustaprototypeandyouaremorethanwelcometoenhanceitandmakeitperfectandpublic!

Page 285: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

UsingtheplugininthePomodoroapplicationFinethen,nowwehaveournicenoise-producingplugin,andtheonlythingthatismissingisusingit!Youalreadyknowhowtodoit.Openthemain.jsfile,importVueNoiseGeneratorPlugin,andtellVuetouseit:

importVueNoiseGeneratorPluginfrom

'./plugins/VueNoiseGeneratorPlugin'

Vue.use(VueNoiseGeneratorPlugin)

Fromnowon,wecanattachthenoisedirectiveandusetheVue.noisemethodinanypartofourPomodoroapplication.Let'sbindittoourmaintemplateinsidetheApp.vuecomponent:

//App.vue

<template>

<divid="app"class="container"v-noise="'brown'">

<...>

</div>

</template>

Notethatweusev-noiseinthenameofthedirectiveandnotjustnoise.Wealreadytalkedaboutitwhenwelearnedcustomdirectives.Touseadirective,weshouldalwaysprependthev-prefixtoitsname.Alsonotethatweuseddoublequotesinsidethesinglequotestowrapthebrownstring.Ifwedidn'tdoit,Vuewouldsearchforthedatapropertycalledbrown,becausethat'showtheVueworks.AswecanwriteanyJavaScriptstatementinsidethedirectivebindingassignment,wemustpassthestringwithdoublequotes.Youcangofurtherandcreateadatapropertycallednoiseandassigntoitthevalueyouwant(white,brown,orpink)andreuseitinsidethedirectivebindingsyntax.

Afterthatbeingdone,let'scalltheVue.noise.startmethodinourstartmutation:

//mutations.js

importVuefrom'vue'

<...>

exportdefault{

[types.START](state){

<...>

if(state.isWorking){

Vue.noise.start()

}

},

<...>

Checkthepageandclickonthestartbutton.Youwilllistentoanicebrownnoise.Becareful,however,tonottowakeupyourcoworkersnortoscareyourfamily(orviceversa).Trychangingthevalueofthenoisedirectiveandchooseyourfavoritenoisetoworkwith.

Still,wearenotdone.Wecreatedamechanismsothatthenoiseisstarted,butit'sturningout

Page 286: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

tobeanever-endingnoise.Let'scalltheVue.noise.pauseandVue.noise.stopmethodsonthepauseandstopmutations,respectively:

//mutations.js

exportdefault{

<...>

[types.PAUSE](state){

<...>

Vue.noise.pause()

},

[types.STOP](state){

<...>

Vue.noise.stop()

}

}

Lookatthepage.Nowifyouclickonthepauseorstopbutton,thenoiseissuspended!Wearestillnotdoneyet.Rememberthatourpurposewastohavethenoiseonlyduringworkingtimeandnotduringrestingtime.So,let'shavealookatthetooglePomodoromethodinsidemutations.jsandaddamechanismthatstartsorstopsthenoiseaccordingtothePomodoro'scurrentstate:

//mutations.js

functiontogglePomodoro(state,toggle){

if(_.isBoolean(toggle)===false){

toggle=!state.isWorking

}

state.isWorking=toggle

if(state.isWorking){

Vue.noise.start()

}else{

Vue.noise.pause()

}

state.counter=state.isWorking?WORKING_TIME:RESTING_TIME

}

ThecodeofthePomodoroapplicationafterallthesemodificationscanbefoundinthechapter6/pomodoro2folder.Checkhowthenoiseisstartedwhenwestarttheapplication,howit'spausingwhentheworkingPomodoroiscompleted,andhowitrestartedagainwhenweshouldbebacktowork.Checkalsohowthestart,pause,andstopbuttonstriggerthenoiseaswell.Nicework!

Page 287: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

CreatingabuttontotogglethesoundIt'sreallynicethatwehavethenoisesoundboundtotheworkingstateofthePomodoroapplication.It'salsonicethatthesoundispausedwhenwepausetheapplication.However,itmightbealsousefultobeabletopausethesoundwithouthavingtopausethewholeapplication.Thinkaboutthosesituationswhenyouwanttoworkincompletesilence,oryoumightwanttoreceiveaSkypecall.Inthesesituations,havinganoiseinbackground,evenifit'sniceandpink,isnotniceatall.Let'saddabuttontoourapplicationtotogglethesound.StartbydeclaringastorepropertycalledsoundEnabledandinitializeitwithtrue.Also,creategetterforthisproperty.Sostore.jsandgetters.jsstartlookinglikethefollowing:

//store.js

<...>

conststate={

<...>

soundEnabled:true

}

//getters.js

exportdefault{

<...>

isSoundEnabled:state=>state.soundEnabled

}

Nowwemustprovideamechanismtotogglethesound.Let'screateamutationmethodforthisandaddanactionthatdispatchesthismutation.StartbydeclaringamutationtypecalledTOGGLE_SOUND:

//mutation_types.js

<...>

exportconstTOGGLE_SOUND='TOGGLE_SOUND'

Nowlet'sopenmutations.jsandaddthemutationmethodthattogglesthesoundEnabledstoreproperty:

//mutations.js

[types.TOGGLE_SOUND](state){

state.soundEnabled=!state.soundEnabled

if(state.soundEnabled){

Vue.noise.start()

}else{

Vue.noise.pause()

}

}

Nowlet'saddtheactionthatdispatchesthismutation:

//actions.js

exportdefault{

<...>

toggleSound:({commit})=>{

Page 288: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

commit(types.TOGGLE_SOUND)

}

}

Okaythen,nowwehaveeverythingweneedtocreateatogglesoundbutton!Let'sdoitinourControlsComponent.Startbyaddinganecessarygetterandactiontothemapofmethods:

//ControlsComponent.vue

<script>

import{mapGetters,mapActions}from'vuex'

exportdefault{

computed:mapGetters(['isStarted','isPaused','isStopped',

'isSoundEnabled']),

methods:mapActions(['start','stop','pause','toggleSound'])

}

</script>

Nowwecanaddthebuttontoourtemplate.Isuggestthatitwillbetheiconwiththeglyphiconclassthatwillbealignedtotheright.

Let'sonlyshowthisiconwhentheapplicationisstartedandnotpaused,andonlywhenthePomodorostateisworkingsothatwedon'tmessupthetogglesoundbuttoninastatewhereitisnotsupposedtohavesoundatall.Thismeansthatourv-showdirectiveonthiselementwilllooklikethefollowing:

v-show="isStarted&&!isPaused&&isWorking"

NotethatweareusingtheisWorkingpropertyhere,whichhasnotyetbeenimported.Addittothemapofmethods:

//ControlsComponents.vue

<script>

import{mapGetters,mapActions}from'vuex'

exportdefault{

computed:mapGetters(['isStarted','isPaused','isStopped',

'isWorking','isSoundEnabled']),

methods:mapActions(['start','stop','pause','toggleSound'])

}

</script>

Let'salsousetheglyphicon-volume-offandglyphicon-volume-onclassesonthiselement.Theywillindicatecallingfortheactiontotogglethesound'sstate.Thismeansthattheglyphicon-volume-offclassshouldbeappliedwhenthesoundisenabledandtheglyphicon-volume-onclassshouldbeappliedwhenthesoundisdisabled.Puttingitinthecode,ourclassdirectiveshouldlooklikethefollowing:

:class="{'glyphicon-volume-off':isSoundEnabled,'glyphicon-volume-up':

!isSoundEnabled}"

Lastbutnotleast,weshouldcallthetoggleSoundactionwhenthebuttonisclicked.This

Page 289: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

meansthatweshouldalsobindtheclickeventlistenertothiselement,whichwilllooklikethefollowing:

@click='toggleSound'

So,thewholejademarkupcodeforthisbuttonwillbelikethefollowing:

//ControlsComponent.vue

<template>

<span>

<...>

<iclass="toggle-volumeglyphicon"v-show="isStarted&&

!isPaused&&isWorking":class="{'glyphicon-volume-off':

isSoundEnabled,'glyphicon-volume-up':!isSoundEnabled}"

@click="toggleSound"></i>

</span>

</template>

Let'sjustaddabitofstylingtothisbuttonsothatitappearsalignedtotheright:

<stylescoped>

<...>

.toggle-volume{

float:right;

cursor:pointer;

}

</style>

OpenthepageandstartthePomodoroapplication.Nowyoucanseethisnicebuttononthetop-rightcornerthatwillallowyoutoturnthesoundoff,asshowninthefollowingscreenshot:

Nowwecanturnthesoundoffwhileworking!

Ifyouclickonthisbutton,itwilltransformintoanotherbutton,whosepurposeistoturnthesoundonagain,asshowninthefollowingscreenshot:

Page 290: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Andwecanturnitonagain!

Nowconsiderthefollowingscenario:westarttheapplication,turnoffthesound,pausetheapplication,andresumetheapplication.Ourcurrentlogicsuggeststhatthesoundisstartedeachtimetheapplicationisstarted.Wewillbeinaninconsistentstate—theapplicationhasstarted,thesoundisplaying,butthetogglingsoundbuttonissuggestingtoturnthesoundon.That'snotright,isit?Butthishasaneasyfix—justaddonemoreconditiontothestartmutation,notonlyitshouldcheckifisWorkingistrue,butalsothatthesoundisenabled:

//mutations.js

[types.START](state){

<...>

if(state.isWorking&&state.soundEnabled){

Vue.noise.start()

}

},

Nowwearefine.Thecodeafterallthesemodificationscanbefoundinthechapter6/pomodoro3folder.

Checkthecode,runtheapplication,enjoythesound,anddonotforgettohaveabreak!

Page 291: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

ExerciseItwouldbeniceifduringourPomodorointervalswecouldalsoenjoysomehappynicemusicwhilelookingatcats.Createapluginthatplaysachosenmp3fileanduseitonthePomodorointervals.

Page 292: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

SummaryWhileIwaswritingthelastlinesofcodeforthischapterandcheckingthepage,atonepointIgotstucklookingatthispicture:

Alotofcatslookingatmeandasking:willthischaptergettoitsendatsomepoint?

Ievenpausedtheapplicationtohaveabetterlookatthispicture(yes,whenyoupausethePomodoroapplicationduringrestingtime,thepicturewillpauseaswellbecausethecache-bustertimestampisnotbeingupdatedanymore).Doesn'titseemlikethesecatsareaskingustogetsomerest?Also,theamountofthemisprettyclosetothenumberofthingsthatwe'velearnedinthischapter!

Inthischapter,youlearnedhowthepluginssystemworkwithVue.js.Weusedanexistingresourceplugintoattachtheserver-sidebehaviortoourshoppinglistapplication.Nowwe

Page 293: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

cancreate,delete,andupdateourshoppinglists.

Wehavealsocreatedourownplugin!Ourpluginisabletoproducesoundthatcanhelpinconcentratingduringtheworkingperiod.Notonlyhavewecreatedit,butwehavealsouseditinourPomodoroapplication!NowwecanconcentratebetterwhilePomodoroisworkingandtogglethesoundatanytime!

Nowwehavetworeallyniceapplicationsinourhands.Doyouknowwhatisbetterthananiceapplication?

Theonlythingthatisbetterthananiceapplicationisanicelytestedapplication!

Withthatinmind,it'sabouttimewetestedourapplications.Inthenextchapter,wewillcheckandapplysometestingtechniques.WewillwriteunittestsusingKarmatestrunnerandJasmineasanassertionlibrary.Wewillalsowriteend-to-endtestsusingNightwatch.IlovetotestapplicationsandIhopethatyouwillloveitaswell.Let'sgo!

Page 294: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Chapter7.Testing–TimetoTestWhatWeHaveDoneSoFar!Inthepreviouschapter,youlearnedhowtouseandcreateVueplugins.WeusedtheexistingresourcepluginforVueandcreatedourownNoiseGeneratorplugin.

Inthischapter,wewillensurethequalityofboththePomodoroandshoppinglistapplications.Wewilltesttheseapplicationsapplyingdifferenttestingtechniques.First,wewillperformaclassicunittestonVuecomponentsandonVuex-relatedcodesuchasactions,mutations,andgetters.Afterthat,wewilllearnhowtoperformend-to-endtestingusingNightwatch.So,inthischapter,wewilldothefollowing:

Talkabouttheimportanceofunitandend-to-endtestsImplementunittestsforthePomodoroandshoppinglistapplicationsLearnhowtomockserverresponsesinunittestsImplementend-to-endtestsforbothapplicationsusingNightwatch

Page 295: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Whyunittests?Beforewestartwritingunittests,let'strytounderstandwhatwe'retryingtoachievebywritingthem.Whyisunittestingsoimportant?SometimeswhenIwritemytests,theonlythingIcanthinkaboutismycodecoverage;Iwanttoachievealevelof100%.

Codecoverageisaveryimportantmetricandhelpsalottounderstandthecodeflowandwhatneedstobetested.Butitisnotametricofunittestquality.Thisisnotametricofagoodcodequality.Youcanhaveyourcode100%coveredjustbecauseyoucallallyourfunctionsinyourtestingcode,butifyourassertionsarewrong,thecodemightbewrongaswell.Writinggoodunittestsisanartthatrequirestimeandpatience.Butwhenyourunittestsaregoodenoughandwhenyouareconcentratingonmakinggoodassertions,withregardtocornercasesandbranchcoverage,theyprovidethefollowing:

HelpustoidentifyfailuresinalgorithmsandlogicHelpustoimprovethecodequalityMakeuswritecodethatiseasytotestPreventfuturechangesfrombreakingthefunctionalityHelpustohavemorepredictabledeadlinesandestimations

Codethatiseasytocoverwithunittestsisatthesametimecodethatiseasytoread.Codethatiseasytoreadislesserror-proneandmoremaintainable.Maintainabilityisoneofthemainpillarsofanapplication'squality.

Note

Checkmoreaboutunittestinginthepresentationathttps://chudaol.github.io/presentation-unit-testing.

Let'swritesomeunittestsforourapplications.

WewillusetheKarmatestrunner,Mochatestframework,Chaiexpectationslibrary,andSinonformocks.

Formoreinformationaboutthesetools,refertothefollowing:

Karma:http://karma-runner.github.io/Mocha:https://mochajs.orgChaijs:http://chaijs.com/Sinon:http://sinonjs.org/

Ifwehadn'tbootstrappedourapplicationusingvue-cliwebpackscaffolding,wewouldhavetoinstallallthesetoolsvianpm.Butinourcase,wedon'tneedthisinstallation.Checkyourpackage.jsonfileandyoucanseethatallthesethingsarealreadythere:

"devDependencies":{

Page 296: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

<...>

"chai":"^3.5.0",

<...>

"karma":"^0.13.15",

"karma-chrome-launcher":"^2.0.0",

"karma-coverage":"^0.5.5",

"karma-mocha":"^0.2.2",

"karma-phantomjs-launcher":"^1.0.0",

"karma-sinon-chai":"^1.2.0",

"mocha":"^2.4.5",

<...>

}

Youcertainlyknowhowsimpleitistowriteunittestsforsimplefunctions.It'salmostlikespeakinghumanlanguage.It(thisfunction)shouldreturnXiftheinputisY.IexpectittobeX.

Soifwehaveamodulethatexports,let'ssay,afunctionthatreturnsthesumoftwoarguments,theunittestforthisfunctionmustcallthefunctionwithdifferentargumentsandexpectsomeoutput.So,let'sassumewehaveafunctionsuchasthefollowing:

functionsum(a,b){

returna+b

}

Thenourunittestmightlooklikethefollowing:

it('shouldfollowcommutativelaw',()=>{

leta=2;

letb=3;

expect(sum(a,b)).to.equal(5);

expect(sum(b,a)).to.equal(5);

})

Weshouldneverbeshywhenwethinkaboutthepossibleinputstofunctionsthatarebeingunittested.Emptyinputs,negativeinputs,stringinputs,everythingcounts!Haveyouseenthisfamoustweet(https://twitter.com/sempf/status/514473420277694465)?

Page 297: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

ViraltweetaboutQAEngineer'smindset

Thinkaboutallthepossibleinputsandadequateoutputs.Expressthisinexpectationsandassertions.Runthetests.Seewhatisfailing.Fixyourcode.

Page 298: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

UnittestsforVueapplicationFirst,let'scheckonsomeparticularitiesofunittestingourVueapplicationanditscomponents.Inordertobeabletowritetestsforthecomponentinstance,firstofall,itshouldbeinstantiated!Quitelogical,right?Thethingis,howdoweinstantiatetheVuecomponentsothatitsmethodsbecomeaccessibleandeasilytestable?Totestbasicassertionsoftheinitialstateofthecomponent,youmustjustimportthemandasserttheirproperties.Ifyouwanttotestdynamicproperties—thingsthatchangeoncethecomponentisboundtoDOM—youmustdojustthefollowingthreethings:

1. Importacomponent.2. InstantiateitbypassingittotheVuefunction.3. Mountit.

Tip

WhentheinstanceisboundtothephysicalDOM,onceinstantiated,thecompilationisstartedimmediately.Inourcase,wearenotbindingtheinstancetoanyrealphysicalDOMelement,andthuswehavetoexplicitlymakeitcompileitbyinvokingmanuallythemountmethod($mount).

Nowyoucanusethecreatedinstanceandaccessitsmethods.Inpseudo-code,itlookssomethinglikethefollowing

importMyComponentfrom<pathtomycomponent>

varvm=newVue(MyComponent).$mount()

Nowwecanaccessallvminstancemethodsandtestthem.Therestofthethings,suchasdata,props,andsoonwecanjustfake.Thereisnoproblemwithfakingthingsbecauseitoffersusthepossibilityoftryingallsortsofinputeasilyandtestingalltheviableoutputsforeachofthem.

Ifyouwanttohaveamorerealscenariowhiletestingcomponentsthatuseprops,whichcomeboundtothecomponentbyitsparent,oraccesstothevuexstore,andsoon,youcanusetherefattributetobindthecomponenttotheVueinstance.ThisVueinstance,initsturn,instantiatesthestoreanddataandbindsthedataitemstothecomponentinausualway.Afterthat,youaccessthecomponentinstancebyusingthe$refsVueproperty.Thiskindofbindingwilllooklikethefollowing:

importstorefrom<pathtostore>

importMyComponentfrom<pathtomycomponent>

//loadthecomponentwithavueinstance

varvm=newVue({

template:'<div><test:items="items":id="id"ref=testcomponent></test></div>',

components:{

'test':MyComponent

},

Page 299: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

data(){

return{

items:[],

id:'myId'

}

},

store

}).$mount();

varmyComponent=vm.$refs.testcomponent;

NowyoucantestallthemethodsofmyComponentwithoutbeingworriedaboutoverridingitsprops,methods,andotherinstance-relatedthings.Thisisagoodpartofthisapproach;however,asyoucansee,itisnottheeasiestsetupandyoushouldthinkabouteverything.Forexample,ifyourcomponentcallssomestore'sactionthatcallssomeAPI'smethods,youshouldbereadytohavetofaketheserverresponses.

Ipersonallyliketokeepthingsassimpleaspossible,fakeallthedatainputs,andconcentrateontestingthefunctions'possibleoutputsandallthepossibleedgecases.Butitisjustmypersonalpointofview,andalso,weshouldtryeverythinginourlives,sointhischapter,wewilltrydifferentapproaches.

Page 300: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

WritingunittestsfortheshoppinglistapplicationBeforestartingtheactualwritingofourunittests,let'sestablishsomerules.Foreachofour.jsor.vuefiles,therewillexistacorrespondingtestspecfile,whichwillhavethesamenameanda.spec.jsextension.Thestructureofthesespecswillfollowthisapproach:

ItwilldescribethefilewearetestingItwillhaveadescribemethodforeachofthemethodsthatisbeingtestedItwillhaveanitmethodforeachofthecaseswearedescribing

So,ifwehadamyBeautifulThing.jsfileandspecforit,itmightlooklikethefollowing:

//myBeautifulThing.js

exportmyBeautifulMethod1(){

return'hellobeauty'

}

exportmyBeautifulMethod2(){

return'helloagain'

}

//myBeautifulThing.spec.js

importmyBeautifulThingfrom<pathtomyBeautifulThing>

describe('myBeautifulThing',()=>{

//defineneededvariables

describe('myBeautifulMethod1',()=>{

it('shouldreturnhellobeauty',(){

expect(myBeautifulThing.myBeautifulMethod1()).to.equal('hello

beauty')

})

})

})

Let'sstartbycoveringwithunittestsallthethingsthatareinsidethevuexfolder.

Page 301: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Testingactions,getters,andmutationsForthissection,usethecodeinsidethechapter7/shopping-listfolder.Donotforgettorunthenpminstallcommand.Notethattherearetwonewmutations:ADD_SHOPPING_LISTandDELETE_SHOPPING_LIST.ThesemutationsaddnewshoppinglisttothelistandremovethelistbyitsID.TheyareusedinsidethecreateShoppingListanddeleteShoppingListactionsinsidethepromisefailurehandlers:

//actions.js

createShoppingList:(store,shoppinglist)=>{

api.addNewShoppingList(shoppinglist).then(()=>{

store.dispatch('populateShoppingLists')

},()=>{

store.commit(ADD_SHOPPING_LIST,shoppinglist)

})

},

deleteShoppingList:(store,id)=>{

api.deleteShoppingList(id).then(()=>{

store.dispatch('populateShoppingLists')

},()=>{

store.commit(DELETE_SHOPPING_LIST,id)

})

}

Thus,evenifourbackendserverisdown,westillarenotlosingthisfunctionality.

Ifyoucheckyourproject'sstructure,youwillseethatthereisalreadyanexistingdirectorynamedtest.Insidethisdirectory,therearetwodirectories,unitande2e.Fornow,weshouldgototheunitfolder.Here,youwillseeanotherdirectorycalledspecs.Thisiswhereallourunittestspecificationswillreside.Let'sstartbycreatingadirectorycalledvuexinsidespecs.HereiswhereallourspecsforVuex-relatedJavaScriptfileswilllive.

Let'sstartbytestingthemutations.jsmethod.

Createamutations.spec.jsfile.Inthisfile,weshouldimportmutations.jsandmutationtypessothatwecaneasilyinvokemutations.Havealookatmutationsdeclaredinmutations.js.Allofthemreceivestateandsomeotherparameters.Let'salsocreateafakestateobjectwiththeshoppinglistarrayinsideitsowecanuseitinourtests.

Let'salsoresetitbeforeeachtesttoanemptyarray.

So,afterallthepreparations,thebootstrappedspecformutations.jslookslikethefollowing:

//mutations.spec.js

importmutationsfrom'src/vuex/mutations'

import{ADD_SHOPPING_LIST,DELETE_SHOPPING_LIST,POPULATE_SHOPPING_LISTS,

CHANGE_TITLE}from'src/vuex/mutation_types'

describe('mutations.js',()=>{

Page 302: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

varstate

beforeEach(()=>{

state={

shoppinglists:[]

}

})

})

Let'snowaddtestsfortheADD_SHOPPING_LISTmutation.

Checkagainwhatitisdoing:

[types.ADD_SHOPPING_LIST](state,newList){

state.shoppinglists.push(newList)

},

Thismutationjustpushesthereceivedobjecttotheshoppinglistsarray.Prettystraightforwardandeasytotest.

Startbycreatingadescribestatementwiththenameofthefunction:

describe('ADD_SHOPPING_LIST',()=>{

})

Now,insidethisdescribecallback,wecanadditstatementswiththeneededassertions.Let'sthinkwhatshouldhappenwhenweaddanewshoppinglisttotheshoppinglistsarray.Firstofall,thearray'slengthwillincrease,anditwillalsocontainthenewlyaddedshoppinglistobject.Thisisthemostbasicthingtotest.Ouritfunctionwiththeneededassertionswilllooklikethefollowing:

it('shouldadditemtotheshoppinglistarrayandincreaseits

length',()=>{

//calltheadd_shopping_listmutations

mutations[ADD_SHOPPING_LIST](state,{id:'1'})

//checkthatthearraynowequalsarraywithnewobject

expect(state.shoppinglists).to.eql([{id:'1'}])

//checkthatarray'slengthhadincreased

expect(state.shoppinglists).to.have.length(1)

})

Aftercreatingthisfunction,thewholespec'scodeshouldlooklikethefollowing:

//mutations.spec.js

importmutationsfrom'src/vuex/mutations'

import{ADD_SHOPPING_LIST,DELETE_SHOPPING_LIST,POPULATE_SHOPPING_LISTS,

CHANGE_TITLE}from'src/vuex/mutation_types'

describe('mutations.js',()=>{

varstate

beforeEach(()=>{

state={

Page 303: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

shoppinglists:[]

}

})

describe('ADD_SHOPPING_LIST',()=>{

it('shouldadditemtotheshoppinglistarrayandincreaseits

length',()=>{

mutations[ADD_SHOPPING_LIST](state,{id:'1'})

expect(state.shoppinglists).to.eql([{id:'1'}])

expect(state.shoppinglists).to.have.length(1)

})

})

})

Let'srunthetests!Opentheconsoleintheproject'sdirectoryandrunthefollowing:

npmrununit

Youshouldseethefollowingoutput:

Theoutputofrunningourtest

RememberthejokeaboutaQAengineer?Wecantesttheadd_shopping_listfunctionforallpossibleinputs.Whatshouldhappen,forexample,ifwecallitwithoutpassinganyobject?Intheory,itshouldnotaddittotheshoppinglistarray,right?Let'stestit.Createanewitstatementandtrytocallthefunctionwithoutthesecondparameter.Assertforanemptylist.

Thistestwilllooksomethinglikethefollowing:

it('shouldnotaddtheitemifitemisempty',()=>{

mutations[ADD_SHOPPING_LIST](state)

expect(state.shoppinglists).to.have.length(0)

})

Runthetestswiththenpmrununitcommand.Oh,snap!Itfailed!Theerrorisasfollows:

expected[undefined]tohavealengthof0butgot1

Why?Havealookatthecorrespondingmutation.Itjustpushesthereceivedparametertothe

Page 304: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

arraywithoutanychecks.That'swhyweareabletoaddanygarbage,anyundefined,andanyotherinappropriatevalue!DoyourememberwhenIsaidthatwritinggoodunittestshelpsustocreatelesserror-pronecode?Thisisthecase.Nowwerealizethatweshouldprobablyrunsomechecksbeforepushingthenewitemtothearray.Let'saddthecheckthatthereceiveditemisanobject.OpentheADD_SHOPPING_LISTmutationinthemutations.jsfileandrewriteitasfollows:

//mutations.js

[types.ADD_SHOPPING_LIST](state,newList){

if(_.isObject(newList)){

state.shoppinglists.push(newList)

}

}

Runthetestsnow.Theyareallpassing!

Ofcourse,wecouldbeevenmoreprecise.Wecouldcheckandtestforemptyobjectsandwecouldalsorunsomevalidationsforthisobjecttocontainpropertiessuchasid,items,andtitle.Iwillleaveittoyouasasmallexercise.Trytothinkaboutallpossibleinputsandallpossibleoutputs,writeallthepossibleassertions,andmakethecodetocorrespondtothem.

Page 305: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

GoodtestcriteriaAgoodunittestisonethatwouldfailwhenyouchangeyourcode.Imagine,forexample,thatwedecidetoassignadefaulttitletothenewshoppinglistbeforepushingittothearray.So,themutationwouldlooklikethefollowing:

[types.ADD_SHOPPING_LIST](state,newList){

if(_.isObject(newList)){

newList.title='NewShoppingList'

state.shoppinglists.push(newList)

}

}

Ifyourunthetests,theywillfail:

Unittestfailswhenthecodechanges

Andthisisverygood.Whenyourtestsfailafterthechangesinthecode,thepossibleoutcomeisthatyoufixthetestbecausethecodeisperformingtheintendedbehavior,oryoufixyourcode.

Page 306: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

CodecoverageIamsurethatyouhavenoticedsometeststatisticsintheconsoleoutputafterrunningthetests.Thesestatisticsdisplaydifferenttypesofcoveragethatourtestsachievedatthetimeofrunning.Rightnow,itlookslikethefollowing:

Codecoverageofmutations.jsafterwritingtwotestsfortheADD_SHOPPING_LISTmutation

DoyourememberwhenIsaidthatgoodcodecoveragedoesn'tmeanthatourtestsandcodeareperfect?Weactuallyhavesomewhatnicestatements,branches,andlinescoverage,butwestilljusttestedonlyonefunctionofonlyonefile,andwehaven'tevencoveredallpossibleinputsofthisfunction.Butnumbersdonotlie.Wehavealmost100%branchescoveragebecausewealmostdonothavebranchesinourcode.

Ifyouwanttoseeamoredetailedreport,justopentheindex.htmlfilefromthetest/unit/coverage/lcov-reportdirectoryinyourbrowser.Itwillgiveyouacompleteandfullpictureofyourcodeandwhatexactlyiscoveredandhow.Currently,itlookslikethefollowing:

Thewholepictureofourcodebasecoverage

Youcandrilldowntothefolders,openthefiles,andcheckhowexactlyourcodeiscovered.Let'scheckmutations.js:

Page 307: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Coveragereportforactions.jsshowexactlywhichcodewascoveredandwhichwasnot

Nowyouseewhatstillhastobetested.Doyouwanttoseehowitreportstheif…elsemissingbranchcoverage?Justskipoursecondtest:

it.skip('shouldnotaddtheitemifitemisempty',()=>{

mutations[ADD_SHOPPING_LIST](state)

expect(state.shoppinglists).to.have.length(0)

})

Runthetestsandrefreshthereportforactions.js.YouwillseeanEiconontheleftoftheifstatement:

TheEiconneartheifstatementindicatesthattheelsebranchwasnotcoveredbytests

Thisindicatesthatwehaven'tcoveredtheelsebranch.Ifyouskipthefirsttestandleavetheonewiththeemptyobject,youwillseetheIiconthatisindicatingthatwehaveskippedtheifbranch:

Page 308: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

TheIiconneartheifstatementindicatesthattheifbranchwasnotcoveredbytests

Writetestsfortherestofthemutations.Performatleastthefollowingchecks:

FortheDELETE_SHOPPING_LISTmutation,checkthatthelistwiththeIDwepassisactuallydeletedifitexistedbeforeinthelist,andthatcallingthemutationwiththeIDthatdoesn'texistinthelistwillnotcauseanychangeForthePOPULATE_SHOPPING_LISTSmutation,checkthattheshoppinglistarrayisoverriddenwiththearraywepasswhencallthismutationFortheCHANGE_TITLEmutation,checkthatwhenwepassthenewtitleandtheID,exactlythisobject'stitleischanged

Intheend,yourmutation.spec.jsfilewillprobablylooklikethegistathttps://gist.github.com/chudaol/befd9fc5701ff72dff7fb68ef1c7f06a.

Afterthesetests,thecoverageofmutation.jslooksprettynice,actually:

Page 309: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

100%coverageformutations.jsafterwritingunittestsforallthemutations

Intheexactsameway,wecantestourgetters.js.Createagetters.spec.jsfileandfillitwithteststotestourtwogettersfunctions.Intheend,itmightlooklikethegistathttps://gist.github.com/chudaol/e89dd0f77b1563366d5eec16bd6ae4a9.

Theonlyimportantstorecomponentthatismissinginunittestingisactions.js.Butouractions.jsusesextensivelytheAPIthat,inturn,performsHTTPrequests.Itsfunctionsarealsoasynchronous.Canthiskindofthingbeunittestedinthesameflexibleandeasywayaswejusttestedgettersandactions?Yes,itcan!Let'sseehowcanwefakeserverresponsesusingsinon.jsandhowcanwewriteasynchronoustestswithmocha.js.

Page 310: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

FakingserverresponsesandwritingasynchronoustestsOpentheactions.jsfileandchecktheveryfirstactionmethod:

//actions.js

populateShoppingLists:({commit})=>{

api.fetchShoppingLists().then(response=>{

commit(POPULATE_SHOPPING_LISTS,response.data)

})

}

Firstofall,let'saddareturnstatementtothisfunctiontomakeitreturnapromise.Wedoittoenableustocallthe.thenmethodoncethepromiseresolvessothatwecantesteverythingthathappensinthemeantime.So,ourfunctionlookslikethefollowing:

//actions.js

populateShoppingLists:({commit})=>{

returnapi.fetchShoppingLists().then(response=>{

commit(POPULATE_SHOPPING_LISTS,response.data)

})

}

Now,checkwhatishappeninghere:

1. Thisfunctionreceivesstorewithitsdispatchmethod.2. ItperformsacalltotheAPI.TheAPI,inturn,callstheresourcegetmethodthatjust

performsanHTTPrequesttoourserver.3. AftertheAPI'sfetchShoppingListspromiseisresolved,ourmethodiscallingthe

store'scommitmethodwithtwoparameters:aPOPULATE_SHOPPING_LISTSstringandthedatathatcameinresponse.

Howcanweunittestthisworkflow?Ifwewereabletocatchtherequestandmocktheresponse,wecouldcheckifthecommitmethod(passedbyus,whichmeansthatitcanalsobemocked)iscalledwiththeresponsethatweprovideinourserver'smock.Soundsconfusing?Notatall!Thestepsarethefollowing:

1. Createamockforthestoreanditscommitmethod.2. Createamockforthehypotheticalserverresponse.3. CreateafakeserverthatwillintercepttheGETrequestandreturnthemockedresponse.4. Checkthecommitmethodiscalledwithourmockedresponseandthe

POPULATE_SHOPPING_LISTSstring.

Itmeansthatourtestcouldlooksomethinglikethefollowing:

it('shouldtestthatcommitiscalledwithcorrectparameters',()=>{

actions.populateShoppingLists({commit}).then(()=>{

expect(commit).to.have.been.calledWith(<...>)

})

})

Page 311: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Theproblemhereisthatourtestsaresynchronous,meaningthecodewillneverreachwhatisinsideour.thencallback.Luckilyforus,mocha.jsprovidessupportforasynchronoustesting.Checkitoutathttps://mochajs.org/#asynchronous-code.Theonlythingyouhavetodoistopassdonecallbacktoit()andcallitwhenthetestiscomplete.Inthisway,ourpseudo-codeforthistestwouldlookthefollowing:

it('shouldtestthatcommitiscalledwithcorrectparameters',

(done)=>{

actions.populateShoppingLists({commit}).then(()=>{

expect(commit).to.have.been.calledWith(<...>)

done()

})

})

Let'scodenow!Createatestspecandcallitactions.spec.js,andwritealltheneededbootstrappingcode:

//actions.spec.js

importactionsfrom'src/vuex/actions'

import{CHANGE_TITLE,POPULATE_SHOPPING_LISTS}from'src/vuex/mutation_types'

describe('actions.js',()=>{

describe('populateShoppingLists',()=>{

//herewewilladdourtestcase

})

})

Nowlet'sfollowoursteps.Firstofall,let'smocktheserverresponse.JustcreatethelistsvariableandinitializeitinthebeforeEachmethod:

//actions.spec.js

describe('actions.js',()=>{

varlists

beforeEach(()=>{

//mockshoppinglists

lists=[{

id:'1',

title:'Groceries'

},{

id:'2',

title:'Clothes'

}]

})

describe('populateShoppingLists',()=>{

})

})

Now,let'smockthestore'scommitmethod:

//actions.spec.js

describe('actions.js',()=>{

varlists,store

Page 312: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

beforeEach(()=>{

<...>

//mockstorecommitmethod

store={

commit:(method,data)=>{},

state:{

shoppinglists:lists

}

}

})

<...>

})

Now,wehavetospyonthiscommitmethodinordertobeabletoassertthatitwascalledwiththerequiredparameters.Wewillusethesinon.stubmethodforthis.Checkthedocumentationonsinon.jsonthismatterathttp://sinonjs.org/docs/#stubs.Creatingastubonagivenfunctionisveryeasy.Justcallthesinon.stubmethodandpasstoittheobjectanditsmethodthatwewanttospyon:

sinon.stub(store,'commit')

So,ourbeforeEachfunctionwilllooklikethefollowing:

beforeEach(()=>{

<...>

//mockstorecommitmethod

store={

commit:(method,data)=>{},

state:{

shoppinglists:lists

}

}

sinon.stub(store,'commit')

})

It'sveryimportantthataftereachmethod,werestorethestubsothateachtestingmethodrunsinacleanenvironmentthatisnotaffectedbyothertests.Forthis,createanafterEachmethodandaddthefollowingline:

afterEach(function(){

//restorestub

store.commit.restore()

})

Nowtheonlythingweneedtodoisfakeourserverresponsewithourmockeddata.Let'suseSinon'sfakeServerforthispurpose.Checksinon'sdocumentationathttp://sinonjs.org/docs/#fakeServer.WejustneedtocreatefakeServerandtellittorespondwithourmockedresponsetotheGETrequest:

describe('actions.js',()=>{

varlists,store,server

Page 313: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

beforeEach(()=>{

<...>

//mockserver

server=sinon.fakeServer.create()

server.respondWith('GET',/shoppinglists/,xhr=>{

xhr.respond(200,{'Content-Type':'application/json'},

JSON.stringify(lists))

})

})

<...>

})

Afterthesepreparations,eachtestthatwillsomehowperformarequestshouldcalltheserver'srespondmethodinordertoinvoketheserver'sfunctionality.

However,wecansimplifythisbyjusttellingtheservertoauto-respondeachcaughtrequest:

server.autoRespond=true

So,ourcodeformockingtheserverwilllooklikethefollowing:

beforeEach(()=>{

<...>

//mockserver

server=sinon.fakeServer.create()

server.respondWith('GET',/shoppinglists/,xhr=>{

xhr.respond(200,{'Content-Type':'application/json'},

JSON.stringify(lists)

})

server.autoRespond=true

})

Itisveryimportantthataftereachtest,werestoreourfakeserversothatnoothertestisaffectedbyourmocksinthistest.SoaddthefollowinglinetotheafterEachmethod:

afterEach(()=>{

//restorestubsandservermock

store.commit.restore()

server.restore()

})

Nowthatwehavemockedeverythingthatitwaspossibletomock,wecanfinallywriteourtestcase!So,youremember,wecreateanit()statementwithdonecallback,callourpopulateShoppingListsmethod,andcheckthattheresolvedresponseisthesameasourmockedlistobject.Stepintothedescribemethodandjusttranslateintothecodewhatwe'vejustdescribed:

it('shouldcallcommitmethodwithPOPULATE_SHOPPING_LISTandwithmocked

lists',done=>{

actions.populateShoppingLists(store).then(()=>{

expect(store.commit).to.have.been.calledWith(POPULATE_SHOPPING_LISTS,

lists)

Page 314: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

done()

}).catch(done)

})

Ourwholetestspecnowlookslikethegistathttps://gist.github.com/chudaol/addb6657095406234bc6f659970f3eb8.

Runthetestswithnpmrununit.Itworks!

Nowwejusthavetomocktheserver'sresponsesforthePUT,POST,andDELETEmethods.Thesemethodsdonotreturnanydata;however,inordertobeabletotesttheresponses,let'sreturnfakedsuccessmessages,andineachtest,checkthatthereturneddatacorrespondstotheseresponses.Addthefollowingvariablesontopofthespec:

varserver,store,lists,successPut,successPost,successDelete

successDelete={'delete':true}

successPost={'post':true}

successPut={'put':true}

Andaddthefollowingfakeresponsesmethodstoourserver:

server.respondWith('POST',/shoppinglists/,xhr=>{

xhr.respond(200,{'Content-Type':'application/json'},

JSON.stringify(successPost))

})

server.respondWith('PUT',/shoppinglists/,xhr=>{

xhr.respond(200,{'Content-Type':'application/json'},

JSON.stringify(successPut))

})

server.respondWith('DELETE',/shoppinglists/,xhr=>{

xhr.respond(200,{'Content-Type':'application/json'},

JSON.stringify(successDelete))

})

Let'sseehowit'llwork,forexample,forthechangeTitlemethod.Inthistest,wewanttotestthatthecommitmethodwillbecalledwiththegivenIDandtitle.Ourtest,thereforewilllooklikethefollowing:

describe('changeTitle',()=>{

it('shouldcallcommitmethodwithCHANGE_TITLEstring',(done)=>{

lettitle='newtitle'

actions.changeTitle(store,{title:title,id:'1'}).then(()=>{

expect(store.commit).to.have.been.calledWith(CHANGE_TITLE,

{title:title,id:'1'})

done()

}).catch(done)

})

})

Forthistoworkproperly,weshouldalsomockthestore'sdispatchmethodsinceit'sbeing

Page 315: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

usedinsidethechangeTitleaction.Justaddthedispatchpropertytoourstore'smockandreturnaresolvedpromise:

//mockstorecommitanddispatchmethods

store={

commit:(method,data)=>{},

dispatch:()=>{

returnPromise.resolve()

},

state:{

shoppinglists:lists

}

}

Checkthefinalcodeforunittestsatthismomentathttps://gist.github.com/chudaol/1405dff6a46b84c284b0eae731974050.

Finishthetestingforactions.jsbyaddingunittestsfortheupdateList,createShoppingList,anddeleteShoppingListmethods.Checkthewholecodeforunittestsuntilnowinthechapter7/shopping-list2folder.

Page 316: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

TestingcomponentsNowthatallourVuex-relatedfunctionsareunittested,itistimetoapplyspecificVuecomponentstestingtechniquestotestcomponentsofourshoppinglistapplication.

YourememberfromthefirstsectionofthischapterthatinordertopreparetheVueinstancetobeunittested,wemustimport,initiate(passingittonewVueinstance),andmountit.Let'sdoit!Createacomponentsfolderinsidethetest/unit/specsdirectory.Let'sstartbytestingtheAddItemComponentcomponent.CreateanAddItemComponent.spec.jsfileandimportVueandAddItemComponent:

//AddItemComponent.spec.js

importVuefrom'vue'

importAddItemComponentfrom'src/components/AddItemComponent'

describe('AddItemComponent.vue',()=>{

})

ThevariableAddItemComponentcanbeusedtoaccessdirectlyallthecomponent'sinitialdata.Sowecanassert,forexample,thatthecomponentdataisinitializedwithanewItempropertythatequalstoemptystring:

describe('initialization',()=>{

it('shouldinitializethecomponentwithemptystringnewItem',()=>{

expect(AddItemComponent.data()).to.eql({

newItem:''

})

})

})

Let'snowcheckwhichmethodsofthiscomponentwecancoverwithunittests.

Thiscomponenthasonlyonemethod,whichisaddItemmethod.Let'scheckwhatthismethoddoes:

//AddItemComponent.vue

addItem(){

vartext

text=this.newItem.trim()

if(text){

this.$emit('add',this.newItem)

this.newItem=''

this.$store.dispatch('updateList',this.id)

}

}

Thismethodaccesstothestore,so,wehavetouseanotherstrategyofinitializingthecomponentratherthanjustdirectlyusingtheimportedvalue.Inthiscase,weshouldinitializeVuemaincomponentwithAddItemComponentasachild,passallthenecessaryattributestoit,

Page 317: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

andaccessitusingthe$refsattribute.So,thecomponent'sinitializationinsidethetestmethodwilllooklikethefollowing:

varvm,addItemComponent;

vm=newVue({

template:'<add-item-component:items="items":id="id"

ref="additemcomponent">'+

'</add-item-component>',

components:{

AddItemComponent

},

data(){

return{

items:[],

id:'niceId'

}

},

store

}).$mount();

addItemComponent=vm.$refs.additemcomponent

Backtothemethod'sfunctionality.So,theaddItemmethodgrabstheinstance'snewItemproperty,trimsit,checksifit'snotfalsyand,ifnot,emitsthecustomeventadd,resetsthenewItemproperty,anddispatchestheupdateListactiononstore.Wecantestthismethodbyassigningdifferentvaluescomponent.newItem,component.idandcheckingiftheoutputcorrespondstowhatweareexpectingofit.

Tip

Positivetestingmeanstestingasystembygivingitvaliddata.Negativetestingmeanstestingasystembygivingitinvaliddata.

Inourpositivetest,weshouldinitializethecomponent.newItempropertywithavalidstring.Aftercallingthemethod,weshouldensurevariousthings:

The$emitmethodofthecomponenthasbeencalledwithaddandthetextweassignedtothenewItempropertycomponent.newItemwasresettotheemptystringThestore'sdispatchmethodhasbeencalledwiththeidpropertyofthecomponent

Let'sgo!Let'sstartbyaddingthedescribemethodfortheaddItemfunction:

describe('addItem',()=>{

})

Nowwecanaddtheit()methodwherewewillassignavaluetocomponent.newItem,calltheaddItemmethod,andcheckeverythingweneedtocheck:

Page 318: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

//AddItemComponent.spec.js

it('shouldcall$emitmethod',()=>{

letnewItem='LearningVueJS'

//stub$emitmethod

sinon.stub(component,'$emit')

//stubstore'sdispatchmethod

sinon.stub(store,'dispatch')

//setanewitem

component.newItem=newItem

component.addItem()

//newItemshouldbereset

expect(component.newItem).to.eql('')

//$emitshouldbecalledwithcustomevent'add'andanewItemvalue

expect(component.$emit).to.have.been.calledWith('add',newItem)

//dispatchshouldbecalledwithupdateListandtheidofthelist

expect(store.dispatch).to.have.been.calledWith('updateList',

'niceId')

store.dispatch.restore()

component.$emit.restore()

})

Runthetestsandcheckthattheyarepassingandeverythingisokay.CheckthefinalcodeforAddItemComponentinthechapter7/shopping-list3folder.

Trytowriteunittestsfortherestofthecomponentsoftheshoppinglistapplication.Remembertowriteunitteststocoveryourcodesothatitbreaksifyouchangeit.

Page 319: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

WritingunittestsforourPomodoroapplicationOk!Let'smovetoourPomodoroapplication!Bytheway,whenwasthelasttimeyoutookabreak?Probably,itistimetoopentheapplicationinyourbrowser,waitafewminutesofthePomodoroworkingperiodtimer,andcheckforsomekittens.

Ijustdiditanditmademefeelreallyniceandcute:

I'mnotyourclothes...pleasehavesomerest

Let'sstartwithmutations.Openthecodeinthechapter7/pomodorofolder.Openthemutations.jsfileandcheckwhatishappeningoutthere.Therearefourmutationshappening:START,STOP,PAUSE,andTOGGLE_SOUND.Guesswhichonewewillstartwith.Yes,youareright,wewillstartwiththestartmethod.Createavuexsubfolderinsidethetest/unit/specs

Page 320: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

folderandaddthemutations.spec.jsfile.Let'sbootstrapittobereadyfortests:

//mutations.spec.js

importVuefrom'vue'

importmutationsfrom'src/vuex/mutations'

import*astypesfrom'src/vuex/mutation_types'

describe('mutations',()=>{

varstate

beforeEach(()=>{

state={}

//let'smockVuenoiseplugin

//tobeabletolistenonitsmethods

Vue.noise={

start:()=>{},

stop:()=>{},

pause:()=>{}

}

sinon.spy(Vue.noise,'start')

sinon.spy(Vue.noise,'pause')

sinon.spy(Vue.noise,'stop')

})

afterEach(()=>{

Vue.noise.start.restore()

Vue.noise.pause.restore()

Vue.noise.stop.restore()

})

describe('START',()=>{

})

})

NotethatImockedallthemethodsofthenoisegeneratorplugin.Thisisbecauseinthisspec,wedon'tneedtotesttheplugin'sfunctionality(infact,wemustdoitinthescopeofthepluginitselfbeforepublishingit).Forthescopeofthistest,weshouldtestthattheplugin'smethodsarecalledwhentheyneedtobecalled.

Inordertobeabletotestthestartmethod,let'sthinkwhatshouldhappen.Afterthestartbuttonisclicked,weknowthattheapplication'sstarted,paused,andstoppedstatesmustgainsomespecificvalues(actually,true,false,andfalse,respectively).Wealsoknowtheapplication'sintervalshouldbestarted.WealsoknowthatifthePomodoro'sstateisworkingandifthesoundisenabled,thestartmethodofthenoisegeneratorpluginshouldbecalled.Infact,thisiswhatourmethodisactuallydoing:

[types.START](state){

state.started=true

state.paused=false

state.stopped=false

state.interval=setInterval(()=>tick(state),1000)

if(state.isWorking&&state.soundEnabled){

Vue.noise.start()

Page 321: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

}

},

Butevenifitdidn'tdoallthesethingsandwehavewrittenthetesttotestit,wewouldimmediatelyunderstandthatsomethingismissinginourcodeandfixit.Let'sthenwriteourtest.Let'sstartbydefiningtheit()methodthatteststhatallthepropertieswerecorrectlyset.Inordertobesurethattheyarenotalreadysetbeforecallingthemethod,let'salsoassertthatallthesepropertiesarenotdefinedatthestartofthetest:

it('shouldsetallthestatepropertiescorrectlyafterstart',()=>{

//ensurethatallthepropertiesareundefined

//beforecallingthestartmethod

expect(state.started).to.be.undefined

expect(state.stopped).to.be.undefined

expect(state.paused).to.be.undefined

expect(state.interval).to.be.undefined

//callthestartmethod

mutations[types.START](state)

//checkthatallthepropertieswerecorrectlyset

expect(state.started).to.be.true

expect(state.paused).to.be.false

expect(state.stopped).to.be.false

expect(state.interval).not.to.be.undefined

})

Let'snowcheckontheVue.noise.startmethod.Weknowthatitshouldonlybecalledifstate.isWorkingistrueandstate.soundEnabledistrue.Let'swriteapositivetest.Inthistest,wewouldinitializebothBooleanstatestotrueandcheckthatthenoise.startmethodiscalled:

it('shouldcallVue.noise.startmethodifbothstate.isWorkingand

state.soundEnabledaretrue',()=>{

state.isWorking=true

state.soundEnabled=true

mutations[types.START](state)

expect(Vue.noise.start).to.have.been.called

})

Let'saddtwonegativetestsforeachofthestates,withisWorkingandsoundEnabledbeingfalse:

it('shouldnotcallVue.noise.startmethodifstate.isWorkingisnottrue',()

=>{

state.isWorking=false

state.soundEnabled=true

mutations[types.START](state)

expect(Vue.noise.start).to.not.have.been.called

})

it('shouldnotcallVue.noise.startmethodifstate.soundEnabledisnottrue',()

=>{

state.isWorking=true

state.soundEnabled=false

Page 322: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

mutations[types.START](state)

expect(Vue.noise.start).to.not.have.been.called

})

Ourstartmutationisnicelytested!Checkthefinalstateofthecodeinthechapter7/pomodoro2folder.Isuggestthatyounowwritetherestoftheunittestsnotonlyforthemutations,butalsoforallthestore-relatedfunctionsthatresideingettersandactions.Afterthat,applythetechniquestotestVuecomponentsthatwejustlearnedandtestsomeofthecomponentsofourPomodoroapplication.

Atthispoint,wearedonewithunittesting!

Page 323: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Whatisend-to-endtesting?End-to-end(e2e)testingisatechniqueinwhichthewholeflowoftheapplicationisbeingtested.Inthiskindoftesting,neithermocksnorstubsareused,andtherealsystemisbeingunderthetest.Performinge2etestingallowsustotestalltheaspectsoftheapplication—APIs,frontend,backend,databases,serverload,assuringthusthequalityofthesystemintegration.

Inthecaseofwebapplications,thesetestsareperformedviaUItesting.Eachtestdescribesallthestepsfromopeningthebrowseruntilclosingit.Allthestepsneededtoperforminordertoachievesomesystem'sfunctionalitymustbedescribed.Infact,thisisthesameasyouclickinganddoingsomeoperationsonyourapplication'spage,butisautomatedandfast.Inthissection,wewillseewhataSeleniumwebdriveris,andwhatNightwatchis,andhowtheycanbeusedtocreatee2etestsforourapplications.

Page 324: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Nightwatchfore2eIfyouhavealreadyworkedwithtestautomationorifyouhaveworkedwithsomeonewhohasworkedwithtestautomation,forsure,youhavealreadyheardthemagicwordSelenium—Seleniumopensthebrowser,clicks,writes,doeseverythinglikeahuman,inaparallel,nicelydistributed,multiplatform,andcross-browserway.Infact,SeleniumisjustaJARfilethatcontainsanAPItoperformdifferentoperationsonabrowser(click,type,scroll,andsoon).

Note

CheckoutSelenium'sdocumentationathttp://www.seleniumhq.org/.

WhenthisJARfileisexecuted,itconnectstothespecifiedbrowser,openstheAPI,andwaitsforthecommandstobeperformedonthebrowser.ThecommandssenttotheSeleniumservercanbeperformedintonsofdifferentwaysandlanguages.

Therearealotofexistingimplementationsandframeworksthatallowyoutocallseleniumcommandswithcouplelinesofcode:

YoucanusethenativeSelenium'sframeworkforJava(http://seleniumhq.github.io/selenium/docs/api/java/)YoucanusetheFirefoxpluginforbrowsers(https://addons.mozilla.org/en-us/firefox/addon/selenium-ide/)YoucanuseSelenide,whichisyetanotherimplementationforJavabutaloteasiertousethanSelenium'sframework(http://selenide.org/)IfyouareanAngularJSdeveloper,youcanuseProtractor,whichisaverynicee2etestframeworkforAngularJSapplicationsthatalsousestheSeleniumwebdriver(http://www.protractortest.org/)

Inourcase,wewilluseNightwatch,whichisaniceandveryeasy-to-usetestingframeworktocallSelenium'scommandsusingJavaScript.

CheckNightwatch'sdocumentationathttp://nightwatchjs.org/.

Vueapplications,whenbootstrappedusingthevue-cliwebpackmethod,alreadycontainssupportforwritingNightwatchtestsrightawaywithouttheneedtoinstallanything.Basically,eachtestspecwilllooksomewhatlikethefollowing:

module.exports={

'e2etest':function(browser){

browser

.url('http://localhost:8080')

.waitForElementVisible('#app',5000)

.assert.elementPresent('.logo')

.assert.containsText('h1','HelloWorld!')

.assert.elementCount('p',3)

Page 325: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

.end()

}

}

Thesyntaxisniceandeasytounderstand.EachofthehighlightedmethodsisaNightwatchcommandthatbehindthescenesistransformedintotheSeleniumcommandandinvokedassuch.CheckthefulllistoftheNightwatchcommandsintheofficialdocumentationpageathttp://nightwatchjs.org/api#commands.

Page 326: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Writinge2etestsforthePomodoroapplicationSo,nowthatweknowallthetheorybehindtheUItesting,wecancreateourfirstend-to-endtestforourPomodoroapplication.Let'sdefinethestepsthatwewillperformandthethingsthatweshouldtest.So,firstofall,weshouldopenthebrowser.Then,weshouldprobablycheckthatourcontainer(thathasthe#appID)isonthepage.

Wecanalsotrytocheckthatthepauseandstopbuttonsaredisabledandthatthesoundtogglebuttondoesnotexistonthepage.

Thenwecanclickonthestartbuttonandcheckthatthesoundtogglebuttonhasappeared,thestartbuttonhasbecomedisabled,andthepauseandstopbuttonshavebecomeenabled.Thereisaninnumerousnumberofpossibilitiesoffurtherclickingandchecking,butlet'sperformatleastthedescribedsteps.Let'sjustwritethemintheformofbulletpoints:

1. Openthebrowserathttp://localhost:8080.2. Checkthatthe#appelementisonthepage.3. Checkthatthe.toggle-volumeiconisnotvisible.4. Checkthatthe'[title=pause]'and'[title=stop]'buttonsaredisabledandthe

'[title=start]'buttonisenabled.5. Clickonthe'[title=start]'button.6. Checkthatthe'[title=pause]'and'[title=stop]'buttonsarenowenabledand

the'[title=start]'buttonisdisabled.7. Checkthatthe.toggle-volumeiconisnowvisible.

Let'sdoit!Justopenthetest.jsfileinsidethetests/e2e/specsfolder,deleteitscontent,andaddthefollowingcode:

module.exports={

'defaulte2etests':(browser)=>{

//openthebrowserandcheckthat#appisonthepage

browser.url('http://localhost:8080')

.waitForElementVisible('#app',5000);

//checkthattoggle-volumeiconisnotvisible

browser.expect.element('.toggle-volume')

.to.not.be.visible

//checkthatpausebuttonisdisabled

browser.expect.element('[title=pause]')

.to.have.attribute('disabled')

//checkthatstopbuttonisdisabled

browser.expect.element('[title=stop]')

.to.have.attribute('disabled')

//checkthatstartbuttonisnotdisabled

browser.expect.element('[title=start]')

.to.not.have.attribute('disabled')

//clickonstartbutton,checkthattogglevolume

//buttonisvisible

browser.click('[title=start]')

Page 327: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

.waitForElementVisible('.toggle-volume',5000)

//checkthatpausebuttonisnotdisabled

browser.expect.element('[title=pause]')

.to.not.have.attribute('disabled')

//checkthatstopbuttonisnotdisabled

browser.expect.element('[title=stop]')

.to.not.have.attribute('disabled')

//checkthatstopbuttonisdisabled

browser.expect.element('[title=start]')

.to.have.attribute('disabled')

browser.end()

}

}

Doyouseehowsuperhuman-friendlythislanguageis?Let'snowperformachecktoseewhether,aftertheperiodofworkingtime,thekittenelementappearsonthescreen.Inordertomakethetestshorterandnotwaitforalongtimefortesttopass,let'sestablishtheworkingperiodas6seconds.Changethisvalueinourconfig.jsfile:

//config.js

exportconstWORKING_TIME=0.1*60

Theelementthatcontainsthecatimageshasa'div.well.kittens'selector,sowewillcheckwhetheritisvisible.Let'salsocheckinthistestthatafterthekittenelementappears,thesourceoftheimagecontainsthe'thecatapi'string.Thistestwillbeassimpleasthefollowing:

'waitforkittentest':(browser)=>{

browser.url('http://localhost:8080')

.waitForElementVisible('#app',5000)

//initiallythekittenelementisnotvisible

browser.expect.element('.well.kittens')

.to.not.be.visible

//clickonthestartbuttonandwaitfor7sfor

//kittenelementtoappear

browser.click('[title=start]')

.waitForElementVisible('.well.kittens',7000)

//checkthattheimagecontainsthesrcelement

//thatmatchesthecatapistring

browser.expect.element('.well.kittensimg')

.to.have.attribute('src')

.which.matches(/thecatapi/);

browser.end()

}

Runthetests.Inordertodothat,invokethee2enpmcommand:

npmrune2e

Youwillseehowthebrowseropensandperformsalltheoperationsbyitself.

It'sakindofmagic!

Page 328: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Allourtestshavepassedandallexpectationsarefulfilled;checkouttheconsole:

Alltestsarepassing!

Congratulations!You'vejustlearnedhowtouseNightwatchtowritee2etests.Checkthecodeinthechapter7/pomodoro3folder.WritemoretestcasesforourPomodoroapplication.Donotforgetaboutourshoppinglistapplication,whichmighthaveevenmorescenariosforUItests.WritethemallandcheckhowSeleniumdoestheworkforyou.Ifyoudecidetoenhancethecode,notonlyisyourcodequalityprotectedbyunittests,butitalsonowhasregressiontestingappliedtoit.Eachtimeyouchangethecode,runbothtypesoftestsjustwithonecommand:

npmtest

Nowyoucertainlydeservesomerest.Takeacupofcoffeeortea,openyourbrowseronthePomodoroapplicationpage,waitfor6seconds,andappreciateourlittlefluffyfriends:

Page 329: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Actually,thisisnotakittenfromthecatapi.ThisismycatPatuscaswishingyoualltohaveagoodresttime!

Page 330: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

SummaryInthischapter,we'vetestedbothofourapplications.WehavewrittenunittestsforVuexmethodsandVuecomponents.WehaveusedsimpleunittestsandasynchronousunittestsandwegotfamiliarwithSinonmockingtechniquessuchasspyingonmethodsandfakingserverresponses.WealsolearnedhowtocreateUItestsusingNightwatch.Ourapplicationsarenowtestedandpreparedtobedeployedtoproduction!Wewilldiscoverhowtodeploytheminthenextchapter,whichwillbedevotedtodeployingapplicationsusingtheHerokucloudapplicationplatform.

Page 331: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Chapter8.Deploying–TimetoGoLive!Inthepreviouschapter,youlearnedhowtotestyourVueapplications.Wetestedthemapplyingdifferenttestingtechniques.Inthebeginning,wehaveperformedclassicunittestingonVuecomponentsandonVuex-relatedmodules,suchasactions,mutations,andgetters.Afterthat,welearnedhowtoapplyend-to-endtestingtechniquesusingNightwatch.

Inthischapter,wewillmakeourapplicationsgolivebydeployingthemtoaserverandmakingthemavailabletotheworld.Wewillalsoguaranteecontinuousintegrationandcontinuousdeploymentofourapplications.Thismeansthateverytimewecommitchangesperformedontheapplications,theywillautomaticallybetestedanddeployed.

Withthisinmind,inthischapter,wearegoingtodothefollowing:

SetupacontinuousintegrationprocessusingTravisSetupacontinuousdeploymentusingHeroku

Page 332: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

SoftwaredeploymentBeforestartingtodeployourapplications,let'sfirsttrytodefinewhatitactuallymeans:

"Softwaredeploymentisalloftheactivitiesthatmakeasoftwaresystemavailableforuse."–Wikipedia:https://en.wikipedia.org/wiki/Software_deployment

Thisdefinitionmeansthatafterweperformallthenecessaryactivities,oursoftwarewillbeaccessibletothepublic.Inourcase,aswearedeployingwebapplications,itmeansthattherewillbeapublicURL,andanypersonwillbeabletotypethisURLontheirbrowserandaccesstheapplication.Howcanthisbeachieved?ThesimplestwayistoprovideyourownIPaddresstoyourfriendsandruntheapplication.Thus,peopleinsideyourprivatenetworkwillbeabletoaccesstheapplicationontheirbrowser.So,run,forexample,thePomodoroapplication:

>cd<pathtopomodoro>

>npmrundev

AndthencheckyourIP:

ifconfig

Page 333: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

CheckingtheIPaddresswiththeifconfigcommand

Andthensharetheaddresswithyourfriendsonthesameprivatenetwork.Inmycase,itwouldbehttp://192.168.1.6:8080.

However,onlyyourfriendswhoareinsideyournetworkwillbeabletoaccesstheapplication,andthere'sobviouslynotthatmuchfuninit.

Youcanusesomesoftwarethatwillcreateapubliclyaccessibleaddressandthustransformyourcomputerintoahostingprovider,forexample,ngrok(https://ngrok.com/).Runtheapplicationandthenrunthefollowingcommand:

ngrokhttp8080

Thiswillcreateanaddressthatwillbeaccessiblefromanywhere,justlikearegularwebsite:

Usingngroktoprovideatunneltoyourlocalhost

Inmycase,itwouldbehttp://5dcb8d46.ngrok.io.IcansharethisaddressonmysocialnetworksandeverybodywillbeabletoaccessitandtrythePomodoroapplication!Butstop...Icanleavemylaptoponforthewholenight,butIcan'tleaveitonforever.OnceIswitchitoff,thenetworkconnectionislostandthereisnoaccesstomyapplicationanymore.Also,evenifIcouldleaveitonforever,Idon'tlikethiswebsiteaddress.It'sabunchoflettersandnumbers,andIwantittobesomethingmeaningful.

Therearemorerobustways.Icanbuy,forexample,avirtualinstanceonAWS(AmazonWebServices),copymyapplicationtothisinstance,buyadomainatadomainprovidersuchasGoDaddy,associatethisdomaintotheboughtinstance'sIP,andruntheapplicationthereanditwillbeaccessible,maintained,backedup,andtakencareofbytheAmaz(on)ingservice.Amazing,but...expensiveashell.Let'sthinkofthissolutionwhenourapplicationsreachthecorrespondingsizeandpaybacklevel.

Fornow,forthischapter,wewantourdeploymentsolutiontobecheap(wherecheapmeans

Page 334: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

free),robust,andsimple.ThatiswhywewilldeployourapplicationtoHeroku,acloud-applicationplatform.Inordertodothat,wewillfirsthostourapplicationonGitHub.Doyourememberthatdeploymentissomethingthatmakesourapplicationsreadytouse?Iconsideranapplicationtobereadytousewhenit'stestedandwhentestsarenotfailing.ThatiswhywewillalsouseTravistoguaranteethequalityofourapplicationsbeforetheiractualdeployment.So,ournecessaryactivitiestodeploytheapplicationwillbethefollowing:

1. CreateGitHubrepositoriesfortheapplicationsandmovetheapplicationsintotherepositories.

2. SetupcontinuousintegrationwithTravis.3. ConnectapplicationstoHeroku,andsetupandconfiguretheminorderforHerokuto

runthemandtoexposethemtotheworld.

Inthenextthreesubsections,IwillgiveasmallintroductiontoGitHub,Travis,andHeroku.

Page 335: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

WhatisGitHub?GitHubisahostingproviderforGit-basedprojects.

Itcanbeusedatasmall,personalscaleforindividualprivateandpublicprojects.Itcanalsobeusedforbigcorporateprojectsandalldevelopment-relatedactivities,suchascodereviews,continuousintegration,andsoon.

EveryonewholivesintheworldofopensourcesoftwareknowsGitHub.IfyouarereadingthisbookaboutVue,whichishostedonGitHub(https://github.com/vuejs/),Iamsurethatyouareskippingthissubsection,soprobablyIcanwritesomestupidjokesaboutyouhereandyouwillnevernoticethem!Justkidding!

Page 336: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

WhatisTravis?TravisisatoolforGitHubthatallowsustoconnectGitHubprojectstoitandensuretheirquality.Itrunstestsinyourprojectsandtellsyouthatbuildhaspassed,orwarnsyouthatbuildhasfailed.CheckmoreaboutTravisandhowtouseitathttps://travis-ci.org/.

Page 337: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

WhatisHeroku?Herokuisacloudplatformfordeployingyourapps.Itisextremelyeasytouse.Youjustcreateanapplication,giveitanicemeaningfulname,connectittoyourGitHubproject,andvoilà!Eachtimeyoupushtoagivenbranch(forexample,tothemasterbranch),Herokuwilljustrunascriptprovidedbyyouasanentrypointscriptofyourappandredeployit.

Itishighlyconfigurableandalsoprovidesacommand-lineinterfacesothatyoucanaccessallyourapplicationsfromyourlocalcommandlinewithouthavingtocheckyourHerokudashboardwebsite.Let'sthenstartandlearneverythingbydoingitourselves.

Page 338: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

MovingtheapplicationtotheGitHubrepositoryLet'sstartbycreatingtheGitHubrepositoriesforourapplications.

Pleaseusethecodefromthechapter8/pomodoroandchapter8/shopping-listdirectories.

Ifyoustilldon'thaveanaccountatGitHub,createit.NowlogintoyourGitHubaccountandcreatetworepositories,PomodoroandShoppingList:

CreatearepositoryatGitHub

OnceyouhittheCreaterepositorybutton,apagewithdifferentinstructionsappears.We

Page 339: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

areparticularlyinterestedinthesecondparagraph,whichsays...orcreateanewrepositoryonthecommandline.Copyit,pasteittothecommandlinewhileinthePomodoroapplicationdirectory,removethefirstline(becausewealreadyhavetheREADMEfile)andmodifythethirdlinetoaddeverythinginsidethedirectory,andhittheEnterbutton:

gitinit

gitadd

gitcommit-m"firstcommit"

gitremoteaddoriginhttps://github.com/chudaol/Pomodoro.git

gitpush-uoriginmaster

RefreshyourGitHubprojectpage,andyouwillseethatallthecodeisthere!Inmycase,itisathttps://github.com/chudaol/Pomodoro.

Dothesamefortheshoppinglistapplication.IjustdiditandhereIam:https://github.com/chudaol/ShoppingList.

Ifyoudon'twanttocreateyourownrepositories,youcanjustforkmine.Opensourceisopen!

Page 340: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

SettingcontinuousintegrationwithTravisInordertobeabletosetupcontinuousintegrationwithTravis,firstofallyouhavetoconnectyourTravisaccountwithyourGitHubaccount.Openhttps://travis-ci.org/andclickontheSigninwithGitHubbutton:

ClickontheSigninwithGitHubbutton

NowyoucanaddrepositoriesthatwillbetrackedwithTravis.Clickontheplussign(+):

ClickontheplussigntoaddyourGitHubproject

Afteryouclickontheplusbutton,thewholelistofyourGitHubprojectappears.Choosetheprojectsyouwanttotrack:

Page 341: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

ChoosetheprojectsyouwanttotrackwithTravis

NowthatwehaveourprojectsconnectedtotheTravisbuildsystemthatlistenstoeverycommitandpushtothemasterbranch,weneedtotellitsomehowwhatithastodoonceitdetectschanges.AlltheconfigurationforTravisshouldbestoredinthe.travis.ymlfile.Addthe.travis.ymlfiletoboththeprojects.Wehaveatleasttotellwhichnodeversionshouldbeused.ChecktheNodeversionofyoursystem(thisistheonethatyouarecompletelysurethatworkswithourprojects).Justrunthefollowingcommand:

node--version

Inmycase,itisv5.11.0.SoIwilladdittothe.travis.ymlfile:

//.travis.yml

language:node_js

node_js:

-"5.11.0"

Ifyoucommitandpushnow,youwillseethatTravisautomaticallystartsrunningtests.Bydefault,itcallsthenpmtestcommandontheproject.Waitforafewminutesandobservetheresult.Unfortunately,itwillfailwhileperformingend-to-end(Selenium)tests.Whydoesthis

Page 342: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

happen?

Bydefault,virtualimagesoftheTravisbuildingandtestingenvironmentdonothavetheChromebrowserinstalled.AndourSeleniumtestsaretryingtorunontheChromebrowser.Butfortunatelyforus,Travisprovidesamechanismofperformingsomecommandsbeforebuilding.Itshouldbedoneinthebefore_scriptsectionoftheYMLfile.Let'sinvokethenecessarycommandstoinstallChromeandexporttheCHROME_BINvariable.Addthefollowingtoyour.travis.ymlfiles:

before_script:

-exportCHROME_BIN=/usr/bin/google-chrome

-sudoapt-getupdate

-sudoapt-getinstall-ylibappindicator1fonts-liberation

-wgethttps://dl.google.com/linux/direct/google-chrome-

stable_current_amd64.deb

-sudodpkg-igoogle-chrome*.deb

Asyoucansee,inordertoperformtheinstallationandsystemupdate,wemustinvokecommandswithsudo.Bydefault,Travisdoesnotletyouexecutesudocommandsinordertopreventaccidentaldamagebynon-trustworthyscripts.ButyoucantellTravisexplicitlythatyourscriptusessudo,whichmeansthatyouareawareofwhatareyoudoing.Justaddthefollowinglinestoyour.travis.ymlfiles:

sudo:required

dist:trusty

Nowyourwhole.travis.ymlfileshouldlooklikethefollowing:

//.travis.yml

language:node_js

sudo:required

dist:trusty

node_js:

-"5.11.0"

before_script:

-exportCHROME_BIN=/usr/bin/google-chrome

-sudoapt-getupdate

-sudoapt-getinstall-ylibappindicator1fonts-liberation

-wgethttps://dl.google.com/linux/direct/google-chrome-

stable_current_amd64.deb

-sudodpkg-igoogle-chrome*.deb

TrytocommititandcheckyourTravisdashboard.

Ohno!Itfailsagain.Thistime,itseemstobetimeoutissue:

Page 343: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

EvenafterinstallingChrome,testssilentlyfailduetothetimeout

Whydidithappen?Let'srecallwhatactuallyhappenswhenwerunourend-to-endtests.Eachtestopensthebrowserandthenperformsclicks,inputs,andotherthingstotestourUI.ThekeywordofthelastsentenceisUI.IfweneedtotestaUI,weneedagraphicaluserinterface(GUI).Travisvirtualimagesdonothavegraphicaldisplays.Thus,thereisnowaythattheycanopenthebrowseranddisplayourUIsinit.Fortunatelyforus,thereisanicethingcalledXvfb-Xvirtualframebuffer.

Xvfbisadisplayserverthatimplementstheprotocolusedbythephysicaldisplays.Allneededgraphicaloperationsareperformedinmemory;thus,thereisnoneedofhavingphysicaldisplays.Therefore,wecanrunanXvfbserverthatwillprovideavirtualgraphicalenvironmenttoourtests.AndifyoucarefullyreadtheTravisdocumentation,youwillfindthatthisisexactlywhatitsuggestsasawayofrunningteststhatrequireGUI:https://docs.travis-ci.com/user/gui-and-headless-browsers/#Using-xvfb-to-Run-Tests-That-Require-a-GUI.Soopenthe.travis.ymlfilesandaddthefollowingtothebefore_scriptsection:

-exportDISPLAY=:99.0

-sh-e/etc/init.d/xvfbstart

ThewholeYMLfilenowlookslikethefollowing:

//.travis.yml

language:node_js

sudo:required

dist:trusty

node_js:

-"5.11.0"

before_script:

-exportCHROME_BIN=/usr/bin/google-chrome

-sudoapt-getupdate

-sudoapt-getinstall-ylibappindicator1fonts-liberation

-wgethttps://dl.google.com/linux/direct/google-chrome-

stable_current_amd64.deb

-sudodpkg-igoogle-chrome*.deb

-exportDISPLAY=:99.0

-sh-e/etc/init.d/xvfbstart

Page 344: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

CommititandcheckyourTravisdashboard.ThePomodoroapplicationwasbuiltsuccessfully!

ThePomodoroapplicationbuiltwithsuccess!

However,theshoppinglistapplication'sbuildhasfailed.NotethatTravisevenchangesthetab'stitlecolorforeachofthebuildstates:

Travischangestheicononthetab'stitleaccordingtothebuildstate

Sowhatishappeningwiththeshoppinglistapplicationbuild?There'sastepintheend-to-endtestthatchecksfortheGroceriestitlebeingpresentonthepage.Thethingisthatthistitlecomesfromourbackendserverthatshouldberunwiththenpmrunservercommand.DoyourememberweimplementeditinChapter6,Plugins–BuildingYourHousewithYourOwnBricks,usingthevue-resourceplugin?Thismeansthatbeforebuildingtheapplication,weneedtotellTravistorunoursmallserver.Justaddthefollowinglinetothe.travis.ymlfileoftheshoppinglistapplication:

-nohupnpmrunserver&

CommityourchangesandcheckTravisdashboard.Thebuildpassed!Everythingisgreenandwearehappy(atleastIam,andIhopethatsuccessfulbuildmakesyouhappyaswell).Nowitwouldbeniceifwecouldtelltheworldthatourbuildsarepassing.WecandoitbyaddingtheTravisbuttontoourREADME.mdfiles.Thiswillallowustoimmediatelyseethebuildstatusontheproject'sGitHubpage.

ClickonthebuildpassingbuttonontheTravispageofyourapplication,checktheMarkdownoptionfromtheseconddrop-downlist,andcopythegeneratedtexttotheREADME.mdfile:

Page 345: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Clickonthebuildpassingbutton,selectoptionMarkdownfromtheseconddrop-down,andcopythetexttotheREADME.mdfile

LookhowniceitlooksintheREADMEfileontheGitHubpageofourproject:

TheTravisbuttonlooksreallyfancyintheREADMEfileoftheprojectonitsGitHubpage

Nowthatourapplicationsarebeingcheckedoneachcommitandthereforewehaveforsureguaranteedtheirquality,wecanfinallydeploythemtothepubliclyaccessibleplace.

Page 346: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Beforestartingtheprocessofdeployment,pleasecreateanaccountatHeroku(https://signup.heroku.com/dc)andinstallHerokuToolbelt(https://devcenter.heroku.com/articles/getting-started-with-nodejs#set-up).

Nowwearereadytodeployourprojects.

Page 347: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

DeployingthePomodoroapplicationLet'sstartbyaddingnewapplicationtoourHerokuaccount.ClickontheCreateNewAppbuttonontheHerokudashboard.Youcancreateyourownnameorleavethenameinputfieldblank,andHerokuwillcreateanameforyou.IwillcallmyapplicationcatodorobecauseitisPomodorothathascats!

CreatinganewappwithHeroku

ClickontheCreateAppbuttonandyouwillproceedtothepage,choosingadeploymentpipelineforyourapplication.ChoosetheGitHubmethod,andthenfromtheproposeddrop-downofyourGitHubprojects,choosetheprojectthatwewanttodeploy:

Page 348: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

ChoosetheGitHubmethodofdeploymentandselectthecorrespondingprojectfromyourGitHubprojects

AfterclickingontheConnectbutton,twothingsthatyoumightprobablywanttocheckaretheAutomaticdeploysfrommasterareenabledandWaitforCItopassbeforedeployoptions:

ChecktheWaitforCItopassbeforedeploycheckboxandclickontheEnableAutomaticDeploysbutton

EverythingisreadytoperformafirstdeploymentandyoucanevenclickontheDeployBranchbutton,andHerokuwilltrytoperformabuild,butthen,ifyoutrytoopentheapplicationinthebrowser,itwillnotwork.Ifyouwonderwhy,youshouldalwayslookattherunninglogwhileperformingsuchoperations.

Page 349: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

CheckinglogsIhopeyouhavesuccessfullyinstalledtheHerokuCLI(orHerokutoolbelt),sonowyoucanrunherokucommandsinyourcommandline.Let'scheckthelogs.Runtheherokulogscommandinyourshell:

herokulogs--appcatodoro--tail

YouwillseeacontinuouslyrunninglogwhileHerokutriestoperformabuild.AndtheerrorisnpmERR!missingscript:start.Wedon'thaveastartscriptinourpackage.jsonfile.

Thisisentirelytrue.Inordertocreateastartscript,let'sfirsttrytounderstandhowtobuildandrunaVueapplicationforproduction.TheREADMEfiletellsusthatweneedtorunthenpmrunbuildcommand.Let'srunitlocallyandcheckwhathappens:

Theoutputofthenpmrunbuildcommand

Soweknowthattheresultofthebuildcommandgoestothedistfolder.Andwealsoknowthatwehavetoservetheindex.htmlfilefromthisfolderusinganHTTPserver.Wealsoknowthatwehavetocreateastartscriptinthescriptssectionofthepackage.jsonfile,soHerokuknowshowtorunourapplication.

Page 350: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

PreparingtheapplicationtorunonHerokuWewereabletogatheralotofinformationbycheckingthelogfile.Let'salsosummarizehereHeroku'spipelineforrunningtheapplicationbeforeproceedingtothestepsfordeployingtheapplication.

So,Herokudoesthefollowing:

Runsthenpminstallscripttoinstallalltheneededdependencies(itchecksthedependenciesinthedependenciessectionofthepackage.jsonfile)Runsthenpmstartscriptfromthepackage.jsonandservestheresultofitontheknownwebaddress

So,giventhisinformationandtheinformationwegatheredfromthelogsandrunningthenpmbuildscript,weneedtodothefollowing:

TellHerokutoinstallalltheneededdependencies;forthat,weneedtomoveprojectdependenciesfromthedevDependenciessectiontothedependenciessectioninthepackage.jsonfilesothatHerokuinstallsthemallTellHerokutorunabuildscriptafterperformingnpminstall;forthat,weneedtocreateapostinstallscriptinthepackage.jsonfilewherewewillcallthenpmrunbuildcommand.Createaserver.jsfilethatservestheindex.htmlfilefromthedistfolderProvideawayforHerokutoruntheserver.jsscript;forthis,weneedtocreateastartscriptinthepackage.jsonfilethatrunstheserver.jsscript

Startbymovingallthedependencies,excepttheonesthathavetodowithtesting,fromthedevDependenciessectiontothedependenciessectionofourpackage.jsonfile:

"dependencies":{

"autoprefixer":"^6.4.0",

"babel-core":"^6.0.0",

"babel-eslint":"^7.0.0",

"babel-loader":"^6.0.0",

"babel-plugin-transform-runtime":"^6.0.0",

"babel-polyfill":"^6.16.0",

"babel-preset-es2015":"^6.0.0",

"babel-preset-stage-2":"^6.0.0",

"babel-register":"^6.0.0",

"chalk":"^1.1.3",

"connect-history-api-fallback":"^1.1.0",

"cross-spawn":"^4.0.2",

"css-loader":"^0.25.0",

"es6-promise":"^4.0.5",

"eslint":"^3.7.1",

"eslint-config-standard":"^6.1.0",

"eslint-friendly-formatter":"^2.0.5",

"eslint-loader":"^1.5.0",

"eslint-plugin-html":"^1.3.0",

"eslint-plugin-promise":"^2.0.1",

Page 351: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

"eslint-plugin-standard":"^2.0.1",

"eventsource-polyfill":"^0.9.6",

"express":"^4.13.3",

"extract-text-webpack-plugin":"^1.0.1",

"file-loader":"^0.9.0",

"function-bind":"^1.0.2",

"html-webpack-plugin":"^2.8.1",

"http-proxy-middleware":"^0.17.2",

"inject-loader":"^2.0.1",

"isparta-loader":"^2.0.0",

"json-loader":"^0.5.4",

"lolex":"^1.4.0",

"opn":"^4.0.2",

"ora":"^0.3.0",

"semver":"^5.3.0",

"shelljs":"^0.7.4",

"url-loader":"^0.5.7",

"vue":"^2.0.1",

"vuex":"^2.0.0",

"vue-loader":"^9.4.0",

"vue-style-loader":"^1.0.0",

"webpack":"^1.13.2",

"webpack-dev-middleware":"^1.8.3",

"webpack-hot-middleware":"^2.12.2",

"webpack-merge":"^0.14.1"

},

"devDependencies":{

"chai":"^3.5.0",

"chromedriver":"^2.21.2",

"karma":"^1.3.0",

"karma-coverage":"^1.1.1",

"karma-mocha":"^1.2.0",

"karma-phantomjs-launcher":"^1.0.0",

"karma-sinon-chai":"^1.2.0",

"karma-sourcemap-loader":"^0.3.7",

"karma-spec-reporter":"0.0.26",

"karma-webpack":"^1.7.0",

"mocha":"^3.1.0",

"nightwatch":"^0.9.8",

"phantomjs-prebuilt":"^2.1.3",

"selenium-server":"2.53.1",

"sinon":"^1.17.3",

"sinon-chai":"^2.8.0"

}

Nowlet'screateapostinstallscriptinwhichwewilltellHerokutorunthenpmrunbuildscript.Inthescriptssection,addthepostinstallscript:

"scripts":{

<...>

"postinstall":"npmrunbuild"

},

Nowlet'screateaserver.jsfileinwhichwewillservetheindex.htmlfilefromthedistdirectory.Createaserver.jsfileintheproject'sfolderandaddthefollowingcontent:

Page 352: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

//server.js

varexpress=require('express');

varserveStatic=require('serve-static');

varapp=express();

app.use(serveStatic(__dirname+'/dist'));

varport=process.env.PORT||5000;

app.listen(port);

console.log('serverstarted'+port);

Okay,nowwejustneedtocreateastartscriptinthescriptssectionofourpackage.jsonfileandwearedone!Ourstartscriptshouldjustrunnodeserver.js,solet'sdoit:

"scripts":{

<...>

"postinstall":"npmrunbuild",

"start":"nodeserver.js"

},

Commityourchanges,gototheHerokudashboard,andclickontheDeployBranchbutton.Donotforgettocheckrunninglogs!

Andyippee!Thebuildwassuccessful!Afterasuccessfulbuild,youareinvitedtoclicktheViewbutton;don'tbeshy,clickonitandyouwillseeyourapplicationinaction!

ThePomodoroapplicationissuccessfullydeployedtoHeroku

NowyoucanuseyourPomodoroapplicationeverywhere.NowyoucanaskyourfriendstouseitaswellbysimplyprovidingthemtheHerokulink.

Well,congratulations!You'vejustdeployedyourVueapplication,anditcanbeusedbyeveryone.Howniceisit?

Page 353: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

DeployingtheshoppinglistapplicationInordertodeployourshoppinglistapplication,weneedtoperformexactlythesamestepsaswehavedonewiththePomodoroapplication.

CreateanewapplicationonyourHerokudashboardandconnectittoyourGitHubshoppinglistproject.Afterthat,copytheserver.jsfilefromthePomodoroapplication,dealwiththedependenciesinthepackage.jsonfile,andcreatepostinstallandstartscripts.

However,westillhaveonestepleft.DonotforgetaboutourbackendserverthatservestheRESTAPIfortheshoppinglists.Weneedtorunitaswell.

Orevenbetter,whydoweneedtoruntwoserversifwecanhavejustoneserverthatdoeseverything?WecanintegrateourJSONserverwithourexpressserverbyprovidingittheroutingpathtoservetheshoppinglistendpoint,let'ssayapi.Opentheserver.jsfile,importthejsonServerdependencythere,andtelltheexpressapptouseit.So,yourserver.jsfilewilllooklikethefollowing:

//server.js

varexpress=require('express');

varjsonServer=require('json-server');

varserveStatic=require('serve-static');

varapp=express();

app.use(serveStatic(__dirname+'/dist'));

app.use('/api',jsonServer.router('server/db.json'));

varport=process.env.PORT||5000;

app.listen(port);

console.log('serverstarted'+port);

Withtheprecedingline,wetellourexpressapptousejsonServerandservethedb.jsonfileoverthe/api/endpoint.

Weshouldalsochangetheendpoint'saddressinourVueresource.Openindex.jsintheAPIfolderandreplacelocalhost:3000withanapiprefix:

constShoppingListsResource=Vue.resource('api/'+'shoppinglists{/id}')

WeshouldalsoaddJSONserversupporttodev-server.js;otherwise,wewillnotbeabletoruntheapplicationindevelopmentmode.So,openthebuild/dev-server.jsfile,importjsonServer,andtelltheexpressapptouseit:

//dev-server.js

varpath=require('path')

varexpress=require('express')

varjsonServer=require('json-server')

<...>

//compilationerrordisplay

app.use(hotMiddleware)

Page 354: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

//usejsonserver

app.use('/api',jsonServer.router('server/db.json'));

<...>

Trytoruntheapplicationindevmode(npmrundev).Everythingworksfine.

Youcannowalsoremovetheserverrunningcommand(-nohupnpmrunserver&)fromthetravis.ymlfile.Youcanalsoremovetheserverscriptfrompackage.json.

Runtestslocallyandcheckthattheyarenotfailing.

Wearealmostdone.Let'stryourapplicationlocally.

Page 355: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

TryingHerokulocallySometimesitgetsalotoftry-failiterationstogetthingswork.Wetrysomething,commit,push,trytodeploy,seewhetheritworks.Werealizethatwehaveforgottenaboutsomething,commit,push,trytodeploy,seetheerrorlog.Doitagainandagain.Itmightbereallytime-consumingbecausethingsoverthenetworktaketime!Fortunatelyforus,theHerokuCLIprovidesawaytoruntheapplicationlocallyasitwasalreadydeployedtotheHerokuserver.Youjustneedtoruntheherokulocalwebcommandrightafterbuildingtheapplication:

npmrunbuild

herokulocalweb

Tryit.

Openhttp://localhost:5000inyourbrowser.Yes,itworks!

RunningtheapplicationlocallywiththeHerokulocalwebcommand.Itworks!

Let'snowcommitandpushthechanges.

NowyoucanwaitforthesuccessfulTravisbuildandautomaticdeploybyHerokuafterthat,oryoucanjustopenyourHerokudashboardandclickontheDeployBranchbutton.Waitabit.And...itworks!Hereistheresultoftwodeploymentsweperformedtoday:

Pomodoroapplication:https://catodoro.herokuapp.com/Shoppinglistapplication:https://shopping-list-vue.herokuapp.com/

TherespectiveGitHubrepositoriescanbefoundathttps://github.com/chudaol/Pomodoroandhttps://github.com/chudaol/ShoppingList.

Fork,play,test,deploy.Atthismoment,youhavealltheinstrumentsneededtoenhance,improve,andshowtheseapplicationstothewholeworld.Thankyouforbeingwithmethroughthisexcitingjourney!

Page 356: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Chapter9.WhatIsNext?Inthepreviouschapter,wemadeourapplicationsgolivebydeployingthemtoaserverandmakingthemavailabletotheworld.Wehavealsoguaranteedcontinuousintegrationandcontinuousdeploymentofourapplications.Thismeansthateverytimewecommitchangesperformedontheapplications,theywillautomaticallybetestedanddeployed.

Itseemsthatourjourneyinthisbookhasfinished.But,infact,ithasjuststarted.Afterallwehavediscoveredandlearned,thereisstillsomuchtodo!Inthischapter,wewillwrapupeverythingwehavelearnedsofarandseewhatwestillhavetolearnandwhatnicethingswestillcandotoreachthelevelofawesomenessofourapplications.So,inthischapter,wewilldothefollowing:

WrapupeverythingwehavelearnedsofarMakealistoffollowupthings

Page 357: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

ThejourneysofarWehavebeenonabigjourneysofar,andit'stimetosumupwhatwehavedoneandwhatwehavelearned.

InChapter1,GoingShoppingwithVue.js,wehadourfirstdatewithVue.js.WetalkedaboutwhatVue.jsis,howitwascreated,andwhatitdoesandsawsomebasicexamples.

InChapter2,Fundamentals-InstallingandUsing,wewentdeepintobehindthescenesofVue.js.WelearnedaboutMVVMarchitecturalpattern,wesawhowdoesVue.jswork,andwetoucheddifferentaspectsofVue.jssuchascomponents,directives,plugins,andapplicationstate.WelearneddifferentwaysofinstallingVue.js,startingfromusingasimplestandalonecompiledscript,passingbyusingtheCDNversion,NPMversion,andgoingtowardusingthedevelopmentversionofVue.jsbeingabletonotonlyuseitbutalsocontributetoitscodebase.WelearnedhowtodebugandhowtoscaffoldVue.jsapplicationusingVue-cli.WehaveevencreatedareallysimpleChromeapplicationusingCSP-compliantversionofVue.

InChapter3,Components-UnderstandingandUsing,weputourhandsdeepinsidethecomponent'ssystem.WelearnedhowtodefineVuecomponents,howcomponent'sscopeworks,andhowdocomponentsrelatetoeachother,andwestartedusingsingle-filecomponentsintheapplicationsthatwehavebootstrappedbefore.

InChapter4,Reactivity-BindingDatatoYourApplication,wewentdeepintodatabindingandreactivitywithVue.js.Welearnedhowtousedirectives,expressions,andfilters.Webroughtdatabindingtotheapplicationsdevelopedintheinitialchaptersandmadetheminteractive,thankstothereactivityfashionofVue.js.

InChapter5,Vuex-ManagingStateinYourApplication,welearnedhowtomanageglobalstateinVueapplicationsusingtheVuexstoresystem.Welearnedhowtousestate,actions,getters,andmutationsinordertocreateamodularandniceapplicationstructurewherethecomponentscaneasilycommunicatewitheachother.Weappliedthisnewknowledgeinourapplicationsthatwedevelopedsofarinthepreviouschapters.

InChapter6,Plugins-BuildingYourHousewithYourOwnBricks,welearnedhowVuepluginscooperatewithVueapplications.Weusedanexistingplugin,vue-resource,whichhelpedustosavetheapplication'sstatebetweenbrowserrefreshes.WealsocreatedourownpluginforVueapplicationsthatproduceswhite,brown,andpinknoises.Atthispoint,wehadfullyfunctionalapplicationswithaquitenicesetofworkingfeatures.

InChapter7,Testing-TimetoTestWhatWeHaveDoneSoFar!,welearnedhowtotestourVueapplications.Welearnedhowtowriteunittestsandhowtocreateandrunend-to-endtestswithSeleniumdriver.Welearnedwhatcodecoverageisandhowtofakeserverresponsesinunittests.Wecoveredalmost100%ofourcodewithunittestsandwesawtheSeleniumdriver

Page 358: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

inactionrunningourend-to-endtests.

InChapter8,Deploying-TimetoGoLive!,wefinallyexposedourapplicationstothewholeworld.WedeployedthemtotheHerokucloudsystemandnowtheycanbeaccessedfromeverywherewheretheInternetexists.Morethanthat,wemadeourdeploymentprocesscompletelyautomated.Eachtimewepushcodechangestothemasterbranch,theapplicationisdeployed!Evenmorethanthat.Theyarenotonlydeployedoneachpush,butalsoautomaticallytestedwiththeTraviscontinuousintegrationsystem.

Thus,inthisbook,wehaven'tjustlearnedanewframework.Weappliedourknowledgetodeveloptwosimple,yetniceapplicationsfromscratch.WeappliedthemostimportantVue'sconceptstomakeourapplicationsreactive,fast,maintainable,andtestable.However,thisisnottheend.Duringthewritingofthisbook,Vue2.0hasbeenlaunched.Itbringssomenewpossibilitiesandsomenewthingstolearnanduse.

Page 359: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Vue2.0Vue2.0launchedontheSeptember30,2016.CheckoutthispostofEvanYouathttps://medium.com/the-vue-point/vue-2-0-is-here-ef1f26acf4b8#.ifpgtjlek.

Acrossthisbook,weusedthenewestversion;however,ItriedtoreferencethewayofdoingthingsinthefirstgenerationofVuewheneveritwasnecessary.Actually,theAPIisalmostthesame;therearesomeslightchanges,somedeprecatedattributes,butthewholeinterfaceprovidedtothefinaluserremainsalmostuntouched.

Nevertheless,itwasalmostrewrittenfromscratch!Ofcourse,therearesomepartsofcodethatwerealmost100%reused,butoverall,itwasamajorrefactorandsomeoftheconceptswerecompletelychanged.Forexample,therenderinglayerwascompletelyrewritten.If,earlier,therenderingenginewasusingtherealDOM,nowitusesalightweightvirtualDOMstructure(https://github.com/snabbdom/snabbdom).Itsperformancebeatseverything!Checkoutthebenchmarkfigureinthefollowing:

Performancebenchmark(thelowerisbetter)takenfromhttps://medium.com/the-vue-point/vue-2-0-is-here-ef1f26acf4b8#.fjxegtv98

Thereisanotherinterestingpointinthisnewversion.IfyouhavealreadyusedthefirstgenerationofVue,andreadaboutitandlistenedtopodcasts,youprobablyknowthatoneof

Page 360: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

themajordifferencesbetween,let'ssay,VueandReactwasReactNative(theframeworkthatallowsustobuildnativeappsbasedonReact).EvanYouwasalwaysclaimingthatVuewasjustatinylayerforwebinterfaces.Now,wehavetheemergingWeex,aframeworkthatrendersVue-inspiredcomponentsintonativeapps(https://github.com/alibaba/weex).AccordingtoEvanYou,verysoon,"Vue-inspired"willbecome"Vue-powered"!Justwaitforit.Juststaytuned.IwouldliketorecommendthisamazingFullStackRadiopodcast,whereEvanYoutalksaboutthenewversionofVue:http://www.fullstackradio.com/50.

Vuehasevolvedalotsinceitshumblebeginningasasideproject.Todayitiscommunityfunded,widelyadoptedintherealworld,andboastsoneofthestrongestgrowthtrendsamongallJavaScriptlibrariesaccordingtostats.js.org.Webelieve2.0isgoingtopushitevenfurther.It'sthebiggestupdatetoVuesinceitsinception,andweareexcitedtoseewhatyoubuildwithit.-EvanYou,https://medium.com/the-vue-point/vue-2-0-is-here-ef1f26acf4b8#.fjxegtv98)

Withthisinmind,ifyouarecomingfromtheVue1.0generation,itwillnotbehardforyoutoupgradeyourapplications.Checkthemigrationguide,http://vuejs.org/guide/migration.html,installthemigrationhelper,https://github.com/vuejs/vue-migration-helper,applyallneededchanges,andseehowyourapplicationsperformafterthat.

Page 361: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

RevisitingourapplicationsLet'scheckagainwhathavewedonesofar.WehavedevelopedtwoapplicationsusingVue.js.Let'srevisitthem.

Page 362: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

ShoppinglistapplicationTheshoppinglistapplicationthatwehavedevelopedinthisbook'schaptersisawebapplicationthatallowsthefollowing:

CreatedifferentshoppinglistsAddnewitemstotheshoppinglistsandcheckthemoncetheyareboughtRenameshoppinglistsandremovethem

OurshoppinglistapplicationresidesontheHerokucloudplatform:https://shopping-list-vue.herokuapp.com/.

ItscodeishostedonGitHub:https://github.com/chudaol/ShoppingList.

ItiscontinuouslyintegratedwithTravis:https://travis-ci.org/chudaol/ShoppingList

Itsinterfaceissimpleandeasytounderstand:

TheinterfaceoftheshoppinglistapplicationdevelopedusingVue.js

Itisstillfarfromsomethingthatyouwoulduseeverytimeyougoshopping,isn'tit?

Page 363: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

ThePomodoroapplicationThePomodoroapplicationthatwehavedevelopedinthisbookisawebapplicationthatimplementsaPomodorotimerwithwhitenoiseduringtheworkingPomodorosandnicepicturesofcatsduringtheintervaltime.Itallowsthefollowing:

Start,pause,andstoptheapplicationListentowhitenoisewhileworking,noisethathelpsconcentratingMuteandunmutethewhitenoisesoundStareatthekittensduringsparetime

OurPomodoroapplicationisalsohostedontheHerokucloudplatform:https://catodoro.herokuapp.com/.

ItscodeisalsohostedatGitHub:https://github.com/chudaol/Pomodoro.

AnditisalsobuiltandtestedoneachpushusingtheTraviscontinuousintegrationplatform:https://travis-ci.org/chudaol/Pomodoro.

Itsinterfaceiscleanandeasytouse.Hereiswhatitshowsforthe20-minuteworkingPomodorointerval:

ThePomodoroapplicationduringtheworkingPomodoro

Andhere'swhatappearswhenthetimefora5-minutebreakcomes:

Page 364: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

ThePomodoroapplicationduringitsintervaltime

Itisactuallyprettyusable,butalsostillfarfromperfect.

Page 365: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Whyisitjustthebeginning?Intheprevioussection,wesummarizedwhattheapplicationsthatwehavedevelopedthroughoutthebookaredoing.Wehavealsoagreed(Ihope)thattheyarestillfarfromperfect.Thingsthatarefarfromperfectarethingsthatwewanttoimproveandthereforetheygiveuschallengesandpurpose.Thereisactuallystillalotofworktobedone.Ourapplicationsarenice,buttheylackfeatures,style,identity,UXpatterns,extensiontootherplatforms,andsoon.Let'scheckwhatwecanstilldo.

Page 366: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

AddingfeaturestoourapplicationsOurapplicationsalreadyhavesomeprettynicefeatures,buttheycanhaveevenmore.Theycanbemoreconfigurable.Theycanbemoreflexible.TheycanbemoreUI/UXfriendly.Let'slookateachofthemandwritealistoffeaturesthatcanbeadded.Itwillbeyourhomework.

Shoppinglistapplication

Openourshoppinglistapplicationinthebrowserandlookatit.Youcanaddyourlistsanditemstothem.Youcandeleteitemsandlists.Buteverypersonwhoopenstheapplicationinthebrowserwillbeabletodothesame.Itmeansthatwehavetoprovideawayofeverypersonhavingtheirownshoppinglistapplication,whichisonlypossiblewithanauthenticationmechanism.

TherearealsosomeUXissues.Whyshouldwechangethenameoftheshoppinglistusingtheinputfieldinthefooterifwecanchangeit,let'ssay,inline?Well,actually,shoppinglist'snameeditingintheinputfieldwasthefirstthingweimplementedwhenwewerelearninghowtoachievedatabindinginVueapplications.So,itmadesenseatthetime,butnowitcanandshouldbeimproved.

Anotherthinghastodowithdeleteditems.Thereisnowayofclearingthemup.Ifwehavealonglistofitems,evenwhenwedeletethem,theystayforeverunlessweremovethewholeshoppinglist.Thereshouldbeawayofclearingupcheckeditemsonthelist.

Anothercosmeticchangethatwecanapplyhastodowithstyling.Differentlistsmighthaveadifferentbackgroundcolors,differentfontcolors,andprobablyevendifferentfontstylesandsizes.Withthat,here'sthelistofimprovementsfortheshoppinglistapplication:

ImplementanauthenticationmechanismImplementinlinenameeditingImplementclearingupcheckeditemsImplementamechanismofconfiguringdifferentshoppinglists'styling,suchasbackgroundcolor,textcolor,fontsize,andstyle

Youcanalsoimplementcategoriesfortheitemsandiconsforeachofthecategories.Asaninspiration,youcanhavealookattheSplitwiseapplicationathttps://www.splitwise.com/.Whenyoustartaddingitems,theiconoftheitemisgeneric.Onceyoutypeinsomethingmeaningful,theiconchanges,asshowninthefollowingscreenshot:

Page 367: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

ThescreenshotoftheSplitwiseapplicationfortheinspirationfortheiconcategories:itadaptstowhatyoutypeintheinputfield

Trytoimplementthiskindofcategorizationforourshoppinglistapplication.Itwouldbeareallyniceandpowerfulbonus!

ThePomodoroapplication

OpenourPomodoroapplicationinyourbrowserandtryusingit.It'snice,withoutanydoubt.Itissimpleandeasytouse.However,someextraconfigurationmightbringsomeextrapowertoit.Forexample,whyshouldIworkfor20minutes?MaybeIwouldliketohave15-minuteperiodsofworkingPomodoros.OrmaybeIwanttohavebiggerworkingPomodoros,let'ssay25or30minutes.Itshoulddefinitelybeconfigurable.

Let'sthoroughlycheckthedescriptionofthePomodorotechniqueinWikipediatoseeifwearemissingsomething:https://en.wikipedia.org/wiki/Pomodoro_Technique.

I'mprettysureweare.Checkthispointattheunderlyingprinciples:

"Afterfourpomodoros,takealongerbreak(15-30minutes),resetyourcheckmarkcounttozero,thengotostep1."

--https://en.wikipedia.org/wiki/Pomodoro_Technique

Aha!SomethingshouldhappenafterfourPomodoros.Biggerinterval,moretimestaringatcats(ordoingwhateveryouwanttodo).Hmm,probablyitwouldbenicetobeabletoconfigurethisperiodoftimeaswell!

There'sanotherimportantthing.Asanyhumanbeing,afterworkinghard,Iwouldliketosee

Page 368: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

someprogress.Wouldn'titbeniceifourPomodoroapplicationcoulddisplaysomestatisticsabouttheamountoftimewewereabletoconcentrateonourselvesandtodoourwork?Forthis,wecouldcollectsomestatisticsanddisplaytheminourPomodorotimer.

Also,wouldn'titbenicetostorethesestatisticstobeabletovisualizethemduringsomeperiodoftime,let'ssay,oneweek,onemonth,oneyear?Thisleadsustotheneedtoimplementastoragemechanism.Thisstoreshouldstorestatisticsforeachuser,soagain,anauthenticationmechanismisneededaswell.

Let'sthinkaboutournicewhite,brown,andpinknoises.Currently,wejustplaythebrownnoisethatishardcodedinourApp.vue:

<template>

<divid="app"class="container"v-noise="'brown'">

</div>

</template>

Shouldn'twebeabletoswitchbetweennoisesandchooseourfavoriteone?Hence,wehaveidentifiedonemoreitemtoaddtotheapplication'sconfiguration.That'senoughfornow;let'sputallthisinthelist:

ImplementtheauthenticationmechanismImplementastoragemechanism—itshouldcollectthestatisticsaboutworkingtimesandstoretheminsomepersistencelayerImplementthestatisticsdisplayingmechanism—itshouldgrabthestoredstatisticaldataanddisplayitinaniceandcleanway(forexample,charts)AddaconfigurationmechanismtothePomodoroapplication.Thisconfigurationshouldallowthefollowing:

ConfigurethePomodoroworkingperiodoftimeConfiguretherestingintervalsoftimesConfigureabigrestingtimeafteraconfigurableamountofworkingPomodoros(4bydefault)Configurethepreferrednoisetoplayduringtheworkingintervals

Asyoucansee,youstillhavesomeworktodo.It'sagoodthing,youhavealreadyaworkingPomodorotimerapplicationtousewhileyouareworkingonitsimprovements!

Page 369: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

BeautifyingourapplicationsBothapplicationsarecurrentlyprettygray.OnlythePomodorotimerapplicationbecomesalittlebitmorecolorfulwhenacatappearsonthescreen.Itwouldbenicetoaddsomedesigntothem.Makethemunique,givethemtheiridentity;youworkedsohardonthem,theyclearlydeservesomeniceclothes.Let'sthinkaboutwhatwecandowithstyling.

Logotype

Startwiththelogotype.Agoodlogodefinesyourproductandmakesitunique.IcanhelpyouwiththePomodoroapplication'slogo,atleastwiththeideaforit.IhaveaverygoodfriendcalledCarinawhodesignedatomatoformeandIhavejusttriedmybesttoaddalittlekittentoit.Checkitout.Youcanuseitasisorusejustasanideatodevelopyourown.Eventheskyisnotthelimitforyourimagination,really!

TheideaforalogotypeforthePomodoroapplication

Thinkaboutanicelogofortheshoppinglistapp.Whatcanitbe?Abagforthegroceries?Acheckbox?Justinitials—SL?Again,nolimits.Ihopetoseeyournicelogosinthe

Page 370: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

repositoriesforks.Can'twaitforit!

Identityanddesign

Ourapplicationsdefinitelyneedsomeuniquedesign.UsesomeUXtechniquestodevelopaniceidentityguideforthem.Thinkaboutcolors,fonts,andhowtheelementsshouldbecomposedonthepagesothattheyprovideauniqueuser-friendlyexperiencetoourusers.

Animationsandtransitions

Animationsandtransitionsarepowerfulmechanismsthatbringsomelifetoanapplication.However,theycannotbeabused.Thinkaboutwhereandhowtheymakesense.Forexample,hoveringontheshoppingliststitlescouldendupinsomehighlighting,shoppinglistitemscandosometinybouncingwhentheyarechecked,theprocessofchangingthetitleoftheshoppinglistcouldalsobehighlightedinsomeway,andsoon.ThePomodoroapplicationcanchangeitsbackgroundcoloroneachofthestate'stransitions.Itcanalsobeawareofthetimeofthedayandcolorthebackgroundaccordingly.Thenumberofopportunitiesisendless.Useyourcreativity,useVue'spowertoachieveyourideas.

Page 371: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

ExtendingourapplicationstootherdevicesBothofourapplicationsarewebapplications.WhileitmightbeokayforthePomodoroapplicationifweworkthewholedayonthecomputerandusetheWeb,itmightbealittlebituncomfortablefortheshoppinglistapplication.Youdon'tbringyourlaptopwhenyougoshopping.Ofcourse,youcanfilltheshoppinglistwithitemsathomeandthenopenthemobilebrowserinthesupermarket,butitmightbeslowandnotsonicetouse.UseWeex(https://github.com/alibaba/weex)tobringourwebapplicationstothemobiledevices.BoththeapplicationscanalsobeextendedtobeusedasaGoogleChromeapp,justaswelearnedinChapter2,Fundamentals-InstallingandUsing.Extendyourworktoeachandeverydeviceyoucan.Iamlookingforwardtocheckingyourwork.

Page 372: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

SummaryThisisthelastchapterofthisbook.Honestly,Ifeelalittlebitsadaboutit.Ihadareallyfuntimewithyou.IknowthatIdon'tknowyou,butIfeellikeIdo.ItalktoyouandIfeelthatsometimesyoutalktome.Everythingthatwasdevelopedsofar,Icannotsayatallthatitwasdevelopedbyme;Ifeelthatwehavebeenworkingtogetheronitallthistime.

Itisaveryfunnyfeeling,actually,becauseIamatthesametimeinthepresentandinthefuturewhenyouarereadingthisbook(forme,it'sthefuture).Andyouarenowinyourpresentandatthesametimetalkingtomeinthepast.Ilovethewaythatbooksandtechnologiesestablishconnectionsnotonlybetweenpeoplebutalsobetweendifferenttimeintervals.Thisisamazing.

IreallyhopethatyoubecameafanofVue.jsinthesamewayIamafanofit.

Ireallyhopethatyouwillenhanceatleastoneoftheapplicationswehavedevelopedsofarandshowittome.Iwillbereallygladtohelpifyouneedmyhelp.Donothesitatetodropmeamessageatchudaol@gmail.com.

Thankyouforbeingwithmeallthistime,andIhopetomeetyousooninthenextbook!

Page 373: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Chapter10.SolutionstoExercises

Page 374: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Exerciseforchapter1Intheendofthefirstchapter,therewasthefollowingexercise:

Note

ThePomodorotimerthatwehavebuiltinthepreviouschaptersis,withoutanydoubt,great,butitstilllackssomenicefeatures.Areallynicethingthatitcouldprovidewouldbeshowingrandomkittensfromhttp://thecatapi.com/duringrestingtime.Canyouimplementthis?Ofcourseyoucan!Butpleasedonotconfuserestingwithworkingtime!Iamalmostsurethatyourprojectmanagerwillnotlikemuchifyoustareatthekittensinsteadofworking:)

Let'ssolveit.

CheckthecodeforPomodoroathttps://jsfiddle.net/chudaol/b6vmtzq1/.

Checkhttp://thecatapi.com/website.

Let'sstartbyaddingthewellBootstrapelementwithanimagewhosesourcepointstothecatAPI:

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

<...>

<divclass="well">

<img:src="’http://thecatapi.com/api/images/get?

type=gif&size=med’"/>

<div>

</div>

Ifyouopenthepageyouwillseethattheimageisalwaysvisible.Thisisnotwhatwewant,wewant,ittoonlybevisiblewhenweareinourPomodororestinginterval.Youalreadyknowhowtodoit.Thereareseveralwaysofachievingthis;let'susetheclassbindingmethodandbindaclassthat'shiddenwhenthestateisworking:

<divclass="well":class="{'hidden':pomodoroState==='work'}">

<img:src="'http://thecatapi.com/api/

images/get?type=gif&size=med'"/>

</div>

Now,ifyouopenthepageyouwillseethattheimageonlyappearswhentheworkingPomodoroisfinished.

However,theproblemisthatforallthetimethatwerest,theimageisthesame.Itwouldbegreatifwecouldupdateitevery,let'ssay,10seconds.

Let'suseacachebustermechanismforthispurpose.IfweattachsomepropertytoourURLandchangeiteach10seconds,theURLwillchangeandthereforewewillobtainanotherrandomcat.Let'saddatimestampvariabletoourVueapplicationandchangeitinsidethe

Page 375: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

_tickfunction:

<...>

newVue({

el:"#app",

data:{

<...>

timestamp:0

},

<...>

methods:{

<...>

_tick:function(){

//updatetimestampthatisusedinimagesrc

if(this.second%10===0){

this.timestamp=newDate().getTime();

}

<...>

}

}

});

Afterthetimestampiscreatedandupdated,wecanuseitinourimagesourceURL:

<divclass="well":class="{'hidden':pomodoroState==='work'}">

<img:src="'http://thecatapi.com/api/images/get?

type=gif&size=med&ts='+timestamp"/>

</div>

That'sall!CheckthewholecodeinthisJSFiddleathttps://jsfiddle.net/chudaol/4hnbt0pd/2/.

Page 376: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Exercisesforchapter2

Page 377: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

EnhancingMathPluginEnhanceourMathPluginwithtrigonometricalfunctions(sine,cosine,andtangent).

Actually,itisjustaboutaddingthemissingdirectivesandusingtheMathobject'sfunctionsinit.OpenVueMathPlugin.jsandaddthefollowing:

//VueMathPlugin.js

exportdefault{

install:function(Vue){

Vue.directive('square',function(el,binding){

el.innerHTML=Math.pow(binding.value,2);

});

Vue.directive('sqrt',function(el,binding){

el.innerHTML=Math.sqrt(binding.value);

});

Vue.directive('sin',function(el,binding){

el.innerHTML=Math.sin(binding.value);

});

Vue.directive('cos',function(el,binding){

el.innerHTML=Math.cos(binding.value);

});

Vue.directive('tan',function(el,binding){

el.innerHTML=Math.tan(binding.value);

});

}

};

YoucancheckhowthisdirectiveworksintheHTMLfile:

//index.html

<divid="app">

<inputv-model="item"/>

<hr>

<div><strong>Square:</strong><spanv-square="item"></span></div>

<div><strong>Root:</strong><spanv-sqrt="item"></span></div>

<div><strong>Sine:</strong><spanv-sin="item"></span></div>

<div><strong>Cosine:</strong><spanv-cos="item"></span></div>

<div><strong>Tangent:</strong><spanv-tan="item"></span></div>

</div>

That'sit!

Page 378: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

CreatingaChromeapplicationofthePomodorotimerPleasecombineasolutionofbootstrappingtheapplicationusingaSCP-compliantversionofVue.jsandthesimplePomodoroapplicationthatwecreatedinChapter1,GoingShoppingwithVue.js.Checkthecodeinthechrome-app-pomodorofolder.

Page 379: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Exercisesforchapter3

Page 380: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Exercise1Whenwewererewritingtheshoppinglistapplicationusingsimplecomponents,welosttheapplication'sfunctionality.Theexercisesuggestsusinganeventsemittingsysteminordertobringthefunctionalityback.

Thecodeweendedupwithinthissectionwaslookingsimilartowhatisinthechapter3/vue-shopping-list-simple-componentsfolder.

Whydoesn'titwork?Checkthedevtoolserrorconsole.Itstatesthefollowing:

[Vuewarn]:Propertyormethod"addItem"isnotdefinedontheinstancebut

referencedduringrender.Makesuretodeclarereactivedatapropertiesinthe

dataoption.

(foundincomponent<add-item-component>)

Aha!Thishappensbecauseinsideadd-item-templatewearecallingtheaddItemmethodwhichdoesnotbelongtothiscomponent.Thismethodbelongstotheparentcomponent,andofcourse,thechildcomponentdoesnothaveaccesstoit.Whatshouldwedo?Let'semitevents!Wealreadyknowhowtodoit.So,wedon'thavetodotoomuch.Actually,wehavetodothreesmallthings:

AttachtheaddItemmethodtoadd-item-componentinwhichwewillemitaneventandpassthiscomponent'snewItempropertytoit.Modify/simplifytheaddItemmethodoftheparentcomponent.Itshouldnowjustreceiveatextandaddittoitsitemsproperty.Attachthev-onmodifierwiththenameoftheeventtothecomponent'sinvocationinsidethemainmarkupthatwillcalltheaddItemmethodeachtimetheeventisemitted.

Let'sstartbyaddingtheaddItemmethodtoadd-item-component.ItiscalledeachtimetheaddbuttonorEnterishit.ThismethodshouldcheckthenewItemproperty,andifitcontainsatext,shouldemitanevent.Let'scallthiseventadd.Thus,theJavaScriptcodeofourcomponentwillnowlookthefollows:

//additemcomponent

Vue.component('add-item-component',{

template:'#add-item-template',

data:function(){

return{

newItem:''

}

},

methods:{

addItem:function(){

vartext;

text=this.newItem.trim();

if(text){

this.$emit('add',this.newItem);

Page 381: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

this.newItem='';

}

}

}

});

Whentheaddeventisemitted,somehowtheaddItemmethodofthemaincomponentshouldbeinvoked.Let'sbindtheaddeventtoaddItembyattachingthev-on:addmodifiertotheadd-item-componentinvocation:

<add-item-componentv-on:add="addItem":items="items">

</add-item-component>

Okaythen.Asyoucansee,thismethoddoesalmostthesamethattheaddItemmethodofthemaincomponentwasdoingbefore.Itjustdoesn'tpushnewItemtotheitemsarray.Let'smodifytheaddItemmethodofthemaincomponentsoitjustreceivesalreadyprocessedtextandpushesitintothearrayofitems:

newVue({

el:'#app',

data:data,

methods:{

addItem:function(text){

this.items.push({

text:text,

checked:false

});

}

}

});

We'redone!Thefullsolutionofthisexercisecanbefoundintheappendix/chapter3/vue-shopping-list-simple-componentsfolder.

Page 382: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

Exercise2InthesectioncalledRewritingtheshoppinglistapplicationwithsingle-filecomponentsinChapter3,Components–UnderstandingandUsing,wedidquiteanicejobofchangingtheshoppinglistapplicationusingsingle-filecomponents,buttherearestillfewthingsleft.Wehavetwomissingfunctionalities:addingitemstotheitemslist,andchangingthetitle.

Inordertoachievethefirstfunctionality,wehavetoemitaneventfromAddItemComponentandattachthev-onmodifiertotheadd-item-componentinvocationwiththemainApp.vuecomponent,exactlylikewehavedoneinthecaseofdealingwithsimplecomponents.Youcanbasicallyjustcopyandpastethecode.

Thesamegoesforthechangingtitlefunctionality.Weshouldalsoemitaninputevent,justlikewedidinthesimplecomponentsexample.

DonotforgettoaddthestyletotheApp.vuecomponenttomakeitlookjustasitwasbefore.

Checkthefullcodeintheappendix/chapter3/shopping-list-single-file-componentsfolder.

Page 383: Learning Vue.js 2sd.blackball.lv/library/Learning_Vue.js_2_(2016).pdf · Olga is happily married to an awesome guy called Rui, who is also a software engineer. Rui studied with Olga

SummaryInthischapter,youlearnedhowtomakeourapplicationsavailableforeveryone.YoualsolearnedhowtodeploythemusingHerokuintegrationwiththeGitHubrepository.Youalsolearnedhowtodoitautomaticallyoneachcommitandpush.WealsousedTravisforautomaticbuildsoneachdeployment.Nowourapplicationsarebeingfullytestedandautomaticallyredeployedeachtimewecommitachange.Congratulations!

Youprobablythinkthatthisistheendofthejourney.No,itisnot.Thisisjustthebeginning.Inthenextchapter,wewillseewhatyoucanlearnandwhatnicethingsyoucandonextwithboththePomodoroandshoppinglistapplications.Staywithme!