javascript for .net developers - programmer-books.com · he wrote a book in 2013 on web development...
TRANSCRIPT
JavaScriptfor.NETDevelopers
TableofContents
JavaScriptfor.NETDevelopersCreditsAbouttheAuthorAbouttheReviewerwww.PacktPub.com
eBooks,discountoffers,andmoreWhysubscribe?
PrefaceWhatthisbookcoversWhatyouneedforthisbookWhothisbookisforConventionsReaderfeedbackCustomersupport
DownloadingtheexamplecodeDownloadingthecolorimagesofthisbookErrataPiracyQuestions
1.JavaScriptforModernWebApplicationsImportanceofJavaScript
WhatisJavaScript?Comparingruntimes
SettingupyourenvironmentNeweditingexperienceofJavaScriptinVisualStudio2015IDE
ProgramminginJavaScriptCorefundamentalsofJavaScript
AddingJavaScripttoanHTMLpageStatementsinJavaScriptLiteralsandvariablesDatatypes
ArrayinJavaScriptWhatisJSON?
SimpleobjectsinJSONDeclaringarraysinJSONNestingdatainJSON
ConversionsindatatypesElementsofJavaScript
ConstantsinJavaScriptCommentsCasesensitivity
CharactersetExpressions
ThethiskeywordSequenceofcodeexecutioninJavaScriptUsingthethiskeywordonacallingmethodThefunctionstatementandexpressionClassstatementandexpressionGroupingoperatornewsuper
OperatorsAssignmentoperatorsArithmeticoperatorsUnaryoperatorsComparisonoperators
StrictequaloperatorStrictnotequaloperator
LogicaloperatorsLogicalANDLogicalORLogicalNOT
BitwiseoperatorsBitwiseANDBitwiseORBitwiseNOTBitwiseXOR
BitwiseshiftoperatorsBitwiseleftshiftBitwiserightshift
ThetypeofoperatorThevoidoperatorThedeleteoperatorMiscellaneousoperators
ConditionaloperatorsSpreadoperator
Built-indisplaymethodsinJavaScriptDisplayingmessages
AlertboxConfirmboxPromptbox
WritingonapageWritingintothebrowser'sconsolewindow
BrowserObjectModelsinJavaScriptWindow
DocumentNavigator
PropertiesScreen
PropertiesHistory
MethodsLocation
PropertiesMethods
Summary2.AdvancedJavaScriptConcepts
Variables–scopeandhoistingDeclaringlet
ConditionswhereletisefficienttouseFunctionsinloops
EventsinJavaScriptFunctionargumentsObject-orientedprogramminginJavaScript
CreatingobjectsDefiningobjectsusingobjectliteralnotationDefiningobjectsusingaconstructorpattern
UsingtheclasskeywordProperties
DefiningpropertiesusingobjectliteralnotationDefiningpropertiesusingaconstructorpatternDefiningpropertiesusingsetters/gettersinECMAScript6JavaScriptpropertydescriptors
DisplaypropertydescriptorsManagingpropertydescriptorsUsinggettersandsetters
MethodsDefiningmethodsthroughobjectliteralnotationapproachDefiningobjectsusingtheconstructorfunctionapproach
ExtendingpropertiesandmethodsPrivateandpublicmembersInheritance
ChainingconstructorsinJavaScriptInheritanceusingObject.create()PredefinedpropertiesofObject.create()Defininginheritanceusingclass
EncapsulationAbstractionnew.target
NamespaceExceptionhandling
ErrorRangeErrorReferenceErrorSyntaxErrorTypeErrorURIError
ClosuresPracticaluse
JavaScripttypedarraysTypedarrayarchitecture
ThearraybufferCreatingabuffer
Maps,sets,weakmaps,andweaksetsMapsandweakmapsSetsandweaksetsThestrictmode
Summary3.UsingjQueryinASP.NET
GettingstartedwithjQueryUsingacontentdeliverynetwork
TheuseofCDNThedocumentreadyeventThejQueryselectors
SelectingtheDOMelementsusingtheIDSelectingtheDOMelementsusingTagNameSelectingnodesbytheclassnameSelectingbytheattributevalueSelectinginputelementsSelectingalltheelementsSelectingthefirstandlastchildelementsThecontainsselectorinjQuerySelectingtheevenandoddrowsselectors
ManipulatingDOMModifyinganelement'spropertiesCreatingnewelementsRemovingelementsandattributes
EventhandlinginjQueryRegisteringeventsinjQueryBindingeventsusingonandoffUsingthehoverevents
Summary4.AjaxTechniques
IntroducingAjaxHowAjaxworks
AjaxrequestsusingtheclassicXHRobjectXHRmethodsXHReventsXHRproperties
MakinganAjaxrequestusingjQueryjQuery.ajax()
AjaxpropertiesPre-filteringAjaxrequestsSettingdefaultvaluesforallfutureAjaxrequests
LoadingdatathroughthegetfunctionsinjQueryUsingjQuery.get()UsingjQuery.getJSON()UsingjQuery.getScript()
PostingdatatoserverusingthepostfunctionAjaxevents
LocaleventsGlobalevents
Cross-originrequestsJSON-P
UsingJSON-PCORS
SpecifyingtheCORSpolicyatserviceslevelEnableCORSattheConfiguremethod
CallingWCFservicesfromJavaScriptSummary
5.DevelopinganASP.NETApplicationUsingAngular2andWebAPITypeScript
CompilationarchitectureofTypeScriptAdvantagesofTypeScript
SupersetofJavaScriptSupportforclassesandmodulesStatictypecheckingECMAScript6featuresupportOptionaltypingDeclaringtypesinTypeScript
CoreelementsofTypeScriptDeclaringvariablesTypesClassesandinterfaces
DefininginterfacesDerivingclassesandinterfacesGenericclasses
FunctionsGenericfunctions
IteratorsModulesandnamespaces
IntroductiontoAngular2Angular2architecture
EventsofcomponentlifecycleModulesComponents
CorepropertiesofAngular2componentsTemplatesandselectorsInputsandoutputs
UsinginputsUsingoutputs
DirectivesCreatingasimpleHelloWorlddirective
StructuraldirectivesAttributedirectiveProviders
DependencyinjectioninAngularRoutinginAngular
Developingato-doapplicationinASP.NETCoreCreatingaCommonprojectCreatingaTodoServiceAppproject
EnablingMVCinaWebAPIprojectInstallingEntityFrameworkAddingAppSettingstostoreaconnectionstringConfiguringAppSettingsintheStartupclassAddingdataaccessinWebAPIEnablingCORSintheASP.NETWebAPIRunningdatabasemigrationCreatingacontroller
CreatingaTodoWebAppprojectConfiguringAngular2intheTodoWebAppprojectDependencies
DevelopmentdependenciesConfiguringTypeScriptConfiguringGulp
AddingAngularcomponentsAddingtheto-doservicecomponentAddingato-doviewcomponent
Creatingthemainto-dopageCreatingacustomto-dotaghelperAddingato-doMVCcontroller
GeneratingviewsfortheTodoControlleractionmethodsDevelopingtheCreateTodocomponent
Summary6.ExploringtheWinJSLibrary
IntroductiontoWinJSWinJSfeatures
JavaScriptcodingandlanguagepatternsStylesheetsWindowsruntimeaccessSecurityAppmodelDatabindingControlsUtilities
UsageofWinJSAddingtheWinJSlibraryintheASP.NETapplication
CDNNPMNuGet
GettingstartedwithWinJSUsingWinJSintheASP.NETapplicationExistingWindowsapptemplateinVisualStudio
ExploringWinJScorefundamentalsClassesandnamespaces
DefiningclassesinWinJSDerivingclassesinWinJSNamespacesinWinJS
MixinEventsinWinJSDatabinding
OnetimedatabindingOnewaydatabindingTwowaydatabindingAdatabindingworkingmodel
PromisesOtheroperationsofpromises
ChainingpromisesandhandlingerrorsCancelingpromisesJoiningpromisesCheckingpromiseWrappingnon-promiseintopromise
ExploringWinJScontrolsandstylesAddingWinJScontrolsSettingpropertiesofWinJScontrols
UsingWindowsruntimefeaturesHostedappsandaccessingthecamera
CreatingtheASP.NETcoreapplicationConvertinganASP.NETapplicationintoWindowsapplicationusingtheHostedapp
conceptSummary
7.JavaScriptDesignPatternsCreationalpatterns
SingletondesignpatternFactorypatternAbstractfactorypatternPrototypepattern
StructuralpatternsAdapterpatternDecoratorpatternFacadepatternBridgepattern
BehavioralpatternChainofresponsibilitypatternObserverpatternPub/subpatternPromises
Summary8.Node.jsforASP.NETDevelopers
IntroductiontoNode.jsRequestprocessingbytheNode.jswebserverComparisonofNode.jswith.NETNPM
InstallingNode.jsUsingNode.jswithVisualStudio2015
SimpleconsoleapplicationusingNode.jsWebapplicationswithNode.jsCreatingblankNode.jsapplicationsUsingtheExpressframeworkforwebapplicationsinNode.js
ExtendsimpleNode.jstouseExpressExpressviewengines
EJSviewengineJadeviewengineRoutingintheExpressapplication
MiddlewareMVCwiththeExpressframework
MVCpatternCreatingacontrollerCreatingdataservices
AccessingtheMicrosoftSQLserverinNode.jsReadingarecordfromtheMicrosoftSQLserverdatabaseCreatingarecordintheMicrosoftSQLserverdatabase
Summary9.UsingJavaScriptforLarge-ScaleProjects
ThinkbeforeproceedingDevelopinghighlyscalableandmaintainableapplications
ModularizationImplementingthemodulepatternModularizingJavaScriptcodethroughRequireJS
CreatingmodulesusingtheRequireJSAPIBootstrappingRequireJS
Event-drivenmessagingImplementingmediatorpatternforcommunicationbetweenmodules
EncapsulatingcomplexcodeGeneratingdocumentation
InstallingJSDoc3inASP.NETCoreAddingcomments
DeploymentoptimizationSummary
10.TestingandDebuggingJavaScriptTestingtheJavaScriptcode
UnittestingWritingunittests
JasmineKarmaGrunt
DevelopingunittestusingJasmine,Karma,andGruntAddingpackagesAddingtheGruntfile
AddingKarmaspecificationsLoadnpmtaskRegistertask
SourceJavaScriptfileAddingunittestscriptfileRunningtesttaskImplementingModel-View-ViewModelusingKnockoutandRuntest
AddingtheKnockoutpackageAddingProductViewModelAddtheProductviewModifyingtestconfigurationModifyingtheproduct-testingscript
DebuggingJavaScriptDebuggingoptionsinVisualStudio2015
DebuggingfromVisualStudiowithInternetExplorerDebuggingfromVisualStudiowithGoogleChromeDeveloperTools
DebuggingoptionsinMicrosoftEdgeStandardbreakpointsConditionalbreakpointsTracepointsEvent
AddeventtracepointAddeventbreakpoints
XHRDebuggingTypeScriptDebuggerkeywordsupportedbyallbrowsers
SummaryIndex
JavaScriptfor.NETDevelopers
JavaScriptfor.NETDevelopersCopyright©2016PacktPublishing
Allrightsreserved.Nopartofthisbookmaybereproduced,storedinaretrievalsystem,ortransmittedinanyformorbyanymeans,withoutthepriorwrittenpermissionofthepublisher,exceptinthecaseofbriefquotationsembeddedincriticalarticlesorreviews.
Everyefforthasbeenmadeinthepreparationofthisbooktoensuretheaccuracyoftheinformationpresented.However,theinformationcontainedinthisbookissoldwithoutwarranty,eitherexpressorimplied.Neithertheauthor,norPacktPublishing,anditsdealersanddistributorswillbeheldliableforanydamagescausedorallegedtobecauseddirectlyorindirectlybythisbook.
PacktPublishinghasendeavoredtoprovidetrademarkinformationaboutallofthecompaniesandproductsmentionedinthisbookbytheappropriateuseofcapitals.However,PacktPublishingcannotguaranteetheaccuracyofthisinformation.
Firstpublished:July2016
Productionreference:1260716
PublishedbyPacktPublishingLtd.
LiveryPlace
35LiveryStreet
BirminghamB32PB,UK.
ISBN978-1-78588-646-1
www.packtpub.com
CreditsAuthor
OvaisMehboobAhmedKhan
Reviewer
NicholasSuter
CommissioningEditor
EdwardGordon
AcquisitionEditor
NitinDasan
ContentDevelopmentEditor
DeeptiThore
TechnicalEditors
PranilPathare
DeeptiTuscano
CopyEditor
VibhaShukla
ProjectCoordinator
ShwetaHBirwatkar
Proofreader
SafisEditing
Indexer
MariammalChettiyar
Graphics
DishaHaria
ProductionCoordinator
NileshMohite
CoverWork
NileshMohite
AbouttheAuthorOvaisMehboobAhmedKhanisaseasonedprogrammerandsolutionarchitectwithmorethan13yearsofsoftwaredevelopmentexperience.HehasworkedindifferentorganizationsacrossPakistan,theUSA,andtheMiddleEast.Currently,heisworkingforagovernmententitybasedinDubai,andalsoprovidesconsultancyservicestoaMicrosoftgoldpartnerfirmbasedinNewJersey.
HeisaMicrosoftMVPinVisualStudioandDevelopmentTechnologiesandspecializesmainlyinMicrosoft.NETandwebdevelopment.HehasauthorednumeroustechnicalarticlesondifferentwebsitessuchasMSDN,TechNet,DZone,andpersonalblogathttp://ovaismehboob.wordpress.com.
HeisanactivespeakerandgroupleaderofMicrosoftDevelopersUAEMeetup,MicrosoftTechnologyPractices,andDevelopersandEnterprisePracticesusergroups,andhaspresentedvarioustechnicalsessionsindifferenteventsandconferences.
Inshort,Ovaisisapassionatedeveloperwhoisalwaysinterestedinlearningnewtechnologies.Hecanbereachedat<[email protected]>andonTwitter,@ovaismehboob.
Iwouldliketothankmyfamilyforsupporting.Especiallymymother,wife,andbrother,whohavealwaysencouragedmeineverygoalofmylife.Myfather,mayherestinpeace,wouldhavebeenproudofmyachievements.
AbouttheReviewerNicholasSuterisa.NETsoftwarecraftsman,focusedonpatterns,practices,andqualitydevelopmentinaMicrosoftenvironment.HehasbeennominatedforMicrosoft.NETMVPsince2014.
HeworksforCellenza,aFrenchagileconsultingcompanybasedinParis,whereheleads,trains,andauditsteamsmoreontechnicalmattersthanteampractices.
Hewroteabookin2013onwebdevelopmentcalledVisualStudio2013,ConcevoiretdévelopperdesprojetsWeb,lesgéreravecTFS2013,(French)forENIEditions,andreviewedEntityFrameworkTutorial,SecondEdition,forPacktPublishingin2015.
www.PacktPub.com
eBooks,discountoffers,andmoreDidyouknowthatPacktofferseBookversionsofeverybookpublished,withPDFandePubfilesavailable?YoucanupgradetotheeBookversionatwww.PacktPub.comandasaprintbookcustomer,youareentitledtoadiscountontheeBookcopy.Getintouchwithusat<[email protected]>formoredetails.
Atwww.PacktPub.com,youcanalsoreadacollectionoffreetechnicalarticles,signupforarangeoffreenewslettersandreceiveexclusivediscountsandoffersonPacktbooksandeBooks.
https://www2.packtpub.com/books/subscription/packtlib
DoyouneedinstantsolutionstoyourITquestions?PacktLibisPackt'sonlinedigitalbooklibrary.Here,youcansearch,access,andreadPackt'sentirelibraryofbooks.
Whysubscribe?FullysearchableacrosseverybookpublishedbyPacktCopyandpaste,print,andbookmarkcontentOndemandandaccessibleviaawebbrowser
PrefaceThisisabookabouttheJavaScriptprogramminglanguage,andistargetedat.NETdeveloperswhowanttodevelopresponsivewebapplicationsusingpopularclient-sideJavaScript-basedframeworksandcreatearichuserexperience.ItisalsointendedforprogrammerswhohaveabasicknowledgeoftheJavaScriptprogramminglanguageandwantedtolearnsomecoreandadvancedconceptsfollowedbysomeindustry-widebestpracticesandpatternstostructureanddesignwebapplications.
ThisbookstartswiththebasicsofJavaScriptandhelpsthereadertogainknowledgeaboutthecoreconceptsandthenproceedstowardssomeadvancedtopics.ThereisachapterthatprimarilyfocusesonthejQuerylibrary,whichiswidelyusedthroughoutwebapplicationdevelopment,followedbyachapteronAjaxtechniquesthathelpdeveloperstounderstandhowasynchronousrequestscanbemade.ThisisfollowedbytheoptionstomakerequestseitherthroughtheplainvanillaJavaScriptXHRobjectorthroughthejQuerylibrary.ThereisalsoachapterthatdevelopsacompleteapplicationusingAngular2andASP.NETCore,andintroducesTypeScript,asupersetofJavaScriptthatsupportsthelatestandevolvingfeaturesofECMAScript2015.WewillalsoexploretheWindowsJavaScript(WinJS)librarytodevelopWindowsapplicationsusingJavaScriptandHTMLandusethislibrarytobringWindowsbehavior,look,andfeeltoASP.NETwebapplications.ThereisacompletechapteronNode.jsthathelpsdeveloperstolearnhowpowerfultheJavaScriptlanguageisontheserverside,followedbyachapteronusingJavaScriptinalarge-scaleproject.Finally,thisbookwillendwithachapterabouttestinganddebugginganddiscusswhattestingsuitesanddebuggingtechniquesaretheretotroubleshootandmakeanapplicationrobust.
Thisbookhassomeverydensetopicsthatrequirefullconcentration,henceisidealforsomeonehavingsomepriorknowledge.AllthechaptersarerelatedtoJavaScriptandworkaroundJavaScriptframeworksandlibrariestobuildrichwebapplications.Withthisbook,thereaderwillgettheend-to-endknowledgeabouttheJavaScriptlanguageanditsframeworksandlibrariesbuiltontopofit,followedbythetechniquestotestandtroubleshoottheJavaScriptcode.
WhatthisbookcoversChapter1,JavaScriptforModernWebApplications,focusesonthebasicconceptsofJavaScriptthatinvolvedeclarationofvariables,datatypes,implementingarrays,expressions,operators,andfunctions.WewillwritesimpleprogramsinJavaScriptusingVisualStudio2015,andseewhatthisIDEoffersforwritingJavaScriptprograms.WewillalsostudyhowJavaScriptcodecanbewrittenandcomparethe.NETruntimewiththeJavaScriptruntimetoclarifytheexecutioncycleofcode-compilationprocess.
Chapter2,AdvancedJavaScriptConcepts,coverstheadvancedconceptsofJavaScriptandgivesdevelopersaninsightintotheJavaScriptlanguage.ItwillshowtheextenttowhichtheJavaScriptlanguagecanbeusedasfarasfeaturesareconcerned.Wewilldiscussvariableshoistingandtheirscope,propertydescriptors,object-orientedprogramming,closures,typedarrays,andexceptionhandling.
Chapter3,UsingjQueryinASP.NET,discussesjQueryandhowtouseitinwebapplicationsdevelopedinASP.NETCore.WewilldiscusstheoptionsjQueryprovidesandtheadvantagesithaswhencomparingitwiththeplainvanillaJavaScriptformanipulatingDOMelements,attachingevents,andperformingcomplexoperations.
Chapter4,AjaxTechniques,discussesthetechniquesofmakingasynchronousrequestsknownasAjaxrequests.WewillexplorethecoreconceptsofusingtheXMLHttpRequest(XHR)objectandstudythebasicarchitectureofhowAjaxrequestisprocessedandtheeventsandmethodsitprovides.Ontheotherhand,wewillalsoexplorewhatthejQuerylibraryprovidesincomparisonwiththeplainXHRobject.
Chapter5,DevelopinganASP.NETApplicationUsingAngular2andWebAPI,teachesthebasicconceptsofTypeScriptandusesitwithAngular2.WewilldevelopasimpleapplicationinASP.NETCoreusingAngular2asafrontendclient-sideframework,WebAPIforbackendservices,andEntityFrameworkCorefordatabasepersistence.Atthetimeofwriting,Angular2wasinabetaversion,andwehaveusedthebetaversioninthischapter.WiththefuturereleasesofAngular2,therearechancesofhavingsomechangesintheframework,butthebasicconceptswillalmostbethesame.Forfutureupdates,youcanrefertohttp://angular.io/.
Chapter6,ExploringtheWinJSLibrary,explorestheMicrosoftdevelopedWinJSlibrary,whichisaJavaScriptlibrarytonotonlydevelopWindowsapplicationsusingJavaScriptandHTML,butalsouseitwithASP.NETandotherwebframeworks.Wewilldiscussthecoreconceptsofdefiningclasses,namespaces,derivingclasses,mixins,andpromises.Wewillalsolookintothedata-bindingtechniquesandhowtouseWindowscontrolsorspecificattributesinHTMLelementstochangethebehaviour,look,andfeelofthecontrol.Moreover,wewillendupusingtheWinRTAPItoaccessadevice'scamerainourwebapplicationanddiscusstheconceptsofaHostedappthroughwhichanywebapplicationcanbetransformedintoaWindowsapplicationusingaUniversalWindowtemplateinVisualStudio2015.
Chapter7,JavaScriptDesignPatterns,showsthatdesignpatternsprovideefficientsolutionstosoftwaredesign.Wewilldiscusssomeoftheindustry-widebestdesignpatternsspreadintocreational,structural,andbehavioralcategories.EachcategorywillbecoveringfourtypesofdesignpatternsthatcanbeusedandimplementedusingJavaScripttosolveaparticulardesignproblem.
Chapter8,Node.jsforASP.NETDevelopers,focusesonthebasicsofNode.jsandhowtouseittodevelopserver-sideapplicationsusingJavaScript.Inthischapter,wewilldiscussviewenginessuchasEJSandJadeandtheuseofcontrollersandservicestoimplementtheMVCpattern.Moreover,wewillendthischapterbyperformingsomeexamplesofaccessingaMicrosoftSQLServerdatabasetoperform,create,andretrieveoperationsonadatabase.
Chapter9,UsingJavaScriptforLarge-ScaleProjects,providesbestpracticesofusingJavaScriptforlarge-scaleapplications.WewilldiscusshowtostructureourJavaScript-basedprojectsbysplittingthemintomodulestoincreasethescalabilityandmaintainability.WewillseehoweffectivelywecanusetheMediatorpatterntoprovidecommunicationbetweenmodulesandthedocumentationframeworksthatincreasethemaintainabilityofyourJavaScriptcode.Finally,wewilldiscusshowtheapplicationcanbeoptimizedbycompressingandmergingJavaScriptfilesintoaminifiedversionandincreaseperformance.
Chapter10,TestingandDebuggingJavaScript,focusesonthetestinganddebuggingJavaScriptapplications.WewilldiscussoneofthemostpopulartestingsuitesofJavaScriptcodeknownasJasmine,anduseitwithKarmatorunthetestcases.Fordebugging,wewilldiscusssometipsandtechniquestodebugJavaScriptwithVisualStudioandwhatMicrosoftEdgeofferstomakedebuggingeasy.Intheend,wewillstudythebasicconceptsofhowMicrosoftEdgeenablesdebuggingforTypeScriptfilesandtheconfigurationneededtoachieveit.
WhatyouneedforthisbookThroughoutthebook,wewillbeusingVisualStudio2015topracticeexamples.Fortheserver-sidetechnology,wehaveusedASP.NETCoreforwebapplicationdevelopment,andusedJavaScriptontopofit.InChapter8,Node.jsforASP.NETDevelopers,weusedNode.jstoshowhowJavaScriptcanbeusedontheserverside.ForNode.js,wewillrequiresomeextensionsforVisualStudio2015tobeinstalled,andthedetailsarespecifiedinthechapter.
WhothisbookisforThisbookistargetedat.NETdeveloperswhohavesolidprogrammingexperienceinASP.NETCore.Throughoutthisbook,wehaveusedASP.NETCoreforwebdevelopmentandassumedthatdevelopershavethoroughknowledgeorworkingexperiencein.NETCoreandASP.NETCore.
ConventionsInthisbook,youwillfindanumberoftextstylesthatdistinguishbetweendifferentkindsofinformation.Herearesomeexamplesofthesestylesandanexplanationoftheirmeaning.
Codewordsintext,databasetablenames,foldernames,filenames,fileextensions,pathnames,dummyURLs,userinput,andTwitterhandlesareshownasfollows:"JavaScriptcanbeplacedinthe<head>or<body>sectionsofyourHTMLpage."
Ablockofcodeissetasfollows:
<html>
<head>
<script>
alert("Thisisasimpletext");
</script>
</head>
</html>
Anycommand-lineinputoroutputiswrittenasfollows:
dotnetefdatabaseupdate–verbose
Newtermsandimportantwordsareshowninbold.Wordsthatyouseeonthescreen,forexample,inmenusordialogboxes,appearinthetextlikethis:"Whenthepageloads,itwillshowthepop-upmessageandatextasThisisasimpletext."
Note
Warningsorimportantnotesappearinaboxlikethis.
Tip
Tipsandtricksappearlikethis.
ReaderfeedbackFeedbackfromourreadersisalwayswelcome.Letusknowwhatyouthinkaboutthisbook—whatyoulikedordisliked.Readerfeedbackisimportantforusasithelpsusdeveloptitlesthatyouwillreallygetthemostoutof.
Tosendusgeneralfeedback,simplye-mail<[email protected]>,andmentionthebook'stitleinthesubjectofyourmessage.
Ifthereisatopicthatyouhaveexpertiseinandyouareinterestedineitherwritingorcontributingtoabook,seeourauthorguideatwww.packtpub.com/authors.
CustomersupportNowthatyouaretheproudownerofaPacktbook,wehaveanumberofthingstohelpyoutogetthemostfromyourpurchase.
DownloadingtheexamplecodeYoucandownloadtheexamplecodefilesforthisbookfromyouraccountathttp://www.packtpub.com.Ifyoupurchasedthisbookelsewhere,youcanvisithttp://www.packtpub.com/supportandregistertohavethefilese-maileddirectlytoyou.
Youcandownloadthecodefilesbyfollowingthesesteps:
1. Loginorregistertoourwebsiteusingyoure-mailaddressandpassword.2. HoverthemousepointerontheSUPPORT tabatthetop.3. ClickonCodeDownloads&Errata.4. EnterthenameofthebookintheSearchbox.5. Selectthebookforwhichyou'relookingtodownloadthecodefiles.6. Choosefromthedrop-downmenuwhereyoupurchasedthisbookfrom.7. ClickonCodeDownload.
YoucanalsodownloadthecodefilesbyclickingontheCodeFilesbuttononthebook'swebpageatthePacktPublishingwebsite.Thispagecanbeaccessedbyenteringthebook'snameintheSearchbox.PleasenotethatyouneedtobeloggedintoyourPacktaccount.
Oncethefileisdownloaded,pleasemakesurethatyouunziporextractthefolderusingthelatestversionof:
WinRAR/7-ZipforWindowsZipeg/iZip/UnRarXforMac7-Zip/PeaZipforLinux
ThecodebundleforthebookisalsohostedonGitHubathttps://github.com/PacktPublishing/JavaScript-For-.NET-Developers.Wealsohaveothercodebundlesfromourrichcatalogofbooksandvideosavailableathttps://github.com/PacktPublishing/.Checkthemout!
DownloadingthecolorimagesofthisbookWealsoprovideyouwithaPDFfilethathascolorimagesofthescreenshots/diagramsusedinthisbook.Thecolorimageswillhelpyoubetterunderstandthechangesintheoutput.Youcandownloadthisfilefromhttps://www.packtpub.com/sites/default/files/downloads/JavaScriptForNETDevelopers_ColorImages.pdf
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.
PiracyPiracyofcopyrightedmaterialontheInternetisanongoingproblemacrossallmedia.AtPackt,wetaketheprotectionofourcopyrightandlicensesveryseriously.IfyoucomeacrossanyillegalcopiesofourworksinanyformontheInternet,pleaseprovideuswiththelocationaddressorwebsitenameimmediatelysothatwecanpursuearemedy.
Pleasecontactusat<[email protected]>withalinktothesuspectedpiratedmaterial.
Weappreciateyourhelpinprotectingourauthorsandourabilitytobringyouvaluablecontent.
QuestionsIfyouhaveaproblemwithanyaspectofthisbook,youcancontactusat<[email protected]>,andwewilldoourbesttoaddresstheproblem.
Chapter1.JavaScriptforModernWebApplicationsThegrowthinwebdevelopmentevolvedwitharapidpaceinrecentyears.Mostofthebusinessapplicationsthatdevelopedonadesktopplatformarenowshiftedtothewebplatform,andthereasonistheeaseofaccessandcontinuousadditionofrichcapabilitiesonthewebplatform.Typically,anywebapplicationthatprovidesthecharacteristicsofthedesktopapplicationsisconsideredasrichwebapplication.Thus,itinvolvesextensiveuseofJavaScriptanditsframeworksandlibraries.
JavaScriptplaysanimportantroleindevelopingrichapplicationsandallowsdeveloperstodolessserver-sidepost-backsandcallserver-sidefunctionsthroughajaxifiedrequests.Notonlythis,butnowmanycompaniesandcommunitiesaredevelopinggoodframeworkssuchasAngular,Knockout,ReactJS,andsoontobringstate-of-the-artandgroundbreakingcapabilities.MicrosofthasalsoreleasedtheWinJSlibrarytoaccessmobilenativedevicefeaturessuchascamera,storage,andsoonfromawebapplicationrunningonmobilebrowsers.myNFCisalsoagreatJavaScriptlibrarythatallowsdeveloperstocreateapplicationsforsmartphones.
ImportanceofJavaScriptAlltheclient-sideframeworksarebasedonJavaScript.BeinganASP.NETdeveloper,weshouldhavesolidconceptsofJavaScriptbeforeusingorintegratingtheminourapplications.JavaScriptistheclient-sidescriptinglanguageandoneofthemostpopularprogramminglanguagesofalltimesthatrunontopofabrowser.Whenworkingonawebdevelopmentproject,thislanguageservesyouinmanybetterwaystomakeuserinterface(UI)responsive.WithJavaScript,youcanmanipulateHTMLpageDocumentObjectModel(DOM)elements,callserver-sidecodethroughajaxifiedrequestsandbringnewrichexperiencetoyourcustomers.TherearemanyinnovationsbeingdoneatthecoreJavaScriptlibrary,anddifferentframeworksandvariouslibrarieshavebeendeveloped.
WhatisJavaScript?JavaScriptisaprogramminglanguagecreatedin1995byBrendenEich.Initially,itwasonlysupportedbyNetscapeBrowser,butlatertheydecidedtoreleaseastandardknownasECMAspecificationtoletotherbrowsersimplementandprovideenginestoexecuteJavaScriptontheirbrowsers.Thereasonforprovidingthestandardistohavethecompletespecificationdetailsforthepartytofollowandprovideconsistentbehavior.
Earlieritwasonlytargetedtoexecuteonbrowsersandperformclient-sideoperationsthatworkwithHTMLpagesandprovidefeaturessuchasmanipulatingDOMelementsanddefiningeventhandlersandotherfunctionalities.Later,andinrecentyears,ithasbecomeapowerfullanguageandnotonlyboundedtotheclient-sideoperations.WithNode.js,wecanuseJavaScriptonserversideandtherearevariousmodulesandpluginsprovidedbyNodetoperformI/Ooperations,server-sideevents,andmore.
ComparingruntimesAsthisbookistargetedfor.NETdevelopers,let'scomparetheJavaScriptruntimewith.NETruntime.Thereareafewthingsincommon,butthebasicruntimeisdifferent.
In.NET,CommonLanguageRuntime(CLR)doesthejust-in-time(JIT)compilationonthecodethatisrunningandprovidesmemorymanagement.JITcompilationisdoneontheIntermediateLanguage(IL)codethatisgeneratedonceyoubuildyourproject.
IntheJavaScriptworld,browserengineistheruntimefortheJavaScriptlanguage.EverybrowserinterpretsJavaScriptinitsownway,butfollowstheECMAscriptingstandards.Differentbrowsershavedifferentimplementations,forexample,MicrosoftEdgeusesChakraengine,ChromeusesV8,andFirefoxhasMonkeyengines.Initially,JavaScriptwasimplementedasaninterpretedlanguage,butfewmodernbrowsersnowperformJITcompilation.Everyengineprovidesasetofservicessuchasmemorymanagement,compilation,andprocessing.
Thefollowingdiagramshowsthecomparisonbetweenboththearchitectures:
TheJavaScriptparserparsesandtokenizestheJavaScriptcodeintoasyntaxtree.Allthebrowsers,exceptGoogleV8,parsethesyntaxtreeandgenerateabytecodethatfinallyconvertsintoamachinecodethroughJITcompilation.Ontheotherhand,GoogleV8engineparsesthesyntaxtreeandinsteadofgeneratingabytecodefirst,itdirectlygeneratesthemachinecode.
The.NETsourcecodeiscompiledbyitsownlanguagecompiler,suchasC#orVB.NET
compilerandpassesthroughtheseveralstagesofthecompilerpipelinetogenerateanILcode.ThisILcodeisthenreadbytheJITcompilerthatgeneratesthenativemachinecode.
SettingupyourenvironmentBeforegoingthroughthisbook,let'ssetupyourenvironment.TherearemanyrenownededitorsavailableinthemarkettocreateJavaScriptprojectssuchasSublimeText,KomodoIDE,NetBeans,Eclipse,andmore,butwewilluseVisualStudio2015thatcameupwithsomegoodimprovements,helpingdeveloperstoworkonJavaScriptinabetterwaythanbefore.
Toproceed,let'sdownloadandinstallVisualStudio2015.YoucandownloadtheVisualStudio2015communityeditionfromhttps://www.visualstudio.com/,it'safreeversionandprovidescertainimprovementsasdescribedinthefollowingsection.
NeweditingexperienceofJavaScriptinVisualStudio2015IDEThenewVisualStudio2015IDEprovidesmanyrichfeaturesfordevelopingwebapplicationsandvarioustemplatesareavailabletocreateprojectsondifferentframeworksandapplicationmodels.TheearlierversionalreadysupportedIntelliSense,colorization,andformattingbutthenewVisualStudio2015IDEhassomemoreimprovementsthatareasfollows:
AddedsupportfortheECMAScript6scriptinglanguage,whichisformallyknownasES2015.WiththenewES2015,manyfeatureshavebeenadded,youcannowdefineclasses,lambdas,spreadoperator,andproxyobjects.So,withVisualStudio2015,youcangetallIntelliSenseusingthesefeaturesinyourJavaScriptcode.SupportforpopularJavaScriptclient-sideframeworkssuchasAngular,ReactJS,andsoon.DocumentationcommentsthathelpyouaddcommentstoyourJavaScriptmethodsandshowthedescriptionwhenyouusethem:
IntelliSensefornewJavaScriptAPIssuchastoucheventandWebAudioAPI.Youcanusetokenssuchas//TODO,//HACK,and//UNDONE,anditgivesyoualistingintheTaskListwindowthathelpstotracetheto-doitems:
WithJavaScriptfiles,VisualStudio2015providesthesamenavigationbarweusedtoseewhenwritingclassesinany.NETlanguage.SelectingandnavigatingtodifferentmethodsofJavaScriptisfareasierwiththisfeature:
ProgramminginJavaScriptJavaScriptisoneofthemostpowerfullanguagesthatplaysavitalroleinanywebdevelopmentprojectanddeliversclient-sidesupportandrichfunctionality.Inthissection,wewilldiscussthecoreconceptsofwritingprogramsinJavaScriptandusetheminwebapplications.
CorefundamentalsofJavaScriptLikeanyotherprogramminglanguage,JavaScriptalsohasstatements,expressions,operators,andspecificsyntaxtowriteandrunprograms.Wewillgothroughthefollowingtopicsinthissection:
AddingJavaScripttoanHTMLpageStatementsLiteralsandvariablesDatatypesExpressionsandoperators
AddingJavaScripttoanHTMLpage
EverymodernbrowserhasaJavaScriptenginethatcompilesyourJavaScriptdefinedonthepage.JavaScriptcanbeplacedinthe<head>or<body>sectionsofyourHTMLpage.Anystatementthatisdefinedunder<script></script>tagswillbeconsideredaJavaScriptstatementandwillbeprocessedandunderstoodbythebrowser'sengine.
ThefollowingcodesnippetshowsasimpleHTMLpagecontainingthe<script></script>tagsdefinedwithinthe<head></head>section:
<html>
<head>
<script>
alert("Thisisasimpletext");
</script>
</head>
</html>
Whenthepageloads,itwillshowthepop-upmessageandatextasThisisasimpletext.Thebrowser'sJavaScriptengineexecutesanyscriptthatisdefinedunderthe<script>tagandrunsthestatementsdefinedwithinthisblock.Anystatementthatisdefineddirectlyunderthescriptingtagisexecutedeverytimethepageisloaded.
Similarly,wecanalsodefinetheJavaScriptwithinthe<body>sectionoftheHTMLpage:
<html>
<body>
<script>
alert("helloworld");
</script>
</body>
</html>
Tip
Itisagoodideatoplacescriptsatthebottomofthepagebecausethecompilationcanslowdownthepageloading.
Normally,ineveryproject,irrespectiveoftheprojectsize,separatingthe<script>sectionfromHTMLmakesthecodelookcleanerandeasytomaintain.JavaScriptfileextensionsarenamed.jsandyoucanalsocreatethesefilesseparatelyinsomescriptsfolderandreferencetheminourHTMLpage.
InVisualStudio,youcaneasilycreateaJavaScriptfileusingtheAdd|JavaScriptFileoptionasshowninthefollowing:
Oncethefileiscreated,wecandirectlywritetheJavaScriptsyntaxwithoutany<script></script>tags.JavaScriptfilescanbereferencedinyourHTMLpageusingthesrcattributeof<script></script>tags.Herewereferencedtest.jsintheHTMLpage:
<scriptsrc="~/test.js">
</script>
Placingthe<script>tageitherinthe<head>orinthe<body>sectiondependsonthepage.IfyourpagereferencingsomelargeJavaScriptfilestakesalongtimetoload,itisbettertodefinethemattheendofthe<body>section.Thisisabetterapproach,sowhenthebrowserstartsparsingyourpage,itisnotstuckdownloadingyourscriptsanddelayingtherendering.Ontheotherhand,wecandefineJavaScriptfilesinthe<head>sectiononlyiftheydonotimpacttheperformanceorpagelifecycle.Scriptsdefinedatthebottomgetparsedwhenthewholepageloads.Therearealsoafewattributessuchasasyncanddeferthatwecanusewithinthe<script>tagandmostofthebrowserssupportthis.
Thefollowingisanexampleshowingtheuseofasyncinthe<script>tag:
<scriptsrc="~/test1.js"async></script>
<scriptsrc="~/test2.js"async></script>
Scriptsdefinedwithasyncareexecutedasynchronouslywithoutblockingthebrowsertoloadthepage.However,ifmultiplescriptsarethere,theneachscriptwillbeexecutedasynchronouslyandatthesametime.Thismayleadtothepossibilityofcompletingthesecondscriptbeforethefirstonegetscompletedandmightthrowsomeerrorsifoneisdependentontheother.Forexample,whenworkingwithsomeclient-sideframeworks,such
asAngularframework,JavaScriptcodethatisusingAngularcomponentsisdependentonAngularJSlibrary,andinthiscase,ifourcustomJSfilesareloadedbeforetheAngularJSlibraryonwhichtheyaredependent,theywillthrowanexception.
Toovercomethisscenario,wecanusedefertoexecutescriptsinasequence.Wecanusedeferasfollows:
<scriptsrc="~/test1.js"defer></script>
<scriptsrc="~/test2.js"defer></script>
ThebasicdifferencebetweenasyncanddeferisthatasyncdownloadsthefileduringHTMLparsingandpausestheHTMLparsertoexecuteituntilitiscompletelydownloaded,whereasdeferdownloadsthefileduringtheHTMLparsingandexecutesitaftertheHTMLparseriscompleted.
StatementsinJavaScript
Statementsarethecollectionofwords,expressions,andoperatorstoperformaspecifictask.Likeotherprogramminglanguages,statementsinJavaScriptcouldalsobeassigningavaluetothevariable,performingarithmeticoperations,implementingconditionallogic,iteratingthroughcollection,andsoon.
Forexample:
vara;//variabledeclaration
a=5;//valueassignment
a=5*b;//valueassignment
a++;//equivalenttoa=a+1
a--;//equivalenttoa=a-1
varmethod=function(){…}//declarefunction
alert("HelloWorld")//callingbuilt-infunction
if(…){…}else{…}
for(…){…}
while(…){…}
However,youcanusesemicolonswiththedowhileloop:
do{…}while(…);
functionstatement
function(arg){//todo}
Tip
Ifmultiplestatementsaredefinedinthesameline,theyshouldbeseparatedbyasemicolon,otherwisetheywillbetreatedasasinglestatement.Ondifferentlines,asemicolonisnotmandatorybutagoodpracticetouse.
Literalsandvariables
TherearetwotypesofvaluesinJavaScript:literalsorfixedvaluesandvariables.
Literalscouldbenumber,string,ordateobjects.
Forexample:
Numbers
22.30
26
Strings
"John"
"10/Jan/2015"
Variablesareusedtostorevalues.InJavaScript,wecandefinevariablesusingthevarkeyword.JavaScriptisnotatype-safelanguageandthetypeisidentifiedwhenthevalueisassigned.
Forexample:
varx=6;
varx="Samplevalue";
Datatypes
Everyprogramminglanguagehascertaindatatypesavailabletoholdspecificdata.Forexample,inC#,wecanuseStringtoholdstringvalues,inttohold32-bitintegervalue,DateTimetoholdvalueinthedateandtimeformat,andsoon.JavaScriptdoesnotprovidestrongdatatypessuchasC#andotherprogramminglanguages,andit'salooselytypedlanguage.AsperthelatestECMA6standard,JavaScriptprovidessixprimitivedatatypesandanobject.Allprimitivedatatypesareimmutable,thismeansthatassigninganewvaluewillbeallocatedintoaseparatememory.Objectismutableanditsvaluescanbechanged.
Theprimitivetypesareasfollows:
Boolean:Thisholdsthelogicalvaluetrueorfalse.Null:Thisholdsthenullvalue.Undefined:Thisisavariablethatdoesnotassignavalueandhasvalueasundefined.Number:Thisholdsnumericvalues.Thesizeofthenumbertypeisdouble-precision64bitinwhichthenumber(fraction)isstoredfrom0to51bits,theexponentin11bitsfrom52to62,andsignin1bit63.String:Thisholdsanykindoftextualvalue.
Complextypesaretermedasobject.InJavaScript,theobjectisformulatedinaJSONformat.
ArrayinJavaScript
Arrayisusedtostorecollectionsofdata.YoucansimplydefineanarrayinJavaScriptasshowninthefollowing:
varbrowsers=["MicrosoftEdge","GoogleChrome","MozillaFirefox","Safari"];
Youcanaccessthemthroughthearrayindex.Theindexstartsfrom0tillthenumberofitemsinthearray.
Wecanaccessthearrayitemsasfollows:
vara=browsers[0];//returnsMicrosoftEdge
varb=browsers[1];//returnsGoogleChrome
varc=browsers[3];//returnsSafari
Inordertogetthetotalnumberofitemsinanarray,youcanusethelengthproperty:
vartotalItems=browsers.length;
Thefollowingisalistofsomeofthemostcommonlyusedmethods:
Method Description
indexOf()Thisreturnsthefirstindexofanelementavailablewithinthearrayequaltothespecificvalue,returns-1ifnotfound.
lastIndexOf()Thisreturnsthelastindexofanelementavailablewithinanarrayequaltothespecifiedvalue,returns-1ifnotfound.
pop() Thisdeletesthelastelementfromanarrayandreturnsthatelement.
push() Thisaddsoneelementtoanarrayandreturnsthelengthofanarray.
reverse()Thisreversestheorderofelementsinanarray.Thefirstelementbecomesthelastandlastonebecomesthefirst.
shift() Thisdeletesthefirstelementandreturnsthatelement.
splice() Thisisusedtoaddorremoveelementsfromanarray.
toString() Thisreturnsalltheelementsinastringrepresentation.
unshift() Thisaddselementstothefrontofanarrayandreturnsthenewlength.
Tip
Downloadingtheexamplecode
DetailedstepstodownloadthecodebundlearementionedinthePrefaceofthisbook.Pleasehavealook.
ThecodebundleforthebookisalsohostedonGitHubathttps://github.com/PacktPublishing/JavaScript-For-.NET-Developers.Wealsohaveothercodebundlesfromourrichcatalogofbooksandvideosavailableathttps://github.com/PacktPublishing/.Checkthemout!
WhatisJSON?
JavaScriptObjectNotation(JSON)isalightweight,interchangeableformatfordefiningobjectsinJavaScript.AnykindofobjectcanbedefinedthroughJSONanditisusedtobuilduniversaldatastructures.Whetherit'sasimpleobject,arrays,nestedarrays,orcomplexobject,eachcanhandleintheJSONformat.
SimpleobjectsinJSON
Thefollowingcodesnippetshowsthepersonobjectthathasthreeproperties,namelyname,email,andphone:
varperson={
"name":"JohnMartin",
"email":[email protected],
"phone":"201892882"
}
Wecanaccesstheseobjectpropertiesasfollows:
person.name;
person.email;
person.phone;
DeclaringarraysinJSON
ThefollowingcodesnippetshowsthewayofdeclaringarraysinJSON:
varpersons=
[{
"name":"John",
"email":"[email protected]",
"phone":"201832882"
},
{
"name":"Steve",
"email":"[email protected]",
"phone":"201832882"
},
{
"name":"Smith",
"email":"[email protected]",
"phone":"201832882"
}]
Accordingtotheprecedingdeclarationofanarray,itcanbeaccessedasfollows:
//returnsnameofthefirstiteminthecollectioni.e.John
Persons[0].name
Persons[0].email
//returnsnameoftheseconditeminthecollectioni.e.Steve
Persons[1].name
NestingdatainJSON
TheJSONformateasilyhandlesnestedarrays.Let'slookatthecomplexobjectscontaininganemployeeobjectthatcontainstheExperiencesarraywiththenestedarraytoholdprojects,andeachprojecthasanestedarraytoholdtechnologiesusedineachproject:
varemployee=
{
"ID":"00333",
"Name":"Scott",
"DateOfJoining":"01/Jan/2010",
"Experiences":[
{
"companyName":"ABC",
"from":"Nov2008",
"to":"Oct2009",
"projects":[
{
"title":"SharepointMigration",
"noOfTeamMembers":5,
"technologyUsed":[{"name":"SharePointServer"},{"name":"C#"},
{"name":"SQLServer"}]
},
{
"title":"MessagingGateway",
"noOfTeamMembers":5,
"technologyUsed":[{"name":"ASP.NET"},{"name":"C#"},{"name":"SQL
Server"}]
}
]
},
{
"companyName":"XYZ",
"from":"Nov2009",
"to":"Oct2015",
"projects":[
{
"title":"ERPSystem",
"noOfTeamMembers":5,
"technologyUsed":[{"name":"ASP.NET"},{"name":"C#"},{"name":"SQL
Server"}]
},
{
"title":"HealthcareSystem",
"noOfTeamMembers":4,
"technologyUsed":[{"name":"ASP.NET"},{"name":"C#"},{"name":"SQL
Server"}]
}
]
}
]
}
Intheprecedingscript,thearrayhasanemployeeobjectcontainingsomepropertiessuchasID,name,anddateofjoining,andthenoneexperiencespropertythatholdsanarrayofexperiencesandeachexperienceholdstheprojectsthattheemployeedidinaparticularworkplace.
Conversionsindatatypes
AsthedatatypesinJavaScriptaredynamicinnatureanddatatypeisdeterminedbasedonthevalueassignment,JavaScriptdoesnotthrowanyexceptionontypeconversionanddealsinseveralwaysasfollows.
Forexample,thefollowingistheJavaScriptcodesnippetshowingtheassignmentofdifferentexpressions.
Firstassignthestringtotheresvariable:
varres="HelloWorld";
Thenassignnumerictothesameresvariable:
res=2;
Finally,concatenatestring3totheresvariablethatholdsthefollowingnumeric,butduetothehigherprecedenceofnumericalvalue,theresultantvaluebecomes5:
varresult=res+"3"
So,nomatterwhatwasthetypeofthevariablefirstassignedtoit,itwillchangeitstypebasedontheassignmentanddynamicallyhandletheconversions.
ElementsofJavaScriptHerearesomeoftheimportantelementsofJavaScriptthatareessentialtolearnbeforewestartprogramminginJavaScript.
ConstantsinJavaScript
ConstantsinJavaScriptcanbedefinedwithaconstkeyword.Constantsaretheimmutablevaluesthatareknownatcompiletime,andvaluesdonotchangethroughoutthelifecycleoftheprogram.
ThefollowingistheJavaScriptcodeshowingtheassignmentofaconstantvariable.Whenusingconst,varisnotrequiredandyoucandeclareconstantvalueswithonlytheconstkeyword:
constpi=3.42
Comments
Commentscanbeaddedwith//and/**/.Tocommentasingleline,youcanuse//,otherwise/**/forablockofcode.
ThefollowingistheJavaScriptcodeshowingthewayofcommentingasinglelineorblockofcode:
<scripttype="text/javascript">
functionshowInformation(){
//varspObj=window.document.getElementById("spInfo");
spObj.innerHTML=
"AvailableHeight:"+screen.availHeight+"<br>"+
/*"AvailableWidth:"+screen.availWidth+"<br>"+
"Height:"+screen.height+"<br>"+*/
"Width:"+screen.width+"<br>"
}
</script>
Casesensitivity
JavaScriptisacase-sensitivelanguageanditfollowsthePascalnamingconventiontodefinevariablesandmethods.
Forexample,ifthemethodnameisdoWork(),itcanonlybeaccessedbycallingitwiththeexactcase,andcallingeitherDoWork()orDowork()willnotworkandthrowexception.
Characterset
JavaScriptisbasedonaUnicodecharactersetandfollowstheUnicodeStandard.
Note
WhatistheUnicodeStandard?
Itisaworldwidecodingstandardthatmostlanguagesuse.C#andVB.NETfollowthesameUnicodeStandard.Itprovidesauniquenumberforeverycharacter,forexample,A=41,a=61,andsoon.
ThecurrentversionoftheUnicodeStandardisUnicode8.0.0andthedocumentationcanbelocatedathttp://www.unicode.org/versions/Unicode8.0.0/.
ExpressionsExpressioncanberecognizedasthestatementofcodethatassignssomevaluetothevariable.Expressionsarecategorizedintotwotypes.
Thefirsttypeofexpressioncanbetermedassimpleexpressionsthatassignsavaluetothevariable:
varx=2;
Theprecedingexampledenotesthesimpleexpressionofassigningnumericvalue2toanxvariable.
Thesecondtypeofexpressioncanbetermedasanyarithmeticorstringoperationtothevaluesontherightandassigningthemtoanyvariable.Thesetypeofexpressionsperformtheoperationfirstbeforeassigningvaluetothevariable:
varx=2+3
varx="Hello"+"World";
Thisisanexampleofthesecondtypeofexpressionthataddstwonumbersandassignstheresultantvaluetothexvariable.SamegoesforthesecondstatementthatperformsthestringconcatenationoperationandassignstheHelloWorldvaluetothexvariable.
Thethiskeyword
JustlikeC#andotherobject-orientedlanguages,JavaScripthasobjectsandtherearecertainwaystodefineclasses,functions,andsoonthatwewillstudylaterinthischapter.JustlikeC#,inJavaScript,wecanaccesstheobjectanditspropertiesthroughthethiskeyword.Let'stakealookatsomeexamplesshowingthescopeofthethiskeywordinJavaScript.
Thefollowingisacustomerobjectthatcontainsafewpropertiesandtheutilizationofthethiskeyword:
varcustomer=
{
name:"JohnMarting",
email:"[email protected]",
mobile:"109293988844",
show:function(){
alert("Name:"+this.name+"Email:"+this.email+"Mobile:"+
this.mobile);
}
}
Intheprecedingexample,wehavedefinedaJavaScriptobjectthatcontainsthreepropertiesandafunction.Toaccesstheseproperties,wecanusethethiskeywordjustlikeC#.However,wecanalsoaccessthepropertiesusingthecustomervariable,asshowninthefollowing:
varcustomer=
{
name:"JohnMarting",
email:"[email protected]",
mobile:"109293988844",
show:function(){
alert("Name:"+customer.name+"Email:"+customer.email+"Mobile:"+
customer.mobile);
}
}
Thescopeofthethiskeywordislimitedwithintheboundaryofanobject.Whereas,thecustomervariableintheprecedingexamplecouldbedefinedsomewhereelseonthepageandmayleadtoanincorrectbehavior.Itisabetterapproachtousethethiskeywordwhereverpossibleandavoidusingobjectvariablesdirectly.
Allvariablesandfunctionsdefineddirectlyunderthe<script>tagaretermedasglobalvariablesandfunctions.Wecanalsoaccessthemthroughthethiskeyword.Inthiscase,thiswillbereferredastheglobalwindowobjectandnotthechild,thatis,thecustomerobjectwehaveusedinthepreviousexample:
<scripttype="text/javascript">
varname="";
functionShowMessage(){
alert(this.name);
}
</script>
Unlikethis,youcanalsoreferthevariablesandfunctionsthroughthewindowkeyword.Thefollowingcodesnippetwillshowthenameofthewindowinthedialogbox:
alert(window.name);
Let'stakealookatthecompleteexample,wherewehaveglobalvariablesdefined,aswellaschildobjects,andthescopeofthiswillbedeterminedbasedonthecontextofitscall:
<scripttype="text/javascript">
varname="ScottWatson";
varcustomer=
{
name:"JohnMarting",
email:"[email protected]",
mobile:"109293988844",
show:function(){
alert("Name:"+this.name+"Email:"+this.email+"Mobile:"+
this.mobile);
}
}
functionShowMessage(){
alert("Globalnameis"+this.name);
alert("Customerinfois"+customer.show());
}
</script>
Inthisprecedingexample,wewillgettwoJavaScriptalertmessages.ThefirstalertwilldisplayScottWatson,whichisdefinedglobally,andthesecondpopupshowsthecustomername,e-mailaddress,andmobilenumber.Hence,wecanusethisintwoplaces,butthescopeisdeterminedbasedonthecontextfromwhereitiscallingfrom.
SequenceofcodeexecutioninJavaScript
WhenprogramminginJavaScript,wehavetokeepthesequenceofdefiningthingsbeforetheygetcalled.Consideringtheprecedingexample,ifwedefinethecustomerobjectaftertheShowMessage()method,itwillnotberecognizedandnothingwillbedisplayed.
Usingthethiskeywordonacallingmethod
Let'stakealookatthesampleHTMLpagethathasaJavaScriptfunctionnamedMultiplyandtakestwoparameters:objandval.Thismethodwillbecalledwhentheuserentersanyinputintothetextboxanditwillpassthereferenceofthetextboxcontrolatthefirstparameter.Thiscanbepassedthroughthethiskeyword:
<html>
<head>
<scripttype="text/javascript">
functionMultiply(obj,val){
alert(obj.value*val);
}
</script>
</head>
<body>
<inputtype="text"onchange="Multiply(this,2);"/>
</body>
</html>
Thefunctionstatementandexpression
ThefunctionstatementsareawayofdefiningmethodsinJavaScript.Eachfunctionhasasignature,containingthenameandparameterspassedin.FunctionscanbedeclaredinmanywaysinJavaScript.Forexample,thefollowingisthesampleGetPerson(id)functionthatreturnsthepersonobjectbasedontheIDpassedasaparameter.ThisisthenormalwayofdeclaringfunctioninJavaScript:
<script>
functionGetPerson(id){
returnservice.GetPerson(id);
}
</script>
Thefunctionreturntypeiscomputedatruntimeandnotpartofthefunctionsignature.
Returningvaluesisnotmandatoryandyoucankeepfunctionswithoutreturninganyvalues.
Ontheotherhand,anonymousfunctionsdonothaveanynameandtheycaneitherbepassedasanargumenttootherfunctionsordefinedwithoutafunctionname.Thefollowingaretheexamplesofanonymousfunctions:
varshowMessage=function(message){
console.log(message);
}
showMessage("HelloWorld");
Anotherexampleofdefininganonymousfunctionandpassingitasaparameterisasfollows:
functionmessageLogger(message,logMessage){
logMessage();
}
functionconsoleMessage(){
alert("HelloWorld");
}
messageLogger(consoleMessage());
Thefunctionexpressionisequivalenttofunction,buttheonlydifferenceisthatitshouldnotstartwiththefunctionname.
Classstatementandexpression
WithECMAScript6,wecancreateclassesinJavaScript.Justlikeotherprogramminglanguages,wecancreateaclassusingtheclasskeyword.Withthis,wecanwritecleanercodethandevelopingfunctionsthatwererepresentedasclassesintheearlierversionofECMAScript.
Let'stakealookattheRectangleclassthatcalculatesanarea:
<script>
classRectangle{
constructor(height,width){
this.height=height;
this.width=width;
}
getArea(){
returnthis.calcArea();
}
calcArea(){
alert("Areais"+this.height*this.width);
}
}
</script>
Eachclassshouldhaveoneconstructorandgiveanerrorifmultipleconstructorsarespecified.Classexpressionisanotherwayofdefiningclasses.Justlikeanonymousfunctions,wecandefineclassesinasimilarway.
Let'stakealookattheexampleofthesameclassdefinedearlier:
<script>
varRectangle=class{
constructor(height,width){
this.height=height;
this.width=width;
}
getArea(){
returnthis.calcArea();
}
calcArea(){
alert("Areais"+this.height*this.width);
}
}
</script>
Thenextchapterwillcovermoredetailsaboutclassesandtheattributesandkeywordsavailabletostructurethem.
Groupingoperator
Foranyarithmeticexpression,JavaScriptusestheBODMASrule.Theprecedencewillbegiventobracketsthenmultiplication,division,addition,andsubtraction.Thegroupingoperatorisusedtogivehigherprecedencetotheexpressionifanyofthememberintheexpressionhavehigherprecedencebydefault.
Forexample:
vara=1;
varb=2;
varc=3;
varx=a+b*c;
Theresultantxwillbe7asmultiplicationgetsthehigherprecedence.However,whatifweneedtoperformadditionfirst?
Wecanusegroupingoperatorasfollowsthatgivestheresult9:
varx=(a+b)*c;
new
InthesamewayasC#,thenewkeywordisusedtoinstantiateanyobjectinJavaScript.Inordertocreateaninstanceofanyuser-definedorpredefinedtype,usethenewkeyword:
varobj=newobjectType();
super
Thesuperkeywordisusedtocallmethodsoftheparentobject.InC#,weusethebasekeywordtocallthebaseclassmethodorproperties.InJavaScript,wecanuseitasfollows:
super.functionOnParent();
OperatorsOperatorsaretheobjectusedtomanipulatevaluesofanoperand.Forexample,1+2resultsin3,where1and2areoperandsand+isanoperator.InJavaScript,wecanusealmostalltheoperatorstoconcatenatestrings,doarithmeticoperations,andsoon.Inthissection,let'sseewhattypeofoperatorswecanusewhenwritingprogramsinJavaScriptlanguage.
Wewilldiscussthefollowingoperatorsinthissection:
AssignmentoperatorsArithmeticoperatorsUnaryoperatorsComparisonoperatorsLogicaloperatorsBitwiseoperatorsBitwiseshiftoperatorsThetypeofoperatorThevoidoperatorThedeleteoperatorMiscellaneousoperators
Assignmentoperators
Assignmentoperatorisrepresentedas(=)andtheassignmentisdonefromrighttoleft.
Forexample,x=ymeansthatthevalueofyisassignedtox.
Arithmeticoperators
Thefollowingisalistofarithmeticoperatorsyoucanusetoperformaddition,subtraction,division,andmultiplicationandusethemwiththeassignmentstatements:
Name Operator Meaning
Addition x+y Thevalueofxisaddedtoy
Subtraction x–y Thevalueofyissubtractedfromx
Division x/y Thevalueofxisdividedbyy
Multiplication x*y Thevalueofxismultipliedtoy
Remainder x%y Thevalueofxisdividedbyyandtheremainderisreturned
Additionassignment x+=yx=x+y
thatis,thevalueofxandywillbeaddedandassignedtox
Subtractionassignment
x-=y
x=x–y
thatis,thevalueofxandywillbesubtractedandassignedtox
Multiplicationassignment
x*=y
x=x*y
thatis,thevalueofxandywillbemultipliedandassignedtox
Divisionassignment x/=yx=x/y
thatis,thevalueofxwillbedividedbyyandassignedtox
Remainderassignment
x%=y
x=x%y
thatis,thevalueofxwillbedividedbyyandtheremainderwillbeassignedtox
Exponentiationassignment
x**=y
x=x**y
thatis,thevalueofxwillbeexponentiallymultipliedtwicetoyandassignedtox
Unaryoperators
Unaryoperatorworkswithonlyoneoperand.Itcanbeusedforincrement,decrement,inversion,andsoon:
Name Operator Meaning
Incrementoperator x++ Thevalueofxwillbeincrementedby1
Decrementoperator x-- Thevalueofxwillbedecrementedby1
Logicalcomplementoperator !(x) Thisinvertsthevalueofx
Comparisonoperators
ComparisonoperatorisusedtocompareoperandsthatreturnsaBooleantruevalueifthecomparisonistrue.Operandscanbeofanydatatype,haveanassociativityfromlefttoright,andperformdynamicconversioniftheyareofdifferenttypes.
Forexample,ifthevalueofxis2andyis"2".Here,ydenotesthatit'sastring,butincomparison,itwillreturntrue:
x==y//returnstrue
SimilartoC#oranyprogramminglanguage,JavaScriptsupportsalltheseoperators,suchasequal(==),notequal(!=),greaterthan(>),andlessthan(<),butduetothedynamictypebinding,italsoprovidestwooperatorssuchasstrictequal(===)andstrictnotequal(!===)toconfirmifthetypeisalsothesameifthevalueissameandviceversa.
Wewillnowhavealookatstrictoperators.Therearethefollowingtwotypesofstrictoperators:
StrictequaloperatorStrictnotequaloperator
Strictequaloperator
Intheprevioussection,wediscussedthatJavaScriptprovidesdynamicbindingandifthevalueoftwodifferentdatatypes,supposenumberandstring,arethesame,itwillreturntrueoncomparison.Forexample,ifxis1andyis"1",itwillreturntrue.Now,whatifwehavetodothecomparisonworkonlyifthetypeisalsothesame?Herecomesthestrictequaloperatorthatdoesnotonlycheckthevalue,butalsomatchthetypesofboththeoperands.
Thestrictequaloperatorcanberepresentedas===.
Forexample,ifx=1andy="1"andthecomparisonisdonelike(x===y),itwillreturnfalseasxrepresentsnumberandyrepresentsstring.
Strictnotequaloperator
Contrarytothestrictequaloperator,ifwewanttocomparethevaluesoftwooperandsofsametype,wecanusethestrictnotequaloperator.
Thestrictnotequaloperatorcanberepresentedas!===.
Ifx=1andy=2andthecomparisonisdonelike(x!==y),itwillreturntrue.Thisisbecausethetypesarethesameandthevaluesaredifferent.
Logicaloperators
JustlikeC#,JavaScriptusesthesametypesoflogicaloperatorstohandlelogicalconditions.Logicaloperatorsareusedtohandlemultipleconditionsinalogicalstatement.
LogicalAND
LogicalANDisrepresentedas&&andisusedintwoormoreoperandsorconditionsinstatements.
Forexample,thefollowingisthecodesnippetthatshowsthemethodthattakesthreeparametersandthelogicisdefinedthatcheckswhethernumber1isequaltonumber2andthesummationofnumber1andnumber2isequaltonumber3toreturntrue:
<script>
functionCheckNumbers(number1,number2,number3){
if((number1==number2)&&((number1+number2)==number3)){
returntrue;
}
}
<script>
LogicalOR
LogicalORisrepresentedas||andisusedwithtwoormoreoperandsorlogicalconditions.
Forexample,thefollowingisthecodesnippetthatshowsthemethodthattakesthreeparameters,andifanyofthenumbersisequaltothevalue10,itwillreturntrue:
<script>
functionAnyNumber10(number1,number2,number3){
if((number1==10||number2==10||number3==10){
returntrue;
}
}
</script>
LogicalNOT
LogicalNOTisrepresentedas!andusedwithconditionsthatreturnaBooleanvalue.Forexample,ifanylogicalconditionreturnstrue,thisoperatorwillmakeitfalse.Itcanbeusedasfollows.Inthecodesnippet,ifnumber1,number2,andnumber3areequalto10,themethodwillreturnfalse.Iftheyaredifferent,thereturnvaluewillbetrue:
<script>
functionAnyNumber10(number1,number2,number3){
return!(number1==10&&number2==10&&number3==10){
}
}
</script>
Bitwiseoperators
Bitwiseoperatorsconsidereachnumberoroperandasbinary(acombinationof0and1).
Everynumberhasspecificbinarycorrespondingtoit.Forexample,number1binaryisrepresentedas0001and5representedas0101.
Bitwiseoperatorsworkon32-bitnumbersandanynumericoperandisfirstconvertedintoa32-bitnumberandthenconvertedbacktoJavaScriptnumber.
Bitwiseoperatorsperformtheiroperationsinbinaryandreturntheresultasnumbers.
Forexample,xis1andyis9.
1representedas0001.
9representedas1001.
BitwiseAND
BitwiseANDisrepresentedas&andthefollowingisthecomparisonofeachbitofoperand1and9.Ifbothvalueoneachbitis1,theresultwillbe1,otherwise0:
Number=1 Number=9 Result
0 1 0
0 0 0
0 0 0
1 1 1
IntheJavaScriptcode,wecanuseitasfollows:
<script>
vara="1";
varb="9";
varc=a&b;
</script>
Finally,theresultantvaluewillbe0001,whichisequalto1.
BitwiseOR
BitwiseORisrepresentedas|andthefollowingishowthebitORwillbeoperated:
Number=1 Number=9 Result
0 1 1
0 0 0
0 0 0
1 1 1
ThefollowingcodesnippetshowstheusageinJavaScript:
<script>
vara="1";
varb="9";
varc=a|b;
</script>
Finally,theresultantvaluewillbe1001,whichisequalto9.
BitwiseNOT
BitwiseNOTisrepresentedas~anditworksonasingleoperandandinverseeachbitofthebinary.
Forexample,ifthenumber9isrepresentedas1001,itwillbeconvertedtoa32-bitnumberandthenbitwiseNOTwillmakeit11111111111111111111111111110110,whichisequalto-10.
Thefollowingisthecodesnippet:
<script>
vara=~9;
</script>
BitwiseXOR
BitwiseXORisrepresentedas^anditworkswithtwoormoreoperands.
ThefollowingtableshowshowthebitwiseXORisoperated:
Number=1 Number=9 Result
0 1 1
0 0 0
0 0 0
1 1 0
ThefollowingcodesnippetshowstheusageinJavaScript:
<script>
vara="1";
varb="9";
varc=a^b;
</script>
Finally,theresultantvaluewillbe1000,whichisequalto8.
Bitwiseshiftoperators
Therearethreekindsofbitwiseshiftoperators,asfollows:
BitwiseleftshiftoperatorBitwiserightshiftoperator
Bitwiseleftshift
Itisrepresentedas<<andisusedtoshiftabitfromtherightsidetothebinaryvalueofanynumber.
Forexample,number9isrepresentedas01001,andifweusebitwiseleft,theresultantvaluewillbe10010,whichshiftedonebitfromtheright.
ThefollowingcodesnippetshowstheusageinJavaScript:
<script>
vara=9;
varresult=a<<1;
</script>
Finally,theresultantvaluewillbe10010,whichisequalto18.
Bitwiserightshift
Itisrepresentedas>>andisusedtoshiftabitfromtheleftsidetothebinaryvalueofanynumber.
Forexample,number9isrepresentedas1001,usingbitwiserightwillgivetheresultantvalueas0100.
ThefollowingcodesnippetshowstheusageinJavaScript:
<script>
vara="9";
varresult=a>>1;
</script>
Finally,theresultantvaluewillbe0100,whichisequalto4.
Thetypeofoperator
Thisisusedtocheckwhetherthetypeofthevariableisanobject,undefined,number,andsoon.InJavaScript,wecanusethisasfollows:
<script>
if(typeofa=="number"){
alert("thisisanumber");
}
</script>
Hereisthelistofpossiblevaluesreturnedbythetypeofoperator:
Valuereturned Description
"number" Ifoperandisanumber
"string" Ifoperandisastring
"boolean" IfoperandisaBoolean
"object" Ifoperandisanobject
null Ifoperandisnull
"undefined" Ifoperandisnotdefined
Thevoidoperator
Thevoidoperatorpreventsanexpressiontoreturnanyvalue.Itisessentialinconditionswhereyouneedtoevaluatetheexpressionbutdon'tneedthereturnvalueintheprogram.
Youcanwriteanyexpressionorstatementinsidethevoidmethod.
Forexample,thefollowingcodesnippetshowsthesimpleexampleofusingavoidoperator
todisplayalertmessagewhenthelinkisclicked.Here,thealertexpressionisevaluatedoncetheuserclicksonthelink:
<html>
<head></head>
<body>
<ahref="javascript:void(alert('Youhaveclicked!'));">
</a>
</body>
</html>
Whenthepagerunsandtheuserclicksonthelink,itwilldisplayanalertmessageboxasshowninthefollowing:
Moreover,passing0asanexpressionwithinthevoidmethodwilldonothing:
<html>
<head></head>
<body>
<ahref="javascript:void(0);">
DoNothing
</a>
</body>
</html>
Anotherexamplehereisusingvoidtoaddtwonumbersandreturningundefinedfortheassignedoperand:
<script>
varn1=6;
varn2=7;
varn3;
varresult=void(n3=n1+n2);
alert("result="+result+"andn3="+n3);
</script>
Thedeleteoperator
Adeleteoperatorisusedtodeleteobjectsanditsproperties,butnotthelocalvariables.ThefollowingexampleshowsthewayyoucanusethedeleteoperatorinJavaScript:
varcountry={id:1,name:"USA"};
deletecountry.id;
alert(country.id);
Callingcountry.idwillreturnundefined,asthiswasalreadydeletedintheprecedingstatement.Ontheotherhand,ifwedeletethecountryobject,itwouldnotdeleteanddisplaythecountryIDas1:
varcountry={id:1,name:"USA"};
deletecountry;
alert(country.id);
Miscellaneousoperators
HerearefewotheroperatorsthatareavailableinJavaScript.
Conditionaloperators
Conditionaloperatorisrepresentedas(?:):
expression1?expression2:expression3
Itworksascross-selectortoevaluateexpressions.Ifthefirstexpressionistrue,thesecondexpressionwillbeexecuted,otherwisethethirdwillbeexecuted.
Thefollowingisthecodesnippetforusingconditionaloperatortoevaluateexpression.ThereisacompareValues()functionthattakestwoparameters,andanalertwillbedisplayedstatingwhetherboththeparametersareequalornotequal:
<script>
functioncompareValues(n1,n2)
(n1==n2)?alert("Bothvaluesareequal"):alert("Passedvaluesarenot
equal");
</script>
Spreadoperator
Thespreadoperatorisrepresentedas(…).Itisusedwhereyouexpectmultipleargumentsto
bepassedinforafunctioncall.
Forexample,ifyourfunctionistakingfiveparameters,youcaneitherpassthosevaluesonebyoneastheparametervaluewhencallingthatmethodorkeeptheminanarrayandpassthatarraythroughthespreadoperator.
ThefollowingcodesnippetshowstheactualexampleofusingthisinJavaScript:
functionmultipleArgs(a,b,c,d,e){
}
varargs=[1,2,3,4,5]
multipleArgs(…args);
Built-indisplaymethodsinJavaScriptThefollowingarethedisplaymethodsavailableinJavaScripttoprovidenotificationsandmessagestousersindifferentforms.
Displayingmessages
Therearethefollowingthreetypesofpop-updialogboxes:
AlertmessageboxConfirmationmessageboxPromptmessagebox
Alertbox
Usingwindow.alert(),wecanpopupanalertdialogbox:
<!DOCTYPEhtml>
<html>
<body>
<h1>MyFirstWebPage</h1>
<p>Myfirstparagraph.</p>
<script>
window.alert(5+6);
</script>
</body>
</html>
Confirmbox
Usingwindow.confirm(),wecanpopupaconfirmdialogboxthatreturnstheeventresulttheuserhastaken.Whenaconfirmdialogboxpopsup,itprovidestwoactionevents:OKandCancel.IfauserclickonOK,truewillbereturned,otherwisefalse.ThefollowingcodeshowstheusageoftheconfirmdialogboxonyourHTMLpage.
Thefollowingisthecodesnippetforusingaconfirmdialogboxtoconfirmwiththeuserbeforesavingarecord:
<!DOCTYPEhtml>
<html>
<body>
<script>
varr=window.confirm("areyousuretosaverecord");
if(r==true){
alert("Recordsavedsuccessfully");
}
else{
alert("Recordcouldn'tbesaved");
}
</script>
</body>
</html>
Promptbox
Promptdialogboxisusedincaseswhenyouwanttheusertosupplythevalue.Itcanbeusedinconditionswhereyourequireuserinput.
ThefollowingcodesnippetshowsthewayofusingapromptmessageboxintheJavaScriptprogram:
<!DOCTYPEhtml>
<html>
<body>
<script>
varname=window.prompt("Enteryourname","N/A");
if(name!=null){
alert("hello"+name"+,howareyoutoday!");
}
</script>
</body>
</html>
Writingonapage
Wecanusethedocument.write()methodtowriteanythingonthescreen.
ThefollowingcodesnippetshowsthewayofwritinganytextonawebpageinJavaScript:
<!DOCTYPEhtml>
<html>
<body>
<script>
document.write("HelloWorld");
</script>
</body>
</html>
Writingintothebrowser'sconsolewindow
Usingconsole.log(),wecanwriteanytextintothebrowser'sconsolewindow.
ThefollowingcodesnippetshowsthewayofwritingtextintothebrowserconsolewindowfortracingordebuggingpurposesinJavaScript:
<!DOCTYPEhtml>
<html>
<body>
<h1>MyFirstWebPage</h1>
<p>Myfirstparagraph.</p>
<script>
console.log("Enteredintoscriptexecutioncontext");
</script>
</body>
</html>
BrowserObjectModelsinJavaScriptJavaScriptprovidessomepredefinedglobalobjectsthatyoucanusetomanipulatetheDOM,closebrowsers,andsoon.Thefollowingarethebrowserobjectswecanusetoperformdifferentoperations:
WindowNavigatorScreenHistoryLocation
Window
Windowobjectreferstotheopenwindowinabrowser.IfintheHTMLmarkup,someiframesaredefined,aseparatewindowobjectwillcreated.Throughthewindowobject,wecanaccessthefollowingobjects:
AllglobalvariablesAllglobalfunctionsTheDOM
ThefollowingshowsanexampleofaccessingtheDOMfromthewindowobjectandaccessingthetextboxcontrol.
Document
window.documentreturnsthedocumentobjectandwecanuseitspropertiesandmethodsforaspecificreason:
<html>
<body>
<inputtype="text"name="txtName"/>
<script>
vartextbox=Window.document.getElementById("txtName");
textbox.value="HelloWorld";
</script>
</body>
</html>
Thewindowobjectitselfcontainsmanymethodsandfewofthemareasfollows:
Event Description Syntax
Close Toclosecurrentwindow window.close();
Open Toopennewwindow window.open();
Move Tomovewindowtothespecifiedposition window.moveTo();
Resize Toresizewindowtospecifiedwidthandheight window.resizeTo();
Navigator
Thisobjectprovidestheinformationaboutthebrowser.Itisbeneficialwhenyouneedtorunspecificscriptsbasedonthebrowserversionordosomethingspecifictothebrowser.Let'slookintothemethodsitexposes.
Properties
Thepropertiesaredescribedasfollows:
appCodeName:ThisreturnsthecodenameofthebrowserappName:ThisreturnsthenameofthebrowserappVersion:ThisreturnstheversionofthebrowsercookieEnabled:ThisdetermineswhethercookiesareenabledinthebrowsergeoLocation:Thisgetsthelocationoftheuseraccessingthepagelanguage:Thisreturnsthelanguageofthebrowseronline:Thisdetermineswhetherthebrowserisonlineplatform:Thisreturnstheplatformthatthebrowserhascompiledproduct:ThisreturnstheenginenameofthebrowseruserAgent:Thisreturnstheuseragentheadersentbythebrowsertotheserver
Theexamplecodeisasfollows:
<!DOCTYPEhtml>
<html>
<head>
<scripttype="text/javascript">
functionshowInformation(){
varspObj=window.document.getElementById("spInfo");
spObj.innerHTML=
"BrowserCodeName:"+navigator.appCodeName+"<br>"+
"ApplicationName:"+navigator.appName+"<br>"+
"ApplicationVersion:"+navigator.appVersion+"<br>"+
"CookieEnabled?"+navigator.cookieEnabled+"<br>"+
"Language:"+navigator.language+"<br>"+
"Online:"+navigator.onLine+"<br>"+
"Platform:"+navigator.platform+"<br>"+
"Product:"+navigator.product+"<br>"+
"UserAgent:"+navigator.userAgent;
navigator.geolocation.getCurrentPosition(showPosition);
}
functionshowPosition(position){
varspObj=window.document.getElementById("spInfo");
spObj.innerHTML=spObj.innerHTML+"<br>Latitude:"+
position.coords.latitude+
"<br>Longitude:"+position.coords.longitude;
}
</script>
</head>
<bodyonload="showInformation();">
<spanid="spInfo"></span>
</body>
</html>
Theoutputisshownasfollows:
Screen
Throughthescreenobject,youcangetinformationabouttheuser'sscreen.Thisishelpfultoknowfromwhichscreentheuserisviewingthecontent.Ifit'samobilebrowserorstandarddesktopscreen,youcangetthesizeandotherinformationandmodifythecontentasrequired.
Properties
Thepropertiesaredescribedasfollows:
availHeight:ThisreturnstheheightofthescreenavailWidth:ThisreturnsthewidthofthescreencolorDepth:Thisreturnsthebitdepthofthecolorpalettefordisplayingimagesheight:ThisreturnsthetotalheightofthescreenpixelDepth:Thisreturnsthecolorresolution(inbitsperpixel)ofthescreenwidth:Thisreturnsthetotalwidthofthescreen
Theexamplecodeisasfollows:
<!DOCTYPEhtml>
<html>
<head>
<scripttype="text/javascript">
functionshowInformation(){
varspObj=window.document.getElementById("spInfo");
spObj.innerHTML=
"AvailableHeight:"+screen.availHeight+"<br>"+
"AvailableWidth:"+screen.availWidth+"<br>"+
"Height:"+screen.height+"<br>"+
"Width:"+screen.width+"<br>"
}
</script>
</head>
<bodyonload="showInformation();">
<spanid="spInfo"></span>
</body>
</html>
Theoutputisshownasfollows:
History
ThiscontainstheURLsthattheuservisited.Youcanaccessitthroughthewindow.historyobject.
Youcanusethisobjecttonavigatetotherecentlyvisitedlinks.
Methods
Themethodsaredescribedasfollows:
Window.history.back():ThisloadsthepreviousURLWindow.history.forward():ThisloadstherecentURLinthehistorylistWindow.history.go():ThisloadsaspecificURLavailableinthehistorylist
Location
ThelocationobjectgivesyouinformationaboutthecurrentURL.Justlikehistory,itcanalsobeaccessedthroughwindow.location.Thereareafewmethodsandpropertiesyoucanusetoperformspecificoperations.
Properties
Thepropertiesaredescribedasfollows:
window.location.host:ThisreturnsthehostnameandportnumberoftheURLwindow.location.hostname:ThisreturnsonlythehostnameoftheURLwindow.location.href:ThisprovidesthecompleteURLwindow.location.origin:Thisreturnsthehostname,portnumber,andprotocoloftheURLwindow.location.pathname:ThisreturnsthepathnameoftheURLwindow.location.port:ThisreturnsonlytheportnumberoftheURLwindow.location.protocol:ThisreturnstheprotocoloftheURL,forexample,HTTPorHTTPSwindow.location.search:ThisreturnsthequerystringoftheURL
Methods
Themethodsaredescribedasfollows:
window.location.assign():Thisloadsanewdocument.window.location.reload():ThisreloadsthecurrentURL.window.location.replace():ThiscanbeusedtoreplacethecurrentURLwiththenewone.Replacedoesnotrefreshthepage,itcanonlychangetheURL.
SummaryInthischapter,wediscussedthebasicconceptsofJavaScriptandhowtouseitinourwebapplications.Wediscussedthecorefundamentalsofdeclaringvariablesandimplementingarrays,functions,anddatatypestostartwritingprogramsinJavaScript.Inthenextchapter,wewilldiscusssomeadvancedconceptsaboutobject-orientedprogrammingandworkingwithclosures,scopes,andprototypefunctionswithpracticalimplementation.
Chapter2.AdvancedJavaScriptConceptsJavaScript,wheninitiallydesigned,wasnotexpectedtobecomethecoreprogramminglanguageforWebdevelopment.Itwasnormallyusedtoperformsomebasicclient-sideoperationsthatrequiresomemanipulationoftheDocumentObjectModel(DOM)elements.Lateron,withtherecentpaceinWebdevelopment,thingshaveprettymuchchanged.Now,manyapplicationsarepurelyusingJavaScriptandHTMLtohandlecomplexsituations.Fromtimetotime,withdifferentversions,differentfeatureswereaddedand,asperthespecificationofECMAScript6,youcannowhaveclasses,youcandoinheritanceasyoudowithanyotherprogramminglanguage,suchasC#orJava.Closures,prototypefunctions,propertydescriptors,andmanymorethatwewilldiscussinthischaptermakeitmorepowerfulandrobust.
Inthepreviouschapter,welearnedthecoreconceptsandsomebasicfundamentalsofwritingprogramsinJavaScriptandwhatfeaturesasalanguageitprovides.Inthischapter,wewillbefocusingmoreontheadvancedtopics,whichhelpustousetheseconceptsinlargeandcomplexapplications.
Wewillalsofocusonscopingandhoistingvariables,object-orientedprogramming,prototypefunctions,propertydescriptors,closures,exceptionhandling,andsoon.Sometopics,suchaspromises,asynchronouspatternsandAsynchronousJavaScriptandXML(Ajax)techniques,arebroadertopicsandarecoveredinotherchapters.
Variables–scopeandhoistingWealreadyknowhowvariablesaredeclaredinJavaScriptusingthevarkeyword.Anyvariablethatisdeclaredusingthevarkeywordistermedahoistedvariable,andthetermhoistingistheJavaScriptdefaultbehaviorofmovingdeclarationstothetop.WhenJavaScriptiscompiledbytheJavaScriptengine,allthevariablesthataredeclaredusingthevarkeywordareplacedatthetopwithinitsscope.Thismeansthatifthevariableisdeclaredwithinafunctionblock,itwillbeplacedatthetopofthefunction;otherwise,ifit'sdeclaredoutsideanyfunctionandattherootofthescript,itwillbecomegloballyavailable.Let'shavealookatthisexampletoclarifyourunderstanding.
Let'ssupposethefollowingcodeisthesimpleprogramthatreturnstheGMTofthecountrynamepassedinthefunction'sparameter:
functiongetCountryGMT(countryName){
if(countryName=="Pakistan"){
vargmt="+5.00";
}
elseif(country=="Dubai"){
vargmt="+4.00";
}else{
returnnull;
}
}
WhentheJavaScriptenginecompilesthescript,thevargmtvariablewillbeplacedatthetop:
functiongetCountryGMT(countryName){
vargmt;
if(countryName=="Pakistan"){
gmt="+5.00";
}
elseif(country=="Dubai"){
gmt="+4.00";
}else{
returnnull;
}
}
Thisiscalledhoisting,wherethevarvariablesareplacedatthetopwithinitsscope.Moreover,ifyoutrytoaccessthevariablevalueinthelastelsecondition,itwillgiveanundefinedvalueandcouldbeaccessibleineveryconditionblock.
Thiscodeshowsanotherexampleofdeclaringthegmtvariablegloballyanddeclaringitinthebottomofthecode:
functiongetCountryGMT(countryName){
if(countryName=="Pakistan"){
gmt="+5.00";
}
elseif(country=="Dubai"){
gmt="+4.00";
}else{
returnnull;
}
}
vargmt;
Whenthescriptcompiles,itwillputthedeclarationofgmtatthetopofthecode:
vargmt;
functiongetCountryGMT(countryName){
if(countryName=="Pakistan"){
gmt="+5.00";
}
elseif(country=="Dubai"){
gmt="+4.00";
}else{
returnnull;
}
}
ToovercomethisbehaviorinECMAScript6,thereisanewletkeywordintroducedtodeclarevariablesandthescoperemainswhereitisdefined.Thesevariablesareinaccessibleoutsideitsscope.
Tip
NotethatECMAScript6isnotsupportedbyolderbrowserversionsbutMicrosoftEdge,GoogleChrome11,andMozillaFirefoxsupportit.
DeclaringletAswithvar,youcanuselettodeclarevariablesinthesameway.Youcanusethiskeywordinyourprogramsbutitwillbeaccessiblewithinthescopewhereitisdefined.So,forexample,ifsomevariableisdefinedwithintheconditionblock,itwillnotbeaccessibleoutsideitsscope.
Let'shavealookatthefollowingexample,whereavariableisdeclaredinsideaconditionblockandthefinaloutputaftercompilationsremainsasitis.Thisisbeneficialinconditionswhereyouwanttodeclarevariableswithinascopeforaparticularlogicorscenario.Intheelsecondition,gmtwillnotbeaccessible,asitisdefinedwithintheifcondition:
functiongetCountryGMT(countryName){
if(countryName=="Pakistan"){
letgmt="+5.00";
}
else{
returnnull;
}
}
Oncetheletvariableisdeclaredwithinthescopeofthefunctionorscript,itcannotberedeclared.Also,ifthevariablesaredeclaredusingthevarkeyword,theycannotberedeclaredusinglet.
Thiscodewillnotthrowanexceptionasthescopeisdifferent.However,withinthesameblock,itcannotberedeclared:
functiongetCountryGMT(countryName){
vargmt;
if(countryName=="Pakistan"){
letgmt="+5.00";
}
else{
returnnull;
}
}
Conditionswhereletisefficienttouse
Herearetheconditionswhereletisused.
Functionsinloops
Ifweusethevarvariablesinfunctionsinsideloop,thesevariablesgenerateissues.Considerthefollowingexample,wherethereisanarrayofvaluesand,throughlooping,weareinsertingafunctionateachindexofanyarray.Thiswillmakeanerrorandpasstheivariableasareference.So,ifyoutraverseeachindexandcallfunction,thesamevalue,thatis,10,willbeprinted:
varvalues=[];
for(vari=0;i<10;i++)
{
values.push(function(){console.log("valueis"+i)});
}
values.forEach(function(valuesfunc){
valuesfunc();
})
Whereaswithlet,eachvaluewillbepassedbyvalueanddoesnotchangewhenthevariablevalueisupdatedinsidetheloop.
Thecodesnippetofusingletisasfollows:
varvalues=[];
for(leti=0;i<10;i++)
{
values.push(function(){console.log("valueis"+i)});
}
values.forEach(function(valuesfunc){
valuesfunc();
})
EventsinJavaScriptEventsplayanimportantroleinanybusinessapplicationwhereyouwanttosavearecordonabutton-clickevent,orshowsomemessage,orchangesomeelement'sbackgroundcolor.Anyoftheseeventscanbedefinedfromthecontrollevelitselforregisterdirectlythroughthescript.
Let'shavealookatthisexample,whichchangestheinnerhtmlcodeofthedivcontrolwhenthemouseisentered:
<html>
<body>
<divid="contentPane"style="width:200px;height:200px;">
</div>
<script>
vardivPane=document.getElementById("contentPane");
divPane.onmouseenter=function(){
divPane.innerHTML="Youareinsidethediv";
};
divPane.onmouseleave=function(){
divPane.innerHTML="Youareoutsidethediv";
};
</script>
</body>
</html>
TheprecedingexampleregisteredtwoeventsonthescriptsideforanHTMLdivcontrol.Itchangestext,ifthemousehasenteredthefunctionorhaslefttheboundaryofdiv.Alternatively,wecanalsoregistereventsonthecontrolitself,andthisexampleshowsthewayyoucandisplayamessageonabutton-clickevent.Ifyouhavenoticedthescriptingblockisdefinedafterthedivpane,thereasonisthatwhenthepageloads,itwilltrytoexecutethescriptandthrowanerrorbecausethecontentPaneelementwasnotcreatedatthattime:
<html>
<body>
<script>
functiondisplayMessage(){
alert("youhaveclickedbutton");
}
</script>
<inputtype="button"onclick="displayMessage();"/>
</body>
</html>
Inthisexample,thescriptingblockisdefinedatthetopofthepage.Inthisscenario,itcanbedefinedanywhereinthepagebecauseitwillonlybeexecutedwhentheuserclicksonabutton.
FunctionargumentsWealreadyknowthattheJavaScriptfunctionscanhaveparameters.However,thetypeoftheparameterscannotbespecifiedwhencreatingafunction.JavaScriptneitherperformsanytypecheckingontheparametervaluespassednorvalidatesthenumberofparameterswhenthefunctioniscalled.So,forexample,ifaJavaScriptfunctionistakingtwoparameters,asshowninthiscode,wecanevencallitwithoutpassinganyparametervalueorbypassinganytypeofthevaluesormorevaluesthantheexpectednumberoftheparametersdefined:
functionexecute(a,b){
//dosomething
}
//callingwithoutparametervalues
execute();
//passingnumericvalues
execute(1,2);
//passingstringvalues
execute("hello","world");
//passingmoreparameters
execute(1,2,3,4,5);
Themissingparametersaresetasundefined,whereasifmoreparametersarepassed,theseparameterscanbeaccessedthroughtheargumentsobject.Theargumentsobjectisabuilt-inobjectinJavaScriptthatcontainsanarrayoftheargumentsusedwhenthefunctionisinvoked.Wecanuseitasshowninthiscode:
functionexecute(a,b){
//dosomething
alert(arguments[0]);
alert(arguments[1]);
alert(arguments[2]);
alert(arguments[3]);
alert(arguments[4]);
}
//passingmoreparameters
execute(1,2,3,4,5);
}
Argumentsarepassedbyvalue;thismeansifthevaluesarechangedinsidethefunction,itwillnotchangetheparameter'soriginalvalue.
Object-orientedprogramminginJavaScriptAlltheobjectsinJavaScriptareinheritedfromanobject.JavaScriptprovidesdifferentpatternstoadheretotheobject-orientedprogramming(OOP)principleswhenbuildingapplications.Therearedifferentpatterns,suchasconstructorpatterns,prototypepatterns,andobjectliteralrepresentation,and,withECMAScript6,acompletelynewwayofrepresentingobjectsthroughclassesandinheritingabaseclassusingtheextendskeyword.
Inthissection,wewillseehowwecanimplementtheOOPprincipleswithdifferentmethodologies.
CreatingobjectsAclassrepresentsthestructureofanobjectandeveryclasshascertainmethodsandpropertiesusedbytheobject,whereasanobjectisaninstanceofaclassandisknownasaclassinstance.
JavaScriptisaprototype-basedlanguageandbasedonobjects.Inaclass-basedlanguagesuchasC#andJava,wehavetofirstdefinetheclassthatcontainssomemethodsandpropertiesandthenuseitsconstructortocreateobjects.InJavaScript,anyobjectcanbeusedasatemplatetocreatenewobjectsandusethepropertiesormethodsdefinedwithinit.Newobjectscanalsodefinetheirownpropertiesormethodsandcanbeassociatedasaprototypeforanotherobject.ECMAScript6,however,introducesclassesinJavaScript,whichissyntacticalsugaroverexistingparadigmsandmakesiteasyfordeveloperstowritesimplerandcleanercodetocreateobjects.Inthenextsection,wewillseedifferentwaysofcreatingobjectsinJavaScript.
Definingobjectsusingobjectliteralnotation
Objectliteralsarecomma-separatedlistsofnamevaluepairswrappedincurlybraces.
Objectliteralsaredefinedusingthefollowingsyntaxrules:
AcolonseparatesapropertynamefromavalueAvaluecanbeanydatatype,includingarrayliterals,functions,andnestedobjectliteralsEachnamevaluepairisseparatedbyacommafromthenextnamevaluepairdefinedThelastnamevaluepairshouldnotcontainanycommaafterit
Hereisthebasicrepresentationofapersonobjectinobjectliteralnotation:
varperson={id:"001",name:"Scott",isActive:true,
Age:35};
HereisanotherrepresentationofapersonModelobjectwithasavePerson()methodinobjectliteralnotation:
varpersonModel={id:"001",name:"Scott",isActive:true,
Age:35,function:savePerson(){//codetosavepersonrecord}};
Definingobjectsusingaconstructorpattern
ClassescanbedefinedusingfunctionsinJavaScript.ThiscodeshowsthesimplewayofdefiningacustomerclassinJavaScript:
varperson=newfunction(){};
Theprecedingcodejustdefinedanemptyclasswithadefaultconstructorandnopropertiesandmethods.Objectscanbeinitializedusinganewkeyword,asshowninthiscode:
varp1=newperson();
Thesamefunctioncanbedefinedinaregularfunctiondeclarationstyle:
functionperson(){};
Withtheregularfunctiondeclaration,theJavaScriptengineknowstofetchthefunctionwhenitisneeded.Forexample,ifyoucallitbeforethefunctiondeclarationinyourscript,itwillcallthisfunction,whereasthevariabledefiningapproachneedsthevariabletobedeclaredfirstbeforecallingit.
Usingtheclasskeyword
ECMAScript6providesanewwayofdefiningclassesandintroducedaclasskeyword,whichcanbeusedjustlikeinotherprogramminglanguages.Thiscodeistherepresentationofdefiningacustomerobject.Thedefaultconstructorisconstructor()thattakesnoparametersandcanbeoverriddenwithmoreparameters,dependingontherequirements.Eachclassallowsyoutodefineonlyoneconstructor,andiftheconstructorisoverridden,thedefaultconstructorwillnotbeusedtoinstantiateobjects:
classPerson{
constructor(){}
}
Properties
Propertiesareusedtostoreandreturnvalues.Wecandefinepropertieswheninitializingfunctionsandthesepropertieswillbeavailableeachtimetheobjectiscreated.
Definingpropertiesusingobjectliteralnotation
Propertiescanbedefinedinobjectsasliteralstrings.Forexample,inthiscode,thereisthecustomerobjectcontainingtwopropertiesandamethod.Thedrawbackwiththisapproachisthatthereisnoconstructorandwecannotrestrictuserstosupplypropertyvalueswheninitializinganobject.Eitheritcanbesetashardcoded,asshownhere,orafterinitializinganobject:
varperson={
id:"001",
name:"Person1",
savePerson:function(){
}
}
Definingpropertiesusingaconstructorpattern
Aconstructorfunctionpatternallowsyoutodefineparametersthatrestrictuserstopasspropertyvalueswheninstantiatingobjects.Considerthisexample;itcontainsacustomerobjectwithtwoproperties,namelyidandname:
varperson=function(id,name){
this._id=id;
this._name=name;
}
Thethiskeywordreferstothecurrentobjectandpropertiescanbeaccessedusingthiswhencallinginsidetheclass,orthroughtheinstancevariable,asshowninthefollowingcode:
varp1=newperson("001","Person1");
console.log("PersonID:"+p1.PersonID);
console.log("PersonName:"+p1.name);
Propertyvaluescanalsobesetafterinitializinganobject,asshowninthefollowingcode:
varperson=function(){
}
varp1=newperson();
p1.id="001";
p1.name="Person1";
Thissnippetalsorepresentsthesameapproachofdefiningapersonobjectthattakestwoparameters.Wewillseethelimitationsofusingthisapproachinthenextsectionwhendealingwithprototypes:
functionperson(id,name){
this.id=id;
this.name=name;
this.logToConsole:function(){
console.log("PersonIDis"+this.id+",Name:"+this.name);
};
}
Definingpropertiesusingsetters/gettersinECMAScript6
InECMAScript6,thereisanewwayofdefiningpropertiesanditfollowsthestandardwaylikeotherprogramminglanguages:
classPerson{
constructor(id,name){
this.id=id;
this.name=name;
}
}
varp1=newperson("001","Person1");
console.log("PersonID:"+p1.id);
Unlikethisapproach,wecanalsodefinesettersandgettersusingthesetandgetkeywords.ConstructorsareoptionalinJavaScriptwhendefiningclasses;ifnoconstructorisdefined,thedefaultconstructor,constructor(),willbeinvokedonobjectinitialization.Let'shavealookatthisexamplecontainingapersonNamepropertyforbothsetterandgetter:
classPerson{
setName(name){
this.personName=name;
}
getName(){
returnthis.personName;
}
}
varp1=newPerson();
p1.Name="Person1";
console.log("personName"+p1.Name);
JavaScriptpropertydescriptors
Everypropertyhasthepropertydescriptor,whichisusedtoconfigure,andhasthefollowingmeaning:
Writable:Thisattributeisusedtomakethecoderead-onlyorwritable.Thefalsekeywordmakesitread-onlyandthevaluecannotbemodified.Enumerable:Thisattributeisusedtohide/unhidethepropertytobeaccessibleorserializable.Settingthisattributetofalsewillnotshowupthepropertywhenyouiteratethroughanobject'smembersandalsocouldnotbeserializedwhenusingJSON.stringify.Configurable:Thisattributeisusedfortheonandoffconfigurationchanges.Forexample,settingthisattributetofalsewillpreventapropertytobemodifiedordeleted.
Alltheseattributesaretruebydefaultbutcanbeoverridden,asshowninthefollowingexample.Thisexamplehasacarobjectcontainingtwoproperties,namelynameandcolor:
varcar={
name:"BMW",
color:"black"
};
Displaypropertydescriptors
Youcandisplaytheexistingpropertiesusingthefollowingstatement:
display(Object.getOwnPropertyDescriptor(car,'name'));
Managingpropertydescriptors
Thepropertydescriptorsofanyobject'spropertycanbemanagedasshowninthefollowingcode:
Object.defineProperty(car,'color',{enumerable:false});
Object.defineProperty(car,'color',{configurable:false});
Object.defineProperty(car,'color',{writable:false});
Usinggettersandsetters
ThroughObject.defineProperty,wecanalsoaddsettersandgettersforproperties.Thisexampleaddsthefullnameofthecarbyconcatenatingmakeandname,andthensplittingnametogetthemodelandnamethroughtwodifferentproperties:
varcar={name:{make:"honda",brand:"accord"}};
Object.defineProperty(car,'fullname',
{
get:function(){
returnthis.name.make+''+this.name.brand
},
set:function(value){
varnames=value.split('');
this.name.make=names[0];
this.name.brand=names[1];
}
});
car.fullname="HondaAccord";
display(car.fullname);
Methods
Methodsaretheactionsthatcanbeperformedonobjects.InJavaScript,itcanberepresentedasapropertycontainingafunctiondefinition.Let'shavealookatadifferentapproachtodefiningthemethodsoftheJavaScriptobjects.
Definingmethodsthroughobjectliteralnotationapproach
AnexampleshowingthelogToConsole()methoddefinedintheobjectliteralnotationapproachisshownhere:
varperson={
id:"001",
name:"Person1",
logToConsole:function()
{
console.log("PersonIDis"+this.id+",CustomerName:"+this.name);
}
}
Definingobjectsusingtheconstructorfunctionapproach
Theconstructorfunctionapproachtodefiningmethodsisshowninthefollowingcode:
varperson=function(id,name){
this._id=id;
this._name=name;
this.LogToConsole=function(){
console.log("PersonNameis"+this._name);
}
}
varp1=newperson("001","Person1");
p1.LogToConsole();
Anotherwayistodeclaretheconstructorfunctionapproachisasfollows:
functionperson(id,name){
this._id=id;
this._name=name;
this.LogToConsole=function(){
console.log("Nameis"+this._name);
}
}
varp1=newperson("001","Person1");
p1.LogToConsole();
InECMAScript6,thereisabettersyntaxfordefiningmethods.Thecodesnippetwiththesameexampleisasfollows:
classPerson{
constructor(){
}
setName(name){
this._name=name;
}
getName(){
returnthis._name;
}
logToConsole(){
console.log("PersonNameis"+Name);
}
}
varp1=newPerson();
p1.Name="Person1";
p1.logToConsole();
Themethodreturntypeisnotneededwhendefiningamethodanditisrealizedbasedonthemethodbody.
Extendingpropertiesandmethods
EveryJavaScriptobjecthasanobjectknownasaprototype.Aprototypeisapointertoanotherobject.Thisprototypecanbeusedtoextendtheobjectpropertiesandmethods.Forexample,ifyouaretryingtoaccesssomepropertyofanobjectthatisnotdefined,itwilllookintotheprototypeobjectandproceedthroughtheprototypechainuntilitisfoundorreturnsundefined.Therefore,whetheranobjectiscreatedusingaliteralsyntaxapproachoraconstructorfunctionapproach,itinheritsallthemethodsandpropertiesfromaprototypeknownasObject.prototype.
Forexample,anobjectcreatedusingnewDate()inheritsfromDate.prototype,andsoon.However,thebaseobjectitselfdoesnothaveanyprototype.
Wecaneasilyaddpropertiesandfunctionstoobjects,asshownhere:
varPerson=function(name){
this.name=name;
}
varp1=newPerson("Person1");
p1.phoneNo="0021002010";
alert(p1.name);
Extendingexistingfunctionswithoutinitializinganobjectisdoneusingaprototypeobject.Let'shavealookatthisexample,whereweaddonemethod,logToConsole(),andaphoneNopropertyonaPersonfunction:
varPerson=function(name){
this.name=name;
}
Person.prototype.phoneNo="";
Person.prototype.logToConsole=function(){
alert("PersonNameis"+this.name+"andphoneNois"+this.phoneNo)
};
varp1=newperson("Person1");
p1.phoneNo="XXX"
p1.logToConsole();
Privateandpublicmembers
InJavaScript,therearenoaccessmodifierslikewehaveinC#.Allthemembersthataredefinedasthisorwithprototypesareaccessiblefromtheinstance,whereasothermembers,whicharedefinedinsomeotherway,arenon-accessible.
Let'shavealookatthisexample,whichenablesonlytheyandy1()methodstobeaccessibleoutsidethefunction:
functiona(){
varx=1;
this.y=2;
x1=function(){
console.log("thisisprivatelyaccessible");
}
this.y1=function(){
console.log("thisispubliclyaccessible");
}
}
Inheritance
InheritanceisacoreprincipleofOOP.InJavaScript,ifyouareworkingwithanolderversionthatdoesnotcomplywiththeES6standard,itisdoneusingprototype-basedprogramming.
Prototype-basedprogrammingisanOOPmodelthatdoesnotuseclassesbutextendsobjectsorinheritanceusingtheprototypechain.Thismeansthateveryobjecthasaninternalprototypeproperty,whichpointstoaparticularobjectornullifnotused.ThispropertyisnotaccessiblethroughtheprogramandisprivatetotheJavaScriptengine.So,forexample,ifyouarecallingsomeproperty,suchascustomer.getName,itwillfirstcheckthegetNamepropertylocallyontheobjectitself,otherwisegothroughthechainingprocessandtrytofinditbylinkingobjectsthroughtheprototypepropertyuntilitisfound.Ifnopropertyisdefined,
itwillreturnundefined.
Considerthefollowingentity–relationshipmodel(ERD)thathasabasepersonobjectwithsomegenericpropertiesandtwochildobjects,namelyVendorandEmployee,withspecificproperties:
InordertoarticulatethesameinheritanceusingtheJavaScriptconstructorfunctionapproach,wecanusetheprototypepropertyofbothVendorandEmployeetothepersonobject,asshowninthiscode:
varPerson=function(id,name){
this.id=id;
this.name=name;
}
varVendor=function(companyName,location){
this.companyName=companyName;
this.location=location;
}
varEmployee=function(employeeType,dateOfJoining){
this.employeeType=employeeType;
this.dateOfJoining=dateOfJoining;
}
Vendor.prototype=newPerson("001","John");
Employee.prototype=newPerson("002","Steve");
varvendorObj=newVendor("ABC","US");
alert(vendorObj.id);
Intheprecedingexample,vendorObjisanobjectthatwascreatedfromtheVendorconstructorfunction.TheVendorconstructorisbothanobjectandafunctionbecausefunctionsareobjectsinJavaScript,andthevendorObjobjectcanhaveitsownpropertiesandmethods.ItcanalsoinheritmethodsandpropertiesfromtheVendorobject.
BysettingtheprototypepropertyoftheVendorandEmployeeobjectstothePersoninstancethroughtheconstructorfunction,itinheritsallthepropertiesandmethodsofthePersonobjectandbecomesaccessiblebytheVendorandEmployeeobjects.
Object'spropertiesandmethodsdefinedusingtheprototypeobjectareinheritedbyalltheinstancesthatreferencedit.So,inourexample,weextendedtheVendorandEmployeeobjectsthroughtheprototypepropertyandassignedthemtothePersoninstance.Thisway,wheneveranyinstanceoftheVendororEmployeeobjectiscreated,itcanaccessallthepropertiesormethodsofanobjectofPerson.
Propertiesandmethodscanalsobeaddedthroughtheobject;forexample,wecanaddapropertytotheVendorobject,asshowninthefollowingcode,butthiswillbecomethestaticpropertyandnotaccessiblebytheVendorinstance:
Vendor.id="001";
Ontheotherhand,wecanalsoaddpropertiesandmethodstotheVendorinstanceaswellbutthiswillbeaccessiblebythatparticularinstanceonly:
varvendorObj=newVendor("ABC","US");
vendorObj.id="001";
Anothertechniqueofachievinginheritanceisbyassigningtheparent'sprototypetothechild'sprototypeobject,asshownhere:
Vendor.prototype=Person.prototype;
Withthistechnique,anymethodsorpropertiesaddedinthePersonprototypewillbeaccessiblebytheVendorobject:
varPerson=function(id,name){
this.id=id;
this.name=name;
}
//AddingmethodtothePerson'sprototypetoshowmessage
Person.prototype.showMessage=function(message){
alert(message);
}
varVendor=function(companyName,location){
this.companyName=companyName;
this.location=location;
}
//Assigningtheparent'sprototypetochild'sprototype
Vendor.prototype=Person.prototype;
varvendorObj=newVendor("XYZ","Dubai");
vendorObj.showMessage(vendorObjinstanceofPerson);
Afterrunningthisscript,itwillshowtrueinanalertmessage.ThisisbecausetheVendor
objectbecomesaninstanceofthePersonobjectandanymethodorpropertyaddedinanyoftheobjectswillbeaccessiblebyboth.
IfwemodifytheprecedingexampleandaddanothermethodthroughaVendorprototypepropertyaftertheassignmentofthePersonprototypetotheVendorprototype,itwillbeaccessiblebythePersonobject.Thisisbecause,inJavaScript,whenthechild'sobjectprototypeissettotheparent'sobjectprototype,anymethodsorpropertiesaddedineitherobjectaftertheassignmentwillbeaccessiblebyboth.
Let'saddashowConsoleMessage()methodintheVendorobjectthroughaprototypepropertyandaccessitthroughthePersoninstance,asshowninthiscode:
varPerson=function(id,name){
this.id=id;
this.name=name;
}
//AddingmethodtothePerson'sprototypetoshowmessage
Person.prototype.showMessage=function(message){
alert(message);
}
varVendor=function(companyName,location){
this.companyName=companyName;
this.location=location;
}
//Assigningtheparent'sprototypetochild'sprototype
Vendor.prototype=Person.prototype;
//AddingmethodtotheVendor'sprototypetoshowatconsole
Vendor.prototype.showConsoleMessage=function(message){
console.log(message);
}
varpersonObj=newPerson("001","John");
//Personobjectaccessthechild'sobjectmethod
personObj.showConsoleMessage("Console");
ChainingconstructorsinJavaScript
Intheexampleintheprevioussection,wehaveseenhowtoinheritobjects.However,ifsomebaseobjecthassomeoverloadedconstructor,acceptingpropertieswillrequiresomeextraeffort.EveryfunctioninJavaScripthasacallmethod,whichisusedtochainconstructorsforanobject.Wecanusethecallmethodtochainconstructorsandcallbaseconstructors.AsthePersonobjecttakestwoparameters,wewillmodifytheVendorfunctionandtwoproperties,idandnumber,whichcanbepassedwhilecreatingaVendorobject.So,whenevertheVendorobjectiscreated,thePersonobjectwillbecreatedandthevalueswillbepopulated:
varPerson=function(id,name){
this.id=id;
this.name=name;
}
varVendor=function(companyName,location,id,name){
this.companyName=companyName;
this.location=location;
Person.call(this,id,name);
}
varemployee=function(employeeType,dateOfJoining,id,name){
this.employeeType=employeeType;
this.dateOfJoining=dateOfJoining;
Person.call(this,id,name);
}
Vendor.prototype=Person.prototype;
Employee.prototype=Person.prototype;
varvendorObj=newVendor("ABC","US","V-01","Vendor1");
alert(vendorObj.name);
InheritanceusingObject.create()
WithECMAScript5,youcaneasilyinherityourbaseobjectthroughtheObject.create()method.Thismethodtakestwoparameters,theobjecttouseasaprototypeandanobjectcontainingpropertiesandmethodsforthenewobjecttocreate.TheObject.create()methodimprovesconstructor-basedinheritance.It'sagoodchoiceforcreatinganobjectwithoutgoingthroughitsconstructor.Let'sseetheexampleofVendorandEmployeeinheritingthePersonobjectusingtheObject.create()approach:
varPerson=function(id,name){
this.id=id;
this.name=name;
}
varVendor=function(companyName,location,id,name){
this.companyName=companyName;
this.location=location;
Person.call(this,id,name);
}
varEmployee=function(employeeType,dateOfJoining,id,name){
this.employeeType=employeeType;
this.dateOfJoining=dateOfJoining;
Person.call(this,id,name);
}
Vendor.prototype=Object.create(Person.prototype);
Employee.prototype=Object.create(Person.prototype);
varvendorObj=newVendor("ABC","US","V-01","Vendor1");
alert(vendorObj.name);
Intheprecedingexample,weusedObject.create()toinheritthePersonobjecttotheVendorandEmployeeobjects.WhenevertheVendororEmployeeinstancesarecreated,theycanaccess
thepropertiesofthePersonobject.TheObject.create()methodautomaticallyinstantiatesaninstanceofanobjectdefinedastheparameterinitscallmethod.
PredefinedpropertiesofObject.create()
AnObject.create()methoddoesnotexecutethePersonfunction;instead,itwilljustsetthePersonfunctionasaprototypeofthecustomerfunction.Anotherrepresentationofthecustomerobject,containingpropertyasCustomerCode,isshowninthefollowingcode:
varcustomerObj=Object.create(Object.prototype,{
customerCode:{
value:"001",
enumerable:true,
writable:true,
configurable:true
}
});
alert(""+customerObj.customerCode);
Here,valueistheactualvaluerepresentingthecustomercode,whereasenumerable,writable,andconfigurablearepredefinedattributes.
Defininginheritanceusingclass
Intheexampleintheprevioussection,wehavealreadyseenhowtodefineclassesusingECMAScript6.JustlikeJava,wecaninheritaparentclassusingtheextendskeyword.
Anexampleofusingextendsisshownhere:
classPerson{
constructor(id,name){
this._id=id;
this._name=name;
}
getGetID(){returnthis._id;}
getGetName(){returnthis._name;}
}
classVendorextendsPerson{
constructor(phoneNo,location,id,name){
super(id,name);
this._phoneNo=phoneNo;
this._location=location;
}
logToConsole(){
alert("PersonIDis"+this.GetID);
}
}
varvendorObj=newVendor("XXX","US","V-01","Vendor1");
vendorObj.logToConsole();
WithECMAScript6,youcangetthetrueessenceofdeclaringstaticvariablesandmethodsintheclass.Let'shavealookatthefollowingexample,whichcontainsonestaticmethod,logToConsole(),andcallsitfromthecustomerclasswithoutinitializingitsobjectafterinheritingitfromthePersonclass:
classPerson{
staticlogToConsole(){
console.log("Hellodevelopers!");
}
}
classVendorextendsPerson{
}
Vendor.logToConsole();
Encapsulation
Intheexampleintheprevioussection,theVendorobjectdoesn'tneedtoknowtheimplementationofthelogToConsole()methodinthePersonclassandcanusethatmethod.TheVendorclassdoesn'tneedtodefinethismethodunlessoverridingforaspecificreason.Thisiscalledencapsulation,inwhichtheVendorobjectdoesn'tneedtoknowtheactualimplementationofthelogToConsole()methodandeachVendorobjectcanusethismethodtologtoconsole.Thisishowtheencapsulationisdone,throughwhicheveryclassisencapsulatedintoasingleunit.
Abstraction
Abstractionisusedtohidealltheinformationexceptthedata,whichisrelevantaboutanobject,toreducecomplexityandincreaseefficiency.ThisisoneofthecoreprinciplesofOOP.
InJavaScript,thereisnobuilt-insupportforabstractionanditdoesnotprovideanytypesuchasaninterfaceorabstracttocreateinterfacesorabstractclassestoachieveabstraction.However,therearecertainpatternsthroughwhichyoucanimplementabstractionbutstillitdoesnotrestrictandensuresthatalltheabstractmethodsarecompletelyimplementedbytheconcreteclassorfunction.
Let'shavealookatthefollowingexample,wherewehaveapersoncontrollerthattakesaconcreteobjectasaparameter,andthencallsitsspecificimplementation:
varperson=function(id,name){
this._id=id;
this._name=name;
this.showMessage=function(){};
}
varvendor=function(companyName,location,id,name){
this._companyName=companyName;
this._location=location;
person.call(this,id,name);
this.showMessage=function(){
alert("thisisVendor");
}
}
varemployee=function(employeeType,dateOfJoining,id,name){
this._employeeType=employeeType;
this._dateOfJoining=dateOfJoining;
person.call(this,id,name);
this.showMessage=function(){
alert("thisisEmployee");
}
}
vendor.prototype=Object.create(person.prototype);
employee.prototype=Object.create(person.prototype);
varpersonController=function(person){
this.personObj=person;
this.showMessage=function(){
this.personObj.showMessage();
}
}
varv1=newvendor("ABC","USA","V-01","Vendor1");
varp1=newpersonController(v1);
p1.showMessage();
Alternatively,withECMAScript6,wecanimplementthesamescenario,asshowninthefollowingcode:
classperson{
constructor(id,name){
this._id=id;
this._name=name;
}
showMessage(){};
}
classvendorextendsperson{
constructor(companyName,location,id,name){
super(id,name);
this._companyName=companyName;
this._location=location;
}
showMessage(){
alert("thisisVendor");
}
}
classemployeeextendsperson{
constructor(employeeType,dateOfJoining,id,name){
super(id,name);
this._employeeType=employeeType;
this._dateOfJoining=dateOfJoining;
}
showMessage(){
alert("thisisEmployee");
}
}
classpersonController{
constructor(person){
this.personObj=person;
}
showMessage(){
this.personObj.showMessage();
}
}
varv1=newvendor("ABC","USA","V-01","Vendor1");
varp1=newpersonController(v1);
p1.showMessage();
new.target
Thenew.targetpropertyisusedtodetectwhetherthefunctionoraclassiscalledusingthenewkeyword.Itreturnsareferencetothefunctionoraclassifitiscalled,otherwisenull.Consideringtheexampleintheprevioussection,wecanrestrictcreatingthecallobjectsofpersonbyusingnew.target:
classperson{
constructor(id,name){
if(new.target===person){
thrownewTypeError("CannotcreateaninstanceofPersonclassasits
abstractinnature");
}
this._id=id;
this._name=name;
}
showMessage(){};
}
Namespace
ECMAScript6introducedmodulesthroughwhichyoucandefinenamespacesandusetheexportandimportkeywordsbuttheyarestillindraftandnoimplementationsarepresentsofar.
However,withearlierversions,namespacescanbesimulatedusinglocalobjects.Forexample,hereisthesyntaxtodefinealocalobjectrepresentedasanamespaceandwecanaddfunctionsandobjectsinsideit:
varBusinessLayer=BusinessLayer||{};
Wecanthenaddfunctions,asshowninthiscode:
BusinessLayer.PersonManager=function(){
};
Moreover,morenestednamespacehierarchycanalsobedefined,asshowninthefollowingcode:
varBusinessLayer=BusinessLayer||{};
varBusinessLayer.Managers=BusinessLayer.Managers||{};
ExceptionhandlingJavaScriptisbecomingapowerfulplatformfordevelopinglargeapplications,andexceptionhandlingplaysanimportantroleinhandlingexceptionsinprogramsandpropagatethemwhereneeded.JustlikeC#oranyotherprogramminglanguage,JavaScriptprovidesthetry,catch,andfinallykeywordstoannotatecodeforhandlingerrors.JavaScriptprovidesthesamewayofusingthenestedtrycatchstatementsandconditionsforhandlingdifferentconditionsinthecatchblock.
Whenanexceptionoccurs,anobjectiscreatedthatrepresentstheerrorthrown.JustlikeC#,wehavedifferenttypesofexception,suchasInvalidOperationException,ArgumentException,NullException,andException.JavaScriptprovidessixerrortypes,whichareasfollows:
Error
RangeError
ReferenceError
SyntaxError
TypeError
URIError
Error
TheErrorobjectrepresentsgenericexceptionsandismostlyusedinreturninguser-definedexceptions.AnErrorobjectcontainstwoproperties,namelynameandmessage.Namereturnsthetypeoferrorandmessagereturnstheactualerrormessage.Wecanthrowerrorexceptions,asshownhere:
try{}catch{thrownewError("Someerroroccurred");}
RangeError
TheRangeErrorexceptionisthrowniftherangeofanynumberisexceeded.Forexample,creatinganarraywithanegativelengthwillthrowRangeError:
vararr=newArray(-1);
ReferenceError
TheReferenceErrorexceptionoccurswhenaccessinganobjectorvariablethatdoesnotexist;forexample,thefollowingcodewillthrowaReferenceErrorexception:
functiondoWork(){
arr[0]=1;
}
SyntaxError
Asthenamestates,SyntaxErroristhrownifthereisanysyntaxproblemintheJavaScript
code.So,ifsomeclosingbracketismissing,loopsarenotstructuredproperly,andsoon,thiswillcomeundertheSyntaxErrorcategory.
TypeError
TheTypeErrorexceptionoccurswhenavalueisnotoftheexceptedtype.ThefollowingcodethrowsaTypeErrorexceptionastheobjectistryingtocallamethodthatdoesnotexist:
varperson={};
person.saveRecord();
URIError
TheURIErrorexceptionoccurswithencodeURI()anddecodeURI()whenaninvalidURIisspecified.Thefollowingcodethrowsthiserror:
encodeURIComponent("-");
ClosuresClosuresareoneofthemostpowerfulfeaturesofJavaScript.Closuresprovideawaytoexposeinnerfunctionsthatareinsidethebodyofotherfunctions.Afunctioncanbetermedaclosurewhenoneoftheinnerfunctionsismadeaccessibleoutsidethefunctioninwhichitwascontainedandcanbeexecutedaftertheouterfunctionisexecutedandusethesamelocalvariables,parameters,andfunctiondeclarationswhentheouterfunctionwascalled.
Let'shavealookatthefollowingexample:
functionIncrementor(){
varx=0;
returnfunction(){
x++;
console.log(x);
}
}
varinc=Incrementor();
inc();
inc();
inc();
Thisisasimpleclosureexample,inwhichinc()becomestheclosurethatreferencestheinnerfunction,whichincrementsthexvariabledefinedintheouterfunction.Thexvariablewillbeincrementedoneachcallandthevaluewillbecome3onthelastcall.
Aclosureisaspecialkindofobjectthatcombinesthefunctionandtheenvironmentinwhichthatfunctionwascreated.So,callingitmultipletimeswillusethesameenvironmentandthevaluesbeingupdatedinthepreviouscall.
Let'shavealookatanotherexample,wherewehaveatablegeneratorfunctionthattakesatablenumberandreturnsthefunction,whichcanbeusedtogettheresultofanynumbermultiplicationwiththetablenumbersuppliedonthefirstcall:
functiontableGen(number){
varx=number;
returnfunction(multiplier){
varres=x*multiplier;
console.log(x+"*"+multiplier+"="+res);
}
}
vartwotable=tableGen(2);
varthreetable=tableGen(3);
twotable(5);
threetable(6);
Theresultantvaluesaftercallingthetwotable()andthreetable()methodswillbe10and
18.ThisisbecausethetwoTable()functionobjectwasinitializedbypassing2astheparametertothetableGen()function.ThistableGen()functionthenstoresthevaluepassedasaparameterinthexvariableandmultipliesitwiththevariablepassedinthesecondcallwhenitisexecutedthroughthetwoTable()andthreeTable()methodcalls.
Hence,theoutputofthetwoTable(5)functioncallwillbe10,asshowninthefollowingscreenshot:
Theoutputofthesecondstatement,threeTable(6),willbe18,asshowninthefollowingscreenshot:
Practicaluse
Wehaveseenwhatclosuresareandhowwecanimplementthem.However,let'sconsidertheirpracticalimplications.Closuresletyouassociatesomeenvironmentwithafunctionthatoperateswithinthatenvironmentordata.
InJavaScript,functionsmostlyexecuteonanyeventortriggeronanyactiontakenbytheuser.Let'shavealookatthefollowingexampleofthepracticaluseofclosurestologmessagesonaconsoleanddialogwindow:
<body>
<inputtype="text"id="txtMessage"/>
<buttonid="consoleLogger">LogtoConsole</button>
<buttonid="dialogLogger">LogtoDialog</button>
<script>
functiongetLogger(loggerType){
returnfunction(){
varmessage=document.getElementById("txtMessage").value;
if(loggerType=="console")
console.log(message);
elseif(loggerType=="dialog")
alert(message);
}
}
varconsoleLogger=getLogger("console");
vardialogLogger=getLogger("dialog");
document.getElementById("consoleLogger").onclick=consoleLogger;
document.getElementById("dialogLogger").onclick=dialogLogger;
</script>
</body>
Intheprecedingexample,wehavetwologgerclosures:onethatlogstotheconsoleandtheotheronetoapop-updialogwindow.Wecaninitializetheseclosuresandusethemthroughoutourprogramtologmessages.
JavaScripttypedarraysClient-sidedevelopmentinJavaScripthasbecomeapowerfulplatformandtherearecertainAPIsandlibrariesavailablethatallowyoutoworkwithmediafiles,Websockets,andsoon,andhandledatainbinary.Whenworkingwithbinarydata,itisrequiredtosaveitinitsownspecificformat.Herecomestheroleoftypedarrays,whichallowdeveloperstomanipulatedatainarawbinaryformat.
Typedarrayarchitecture
Typedarrayskeepthedataintwoportions,namelybufferandview.Buffercontainstheactualdatainbinarybutitcannotbeaccessiblewithoutview.Viewtellstheactualmetadatainformationandcontextaboutthebuffer,suchasdatatype,startingoffset,andnumberofelements.
Thearraybuffer
Thearraybufferisadatatypethatisusedtorepresentbinarydata.Itscontentcannotbemanipulateduntilitisassignedtoaview,whichrepresentsthebufferinaspecificformatandperformsmanipulationonthedata.
Therearedifferenttypesoftypearrayviews,whichareasfollows:
Type Sizeinbytes Description
Int8Array 1 Thisarrayis8-bitsignedinteger.
UInt8Array 1 Thisarrayis8-bitunsignedinteger.
Int16Array 2 Thisarrayis16-bitsignedinteger.
UInt16Array 2 Thisarrayis16-bitunsignedinteger.
Int32Array 4 Thisarrayis32-bitsignedinteger.
UInt32Array 4 Thisarrayis32-bitunsignedinteger.
Float32Array 4 Thisarrayis32-bitIEEEfloatingpointnumber.
Float64Array 8 Thisarrayis64-bitIEEEfloatingpointnumber.
UInt8ClampedArray 1 Thisarrayis8-bitunsignedinteger(clamped).
Now,let'sgothroughanexampletoseehowwecanstoredatainabufferandmanipulateitthroughaview.
Creatingabuffer
Firstofall,weneedtocreateabuffer,asshowninthiscode:
varbuffer=newArrayBuffer(32);
Theprecedingstatementallocatesthememoryfor32bytes.Nowwecanuseanyofthetypearrayviewstomanipulateit:
varint32View=newInt32Array(buffer);
Andfinally,wecanaccessthefields,asshownhere:
for(vari=0;i<int32View.length;i++){
int32View[i]=i;
}
Thiscodewillmakeeightentriesintotheview,from0to7.Theoutputwilllookasfollows:
01234567
Thesamebuffercanalsobemanipulatedwiththeotherviewtypes.Forexample,ifwewantedtoreadthepopulatedbufferwitha16-bitarrayview,theresultwillbelikethis:
varInt16View=newInt16Array(buffer);
for(vari=0;i<int16View.length;i++){
console.log(int16View[0]);
}
Theoutputwilllookasfollows:
0010203040506070
Thisishoweasilywecanmanipulatesinglebufferdatawithmultipleviewsofdifferenttypesandinteractwiththedataobjectscontainingmultipledatatypes.
Maps,sets,weakmaps,andweaksetsMaps,weakmaps,sets,andweaksetsareobjectsthatrepresentcollections.Mapsarekeyedcollectionsthatholdvaluesinnamevaluepairs,whereassetsstoreuniquevaluesofanytype.Wewilldiscusseachoftheminthenextsections.
Mapsandweakmaps
AMapobjectprovidesasimplekey/valuemapanditeratesitbasedontheinsertion.Thefirstinsertedvaluewillberetrievedfirst.Weakmapsarenon-enumerableandholdobjecttypesonly.Noprimitivetypesareallowedinweakmapsandeachkeyrepresentsanobject.Let'shavealookatthefollowingexampleofusingamapforcurrencies:
varcurrencies=newMap();
currencies.set("US","USDollar");
currencies.set("UK","BritishPound");
currencies.set("CA","CanadianDollar");
currencies.set("PK","Rupee");
currencies.set("UAE","Dirham");
for(varcurrencyofcurrencies){
console.log(currency[0]+"currencyis"+currency[1]);
}
SomeotherpropertiesandmethodsavailableontheMapobjectareshowninthefollowingcode:
currencies.get("UAE");//returnsdirham
currencies.size;//returns5
currencies.has("PK")//returnstrueiffound
currencies.delete("CA")//deleteCanadafromthelist
Insteadofsimpleprimitivevalues,weakmapsholdobjectsandtheirkeysarerepresentedasweakkeys.Thisisbecauseifthereisnoreferencetotheobjectstoredinaweakmapvalueandgotcollectedingarbage,thekeywillbecomeweak.Itisnormallyusedtostoreprivatedataforanobjectortohideimplementationdetails.
Welearnedintheprevioussectionthateverythingthatisexposedontheinstancelevelandprototypelevelispublic.ThepracticalexamplecontainingafunctiontoauthenticateauserfromaTwitteraccountisshowninthefollowingcode.Foropenauthentication(OAuth),Twitterneedstwokeys:theconsumerAPIkeyandasecretkey.Wedon'twantedtoexposeandlettheusertochangethisinformation.Therefore,wehavekeptthisinformationusingweakmaps,andthenretrieveditintheprototypefunctiontoauthenticatetheuser:
varauthenticatorsecrets=newWeakMap();
functionTwitterAuthenticator(){
constloginSecret={
apikey:'testtwitterapikey',
secretkey:'testtwittersecretkey'
};
authenticatorsecrets.set(this,loginSecret);
}
TwitterAuthenticator.prototype.Authenticate=function(){
constloginSecretVal=authenticatorsecrets(this);
//todoauthenticatewithtwitter
};
Setsandweaksets
Setsarethecollectionsofvalueswhereeachvalueshouldbeunique.So,forexample,ifatanyindexyouhaveavalue,1,alreadydefined,youcannotinsertitintothesamesetinstance.
Setsarenottypedandyoucanputanydata,irrespectiveofanydatatype:
varset=newSet();
set.add(1);
set.add("HelloWorld");
set.add(3.4);
set.add(newDate());
Ontheotherhand,weaksetsarecollectionsofuniqueobjectsandnotthearbitraryvaluesofanytype.Justlikeweakmaps,ifthereisnootherreferencetotheobjectstored,itwillbedisposedandgarbagecollected.Similartoweakmaps,theyarenotenumerable:
varno={id:1};
varabc={alphabets:['a','b','c']};
varx=newWeakSet();
x.add(no);
x.add(abc);
Thestrictmode
ThestrictmodeisaliteralexpressionintroducedinECMAScript5.ItisusedtowriteasecureJavaScriptandthrowserrorsifthereareanyminorerrorsonyourscriptanddoesn'toverlookthem.Secondly,itrunsfasterthanthenormalJavaScriptcodebecauseitsometimesfixesmistakes,whichhelpsJavaScriptenginestoperformoptimizationsandmakeyourcoderunfaster.
Wecaninvokethestrictmodeonaglobalscriptlevelorafunctionlevel:
"usestrict;"
Forexample,inthefollowingcode,itwillthrowanerrorasthexvariableisnotdefined:
"usestrict";
x=100;
functionexecute(){
"usestrict;"
x=100;
}
Forlargerapplications,it'sabetterchoicetousethestrictmode,whichwillthrowanerrorifsomethingismissingornotdefined.Thelistofscenarioswhereusingthestrictmodewillresultinanerrorisshowninthefollowingtable:
Code Reasonforerror
x=100; Inthiscode,variableisnotdeclared.
x={id:1,name:'ABC'}; Inthiscode,objectvariableisnotdeclared.
function(x,x){}Duplicatingtheparameternamecausedtheerrorinthiscode.
varx=0001 Inthiscode,octalnumericliteralsareused.
varx=\0001 Escapeisnotallowed,sotheerroroccurred.
varx={getval(){return
'A'}};
x.val='B'
Writingtoagetvaluecausedtheerrorinthiscode.
deleteobj.prototype;Deletingobjectprototypeisnotallowed,sotheerroroccurred.
varx=2;
deletex;
Deletingavariableisnotallowed,sotheerroroccurred.
Moreover,therearecertainreservedkeywords,suchasarguments,eval,implements,interface,let,package,private,protected,public,static,andyield,whicharenotallowedaswell.
SummaryInthischapter,welearntaboutsomeadvancedconceptsofJavaScript,suchashoistedvariablesandtheirscope,propertydescriptors,OOP,closures,typedarraystostoretypesofdata,andexceptionhandling.Inthenextchapter,wewilllearnaboutthemostextensivelyusedlibrary,jQuery,toperformDOMtraversalandmanipulation,eventhandling,andmoreinaverysimpleandeasyway.
Chapter3.UsingjQueryinASP.NETWewillstartoffthischapterwithashortintroductiontojQuery.jQueryisaJavaScriptlibrarydevelopedtoprovideabetterdevelopmentexperienceandafastercodingexperiencebywritinglesscodetodocomplexoperationsmuchfastercomparedtoplainvanillaJavaScript.However,JavaScriptisstilltherewhenwritingcustomscriptsforspecificreasons.So,jQueryhelpsyouperformDOMmanipulation,selectingelementsbasedonclass,elementname,andsoon,andprovidesabettereventhandlingmodeltomakeitsimplerfordeveloperstouseintheirroutineprojects.
ComparedtoJavaScript,anotheradvantageisthecrossbrowserissues.Itoffersconsistentbehavioracrossbrowsers.JavaScript,ontheotherhand,isimplementeddifferentlybyeachbrowser.Also,inordertohandlecross-browserissuesinJavaScript,adevelopertendstowritesomeconditionallogictocheckwhatbrowserversionJavaScriptisrunningonandhandleitaccordingly;whereasjQueryhandlesalltheheavyliftingofwhatthebrowserisandprovidesconsistentbehavior.
SomepowerfulfeaturesofjQuerythatwewilldiscussinthecurrentchapterareasfollows:
WorkingwithselectorsManipulatingtheDOMelementsHandlingevents
GettingstartedwithjQueryThejQuerylibrarycanbedownloadedfromhttp://jquery.com.ThelatestversionofjQueryis3.0.0andyoucanusethislibraryifyouaretargetingmodernbrowsers;forexample,IE9andMicrosoftEdgesupportthisversion.Forolderversions—forexample,IE6-8—youcandownloadjQuery1.x.
OncejQueryisdownloaded,youcanaddittoyourprojectandreferenceit,asshownhere:
<head>
<scriptsrc="~/scripts/jquery.js"></script>
</head>
<body>
</body>
UsingacontentdeliverynetworkInsteadofloadingjQueryfromyourserver,wecanalsoloaditfromsomeotherserver,suchastheMicrosoftserverorGoogleserver.Theseserversarecalledthecontentdeliverynetwork(CDN)andtheycanbereferencedasshownhere:
ReferencingtheMicrosoftCDN:
<scriptsrc="http://ajax.microsoft.com/ajax/jquery/jquery-2.0.js">
</script>
ReferencingtheGoogleCDN:
<scriptsrc="http://ajax.googleapis.com/ajax/libs/jquery/2.0/jquery.min.js">
</script>
TheuseofCDN
Actually,theseCDNsareverycommonandmostofthesitesalreadyusethem.WhenrunninganyapplicationthatreferencesaCDN,therearechancesthatsomeotherwebsitemighthavealsousedthesameCDNofMicrosoftorGoogle,andthesamefilemightbecachedontheclientside.Thisincreasesthepagerenderingperformance.Also,downloadingthejQuerylibraryagainfromyourlocalserverusesthecachedversionofCDN.Moreover,MicrosoftandGooglehavedifferentserversavailable,basedondifferentregions,andtheuserwillgetsomespeedbenefitstoowhenusingitfromaCDN.
However,therearecertaincaseswhentheCDNmightbedown,andinthiscase,youmighthavetorefertoanddownloadscriptsfromyourownserver.Tohandlethisscenario,wecanspecifythefallbackURL,whichdetectswhetherithasbeendownloadedfromCDN;otherwise,itdownloadsfromthelocalserver.Wecanusethefollowingscripttospecifythefallback:
<scriptsrc="//ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js">
</script>
<script>if(!window.jQuery){document.write('<script
src="/path/to/your/jquery"><\/script>');}
</script>
Thewindow.jQueryinstancetellsuswhetherjQueryisloaded;otherwise,itwritesthescriptontheDOM,whichreferstothelocalserver.
Alternatively,inASP.NETCore,wecanusetheasp-fallback-srcattributetospecifythefallbackURL.ASP.NETCore1.0providesawiderangeoftaghelpers.ComparedtotheHTMLhelpers,thesehelperscanbeusedjustbyaddingtheHTMLattributestothepageelementsandtheyofferdevelopersthesameexperienceaswritingthefrontendcode.
ThesamecodecanbewritteninasimplewaytohandlethefallbackscenariosinASP.NET:
<scriptsrc="https://ajax.aspnetcdn.com/ajax/jquery/jquery-2.1.4.min.js"
asp-fallback-src="~/lib/jquery/dist/jquery.min.js"
asp-fallback-test="window.jQuery">
</script>
InASP.NETCore,thereisonemoretaghelper,<environment>,whichcanbeusedtoloadscriptsbasedonthecurrentenvironmentsetinthelaunchSettings.jsonfile:
Basedonthecurrentenvironmentsetintheproject'sprofile,wecanloadscriptstocatertodebuggingandproductionscenarios.Forexample,inaproductionenvironment,preferably,weusedtospecifytheminifiedversionoftheJavaScriptlibrariesasitremovesallthewhitespacesandrenamesthevariablestomakeitmorecompressedinsizetoloadfast.However,fordebuggingpurposes,thestandardnon-minifiedversionismuchbetterasfarasthedevelopmentexperienceisconcerned.Therefore,wecanusetheenvironmenttaghelper,asshowninthefollowingcode,toloadtheminifiedversionforproductionandstandardwhendevelopinganapplication:
<environmentnames="Development">
<scriptsrc="~/lib/jquery/dist/jquery.js"></script>
<scriptsrc="~/lib/bootstrap/dist/js/bootstrap.js"></script>
<scriptsrc="~/js/site.js"asp-append-version="true"></script>
</environment>
<environmentnames="Staging,Production">
<scriptsrc="https://ajax.aspnetcdn.com/ajax/jquery/jquery-2.1.4.min.js"
asp-fallback-src="~/lib/jquery/dist/jquery.min.js"
asp-fallback-test="window.jQuery">
</script>
<scriptsrc="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.5/bootstrap.min.js"
asp-fallback-src="~/lib/bootstrap/dist/js/bootstrap.min.js"
asp-fallback-test="window.jQuery&&window.jQuery.fn&&
window.jQuery.fn.modal">
</script>
<scriptsrc="~/js/site.min.js"asp-append-version="true"></script>
</environment>
ThedocumentreadyeventThejQuerylibrarycanbeaccessedthrougha$signorsimplybywritingjQuery.However,preferably,developersaccessitusingadollarsign.ItalsoprovidesawaytocatchaneventwhentheDOMhierarchyiscompletelyloaded.ThismeansthatoncetheDOMstructureisloaded,youcancatchthiseventtoperformdifferentoperations,suchasassociatingtheCSSclasswithcontrolsandmanipulatingcontrolvalues.TheDOMhierarchyisnotdependentonthetheimagesorCSSfileswhenthepageisloadingandthedocumentreadyeventisraisedinparallelirrespectiveofwhethertheimagesorCSSfilesaredownloadedornot.
Wecanusethedocumentreadyevent,asshowninthiscode:
<html>
<head>
<scriptsrc="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.12.0.min.js">
</script>
<script>
$(document).ready(function(){
console.log("Documentislo aded");
});
</script>
</head>
</html>
Asexplainedintheprecedingcode,$isthewayofaccessingajQueryobject.Ittakesadocumentobject,whichispassedasaparameter,whereasreadycheckswhetherthedocumentobjectmodelhierarchyisloadedcompletelyonce.Finally,ittakesananonymousfunctioninwhichwecanwritetheoperationthatweneedtoperform.Intheprecedingexample,wearejustdisplayingasimpletextmessagewhentheDOMhierarchygetsloaded.
ThejQueryselectorsForDOMmanipulation,thejQueryselectorsplayanimportantroleandprovideabetterandeasyone-lineapproachtoselectanyelementfromDOMandmanipulateitsvaluesandattributes,forexample,searchingalistofelementswithaspecificCSSclassiseasierwiththejQueryselectors.
ThejQueryselectorscanbewrittenwithadollarsignandparentheses.WecanusethejQueryselectorstoselectelementsbasedontheelement'sID,tagname,class,attributevalue,andinputnodes.Wewilllookintotheseelementsonebyonewithapracticalexampleinthenextsection.
SelectingtheDOMelementsusingtheID
ThefollowingexampleshowsyouthewayofselectingadivelementwithitsID:
<!DOCTYPEhtml>
<html>
<head>
<scriptsrc="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.12.0.min.js">
</script>
<script>
$(document).ready(function(){
$('#mainDiv').html("<h1>HelloWorld</h1>");
});
</script>
</head>
<body>
<divid="mainDiv">
</div>
</body>
</html>
Afterselectinganelement,wecancallvariousmethodstosetvalues.Inthegivenexample,wecalledthehtml()methodthattakesthehtmlstringandsetsHelloWorldasthefirstheading.Ontheotherhand,thehtmlcontentcanberetrievedbycallingthiscode:
<script>
$(document).ready(function(){
varhtmlString=$('#mainDiv').html();
});
</script>
SelectingtheDOMelementsusingTagName
InJavaScript,wecanretrievetheDOMelementsbycallingdocument.getElementsByTagName().Thiselementreturnsanarrayofelementsmatchedwiththetagname.InjQuery,thiscanbeachievedinaneasierwayandthesyntaxisquitesimple.
Considerthefollowingexample:
$('div')//returnsallthedivelements
Let'shavealookatthefollowingexampletoclarifyourunderstanding:
<!DOCTYPEhtml>
<html>
<head>
<scriptsrc="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.12.0.min.js">
</script>
<script>
$(document).ready(function(){
$('div').css('text-align,'left');
});
</script>
</head>
<body>
<divid="headerDiv">
<h1>Header</h1>
</div>
<divid="mainDiv">
<p>Main</p>
</div>
<divid="footerDiv">
<footer>Footer</footer>
</div>
</body>
</html>
Theprecedingexamplesetsallthedivchildcontrolsalignmenttotheleft.Ifyounotehere,wedidn'thavetoloopthroughallthedivcontrolstosetthebackgroundcolorandthestylehasbeensetonall.However,therearecertaincasesinwhichyoumightneedtosetdifferentvaluesbasedontheindexofeachelementresiding,andthiscanbedoneusingtheeach()functionondiv.Forexample,thefollowingscriptshowsyouthewayofassigninganindexvalueasanhtmlstringoneachdivcontrolusingtheeachfunction:
<script>
$(document).ready(function(){
$('div').each(function(index,element){
$(element).html(index);
});
});
</script>
Eachfunctiontakesafunctionwiththeindexandelementsasaparameter.Wecanaccesseachelementusingadollarsign,asshownintheprecedingcode,andsettheindexasthecontentbycallingthehtmlmethod.Theoutputwillbesimilartothefollowingscreenshot:
Let'shavealookatanotherexamplethatdisplaysthecontentofeachdivcontrolinaconsolewindow.Here,theeach()functiontakesnoparametersandeachitemintheloopcanbeaccessedthroughthethiskeyword:
<!DOCTYPEhtml>
<html>
<head>
<scriptsrc="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.12.0.min.js">
</script>
<script>
$(document).ready(function(){
$('div').each(function(){
alert($(this).html());
});
});
</script>
</head>
<body>
<divid="headerDiv">
<h1>Demo</h1>
</div>
<divid="mainDiv">
<p>ThisisademoofusingjQueryforselectingelements</p>
</div>
<divid="footerDiv">
<footer>Copyright-JavaScriptfor.NetDevelopers</footer>
</div>
</body>
</html>
Theoutputwillbeasfollows:
Therearevariousothermethodsavailable,whichyoucanrefertointhejQuerydocumentation.Therefore,withselectors,wecansearchanyelementinafasterandmoreefficientway.
Anotherexampleisselectingmultipleelementsusingthetagname,asfollows.
<html>
<head>
<scriptsrc="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.12.0.min.js">
</script>
<script>
$(document).ready(function(){
$('div,h1,p,footer').each(function(){
console.log($(this).html());
});
});
</script>
</head>
<body>
<divid="headerDiv">
<h1>Demo</h1>
</div>
<divid="mainDiv">
<p>ThisisademoofusingjQueryforselectingelements</p>
</div>
<divid="footerDiv">
<footer>Copyright-JavaScriptfor.NetDevelopers</footer>
</div>
</body>
</html>
Theresultwillbeasfollows.Eachitem'sinnerhtmlcodewillbeloggedintheconsole:
Selectingnodesbytheclassname
TheclassnameselectorisquitesimilartotheIDselector;theonlydifferenceisthatitusesaperiodcharacter,.,beforetheclassname.ItfacilitatestraversingalltheDOMelementsandfindingtheelementsthathavethesameclassnamespecifiedintheselector.Itcanbeusedasfollows:
$(.classname);
Let'shavealookatthefollowingexamplethatshowsyouthewayofselectingelementsbasedontheclassnameselector.Inthefollowingcodesnippet,weusethebootstrapthemeandapplydifferentclassestothebuttons.Withthehelpoftheclassnameselector,wecanselectcontrolsandupdatetheclassname.Thefollowingexamplewillreturntwoelementsbasedontheselectioncriteriaspecified:
<!DOCTYPEhtml>
<html>
<head>
<linkrel="stylesheet"type="text/css"href="Content/bootstrap.css"/>
<scriptsrc="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.12.0.min.js">
</script>
<script>
$(document).ready(function(){
varlst=$('.btn-primary');
alert(lst.length);
});
</script>
</head>
<body>
<divclass="container">
<p></p>
<buttontype="button"class="btnbtn-primaryactive">Edit</button>
<buttontype="button"class="btnbtn-primarydisabled">Save</button>
<buttontype="button"class="btnbtn-danger"
value="Cancel">Cancel</button>
</div>
</body>
</html>
Unlikeaccessingclassnames,wecanrestrictthesearchbyspecifyingthetagnamebeforetheperiodandclassname.Youcanuse$('button.active')tosearchforallthebuttonsthatareactive.
Selectingbytheattributevalue
Incertaincases,youmayhavetoselecttheelementsbasedontheattributeoritsvalue.ThejQuerylibraryprovidesaveryconcisewayofsearchingelementsbasednotonlyontheattribute,butitsvalueaswell.
Thesyntaxofusingthisselectorisspecifyingtheelementnamefollowedbyasquarebracketcontainingtheattributenameandvalue,whichisoptional:
$(elementName[attributeName=value])
Forexample,thefollowingcodeselectsalltheelementsthathavetypeasanattribute:
<!DOCTYPEhtml>
<html>
<head>
<linkrel="stylesheet"type="text/css"href="Content/bootstrap.css"/>
<scriptsrc="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.12.0.min.js">
</script>
<script>
$(document).ready(function(){
varlst=$('input[type]');
console.log(lst.length);
});
</script>
</head>
<body>
<divclass="container">
<p></p>
<inputtype="text"value="helloworld"/>
<inputtype="text"value="thisisademo"/>
<inputtype="button"value="Save"/>
</div>
</body>
</html>
Inthisexample,wehavethreeinputcontrolsthathaveatypeattribute.So,theresultwillbe3.Inthesameway,ifyouwanttosearchfortheelementsthathaveavalueequaltohelloworld,wecanusethefollowingcode:
<script>
$(document).ready(function(){
varlst=$('input[value="helloworld"]');
alert(lst.length);
});
</script>
Onethingtonoteisthattheattributevalueiscasesensitive,andso,withthisexpression,youshouldconsidertheexactcaseastheattributevalue.However,thereareotherwaysaswell,thatis,using^tosearchavaluethatcontains,starts,orendswithparticulartext.
Let'shavealookatthefollowingexample,alert,whichisbasedonsearchingavaluethatstartswithanexpression:
<!DOCTYPEhtml>
<html>
<head>
<linkrel="stylesheet"type="text/css"href="Content/bootstrap.css"/>
<scriptsrc="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.12.0.min.js">
</script>
<script>
$(document).ready(function(){
varlst=$('input[value^="Pr"]');
alert(lst.length);
});
</script>
</head>
<body>
<divclass="container">
<p></p>
<inputtype="text"value="Product1"/>
<inputtype="text"value="Thisisadescription"/>
<inputtype="button"value="Process"/>
</div>
</body>
</html>
Ontheotherhand,wecanalsosearchavaluethatendswithatextusingthe$symbol.Hereisthecodetosearchthetextthatendswith1:
<script>
$(document).ready(function(){
varlst=$('input[value$="1"]');
alert(lst.length);
});
</script>
Finally,searchingforatextthatcontainssometextcanbeachievedusing*andhereisthecodetorunthisexample:
<script>
$(document).ready(function(){
varlst=$('input[value*="ro"]');
alert(lst.length);
});
</script>
Selectinginputelements
InputcontrolsinHTMLhaveawiderangeofdifferentcontrols.Controlssuchastextarea,button,input,select,image,andradioareinputcontrols.Thesecontrolsarenormallyusedinform-basedapplications.Therefore,jQueryspecificallyprovidestheselectingoptiontoselectinputcontrolsbasedondifferentcriteria.
Thisselectorstartswithadollarandtheinputkeywordfollowedbytheattributeandvalue:
$(':input[attributeName=value]);
However,intheprevioussection,wehavealreadyseenhowtosearchanyelementwiththeattributenameandvalue.So,ifwewanttosearchalltheinputcontrolswiththetypethatequalstotext,itisachievable.
Thisselectorislessperformance-efficientinparticularscenariosandsearchesoutallthecontrolsthatareapartoftheinputgroupandfindstheattributewithitsvalue;whereas,thisselectorwillonlysearchintheinputcontrols.Whenwritingprograms,usingthismethodisabetterchoiceifsomethingisspecificallytargetingtheinputcontrolproperties.
Let'shavealookatthefollowingexampleinASP.NETCoreMVC6thatappliestheCSSpropertiesoncethedocumentisloadedcompletely:
@modelWebApplication.ViewModels.Book.BookViewModel
@{
ViewData["Title"]="View";
}
<scriptsrc="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.12.0.min.js">
</script>
<script>
$(document).ready(function(){
$(':input').each(function(){
$(this).css({'color':'darkred','background-color':'ivory','font-
weight':'bold'});});
});
</script>
<formasp-action="View"class="container">
<br/>
<divclass="form-horizontal">
<divclass="form-group">
<labelasp-for="Name"class="col-md-2control-label"></label>
<divclass="col-md-10">
<inputasp-for="Name"class="form-control"/>
<spanasp-validation-for="Name"class="text-danger"/>
</div>
</div>
<divasp-validation-summary="ValidationSummary.ModelOnly"class="text-
danger"></div>
<divclass="form-group">
<labelasp-for="Description"class="col-md-2control-label"></label>
<divclass="col-md-10">
<textareaasp-for="Description"class="form-control"></textarea>
<spanasp-validation-for="Description"class="text-danger"/>
</div>
</div>
<divclass="form-group">
<divclass="col-md-offset-2col-md-10">
<inputtype="submit"value="Save"class="btnbtn-primary"/>
</div>
</div>
</div>
</form>
<div>
<aasp-action="Index">BacktoList</a>
</div>
Theoutputoftheprecedingcodesnippetisasfollows:
Selectingalltheelements
ThejQuerylibraryprovidesyouwithaspecialselectorthatbringsthecollectionofalltheelementsdefinedinaDOM.Insteadofthestandardcontrols,italsoreturnstheelements,suchas<html>,<head>,<body>,<link>,and<script>.
Thesyntaxofgettingalltheelementsis$("*")andthefollowingexamplelistsdownalltheelementsoftheDOMonthebrowser'sconsole:
<!DOCTYPEhtml>
<html>
<head>
<linkrel="stylesheet"type="text/css"href="Content/bootstrap.css"/>
<scriptsrc="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.12.0.min.js">
</script>
<script>
$(document).ready(function(){
$("*").each(function(){
console.log($(this).prop('nodeName'));
});
});
</script>
</head>
<body>
<formclass="container">
<divclass="form-group">
<label>Name</label>
<inputtype="text"class="form-control"/>
</div>
</form>
</body>
</html>
Intheprecedingcode,weusedthepropmethodthattakesthepropertynametodisplayelementnames.Here,inthepropmethod,wecanuseeithertagNameornodeNametodisplaynametypes.Finally,onthebrowser'sconsole,aloginpagewillbedisplayed,asfollows:
Selectingthefirstandlastchildelements
ThejQuerylibraryprovidesspecialselectorstoselectallthefirstelementsorlastelementsoftheirparentelement.
Thesyntaxofselectingthefirstchildofalltheparentelementsisasfollows:
$(elementName:first-child);
Thesyntaxofselectingthelastchildofalltheparentelementsisasfollows:
$(elementName:last-child);
Thefollowingexampleshowsyouthewayofchangingthefontstyleofthefirstandlastchildoftheselectoptions:
<!DOCTYPEhtml>
<html>
<head>
<linkrel="stylesheet"type="text/css"href="Content/bootstrap.css"/>
<scriptsrc="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.12.0.min.js">
</script>
<script>
$(document).ready(function(){
$('option:first-child').css('font-style','italic');
$('option:last-child').css('font-style','italic');
alert(lst.length);
});
</script>
</head>
<body>
<select>
<option>--select--</option>
<option>USA</option>
<option>UK</option>
<option>Canada</option>
<option>N/A</option>
</select>
</body>
</html>
Theoutputwillbeasfollows:
ThecontainsselectorinjQuery
ThecontainsselectorisusedtofindthetextintheHTMLcontainerelements,suchas<div>and<p>.Thisselectorsearchesalltheelementsofaspecifictypeandfindsthetextpassedasaparametertothecontains()function.Anexamplethatdisplaysthetextofthedivelementsthatcontainsthetextisshowninthefollowingcode.Thisiscase-sensitive,somakesuretosupplythecorrectcasewhensearching.
Thefollowingcodewilldisplayanalertwiththevalue,2,asitfindstwodivelementscontainingthetext,demo:
<!DOCTYPEhtml>
<html>
<head>
<linkrel="stylesheet"type="text/css"href="Content/bootstrap.css"/>
<scriptsrc="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.12.0.min.js">
</script>
<script>
$(document).ready(function(){
varlst=$('div:contains("demo")');
alert(lst.length);
});
</script>
</head>
<body>
<div>
Thisisasampledemoforcontainsselector
</div>
<div>
Demooftheselector
</div>
<div>
Sampledemo
</div>
</body>
</html>
Selectingtheevenandoddrowsselectors
Thesetypesofselectorsworkontherowsinatableandareusuallyusedtoprovidespecialformattingtotheoddorevenrowsbychangingthecolorofeachoddrowtomakeitlookmorelikeagrid.Wecanusethistypeofselectorwiththefollowingsyntax:
$('tr:even');
$('tr:odd');
Let'shavealookatthefollowingexampletochangealltherowcolorsinatabletogray:
<!DOCTYPEhtml>
<html>
<head>
<linkrel="stylesheet"type="text/css"href="Content/bootstrap.css"/>
<scriptsrc="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.12.0.min.js">
</script>
<script>
$(document).ready(function(){
$('tr:odd').css('background-color','grey');
});
</script>
</head>
<body>
<table>
<thead>
<tr><th>ProductName</th><th>Description</th><th>Price</th></tr>
</thead>
<tbody>
<tr><td>Product1</td><td>ThisisProduct1</td><td>$100</td></tr>
<tr><td>Product2</td><td>ThisisProduct2</td><td>$500</td></tr>
<tr><td>Product3</td><td>ThisisProduct3</td><td>$330</td></tr>
<tr><td>Product4</td><td>ThisisProduct4</td><td>$50</td></tr>
<tr><td>Product5</td><td>ThisisProduct5</td><td>$1000</td></tr>
<tr><td>Product6</td><td>ThisisProduct6</td><td>$110</td></tr>
<tr><td>Product7</td><td>ThisisProduct7</td><td>$130</td></tr>
<tr><td>Product8</td><td>ThisisProduct8</td><td>$160</td></tr>
<tr><td>Product9</td><td>ThisisProduct9</td><td>$20</td></tr>
<tr><td>Product10</td><td>ThisisProduct10</td><td>$200</td></tr>
</tbody>
</table>
</body>
</html>
ManipulatingDOMInthissection,wewillseesomeexamplesofmanipulatingDOMthroughthejQuerymethods.ThejQuerylibraryprovidesanextensivelibraryofperformingdifferentoperationsontheDOMelements.Wecaneasilymodifytheelementattributes,applystyles,anditeratethroughdifferentnodesandproperties.Wehavealreadyseensomeexamplesintheprevioussection,andthissectionwillfocusontheDOMmanipulationspecifically.
Modifyinganelement'sproperties
Whenworkingwithaclient-sidescriptinglanguage,modifyinganelement'sattributesandreadingthemisavitaltask.InplainJavaScript,thiscanbeachievablebywritingafewlinesofcode;however,withjQuery,itcanbeachievedinaquickerandnicerway.
Modifyinganypropertiesofanelement,whichistobeselected,canbedonewiththevariousoptionslistedintheprevioussection.Eachpropertylistedinthefollowingtableprovidesboththegetandsetoptionsandtakesparameter(s)whensettingsomethingandnoparameterswhenreadingit.
Therearesomecommonmethodsavailabletomodifyanelement,namelyhtml,value,andsoon,injQuery.Formoremethods,youcanrefertohttp://api.jquery.com/category/manipulation/.
Thegetmethod Thesetmethod Description
.val() .val('anyvalue')ThismethodisusedtoreadorwriteanyvalueoftheDOMelement.
.html().html('anyhtml
string')
ThismethodisusedtoreadorwriteanyHTMLcontentoftheDOMelement.
.text() .text('anytext')Thismethodisusedtoreadorwritethetextcontent.HTMLwillnotbereturnedinthismethod.
.width() .width('anyvalue')Thismethodisusedtoupdatethewidthofanyelement.
.height() .height('anyvalue')Thismethodisusedtoreadormodifytheheightofanyelement.
.attr() .attr('attributename',
'value')Thismethodisusedtoreadormodifythevalueofaspecificelement'sattribute.
.prop() .prop()
Thismethodisthesameasattr()butmoreefficientwhendealingwiththevaluepropertythatreturnsthecurrentstate.Forexample,theattr()checkboxprovidesthedefaultvaluewhereasprop()givesthecurrentstate,thatis,trueorfalse.
.css('style-
property')
.css({'style-
property1':value1,
'style-property2':
value2,'style-
propertyn':valueN}
Thismethodisusedtosetanypropertyofstyle,suchasthefontsize,fontfamily,andwidthforaparticularelement.
Let'shavealookatthefollowingexample,whichusesthehtml(),text(),andcss()modifiersandupdatesthepelementwithhtml,text,andincreaseFontSize:
<!DOCTYPEhtml>
<html>
<head>
<linkrel="stylesheet"type="text/css"href="Content/bootstrap.css"/>
<scriptsrc="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.12.0.min.js">
</script>
<script>
functionupdateHtml(){
$('p').html($('#txtHtml').val());
}
functionupdateText(){
$('p').text($('#txtText').val());
}
functionincreaseFontSize(){
varfontSize=parseInt($('p').css('font-size'));
varfontSize=fontSize+1+"px";
$('p').css({'font-size':fontSize});
}
</script>
</head>
<body>
<formclass="form-control">
<divclass="form-group">
<p>thisisabookforJavaScriptfor.NetDevelopers</p>
</div>
<divclass="form-group">
EnterHTML:<inputtype="text"id="txtHtml"/>
<buttononclick="updateHtml()">UpdateHtml</button>
</div>
<divclass="form-group">
UpdateText:<inputtype="text"id="txtText"/>
<buttononclick="updateText()">UpdateText</button>
</div>
<divclass="form-group">
<buttononclick="increaseFontSize()">IncreaseFontSize</button>
</div>
</form>
</body>
</html>
TheoutcomeoftheprecedingHTMLcodeisasfollows:
YoucanupdateHTMLbyclickingontheUpdateHtmlbuttonandplaintextbyclickingontheUpdateTextbutton:
Finally,thefontsizecanbeincreasedbyclickingontheIncreaseFontSizebutton:
Creatingnewelements
ThejQuerylibraryprovidesasmartwayofcreatingnewelements.Elementscanbecreatedusingthesame$()methodandpassinghtmlasaparameter.Oncetheelementiscreated,itcannotbeshownuntilitisaddedtotheDOM.Therearevariousmethodsavailablethathelpappend,insertafter,orinsertbeforeanyelement,andsoon.ThefollowingtableshowsthelistofallthemethodsusedtoaddnewelementstotheDOM:
Thegetmethod Description
.append()ThismethodisusedtoinserttheHTMLcontentintotheelementfromwhichithasbeencalled
.appendTo()Thismethodisusedtoinserteveryelementattheendfromwhichithasbeencalled
.before()ThismethodisusedtoinserttheHTMLcontentbeforetheelementfromwhichithasbeencalled
.after()ThismethodisusedtoinserttheHTMLcontentaftertheelementfromwhichithasbeencalled
.insertAfter()ThismethodisusedtoinserttheHTMLcontentaftereveryelementfromwhichithasbeencalled
.insertBefore()ThismethodisusedtoinserttheHTMLcontentbeforeeveryelementfromwhichithascalled
.prepend()ThismethodisusedtoinserttheHTMLcontentintotheelementatthestartingpositionfromwhichithasbeencalled
.prepend()ThismethodisusedtoinserttheHTMLcontentfromthestartingpositionforeachelementfromwhichithasbeencalled
Thefollowingexamplecreatesaformwithtwofields,NameandDescription,andabuttontosavethesevalues:
<!DOCTYPEhtml>
<html>
<head>
<linkrel="stylesheet"type="text/css"href="Content/bootstrap.css"/>
<scriptsrc="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.12.0.min.js">
</script>
<script>
$(document).ready(function(){
varformControl=$("<formid='frm'class='container'></form>");
$('body').append(formControl);
varnameDiv=$("<divclass='form-group'><labelid='lblName'>EnterName:
</label><inputtype='text'id='txtName'class='form-control'/></div>");
vardescDiv=$("<divclass='form-group'><labelid='lblDesc'>Enter
Description:</label><textareaclass='form-control'type='text'
id='txtDescription'/></div>");
varbtnSave=$("<buttonclass='btnbtn-primary'>Save</button>")
formControl.append(nameDiv);
formControl.append(descDiv);
formControl.append(btnSave);
});
</script>
</head>
<body>
</body>
</html>
Thiscodewillgivethefollowingoutput:
Removingelementsandattributes
WiththeoptionofusingdifferentmethodstocreateandrenderelementsintheDOM,jQueryalsoprovidesafewmethodstoremoveelementsfromtheDOM.Thefollowingtableisalistofmethodsthatwecanusetoremoveaparticularelement,setofelements,orallchildnodes:
Methods Description
.empty() ThismethodremovestheinnerHTMLcodefromtheelement
.detach() ThismethodremovesthesetofmatchedelementsfromtheDOM
.remove() ThismethodremovesthesetofmatchedelementsfromtheDOM
.removeAttr() Thismethodremovesaparticularattributefromtheelement
.removeClass() Thismethodremovesaclassfromanelement
.removeProp() Thismethodremovesapropertyfromanelement
Thedifferencebetweenremove()anddetach()isthatremoveremovesthecontentfromtheDOMpermanently;thismeansthatiftheelementhasspecificeventsordataassociated,theseeventsordatawillalsoberemoved.However,detachjustseparatesandisolatestheelementfromtheDOMandreturnsthecontentthatyoucansaveinsomevariableforlaterattachment:
@modelWebApplication.ViewModels.Book.BookViewModel
@{
ViewData["Title"]="View";
}
<scriptsrc="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.12.0.min.js">
</script>
<script>
varmainDivContent=undefined
$(document).ready(function(){
$('button').click(function(){
if(mainDivContent){
mainDivContent.appendTo('#pageDiv');
mainDivContent=null;
}else{
mainDivContent=$('#mainDiv').detach();
}
});
});
</script>
<divid="pageDiv"class="container">
<br/>
<divid="mainDiv"class="form-horizontal">
<divclass="form-group">
<labelasp-for="Name"class="col-md-2control-label"></label>
<divclass="col-md-10">
<inputasp-for="Name"class="form-control"/>
</div>
</div>
</div>
<divclass="form-group">
<divclass="col-md-offset-2col-md-10">
<buttonclass="btnbtn-primary">Detach/Attach</button>
</div>
</div>
</div>
Ondetaching,theoutputwillbeasfollows:
Onattaching,theoutputwillbesimilartothefollowingscreenshot:
EventhandlinginjQueryThejQueryeventmodelprovidesabetterwayofhandlingeventsontheDOMelements.Programmatically,ifdeveloperswanttoregisteranyeventoftheuser'saction;forexample,abuttonclickeventcanbeacumbersomeprocesswhenworkingwithplainJavaScript.Thisisbecausedifferentbrowsershavedifferentimplementationsandthesyntaxissomehowdifferentfromoneanother.ThejQuerylibrary,ontheotherhand,providesacleanersyntaxanddevelopersdon'thavetoworkonthecrossbrowserissues.
RegisteringeventsinjQuery
TherearemanyshortcutsavailableinjQuerytoregistereventstodifferentelements.Thefollowingtableshowsyoualistofalltheseeventswithspecificdescriptions:
Events Description
click() Thiseventisusedwhenthemouseclickoccurs
.dblclick() Thiseventisusedwhenthedouble-clickoccurs
.mousedown() Thiseventisusedwhenanyofthemousebuttonsarepressed
.mouseup() Thiseventisusedwhenanyofthemousebuttonsarereleased
.mouseenter() Thiseventisusedwhenthemouseentersthesection
.mouseleave() Thiseventisusedwhenthemouseleavesthesection
.keydown() Thiseventisusedwhenakeyboardkeyispressed
.keyup() Thiseventisusedwhenthekeyboardkeyisreleased
.focus() Thiseventisusedwhentheelementisfocused
.blur() Thiseventisusedwhentheelementlosesfocus
.change() Thiseventisusedwhentheitemischanged
Therearevariousotherevents,whichyoucancheckoutathttp://api.jquery.com/category/events.
RegisteringaneventisquitesimpleusingjQuery.Firstofall,theelementhastobeselectedbychoosinganyoftheselectorsandthenregisteringtheeventsbycallingaspecificeventhandler;forexample,thefollowingcodesnippetwillregistertheclickeventforthebutton:
$(document).ready(function({
$('#button1').click(function(){
console.log("buttonhasbeenclicked");
});
)};
Aftertheprecedingexamplecode,registertheasp.netbuttonclickeventandcalltheContactactionofthecontrollerofHomeinASP.NET:
<scriptsrc="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.12.0.min.js">
</script>
<script>
varmainDivContent=undefined
$(document).ready(function(){
$('#btnSubmit').click(function(){
window.location.href='@Url.Action("Contact","Home")';
});
});
</script>
<divid="pageDiv"class="container">
<br/>
<divclass="form-group">
<divclass="col-md-offset-2col-md-10">
<buttonid="btnSubmit"class="btnbtn-primary">Submit</button>
</div>
</div>
</div>
Intheprecedingexample,weusedtheHTMLhelper,Url.Action,throughtheRazorsyntax,whichgeneratedtheURLandsetittothehrefpropertyofthewindow'scurrentlocation.Now,clickonthebuttonshowninthefollowingscreenshot:
Thefollowingcontactpagewillbedisplayed:
Anotherexampleshownherewillchangethebackgroundcolorofallinputcontrolstoalicebluewhenthecontrolisfocusedandchangebacktowhitewhenitblurs:
@modelWebApplication.ViewModels.Book.BookViewModel
@{
ViewData["Title"]="View";
}
<scriptsrc="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.12.0.min.js">
</script>
<script>
varmainDivContent=undefined
$(document).ready(function(){
$('#btnSubmit').click(function(){
window.location.href='@Url.Action("Contact","Home")';
});
$('input').each(function(){
$(this).focus(function(){
$(this).css('background-color','aliceblue');
})
$(this).blur(function(){
$(this).css('background-color','white');
});
});
});
</script>
<divid="pageDiv"class="container">
<br/>
<divid="mainDiv"class="form-horizontal">
<divclass="form-group">
<labelasp-for="Name"class="col-md-2control-label"></label>
<divclass="col-md-10">
<inputasp-for="Name"class="form-control"/>
</div>
</div>
<divclass="form-group">
<labelasp-for="Description"class="col-md-2control-label"></label>
<divclass="col-md-10">
<inputasp-for="Description"class="form-control"/>
</div>
</div>
</div>
<divclass="form-group">
<divclass="col-md-offset-2col-md-10">
<buttonid="btnSubmit"class="btnbtn-primary">Submit</button>
</div>
</div>
</div>
Bindingeventsusingonandoff
Apartfromregisteringeventsdirectlybycallingtheeventhandler,wecanalsoregisterthemusingonandoff.Theseeventsregisterandderegistertheeventforspecificelements.
Hereisasimpleexampleofbindingaclickeventtoabuttonusingon:
$(document).ready(function(){
$('#btnSubmit').on('click',function(){
window.location.href='@Url.Action("Contact","Home")';
});
});
Thisisaveryusefultechniqueandcanbeusedincertainconditionswhereyouwanttoderegisteranyevent.Forexample,businessapplicationsaremostlyrelatedtoformhandling,andformscanbesubmittedusingsomebuttonthatpoststherequesttosomeserver.Incertainconditions,wehavetorestricttheusertosubmitmultipletimesuntilthefirstrequesthasbeenprocessed.Tohandlethisproblem,wecanusetheon()andoff()eventstoregisterandderegisterthemwhentheuserclicksthefirsttime.Hereisanexamplethatderegistersthebutton-clickeventwhenitisclickedforthefirsttime:
<scriptsrc="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.12.0.min.js">
</script>
<script>
$(document).ready(function(){
$('#btnSubmit').on('click',function(){
$('#btnSubmit').off('click');
});
});
</script>
ThepreventDefault()eventisjustthecanceleventthatweusedtohavein.NET.Thiseventisusedtocanceltheeventfromexecution.Itcanbeusedasfollows:
<scriptsrc="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.12.0.min.js">
</script>
<script>
$(document).ready(function(){
$('#btnSubmit').on('click',function(event){
event.preventDefault();
});
});
</script>
Theon()methodisequivalenttothedelegate()methodusedwiththepreviousversionofjQuery.SincejQuery1.7,delegate()hasbeenreplacedwithon().
Thereisonemoreoverloadedmethod,on,whichtakesfourparameters:
$(element).on(events,selector,data,handler);
Here,elementisthecontrolname,eventsistheeventthatyouwanttoregister,andselectorisanewthing,whichcanbethechildelementoftheparentcontrol.Forexample,foratableelementselector,itcouldbetd;andoneachclickeventoftd,wecandosomethingasfollows:
@modelIEnumerable<WebApplication.ViewModels.Book.BookViewModel>
<scriptsrc="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.12.0.min.js">
</script>
<script>
$(document).ready(function(){
$('table').on('click','tr',null,function(){
$(this).css('background-color','aliceblue');
});
});
</script>
<p>
<aasp-action="Create">CreateNew</a>
</p>
<tableclass="table">
<tr>
<th>
@Html.DisplayNameFor(model=>model.Description)
</th>
<th>
@Html.DisplayNameFor(model=>model.Name)
</th>
<th></th>
</tr>
@foreach(variteminModel){
<tr>
<td>
@Html.DisplayFor(modelItem=>item.Description)
</td>
<td>
@Html.DisplayFor(modelItem=>item.Name)
</td>
<td>
<aasp-action="Edit"asp-route-id="@item.Id">Edit</a>|
<aasp-action="Details"asp-route-id="@item.Id">Details</a>|
<aasp-action="Delete"asp-route-id="@item.Id">Delete</a>
</td>
</tr>
}
</table>
Theprecedingcodesnippetoutputwouldbesimilartothefollowingscreenshot.Whentheuserclicksonanyrow,thebackgroundcolorwillbechangedtoAliceblue:
Usingthehoverevents
Wecanusethehovereventswhenthemouseentersorexitsaparticularelement.Itcanbeusedbycallingthehover()methodonanyelementoftheDOM.Thesyntaxofcallingthismethodisasfollows:
$(selector).hover(mouseEnterHandler,mouseExitHandler);
Thefollowingexamplechangestheinputtextcontrol'sbordercolorwhenthemouseentersorexits:
@{
ViewData["Title"]="View";
}
<scriptsrc="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.12.0.min.js">
</script>
<02>
$(document).ready(function(){
$("input[type='text']").hover(function(){
$(this).css('border-color','red');
},
function(){
$(this).css('border-color','black');
}
});
</script>
<divid="pageDiv"class="container">
<br/>
<divid="mainDiv"class="form-horizontal">
<divclass="form-group">
<labelasp-for="Name"class="col-md-2control-label"></label>
<divclass="col-md-10">
<inputasp-for="Name"class="form-control"/>
</div>
</div>
<divclass="form-group">
<labelasp-for="Description"class="col-md-2control-label"></label>
<divclass="col-md-10">
<inputasp-for="Description"class="form-control"/>
</div>
</div>
</div>
<divclass="form-group">
<divclass="col-md-offset-2col-md-10">
<buttonid="btnSubmit"class="btnbtn-primary">Submit</button>
</div>
</div>
</div>
Thefollowingscreenshotwillbetheoutputoftheprecedingcodesnippet.Thiswillchangethebordercoloroftheinputtextcontroltoredwhenthemouseentersandblackwhenitexits:
SummaryInthischapter,youlearnedaboutjQuerybasicsandhowtousetheminwebapplications,especiallyinASP.NETcore1.0.Thisisaverypowerfullibrary.Iteliminatescross-browserissuesandprovidesconsistentbehavioracrossallbrowsers.Thislibraryprovidessimpleandeasymethodstoselectelements,modifyattributes,attachevents,andperformcomplexoperationsbywritingcodeinacleanerandmoreprecisemanner.Inthenextchapter,wewilllookintothevarioustechniquesofdoingAjaxrequestsusingjQueryandplainJavaScripttoperformserver-sideoperations.
Chapter4.AjaxTechniquesOneofthecorecharacteristicsofmakingresponsivewebapplicationsisAjax.Traditionally,inserver-sidepostbacks,wheneverauserperformsanyaction,theinformationsuppliedintheformissentbacktotheserverandthesamepageloadsagain,containingalltheimages,CSS,andJavaScriptfilesloadedagainontheclientside.Thisapproachisquiteheavyintermsofthesizeoftherequestandresponsebeingsentfromtheclientandserver.Thus,theapplicationbecomeslessresponsiveandtheuserhastowaitforthepagetorefresheverytimeanyactionistaken.Inthischapter,wewilldiscusshowtosimplifythewholeprocessandavoidheavyserver-sidepostbacksthroughAjax.
IntroducingAjaxAjaxstandsforAsynchronousJavaScriptandXML;itcreatesasynchronousrequestsonserverwithoutsendingandrenderingthewholepageagainonclientside,whereasitonlysendsabitofinformationthatneedstobesentouttotheserverandreceivesresponseinaspecificformattoupdateaspecificsectionortheelementsofDOMthroughJavaScript.Thisallowsdeveloperstodevelopresponsivewebapplicationsanddynamicallyupdatethecontentofthepagewithoutreloadingiteverytimeforaparticularaction.Forexample,inamaster-childpagerelationship,thechildcontentisdependentontheparentitemselection;andwithaclassicapproach,everytimetheparentitemisselected,thepageisbeingpostedbacktotheserverside,wheretheserverdoessomebackendjobtofillthechildsectionandreturnstheHTMLcode,whichisthenrenderedontheclientside.ThroughAjax,thiscanbeachievedbymakinganasynchronousrequesttosendtheselectedinformationandupdatetheselectedpartsofthepagecontent.
HowAjaxworksAjaxusestheXMLHttpRequest(XHR)objecttoinvoketheserver-sidemethodsasynchronously.XHRisdevelopedbyMicrosoft,anditwasinitiallyprovidedwithInternetExplorer5.ItwasusedinitiallybycallinganActionXObjectinstancetocreateaninstance;however,withmodernversions,everybrowsersupportsinitializingtheXHRobjectthroughtheXMLHttpRequestobject.
ThefollowingdiagramshowsthearchitecturalviewofhowAjaxworks:
Traditionally,whenanyactionistakenfromtheclientside,theentiredataissentbacktotheserverandisloadedagainontheclientsideoncetheresponseisreceived.Insteadofupdatingthedata,whichneedstobeupdated,includingallthestaticfilessuchasCSS,JavaScript,andimages,itisloadedfromtheserveragainandrenderedontheclientside,unlesssomecachingmechanismisimplemented.WithAjax,wesendthedatainaJSONstringorXMLandgettheresponseinaJSON,XML,HTML,oranyotherformat,dependingontheserver.Wecanalsouserequestheader,suchasAccept,whensendingtherequest,sotheserverknowswhattheclientisaccepting;andbasedontheformatter,itcanserializethedataintoaparticularformat.InASP.NETMVC6,therearetwoformattersimplementedbydefaultfor
JSONandXML,whichsendthedata,basedontherequestAcceptheaderandserializetheobjectaccordingly.Customformatterscanalsobeimplementedonaserverleveltohandlespecificscenarios.
AjaxrequestsusingtheclassicXHRobject
Allbrowsers,includingInternetExplorer,Chrome,Firefox,andSafari,providethisobjectthatcanbeusedfromJavaScripttoexecutetheAjaxrequests.
InJavaScript,wecaninitializetheXMLHttpRequestobjectasfollows:
varxhr=newXMLHttpRequest();
EveryrequestcouldbeaGETorPOSTrequest.Oncetheresponseisreceivedfromserver,afewpropertiesgetpopulatedandeventhandlersareinvoked,whichcanbeconfiguredfortheXHRobjectwhenmakingtheAjaxrequest.
Let'slookintothedetailsofwhatmethods,properties,andeventstheXHRobjectprovides.
XHRmethods
TheXHRobjectprovidesvariousmethodsasfollows,butthetwomostimportantmethodstoinitiateanAjaxifiedrequestareopen()andsend():
Sendingrequest:
RequestcaneitherbeGETorPOST.Whenmakinganyrequest,willwefirsthavetoinvoketheopenmethodandspecifytheHTTPmethod,suchasGETorPOST,andtheURLoftheserver.Restoftheparameters,suchasasyncbit,user,andpassword,areoptional.
Thesignatureoftheopenmethodisasfollows:
voidOpen(
DOMStringmethod,
DOMStringURL,
optionalbooleanasync,
optionalDOMStringuser?,
optionalDOMStringpassword
);
Thesendmethodisusedtosendtherequesttotheserver.Thisistheactualmethod,whichsendstherequesttotheserveranditacceptsthedatainvariousformats.
Thefollowingtableshowstheoverloadedmethodsavailableforthesendmethod:
Methods Description
voidsend() ThismethodisusedwhenmakingtheGETrequests
voidsend(DOMString?
Data)Thismethodisusedwhenpassingthedatainstring
voidsend(Documentdata) Thismethodisusedwhenpassingthedocumentdata
voidsend(Blobdata)Thismethodisusedtopasstheblobdataordatainbinary
voidsend(FormDatadata) Thismethodisusedtopassthewholeform
Abortingrequest:
Therearecertaincasesinwhichdevelopersmightneedtoabortthecurrentrequest.Thiscanbedonebycallingtheabort()functionoftheXHRobject:
varxhr=newXMLHttpRequest();
xhr.abort();
Settingrequestheaders:
XHRprovidesseveraltechniquesofmakinganAjaxrequest.ThismeansthattherearecaseswhenweneedtosenddataintheJSON,XML,orsomecustomformat,basedontheserverimplementation.Forexample,whenworkingwithASP.NETMVC6,therearetwodefaultformattersimplemented,JSONandXML,andifyouwanttoimplementyourowncustomformatter,thisisalsopossible.Whensendingdatainaspecificformat,weneedtotelltheformattotheserverthroughrequestheaders.Thishelpstheserverinidentifyingtheformatterthathastobeloadedtoserializetheresponseandprocesstherequest.
ThefollowingtableshowsthedefaultheadersthatcanbesuppliedwiththeAjaxrequest:
Headers Description
Cookie Thisheaderspecifiesanycookiesetintheclientside
Host Thisheaderspecifiesthedomainnameofthepage
Connection Thisheaderspecifiesthetypeofconnection
Accept Thisheaderspecifiesthecontenttypethattheclientcanhandle
Accept-charset Thisheaderspecifiesthecharactersetthattheclientcandisplay
Accept-
encodingThisheaderspecifiestheencodingsthatclientcanhandle
Accept-
language
Thisheaderspecifiesthepreferrednaturallanguagesacceptedasaresponse
User-Agent Thisheaderspecifiesauseragentstring
Referer ThisheaderspecifiestheURLofthepage
ThroughtheXHRobject,wecansettherequestheadersthroughthesetRequestHeader()function,asshowninthefollowingcode:
varxhr=newXMLHttpRequest();
xhr.setRequestHeader('Content-Type','application/json');
Gettingresponseheaders:
Whentheresponseisreturnedbytheserver,wecanreadtheresponseheadersbyusingthefollowingtwomethods:
varxhr=newXMLHttpRequest();
functioncallback(){
vararrHeaders=xhr.getAllResponseHeaders();
//or
varcontentType=xhr.getResponseHeader('Content-Type');
}
ThegetAllResponseHeaders()functionreturnsthelistofalltheresponseheaders,whereasthegetResponseHeader()functionacceptstheheadernameandreturnsthevalueoftheheadernamesupplied.
XHRevents
ThemostusefuleventhandlerintheXHRobject,whichisinvokedwhenthevalueofthereadystatepropertyischanged,istheonreadystatechangeevent.Oninitiatingrequest,wecanassociatethefunctionwiththiseventhandlerandreadtheresponse:
varxhr=newXMLHttpRequest();
xhr.onreadystatechange=callback;
functioncallback(){
//dosomething
}
Anothercoreeventhandlerisontimeout,whichcanbeusedinconditionstohandletherequesttimed-outscenario.WheninitiatinganXHRrequest,thereisatimeoutpropertythroughwhichthetimeoutcanbesetinmilliseconds,andiftherequestexceedsthetimed-outvalue,theontimeouteventhandlerwillbeinvoked.Theexample,wheretimeoutissetto5,000milliseconds,ifitexceedsthetimeoutproperty,thetimeouthandlerfunctionwillbeinvoked,asshownhere:
varxhr=newXMLHttpRequest();
xhr.timeout=5000;
xhr.ontimeout=timeouthandler;
functiontimeouthandler(){
//dosomething
}
XHRproperties
ThelistofpropertiesavailablefortheXMLHttpRequestobjectisasfollows:
GETrequeststate:
Thispropertyreturnsthestatusinformationabouttheresponse.Itisnormallyusedtotakeactionbasedontherequeststatus:
varxhr=newXMLHttpRequest();
xhr.readystate;
Thelistofstatuseswiththeirmeaningavailableforthereadystatepropertyisgiveninthefollowingtable:
Statusvalue State Description
0 UNSENTInthisstate,theXMLHttpRequestobjectiscreated,buttheopen()methodisnotcalled
1 OPENED Inthisstate,theopenmethodiscalled
2 HEADERS_RECEIVEDThisstateoccursoncesend()iscalledandheadersarereceived
3 LOADING Thisstateoccurswhentheresponseisdownloading
4 DONE
Thisstateoccurswhentheresponseiscomplete
Getresponsedata:
ResponsecanberetrievedbycallingtheresponseorresponseTextproperty.ThedifferencebetweenthesepropertiesisthattheresponseTextpropertyreturnstheresponseasastring,whereastheresponsepropertyreturnstheresponseasaresponseobject.Theresponseobjectcanbeadocument,blob,orJavaScriptobject:
varxhr=newXMLHttpRequest();
xhr.response;
//or
xhr.responseText;
Getresponsestatus:
ResponsestatuscanberetrievedbycallingthestatusorstatusTextproperty.Thedifferencebetweenthesepropertiesisthatthestatuspropertyreturnsthenumericalvalue,forexample,200,iftherequestissuccessfullyprocessedbyserver,whereasthestatusTextpropertyincludesthecompletetext,suchas200OKandsoon:
varxhr=newXMLHttpRequest();
xhr.status;
or
xhr.statusText;
Let'stakealookatthefollowingexamplethatmakestheformPOSTrequestusingtheXHRobjectinASP.NETMVC6.Thefollowingformhastwofields,NameandDescription:
HereisthecodesnippetthatsendstherequesttoserversideusingtheXHRobject.ThisexamplesendsthedatainJSON:
@modelWebApplication.ViewModels.Book.BookViewModel
@{
ViewData["Title"]="View";
}
<script>
varxhr=null;
functionsubmit(){
xhr=newXMLHttpRequest();
xhr.open("POST",'/Book/SaveData');
varname=document.getElementById("Name").value;
vardescription=document.getElementById("Description").value;
vardata=
{
"Name":name,
"Description":description
};
xhr.setRequestHeader('Content-Type','application/json;charset=utf-8');
xhr.onreadystatechange=callback;
xhr.send(JSON.stringify(data));
}
functioncallback(){
if(xhr.readyState==4){
varmsg=xhr.responseText;r
document.getElementById("msg").innerHTML=msg;
document.getElementById("msgDiv").style.display='block';
}
}
</script>
<formasp-action="SaveData"id="myForm">
<p></p>
<divid="msgDiv"style="display:none"class="alertalert-success">
<ahref="#"class="close"data-dismiss="alert"aria-label="close">×</a>
<strong>Success!</strong><labelid="msg"></label>
</div>
<divid="pageDiv"class="container">
<br/>
<divid="mainDiv"class="form-horizontal">
<divclass="form-group">
<labelasp-for="Name"class="col-md-2control-label"></label>
<divclass="col-md-10">
<inputasp-for="Name"class="form-control"/>
</div>
</div>
<divclass="form-group">
<labelasp-for="Description"class="col-md-2control-label"></label>
<divclass="col-md-10">
<textareaasp-for="Description"class="form-control"></textarea>
</div>
</div>
</div>
<divclass="form-group">
<divclass="col-md-offset-2col-md-10">
<buttonid="btnSubmit"onclick="submit()"type="submit"class="btnbtn-
primary">Submit</button>
</div>
</div>
</div>
</form>
InASP.NETCore,forJSONandXML,wehavetoexplicitlyannotatethecomplextypewiththe[FromBody]attribute.ThisisbecauseMVC6firstsearchesforthevaluesinthequerystringirrespectiveofitstype,whetheracomplextypeoraprimitivetype.FortheJSONandXMLdata,weneedtoexplicitlyannotatethemethod'sparameterwiththe[FromBody]attributesothatthedatacanbeeasilyboundwithoutanyissue:
publicIActionResultSaveData([FromBody]BookViewModelbookViewModel)
{
returnContent("Datasavedsuccessfully");
}
Intheprecedingcodesnippet,wereadtheformvaluesthroughdocument.getElementByIdandthenmadeaJSONstringtopasstheformdatainaJSONformat.
Theoutputwillbeasfollows:
However,thereisalibraryprovidedbyGoogle,whichserializestheformdatabycallingtheserialize()function.Theonlydifferenceissettingtherequestheader'Content-Type'to'application/x-www-form-urlencoded',andaddingthefollowingscriptfile:
<scriptsrc=http://form-serialize.googlecode.com/svn/trunk/serialize-0.2.min.js
/>
Thefollowingcodeistherevisedversionofthesubmitfunction,whichserializestheformdatathroughtheserialize()functionandsendsthedataasform-encodedvalues:
functionsubmit(){
xhr=newXMLHttpRequest();
xhr.open('POST','/Book/SaveData');
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
varhtml=serialize(document.forms[0]);
xhr.onreadystatechange=callback;
xhr.send(html);
}
Fortheform-encodedvalues,wewillremovethe[FromBody]attribute.Thisisbecausetheform-encodedvaluesaresentasthenamevaluepairsinthequerystring:
publicIActionResultSaveData(BookViewModelbookViewModel)
{
returnContent("Datasavedsuccessfully");
}
InthepreviousversionsofASP.NETWebAPI,iftheactionmethodofWebAPIcontrollercontainsacomplextype,WebAPIframeworkautomaticallyboundthevaluesfromtherequestbody.WhereaswithASP.NETCore,theWebAPIandMVChavebecomeoneunifiedframework,andthemodelbindingisnotequivalenttowhatwehasinthepreviousversionsofWebAPI.
Intheprecedingexamples,wesawhoweasilywecanmakeaPOSTrequestandsenddatainJSONandform-encodedvalues.Now,let'sseeanotherexampleinwhichwewillloadthepartialviewbasedontheJSONresponsesentfromserver.
ThefollowingscreenshotisoftheASP.NETpagethatcontainsabuttontoloadthelistofbooksinatable:
Hereisthecodesnippetforthemainpage:
@modelWebApplication.ViewModels.Book.BookViewModel
@{
ViewData["Title"]="Books";
}
<script>
varxhr=null;
functionloadData(){
xhr=newXMLHttpRequest();
xhr.open('GET','/Book/Books',true);
xhr.onreadystatechange=callback;
xhr.send();
}
functioncallback(){
if(xhr.readyState==4){
varmsg=xhr.responseText;
document.getElementById("booksDiv").innerHTML=msg;
}
}
</script>
<divclass="container">
<buttonid="btnLoad"onclick="loadData()"type="submit"class="btnbtn-
primary">Load</button>
<hr/>
<divid="booksDiv">
</div>
</div>
Thefollowingisthepartialviewthatdisplaysthelistofbooksinatable:
@{
Layout=null;
}
@modelIEnumerable<WebApplication.ViewModels.Book.BookViewModel>
<scriptsrc="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.12.0.min.js">
</script>
<script>
$(document).ready(function(){
$('table').on('click','tr',null,function(){
$(this).css('background-color','aliceblue');
});
});
</script>
<p>
<aasp-action="Create">CreateNew</a>
</p>
<tableclass="table">
<tr>
<th>
@Html.DisplayNameFor(model=>model.Description)
</th>
<th>
@Html.DisplayNameFor(model=>model.Name)
</th>
<th></th>
</tr>
@foreach(variteminModel){
<tr>
<td>
@Html.DisplayFor(modelItem=>item.Description)
</td>
<td>
@Html.DisplayFor(modelItem=>item.Name)
</td>
<td>
<aasp-action="Edit"asp-route-id="@item.Id">Edit</a>|
<aasp-action="Details"asp-route-id="@item.Id">Details</a>|
<aasp-action="Delete"asp-route-id="@item.Id">Delete</a>
</td>
</tr>
}
</table>
HereisthecodesnippetoftheASP.NETMVCBookscontrollerthatcontainstheBooksactionmethodthatreturnsalistofbooks:
publicclassBookController:Controller
{
//GET:/<controller>/
publicIActionResultIndex()
{
returnView();
}
publicIActionResultBooks()
{
List<BookViewModel>books=newList<BookViewModel>();
books.Add(newBookViewModel{Id=1,Name="JavaScriptfor.Net
Developers",Description="Bookfor.NETDevelopers"});
books.Add(newBookViewModel{Id=1,Name="BeginningASP.NETCore1.0",
Description="BookforbeginnerstolearnASP.NETCore1.0"});
books.Add(newBookViewModel{Id=1,Name="MasteringDesignPatterns",
Description="AllaboutDesignPatterns"});
returnView(books);
}
publicIActionResultCreate()
{
returnView();
}
}
So,withthisinplace,whentheuserclicksontheLoadbutton,therequestwillbemadetotheserverandtheASP.NETMVCcontrollerBooksactionmethodwillbeinvoked,whichreturnsViewthatrendersthepartialviewthatwillberenderedinsidethebooksDivelementonthemainpage:
MakinganAjaxrequestusingjQuery
Intheprevioussections,wediscussedhowtosendanAjaxrequestusingaplainXMLHttpRequestobject,whichisavailableinallbrowsers.Inthissection,wewillseewhatjQueryoffersinmakingtheAjaxrequestandhowtousetheHTTPGETandPOSTrequeststhroughthejQueryobject.
jQuery.ajax()
ThismethodisusedtomakebothGETandPOSTasynchronousrequests.Thefollowingcodeisthesignatureofthismethod,whichtakestwoparameters:URLandoptions.TheURLparameteristheactualserverURL,whereasoptionstakestheconfigurerequestheadersandotherpropertiesinaJSONrepresentation:
$.([URL],[options]);
$.([options]);
ThefollowingexampleshowshowtomakeanasynchronousrequestontheMVCcontrolleranddisplaysanalertonsuccessfulresponsebeingreturnedfromserver:
<scriptsrc="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.12.0.min.js">
</script>
<script>
$(document).ready(function(){
$.ajax('/Book/Books',{
success:function(data){
$('#booksDiv').html(data);
},
error:function(data){
$('#booksDiv').html(data);
}
});
});
</script>
TheBooksactionmethodreturnstheASP.NETMVCview,whichpassesthelistofbooksthatwillbepopulatedinsidethebooksDivDOMelement:
Ajaxproperties
ThefollowingtableshowssomecorepropertiesthatyoucanspecifytoconfiguretheAjaxrequest:
Name Type Description
accepts PlainObjectThispropertytellstheserveraboutthetypeofresponsethattheclientwillaccept.
async BooleanBydefault,thispropertyistrue(forasynchronousrequest),butitcanbesettofalse(synchronous).
cache BooleanIfthispropertyissettofalse,forcerequestedpageswillnotbecachedbythebrowser.
contents PlainObjectThispropertyisusedtospecifyregularexpressionsforparsingresponse.
Thispropertytellstheserveraboutthetypeofdatapassedinthe
contentType StringorBoolean
request.Thedefaultvalueisapplication/x-www-form-urlencoded;charset=UTF-8.
crossDomain BooleanThispropertyissettotrueifyouwanttoforcethecross-domainrequest.
data
PlainObject,String,orArray
ThispropertycanbeusedtopassthedatainJSON,XML,oranyotherformat.
dataType StringThispropertyspecifiesthetypeofdataexpectingfromserver.SomecoredatatypesareXML,JSON,script,andHTML.
Pre-filteringAjaxrequests
Thisisagreatfeaturetofiltertheexistingrequestoptionsandconfigurationattributesbeforetheyaresentout.Itprovidestwooverloadedmethods:onethattakesafunctionthatinjectstheoptions,originalOptions,andjqXHRobjects,andtheotherthattakesastringwhereyoucanfilterouttheconfigurationattributesforspecificrequestsfollowedwiththefunctionacceptingparametersasoptions,originalOptions,andjqXHR.Thefollowingcodeisthesignatureofbothoverloadedmethods:
$.ajaxPrefilter(function(options,originalOptions,jqXHR){
//Modifyoptions,originalOptionsandstorejqXHR
}
$.ajaxPrefilter('dataType',function(options,originalOptions,jqXHR){
//Modifyoptions,originalOptionsandstorejqXHR
}
Theobjectsintheprecedingcodeareexplainedasfollows:
options:TheseobjectsarethesameastherequestoptionssuppliedintheAjaxrequest,buttheycanbeoverriddenandfilteredaccordingly.originalOptions:TheseobjectsprovidetheactualoptionsbeingsuppliedintheAjaxrequest.Theycanbeusedtoreferandcannotbemodified.Anychangeintheconfigurationcanbedoneusingtheoptionsobject.jqXHR:ThisobjectisequivalenttotheXMLHttpRequestobjectinjQuery.
Let'stakealookatthefollowingexample,whichappendsthefromAjaxparametertotelltheMVCcontrollerthattherequestisexecutedfromJavaScript:
<script>
$(document).ready(function(){
$.ajaxPrefilter(function(options,originalOptions,jqXHR){
options.url+=((options.url.indexOf('?')<0)?'?':'&')+
'fromAjax=true';
});
$.ajax('/Book/Books',{
success:function(data){
$('#booksDiv').html(data);
},
error:function(data){
$('#booksDiv').html(data);
}
});
});
</script>
ThefollowingcodeisthecontrolleractionmethodthatreturnsthelistofbooksiftherequestisanAjaxrequest:
publicIActionResultBooks(boolfromAjax)
{
if(fromAjax)
{
List<BookViewModel>books=newList<BookViewModel>();
books.Add(newBookViewModel{Id=1,Name="JavaScriptfor.Net
Developers",Description="Bookfor.NETDevelopers"});
books.Add(newBookViewModel{Id=1,Name="BeginningASP.NETCore1.0",
Description="BookforbeginnerstolearnASP.NETCore1.0"});
books.Add(newBookViewModel{Id=1,Name="MasteringDesignPatterns",
Description="AllaboutDesignPatterns"});
returnView(books);
}
returnContent("RequesttothismethodisonlyallowedfromAjax");
}
Therearevariouspropertiesforoptionsavailable,whichyoucanreferathttp://api.jquery.com.
SettingdefaultvaluesforallfutureAjaxrequests
Withthe$.ajax.setupfunction,wecansettheconfigurationvaluesforallthefuturerequeststobemadethroughthe$.ajax()or$.get()function.Thiscanbeusedtosetthedefaultsettingsbeforecallingthe$.ajax()function,andtheajaxfunctionwillpickthesettingsdefinedinthe$.ajaxSetup()function.
Thefollowingisthesignaturetocall$.ajax.setup:
$.ajaxSetup({name:value,name:value,name:value,…});
ThefollowingexamplesetsthedefaultURLfortheajaxrequestbeingmadethroughthe$.ajaxfunction:
<script>
$(document).ready(function(){
$.ajaxSetup({url:"/Book/Books"});
$.ajax({
success:function(data){
$('#booksDiv').html(data);
},
error:function(data){
$('#booksDiv').html(data);
}
});
});
</script>
LoadingdatathroughthegetfunctionsinjQuery
ThejQuerylibraryprovidesdifferentfunctionsforretrievingdatafromserver.Thefunction,suchas$.get(),canbeusedtoloadthedatabyusingtheHTTPGETrequest,whereas$.getJSON()isspecificallyusedtoloadtheJSON-encodeddata,and$.getScript()isusedtoloadandexecuteaJavaScriptfromtheserver.
UsingjQuery.get()
The$.get()functionisashorthandfunctionof$.ajax()andonlyallowstheGETrequest.Itabstractsmostoftheconfigurationvaluestodefaultvalues.Similartothe$.ajax()function,itreturnsthedatatothecallbackfunction,butdoesnotprovideanerrorcallback.So,ifanyerroroccurredduringtherequestprocessing,itcannotbetracked.
Ittakesfourparameters,URL,data,callback,andtype.WhereURListheaddresstowhichtherequestissent,datathattakesastringthatissenttotheserverwhentherequestismade,callbackreferstothefunctionwhichisexecutedwhentherequestissucceededandtypedenotesthetypeofdataexpectedfromtheserverlikeXML,JSONandsoon.
Thefollowingisthesignatureofthe$.get()function:
$.get('URL',data,callback,type);
Hereistheexamplethatloadsthebooks,whichcontainanetstringinitstitle:
<script>
$(document).ready(function(){
$.get('/Book/Books',{filter:"net"},function(data){
$('#booksDiv').html(data);
}
);
});
</script>
UsingjQuery.getJSON()
ThejQuery.getJSON()functionisusedtoloadJSONfromtheserver.Itcanbeusedby
callingthe$.getJSON()function:
$.getJSON('URL',{name:value,name:value,name:value,…});
ThefollowingistheexamplethatloadstheJSONbycallinganactionmethod,whichreturnstheJSONresponseanddisplaysthebooktitleinthebooksDivelement:
<script>
$(document).ready(function(){
$.getJSON('/Book/Books',function(data){
$.each(data,function(index,field){
$('#booksDiv').append(field.Name+"<br/>");
});
}
);
</script>
TheActionmethodreturnstheJSONresponseasfollows:
publicIActionResultBooks()
{
List<BookViewModel>books=newList<BookViewModel>();
books.Add(newBookViewModel{Id=1,Name="JavaScriptfor.NetDevelopers",
Description="Bookfor.NETDevelopers"}
books.Add(newBookViewModel{Id=1,Name="BeginningASP.NETCore1.0",
Description="BookforbeginnerstolearnASP.NETCore1.0"});
books.Add(newBookViewModel{Id=1,Name="MasteringDesignPatterns",
Description="AllaboutDesignPatterns"});
returnJson(books);
}
Onthepage,thebooktitleswillberenderedasshowninthefollowingscreenshot:
UsingjQuery.getScript()
ThejQuery.getScript()functionisashorthandof$.ajax(),anditisspecificallydesignedtoloadthescriptfromtheserver.Thefollowingisthesignatureofthe$.getScript()function:
$.getScript(url,callback);
Thefollowingexampleloadsthecustom.jsfileoncethedocumentisloaded:
<script>
$(document).ready(function(){
$.getScript("/wwwroot/js/custom.js");
</script>
Postingdatatoserverusingthepostfunction
Similartothe$.get()function,jQueryalsoprovidesa$.post()function,whichisashorthandof$.ajax(),andisspecificallydesignedtoonlymaketheHTTPPOSTrequests.
Hereisthesignatureofthe$.post()function:
$.post(url,data,callback,type);
Thefollowingexamplesubmitstheformdatausingthe$.post()function:
<script>
functionsubmit(){
$.post('/Book/SaveData',$("form").serialize(),function(data){
alert("formsubmitted");
});
}
</script>
ThefollowingisthecodesnippetoftheBookcontroller'sSaveDataactionmethodthattakestheobjectandreturnstheresponseasastring:
publicIActionResultSaveData(BookViewModelbookViewModel)
{
//callsomeservicetosavedata
returnContent("Datasavedsuccessfully")
}
Similarly,wecanpassdatainJSONbyspecifyingthetypeasjson:
<script>
functionsubmit(){
$.post('/Book/SaveData',{Name:"DesignPatterns",Description:"Allabout
designpatterns"},function(data){
},'json');
}
</script>
Ajaxevents
Ajaxeventsarecategorizedintolocalandglobalevents.LocaleventscanbedeclaredwhenmakinganAjaxrequestusingthe$.ajaxfunction.Eventssuchassuccessanderroraretermedaslocalevents,whereasglobaleventsworkwitheveryAjaxrequestexecutedwithinthepage.
Localevents
Thefollowingisthelistoflocalevents,anditisspecificallyrelatedtothe$.ajax()function.Othershorthandfunctions,suchas$.get()and$.post(),donothavethesemethodsavailable,aseachofthemhavespecificvaluestopasstheparametersandconfigurationattributes:
beforeSend:Thiseventistriggeredbeforetheajaxrequestisbeingmade.success:Thiseventoccurswhenthesuccessfulresponseisbeingmadefromtheserver.error:Thiseventoccurswhenanerrorisoccurredduringtheajaxrequest.complete:Thiseventoccurswhentherequestiscompleted.Itdoesnotcheckwhetheranerrorhasoccurredortheresponsewassuccessfulandexecutedwhentherequestiscompleted.
Globalevents
Thefollowingisthelistofglobalevents,anditworkswithallothershorthandfunctionsaswell,suchas$.post(),$.get(),and$.getJSON:
ajaxStart:ThiseventisusedwhenthereisnoajaxrequestinthepipelineandthefirstajaxrequestisstartingupajaxSend:ThiseventisusedwhenanajaxrequestissenttotheserverajaxSuccess:ThiseventisusedwhenanyofthesuccessfulresponsereturnsfromtheserverajaxError:Whenanerroroccursforanyajaxrequest,thiseventisfiredajaxComplete:Thiseventisusedwhenanyoftheajaxrequestiscompleted
ThefollowingcodeisasimpleexampleofASP.NETthatcallstheactionmethod,Books,ofBookController,whichreturnsthelistofbooksandtriggersglobalandlocalevents:
@modelWebApplication.ViewModels.Book.BookViewModel
@{
ViewData["Title"]="Books";
}
<scriptsrc="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.12.0.min.js">
</script>
<script>
$(document).ready(function(){
$(document).ajaxComplete(function(e){
alert("Ajaxrequestcompleted");
}).ajaxSend(function(){
alert("Ajaxrequestsending");
}).ajaxSend(function(){
alert("Ajaxrequestsenttoserver");
}).ajaxStop(function(){
alert("Ajaxrequeststopped");
}).ajaxError(function(){
alert("SomeerroroccurredinAjaxrequest");
}).ajaxSuccess(function(){
alert("Ajaxrequestwassuccessful");
})
$('#btnLoad').click(function(){
$.ajax('/Book/Books',{
success:function(data){
$('#booksDiv').html(data);
},
error:function(data){
$('#booksDiv').html(data);
}
});
});
});
</script>
<divclass="container">
<br/>
<h4>BooksView</h4>
<h5>Clickonthebuttontoloadallthebooks</h5>
<buttonid="btnLoad"type="submit"class="btnbtn-primary">Load</button>
<hr/>
<divid="booksDiv">
</div>
</div>
Cross-originrequestsDuetosecurityreasons,everybrowserrestrictswebapplicationstomakecross-originrequestthroughJavaScript.Cross-originrequestsaretherequeststhatmakeanHTTPrequesttoanotherserver,whichistheoutsidedomain.Forexample,iftheapplication-basedURLislocalhost,andyouaregoingtomakearequestonanotherdomain.com,itwillnotwork.However,therearefewdifferencesbetweenbrowsers'implementation,asinChromeiftheportnumberdiffers,itwon'tallowyoutomakearequest;whereasInternetExplorerignoresthis.
Cross-originrequestisonlyrestrictedtotherequestbeingmadefromtheXMLHttpRequestobject;however,wecanstillhaveresourcessuchasCSS,images,andscriptfilesloadedfromothersourcesordomains.
Thereasonfornotenablingthisistoprotectthedatafrommalicioussiteattacks.However,tohandlethesescenarios,wecaneitheruseJSON-P(anoldertechnique)orCORS,whichwillbediscussedinthefollowingsection.
JSON-P
TheJavaScriptObjectNotationPadding(JSON-P)techniqueisusedwitholderbrowsers.Thistechniqueisobsoletenow,andalternatively,CORSissaferandneater.
UsingJSON-P
JSON-Pisatechniquethatfakesabrowserformakingcross-originrequest.Itworksbyspecifyinga<script>tagthatmakesacross-originrequesttosomeotherserver,andtheresponsereturnedisactuallythefunctionname,whichisalreadydefinedinthewebpage.Itthenpassesthedataasaparametertothefunctionwhenthescriptisexecuted.
Itcanbeimplementedbyaddinga<script>tag,specifyingtherequestURLinthesrcattributeandJavaScriptcallbackfunctioninaquerystringforthesameURL.Forexample,considerthefollowingrequestURL:
http://otherdomain.com?Id=1
AppendthefunctionintherequestURL,whichwillbeinvokedwhenthescriptisloaded:
http://otherdomain.com?Id=1&callback=jsonPCallback
Thefollowingcodesnippetcallsthegeoserviceandspecifiesacallbackparameter,whichpointstothejsonCallbackfunctiondefinedinthescript.ThisscriptwillbeloadedwhenthepageloadsandexecutesthesrcURL,whichfinallycallsthejsonCallbackmethodandpassestheresponse.
ThiscodesnippetisasampleHTTPGETrequestthatusestheBingAPItogetthelocationinformationbasedonthelatitudeandlongitudevaluesprovided:
<script>
varscrpt=document.createElement('script');
scrpt.setAttribute('src','
http://dev.virtualearth.net/REST/v1/Locations/latitudeNo,longitudeNo?
o=json&key=BingMapsKey);
document.body.appendChild(scrpt);
functionjsonCallback(data){
alert("CrossOriginrequestgotmade");
}
</script>
Ontheotherhand,withjQuery,cross-originrequestscanbemadebyspecifyingthedataTypeattributeasjsonpandcrossDomainastrueinthe$.ajaxcall:
$.ajax({
url:serviceURL,
type:"GET",
dataType:"jsonp",
method:"GetResult",
crossDomain:true,
error:function(){
alert("listfailed!");
},
success:function(data){
alert(data);
}
});
CORS
Alternatively,CORSisamorepreferredwaywhenmakingcross-originrequests.ItisaW3Cstandardandallowsaservertosendcross-originrequestsfromanydomain.Thisneedstobeenabledontheserverside.
ASP.NETCoreprovidesaneasywayofenablingCORSontheserverside,andthiscanbedonebyaddingMicrosoft.AspNet.WebApi.CorsthroughNuGet,orbymodifyingproject.jsonandaddingadependencyasfollows:
"Microsoft.AspNet.Cors":"6.0.0-rc1-final"
EnabletheCORSserviceusingtheConfigureServicesmethodintheStartupclass:
publicvoidConfigureServices(IServiceCollectionservices
{
services.AddCors();
}
AddtheCORSmiddlewarebyusingtheUseCors()methodintheConfiguremethod.TheUseCorsmethodprovidestwooverloadedmethods:onethattakestheCORSpolicyandotherthattakesthedelegate,whichcanbeusedasabuildertobuildpolicy.
Note
NotethatUseCors()shouldbeaddedbeforeUseMVC.
ThroughtheCORSpolicy,wecandefinetheallowedorigins,headers,andmethods.TheCORSpolicycaneitherbedefinedattheConfigureServicesorConfiguremethodwhendefiningmiddleware.
SpecifyingtheCORSpolicyatserviceslevel
ThissectionwillcoverthewayofdefiningthepolicyattheConfigureServicesmethodandreferringwhenaddingmiddleware.TheAddPolicymethodtakestwoparameters:thenameofthepolicyandaCorsPolicyobject.TheCorsPolicyobjectallowschainingmethodsandallowsyoutodefineorigins,methods,andheadersusingtheWithOrigins,WithMethods,andWithHeadersmethods.
Hereisthesamplecodesnippetthatallowsallorigins,methods,andheaders.So,whatevertherequestorigin(domain)andHTTPmethodsorrequestheadersarepassed,therequestwillbeprocessed:
publicvoidConfigureServices(IServiceCollectionservices)
{
services.AddCors(options=>{
options.AddPolicy("AllowAllOrigins",builder=>
builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
});
}
Intheprecedingcode,Originsrepresentsthedomainnames,MethodrepresentstheHTTPmethods,andHeaderrepresentstheHTTPrequestheaders.ItcanbesimplyusedintheConfiguremethodasfollows:
publicvoidConfigure(IApplicationBuilderapp,IHostingEnvironmentenv,
ILoggerFactoryloggerFactory
{
app.UseCors("AllowAllOrigin");
}
Wecanalsodefinemultiplepolicies,asfollows:
publicvoidConfigureServices(IServiceCollectionservices)
{
services.AddCors(options=>{
options.AddPolicy("AllowAllOrigins",builder=>
builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
options.AddPolicy("AllowOnlyGet",builder=>
builder.WithMethods("GET").AllowAnyHeader().AllowAnyOrigin());
});
EnableCORSattheConfiguremethod
Alternatively,wecandefinetheCORSpolicyontheConfiguremethoditself.TheUseCors
methodhastwooverloadedmethods:onethattakesthepolicynamethatisalreadydefinedintheConfigureServicesmethod,andtheotherisCorsPolicyBuilderthroughwhichthepolicycandefinedirectlyontheUseCorsmethoditself:
publicvoidConfigure(IApplicationBuilderapp,IHostingEnvironmentenv,
ILoggerFactoryloggerFactory)
{
app.UseCors(policyBuilder=>policyBuilder.WithHeaders("accept,content-
type").AllowAnyOrigin().WithMethods("GET,POST"));
}
DefiningontheConfigureMethodclassenablestheCORSpolicythroughouttheapplication.InsteadofusingtheEnableCorsattribute,wecanspecificallydefinethepolicynamepercontroller,andactionlevelaswell,andusethepolicydefinedintheConfigureServicesmethod.
Definingthroughattributeisanalternative,whichreferstothepolicynamefromtheConfigureServicesmethodandignoresthepolicydefinedatthemiddlewarelevel.HerearethewaysofenablingCORSatcontroller,action,andgloballevel:
EnablingCORSatthecontrollerlevel:
ThefollowingcodeenablestheCORSpolicyattheMVC-controllerlevel:
[EnableCors("AllowAllOrigins")]
publicclassBookController:Controller
{
//todo
}
EnablingCORSattheactionlevel:
ThefollowingcodeenablestheCORSpolicyattheMVCactionmethodlevel:
[EnableCors("AllowAllOrigins")]
publicIActionResultGetAllRecords(
{
//Callsomeservicetogetrecords
returnView();
}
EnablingCORSglobally:
Globally,CORScanbeenabledbydefiningatthemiddlewarelevel,aswehaveseenthroughtheConfiguremethod.Otherwise,ifitisdefinedattheConfigureServiceslevel,enablingitgloballycanbeachievedbyusingtheCorsAuthorizationFilterFactoryobject,asshownhere:
publicvoidConfigureServices(IServiceCollectionservices)
{
services.AddCors(options=>{
options.AddPolicy("AllowAllOrigins",builder=>
builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
options.AddPolicy("AllowOnlyGet",builder=>
builder.WithMethods("GET").AllowAnyHeader().AllowAnyOrigin());
});
services.Configure<MvcOptions>(options=>
{
options.Filters.Add(newCorsAuthorizationFilterFactory("AllowOnlyGet"));
});
}
Theprecedingcodesnippetcontainstwopolicies:AllowAllOriginsandAllowOnlyGet,andthroughCorsAuthorizationFilterFactory,wecanpasstheAllowOnlyGetpolicyasthepolicynameandmakeitglobal.
CallingWCFservicesfromJavaScriptToconsumetheWCFservicemethodsfromJavaScript,weneedtoexposethemastheRESTfulservicemethodsthatacceptandreturnthedataineitherJSONorXMLformats.ThishelpsdeveloperstoconsumetheWCFservicesaseasilyastheRESTservices,andusethemwiththejQuery$.ajaxor$.getJSON(shorthandmethodof$.ajax)methods.ToexposeaWCFserviceasaRESTservice,weneedtoannotatetheWCFservicemethodswiththeWebGetorWebInvokeattributes.TheWebGetattributeismostlyusedwhenmakinganyHTTPGETrequest,whereasWebInvokeisusedforallHTTPrequestmethods.
ThefollowingcodeshowstherepresentationofusingtheWebGetattributeonaWCFoperationcontractthatreturnstheproductbasedonproductCodepassedduringthemethodcall:
[OperationContract]
[WebGet(ResponseFormat=WebMessageFormat.Json,BodyStyle=
WebMessageBodyStyle.Wrapped,UriTemplate="json/{productCode}")]
ProductGetProduct(stringproductCode);
WecanalsorepresentthesamemethodusingWebInvoke,asshowninthefollowingcode:
[OperationContract]
[WebInvoke(Method="GET",ResponseFormat=WebMessageFormat.Json,BodyStyle=
WebMessageBodyStyle.Wrapped,UriTemplate="products/{productCode}")]
ProductGetProduct(stringproductCode);
ThefollowingcodeshowstherepresentationofusingWebInvokefortheHTTPPOSTrequest:
[OperationContract]
[WebInvoke(Method="POST",ResponseFormat=WebMessageFormat.Json,RequestFormat
=WebMessageFormat.Json,BodyStyle=WebMessageBodyStyle.Wrapped,UriTemplate=
"products/SaveProduct")]
boolSaveProduct(Productproduct);
Ifyounotice,thePOSTmethodcontainsbothRequestFormatandResponseFormatattributesthattelltheserverthetypeofdataprovidedwhenmakinganyHTTPPOSTrequestandtheresponsewillbereturnedbasedontheResponseFormattypedefined.
WhenworkingwiththeRESTfulservices,makesurethatthebindingissettowebHttpBinding,asshowninthefollowingscreenshot.Alsowiththe.NETframework4andhigher,MicrosoftintroducedanotherattributeknownascrossDomainScriptAccessEnabled,whichcanbesettotruetodealwithcross-originrequests:
Moreover,inordertoenableCORS,youcanspecifystandardEndpoints,asshowninthefollowingscreenshot,undersystem.serviceModel:
Addcustomheadersasfollows.Specifyingasterisk(*)allowseverything,whereasforsecuritypurpose,origin,headers,andmethodscanbedefinedexplicitlytospecificvaluesthatareseparatedbycommas:
Thefollowingtableshowsthedescriptionofeachaccesscontrolkeys:
Accesscontrolkey Description
Access-
Control-
Allow-Origin
Thiskeyisusedtoallowtheclient'sdomainfromwheretheservicewillbeinvoked
Access-
Control-
Allow-Headers
Thiskeyisusedtospecifytheheaderspermittedwhentheclientismakingarequest
Access-
Control-
Allow-Method
Usingthiskey,theHTTPmethodsallowedwhentheclientismakingarequest
Access-
Control-Max-
Age
Thiskeytakesthevalueinsecondstoseehowlongtheresponsetothepreflightrequestcanbecachedforwithoutsendingtheanotherpreflightrequest
ToinvoketheSaveProductmethod,wecanusethejQuery$.ajax()methodandsupplythefollowingparameters,asshowninthefollowingcode.Ifyounotice,wedefinedcontentTypeaswellasdataType.ThedifferenceisthatcontentTypeisusedtotelltheserveraboutthetypeofdataclientissending,whereasdataTypeisusedtolettheserverknowthetypeofdatatheclientisexpectingtoreceiveinresponse.ThedataTypevaluescanbejson,jsonp,xml,html,orscript:
functionSaveProduct(){
varproduct={
"ProductName":"Product1",
"ProductDescription":"ThisisProductA"
};
$.ajax({
type:"POST",
url:"http://localhost/products/SaveProduct",
data:JSON.stringify(product),
contentType:"application/json",
dataType:"json",
processData:true,
success:function(data,status,xhr){
alert(data);
},
error:function(error){
alert(error);
}
});
}
Inordertomakeacalltoanotherdomain,wecanusejsonp,sotheserverwrapstheJSONdatainaJavaScriptfunction,whichisknownasacallbackfunction,andwhentheresponsecomesbacktotheclient,itwillautomaticallycallthesuccessmethod.Themodifiedversionoftheprecedingmethodtohandlecross-originrequestisshowninthefollowingcode.
Inthiscode,wemodifiedtheURLandpassedthecallback=?querystringasaparameter.Moreover,thecrossDomainattributeisusedtoensurethattherequestwillbecrossDomain.Whentheserverresponds,?specifiedinthecallbackqueryandthestringwillbereplacedbythefunctionname,suchasjson43229182_22822992,andwillcallthesuccessmethod:
functionSaveProduct(){
varproduct={
"ProductName":"Product1",
"ProductDescription":"ThisisProductA"
};
$.ajax({
type:"POST",
url:"http://localhost:4958/ProductService.svc/products/SaveProduct?
callback=?",
data:JSON.stringify(product),
contentType:"application/json",
dataType:"jsonp",
crossDomain:true,
processData:true,
success:function(data,status,xhr){
alert(data);
},
error:function(error){
alert(error);
}
});
}
Similarly,wecaninvoketheGetProductmethodasshowninthefollowingcode:
(function(){
varproductCode="Prod-001";
varwebServiceURL=
"http://localhost:4958/ProductService.svc/products/GetProduct/"+productCode;
$.ajax({
type:"GET",
url:webServiceURL,
dataType:"json",
processData:false,
success:function(data){
alert(data);
},
error:function(error){
alert(error);
}
});
});
Forcrossdomain,itcanbemodifiedasfollows:
(function(){
varproductCode="Prod-001";
varwebServiceURL=
"http://localhost:4958/ProductService.svc/products/GetProduct/"+productCode;
$.ajax({
type:"GET",
url:webServiceURL+"?callback=?",
dataType:"jsonp",
crossDomain:true,
processData:false,
success:function(data){
alert(data);
},
error:function(error){
alert(error);
}
});
});
Alternatively,fortheprecedingsolution,wecanalsooverridethecallbackfunctionnameinajsonprequest,andthevaluespecifiedinjsonpCallbackwillbeusedinsteadofcallback=?passedinaURL.ThefollowingcodesnippetcallsyourlocalfunctionwhosenameisspecifiedinthejsonpCallbackvalue:
functioncallbackFn(data){
}
(function(){
varproductCode="Prod-001";
varwebServiceURL=
"http://localhost:4958/ProductService.svc/products/GetProduct/"+productCode;
$.ajax({
type:"GET",
url:webServiceURL,
dataType:"jsonp",
crossDomain:true,
processData:false,
jsonpCallback:callbackFn,
success:function(data){
alert(data);
},
error:function(error){
alert(error);
}
});
});
SummaryInthischapter,wediscussedAjaxtechniquesandconceptsofusingtheXMLHttpRequestobject.WehaveseenthebasicarchitectureofhowtheAjaxrequestisprocessedandwhateventsandmethodsitprovides.Similarly,wealsodiscussedwhatjQueryoffersandtheextensivelibraryithasforperformingdifferenttypesoftheHTTPGETandPOSTrequests.Inthenextchapter,wewilldiscussthebasicsofTypeScript,andoneofthemostpopularclient-sideframework,Angular2.WewillalsogothroughdevelopingasimpleapplicationusingASP.NETCoreMVC6withAngular2asafrontendframeworkandEntityFramework7forbackendoperations.
Chapter5.DevelopinganASP.NETApplicationUsingAngular2andWebAPIInthischapter,wewilldevelopacompleteapplicationonASP.NETCoreusingMVC6forMVCviewsandWebAPIforwebservices.Fortheclientside,wewilluseAngular2,whichisoneofthemostpopularframeworksforclient-sidedevelopment.Angular2iswritteninTypeScript,butitprovidestheoptiontowritecodeinJavaScriptandDart.Inthischapter,wewilluseTypeScriptbecauseitadherestotheECMAScript6standard,withaprovisiontogenerateJavaScriptwhenyoubuildyourprojectinECMAScript3,ECMAScript4,andECMAScript5standards.TypeScriptisasupersetofJavaScriptandmostofthethingsarecommontoboth;infact,TypeScriptprovidessomefeaturesthatinJavaScriptarenotimplementedbymanybrowsers,exceptMozillaFirefox.
ThischapterfocusesonthebasicconceptsandtakesyouthroughasampleapplicationtolearnhowAngular2canbeusedwithASP.NETCoreandMVC6.
TypeScriptTypeScriptisalanguagedevelopedbyMicrosoftandisasupersetofJavaScript.TypeScripttranspilesintoJavaScriptatcompiletime.VisualStudio2015automaticallybuildstheTypeScriptintoJavaScriptfilesandplacestheminsideafolderconfiguredwiththeTypeScript.tsconfigconfigurationfile.ItprovidesalotmorethanJavaScriptprovides,butdeveloperscanstillusesomeofthetypesandobjectsinTypeScriptthattheyuseinJavaScript.However,TypeScriptgeneratescleanerandmoreoptimizedcode,whichisthenrunbytheAngular2framework.So,whentheTypeScriptcompiles,itgeneratesJavaScriptandstoresamapfiletohandledebuggingscenarios.SupposeyouwanttodebugyourTypeScriptcodefromVisualStudio2015;thismappingfilecontainsthemappinginformationofthesourceTypeScriptfileandgeneratedJavaScriptfilebeingruninsideyourAngularpageandthebreakpointscanbesetonyourTypeScriptfile.
CompilationarchitectureofTypeScriptTheTypeScriptcompilergoesthroughseveralstagestocompileTypeScriptfilesandgenerateJavaScriptfiles.
Thecompilationprocessstartswithapre-processor,whichdetermineswhatfilesneedtobeincludedbyfollowingreference///<referencepath=…/>tagsandimportstatements.Oncethefilesareidentified,theparserparsesandtokenizesthesourcecodeintoanAbstractSyntaxTree(AST).
AnASTrepresentsthesyntacticalstructureofthesourcecodeinatreeformatthatconsistsofnodes.ThebinderthenpassesovertheASTnodesandgeneratesandbindssymbols.Onesymboliscreatedforeachnamedentityandiftherearemultipleentitieswiththesamename,theywillhavethesamesymbol.
Symbolsrepresentnamedentitiesandmergemultiplefilesifseveraldeclarationsarefound.Torepresentaglobalviewofallthefiles,aprogramisbuild.Aprogramisthemainentrypointtothetypesystemandcodegeneration.Oncetheprogramiscreated,atypecheckerandanemittercanbecreated.
AtypecheckeristhecorepartoftheTypeScriptsystem,consolidatesallthesymbolsfrommultiplefilesintoasingleview,andbuildsasymboltable.Thissymboltablecontainsthetypesofeachsymbolidentifiedandmergedintoacommonsymbol.Atypecheckercontainscompleteinformationaboutwhichsymbolbelongstowhichnode,thetypeofaparticularsymbol,andsoon.
Finally,anemitterisusedbytheTypeScriptcompiler,throughaprogram,togeneratetheoutputfile:.js,.js.map,.jxs,ord.ts.
AdvantagesofTypeScriptThefollowingaresomecorebenefitsofusingTypeScriptwithAngular2.
SupersetofJavaScript
TypeScriptisatypedsupersetofJavaScriptthatcompilestoJavaScript.ThebasicadvantageofbeingasupersetisthatitprovidesthelatestfeaturesofJavaScriptthatmanybrowsersdonotsupportyet.Developersusefeaturessuchasasyncfunctions,decorators,andothersduringapplicationdevelopment,whichthencompileintoaJavaScriptfilethattargetstheECMAScript4orECMAScript3versions,whichbrowserscaneasilyunderstandandinterpret.
Supportforclassesandmodules
Typescriptsupportsclass,interface,extends,andimplementskeywords.
HereishowyoucandefineclassinTypeScript:
classPerson{
privatepersonId:string='';
privatepersonName:string='';
privatedateOfBirth:Date;
constructor(){}
getPersonName():string{
returnthis.personName;
}
setPersonName(value):void{
this.personName=value;
}}
HereisthetranspiledversionofTypeScriptinJavaScript:
varPerson=(function(){
functionPerson(){
this.personId='';
this.personName='';
}
Person.prototype.getPersonName=function(){
returnthis.personName;
};
Person.prototype.setPersonName=function(value){
this.personName=value;
};
returnPerson;
})();
Statictypechecking
ThemainbenefitofusingTypeScriptisstatictypechecking.Whenyoubuildyourproject,theTypeScriptcompilerchecksthesemanticsandgiveserrorsatcompiletimetoavoidruntime
errors.Forexample,thefollowingcodewillgiveanerroratcompiletime:
varname:string
name=2;//giveerror
HereisanotherexamplethatextendsthePersonclassandgivesatypemismatchederroratcompiletype:
classPerson{
constructor(name:string){
}
}
classEmployeeextendsPerson{
constructor(){
super(2);//error
}
}
ECMAScript6featuresupport
Atthetimeofwriting,mostbrowsersstilldonotsupportECMAScript6completely,butwithTypeScript,wecanwritecodeanduseECMAScript6features.AsECMAScript6supportsbackwardcompatibility,wecansetthetargetversionthroughtheTypeScriptconfigurationfile,whichgeneratestheJavaScriptbasedontheversionspecified.ThishelpsdeveloperstowritecodeusingECMAScript6featuresandtheoutputJSfileswillbegeneratedinECMAScript3,ECMAScript4,orECMAScript5standards.
Optionaltyping
TypeScriptsupportsstricttypingandvalidatestypesatcompiletype,butusingstricttypingisnotmandatory.Youcanevendeclareavariablewithoutspecifyingitstypeanditwillberesolvedwhenthevalueisassigned.
DeclaringtypesinTypeScript
Hereisanexampleofdeclaringavariablewithoutitstype:
privatesNo=1;
privatetext='Helloworld';
Hereisanexampleofdeclaringavariablewithtypes:
privatesNo:number=1;
privatetext:string='Helloworld';
CoreelementsofTypeScriptThissectiondiscussesthecoreelementsofTypeScript:
DeclaringvariablesTypesClassesandinterfacesFunctionsIteratorsModulesandnamespaces
Declaringvariables
VariabledeclarationisequivalenttowhatwedoinJavaScript.However,asTypeScriptfollowstheECMAScript6standard,itprovidesstrongtypesaswell.Strongtypescanbedeclaredbynamingavariablefollowedbyacolon(:)anditstype.
HereisasimplevariabledeclarationinJavaScript:
varname;
ItcanbedeclaredinTypeScriptasfollows:
varname:string;
VariablescanbeinitializedinTypeScriptasfollows:
varname:string="HelloWorld";
Types
MostofthetypesavailableinTypeScriptareequivalenttoJavaScripttypes.Thefollowingtablecontainsalistofallavailabletypes,withacodesnippetforusingthem:
Type Description Codesnippet
NumberTypeScriptprovidesanumbertypethatholdsalltypesofdecimal,hexadecimal,binary,andoctalvalues.
letdecimal:number=2;
lethex:number=0x001;
letbinary:number=
0b1010;
letoctal:number=0o744;
StringIt'sthesameasweuseinanyotherlanguage.Stringvaluescanbesurroundedwithsingleordoublequotes.
letx:string='Hello';
lety:string="Hello";
ArrayTypeScriptsupportssimplearrays,andgenericarraysaswell.
letcountries=['US',
'UK','UAE'];
letcountries<string>=
['US','UK','UAE'];
Tuple Throughtuples,wecandefineanarraywhoseelementtypesareknown.
letval:[string,number,
Date];
val=['HelloWorld',10,
newDate()];
val[0];//printHelloWorld
EnumUsedtogivenamestothenumericalvalues.Bydefault,thefirstvaluespecifiedis0butcanbesetexplicitlytoanynumber.
enumStatus{InProcess,
Active,Ready,Success,
Error}
lets:Status=
Status.Active;
//specifyvalues
explicitly
enumStatus{InProcess=1,
Active=2,Ready=3,
Success=4,Error=5}
Any Thistypecanbeusedincaseswherethetypeisnotknownandisdependentonanassignment.
letx:any;
x=['Hello',1,2];
//tuple;
x=1;//number
x='HelloWorld';//string
Classesandinterfaces
Thefollowingarethewaysofdefininginterfaces,derivingclassesandinterfaces,andwritinggenericclassesinTypeScript.
Defininginterfaces
JustlikeC#,TypeScriptallowsyoutodefineinterfacesthatcanbeimplementedinTypeScriptclassesandforcetheimplementerclasstoimplementallthemembersdefinedintheinterface.
HereisthecodetodefineaninterfaceinTypeScript:
interfaceIShape{
shapeName:string;
draw();
}
classTodoServiceimplementsIShape{
constructor(privatehttp:Http){
this.shapeName="Square";
}
shapeName:string;
draw(){
alert("thisis"+this.shapeName);
}
}
Derivingclassesandinterfaces
LikeC#,classesandinterfacescanbeextendedbyderivingfrombaseclassesorinterfaces.Toextendanyclass,wecanusetheextendskeyword,andforaninterfacewecanuseimplements,asshownhere:
interfaceIPerson{
id:number;
firstName:string;
lastName:string;
dateOfBirth:Date;
}
interfaceIEmployeeextendsIPerson{
empCode:string;
designation:string;
}
classPersonimplementsIPerson{
id:number;
firstName:string;
lastName:string;
dateOfBirth:Date;
}
classEmployeeextendsPersonimplementsIEmployee{
empCode:string;
designation:string;
}
Inthepreviouscodesnippet,wehavedeclaredtwointerfaces,IPersonandIEmployee.IPersoncontainscommonpropertiessuchasid,firstName,lastName,anddateOfBirth,whichcanbeusedinallderivedinterfaces,suchasIEmployeeoranyother.
Then,wehaveimplementedtheIPersoninterfaceinthePersonclass,andfinallyderivedtheEmployeeclassfromPersonandimplementedtheIEmployeeinterface.Ifyouhavenoticed,asthePersonclassalreadyimplementstheIPersoninterface,wedonothavetoimplementitagainandonlyimplementtheproperties,suchasempCodeanddesignationintheEmployeeclass.
Genericclasses
Genericclassesareusefultodefineaparticularclasswhosetypeisgenericanddeterminedwhenitiscalled.Genericclassescanbedefinedbyusing<T>followedbytheclassname.
Hereisasimpleexamplethatshowsthegenericclassprocess,whichcanworkasperthetypespecifiedduringinitialization.ThegetTypeInfo()methodwillprintaspecificmessagebasedonthetypeofobjectinitialized:
classProcess<T>{
value:T;
getTypeInfo(){
if(typeofthis.value=="string")
console.log("Typeisastring");
elseif(typeofthis.value=="number")
console.log("Typeisanumber");
elsealert("typeisunknown");
}
}
letpString=newProcess<string>();
pString.getTypeInfo();//printTypeisastring
letpNumber=newProcess<number>();
pNumber.getTypeInfo();//printTypeisanumber
Functions
FunctionscanbedefinedinthesamewayasJavaScript.TypeScriptsupportsbothnamedandanonymousfunctions.InTypeScript,functionparameterscanbetypedparameters,asshownhere:
functionconcat(x:string,y:string):string{
returnx+""+y;
}
Functionscanalsohaveoptionalparametersandcanbedeclaredbyusing(?)asshownhere:
functionconcat(x:string,y:string,z?:string):string{
returnx+""+y+""+z;
}
Withthisoption,wecancallthefunctionbypassingtwoparametersorthreeparametersbecausethethirdparameterisoptional.
Genericfunctions
TypeScriptallowsyoutodefinegenericfunctions,whichacceptanytypeofargumentorreturntype.Genericfunctionscanbedefinedbyspecifyingthe<T>afterthefunctionname,asshowninthefollowingcode,andtheargumentsorreturnedtypecanalsobegenericandrefertothesameTtype.Thisisusefultodefineaparticularfunctionthatacceptsalltypesofargumentsandworksasexpected.Thefollowingexampleshowstheprocessfunctionbasedonthetypeofargumentconcatenatedoradded:
functionprocess<T>(x:T,y:T):string{
if(typeofx=="string")
returnx+""+y;
elseif(typeofx=="number")
return"Sumis:"+x+y;
else
return"Typeinunknown";
}
Iterators
Apartfromstandardloopssuchasfor,while,TypeScriptalsoprovidestwotypesofforstatements,for..ofandfor..in.Bothstatementsiterateoncollections.Thedifferencebetweentheseisthatthefor..ofstatementreturnsthekeysoftheobjectwhereasfor..inreturnsthevalues:
countries=['USA','UK','UAE'];
//thisloopwilldisplaykeys0,1,2
for(letindexinthis.countries){
console.log(index);
}
//thisloopwilldisplayvaluesUSA,UK,UAE
for(letindexofthis.countries){
console.log(index);
}
Modulesandnamespaces
ECMAScript6introducestheconceptofmodules.Modulescanbethoughtofaslogicalcontainersthathavetheirownscope.Anyclass,variable,ormethoddeclaredinsideamoduleisscopedwithinitsowncontainerandaccessibletoothermodulesonlyifitisallowedexplicitly.InTypeScript,anyfilecontainingimportsorexportsatatoplevelareconsideredmodules.Modulesimportoneanotherusingamoduleloader,andatruntimethemoduleloaderisresponsibleforloadingallthedependenciesofthemoduledefinedwithinit.Modulescanbeexportedusingtheexportkeywordandothermodulescanimportitusingtheimportkeyword.
HereisanexampleofdefiningandexportingamoduleinTypeScript:
//BaseManager.ts
exportclassBaseManager{
}
Tousethemoduleinsomeotherarearequiresyoutousetheimportkeyword,asshownhere:
//ServiceManager.ts
exportclassServiceManagerextendsBaseManager{
}
Modulescanbeimportedbyusingtheimportkeyword.Whenimportinganymodule,youhavetousetheimportkeyword,followedbytheclassnameinbrackets{},followedbytheactualfilenamethatcontainstheclass.Forexample,thefollowingcodeshowsthewayofimportingServiceManagerintoMain.ts:
//Main.ts
import{ServiceManager}from"./ServiceManager"
Wecanalsogiveafriendlynametotheclass,asfollows:
//Main.ts
import{ServiceManagerasserviceMgr}from"./ServiceManager"
Namespaces,ontheotherhand,arethelogicalmodulestocategorizeclasses,methods,andsoon.JustlikeC#,theycanbedefinedbyusinganamespacekeyword.OnenamespacecanbesplitacrossdifferentTypeScriptfilesandthisgivesdevelopersahandywayofcategorizingspecificfilestoasinglenamespace.ThefollowingexampleshowsthewaytocategorizeTypeScriptfilesintoasinglenamespaceandusingthem:
//PersonManager.ts
namespaceBusinessManagers{
exportclassPersonManager{}
}
//SecurityManager.ts
namespaceBusinessManagers{
exportclassSecurityManager(){
}
}
//main.ts
///<referencepath="personmanager.ts"/>
///<referencepath="SecurityManager.ts"/>
personObj=newBusinessManagers.PersonManager();
securityObj=newBusinessManagers.SecurityManager();
Ifyounotice,wehaveusedatriple-slashdirective,whichisusedtorefertothedependentfilesbeforeexecutingthecodeintheTypeScriptfile.Therefore,asthesefilespersistsomewhereelse,wehavetoexplicitlyreferencethemintheprecedingcode.
Tosummarize,namespacesareabettermethodtousethanmodulesastheycategorizefileslogicallybyprovidingafriendlyname,andallowdeveloperstostructurecodeproperlywhenworkingwithmedium-to-large-sizedprojects.
Wecanalsogiveashortenednametoanamespaceifit'sunfriendlybyusinganimportkeywordasshownhere:
namespaceBusinessManagers{
exportclassPersonManager{
}
}
importmgr=BusinessManagers;
letpersonObj=newmgr.PersonManager();
ToLC:Applycodeto:
"namespaceBusinessManagers{
exportclassPersonManager{
}
}
importmgr=BusinessManagers;
letpersonObj=newmgr.PersonManager();
SothiswindsupthecoretopicsofTypeScript.TolearnmoreaboutTypeScript,youcanrefertohttp://www.typescriptlang.org/.
IntroductiontoAngular2Angular2isaclient-sideframeworktobuildwebapplications.Itisveryflexibleintermsofbeingusedwithbothmobileandwebplatforms.ThebasicadvantageofusingAngularisthatitfollowstheECMAScript6standardanddeveloperscandoobject-orientedprogramming,defineclassesandinterfaces,implementclasses,anddefinedatastructuresusingPlainOldJavaScriptObjects(POJO)forbindingdata.Anotherbigadvantageintermsofperformanceistheunidirectionaldataflow.UnlikeAngular1.x,Angular2providesboththeoptionofdoingtwo-waydatabindingorunidirectionaldatabinding.Incertaincases,unidirectionalbindingisgoodforperformance.Forexample,whensubmittingaform,twowaybindingswithcontrolsmaybeoverkill.
Angular2architectureAngular2consistofanumberofcomponents.Eachcomponentcanbeboundtothepagebyeitheraselector,forexample<my-app></my-app>,oraroutingmodule.Eachcomponenthasaselector,templateHTMLortemplatereferencelink,directives,providers,andacontrollerclasswhosepropertiesandmethodscanbeaccessedintheassociatedview.Whenthewebapplicationfirststarts,System.importloadsthemaincomponentoftheapplication,whichbootstrapstherootcomponent.HereisasamplemaincomponentbootstrappinganAngularapp:
//LoadingmodulethroughImportstatement
Import{AppComponent}from'pathofmycomponent'
bootstrap(AppComponent,[Providers]);
Providerscanbedefinedinsidesquarebrackets.Therearevariousprovidersavailable,whichwewilldiscussinalaterchapter.
Thisbootstrapobjectisinangular2/platform/browser,whichcanbeimportedintotheTypeScriptfilewiththeimportcommand:
import{bootstrap}from'angular2/platform/browser';
ThisbootstrapobjectdirectsAngulartoloadthecomponentdefinedinit.Whenthecomponentisloaded,alltheattributesormetadatadefinedforthecomponentareevaluated.Eachcomponentshouldhavethe@Componentannotation,somepropertiestodefinemetadataaboutthecomponent,andoneormoreclassestermedascomponentcontrollersthatcontainpropertiesandmethodsaccessiblebythetemplatedefinedinthe@ComponenttemplateortemplateUriproperties.
Hereisasampleapp.component.tsthatcontainsaselector,atemplate,andaclass,AppComponent:
//app.component.ts
import{Component,View}from'angular2/core';
import{bootstrap}from'angular2/platform/browser';
@Component({
selector:"my-app",
template:`<p>Thisisafirstcomponent</p>`,
})
classAppComponent{
}
bootstrap(AppComponent);
Eventsofcomponentlifecycle
Whenthecomponentinitializes,itgoesthroughseveraleventsandhasaverystructuredlifecycleprocess.Wecanimplementtheseeventstodospecificoperations.Thefollowingtableshowsthelistofeventswecanuseinourcomponentcontrollerclass:
Event Description
ngOnInit()Itiscalledafterthecomponentisinitializedandthecontrollerconstructorisexecuted.
ngOnDestroy()Itisusedtocleanupresourceswhenthecomponentisdisposedof.
ngDoCheck()Itisusedtooverridethedefaultchangedetectionalgorithmforadirective.
ngOnChanges(changes)
Itisinvokedwhenanyofthecomponentselectorpropertyvaluesgetmodified.(Custompropertiesoftheselectorscanbedefinedthroughinputs.)
ngAfterContentInit()Itisinvokedwhenthedirective'scontentisinitialized.(Directivesaredefinedlater.)
ngAfterContentChecked() Itisinvokedeverytimethedirective'scontentischecked.
ngAfterViewInit() Itisinvokedwhentheviewiscompletelyinitialized.
ngAfterViewChecked() Itisinvokedoneverycheckofyourcomponent'sview.
Modules
Amodulerepresentsacontainerthatcontainsclasses,interfaces,andmore,toexportfunctionality,soothermodulescanbeimportedusingtheimportstatement.Forexample,hereismath.ts,usedtoperformdifferentarithmeticoperations:
//math.ts
import{Component}from'angular2/core';
@Component({
})
exportclassMathService{
constructor(){
}
publicsum(a:number,b:number):number{
returna+b;
}
publicsubtract(a:number,b:number):number{
returna-b;
}
publicdivide(a:number,b:number):number{
returna/b;
}
publicmultiply(a:number,b:number):number{
returna*b;
}
}
Components
Acomponentisacombinationofthe@Componentannotationtodefinemetadatapropertiesandtheassociatedcontrollerclassthatcontainstheactualcode,suchastheclassconstructor,methods,andproperties.The@Componentannotationcontainsthefollowingmetadataproperties:
@Component({
providers:string[],
selector:string,
inputs:string[],
outputs:string[],
properties:string[],
events:string[],
host:{[key:string]:string},
exportAs:string,
moduleId:string,
viewProviders:any[],
queries:{[key:string]:any},
changeDetection:ChangeDetectionStrategy,
templateUrl:string,
template:string,
styleUrls:string[],
styles:string[],
directives:Array<Type|any[]>,
pipes:Array<Type|any[]>,
encapsulation:ViewEncapsulation
})
CorepropertiesofAngular2componentsWhendefiningacomponent,wecanspecifyvariousproperties,aslistedpreviously.HerewewillseesomeofthecorepropertiesthatareoftenrequiredwhencreatingAngular2components:
TemplatesandselectorsInputsandoutputsDirectivesProviders
Templatesandselectors
Thefollowingrealexamplecontainsthetemplateandtheselectordefinedinthecomponentclass.Whenthebuttonisclicked,itwillcallthelogMessage()method,whichprintsthemessageinthe<p>element.Ifyounotice,wehavenotusedtheexportkeywordwiththeclassbecausewehavealreadybootstrappedthecomponentonthesamefileandthiscomponentdoesnotneedtobereferencedanywhereelse:
import{Component,View}from'angular2/core';
import{bootstrap}from'angular2/platform/browser';
@Component({
selector:"my-app",
template:"<p>{{message}}</p><button(click)='logMessage()'>Log
Message</button>"
})
classAppComponent{
logMessage(){
this.message="HelloWorld";
}
message:string="";
}
bootstrap(AppComponent);
TheappselectorcanbeusedanywhereintheHTMLorindex.cshtmlpageifworkingonanASP.NETproject,andthetemplatewillberenderedinsideit.Hereisanexampleofusingthecustomtagmy-app:
<html>
<body>
<my-app></my-app>
</body>
</html>
Oncethepageruns,itwillrendertheoutputwiththisgeneratedsource:
<html>
<body>
<p>HelloWorld</p>
<button(click)='logMessage()'>LogMessage</button>
</body>
</html>
Inputsandoutputs
Inputsallowdeveloperstospecifythecustomattributesmappedtosomepropertyofthecomponentclassdownwardinthehierarchyofcomponents,whereasoutputsareusedtodefinecustomeventhandlersonthecomponentthatcanberaisedupwardinthehierarchyofcomponents.Inshort,inputsareusedtosenddatafromparenttochildcomponents,whereasoutputsareusedtoinvokeeventsfromchildtoparentcomponents.Inthepreviousexample,wesawhowselectorscanbeused,andtheassociatedtemplateisrenderedinplaceofaselector,withtheprovisionofhavingallthemembersofthecomponentclassavailable.Incertaincases,wehavetospecifysomeattributesinourcustomselectortopassthevaluetohandleparticularactions.Forexample,wemayneedsomeattributeintheprevious<my-app>tagtospecifytheloggingtype,suchastologontoadeveloper'sconsoleorshowanalertmessage.
Usinginputs
Inthisexample,wewillcreatetwoinputattributes,logToConsoleandshowAlert.Wecandefineinputattributesinthe@Componentannotation.Thefollowingcodesnippetistheseparatecomponentdefinedinchild.component.tsandcontainstheselectoraschild;thetemplatedisplaystheBooleanvaluesofthelogToConsoleandshowAlertattributesspecifiedinthechildtag.Theinputscontainthelistofstringvariablesthatwillbedefinedasthechildtagattributes:
//child.component.ts
import{Component}from'angular2/core';
@Component({
selector:'child',
template:`<div>LogtoConsole:{{logToConsole}},ShowAlert:{{showAlert}}
<button(click)="logMessage()">Log</button></div>`,
inputs:['logToConsole','showAlert'],
})
HereistheChildComponentclassthatcontainsthelogToConsoleandshowAlertBooleanvariables.Thesevariablesactuallyholdthevaluessuppliedfromthenotificationtag.Finally,wehavethelogMessage()methodthatwillbeinvokedonabuttonclickeventandeitherlogthemessageonthedeveloper'sconsoleorshowanalertmessagebasedonthevaluethathasbeensetbytheparentcomponentinthehierarchy:
exportclassChildComponent{
publiclogToConsole:boolean;
publicshowAlert:boolean;
logMessage(message:string){
if(this.logToConsole){
console.log("Consoleloggingisenabled");
}
if(this.showAlert){
alert("Showingalertmessageisenabled");
}
}
}
Intheapp.component.tsfile,wherewehavethemainAppComponentdefined,wecanusethechildselectorasshowninthefollowingcode.Whendefiningthechildselector,wecansetthevaluesforcustominputsdefinedintheChildComponent,logToConsoleandshowAlert.Thiswaytheparentcomponentcanspecifythevaluestothechildcomponentthroughinputs.HereisthecompletecodeofAppComponent:
//app.component.ts
import{Component,View}from'angular2/core';
import{bootstrap}from'angular2/platform/browser';
import{ChildComponent}from'./child.component';
@Component({
selector:"my-app",
template:`<child[logToConsole]=true[showAlert]=true></child>`,
directives:[ChildComponent]
})
exportclassAppComponent{
}
bootstrap(AppComponent);
Tip
WhenusingtemplatetodefineHTML,wecanuseabacktick(`)ratherthanthedoublequotes('')orsinglequotes('),asshowntheprecedingexample.ThisallowstheHTMLcontenttospanmultiplelines.
Usingoutputs
Outputsareusedtoinvokeeventsontheparentcomponentfromchildcomponentsinthehierarchyofcomponents.WewillmodifytheprecedingexampleandaddtheoutputseventintheChildComponent,thenregisteritintheAppComponentusingtheChildComponentselector.
HereisthemodifiedcodesnippetforChildComponent:
//child.component.ts
import{Component,EventEmitter,Output}from'angular2/core';
@Component({
selector:'child',
template:`<div>LogtoConsole:{{logToConsole}},ShowAlert:{{showAlert}}
<button(click)="logMessage()">Log</button></div>`,
inputs:['logToConsole','showAlert']
})
exportclassChildComponent{
publiclogToConsole:boolean;
publicshowAlert:boolean;
@Output()clickLogButton=newEventEmitter();
logMessage(message:string){
this.clickLogButton.next("Fromchild");
}
}
The@OutputpropertylistsclickLogButtonasacustomeventthatChildComponentcanemit,whichitsparentAppComponentwillreceive.
WehaveaddedEventEmitterintheimportstatement.EventEmitterisabuilt-inclassthatshipswithAngularandprovidesmethodsfordefiningandfiringcustomevents.OncethelogMessage()methodisexecuted,itwillexecutetheclickLogButton.next()methodfromtheChildComponent,whichfinallycallstheeventregisteredintheAppComponent.
WehaveaddedtheclickLogButtonintheAppComponent,asshowninthefollowingcode.InAngular2,wecanspecifytheeventbyspecifyingtheeventnameinbrackets()followedbythemethodthatwillbecalledwhentheeventisraised.Thisishowtheeventisregistered.Here,logMessageisthelocalmethoddefinedintheAppComponent:
(clickLogButton)="logMessage($event)"
HereisthecodesnippetforAppComponent:
//app.component.ts
import{Component,View}from'angular2/core';
import{bootstrap}from'angular2/platform/browser';
import{ChildComponent}from'./child.component';
@Component({
selector:"my-app",
template:`<child[logToConsole]=true[showAlert]=true
(clickLogButton)="logMessage($event)"></child>`,
directives:[ChildComponent]
})
exportclassAppComponent{
logMessage(value){
alert(value);
}
}
bootstrap(AppComponent);
ThelogMessagemethodisthemethodthatwillbeinvokedwhentheeventisraisedfromtheChildComponent.
Directives
DirectivesarecustomtagsthatrendertheHTMLatruntimebutencapsulatetherenderingcontentinthedirectiveitself.WecanrelateittothetaghelpersinASP.NET.Therearethreekindsofdirectives,components,structuraldirectives,andattributedirectives:
Components:Itisadirectivewithatemplate.Structuraldirective:ItisadirectivetoaddorremoveDOMelements.Therearesomebuilt-instructuraldirectivesthatAngularprovides.DirectivessuchasngIf,ngSwitch,andngForarestructuraldirectives.
Attributedirective:ItchangestheappearanceofanyDOMelement.CreatingasimpleHelloWorlddirective
Directivescanbecreatedinasimpleway,asacomponentiscreated,andcanbereferencedinthecallingcomponentthroughitsselectortag.
HereisanexampleofHelloWorldComponentthatdefinesasimpledirectivetodisplaya"Helloworld"messageintheheadingformat:
//helloworld.component.ts
import{Component}from'angular2/core';
@Component({
selector:"helloworld",
template:"<h1>Helloworld</h1>"
})
exportclassHelloWorldComponent{
}
Thefollowingexampleisthecomponentthatusesthisdirective.Whenusinganydirective,ithastobefirstimportedthroughtheimportstatement,thenthe@Componentmetadatapropertyneedstobesettoaccessitintheassociatedtemplate:
import{Component,View,provide,Inject}from'angular2/core';
import{bootstrap}from'angular2/platform/browser';
import{HelloWorldComponent}from'./helloworld.component';
@Component({
selector:"my-app",
template:`<helloworld></helloworld>`,
directives:[,HelloWorldComponent],
})
exportclassAppComponent{
}
bootstrap(AppComponent);
Thisdirectivecanbeusedonthepageasfollows:
<helloworld></helloworld>
Structuraldirectives
StructuraldirectivescanbeusedtoaddorremoveDOMelements.Forexample,wecanaddthelistofcountriesasatablethrough*ngFor,asshowninthefollowingcode,andhideorunhidethedivthroughthe*ngIfdirective:
<div*ngIf="display">
<table>
<thead>
<tr>
<th>
Country
</th>
<th>
Currency
</th>
</tr>
</thead>
<tbody*ngFor="#countryofcountries">
<tr><td>{{country.CountryName}}</td><td>{{country.Currency}}</td></tr>
</tbody>
</table>
</div>
Hereisthebackendcountries.component.ts,whichusestheHTTPmoduletocalltheASP.NETWebAPIservice.Itreturnsalistofcountries,whichisassignedtothecountriesarray.Thedisplaydefaultvalueissettotrue,whichgeneratesthetable.Bysettingthedisplayvaluetofalse,thetablewillnotbegenerated:
///<referencepath="../../node_modules/angular2/typings/browser.d.ts"/>
import{Component}from'angular2/core';
import{Http,Response}from'angular2/http';
@Component({
selector:'app',
templateUrl:'Countries'
})
exportclassTodoAppComponent{
countries=[];
display:boolean=true;
//constructor
constructor(privatehttp:Http){
}
//PageInitializedEventHandler
ngOnInit(){
this.getCountries();
}
getCountries(){
this.http.get("http://localhost:5000/api/todo").map((res:Response)=>
res.json())
.subscribe(data=>{
this.countries=data;
},
err=>console.log(err),
()=>console.log("done")
);
}
}
ThisishowstructuraldirectivescanbeusedinAngular2.Inthefollowingchapter,wedevelopasampleapplicationanddiscusseachartifactformakingHTTPGETandPOST
requestsusingAngular2.
Attributedirective
Anattributedirectiverequiresbuildingacontrollerclassannotatedwith@Directiveanddefinesaselectortoidentifytheattributeassociatedwithit.Inthefollowingexample,wewilldevelopasimplemyFontdirectivethatchangesthetexttoitalicwhenitisappliedtoanypageelements.Hereisthefont.directive.tsfile:
import{Directive,ElementRef,Input}from'angular2/core';
@Directive({selector:'[myFont]'})
exportclassFontDirective{
constructor(el:ElementRef){
el.nativeElement.style.fontStyle='italic';
}
}
Foreachmatchingelementonthepage,AngularcreatesanewinstanceandinjectsElementRefintotheconstructor.ElementRefisaservicethroughwhichwecandirectlyaccesstheelementthroughitsnativeElementpropertyandaccessotherattributes.Intheprecedingcodesnippet,wearechangingthefontstyletoitalicfortheelementsthathavethemyFontdirectiveapplied.
Onthepagelevel,itcanbeusedasfollows:
<pmyFont>myFontisanAttributedirective</p>
Providers
ProvidersareusedtoregisterthetypesthatgetsinstantiatedthroughthedependencyinjectionframeworkofAngular2.Whenacomponentisinitialized,Angularcreatesadependencyinjectorwhichregistersallthetypesspecifiedintheprovidersarray.Thenattheconstructorlevel,ifthereisanytypedefinedintheprovidersarray,itwillgetinitializedandinjectedintotheconstructor.
ThefollowingexampleisMathComponent,whichwillbeinjectedintothemainappcomponentconstructorandcallthesummethodtoaddtwonumberstogether:
//math.component.ts
import{Component}from'angular2/core';
@Component({})
exportclassMathComponent{
publicsum(a:number,b:number):number{
returna+b;
}
publicdivide(a:number,b:number):number{
returna/b;
}
publicsubtract(a:number,b:number):number{
returna-b;
}
publicmultiply(a:number,b:number):number{
returna*b;
}
}
ThefollowingexampleisAppComponent,showinghowtoimportamathcomponent,thendefiningtheproviderandinjectingitattheconstructorlevel:
//app.component.ts
import{Component,View}from'angular2/core';
import{bootstrap}from'angular2/platform/browser';
import{MathComponent}from'./servicemanager.component';
@Component({
selector:"my-app",
template:"<button(click)="add()">Log</button>",
providers:[MathComponent]
})
exportclassAppComponent{
obj:MathComponent;
constructor(mathComponent:MathComponent){
this.obj=mathComponent;
}
publicadd(){
console.log(this.obj.sum(1,2));
}
}
bootstrap(AppComponent);
OtherprimitivetypescanalsobeinjectedinaslightlydifferentwayusingtheInjectAngularmodule.Wecanalsodefineatypeusingtheprovidekeyword,whichtakesakeyandthevalue:
providers:[provide('Key',{useValue:'HelloWorld'})]
Theprecedingsyntaxcanalsobeusedwhendefiningtypesinproviders,asfollows:
providers:[provide(MathComponent,{mathComponent:MathComponent})]
Oneofthemainbenefitsofdefiningproviderswiththeprovidekeywordiswhentesting.Whentestingapplications,wecanreplacetheactualcomponentswiththemockortestcomponents.Forexample,supposewehaveaclassthatcallssomeSMSservicetosendSMSusingsomepaidgateway,andinthetestingcyclewedon'twanttousetheproductionSMSgatewaycomponent,butratherwewouldliketohavesomecustomtestcomponentthatjustinsertstheSMSintoalocaldatabase.Inthiscase,wecanassociatesomemockclass,suchasSMSTestComponent,toperformtestingscenarios.
Thefollowingexampleinjectsthestringvalueintotheconstructor.WeneedtoaddtheInjectmoduleasspecifiedinthefollowingcode,andthenuse@Injecttoinjectthevalueassociatedtothekey:
//app.component.ts
import{Component,View,provide,Inject}from'angular2/core';
import{bootstrap}from'angular2/platform/browser';
import{MathComponent}from'./servicemanager.component';
@Component({
selector:"my-app",
template:`button(click)="logMessage()">Log</button>`,
providers:[MathComponent,provide('SampleText',{useValue:'SampleValue'})]
})
exportclassAppComponent{
obj:MathComponent;
Val:string;
constructor(mathComponent:MathComponent,@Inject('SampleText')value){
this.obj=mathComponent;
this.Val=value;
}
publiclogMessage(){
alert(this.kVal);
}
}
bootstrap(AppComponent);
DependencyinjectioninAngularAngularchainsdependencyinjectionandinjectscomponentsintothechildcomponentsiftheyaredefinedintheprovidersarrayoftheparentcomponent.However,achildcomponentcandefinethesamecomponentinitsownprovidersarray.Thescopeofthecomponenttravelsthroughthechainofcomponents.However,componentsthataredefinedintheviewprovidersarrayaren'tinjectedorinheritedbythechildcomponentsinthehierarchicalchain.
Let'stakeasimpleexamplethatcontainsonemaincomponentinapp.tsandAppComponentdefinestwoproviders:ChildComponentandMathComponent.ChildComponentisthechildoftheparentcomponent,whereasMathComponentisusedinboththeparentandchildcomponents.Ifyounotice,inthefollowingcodesnippet,wehavenotspecifiedtheMathComponentintheprovidersarrayoftheChildComponent,andasitisdefinedintheParentComponent,itisalreadyinjectedbytheAngulardependencyinjectionmodule.
HereisthecodesnippetforAppComponent(parent):
//app.component.ts
import{Component}from'angular2/core';
import{bootstrap}from'angular2/platform/browser';
import{MathComponent}from'./servicemanager.component';
import{ChildComponent}from'./child.component';
@Component({
selector:"my-app",
template:`<button(click)="callChildComponentMethod()">Log</button>`,
providers:[MathComponent,ChildComponent]
})
exportclassAppComponent{
childObj:ChildComponent;
constructor(childComponent:ChildComponent){
this.childObj=childComponent;
}
publiccallChildComponentMethod(){
this.childObj.addNumbers(1,2);
}
}
bootstrap(AppComponent);
ThefollowingisthecodesnippetforMathComponent,whichcontainssomebasicarithmeticoperations:
//math.component.ts
import{Component}from'angular2/core';
@Component({})
exportclassMathComponent{
publicsum(a:number,b:number):number{
returna+b;
}
publicdivide(a:number,b:number):number{
returna/b;
}
publicsubtract(a:number,b:number):number{
returna-b;
}
publicmultiply(a:number,b:number):number{
returna*b;
}
}
Finally,hereistheChildComponentcode,whichdoesnothavetheMathComponentproviderdefinedintheprovidersarray:
//child.component.ts
import{Component}from'angular2/core';
import{MathComponent}from'./servicemanager.component';
@Component({
selector:'child-app',
template:'<h1>HelloWorld</h1>'
})
exportclassChildComponent{
obj:MathComponent;
constructor(mathComponent:MathComponent){
this.obj=mathComponent;
}
publicaddNumbers(a:number,b:number){
alert(this.obj.sum(a,b));
}
}
RoutinginAngularRoutinghasanessentialrolewhenworkingwithlargeapplications.Routingisusedtonavigatetodifferentpages.Routingcanbedefinedinthreesteps:
1. Define@RouteConfigatanycomponentlevel:
@RouteConfig([
{path:'/page1',name:'Page1',component:Page1Component,useAsDefault:
true},
{path:'/page2',name:'Page2',component:Page2Component}]
)
2. Usethe[routerLink]attributeontheanchorHTMLtagandspecifytheroutenameconfiguredin@RouteConfig.
3. Finally,addthe<router-outlet>tagtorenderthepageonthecurrentnavigatedroute.
Thefollowingexamplecontainstwocomponents,Page1ComponentandPage2Component,andthemainAppComponenthasroutingdefinedlikethis:
//app.component.ts
import{Component}from'angular2/core';
import{RouteConfig,ROUTER_DIRECTIVES}from'angular2/router';
import{Page1Component}from'./page1.component';
import{Page2Component}from'./page2.component';
@Component({
selector:"my-app",
template:`{{name}}
<a[routerLink]="['Page2']">Page2</a>
<router-outlet></router-outlet>`,
directives:[ROUTER_DIRECTIVES],
})
@RouteConfig([
{path:'/',name:'Page1',component:Page1Component,useAsDefault:true},
{path:'/page2',name:'Page2',component:Page2Component}]
)
exportclassAppComponent{
}
Intheprecedingcode,firstweimportedtheRouteConfigandROUTER_DIRECTIVESfromangular2/routerandthendefinedtheRouteConfigforpage1andpage2.Intheinlinetemplate,weplacedtheanchortaganddefinedtheroutenameforpage2.Whentheapplicationruns,page1issetasadefaultpageonarootpath/,sothepage1contentwillbedisplayedinplaceoftherouteroutlet.WhentheuserclicksonthePage2link,thepage2contentwillberenderedinthesameplace.
Hereisthecodeofpage1.component.ts:
//page1.component.ts
import{Component}from'angular2/core';
@Component({
template:'<h1>Page1Content</h1>'
})
exportclassPage1Component{
}
Hereisthecodeofpage2.component.ts:
//page2.component.ts
import{Component}from'angular2/core';
@Component({
template:'<h1>Page2Content</h1>'
})
exportclassPage2Component{
}
Developingato-doapplicationinASP.NETCoreWehavelearnedthecorefeaturesofAngular2andhowtowriteprogramsinTypeScript.Nowit'stimetodevelopasimpleto-doapplicationusingAngular2andASP.NETCore.ASP.NETCoreisthelatestwebdevelopmentplatformfromMicrosoft,whichismoreoptimizedandmodularthanpreviousversionsofASP.NET.Itprovidesanoptiontousethemachine-wide.NETFramework,oranew.NETCorewhichrunsonanapp-by-appbasisandevencontainstheframeworkbinariesinthepublishedwebapplicationfolderitself.WiththenewASP.NETCore,wearenotdependentforrunningourapplicationonIIS,andthereareseveralotherserversprovidedtoruncross-platformusingKestrel.TolearnmoreaboutASP.NETCore,pleaserefertohttp://docs.asp.net.
Wewillgothroughastep-by-steptutorialthatleadstoaworkingto-doapplication.Thefollowingscreenshotshowasnapshotofthemainpage.Oncetheuserlogsin,itwillshowalistofalltheto-doitemsavailable.Usercanaddanewto-doitembyclickingaCreateTodobuttonanddeletingtheexistingoneaswell.Wewillnotbecoveringthesecurityauthenticationandauthorizationmoduleinthischapter,insteadfocusingonhowtouseAngular2withASP.NETCore:
Inthisapplication,wewillhavethreeprojects.TodoWebAppcallstheTodoServiceApp,andCommonisusedbyWebAPI,whichholdstheentitymodels.ThefollowingdiagramshowshowtodevelopthesethreeprojectsandconfigureanduseAngular2:
CreatingaCommonprojectCommonprojectsholdtheentitiesthatwillbeusedbytheEntityframeworktocreateadatabase.WewillreferencethisassemblyintheWebAPIprojectatalaterstage:
1. Createa.NETCoreClassLibraryproject:
2. Addanewfolder,Models,andaddaTodoItemclassasfollows:
usingSystem;
usingSystem.Collections.Generic;
usingSystem.Linq;
usingSystem.Threading.Tasks;
namespaceCommon
{
publicclassTodoItem
{
publicintId{get;set;}
publicstringTitle{get;set;}
publicstringDescription{get;set;}
publicDateTimeDueDateTime{get;set;}
publicintUserId{get;set;}
}
}
TheprecedingTodoItemclasscontainstheId(primarykey)andTitle,Description,DueDateTime,andtheUserIDtosaveto-dosforaspecificuser.
CreatingaTodoServiceAppprojectInthisproject,wewillcreateawebAPIthatwillreferencetheCommonprojectwhichcontainstheTodoItemPOCOmodel.Inthisproject,wewillexposeservicesandcreateadatabaserepositorythatwilluseEntityFrameworkCoretoperformCreate,Read,Update,andDelete(CRUD)operationsintheMicrosoftSQLServerdatabase:
1. CreateanewWebAPIproject,choosingASP.NETCoretemplate.WebAPIandASP.NETMVChavebeenmergedintooneunifiedframework,sothereisnoseparateprojecttemplateforWebAPI.Inthiscase,wewillusetheEmptyProjectModelavailableintheASP.NETCoreprojecttemplates.
2. Openproject.jsonandaddareferencetoourCommonassembly:
"dependencies":{
"Microsoft.NETCore.App":{
"version":"1.0.0-rc2-3002702",
"type":"platform"
},
"Microsoft.AspNetCore.Server.IISIntegration":"1.0.0-rc2-final",
"Microsoft.AspNetCore.Server.Kestrel":"1.0.0-rc2-final",
"Common":"1.0.0-*"
}
EnablingMVCinaWebAPIproject
InordertoenabletheMVCproject,wehavetocallAddMvc()intheConfigureServicesmethod,andUseMvc()intheConfiguremethod:
1. AddtheMVCpackageinproject.json:
"Microsoft.AspNetCore.Mvc":"1.0.0-rc2-final"
2. CallAddMvc()fromtheConfigureServicesmethod:
publicvoidConfigureServices(IServiceCollectionservices)
{
services.AddMvc();
}
3. Finally,callUseMvc()fromtheConfiguremethod:
publicvoidConfigure(IApplicationBuilderapp)
{
app.UseMvc();
}
InstallingEntityFramework
HerearethestepstoinstallEntityFramework:
1. AddtwoEntityFrameworkassemblies,Microsoft.EntityFrameworkCore.SqlServerandMicrosoft.EntityFrameworkCore.Tools,asshowninthefollowingcode:
"dependencies":{
"Microsoft.NETCore.App":{
"version":"1.0.0-rc2-3002702",
"type":"platform"
},
"Microsoft.AspNetCore.Server.IISIntegration":"1.0.0-rc2-final",
"Microsoft.AspNetCore.Server.Kestrel":"1.0.0-rc2-final",
"common":"1.0.0-*",
"Microsoft.AspNetCore.Mvc":"1.0.0-rc2-final",
"Microsoft.EntityFrameworkCore.SqlServer":"1.0.0-rc2-final",
"Microsoft.EntityFrameworkCore.Tools":{
"type":"build",
"version":"1.0.0-preview1-final"
}
}
AddingAppSettingstostoreaconnectionstring
ASP.NETCoreprovidesvariousoptionsforstoringapplicationsettings.Thedefaultconfigurationfileisnowappsettings.json,whichstoresthedatainaJSONformat.However,thereareothermethodsavailableaswelltostoredataintheenvironmentvariables,XML,andINIformatsaswell.Inthisproject,wewillstoretheconnectionstringintheappsettings.jsonfile:
1. AddtheASP.NETconfigurationfileappsettings.jsonandspecifytheconnectionstringasfollows:
{
"Data":{
"DefaultConnection":{
"ConnectionString":"DataSource=.;InitialCatalog=tododatabase;
IntegratedSecurity=True;MultiSubnetFailover=False;"
}
}
}
2. Addthefollowingpackagesinproject.json:
"Microsoft.Extensions.Configuration.Json":"1.0.0-rc2-final",
"Microsoft.Extensions.Options.ConfigurationExtensions":"1.0.0-rc2-final",
ConfiguringAppSettingsintheStartupclass
ThenewconfigurationsystemofASP.NETCoreisbasedonSystem.Configuration.Tousesettingsinourproject,wewillinstantiateaConfigurationobjectinourStartupclassandusetheOptionspatterntoaccessindividualsettings.
TheOptionspatternconvertsanyclassintoasettingsclassandthenwecaninjectthatclassintothecontrollersthroughASP.NET'sbuilt-independencyinjection.Throughtheoptionsclass,thedevelopercanaccessthesettingskeysandvalues,asshowninthefollowingsteps:
1. IntheStartupclassconstructor,wewilladdtheappsettings.jsonfileusingthe
ConfigurationBuilderobject.ConfigurationBuilderallowsaprovisiontoadddifferentprovidersandhaveabuildmethodthatbuildstheconfigurationstoresindifferentprovidersandreturnstheIConfigurationRootinstance:
publicStartup()
{
//Setupconfigurationsources.
varbuilder=newConfigurationBuilder()
.AddJsonFile("appsettings.json")
Configuration=builder.Build();
}
publicIConfigurationRootConfiguration{get;set;}
Tip
Ifmultipleprovidershavethesamekeys,thelastonespecifiedintheConfigurationBuilderwillbeused.
2. NowwecanusetheConfigurationpropertytoaccesstheconnectionstring,asfollows:
Configuration["Data:DefaultConnection:ConnectionString"];
AddingdataaccessinWebAPI
Inthissection,wewilladdaTodoContextandTodoRepositoryclasstoperformCRUDoperations:
1. Addanewfolder,DataAccess,andaddtheTodoContextclass,whichwillbederivedfromtheDbContextclass.ThisisthemainTodoContextclassEntityFrameworkusedtocreatethedatabase:
usingCommon;
usingMicrosoft.Data.Entity;
usingSystem;
usingSystem.Collections.Generic;
usingSystem.Linq;
usingSystem.Threading.Tasks;
namespaceTodoServiceApp.DataAccess
{
publicclassTodoContext:DbContext
{
publicDbSet<TodoItem>TodoItem{get;set;}
}
}
2. WehavetonowoverridetheOnConfiguring()methodandcalltheUseSqlServer()methodoftheDbContextOptionsBuilderobject.TheOnConfiguring()methodiscalledeverytimetheContextobjectisinitializedandconfigurestheoptionsspecified.TheUseSqlServer()methodtakestheconnectionstringthatisdefinedintheappsettings.jsonfile,whichwehaveconfiguredintheStartupclass.Nowwewantto
injecttheappsettingsobjectintothisclass.Inordertodoso,wewillusetheOptionspattern.Aspertheoptionspattern,weshouldn'tusetheConfigurationpropertywehavedefinedintheStartupclassdirectly,andinsteadwewillcreateacustomPOCOclassthatcontainsthesamekeyswehaveinourappsettingsfileandoverloadthedefaultTodoContextconstructor,whichacceptsIOptions<T>,whereTisourcustomPOCOappsettingsclass.
3. Astheconnectionstringisdefinedinanestedobject,ourDataclasswillbeasfollows:
usingSystem;
usingSystem.Collections.Generic;
usingSystem.Linq;
usingSystem.Threading.Tasks;
namespaceTodoServiceApp
{
publicclassData
{
publicDefaultConnectionDefaultConnection{get;set;}
}
publicclassDefaultConnection{
publicstringConnectionString{get;set;}
}
}
4. IntheStartupclass,wewillcalltheservices.Configure()methodtopopulatethisDataobjectwiththekeysspecifiedintheappsettings.jsonfile,andinjectitintherepositorywewillbecreatingnext.
5. CreateaTodoRepositoryclassthatcontainsanITodoRepositoryinterfaceanditsimplementation,TodoRepository.ThisclasswillusetheTodoContextobjecttoperformdatabaseoperations.HereisthecodesnippetfortheTodoRepositoryclass:
usingCommon;
usingSystem;
usingSystem.Collections.Generic;
usingSystem.Linq;
usingSystem.Threading.Tasks;
usingTodoServiceApp.DataAccess;
namespaceTodoServiceApp.Repository
{
publicinterfaceITodoRepository
{
voidCreateTodo(TodoItemtodoItem);
voidDeleteTodo(inttodoItemId);
List<TodoItem>GetAllTodos(intuserId);
voidUpdateTodo(TodoItemtodoItem);
}
publicclassTodoRepository:ITodoRepository
{
privateTodoContextcontext;
publicTodoRepository()
{
context=newTodoContext();
}
publicList<TodoItem>GetAllTodos(intuserId)
{
returncontext.TodoItems.ToList();
}
publicvoidCreateTodo(TodoItemtodoItem)
{
context.TodoItems.Add(todoItem);
context.SaveChanges();
}
publicvoidDeleteTodo(inttodoItemId)
{
varitem=context.TodoItems.Where(i=>i.Id==
todoItemId).FirstOrDefault();
context.Remove(item);
context.SaveChanges();
}
publicvoidUpdateTodo(TodoItemtodoItem)
{
context.Update(todoItem);
context.SaveChanges();
}
}
}
6. IntheStartupclass,addtheEntityFrameworkintheConfigureServices()method,asshownthefollowingcode.OurWebAPIcontrollerswillhaveanoverloadedconstructorthattakestheITodoRepositoryobject.Wewillusetheservices.AddScoped()methodtoinjectTodoRepositorywhereverITodoRepositoryisrequired.Finally,calltheservices.Configure()methodtopopulatetheDataobjectwiththekeysspecifiedintheappsettings.jsonfile:
publicvoidConfigureServices(IServiceCollectionservices)
{
stringconnString=
Configuration["Data:DefaultConnection:ConnectionString"];
services.AddDbContext<TodoContext>(options=>
options.UseSqlServer(connString));
services.AddMvc();
services.AddScoped<ITodoRepository,TodoRepository>();
services.Configure<Data>(Configuration.GetSection("Data"));
}
EnablingCORSintheASP.NETWebAPI
WelearnedaboutCORSinthepreviouschapter;wehavetoenableCORSinourWebAPIproject,sothatfromAngularserviceswecanmakearequesttoaccesstheTodoService
methods:
1. Callservices.AddCors()intheConfigureServicesmethodintheStartupclass:
services.AddCors(options=>{options.AddPolicy("AllowAllRequests",builder
=>builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());});
2. Callapp.UseCors()intheConfiguremethodinStartupclass:
app.UseCors("AllowAllRequests");
Runningdatabasemigration
WeareusingtheEntityFrameworkCodeFirstmodel,sonowwewanttocreateadatabaseinMicrosoftSQLServer.Todoso,wewillfirstaddtheEntityFrameworktoolsupportintheproject.jsonfileoftheTodoServiceApp,andthenrun.NETCLIcommandstoaddmigrationsandcreatethedatabase:
1. AddMicrosoft.EntityFrameworkCore.Toolsintheproject.jsonfile,asshownhere:
"tools":{
"Microsoft.AspNetCore.Server.IISIntegration.Tools":{
"version":"1.0.0-preview1-final",
"imports":"portable-net45+win8+dnxcore50"
},
"Microsoft.EntityFrameworkCore.Tools":{
"imports":["portable-net451+win8"],
"version":"1.0.0-preview1-final"
}
},
2. Nowwecanruncommands,createmigrations,andupdatethedatabase.3. Tocreatemigrations,gotothecommandpromptandnavigatetotheTodoServiceApp
projectwhereproject.jsonresides.4. Then,rundotnetefmigrationsaddInitial,whereInitialisthenameofthe
migrationcreated.RunningthiscommandwilladdtheMigrationsfolderandaclasscontainingcodeabouttheDDLoperations.
ThefollowingscreenshotshowstheMigrationsfoldercreatedafterrunningtheprecedingcommand,andthecreationofthe20160405115641_Initial.csfilethatcontainstheactualmigrationcodesnippetstoapplyorremovemigrationfromthedatabase:
5. Tocreateadatabase,weneedtoexecuteanothercommandinthesamefolderwhereproject.jsonresidesintheTodoServiceAppproject:
dotnetefdatabaseupdate–verbose
6. Thiswillcreateadatabase,andwecannowgothrough,addingacontrollertohandledifferentHTTPrequestsandaccessthedatabase.
Creatingacontroller
Followthesestepstocreateacontroller:
1. AddanewControllersfolderandaddaclassnamedTodoController.2. HereisthecodesnippetfortheTodoControllerclass:
usingSystem;
usingSystem.Collections.Generic;
usingSystem.Linq;
usingSystem.Threading.Tasks;
usingMicrosoft.AspNetCore.Mvc;
usingCommon;
usingTodoServiceApp.Repository;
namespaceTodoApi.Controllers
{
[Route("api/[controller]")]
publicclassToDoController:Controller
{
ITodoRepositoryrepository;
publicToDoController(ITodoRepositoryrepo)
{
repository=repo;
}
//GET:api/values
[HttpGet]
publicIEnumerable<string>Get()
{
returnrepository.GetAllTodos();
}
//GETapi/values/5
[HttpGet("{id}")]
publicIEnumerable<TodoItem>Get(intid)
{
returnrepository.GetAllTodos(id);
}
//POSTapi/values
[HttpPost]
publicvoidPost([FromBody]TodoItemvalue)
{
repository.CreateTodo(value);
}
//PUTapi/values/5
[HttpPut("{id}")]
//DELETEapi/values/5
[HttpDelete("{id}")]
publicvoidDelete(intid)
{
repository.DeleteTodo(id);
}
}
}
NowwehavecompletedourTodoServiceproject,sowewilldevelopatodowebapplicationprojectandconfigureAngular2.
CreatingaTodoWebAppprojectWewilldevelopasingle-pageapplicationandusetheMVCviewtorenderitusingAngular2.Thisapplicationwillhaveonemainpagethatlistsalltheto-doitemsforaparticularuser,whereastoaddanewto-doitem,anewpagewillopeninamodaldialogwindow:
1. Tostart,let'screateanemptyprojectusingtheASP.NETCoreprojecttemplateavailableinVisualStudio2015,andnameitTodoWebApp.
2. AddanMVCreferenceinproject.json:
"Microsoft.AspNetCore.Mvc":"1.0.0-rc2-final",
"Microsoft.AspNetCore.StaticFiles":"1.0.0-rc2-final",
3. IntheStartupclass,addtheAddMvc()methodintheConfigureServicesmethodandtheUseMvc()methodintheConfiguremethod.HereisthecodesnippetoftheStartupclass:
namespaceTodoWebApp
{
publicclassStartup
{
//Thismethodgetscalledbytheruntime.Usethismethodtoadd
servicestothecontainer.
//Formoreinformationonhowtoconfigureyourapplication,visit
http://go.microsoft.com/fwlink/?LinkID=398940
publicvoidConfigureServices(IServiceCollectionservices)
{
services.AddMvc();
}
//Thismethodgetscalledbytheruntime.Usethismethodtoconfigure
theHTTPrequestpipeline.
publicvoidConfigure(IApplicationBuilderapp)
{
app.UseStaticFiles();
app.UseMvc(routes=>
{
routes.MapRoute(name:"default",template:"
{controller=Home}/{action=Index}/{id?}");
}
}
ConfiguringAngular2intheTodoWebAppproject
Angular2ispartoftheNodemodule,andwecanaddNodepackagesthroughtheNodePackageManager(NPM)configurationfile,package.json.Inpackage.json,wecanaddpackagesthroughthedevDependenciesnodeandthedependenciesnode.ThedevDependenciesnodeholdsthepackagesthatareusedduringdevelopment,suchasGulp,whichcanbeusedtoconcatenateandminifyJavaScriptandCSSfiles,TypeScriptfordevelopingAngular2components,andrimraftodeletethefiles.Inthedependenciesnode,wewillspecifypackagessuchasangular2,systemjs,reflect-metadata,rxjs,andzone.js,whichwillbeusedwhentheapplicationruns:
1. Addanewpackage.jsonfilefromtheVisualStudioprojecttemplateoptionNPM
ConfigurationFileandaddthefollowingJSONsnippet:
{
"name":"ASP.NET",
"version":"0.0.0",
"dependencies":{
"angular2":"2.0.0-beta.9",
"systemjs":"0.19.24",
"reflect-metadata":"0.1.3",
"rxjs":"5.0.0-beta.2",
"zone.js":"0.6.4"
},
"devDependencies":{
"gulp":"3.8.11",
"typescript":"1.8.7",
}
}
2. VisualStudioautomaticallydownloadsandrestorespackagesspecifiedinthepackage.jsonfile,createsanode_modulesfolderintheprojectitself,andplacesallthepackagesthere.TheNode_modulesfolderisbasicallyhiddenbydefaultinVisualStudio,butcanbemadevisiblebyenablingtheShowAllFilesoption.
Dependencies
Thefollowingisthelistofdependencieswiththeirdescriptions:
angular2:ItistheAngular2package.systemjs:ItprovidesSystem.importtohookupthemainentrypointofAngular.reflect-metadata:ItisaproposaltoadddecoratorstoES7.Throughthis,wecanspecifythemetadatatoourclassinAngular2.rxjs:Itisareactivestreamslibrarythatallowsworkingwithasynchronousdatastreams.zone.js:Itprovidesanexecutioncontextthatpersistsacrossasynchronoustasks.
Developmentdependencies
Thefollowingisthelistofdevelopmentdependencieswiththeirdescriptions:
gulp:Usedtocopythefilestothewwwrootfoldertypescript:UsedtowriteprogramsinTypeScript
ConfiguringTypeScript
ToconfigureTypeScript,performthefollowingsteps:
1. AddtheScriptsfolderwherealltheTypeScriptfilesreside.InthecurrentversionofASP.NET,thereisarestrictiononnamingthisfolderScripts,anditshouldbeaddedintherootoftheproject;otherwise,TypeScriptfileswillnotbetranspiledtoJavaScriptfiles.
2. AfteraddingtheScriptsfolder,addtheTypeScriptconfigurationfile(tsconfig.json)andaddthefollowingconfigurationtoit:
{
"compilerOptions":{
"noImplicitAny":false,
"noEmitOnError":true,
"removeComments":false,
"sourceMap":true,
"target":"es5",
"module":"commonjs",
"moduleResolution":"node",
"outDir":"../wwwroot/todosapp",
"mapRoot":"../scripts",
"experimentalDecorators":true,
"emitDecoratorMetadata":true
},
"exclude":[
"node_modules",
"wwwroot"
]
}
ConfigurationsdefinedwithinthecompilerOptionsnodeareusedbyVisualStudiowhenyoubuildyourproject.Basedontheconfiguration,theJavaScriptfilesaregeneratedandstoredintheoutputdirectory.Thefollowingtableshowsthedescriptionofeachpropertyspecifiedintheprecedingcode:
Compileroptions Description
noImplicitAny Iftrue,thenitwarnstheexpressionimpliedwithanytype
noEmitOnErrorIftrue,itdoesnotgenerateJavaScriptifanyerrorsarepresentintheTypeScript
removeComments Ittrue,removescommentswhengeneratingJavaScriptfiles
sourceMap Iftrue,thengeneratesthecorrespondingmapfile
Target SetsthetargetECMAscriptversion,suchasES5
modulezSpecifiesthemodulethatgeneratedthecode,suchascommonjs,AMD,orsystem
moduleResolution Specifiesthemoduleresolutionstrategy,suchasnode
outDir PathwherethegeneratedJavaScriptfileswillbedumped
mapRoot Pathwherethemapfileswillbelocated
experimentalDecorators Iftrue,itenablessupportforES7experimentaldecorators
emitDecoratorMetadataIftrue,itemitsdesign-typemetadatafordecoratordeclarationsinsource
ConfiguringGulp
Inthissection,wewilluseGulptominifytheJavaScriptgeneratedbytheTypeScriptcompiler:
1. AddtheGulpconfigurationfile,gulpfile.js.2. Gulpisusedtoruntasks,andVisualStudioprovidesataskrunnerwindowthatlistsall
thetasksspecifiedinthegulpfile.js,andalsoallowsustobindthosetaskstobuildevents.
3. Let'saddthefollowingscriptingulpfile.js:
///<bindingClean='clean'/>
"usestrict";
vargulp=require("gulp")
varpaths={
webroot:"./wwwroot/"
};
varconfig={
libBase:'node_modules',
lib:[
require.resolve('systemjs/dist/system.js'),
require.resolve('systemjs/dist/system.src.js'),
require.resolve('systemjs/dist/system-polyfills.js'),
require.resolve('angular2/bundles/angular2.dev.js'),
require.resolve('angular2/bundles/angular2-polyfills.js'),
require.resolve('angular2/bundles/router.dev.js'),
require.resolve('angular2/bundles/http.dev.js'),
require.resolve('angular2/bundles/http.js'),
require.resolve('angular2/bundles/angular2'),
require.resolve('rxjs/bundles/Rx.js')
]
};
gulp.task('build.lib',function(){
returngulp.src(config.lib,{base:config.libBase})
.pipe(gulp.dest(paths.webroot+'lib'));
});
Intheprecedinggulpfile.js,wehavefirstdeclaredtheobjectsofGulp.Thenthepathsvariabledefinestherootfolder(./wwwroot)forstaticfiles.InASP.NETCore,allthestaticfilesshouldresideunderthewwwrootfolder;otherwise,theycannotbeaccessed.NowweneedtocopytheAngularandotherrelatedJavaScriptfilesintothewwwrootfolder.Therefore,wehaveaddedthetaskbuild.libthatcallsgulp.src()andchainsthegulp.dest()methodtocopythefilesfromthenode_modules/*foldertothewwwroot/libfolder.Hereisthescreenshotofthewwwrootfolder,whichcreatesthelibfolderwhenyouruntheprecedingsteps:
Tip
TaskscanrunthroughthetaskrunnerwindowinVisualStudio.
AddingAngularcomponents
WehaveinstalledtheAngularpackagesandconfiguredGulptocopythepackagedJavaScriptfilestothewwwrootfolder.NowwewilladdAngularcomponentstodefineourmainapplicationselectorandrendertheASP.NETpageinsideit:
1. IntheScriptsfolder,createtwofolders,appandservices.Theappfolderholdsthecomponentsthatwewilluseintheview,whereastheservicesfolderholdstheservicesthatwillbeusedtocalltheWebAPImethods.
2. AddamainTypeScriptfile,whichwillbootstrapthemainTodoAppComponent.Hereisthe
codeofmain.ts:
//main.ts
import{bootstrap}from'angular2/platform/browser';
import{TodoAppComponent}from'./apps/todoapp.component';
import{HTTP_PROVIDERS}from'angular2/http';
import'rxjs/add/operator/map';
bootstrap(TodoAppComponent,[HTTP_PROVIDERS]);
Intheprecedingcodesnippet,wehaveaddedabootstrapcomponenttobootstrapourfirstTodoAppComponent.HTTP_PROVIDERScontainsalltheproviderstomakeanyHTTPrequest.Itisprovidedwhilebootstrapping,sotheTodoAppComponentorthechainofcomponentsinthefollowinghierarchycandoHTTP-basedoperations.Rxjs/add/operator/mapisadependentpackageforHTTP_PROVIDERS,whichneedstobeaddedaswell:
1. AddanewTypeScriptfileandnameittodoapp.component.ts.2. AddthefollowingcodesnippetforTodoAppComponent.Inordertofirsttestwhether
everythingisconfiguredproperly,wewillsimplyaddasampleheadingtagthatshowsHelloWorld:
//todoapp.component.ts
///<referencepath="../../node_modules/angular2/typings/browser.d.ts"/>
import{Component}from'angular2/core';
@Component({
selector:'todo',
template:'<h1>{{message}}</h1>'
})
exportclassTodoAppComponent{
message:string="HelloWorld";
}
3. Nowwewilladdtwofiles,importer.jsandangular_config.js.importer.jscallsSystem.importandpointstothemainfilethatbootstrapstheapplicationcomponent.angular_config.jsholdstheconfigurationpropertytoallowdefaultJavaScriptextensionstobesettotrue.
Hereisthecodesnippetforimporter.js:
System.import('todosapp/Main')
.then(null,console.error.bind(console));
Hereisthecodeforangular_config.js:
System.config({defaultJSExtensions:true});
4. NowweneedtoaddtheMVClayoutpageandaddallthescripts.Addthefollowingscripts:
//_Layout.cshtml
<environmentnames="Development">
<linkrel="stylesheet"href="~/lib/bootstrap/dist/css/bootstrap.css"/>
<linkrel="stylesheet"href="~/css/site.css"/>
<scriptsrc="~/lib/angular2/bundles/angular2-polyfills.js"></script>
<scriptsrc="~/lib/systemjs/dist/system.js"></script>
<scriptsrc="~/lib/custom/angular_config.js"></script>
<scriptsrc="~/lib/rxjs/bundles/Rx.js"></script>
<scriptsrc="~/lib/angular2/bundles/angular2.dev.js"></script>
<scriptsrc="~/lib/angular2/bundles/router.dev.js"></script>
<scriptsrc="~/lib/angular2/bundles/http.js"></script>
<scriptsrc="~/lib/custom/importer.js"></script>
<scriptsrc="https://ajax.aspnetcdn.com/ajax/jquery/jquery-2.1.4.min.js"
asp-fallback-src="~/lib/jquery/dist/jquery.min.js"
asp-fallback-test="window.jQuery">
</script>
<script
src="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.5/bootstrap.min.js"
asp-fallback-src="~/lib/bootstrap/dist/js/bootstrap.min.js"
asp-fallback-test="window.jQuery&&window.jQuery.fn&&
window.jQuery.fn.modal">
</script>
</environment>
5. Nowlet'saddHomeControllerandviewIndex.cshtml.6. InIndex.cshtml,addtheto-doselectortodo-app:
@{
ViewData["Title"]="TodoApplications";
Layout="~/Views/Shared/_Layout.cshtml";
}
<divid="myCarousel"class="container"data-ride="carousel"data-
interval="6000">
<todo-app>Loading...</todo-app>
</div>
7. BuildandruntheapplicationanditwillshowHelloWorld:
Addingtheto-doservicecomponent
WewillnowaddthecomponentsinsidetheservicesfolderthatwillberesponsibleforgettingdatabycallingtheTodoservice:
1. Firstofall,addtheBaseServicecomponent,whichcontainsbaseURL.AlltheservicecomponentswillderivefromBaseServicesotheycanusethebaseURLpropertyformakingAjaxrequests.AddanewTypeScriptfileandnameitbaseservice.component.ts.Hereisthecodesnippetforbaseservice.component.ts:
//baseservice.component.ts
import{Component}from'angular2/core';
import{Http,Headers}from'angular2/http';
@Component({})
exportclassBaseService{
baseUrl:string;
constructor(){
this.baseUrl="http://localhost:7105/api/";
}
}
2. Nowaddtodoservice.component.ts,whichcontainsthemethodstogetallto-doitems,addanewto-doitem,anddeleteanexistingto-doitem.HereisthecodesnippetforTodoService:
//todoservice.component.ts
import{Component}from'angular2/core';
import{Http,Headers}from'angular2/http';
import{BaseService}from'../services/baseservice.component';
@Component({
providers:[TodoService]
})
exportclassTodoServiceextendsBaseService{
constructor(privatehttp:Http){
super();
}
publicgetTodoItems(){
returnthis.http.get(this.baseUrl+'todo/1');
}
publiccreateTodo(item){
varpath=this.baseUrl+'todo';
constheaders=newHeaders({'Content-Type':'application/json'});
returnthis.http.post(path,JSON.stringify(item),{headers:headers});
}
publicdeleteTodo(itemId){
varpath=this.baseUrl+'todo';
returnthis.http.delete(path+"/"+itemId);
}
}
Intheprecedingcode,weimportedthehttpcomponentandinjectedtheconstructor.Thehttpobjectprovidesmethodssuchasget,post,put,anddeletetoread,insert,update,anddeleteoperations.InourTodoServiceWebAPIproject,wehavethesemethodsavailable,whichwecallasshownintheprecedingcode.Eachmethodreturnsapromise,andinthecallingcomponentswewillchecktheresultandtakeappropriateactions.
Addingato-doviewcomponent
Wehavealreadyaddedtodoapp.component.tsintheprecedingsteptocheckwhetherAngularisconfiguredproperly.NowwewillmodifythesamecomponenttocalltheTodoServiceComponentanddisplaytheresultsonawebpage.
HereisthecodesnippetforTodoApp.Component.ts:
//todoApp.component.ts
///<referencepath="../../node_modules/angular2/typings/browser.d.ts"/>
import{Component}from'angular2/core';
import{Http,Response}from'angular2/http';
import{CreateTodoComponent}from'../apps/createTodo.component';
import{TodoService}from'../services/todoservice.component';
@Component({
selector:'todo-app',
templateUrl:'Todo',
directives:[CreateTodoComponent],
providers:[TodoService]
})
exportclassTodoAppComponent{
//membervariables
todos=[
];
//constructor
constructor(privatehttp:Http,privatetodoService:TodoService){
}
//PageInitializedEventHandler
ngOnInit(){
this.getTodoItems();
}
//MemberFunctions
getTodoItems(){
this.todoService.getTodoItems().map((res:Response)=>res.json())
.subscribe(data=>{
this.todos=data
this.parseDate();
},
err=>console.log(err),
()=>console.log('done')
);
}
deleteTodoItem(itemID){
varr=confirm("Areyousuretodeletethisitem");
if(r==true){
this.todoService.deleteTodo(itemID)
.map(r=>r.json())
.subscribe(result=>{
alert("recorddeleted");
});
}
this.getTodoItems();
}
parseDate(){
for(lettodoofthis.todos){
lettodoDate=newDate(todo.DueDateTime);
todo.DueDateTime=todoDate;
}
}
handleRefresh(args){
this.getTodoItems();
}
}
InTodoAppComponent,wehavefirstaddedtheCreateTodoComponentdirectivewewillbeusingintheTodo/Index.cshtmlpageinalaterstep.WehaveimplementedthengOnInit()eventhandlerthatgetsthelistofto-dosandboundittothetodosarrayobject.ThegetTodoItems()methodcallstheTodoServicetogetthelistofto-doitems,whereasdeleteTodoItem()isusedtodeletetheitem.
EveryrequestinAngularreturnsanObservableresponseobjectthatprovidesamapmethodtotellAngulartoparsetheresponseinaspecificformat.ThemapalsoreturnstheObservableobject,whichcanbeusedtosubscribetothedataonceitisparsedintotheJSONformat,asinourcase.Finally,wehavecalledthesubscribemethodandsenttheJSONresponsedatatothetodosarray.Tohandleerrors,wecanchainthecallwiththeerrmethod.Theanonymousexpression()methodisinvokedineverycall,irrespectiveoftheresponsestatus.Thatmeanswhethertheresultisasuccessoranerror,thecodedefinedundertheanonymousexpression()methodwillbeexecuted.
Forcreatingnewto-dos,wewillcreateanotherCreateTodoComponentlater,whichwillcallthehandleRefresh()methodthroughtheOutputseventtorefreshthelistandreflectthenewlyaddeditemonthemainpage.
Creatingthemainto-dopage
WehavecreatedtheAngularcomponentsthatwewilluseintheMVCview.WehavealreadybootstrappedtheAngularcomponentsintheprevioussectionandplacedthe<todo-app>tagintheHome/Index.cshtmlpage,whichisthelandingpageofourapplication.Next,wewillcreateacustomtaghelper,thenaddaTodoController,andusethistaghelperintheindexpage.
Creatingacustomto-dotaghelper
Onthemainpage,wewilllistalltheto-doitemsforaparticularuser.Forthis,wewillcreateacustomtaghelperinASP.NET:
Performthefollowingstepstocreatethistaghelper:
1. CreateanewcontrolsfolderintherootoftheTodoWebAppprojectandaddaTodoTagHelperclass.HereisthecodeforTodoTagHelper,whichusesAngular2ngControltobindvaluesfromAngularTodoAppComponenttotheform:
[HtmlTargetElement("todo")]
publicclassTodoTagHelper:TagHelper
{
publicoverridevoidProcess(TagHelperContextcontext,TagHelperOutput
output)
{
stringtodo="<divclass='thumbnail'><divclass='caption'><nav
class='navnavbar-inverse'role='navigation'></nav>";
todo+="<labelclass='date'>{{todo.DueDateTime|date:'short'}}
</label><imgsrc='images/delete.png'(click)=deleteTodoItem(todo.Id)/>";
todo+="<h4><ahref='#'>{{todo.Title}}</a></h4>";
todo+="<textareareadonlyclass='form-control'style='resize:none;'
rows='4'cols='28'>{{todo.Description}}</textarea></div></div>";
output.Content.AppendHtml(todo);
}
}
2. Addthetaghelperin_ViewImports.cshtml:
@addTagHelper"*,TodoWebApp"
Addingato-doMVCcontroller
AddTodoControllerintheTodoWebAppprojectandspecifytwomethodsfortheindexview,whichisthemainviewthatdisplaysalltheitemsandcreatesanewto-doitem:
usingSystem.Linq;
usingMicrosoft.AspNetCore.Mvc;
usingTodoNotes.Models;
namespaceTodoNotes.Controllers
{
publicclassTodoController:Controller
{
publicTodoController()
{
_context=context;
}
//GET:Todo
publicIActionResultIndex()
{
returnView();
}
//GET:Todo/Create
publicIActionResultCreate()
{
returnView();
}
}
GeneratingviewsfortheTodoControlleractionmethods
GenerateviewsfortheprecedingactionmethodsIndexandCreate.
HereisthecodesnippetforTodo/Index.cshtml:
@{
Layout=null;
}
<divclass="col-md-3">
<pclass="lead">ToDoItems</p>
<divclass="list-group">
<h4>
<ahref="#">WanttoaddnewTodo?</a>
</h4>
<p>Clickonthebuttonbelow</p>
<divclass="col-md-3">
<aclass="btnbtn-primary"data-toggle="modal"data-
target="#todoModal">CreateTodo</a>
</div>
</div>
</div>
<divid="todoModal"class="modalfade"role="dialog">
<divclass="modal-dialog">
<!--Modalcontent-->
<divclass="modal-content">
<divclass="modal-header">
<buttontype="button"class="close"data-dismiss="modal">×</button>
<h4class="modal-title">InsertTodo</h4>
</div>
<divclass="modal-body">
<createTodo(refreshTodos)="handleRefresh($event)"></createTodo>
</div>
</div>
</div>
</div>
<divclass="col-md-9">
<divclass="row">
<divclass="col-sm-4col-lg-4col-md-4"*ngFor="#todooftodos">
<todo></todo>
</div>
</div>
</div>
IntheprecedingHTMLmarkup,wehavefirstdefinedabuttonthatopensupamodaldialog,todoModal.InthetodoModaldialogmarkup,wehaveusedthecreateTododirective,whichisdefinedinthetodoapp.component.tsfileassociatedwiththispage,andthelinkactuallypointstotheTodo/CreateMVCview,whichwillberenderedattheplaceofrouter-outlet.Withthecombinationoftherouterlinkandtherouteroutlet,wecanrenderthetemplate.Intodoapp.component.ts,wewillseehowwecanuseroutinginAngular.Finally,wehaveusedthecustomtaghelper<todo>todisplayeachitemavailableintheto-dolist.
DevelopingtheCreateTodocomponent
Inthissection,wewilladdtheAngularcomponentandnameitCreateTodoComponent.ThisisneededbecausewewillbeopeninganewMVCviewinamodaldialogthroughacustomcreateTodoselector,andCreateTodoComponenthasamethodtosaveanewto-dointhedatabase,asshowninthefollowingcode.
Addanewcreatetodo.component.tsundertheScripts>appsfolder,andthenaddthefollowingcodesnippet:
//createtodo.component.ts
///<referencepath="../../node_modules/angular2/typings/browser.d.ts"/>
import{Component}from'angular2/core';
import{Http,Response}from'angular2/http';
import{FormBuilder,Validators}from'angular2/common';
import{TodoService}from'../services/todoservice.component';
@Component({
selector:'createTodo',
templateUrl:'Todo/Create'
})
exportclassCreateTodoComponent{
@Output()refreshTodos=newEventEmitter();
addTodoForm:any;
constructor(fb:FormBuilder,privatetodoService:TodoService){
this.addTodoForm=fb.group({
title:["",Validators.required],
description:["",Validators.required],
dueDateTime:["",Validators.required]
});
}
addTodoItem():void{
this.todoService.createTodo(this.addTodoForm.value)
.map(r=>r.json())
.subscribe(result=>{});
this.refreshTodos.next([]);
alert("Recordaddedsuccessfully");
}
}
Intheprecedingcodesnippet,wehaveimportedtheHttpandResponseobjectstohandletheresponsereceivedfromTodoService.Inthe@Componentannotation,wehavedefinedtheselectorthatisusedintheparentTodoAppComponentcomponenttorendertheCreateTodoviewinsidethemodaldialog.
FormBuilderandValidatorareusedtodefinepropertieswithspecificvalidatorsthatcanbeboundtotheHTMLformusingthengControldirective.Lastly,wehavetheaddTodoItemmethod,whichwillbeinvokedonformsubmissionandmakeato-doentryinthedatabasebycallingTodoService.
Nowlet'saddthefollowingcodeinCreate.cshtml:
@{
Layout=null;
}
<form[ngFormModel]="addTodoForm"(submit)="addTodoItem($event)"
class="container">
<divclass="form-horizontal">
<divclass="form-group">
<labelclass="col-md-2control-label">Title</label>
<divclass="col-md-10">
<inputngControl="title"class="form-control"id="Title"
placeholder="EnterTodoTitle"[(ngModel)]="title"/>
</div>
</div>
<divclass="form-group">
<labelclass="col-md-2control-label">Description</label>
<divclass="col-md-10">
<textareangControl="description"class="form-control"
placeholder="EnterDescription"></textarea>
{{description}}
</div>
</div>
<divclass="form-group">
<labelclass="col-md-2control-label">DueDate</label>
<divclass="col-md-10">
<inputngControl="dueDateTime"class="form-control"type="datetime-local"
placeholder="EnterDueDate"/>
</div>
</div>
<divclass="form-group">
<divclass="col-md-offset-2col-md-10">
<inputtype="submit"value="Create"class="btnbtn-primary"/>
</div>
</div>
</div>
</form>
@sectionScripts{
<scriptsrc="~/lib/jquery/dist/jquery.min.js"></script>
<scriptsrc="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
<scriptsrc="~/lib/jquery-validation-
unobtrusive/jquery.validate.unobtrusive.min.js"></script>
}
Intheprecedingcodesnippet,wehavesetthengFormModeltothemodelwedefinedinthecreatetodo.component.tsandthesubmitform,andwearecallingtheaddTodoItemmethod,whichsendsallthevaluesboundwiththengControldirective.ngControlisanewdirectiveintroducedinAngular2thatprovidesunidirectionalbinding.Withforms,ngControlnotonlybindsthevalue,butalsotracksthestateofthecontrol.Ifthevalueisinvalid,itupdatesthecontrolwithspecialCSSclassestotelltheuserthatthevalueisinvalid.
SummaryInthischapter,welearnedaboutthecorecomponentsofTypeScriptandwritingprogramsusingTypeScript.WealsolearnedthecorefundamentalsandconceptsoftheAngular2frameworkanddevelopedasimpleto-doapplicationusingASP.NETCore,Angular2,MVC6forWebAPI,andEntityFrameworkCorefordataaccessproviders.Inthenextchapter,wewilllearnaboutWindowsJavaScriptLibrary(WinJS),developedbyMicrosoft,andseehowwecanaccessWindowsruntimefeatures,changetheappearanceofHTMLcontrols,andotheroptionsavailableinthislibrary.
Chapter6.ExploringtheWinJSLibraryWebdevelopmenthasledtorevolutionaryexperiences.Withframeworkslikebootstrap,material,andothers,wearenowabletorunwebapplicationsattheirbestondifferentscreensizesandadjusttheircontentaccordingly.Developerstargetwebapplicationstorunondifferentplatformsprovidingaconsistentexperiencetotheircustomers.Forexample,anywebapplicationusingbootstrapandotherframeworkscanrunonabrowser,tablet,andamobiledeviceprovidingthebestuserexperienceeverimagined.Withthesebenefits,newprospectswereintroducedandallowwebapplicationstotargetdifferentdevicesbringingtheneedforaccessingclient-sidedevice-specificfeaturesandlayoutsaswell.Withtheserevolutionaryexperiences,companiesstartedbringingJavaScript-basedlibrariesthatnotonlychangedthelookandfeeloftheapplicationsrunningondevicesbutalsoalloweddeveloperstousedevice-specificfeatureslikesendingtoastnotifications,accessingthecameratouploadpictures,andsoon,leveraginguserexperience.
IntroductiontoWinJSWindowsJavaScript(WinJS)libraryisanopensourceJavaScriptlibrarydevelopedbyMicrosoft.ItwasreleasedinApril2014duringtheMicrosoftbuildconferenceandwithWindows10,Microsoftofficiallyreleasedversion4.0.Currentlyit'sopensourceandunderanApache2.0license.
ItwasinitiallytargetedforWindowsstoreappsthatwerebasedonJavaScript,CSS,andHTMLbutlatersupportedinmodernbrowsersaswell.TodaydeveloperscandevelopmobileapplicationsforanyplatformincludingWindowsapps,Androidapps,andiOSappsusingJavaScript,CSS,andHTMLandtheycanusethislibrarytotransformtheuserinterface(UI)toanativemobileinterfacewiththeprovisionofaccessingfeaturesoftheWindowsruntime.TheWinJSlibraryexposesnotonlytheWindowsruntimemodulesbutalsoprovidesWindowsUIcontrolsetupforuseinwebapplications.WinJSprovidesWindowsruntimefeaturesliketheclassesandruntimecomponentsandtheycanbeaccessedthroughJavaScriptcode.Userscanbuildappsthataccessdevicefeatureslikethecamera,storage,geolocations,filesystems,andstyleapplicationsthatgivethebestuserexperience.Italsoprovidesalayerofsecuritywhichkeepsthedevicefeaturessafeandprotectsthemfrommaliciousattacks.Asfarasbrowsercompatibilityisconcerned,allthemodernbrowsersincludingMicrosoftEdge,GoogleChrome,andotherssupportthislibrary.ThebasicadvantageisthatnowwebdeveloperscanbuildWindowsstoreapplicationsusingWinJScontrolssuiteandlibrarytouseWindowsruntimefeatures.Moreover,MicrosofthasalsoempoweredWinJSlibrarytointegratewithpopularclient-sideframeworkslikeAngularJS,Knockout,Ember,andBackboneandyoucanuseWinJSdirectivesinyourHTMLwithothercontrolsdirectivesanditworksasexpected.
WinJSfeaturesWinJSisnotonlydesignedtoserveuniversalwindowsappsthatarebasedonHTMLandJavaScriptbutageneralizedJavaScriptlibrarythatcanbeusedwithwebapplicationsaswell.WinJSbringsvariousfeaturesthatwewilldiscussinthefollowingsection.
JavaScriptcodingandlanguagepatterns
WinJSprovidesthecodingpatternofdefiningcustomnamespacesandclassesperformingbindingimplementationsandpromises.
Stylesheets
Itprovidestwostylesheets,namelyUI-darkandUI-light,whichcanbeusedwithHTMLelementstogiveaparticularWindowsappathemedappearance.Also,itallowsyoutohandledifferentscreensizesandorientationlikelandscapeandportrait.
Windowsruntimeaccess
Wecanaccessthewindowsruntimefeatureslikefilesystem,camera,geo-location,andotherswhichareapplicablethroughthenativeapplicationAPI.
Security
Withtheprovisionofenablingwindowsruntimefeatures,WinJSalsorestrictstheaccesstosensitivedataonthedevice.
Appmodel
AppmodelofferseventsinitiatedbyaWindowsapplicationandcanberegisteredinourJavaScripttodoaspecificoperation.Forexample,suspend,resume,andinitializationaresomeusefuleventswecanusetohandlespecifictasksthroughregisteringtheminWinJS.
Databinding
JustlikeotherframeworkslikeAngularJS,KnockOut,andsoon,WinJSalsoprovidesspecificdatabindingdirectivesandsyntaxthatisusedtobindHTMLcontrolswiththedatasuppliedorthatexistinyourJavaScriptcode.
Controls
WinJSprovidesspecificcontrolsapartfromtheextendedattributesonHTMLelements.ThesecontrolsareavailableinthenativeWindowsappsprojectandwithWinJSwecanusetheminourHTMLpagetobringthesameexperience.
Utilities
WinJSprovidesseveralutilitiestoperformlocalization,animations,andDOMselectors.
UsageofWinJSMicrosofthasdevelopedvariousapplicationsusingWinJSlibrary.ApplicationslikeSkype,Store,Weather,News,andothersarealldevelopedinHTML,CSS,andJavaScriptusingWinJSlibrary.ThemoderneraofwebdevelopmentmadeJavaScriptacoreframeworkofdevelopingresponsiveandrichapplicationsthatrunonanyplatformandonanydevice.ThisleadMicrosofttoinvestheavilyonWinJSandtomakethislibraryusefulforwebdeveloperswhowanttocreateWindowsappsoruseWindowsplatformfeaturesfromwebapplications.WiththereleaseofUniversalWindowsPlatform(UWP),MicrosoftreleasedthenewUniversalAppPlatform(UAP),asupersetoftheWinRTplatformusedbyWindows8applications.WithUWPthereisanewHostedappconceptintroduced,thatallowsanywebapplicationtoconvertintotheWindowsappwithaveryminimumsetofconfigurationproperties.
AddingtheWinJSlibraryintheASP.NETapplicationWinJScanbeaddedthroughNodePackageManager(NPM),NuGet,andbyreferencingaCDN.ThisdependsonwhetheryouwanttokeepthefileslocalontheserverorasreferencefromCDN.
CDNHereistheCDNlibrarythatcontainsJavaScriptandCSSfilesthatyoucanaddinyourapplication:https://cdnjs.com/libraries/winjs.
NPMToinstallitwithNPMyoucanrunnpminstallwinjsorjustaddthewinjspackageinthepackage.jsonfilewhenworkingintheASP.NETcoreapplication.
NuGetToinstallitviaNuGetyoucanaddtheWinJSpackagethroughtheNuGetpackagemanagerconsoleorjustrunthefollowingcommandintheASP.NETapplication:
Install-PackageWinJs
TheWinJSpackagecomeswithasetofJavaScriptfilesandtheCSSstylesheetsfordarkerorlighterUI.Thefollowingtabledefinesthefilesandtheirusage:
File Type Usage
Base.js JavaScript ThisisacoremoduleanditisusedbyUI.jstoprovideWindowsruntimefeatures
UI.js JavaScript ContainsUIcontrols
WinJS.intellisense.js JavaScript ProvideintellisensewhenusingWinJScomponentsinJavaScript
ui-dark.css CSS StylesheetfordarkerUItheme
ui-light.css CSS StylesheetforlighterUItheme
GettingstartedwithWinJSMicrosofthasprovidedcertaintemplatesinVisualStudiotodevelopstoreapplicationsusingJavaScriptandHTML,ontheotherhand,wecanalsoadditinourASP.NETapplicationtobringcertainfunctionalitiesofWindowsruntimefeaturesorchangingalookandfeelaccordingly.
UsingWinJSintheASP.NETapplicationYoucanstartusingWinJSbyaddingtheJavaScripttouseWindowsruntimefeaturesandCSStomakeUIasWindowsapplications.IntheASP.NETwebapplicationyoucanaddthepackagethroughNPMbymakinganentry,asfollows:
Onsavingthefile,thepackagewillbedownloadedautomaticallyinVisualStudio2015underthenode_modules\npmfolder.
HereisascreenshotofthefolderstheWinJSlibrarycontains.JScontainswinjsmodules,css,andfontsthatcanbeusedtochangeUIlookandfeel:
YoucanuseGulp.jstocopythecssandjsfilestothewwwrootfolderandreferencethemonthepage,wecanaddthefollowingsamplecodethatdisplaystheYouhaveclicked!textonabuttonclickevent:
<!DOCTYPEhtml>
<html>
<head>
<metacharset="utf-8"/>
<title></title>
<scriptsrc="lib/winjs/js/base.js"></script>
<scriptsrc="lib/winjs/js/ui.js"></script>
<scriptsrc="lib/winjs/js/winjs.intellisense-setup.js"></script>
<scriptsrc="lib/winjs/js/winjs.intellisense.js"></script>
<scriptsrc="http://ajax.aspnetcdn.com/ajax/jquery/jquery-1.9.0.js"></script>
<linkrel="stylesheet"href="lib/winjs/css/ui-dark.css"/>
</head>
<body>
<divclass="win-container">
<buttonclass="win-button"id="btn">Show</button>
<spanid="txtMessage"></span>
</div>
<script>
(function(){
WinJS.UI.processAll().done(function(){
$('#btn').click(function(){
$('#txtMessage').text("Youhaveclicked!");
});
});
})();
</script>
</body>
</html>
Thefollowingistheoutput:
Onpageload,thefunctionwillbeexecutedthatregisterstheclickeventforthebuttonwhenalltheWinJScontrolsareprocessed.TheWinJS.UI.processAll()methodparseswholedocumentobjectmodel(DOM)andsearchesfortheWinJScontrolstoprocessandreturnapromiseafterthebindingisdoneforallthecontrols.
Underthehood,WinJS.UI.processAll()onlyprocessesthosecontrolswhichhavetheisDeclarativeControlContainerpropertysetastrue.ThistellsWinJSwhichcontrolsneedtobeboundwiththeWinJSlibrary.IfyouareusingcustomcontrolsthenyouneedtospecifythisisDeclarativeControlContainerpropertysoitcanbeprocessedbyWinJS.
EventscanberegisteredthroughdeclarativebindingorbyregisteringaneventfromJavaScript.IntheprecedingcodewehaveregisteredthebuttonclickeventthroughJavaScripthowever;declarativelyyoucanalsosettheeventandcallsomeJavaScriptfunctionsthatcanbeinvokedwhenthebuttonisclicked.
ExistingWindowsapptemplateinVisualStudioWindowsappscanbedevelopedeitherusingtheXAMLandC#orbyusinganHTML,CSS,andJavaScriptprojecttemplate.VisualStudioprovidescertaintemplatesforboththemodelsandautoconfigurestheWinJSlibrarytostartaddingfeaturesinyourapplicationinsteadofaddingpackagesandconfigurationsforeachprojectmanually.YoucanaddthenewprojectformwiththefollowingoptionsinVisualStudio2015.Asshowninthefollowingscreenshot:
ThistemplateaddsalltherelatedCSS,JavaScript,andrelatedimagesthatWinJSprovidesandalsoaddsthedefault.jsfilethatcontainsthewinJs.ProcessAll()functiontobindtheHTMLelementswithWinJSlibrary.Thefollowingisthedefault.htmlpagesnippetthatcontainstheWinJSlibrariesanddefault.js:
<!DOCTYPEhtml>
<html>
<head>
<metacharset="utf-8"/>
<title>App1</title>
<!--WinJSreferences-->
<linkhref="WinJS/css/ui-dark.css"rel="stylesheet"/>
<scriptsrc="WinJS/js/base.js"></script>
<scriptsrc="WinJS/js/ui.js"></script>
<!--App1references-->
<linkhref="/css/default.css"rel="stylesheet"/>
<scriptsrc="/js/default.js"></script>
</head>
<bodyclass="win-type-body">
<p>Contentgoeshere</p>
</body>
</html>
Andhereisthecodesnippetfordefault.js:
(function(){
"usestrict";
varapp=WinJS.Application;
varactivation=Windows.ApplicationModel.Activation;
app.onactivated=function(args){
if(args.detail.kind===activation.ActivationKind.launch){
if(args.detail.previousExecutionState!==
activation.ApplicationExecutionState.terminated){
}else{
}
args.setPromise(WinJS.UI.processAll());
}
};
app.oncheckpoint=function(args){
};
app.start();
})();
ExploringWinJScorefundamentalsBeforeusingWinJSlibraryinanyoftheprojects,itisbesttoknowthecoreconceptsthathelpustowritequalityprogramsandusethebestofwhatthelibraryoffers.
ClassesandnamespacesThroughWinJSwecancreateclassesandnamespacewithsomespecialsyntax.ThisisprovidedintheWinJSlibrarytohandlecomplexscenarios.Asweknow,classesandnamespacesarethefeaturesofECMAScript6,butunfortunatelynoneofthebrowsershaveproperimplementationyet.However,withWinJSwecandefineclassesandnamespacesandit'sausefuloptiontousethemwhereneeded.
DefiningclassesinWinJS
ClassesinWinJScanbedefinedthroughtheWinJS.Class.define()method.HereisthesamplecodeofaclassinWinJS:
<script>
varLogger=WinJS.Class.define(function(value){
//constructor
console.log("Constructorisexecuting,valuepassedis:"+value);
}
);
//InitializingLoggerclassobject
varlog=newLogger("HelloWorld");
</script>
IntheprecedingcodewehavecreatedaclassnamedLogger,wherethefirstfunction'sparameteristheconstructor,secondisforanyinstanceMemberslikepropertiesandmethodsandthirdforstaticMemberstodefinestaticmembersandproperties.Thefollowingisthesignatureofthedefinemethod:
Nowlet'saddthepropertymessageandtheLogMessage()methodinthesameclassLogger:
<script>
varLogger=WinJS.Class.define(function(value){
this.logName=value;
this.enabled;
//constructor
console.log("Constructorisexecuting,valuepassedis"+value);
},{
logMessage:function(message){
if(this.logEnabled){
alert("Themessageis"+message);
}
},
logEnabled:{
get:function(){returnthis.enabled;},
set:function(value){this.enabled=value;}
}
}
);
varlog=newLogger("Samplelog");
log.logEnabled=true;
log.logMessage("HelloWorld");
<script>
Thesyntaxofdefiningthemethodsfortheclassisamethodnamefollowedwithacolon(:)andthefunctionbodyasfollows:
logMessage:function(message){
alert("Themessageis"+message);
}
Thepropertiescanbedefinedwithgetandsetfunctionmethodsasshowninthefollowingcode:
logEnabled:{
get:function(){returnthis.enabled;},
set:function(value){this.enabled=value;}
}
Multiplepropertiesandmethodscanbedefinedinasamewayseparatedwithcommaasshowninthefollowingcode:
logEnabled:{
get:function(){returnthis.enabled;},
set:function(value){this.enabled=value;}
},
logType:{
get:function(){returnthis.loggerType;},
set:function(value){this.loggerType=value;}
}
DerivingclassesinWinJS
ClassesinWinJScanbederivedbyusingtheWinJS.class.derive()method.Consideringthepreviousexample,wecanalsoaddthelogEnabledandlogTypepropertiesonthebaseclassandthenderivetheLoggerclassfromtheBaseLoggerclass.HereisthecodetoderiveclassesinWinJS:
<script>
varBaseLogger=WinJS.Class.define(function(logName){
this.enabled;
this.loggerType;
this.loggerName=logName;
},{
logEnabled:{
get:function(){returnthis.enabled;},
set:function(value){this.enabled=value;}
},
logType:{
get:function(){returnthis.loggerType;},
set:function(value){this.loggerType=value;}
}
});
varLogger=WinJS.Class.derive(BaseLogger,function(logName){
//callingbaseconstructorandpassingtheLogNametothebase
constructor
BaseLogger.call(this,logName);
},
{
logMessage:function(message){
if(Object.getOwnPropertyDescriptor(BaseLogger.prototype,
"logEnabled").get.call(this)==true){
alert("Themessageis"+message);
}
},
}
);
varlog=newLogger("HelloWorld");
log.logEnabled=true;
log.logType="Alert";
log.logMessage("hello");
</script>
IntheabovescriptwehavetakenboththepropertieslogTypeandlogEnabledtothebaseclassBaseLogger.InWinJS,basepropertiescanbeaccessedthroughthefollowingsyntax:
Object.getOwnPropertyDescriptor(BaseLogger.prototype,
"logEnabled").get.call(this)
SettingscanbedonebycallingthesetmethodafterthegetOwnPropertyDescriptor()methodcall:
Object.getOwnPropertyDescriptor(BaseLogger.prototype,
"logEnabled").set.call(this)=true;
NowifyouwanttotakethelogMessage()methodontheBaseLoggerclass,wecandoitthroughprototyping,asfollows:
BaseLogger.prototype.logMessage.call(this);
NamespacesinWinJS
Inobjectorientedprogramming,namespacesplayanimportantroleinorganizingclassesandcategorizingyourcode.Forexample,theservicescanresideundertheApplicationName.Servicesnamespace;modelscanresideundertheApplicationName.Modelsnamespace,andsoon.
Weshouldalwaysusenamespaceswherepossibleasitsolvesmanyproblemsthatcouldoccurinmid-sizetolargerprojects.Forexample,wehavetwoJavaScriptfilesaddedinourpagethathavesimilarnamesofpropertiesorfunctions.Theonereferencedlaterwillsupersedethememberfunctionsorvariablesofthepreviousonesiftheyhavethesamename.
WinJSprovidesaneasywaytologicallyorganizeclassesintonamespacesandyoucandefineanamespacebycallingWinJS.Namespace.define("namespacename",{}))).
HereistheexamplethatencapsulatesboththeBaseLoggerandLoggerclassintotheDemo.App.Utilitiesnamespace:
WinJS.Namespace.define("DemoApp.Utilities",{
//BaseLoggerclass
BaseLogger:WinJS.Class.define(function(logName){
this.enabled;
this.loggerType;
this.loggerName=logName;
},{
logEnabled:{
get:function(){returnthis.enabled;},
set:function(value){this.enabled=value;}
},
logType:{
get:function(){returnthis.loggerType;},
set:function(value){this.loggerType=value;}
},
}),
//Loggerclass
Logger:WinJS.Class.derive(BaseLogger,function(logName){
//callingbaseconstructorandpassingtheLogNametothebase
constructor
BaseLogger.call(this,logName);
},
{
logMessage:function(message){
if(Object.getOwnPropertyDescriptor(BaseLogger.prototype,
"logEnabled").get.call(this)==true){
alert("Themessageis"+message);
}
},
})
});
NowtheLogclasscanbeaccessedbyspecifyingitsnamespace,asshowninthefollowingcode:
varlog=newDemoApp.Utilities.Logger("SampleLogger");
log.logEnabled=true;
log.logType="Alert";
log.logMessage("hello");
MixinMostofthelanguagesdonotsupportmultipleinheritance.However,inWinJSwecandoitthroughmixins.Likeclass,mixinisacollectionofmethodsandpropertiesbuttheobjectofmixinscannotbeinstantiated.Itisusedtomixwithaclasstobringthemethodsandpropertiesthatmixinshave.Forexample,thefollowingisaMixinlogMixinthatcontainsalogMessage()method:
varlogMixin={
logMessage:function(message){
alert(message);
}
};
varSampleClass=WinJS.Class.define(function(){
});
WinJS.Class.mix(SampleClass,logMixin);
varsample=newSampleClass();
sample.logMessage("Mixin");
Wecanaddasmanymixinswhencallingthemixmethod.Iftwoormorehavecommonmethodsorproperties,lateronewilloverridetheexistingone.Let'slookintotheexampleswhichhavetwomixins,namelylogMixinandlogConsoleMixin.BoththemixinsandaSampleClasshaveasamelogMessage()method.Nowbasedonthespecification,themethodswillbeoverriddenandwhenthelogMessage()isinvokeditwillwriteamessageonaconsolelog:
//FirstMixin
varlogMixin={
logMessage:function(message){
alert(message);
}
};
//SecondMixin
varlogConsoleMixin={
logMessage:function(message){
console.log(message);
}
}
//Class
varSampleClass=WinJS.Class.define(function(){
},
logMessage=function(message){
varresult=confirm(message);
});
WinJS.Class.mix(SampleClass,logMixin,logConsoleMixin);
varsample=newSampleClass();
sample.logMessage("Mixin");
EventsinWinJSWinJSprovidesaneventMixinobjectthatcanbeusedtoregister,unregister,anddispatcheventsthroughthefollowingbasicsteps:
1. Firstofall,theclassfromwhichweneedtocallthedispatcheventneedstohaveWinJS.Utilities.eventMixinadded.WecanaddthisthroughtheWinJS.Class.mixmethod,asfollows:
WinJS.Class.mix(SampleClass,WinJS.Utilities.eventMixin);
2. OncetheeventMixinisinheritedbytheSampleClass,wecancallthedispatchEvent()methodtodispatchonaparticularaction.HereisthecodeoftheSampleclassthatdispatchestheeventoncetheexecutemethodiscalled:
varSampleClass=WinJS.Class.define(function(){
},
{
execute:function(message){
this.dispatchEvent("executeInvoked",{message:"Executed"
});
}
});
3. Next,wecanaddtheaddEventListener()methodandprovidetheeventHandler()thatwillbeinvokedoncethedispatchmessageiscalled:
varsampleClass=newSampleClass();
varsampleEventHandler=function(event){
alert(event.detail.message);
};
sampleClass.addEventListener("executeInvoked",sampleEventHandler);
sampleClass.execute("hello");
DatabindingWinJSprovidesaneasywayofbindinganyJavaScriptdatasourcetotheHTMLelement.AnyJavaScriptdatasourcecanbeboundusingdata-win-bindattributeonanHTMLelement.DatabindingfacilitatesinseparatingthedatawiththeviewandallowsyoutowritelesscodeandbindthedatawiththeelementsusingWinJS,whichprovidesthreetypesofdatabindingasfollows.
Onetimedatabinding
OnetimedatabindingisusedtobindtheelementonanHTMLpagefromaJavaScriptdatasource.Itisunidirectional,thatmeansiftheJavaScriptdatasourceisupdateditwillnotreflectthechangeontheHTMLtowhichitisboundto.
HereistheHTMLcodethathastwocontrolswhichbindsthepropertiesnameanddescriptionwiththeviewmodeldefinedinyourJavaScript:
<divid="rootDiv">
<div>CourseName:
<spanid="divForm"data-win-bind="innerText:name">loading</span>
</div>
<div>
CourseDescription:
<spanid="divForm"data-win-bind="innerText:
description">loading</span>
</div>
</div>
BelowistheJavaScriptcodewhichdefinestheviewmodel
letViewModel=WinJS.Class.define(function(){
this.nameProp;
this.descProp;
},
{
name:{
get:function(){returnthis.nameProp;},
set:function(value){this.nameProp=value;}
},
description:{
get:function(){returnthis.descProp;},
set:function(value){this.descProp=value;}
}
});
letviewModel=newViewModel();
viewModel.name="WinJSdatabinding";
viewModel.description="IntroductiontoWinJSdatabinding";
varpersonDiv=document.querySelector('#rootDiv');
WinJS.Binding.processAll(personDiv,viewModel);
Onewaydatabinding
Onewaydatabindingisaunidirectionalbinding.OncetheHTMLelementisboundtothe
JavaScriptdatasource,anychangesinthedatasourcewillreflectthechangeontheHTMLpagebutifsomethingisupdatedontheHTMLelement,itwillnotupdatethebackendJavaScriptdatasource.Onewaydatabindingcanbedonebymakingthesourcemodelobservable.SoifanythingchangesonthesourceobjectitwillupdatetheUIelementtowhichitisboundto.ItcaneitherbedonebyusingtheWinJS.binding.as()methodoraddingtheobservableMixinwiththesourceclass.
ThefollowingisanexampleofonewaydatabindingthatbindsthepropertiesNameandDescriptionandonthebuttonclickevent,updatestheHTMLelementandsetsthevaluesetfromthebackenddatasource.AddingabuttoninthepreviousHTMLpageaddedintheOnetimedatabindingsection:
//HTMLmarkup
<buttonid="btnUpdate">Click</button>
//JavaScript
letViewModel=WinJS.Class.define(function(){
this.nameProp;
this.descProp;
},
{
name:{
get:function(){returnthis.nameProp;},
set:function(value){this.nameProp=value;}
},
description:{
get:function(){returnthis.descProp;},
set:function(value){this.descProp=value;}
}
});
letviewModel=newViewModel();
viewModel.name="WinJSdatabinding";
viewModel.description="IntroductiontoWinJSdatabinding";
varpersonDiv=document.querySelector('#rootDiv');
letobservableViewModel=WinJS.Binding.as(viewModel);
WinJS.Binding.processAll(personDiv,observableViewModel);
document.querySelector('#btnUpdate').onclick=function(){
observableViewModel.name="newname";
observableViewModel.description="newdescription";
}
Twowaydatabinding
Twowaydatabindingworksinbothdirections.OncetheJavaScriptobjectisboundtotheHTMLcontrol,anychangesdoneonthecontrolitselforifthevalueoftheJavaScriptobjectgetschanged,thecontrolvaluewillbeupdatedandviceversa.ImplementingtwowaybindinginWinJSisnotstraightforward.Weneedtohavetheonewaybindinginplacetoreflectanychangehappeningonthebackenddatasourcetoreflectonthefrontend,aswellastoupdate
thebackenddatasourcefromanychangesdoneontheUIelement.ThiscanbedonebyimplementingonPropertyChange(),onKeyDown(),onChange(),oronClick()andothersbasedontheHTMLelement:
someTextboxElement.onpropertychange=function(){
someModel.property=someTextboxElement.value;
}
Anotherapproachistoimplementacustombindinginitializerwhichcanbeusedashighlightedinthefollowingcode:
<inputtype="text"data-win-bind="value:somePropertyBindingtwoWayBinding"/>
Let'screateacustomtwowaybindinginitializerandextendthesameviewModeltoacceptthenameanddescriptionupdatesthroughtextboxes.Hereisthecodeofourcustomtwowaybindinginitializer:
//DefiningBindinginitializertosupporttwowaybinding
WinJS.Namespace.define("Binding.Mode",{
twoway:WinJS.Binding.initializer(function
(source,sourceProperties,destination,
destinationProperties){
WinJS.Binding.defaultBind(source,sourceProperties,destination,
destinationProperties);
destination.onchange=function(){
vardestValue=destination[destinationProperties[0]];
source[sourceProperties[0]]=destValue;
}
})
});
Thencreateaclassthatcontainstwoproperties,namelynameanddescription:
//Definingclass
letViewModel=WinJS.Class.define(function(){
this.nameProp;
this.descProp;
},
{
name:{
get:function(){returnthis.nameProp;},
set:function(value){this.nameProp=value;}
},
description:{
get:function(){returnthis.descProp;},
set:function(value){this.descProp=value;}
}
});
//InitializingclassInstance
letviewModel=newViewModel();
viewModel.name="WinJSdatabinding";
viewModel.description="IntroductiontoWinJSdatabinding";
varrootDiv=document.querySelector('#rootDiv');
letobservableViewModel=WinJS.Binding.as(viewModel);
WinJS.Binding.processAll(rootDiv,observableViewModel);
Intheabovecode,wehavefirstdefinedthebindinginitializerusingWinJS.Binding.initializer.Whendefiningthisinitializerwehavetopassfourpropertiesnamelysourceelementanditspropertiesobjectanddestinationelementanditsproperties.Soforexample,inourcasethesourceelementisatextboxandthesourcepropertyisitsvalue,whereasthedestinationelementwillbeaspanandinnerTextasitsdestinationproperty.WinJS.Binding.defaultBindcreatestheonewaybindingandthenwecanregistertheonchange()eventofthesourcepropertywhichupdatesthedestinationproperty.Thenwedefinedaclassandtheninitializedthevaluesbyinitializinganinstance.Andfinally,wehavetransformedthemodelintotheobservablemodeltoprovidetwowaybinding.
Now,intheHTMLelement,wecanaddthebindingasfollows:
<divid="rootDiv">
<div
<inputtype="text"data-win-bind="value:nameBinding.twoWayBinding"/>
</div>
<div>
CourseName:
<spanid="spanName"data-win-bind="innerText:
name">loading</span>
</div>
<div>
<inputtype="text"data-win-bind="value:descriptionBinding.twoWayBinding"/>
</div>
<div>
CourseDescription:
<spanid="spanDesc"data-win-bind="innerText:
description">loading</span>
</div>
</div>
Adatabindingworkingmodel
WhenthedatabindingisdoneinWinJS,theWinJS.processAll()methodhastobecalledifit'sdoneusingWinJS.Thismethodscansalltheelementswhichspecifythedata-win-bindattributes.Foreachelement,itchecksifthedataboundwiththeelementisobservableornot.Thisisacrucialstepwhichidentifiesthetypeofbindinganddeclareswhetherthebindingisaonewaybinding,onetimebinding,ortwowaybinding.
PromisesPromisesrepresentanobjectthatcontainsavaluethatmightbeavailableatanytime.It'sapromisewhichsatisfiestheconsumerthattheresourcewillbeavailableandtheconsumercandotherestoftheworkwithoutwaitingfortheresourceinanasynchronousmanner.
Itworksasanasync/awaitfeatureofC#.Promisesallowconsumerstodootherworkratherthanwaitingforthevaluetoreturnandprovidescertainmethodstoacknowledgetheconsumeroncethepromiseisreceived.Incertaincases,thereisachanceofnothavingtheresponsereturnduetosomeerrorandthatcanalsobehandledbyimplementingspecificcallbacks.
InWinJS,promiseisanobjectwithfunctionsthenanddone.Wecaninitializepromiseasfollows:
varpromise=newWinJS.Promise(function(completed,error,progress)
//Callifweneedtoupdateconsumerthatstillinprogress
progress("progress");
//Callifanyerroroccurs
error("error");
//Callwhenthefunctioniscompleted
completed("completed");
}
);
Theprecedingcodeisthewayofdefiningafunctionthatreturnsapromise.Wecanthencallaprogressmethodifthemethodisnotcompletedandweneedtonotifytheconsumerifsomethingisinprogress.Oncethepromiseisdefined,wecanusethethenanddonemethodstoimplementcallbackmethodsthatwillbetriggeredbypromise.Thethenmethodreturnsapromiseanddenotestheintermediatestageoftheoperationwhereasdoneisthefinalstageoftheoperationanddoesnotreturnapromise:
promise.then(
function(){console.log("completed");},
function(){console.log("error")},
function(){console.log("promise")}
);
Thefollowingexampleshowsthefunctionthatdisplaysthetableinaconsolewindowandreturnscompletedoncethepromiseisprocessed:
functionexecuteTable(table,max)
{
returnnewWinJS.Promise(function(completed,error,progress){
for(i=1;i<max;i++){
console.log(table+'X'+i+'='+(table*i));
}
completed("executedtable")
});
};
executeTable(2,10).then(
function(completedVal){
console.log(completedVal);
},function(errorVal){
console.log(errorVal);
},
function(onProgressVal){
console.log(onProgressVal);
}
)
Thefollowingistheoutput:
Nowlet'smodifythesameexampleandinvokeprogresstosendintermediateresultstotheconsumeroneachiteration.Theprecedingmethodissynchronousandreturningpromisedoesn'tmeanthemethodwillbeexecutedasynchronously.TomakethismethodrunasynchronouslywecanwraptheblockofcodethroughthesetImmediate()function.
setImmediate()istheJavaScriptfunctionwhichisusedtointerrupttheexecutionofthefunctionandreturnsthecallbackfunctionimmediately,thateventuallyinvokestheonProgress()functionofpromiseinourcase.HereisthemodifiedversionwiththesetImmediate()andonProgress()methods:
functionexecuteTable(table,max)
{
returnnewWinJS.Promise(function(completed,error,
onProgress){
window.setImmediate(function(){
for(i=1;i<=max;i++){
varrow=table+'X'+i+'='+(table*i);
onProgress(row);
}
completed("executedtable")
},0);
});
};
executeTable(2,10).then(
function(completedVal){
console.log(completedVal);
},function(errorVal){
console.log(errorVal);
},
function(onProgressVal){
console.log(onProgressVal);
}
)
Theresultoftheprecedingcodesnippetwillbethesameasshowninthepreviousexample.However,theuseofthesetImmediate()functionallowstheonProgress()methodtowritemessagestotheconsolewindowasynchronouslyandismoreefficientintermsofperformance.
Otheroperationsofpromises
Thereareseveralothermethodsonpromisesthatcanbeusedtocancelanypromises,chainpromises,timeout,wrap,andsoon.Let'slookovereachmethodandseehowitcanbeused.
Chainingpromisesandhandlingerrors
Multiplepromisescanbechainedusingthenandbasedontheordertheyarechained,getexecutedonebyonesequentially.HereisthesimpleexamplethatloadsthewebpageusingtheWinJS.xhr()method.ThismethodisthebuiltinmethodthatreturnsapromiseandwecanusethismethodtomakeHTTPrequests:
varpromise1=function(){returnWinJS.xhr({url:"http://microsoft.com"})
};
varpromise2=function(){returnWinJS.xhr({url:
"http://google.com"})};
varpromise3=function(){returnWinJS.xhr({url:
"http://techframeworx.com"})};
varpromise4=function(){returnWinJS.xhr({url:
"http://msdn.microsoft.com"})};
promise1().then(function(dataPromise1){
console.log("gottheresponsefrompromise1");
returnpromise2();
}).then(function(dataPromise2){
console.log("gottheresponsefrompromise2");
returnpromise3();
}).then(function(dataPromise3){
console.log("gottheresponsefrompromise3");
returnpromise4();
}).done(function(dataPromise4){
console.log("gottheresponsefrompromise4");
console.log("completedthepromisechain");
});
Intheprecedingcodewearereturningthenextpromiseoneverypromisechainexecutionblock.Thisisrequiredwhenchainingpromisesotherwiseitwouldnotcallthenextpromiseinthepipeline.Forthelastpromiseinthepipeline,wehaveuseddoneinsteadofthenwhichactuallytellsusthatthereisn'tapromisenextinthechainandnochainingcanbedonenow.Anotherbenefitistoperformerrorhandling.Inthedonemethod,wecangetalltheerrorsbeingthrownbyanyofthepromisesinthechain.Ifwedon'tusedonethenwewillnotbeabletoaccessanyerrorsthrowninthepromisechain.Thefollowingexampleisthemodifiedversionofthepreviousexamplewitherrorhandling:
varpromise1=function(){returnWinJS.xhr({url:
"http://microsoft.com"})};
varpromise2=function(){returnWinJS.xhr({url:
"http://google.com"})};
varpromise3=function(){returnWinJS.xhr({url:
"htt://techframeworx.com"})};
varpromise4=function(){returnWinJS.xhr({url:
"http://msdn.microsoft.com"})};
promise1().then(function(dataPromise1){
console.log("gottheresponsefrompromise1");
returnpromise2();
}).then(function(dataPromis2){
console.log("gottheresponsefrompromise2");
returnpromise3();
}).then(function(dataPromise3){
console.log("gottheresponsefrompromise3");
returnpromise4();
}).done(function(dataPromise4){
console.log("gottheresponsefrompromise4");
console.log("completedthepromisechain");
},function(error){
console.log("someerroroccurred,cause:"+error);
});
Intheprecedingexamplewehaveuseddoneinthefinalpromiseinthechain.Now,ifyouhavenoticed,thepromise2URLisnotvalidandthereisatypomistake.Nowifweexecutetheprecedingcode,promise1andpromise2willbeexecutedandwillwritethemessagesintheconsolelogwindow.Whereas,thepromisewillnotbeexecutedbuttheerrormethodwillbeinvokedanddefinedunderthedonemethodandwillwritetheerrordescriptionintheconsolelogwindow:
Cancelingpromises
Promisescanbecanceledbycallingthecancelmethodonthepromiseobject.Thefollowingisanexampletocancelanypromise:
varpromiseGoogle=function(){returnWinJS.xhr({url:"http://google.com"})
};
googlePromiseObj=promiseGoogle();
googlePromiseObj.cancel();
Promisescanonlybecanceledifit'snotcompletedandwentintotheerrorstateonceitwascanceled.
Joiningpromises
Multiplepromisescanbejoinedtogetherandreturnwhenallofthemarefinished.Wecanjoinpromises,asshowninthefollowingcode:
varpromise1=function(){returnWinJS.xhr({url:"http://microsoft.com"
})};
varpromise2=function(){returnWinJS.xhr({url:
"http://googe.com"})};
varpromise3=function(){returnWinJS.xhr({url:
"http://techframeworx.com"})};
WinJS.Promise.join([promise1,promise2,promise3])
.done(function(){
console.log("Allthepromisesarefinished");
});
Promise.any()canbeusedincaseswhenweneedtoknowifanyofthedefinedpromisesinsidetheanymethodhaveexecuted:
varpromise1=function(){returnWinJS.xhr({url:"http://microsoft.com"})
};
varpromise2=function(){returnWinJS.xhr({url:"http://googe.com"})};
varpromise3=function(){returnWinJS.xhr({url:"http://techframeworx.com"
})};
WinJS.Promise.any([promise1,promise2,promise3])
.done(function(){
console.log("Oneofthepromisesisfinished");
});
Checkingpromise
WinJS.Promise.is()isamethodthattakesavalueasaparameterandchecksifthatvalueisapromiseornot.Forexample,callingWinJS.xhrintheWinJS.Promise.is()methodwillreturntrue:
WinJS.Promise.is(WinJS.xhr({url:"http://microsoft.com"}));
Wrappingnon-promiseintopromise
AnyfunctioncanbewrappedintothepromiseusingtheWinJS.Promise.as()method.Thefollowingcodewrapsthenon-promisedisplayMessage()methodintoapromise:
functiondisplayMessage(){
console.log("Thisisanonpromisefunction")
}
varpromiseDisplayMessage=WinJS.Promise.as(displayMessage);
promiseDisplayMessage.done(function(){console.log("promiseis
executed")});
ExploringWinJScontrolsandstylesWindowslibraryforJavaScriptprovidesarichsetofcontrols,databindingoptions,andpromisesandinthissectionwewillexploreafewpopularcontrolsandstylingoptions.
NoneoftheWinJScontrolshaveseparatemarkup,insteadWinJSlibraryprovidesseveralattributesthatcanbeusedwiththeexistingHTMLelements.
AddingWinJScontrolsAswehaveseen,therearenoanymarkupsforWinJScontrolsandtheycanbeaddedthroughattributesontheHTMLelements.WinJScontrolscanbeaddedbyaddinganyHTMLelementandsettingitsdata-win-controlattributevaluetothenameoftheWinJScontrol.
Inthefollowingexample,wearechangingasimpleHTMLbuttonelementintothebackbuttonusuallyseeninstoreapps.Andthiscanbedonebyaddingthedata-win-controlattributeandsettingafullyqualifiednametoWinJS.UI.BackButton.
HereistheHTMLmarkup:
<buttondata-win-control="WinJS.UI.BackButton">WinJSbutton</button>
Whenyourunit,itwillrenderabackbuttononthepage,asshowninthefollowingfigure:
Alsoitdoesnotonlychangethelookbutalsoprovidesthebackwardnavigationfunctionalityoutofthebox.
SettingpropertiesofWinJScontrolsEveryHTMLelementhasseveralpropertieswhichcanbeaddressedbyspecifyingvaluesthroughattributes.Forexample,ratingcontrolallowsausertorateanyitemandwecansettheproperties,likethemaxandminrangeofstarstobedisplayed:
<divid="ratingControl"data-win-control="WinJS.UI.Rating"
data-win-options="{minRating:1,averageRating:5,maxRating:10}">
</div>
Theoutputoftheprecedingmark-upwillgeneratearatingcontrollikethefollowingfigure:
ThereareotherWindowsspecificcontrolslikeListView,FlipView,andZoomthatyoucanuseinyourpageandbringhighperformanceonlargecollectionsorobjects.YoucanlearnmoreaboutcontrolsattheWindowsDevCenterwebsiteat:https://msdn.microsoft.com/en-us/library/windows/apps/mt502392.aspx
UsingWindowsruntimefeaturesWinJSprovidesacompleteAPItouseWindowsruntimefeaturesanddevicespecificfeatures.WhenaccessingthedevicespecificfeaturesusingWinJS,thewebapplicationshouldrunasawindowsapplicationandaccessingitfromabrowserwillresultinanerror.Also,MicrosofthasreleasedtheconceptofHostedappswhichenableanywebapplicationtohostasawindowsapplicationwithafewconfigurationsteps.
HostedappsandaccessingthecameraHostedappswereintroducedwiththelaunchofUWP.Let'screateasimpleexampletoconvertasimpleASP.NETcoreapplicationintoaWindowsapplicationusingtheHostedappconceptandaccessthecamera.
CreatingtheASP.NETcoreapplication
CreateasimpleASP.NETcoreapplicationinVisualStudio2015andaddtheWinJSpackagesthroughNPM.Hereisthecodesnippetofpackage.json:
{
"version":"1.0.0",
"name":"ASP.NET",
"private":true,
"dependencies":{
"winjs":"4.4.0"
},
"devDependencies":{
"gulp":"^3.9.1"
}
}
WecanaddWinJSunderthedependenciessectionandonsavingthepackage.jsonfile,thepackagewillbedownloadedautomatically.WehavetoaddgulpaswelltocopytherelatedlibrariesandCSSfilesinthewwwrootfolder.Afterthis,addthegulpfile.jsandaddthefollowingscript:
///<bindingClean='clean'/>
"usestrict";
vargulp=require("gulp");
varpaths={
webroot:"./wwwroot/"
};
varconfig={
libBase:'node_modules',
lib:[
require.resolve('winjs/js/base.js'),
require.resolve('winjs/js/ui.js'),
require.resolve('winjs/js/winjs.intellisense.js'),
require.resolve('winjs/js/winjs.intellisense-setup.js')
],
libCss:[require.resolve('winjs/css/ui-dark.css'),
require.resolve('winjs/css/ui-light.css')
]
};
gulp.task('build.lib',function(){
returngulp.src(config.lib,{base:config.libBase})
.pipe(gulp.dest(paths.webroot+'lib'));
});
gulp.task('build.libCss',function(){
returngulp.src(config.libCss,{base:config.libBase})
.pipe(gulp.dest(paths.webroot+"lib"));
});
Whenyourunthebuild.libandbuild.LibCsstasksthroughataskrunnertabinVisualStudio2015,itwillcopytheWinJSlibrariesandCSSfilesinsidethewwwrootfolder:
Inthisapplication,wewillhaveasimpleHTMLpagethatwecandirectlyaddintothewwwrootfolder,forthisweneedtocalltheapp.UseStaticFiles()methodintheConfigure()methodandaddthepackageinproject.json:
"Microsoft.AspNet.StaticFiles":"1.0.0-rc1-final"
Let'saddtheIndex.htmlpageinsidethewwwrootfolderandaddthefollowingscriptsintheHTMLheadelement:
<scriptsrc="lib/winjs/js/base.js"></script>
<scriptsrc="lib/winjs/js/ui.js"></script>
<scriptsrc="lib/winjs/js/winjs.intellisense-setup.js"></script>
<scriptsrc="lib/winjs/js/winjs.intellisense.js"></script>
<scriptsrc="http://ajax.aspnetcdn.com/ajax/jquery/jquery-1.9.0.js"></script>
WewillbeusingthelightWindowsthemesoaddui-light.css,asfollows:
<linkrel="stylesheet"href="lib/winjs/css/ui-light.css"/>
NowaddthepagecontentwhichcontainsabuttonCapturetocapturetheimageandanimageelementtodisplaythecapturedimage:
<divid="rootDiv">
<divclass="col-md-4">
Clicktocaptureimage<inputtype="button"value="Capture"
onclick="returnCaptureCamera();"/>
</div>
<br/>
<imgid="imgPhoto"width="500"height="500"style="border:dotted;"/>
</div>
Thefollowingistheoutputofthepage:
Nowaddthefollowingscripttoaccessthecameraandattachthecapturedimagewiththeimageelement:
<script>
if(window.Windows){
functionCaptureCamera(){
varnotifications=Windows.UI.Notifications;
vardialog=newWindows.Media.Capture.CameraCaptureUI();
varaspectRatio={width:1,height:1};
dialog.photoSettings.croppedAspectRatio=aspectRatio;
dialog.captureFileAsync(Windows.Media.Capture.CameraCaptureUIMode.photo).done(fun
ction(capturedImage){
if(capturedImage){
varimageURL=URL.createObjectURL(capturedFile,{
oneTimeOnly:true});
document.getElementById("img").src=imageURL;
}
else{
WinJS.log&&WinJS.log("Noimagecapturedyet",
"WinJSTestApp","Status");
}
},function(err){
WinJS.log&&WinJS.log(err,"WinJSTestApp","Error");
});
}
}else{
functionCaptureCamera(){
alert("Cannotaccesscamera,itshouldbehostedasa
windowsapplication");
}
}
</script>
ConvertinganASP.NETapplicationintoWindowsapplicationusingtheHostedappconcept
ConvertinganywebapplicationintotheWindowsapplicationisverysimple.InVisualStudio2015youcanstartcreatingasimpleJavaScriptbasedWindowsapplicationusingtheBlankApp(UniversalWindows)templateasshowninthefollowingscreenshot:
Whenyouaddaprojectitwilladdthecss,images,js,andwinjsfolders.Wehavetodeletethecss,js,andwinjsfoldersaswewillnotbeusinganyofthefilesinthisprojectand
configurethewebapplicationcreatedaboveandtransformittoaWindowsapplication.
Openthepackage.appxmanifestwindow.AddtheURLintheStartPagetextboxasshowninthefollowingscreenshot.OursampleASP.NETapplicationcreatedabovewashostedonport41345:
IntheContentURIstab,addtheURIofourwebapplicationandselectAllunderWinRTAccess.YoucanspecifytheURLofanywebapplicationwhichshouldbehostedsomewhere.Intheprecedingscreenshotweareusinglocalhostwhichactuallypointstoawebapplicationhostedlocally:
ThiswindowallowsustospecifyaccessrulestoWinRTfeaturesandwecansetittoNone,All,orAllowforwebonly.
Buildingandrunningtheapplicationwillshowthewindowsapplicationdialoghostingourwebapplicationindex.htmlpage:
ClickingontheCapturebuttonwillprovideapopupofanotherdialogtotakeasnapshot,asshowninthefollowingscreenshot:
Aftertakingthedesiredshot,itwillaskyoutosaveorrejectthroughtickandcrossbuttons:
SelectingtickwillrenderthephotointheimgHTMLelement,asshowninthefollowingscreenshot:
SummaryInthischapter,weexploredtheWinJSWindowslibraryforJavaScriptlibrarywhichisopensourceandunderanApachelicense.Welearnedthecoreconceptsofdefiningclasses,namespaces,derivingclasses,mixins,andpromises.WealsolookedintothedatabindingtechniquesandhowtousethewindowscontrolsorattributesinHTMLelementstochangethebehaviorandlookandfeelofthecontrol.Andfinally,weusedtheWinRTAPItoaccessthedevicecamerainourwebapplicationandlearnedtheconceptofHostedappandtransforminganywebapplicationintotheWindowsappsusingtheUniversalWindowtemplateinVisualStudio2015.Inthenextchapter,wewilllearnaboutafewgooddesignpatternsthatcanbeimplementedinJavaScripttoachievespecificrequirements.
Chapter7.JavaScriptDesignPatternsIneverymid-tolarge-sizedprojects,goodarchitectureanddesignalwaysplaysanimportantroleinhandlingcomplexscenariosandincreasingthemaintainabilityoftheproduct.Designpatternsarebestpracticesdevelopedandusedbyprofessionaldeveloperstosolveaparticularproblem.Ifadesignpatternhasbeenusedintheapplicationforspecificscenarios,itevadesmanyoftheissuesonecouldfaceduringdevelopmentorwhenrunningtheapplicationinproduction.Designpatternssolvetheproblemsbyprovidingtheguidelineswhichareindustrybestpracticestohandleproblemsortoachieveorimplementanyrequirement.Asingletonpattern,forexample,isusedtocreateonlyoneinstancethatissharedamongall,whereasaprototypeisusedtoextendtheexistingfunctionalityofanobjectbyaddingmorepropertiesandmethodsandsoon.Designpatternsareclassifiedintothreecategories,namelycreational,structural,andbehavioralpatterns.Thetopicswhichwewillcoverinthischapterareasfollows:
Creationalpatterns:Thefollowingarethecreationalpatternswewilldiscussinthischapter:
SingletonpatternFactorypatternAbstractfactorypatternPrototypepattern
Structuralpatterns:Thefollowingarethelistofstructuralpatternswewilldiscussinthischapter:
AdapterpatternDecoratorpatternFacadepatternBridgepattern
Behavioralpatterns:Thefollowingarethelistofbehavioralpatternswewilldiscussinthischapter:
ChainofresponsibilityObserverpatternPub/subpatternPromises
CreationalpatternsCreationalpatternsareusedforobjectinstantiation.Theyareusedinsituationswherethebasicformofobjectcreationcouldresultindesignproblemsorincreasecomplexitytothedesign.Inthefollowingsection,wewilldiscussallfourcreationalpatternsmentionedpreviously,andhowtoimplementtheminJavaScript.
SingletondesignpatternSingletonisthemostwidelyusedpattern.Itisusedinscenarioswhereweneedtosharethesameinstanceofaclassorfunction(intermsofJavaScript)betweendifferentobjects.Itensuresthatthereisonlyoneinstanceofparticularobjectwhichcanbeaccessedgloballyatanypoint:
Inasingletonpattern,theconstructorshouldbeprivatewhichrestrictstheusertocreateobjectsusinganewkeywordandexposesonemethodthatcreatesaninstanceandverifiesthatonlyoneinstanceexists.Asimpleexamplecouldbealoggerobjectthatwritesthelogtothebrowser'sconsolewindow:
<script>
varLogger=(function(){
//privatevariable
varinstance;
//privatemethod
functioninitializeInstance(){
//closurereturnsthepublicaccesstothewriteLogfunctionthat
canbeaccessiblebythesingletonobject
return{
writeLog:function(message){
console.log(message);
}
};
};
//closurethatreturnsthepublicaccesstothegetInstancemethodthat
returnsthesingletonobject
return{
//Thisisapublicmethodthatreturnsthesingletoninstance
getInstance:function(){
if(!instance){
instance=initializeInstance();
}
returninstance;
},
};
})();
varlogger=Logger.getInstance();
logger.writeLog("Helloworld");
</script>
Tip
InJavaScript(ES5standard),classesarestillrepresentedthroughfunctions.
InJavaScript,toimplementasingleton,wecanuseclosures.Closuresareinnerobjectsthathaveaccesstotheprivatemembersofthefunction,suchasaccessingvariablesandmethodsdefinedwithinaparentfunction,andareaccessiblefromclosures.
Brackets()inthelaststatementarespecifiedtoassigntheobjectreturningtotheloggervariableratherthanthefunctionitself.Thisactuallyrestrictstheobjectfrominitializingthroughanewkeyword.
Intheprecedingscript,thefunctionfirstreturnstheclosurethathasonegetInstance()method,whichactuallycheckstheprivatemembervariableinstanceandifitisnotinitializeditcallstheinitializeInstance()methodthatreturnsanotherclosurecontainingthewriteLog()method.Wecanaddmoremethodsorvariablesseparatedbycommasandtheywillbeaccessiblewiththeloggerobject.HereisthemodifiedversionoftheinitializeInstance()methodthathasonemoremethod,showAlert(),andavariable,logEnabled:
functioninitializeInstance(){
//closurereturnsthepublicaccesstothewriteLogfunctionthat
canbeaccessiblebythesingletonobject
return{
writeLog:function(message){
if(this.logEnabled)
console.log(message);
},
showAlert:function(message){
if(this.logEnabled)
alert(message);
},
logEnabled:false
};
};
FactorypatternThefactorypatterndelegatesobjectinstantiationtothecentralizedclass.Insteadofinstantiatingtheobjectusinganewkeyword,wecallthefactorymethodthatreturnsthetypeoftheobjectrequested:
HereisanexampleoftheLoggerFactorythatcreatestheloggerinstancesbasedontheloggertype:
//LoggerFactorytoinstantiateobjectsbasedonloggertype
functionLoggerFactory(){
varlogger;
this.createLogger=function(loggerType){
if(loggerType==="console"){
logger=newConsoleLogger();
}
elseif(loggerType==="alert"){
logger=newAlertLogger();
}
returnlogger;
}
}
//Consoleloggerfunction
varConsoleLogger=function(){
this.logMessage=function(message){
console.log(message);
}
};
//Alertloggerfunction
varAlertLogger=function(){
this.logMessage=function(message){
alert(message);
}
};
varfactory=newLoggerFactory();
//creatingConsoleloggerobjectusingLoggerFactory
varconsoleLogger=factory.createLogger("console");
consoleLogger.logMessage("Factorypattern");
//createAlertloggerobjectusingLoggerFactory
varalertLogger=factory.createLogger("alert");
alertLogger.logMessage("Factorypattern");
Inourexample,thefactoryclassisLoggerFactorythatcreatesinstancesofConsoleLoggerandAlertLoggerobjects.LoggerFactoryexposesacreateLogger()methodthattakesthetypeofloggerasaparametertodeterminewhichobjectneedstobeinstantiated.EachtypeofloggerhasitsownlogMessage()methodtoeitherlogontheconsolewindoworshowanalertmessage.
AbstractfactorypatternTheabstractfactorypatternencapsulatesthecollectionoffactoriestocreateinstances.Theinstanceexposesthesamemethodthatcanbeinvokedbythefactory.Thefollowingisanexampleoftwofactories,ShapeFactoryandCarFactoryandeachonereturnstwotypesofinstance.ShapeFactoryreturnsCircleandSquareinstanceswhereasCarFactoryreturnsHondaCarandNissanCarinstances.Eachoftheinstancehavethesamemethodmake()thatcanbecalledforanyinstance:
HereisthecodeforShapeFactory:
<script>
//ShapeFactorytocreateinstancesofCircleandSquare
varShapeFactory=function(){
varshape;
this.createShape=function(shapeType){
if(shapeType==="circle"){
returnnewCircleShape();
}
elseif(shapeType==="square"){
returnnewSquareShape();
}
}
}
//Circleobjecttodrawcircle
varCircleShape=function(){
this.make=function(){
varc=document.getElementById("myCanvas");
varctx=c.getContext("2d");
ctx.beginPath();
ctx.arc(100,75,50,0,2*Math.PI);
ctx.stroke();
}
}
//Squareobjecttodrawsquare
varSquareShape=function(){
this.make=function(){
varc=document.getElementById("myCanvas");
varctx=c.getContext("2d");
ctx.beginPath();
ctx.rect(50,50,50,50);
ctx.stroke();
}
}
ThefollowingisthecodeforCarFactorythatcreatesinstancesofHondaandNissancars:
//Carfactorytocreatecars
varCarFactory=function(){
varcar
this.createCar=function(carType){
if(carType==="honda"){
returnnewHondaCar();
}
elseif(carType==="nissan"){
returnnewNissanCar();
}
}
}
//Hondaobject
varHondaCar=function(){
this.make=function(){
console.log("ThisisHondaAccord");
}
}
//Nissanobject
varNissanCar=function(){
this.make=function(){
console.log("ThisisNissanPatrol")
}
}
Wecalltheexecutemethodonabuttonclickeventwhichcreatestheinstancesandholdstheminanarray.Eventuallytheobjectsmake()methodwillbeexecutedthatdrawsthecircleonaHTMLcanvasandwritesmessagesontheconsole.AsinJavaScript,wecannotdefineabstractmethods;wehavetoexplicitlydefinethesamemethod,suchasmake()inourcasethatcompletesourabstractfactorypattern:
functionexecute(){
//initializinganarraytoholdobjects
varobjects=[];
//CreatingShapeFactorytocreatecircleshape
varshapeFactory=newShapeFactory();
varcircleShape=shapeFactory.createShape("circle");
//CreatingCarFactorytocreatecars
varcarFactory=newCarFactory();
varhondaCar=carFactory.createCar("honda");
varnissanCar=carFactory.createCar("nissan");
//Addingalltheinstancescreatedthroughfactories
objects.push(circleShape);
objects.push(hondaCar);
objects.push(nissanCar);
//Callingmakemethodofalltheinstances.
for(vari=0;i<objects.length;i++){
alert(objects[i]);
objects[i].make();
}
}
</script>
HereistheHTMLcodecontainingacanvasandabutton:
<div>
<inputtype="button"onclick="execute()"value="Execute"/>
</div>
<div>
<canvasid="myCanvas"></canvas>
</div>
Theoutputwillbeasfollowsonabuttonclickevent.ThecirclewillbedrawnandtwomessageswillbeprintedontheconsolewindowforHondaandNissancarobjects:
PrototypepatternTheprototypepatternisusedtocreateinstancesthatareclonesofexistinginstances.Itisusedinscenarioswhereweneedtoautoconfiguretheobjectwithsomespecificvaluesorpropertiesandusersdon'thavetoexplicitlydefine:
Thefollowingisthecodethatimplementstheprototypepattern:
<script>
functionCar(make,model,year,type)
{
this.make=make;
this.model=model;
this.year=year;
this.type=type;
this.displayCarDetails=function(){
}
}
functionCarPrototype(carPrototype){
varcar=newCar();
this.getPrototype=function(){
car.make=carPrototype.make;
car.model=carPrototype.model;
car.year=carPrototype.year;
car.type=carPrototype.type;
returncar;
}
}
(function(){
varcar=newCar("Honda","Accord","2016","sedan");
varcarPrototype=newCarPrototype(car);
varclonedCar=carPrototype.getPrototype();
})();
</script>
Intheprecedingcodesnippet,thereisacarobjectthatacceptsfourparameters,make,model,yearandtype.CarPrototype()isafunctionthatacceptsthecarobjectandreturnstheclonedversionofthecarobject.Thispatternisperformanceefficientandsavesdeveloperstimecreatingaclonecopyoftheobjectbyjustcallingtheprototypeobject'sclonemethod.Theuserdoesnotneedtocareaboutpopulatingthepropertiesafterobjectinstantiation;itinitializeswhentheobjectiscreatedandgetsthesamevaluesastheoriginalobject.ItisusedinconditionswhereweneedtocloneinstancesofobjectswhentheyareinaspecificstateandcanbeeasilyclonedbycallingthegetProtoype()method.
StructuralpatternsStructuralpatternsareusedtosimplifytherelationshipsbetweenobjects.Inthefollowingsections,wewilldiscussallfourstructuralpatternsmentionedpreviously,andhowtoimplementtheminJavaScript.
AdapterpatternTheadapterpatternisusedinsituationsinwhichourapplicationisdependentonanyobjectwhosepropertiesandmethodschangefrequentlyandwewanttoavoidmodifyingthecodetousethem.Theadapterpatternallowsustowraptheinterfaceofaspecificobjectaswhattheclientexpectsandratherthanchangingthewholeimplementationwecanjustcallthewrapperobjectwhichcontainsthecodeasperthemodifiedversion.Thiswrapperobjectiscalledanadapter.Let'shavealookatabasicexamplethatusesthePersonRepositorytosavethepersonobjectbyperforminganAjaxrequest:
ThefollowingistheoldinterfaceofthePersonRepositoryobject:
//oldinterface
functionPersonRepository(){
this.SavePerson=function(name,email,phoneNo){
//Callajaxtosaveperson
console.log("Name:"+name+",Email:"+email+",PhoneNo:"+
phoneNo);
}
}
TheprecedinginterfacehasoneSavePerson()methodthattakesthreeparameters:name,
email,andphoneNo.HereistheoriginalcodeforusingtheSavePerson()method:
varexecute=function(){
varpersonRepository=newPersonRepository();
personRepository.SavePerson("John","[email protected]","1201111111");
}
SupposethisinterfacehaschangedandthenewpersonrepositoryinterfaceacceptsthepersonJSONobject,insteadofpassingvaluesasparameters.OnewayistomodifythefunctionhereitselfandencapsulatetheseparametersintheJSONobjectandsend.Alternatively,wecanimplementanadapterpatternwhichcontainsanadapterfunctionthattakesthreeparameters,callsthenewPersonRepositoryobjectandpassestheJSONobject.
ThefollowingisthenewinterfaceofPersonRepository:
functionPersonRepository(){
this.SavePerson=function(person){
//callajaxtosendJSONpersondata
console.log("Name:"+person.name+",Email:"+person.email+",
PhoneNo:"+person.phoneNo);
}
}
HereistheadapterpatternthatencapsulatestheparametersinaJSONobjectandcallsthenewPersonRepositoryinterface:
functionPersonRepositoryAdapter(){
this.SavePerson=function(name,email,phoneNo){
varperson={"name":name,"email":email,"phoneNo":phoneNo};
varpersonRepository=newPersonRepository();
//callingnewPersonRepository
personRepository.SavePerson(person);
}
}
HereisthemodifiedversionthatcallstheadapterpatternratherthancallingtheoldPersonRespositoryinterface:
varexecute=function(){
//oldinterface
//varpersonRepository=newPersonRepository();
//personRepository.SavePerson("John","[email protected]","1201111111");
//callingadapterpattern
varpersonAdapter=newPersonRepositoryAdapter();
personAdapter.SavePerson("John","[email protected]","1201111111");
}
DecoratorpatternThedecoratorpatternisusedtochangethebehavioroftheobjectatruntime.DecoratorsarelikeannotationattributesinC#.Likewise,wecanaddmultipledecoratorsononeobjectaswell.Thedecoratorpatterncanbeimplementedbycreatingadecoratorobjectandassociatingthatwiththetargetobjectwhosebehaviorneedstobechanged:
ThefollowingisanexampleofadecoratorthataddstheTaxandCourierchargedecoratorsontheProductobject:
<script>
varProduct=function(code,quantity,price){
this.code=code;
this.quantity=quantity;
this.price=price
this.total=function(){
this.price=price*quantity;
returnthis.price;
}
}
//DecoratorthattakesproductandpercentasparametertoapplyTax
functionAddTax(product,percent){
product.total=function(){
product.price=product.price+(product.price*percent/100);
returnproduct.price;
}
}
//DecoratortoaddCourierchargesinthetotalamount.
functionAddCourierCharges(product,amount){
product.total=function(){
product.price=product.price+amount;
returnproduct.price;
}
}
varexecute=(function(){
varprod=newProduct("001",2,20);
console.log("Totalprice:"+prod.total());
AddTax(prod,15);
console.log("TotalpriceafterTax:"+prod.total());
AddCourierCharges(prod,200);
console.log("TotalpriceafterCourierCharges:"+prod.total());
})();
Theproductobjecttakesthreeparameters,code,quantity,andprice,andcalculatesthetotalpricebasedonquantityandprice.AddTax()andAddCourierCharges()aretwodecoratorobjectsproductobjectfollowedwithaparametertoapplyspecificcalculationonchangethetotalprice.TheAddTax()methodappliesthetaxbasedonthevaluesupplied,whereasAddCourierCharges()willaddthecourierchargeamounttothetotalprice.TheExecute()methodwillbecalledimmediatelywhenthepagerendersanddisplaysthefollowingoutputintheconsolewindow:
FacadepatternThefaçadepatternisusedtosimplifyvariousinterfacesorsubsystemsintooneunifiedinterface.Itsimplifiesthingstotheuserandratherthanunderstandingthecomplexitiesofdifferentsubsystems,theusercancallthefaçadeinterfacetoperformaspecificoperation.
Let'sseethefollowingexample,whichhasthreemethodstoloadpermissions,theuserprofile,andtheuserchatwindowonsuccessfullogin.Ithasthreeinterfaces,andwithfaçadewecansimplifyittooneunifiedinterface.ThisallowstheusertocallUserFacadeonlyonceiftheloginissuccessfulanditwillloadthepermissions,userchat,anduserprofilefromoneinterface:
<script>
varPermission=function(){
this.loadPermission=function(userId){
//loaduserpermissionsbycallingserviceandpopulateHTMLelement
varrepo=newServiceRepository();
repo.loadUserPermissions(userId);
}
}
varProfile=function(){
this.loadUserProfile=function(userId){
//loaduserprofileandsetusernameandimageinHTMLpage
varrepo=newServiceRepository();
repo.loadUserProfile(userId);
}
}
varChat=function(){
this.loginUserChat=function(userId){
//LoginuserchatandupdateHTMLelement
varrepo=newServiceRepository();
repo.loadUserChat(userId);
}
}
varUserFacade=function(){
this.loadUser=function(userId){
varuserPermission=newPermission();
varuserProfile=newProfile();
varuserChat=newChat();
userPermission.loadPermission(userId);
userProfile.loadUserProfile(userId);
userChat.loginUserChat(userId);
}
}
varloginUser=(function(username,password){
//Servicetologinuser
varrepo=newServiceRepository();
//Onsuccessfulllogin,useridisreturned
varuserId=repo.login(username,password);
varuserFacade=newUserFacade();
userFacade.loadUser(userId);
})();
<
BridgepatternThebridgepatternisusedtodecoupletheabstractionfromitsimplementationandmakeconcreteimplementationindependentfromtheinterface.Thisisachievedbyprovidingabridgebetweentheinterfaceandtheconcreteimplementer:
Thefollowingcodeshowsthebridgepatternimplementation:
<script>
varInvitation=function(email){
this.email=email;
this.sendInvite=function(){
this.email.sendMessage();
}
}
varReminder=function(sms){
this.sms=sms;
this.sendReminder=function(){
this.sms.sendMessage();
}
}
varSMS=function(){
//sendSMS
this.sendMessage=function(){console.log("SMSsent");}
}
varEmail=function(){
//sendemail
this.sendMessage=function(){console.log("Emailsent");}
}
varexecute=(function(){
varemail=newEmail();
varsms=newSMS();
varinvitation=newInvitation(email);
varreminder=newReminder(sms);
invitation.sendInvite();
reminder.sendReminder();
})();
</script>
Theobjectiveoftheprecedingexampleistoseparatethenotificationtypes(InvitationandReminder)fromthenotificationgateway(EmailandSMS).Soanynotificationcanbesentthroughanygatewayandcanhandleanynotificationthatseparatesthegatewaytothenotificationtypesanddisabletheboundingwiththetypeofthenotification.
BehavioralpatternBehavioralpatternsareusedtodelegateresponsibilitiesbetweenobjects.Inthefollowingsection,wewilldiscussallfourbehavioralpatternsmentionedpreviously,andhowtoimplementtheminJavaScript.
ChainofresponsibilitypatternThechainofresponsibilitypatternprovidesachainofobjectsexecutedinorderastheyarechainedtofulfillanyrequest.AgoodexampleforASP.NETdevelopersistheOWINpipelinethatchainsthecomponentsorOWINmiddlewaretogetherandisbasedontheappropriaterequesthandlerbeingexecuted:
Let'slookintoaverybasicexamplethatexecutesthechainofobjectsanddisplaysthetablefor2,3and4:
<script>
//Maincomponent
varHandler=function(table){
this.table=table;
this.nextHandler=null;
}
//Prototypetochainobjects
Handler.prototype={
generate:function(count){
for(i=1;i<=count;i++){
console.log(this.table+"X"+i+"="+(this.table*i));
}
//Ifthenexthandlerisavailableexecuteit
if(this.nextHandler!=null)
this.nextHandler.generate(count);
},
//Usedtosetnexthandlerinthepipeline
setNextHandler:function(handler){
this.nextHandler=handler;
}
}
//functionexecutedonPageload
varexecute=(function(){
//initializingobjects
varhandler1=newHandler(2),
handler2=newHandler(3),
handler3=newHandler(4);
//chainingobjects
handler1.setNextHandler(handler2);
handler2.setNextHandler(handler3);
//callingfirsthandlerorthecomponentinthepipeline
handler1.generate(10);
})();
<script>
Let'sgetintoamorepracticalexamplewhichtakestheamountandchecksthebudgetownerintheobjectschainedinthepipeline.ThefollowingcodehaveamainhandlerobjectthattakesthebudgetamountandthebudgetownertosetthebudgetamountforLineManager,HeadofDepartment,CTO,andCEO.Finally,wecansetthemainentrypointinthechainbycallingthehandler1method,whichfirstcheckswhethertheamountisunderthelinemanager'sbudget,thentheheadofdepartment's,thentheCTO's,andfinallytheCEO's:
<script>
//Maincomponent
varHandler=function(budget,budgetOwner){
this.budget=budget;
this.budgetOwner=budgetOwner;
this.nextHandler=null;
}
//Prototypetochainobjects
Handler.prototype={
checkBudget:function(amount){
varbudgetFound=false;
if(amount<=this.budget){
console.log("Amountisunder"+this.budgetOwner+"level");
budgetFound=true;
}
//Ifthenexthandlerisavailableandbudgetisnotfound
if(this.nextHandler!=null&&!budgetFound)
this.nextHandler.checkBudget(amount);
},
//Usedtosetnexthandlerinthepopeline
setNextHandler:function(handler){
this.nextHandler=handler;
}
}
//funcitonexecutedonPageload
varexecute=(function(){
//initializingobjects
varhandler1=newHandler(10000,"LineManager"),
handler2=newHandler(50000,"HeadofDepartment"),
handler3=newHandler(100000,"CTO"),
handler4=newHandler(1000000,"CEO");
//chainingobjects
handler1.setNextHandler(handler2);
handler2.setNextHandler(handler3);
handler3.setNextHandler(handler4);
//callingfirsthandlerorthecomponentinthepipeline
handler1.checkBudget(20000);
})();
</script>
Andthefollowingistheoutput:
ObserverpatternTheobserverpatterniswidelyusedtoimplementapublisher/subscribermodelinwhichifastateofanyobjectchanges,itnotifiesalltheobserverssubscribed.Observerpatternshavethreemethods,namelytoadd,remove,andnotifyobservers:
Thefollowingisthecodesnippetthatimplementstheobserverpattern:
<script>
//SamplefunctiontoconverttexttoFrenchlanguage
functiontranslateTextToFrench(value){
//callsomeservicetoconverttexttoFrenchlanguage
returnvalue;
}
//SamplefunctiontoconverttexttoArabiclanguage
functiontranslateTextToArabic(value){
//calsomeservicetoconverttexttoArabiclanguage
returnvalue;
}
//HelperfunctionusedbytheObserverimplementors
varHelperFunction=function(type){
vartxtEntered=document.getElementById("txtEntered");
varenglishText=document.getElementById("englishText");
varfrenchText=document.getElementById("frenchText");
vararabicText=document.getElementById("arabicText");
if(type=="english"){
englishText.innerText=txtEntered.value;
}elseif(type=="french"){
frenchText.innerText=translateTextToFrench(txtEntered.value);
}elseif(type=="arabic"){
arabicText.innerText=translateTextToArabic(txtEntered.value);
}
}
varEnglishTranslator={
update:function(){
//CallhelperfunctiontochangetexttoEnglish
HelperFunction("english");
}
}
varFrenchTranslator={
update:function(){
//CallhelperfunctiontochangetexttoFrench
HelperFunction("french");
}
}
varArabicTranslator={
update:function(){
//CallhelperfunctiontochangetexttoArabic
HelperFunction("arabic");
}
}
//Observerfunctionthatcontainsthelistofobserverhandlers
functionObserver(){
this.observers=[];
}
//toaddobserver
Observer.prototype.addObserver=function(object){
console.log('addedobserver:'+object);
this.observers.push(object);
};
//toremoveobserver
Observer.prototype.removeObserver=function(object){
console.log("removingobserver");
for(i=0;i<this.observers.length;i++){
if(this.observers[i]==object){
this.observers.splice(object);
returntrue;
}
}
returnfalse;
};
//Tonotifyallobserversandcalltheirupdatemethod
Observer.prototype.notify=function(){
for(i=0;i<this.observers.length;i++){
this.observers[i].update();
}
}
//Addingobjectsasobserversthatimplementstheupdatemethod
varobserver=newObserver();
observer.addObserver(EnglishTranslator);
observer.addObserver(FrenchTranslator);
observer.addObserver(ArabicTranslator);
//Executewillbecalledonbuttonclicktonotifyobservers
varexecute=function(){
observer.notify();
};
</script>
<body>
<div>
Specifysometext:<inputtype="text"id="txtEntered"/>
<inputtype="button"onclick="execute()"value="Notify"/>
</div>
<div>
<spanid="englishText"></span>
<spanid="frenchText"></span>
<spanid="arabicText"></span>
</div>
</body>
Intheaboveexample,wehavetakenascenariothattranslatesthetextforallthelanguagesaddedasobserverobjects.WeextendedtheObserverobjectanddefinedthreemethods,namelyaddObserver(),removeObserver(),andnotify()throughaprototype.Addingmethodsthroughaprototypeconsumeslessmemoryandeachmethodissharedamongallinstances.Thesemethodsarecreatedonceandtheninheritedbyeachinstance.Ontheotherhand,methodsthataredefinedinsidetheconstructorfunctionarecreatedeverytimethenewinstanceiscreatedandconsumemorememory.
TheaddObserver()methodisusedtoaddanyobjectinanobserverlist,removeObserver()isusedtoremoveaspecificobjectfromobserverlist,andnotify()executestheobserver'supdate()method.
EnglishTranslator,FrenchTranslatorandArabicTranslationareobjectsthathaveimplementedtheupdate()methodwhichiscalledwhennotify()isexecuted.Onpageloadwehaveregisteredallthetranslatorobjectsasobserversandprovidedatextboxwithabuttonthroughwhichtheusercantypeanytextonthetextboxandonabuttonclickeventitwillcalltheobserver'snotify()methodthateventuallycallstheregisteredobserver'supdate()method.
Pub/subpatternThepub/subpatternisanalternativepatterntotheobserverpatternwithaslightdifferenceinitsimplementation.Intheobserverpattern,theObserverobjectcaninvokethenotify()methodofalltheobservers,whereasinthepublisher/subscriberpattern,thereisacentralizedeventsystemwhichisusedtopublisheventstothesubscribers.Inthispattern,publishersandsubscribersarelooselytiedandneitherthepublishernorthesubscriberknowtowhomthemessagewassentorreceived.
Thefollowingexampleimplementsapub/subpatternthatholdsatwo-dimensionalarray.Theusercanaddeventsbasedonaneventnameandacallbackfunction.Ithasthreemethods:subscribe()tosubscribetoeventsinaneventsarray,unsubscribe()toremoveeventsfromanarray,andpublish()tocallthecallbackfunctionsforaspecificeventname:
Thefollowingisacodesnippettoimplementthepub/subpatterninJavaScript:
varPubSub=function(){
this.events=[];
this.subscribe=function(eventName,func){
this.events[eventName]=this.events[eventName]||[];
this.events[eventName].push(func);
};
this.unsubscribe=function(eventName,func){
if(this.events[eventName]){
for(i=0;i<this.events[eventName].length;i++){
if(this.events[eventName][i]===func){
this.events[eventName].splice(i,1);
break;
}
}
}
};
this.publish=function(eventName,data){
console.log(data);
if(this.events[eventName]){
this.events[eventName].forEach(function(event){
event(data);
})
}
};
};
varexecute=(function(){
varpubSub=newPubSub();
pubSub.subscribe("myevent1",function(){
console.log("event1isoccurred");
});
pubSub.subscribe("myevent1",function(){
console.log("event1isoccurred");
});
pubSub.subscribe("myevent2",function(value){
console.log("event2isoccurred,valueis"+value);
});
pubSub.publish("myevent1",null);
pubSub.publish("myevent2","myeventtwo");
})();
Intheprecedingexample,wehaveonePubSubobjectthatprovidesthreemethodstosubscribe,unsubscribe,andpublishevents.TheSubscribe()methodisusedtosubscribeforanyeventandtakestwoparameters,theeventnameandfunction,andaddsthemtothearrayforaspecificeventname.Iftheeventnamedoesnotexistanewarraywillbeinitializedforthateventname,otherwisetheexistinginstancewillberetrievedtoaddtheitem.Theusercanregisterasmanyeventsastheywant,bypassingtheeventnameandtheanonymousfunctionbodythatwillbeexecutedwhentheeventispublished.Topublishevents,thepublish()methodcanbecalledthattakestheeventnameandthedatayouwanttopasstothecorrespondingfunction,whichhasbeenexecuted.
PromisesPromisesareoneofthemostpopularpatternsextensivelyusedinJavaScriptAPIsandframeworkstomakeasynchronouscallssimpler.AnasynchronousoperationinJavaScriptneedstohaveacallbackfunctionregister,whichinvokeswhenthevalueisreturned.Withpromises,whenyoumakeanyasynchronouscall,itimmediatelyreturnsapromiseandprovidesobjectssuchasthenanddonetodefineafunctionwhentheresultantvalueisresolved.Intherealworld,promisesarejustlikeatokenorareceiptforthefoodyouorderinafastfoodrestaurant,andthatreceiptguaranteesyoutohavethefooddeliveredwhenitisready.Promisesaretokensthatconfirmyougetaresponsetoaspecificrequest:
InJavaScript,promisesarewidelyusedbyAPIsandframeworkssuchasAngularJS,Angular2,andmore.Let'shavealookatthefollowingexamplethatimplementsthepromisepattern:
//DefiningPromisethattakesafunctionasaparameter.
varPromise=function(func){
//Declaredmembervariable
varcallbackFn=null;
//Exposedonefunctionthatcanbeinvokedbytheobjectreturning
promise
//done()functiontakesacallbackfunctionwhichcanbedefinewhen
usingdonemethod.
this.done=function(callback){
callbackFn=callback;
};
functionresolve(value){
setTimeout(function(){
callbackFn(value)
},3000)
}
//Hereweareactuallyexecutingthefunctiondefinedwheninitializing
thepromisebelow.
func(resolve);
}
//Objectthatisusedtoorderfoodandreturnsapromise
varorderFood=function(food){
//returnsthePromiseinstanceandpassanonymousfunctionthatcall
resolvemethodwhichactuallyservetherequestafterdelaying3seconds.
returnnewPromise(function(resolve){
resolve(food);
});
}
//InitializedorderFoodthatreturnspromise
varorder=neworderFood("GrilledBurger");
//Callingdonemethodwhichwillbeinvokedoncetheorderisready
order.done(function(value){
console.log(value);
});
Intheprecedingexample,wehavedevelopedaPromisefunctionthatcontainsdoneandresolvemethods,wheredoneisusedtoinvokethecallbackfunctionimplementedbytheconsumerobjectandresolveiscalledinternallytoexecutetheactualtask.ThesecondfunctionisorderFood,whichreturnsapromiseobjectandcallstheresolvemethodtoactuallyrunthetaskwhenorderFoodisinitializedbytheconsumer.Thefollowingsnapshotshowsthestepsofhowthiscodeisexecuted:
Ifyouhavenoticed,intheprecedingexamplewehaveusedthesetTimeout()functiontodelaytheresponsefor3seconds.WehavetousesetTimeout()torepresentanasynchronousscenariowheresetTimout()andtheregisteringofthedonecallbackhandlerareexecutedinparallel.
Theprecedingcodesnippetisaverybasicimplementationofapromisepattern.However,thereareotherpartsofthepromisepatternthatwecanimplementtomakeitrobust.Promiseshavestatesandthefollowingisthemodifiedversionthatnotonlymaintainsthestatesforinprogress,done,andfailedbutalsoprovidesthefailedhandlertocatchexceptions.Thefollowingisthedescriptionofthestates:
Inprogress:Whentheresolvemethodiscalledthestatewillbesettoinprogress.Thisstatewillpersistuntilweregisterthehandlersfordoneandfailedscenarios.Done:Whendoneisinvoked,thestatuswillbesettodone.
Failed:Whenanyexceptionoccurs,thestatuswillbesettofail.
ThefollowingisthemodifiedversionoftheorderFoodexample:
//DefiningPromisethattakesafunctionasaparameter.
varPromise=function(func){
//Defaultstatuswhenthepromiseiscreated
varstatus='inprogress';
varerror=null;
//Declaredmembervariable
vardoneCallbackFn=null;
varfailedCallbackFn=null;
//Exposedonefunctionthatcanbeinvokedbytheobjectreturning
promise
this.done=function(callback){
//Assigntheargumentvaluetolocalvariable
doneCallbackFn=callback;
if(status==="done"){
doneCallbackFn(data);
}else{
doneCallbackFn(status);
}
//returnpromisetoregisterdoneorfailedmethodsinchain
returnthis;
};
//Exposefailedfunctiontocatcherrors
this.failed=function(callback){
if(status==="failed"){
failedCallbackFn(error);
}
//returnpromiseinstancetoregisterdoneorfailedmethodsin
chain
returnthis;
};
functionprepareFood(){
setTimeout(function(){
status="done";
console.log("foodisprepared");
if(doneCallbackFn){
doneCallbackFn(data);
}
},3000);
}
functionresolve(value){
try{
//setthevalue
data=value;
//checkifdoneCallbackFnisdefined
if(doneCallbackFn){
doneCallbackFn(value);
}
prepareFood();
}catch(error){
//setthestatustofailed
status="failed";
//settheexceptioninerror
error=error;
//checkiffailedCallbackFnisdefined
if(failedCallbackFn){
failedCallbackFn(value);
}
}
}
//Hereweareactuallyexecutingthefunctiondefinedwheninitializing
thepromisebelow.
func(resolve);
}
//Objectthatisusedtoorderfoodandreturnsapromise
varorderFood=function(food){
//returnsthePromiseinstanceandpassanonymousfunctionthatcall
resolvemethodwhich
//actuallyservetherequestafterdelaying3seconds.
returnnewPromise(function(resolve){
resolve(food);
});
}
//InitializedorderFoodthatreturnspromise
varorder=neworderFood("GrilledBurger").done(function(value){
console.log(value);}).failed(function(error){console.log(error);})
SummaryInthischapter,wehavelearnedtheimportanceofdesignpatternsinsmall-to-largescaleapplicationsandhowwecanusethemeffectivelytoresolvespecificproblems.Wehavecoveredfourtypesofdesignpatternsforeachcategory,suchaswhencreatingobjects,structuringobjects,andaddingabehavioralchangeorstatestoobjects.Therearevariousmoredesignpatternsavailableanddocumented,whichcanbereferredtohere:http://www.dofactory.com/javascript/design-patterns.
Inthenextchapter,wewilllearnaboutNode.jsthatrunsJavaScriptontheserverside.WewillseehowwebapplicationscanbedevelopedinNode.jsusingVisualStudio2015andexploresomepopularframeworksandviewtheenginesitprovides.
Chapter8.Node.jsforASP.NETDevelopersJavaScripthasbecomeoneofthemostprevalentlanguagesthatnotonlyrunsontheclientside,butalsorunsontheserversideaswell.Node.jsempowersJavaScripttorunontheserversideandprovidenon-blockingI/O,aneventdrivenmodelthatmakesitmorelightweight,scalableandefficient.Today,itismorewidelyusedinperformingreal-timeoperations,developingbusinessapplications,databaseoperations,andmore.JavaScriptonNode.jscanrelatetoASP.NETthatrunsonIISoranyotherwebserver.
IntroductiontoNode.jsNode.jsisapowerfulplatformtobuildserver-sideapplicationsusingJavaScript.Node.jsitselfisnotwritteninJavaScriptbutprovidesaruntimeenvironmenttorunJavaScriptcode.ItallowsJavaScriptcodethatrunsontheserverside,providingtheruntimebuiltontheGoogleV8JavaScriptengine,whichisanopensourceJavaScriptenginewritteninC++,andusedbyGoogleChrome,tocompileJavaScriptcodeintomachinecode,atthetimeofexecutingthroughtheV8JITcompiler.
Node.jsworksonasinglethread;unlikeotherserver-sidetechnologiesthatcreateaseparatethreadforeachrequest,Node.jsusestheeventcallbacksystemthatprocessestherequestusingasinglethread.Ifmultiplerequestsarrivetheyhavetowaituntilthethreadbecomesavailableandthenacquireit.Inthecaseoferrors,Node.jsdoesnotthrowanerrorandthisisanessentialtechniquetoavoiderrorbubblingandtheabortionofthesinglethread.Ifanyerrorariseswhileservingarequest,Node.jssendstheerrorlog,inthecallbackparameters,intheresponseitself.Thisallowsthemainthreadtopropagatetheerroranddelaytheresponse.Node.jsisgoodforwritingnetworkapplications.ItconsistsofHTTPrequests,othernetworkcommunicationstasks,andreal-timeclient/servercommunicationsusingwebsockets.
RequestprocessingbytheNode.jswebserverTheNode.jswebservermaintainsalimitedthreadpooltohandleclientrequests.Whentherequestgetstotheserver,theNode.jswebserverplacesthatrequestintoaneventqueue.Therequestisthenpickedupbytheeventloopcomponentthatworksinaninfiniteloopandprocessestherequestwhenitisfree.Thiseventloopcomponentissingle-threaded,andiftherequestinvolvesI/Oblockingoperationssuchasfilesystemaccess,databaseaccess,orothers,itcheckstheavailabilityofthethreadintheinternalthreadpoolandassignstherequesttotheavailablethread.Otherwise,itprocessestherequestandsendstheresponsebacktotheclientinasinglego.WhentheI/Oblockingrequestiscompletedbytheinternalthread,itsendstheresponsebacktotheeventloopfirst,whichsendstheresponsebacktotheclient.
ComparisonofNode.jswith.NETBothASP.NETandNode.jsareserver-sidetechnologies.ThefollowingdiagramshowsthecomparisonofNode.jswith.NET:
NPMNodePackageManager(NPM)istheNode.jspackagemanagerusedtoinstallNodemodules.Node.jsprovidesawaytowritemodulesinJavaScript,andwithNPMwecanaddandreusethosemodulesinotherapplications.WithASP.NETCore,wealreadyusesomemodules,suchasGulpandGruntforminifyingtheCSSandJavaScriptfiles,anddoingcopyingandmergingoperations.Thepackage.jsonfileistheconfigurationfilethatholdsthemetadatainformationabouttheapplicationandNodemodulesusedinourproject.Hereisthesamplescreenshotofthepackage.jsonfile:
Dependenciescanbeinstalledbyexecutingthefollowingcommand:
npminstallNAME_OF_THE_PACKAGE–save
Example:
npminstallgulp–save
--saveisusedtoupdatethepackage.jsondependenciessectionandaddthepackagesdownloaded.
InstallingNode.jsVisualStudioprovidesgreatsupportfordevelopingprogramsusingNode.js.ToconfiguretheNode.jsdevelopmentenvironmentontheWindowsplatform,downloadandinstallNode.jsfromhttp://nodejs.org.Therearevariousinstallersavailableaspertheplatform,asshowninthefollowingscreenshot:
ForWindows,wewilldownloadthe64-bitWindowsinstallerthatdownloadsthe.msipackageandtakeyouthroughsomesimplewizardscreens.YouwillnoticethattheNode.jsinstallercontainsaruntimetorunnodeprogramsandNPMtoreferenceotherNodemodulesinyourprogram.Thiscanbeseeninthefollowingscreenshot:
Commandssuchasnpmandnodearealreadyaddedintheenvironmentpathandwecanexecutethesecommandsdirectlyfromthecommandprompt.Therefore,ifweopenthecommandpromptandwritenode,itwillgiveyoutheNodeprompt,whichallowsyoutowriteJavaScriptcodeontheflyandexecute,asshowninthefollowingscreenshot:
Alternatively,wecanalsorunthe.jsfilebycallingnodejavascriptfile.js.
Thefollowingisthesampleexample1.jsfilethatsumsthenumbersdefinedinanarray:
console.log("NodeJsexample");
varnumbers=[100,20,29,96,55];
varsum=0;
for(i=0;i<numbers.length;i++)
{
sum+=numbers[i];
}
console.log("totalsumis"+sum);
Thefollowingistheoutput:
UsingNode.jswithVisualStudio2015TherearemanyIntegratedDevelopmentEnvironments(IDEs)availableinthemarketthathaveNode.jstoolingsupport.IDEssuchasVisualStudioCode,Sublime,KomodoandNodeEclipsearepopularIDEstoworkwithNode.js,butinpractice,most.NETdevelopersaremorecomfortableandfamiliarworkingwiththeVisualStudioIDE.Therefore,wewillbeusingtheVisualStudio2015Communityeditioninthischapter.
Node.jstemplatescanbeinstalledinVisualStudio2015byinstallingitsextensions.ExtensionscanbeinstalledfromtheVisualStudiomenuoptionTools|ExtensionsandUpdates:
ThisextensionofNode.jsisinstalledwithvarioustemplatestostartdevelopingapplicationsusingNode.js.ThereisatemplatetodevelopconsoleapplicationsusingtheblankNode.jsconsoleapplicationtemplate,awebapplicationusingNode.jsexpresstemplates,andsoon:
Thebasicadvantageofusingthesetemplatesistosavetimeinconfiguringthingsmanually,andthesetemplatesfacilitatedevelopersbyprovidingthebasicprojectstructuretokick-starttheNode.jsapplicationrightaway.
Let'sstartbycreatingabasicconsoleapplicationtemplate.Thebasicconsoleapplicationhasannpmfolder,containingnodepackages,package.jsonthatcontainsthemetadatainformationandotherconfigurationattributes,andapp.js,whichcontainsactualJavaScriptcode:
ThisextensionforNode.jsprovidesahandyfeatureforaddingNodemodulesbysimplyright-clickingonthenpmfolderandselectingtheInstallNewnpmPackagesoption,asshowninthefollowingscreenshot:
Onselectingthisoption,VisualStudioopensupthewindowthathelpstosearchanynodepackageandaddittoyourapplicationwithafewclicks:
TheprecedingdiagramshowstheversionsofGulppackagesthatcanbeaddedthroughthisoption.
InteractiveWindowisanothernicefeatureinVisualStudio,whichopensupthecommandpromptintegratedintheVisualStudiotab,andyoucanwriteJavaScriptcodeandexecutecommandsinstantly,asshowninthefollowingscreenshot:
ThereareseveralotherbenefitsofusingVisualStudio:youcanusetheGitorTFSversionrepositories,debugyourcodeandenablebreakpointsonyourJavaScriptfiles,andsoon.TheVisualStudio-specificprojectfileforNode.jsisknownas.njsprojandresidesintherootfolderofyourproject.
SimpleconsoleapplicationusingNode.jsANode.jsapplicationconsistsofoneormoreJavaScriptfilesthatprovidespecificfunctionalitytotheapplication.WritingthousandsoflinesofcodeinoneJavaScriptfileisnotpracticallypossible,andalsoincreasesmaintainabilityissues.InNode.js,wecancreatemultipleJavaScriptfilesandusethemthroughrequireandexportobjects,whicharepartsoftheCommonJSmodulesystem:
export:usedtoexportvariables,functionsandobjects
//exportexample.js
module.exports.greeting="HelloWorld";
require:TousetheobjectsresidesindifferentJavaScriptfilesusingrequire
object.
//consumerexample.js–referencingthroughfile
varobj=require('./exportexample.js');
Alternativelywecanalsocallrequirewithoutspecifyingthe.jsfileextension,anditautomaticallyloadsthefilethatexistsonaparticularpath.Ifthepathcorrespondstoafolder,alltheJavaScriptfileswillbeloaded:
//consumerexample.js–referencingthroughfile
varobj=require('./exportexample');
Themainentrypointwhentheapplicationisbootstrappedisdefinedunderpackage.json.Inthefollowingscreenshot,app.jsisthemainentrypointfilethatisloadedfirstandexecutedbyNode.js:
Let'simplementabasicexamplethathastwofiles,namelyapp.js(mainentry)andcars.js,andreturnsafewpropertiesofthecarobject,suchasname,model,andengine.Tostart,createaconsoleapplicationprojectandaddonecars.jsfile.
Hereisthecodeforcars.js:
module.exports.cars=[
{name:"HondaAccord",model:"2016",engine:"V6"},
{name:"BMWX6",model:"2015",engine:"V8"},
{name:"MercedezBenz",model:"2016",engine:"V12"}
];
Throughmodule.exports,wecanexportanyobject.Whetherit'savariable,afunction,oraJSONobject,itcanbeexportedthroughthis.Furthermore,theobjectsexportedcanbeusedthroughtherequireobjectinapp.js,asshowninthefollowingcode:
varcars=require('./cars.js');
console.log(cars);
Thefollowingistheoutput:
TheprecedingcodedisplaystheJSONoutputasdefinedinthecars.jsfile.Inordertoinitializethecarsobject,andloopthroughthecaritemsdefinedinthelist,weneedtoexportitasafunctionanddefineitthroughthethiskeyword.Specifyingitthroughthiswillmakethelistaccessiblefromthecarsobjectwecreateintheapp.jsfile.
Hereisthemodifiedversionofcars.js:
module.exports=function(){
this.carsList=
[
{name:"HondaAccord",model:"2016",engine:"V6"},
{name:"BMWX6",model:"2015",engine:"V8"},
{name:"MercedezBenz",model:"2016",engine:"V12"}
];
};
Andhereisthemodifiedversionoftheapp.jsfilethatinitializedthecarsobjectandloopsthroughthelist:
varcars=require('./cars.js');
varc=newcars();
varcarsList=c.carsList;
for(i=0;i<carsList.length;i++){
console.log(carsList[i].name);
}
WebapplicationswithNode.jsTherearevariousNode.jswebframeworksavailable.FrameworkssuchasExpressandHapi.jsarepowerfulframeworksandhavedifferentarchitecturesanddesigns.Inthissection,wewillusetheExpressframework,whichisoneofthemostwidelyusedwebframeworksforNode.js,forbothwebandmobileapplications,andalsoprovidestheapplicationframeworkmodeltodevelopwebApplicationProgrammingInterfaces(APIs).
CreatingblankNode.jsapplicationsAnextensionofNode.jsforVisualStudioprovidesvarioustemplatestodevelopwebapplications.WewillstartbycreatingablankNode.jswebapplication,asshowninthefollowingscreenshot:
AblankNode.jsapplicationtemplatecreatesoneserver.jsfileandsetsthemainattributeinthepackage.jsonfiletoloadserver.js.Thecontentofserver.jsisasfollows:
ThefirststatementaddsthedependencyofanhttpmoduleofNode.js,thesecondstatementistheportnumberatwhichtheserverwilllistenforHTTPrequests,andthethirdstatementcreatestheserverusinganhttpobject,onceitisstartedandreturnstheresponseasHelloWorld.Whentheserverstarts,arequestcanbemadebycallinghttp://localhost:1337.
Theprecedingcodesnippetchainsthelisten()methodthatactuallylistensfortheincomingrequests,andsendstheresponseusingtheres.end()method.Alternatively,wecanalsospecifythecontentwearereturningusingtheres.write()method.Hereisthemoresimplifiedversionofthesamecode,tounderstandhowthepiecesfittogether:
//Initializedhttpobject
varhttp=require('http');
//declaredport
varport=process.env.port||1337;
//Initializedhttpserverobjectanduseres.write()tosendactualresponse
content
varhttpServer=http.createServer(function(req,res){
res.writeHead(200,{'Content-Type':'text/plain'});
res.write('HelloWorld\n');
res.end();
});
//listeningforincomingrequest
httpServer.listen(port);
UsingtheExpressframeworkforwebapplicationsinNode.jsInanyprogramminglanguage,aframeworkhasanimportantbenefitthatminimizestheeffortrequiredtodevelopwebapplications.Theframeworkplaystheimportantroleofprocessingrequests,suchasloadingaspecificview,injectingmodelsintoaview,andmore.AswithASP.NET,wherewhavetwoweb-applicationframeworks,ASP.NETWebFormsandASP.NETMVC,Node.jsprovidesExpressEJS,Jade,andmanyotherwebapplicationframeworkstobuildrobustwebapplications.
ExtendsimpleNode.jstouseExpress
WiththeNode.jsextensionforVisualStudio,youcangetallthetemplatestostartworkingwiththeExpress3.0andExpress4.0applicationframeworks.Express4.0isthemostrecentversionandhassomenewfeaturesandimprovements.Wecanusethetemplatethatbootstrapsmostoftheconfiguration-levelstuffforyoubut,inordertogetmoreclarity,wewillextendthesimpleNode.jsexamplecreatedpreviously,andusetheExpressframeworktodevelopasimplewebapplicationontopofit.
TouseExpress,wehavetoadditspackagedependencyusingNPM,asshowninthefollowingscreenshot:
OnceyouaddtheExpresspackage,youcanaddthefollowingcodesnippettokickstarttheExpressapp:
//Initializedhttpobject
varhttp=require('http');
//addingexpressdependency
varexpress=require('express');
//creatingexpressapplication
varexpressApp=express();
//Configuringrootcallwhere'/'representsrootpathoftheURL
expressApp.get("/",function(req,res){
res.send("<html><body><div>HelloWorld</div></body></html>");
});
//declaredport
varport=process.env.port||1337;
//Initializedhttpserverobjectanduseres.write()tosendactualresponse
content
varhttpServer=http.createServer(expressApp);
//listeningforincomingrequest
httpServer.listen(port);
ThisisasimpleHelloWorldexamplethatreturnstheHTMLcontent.Now,inscenarioswherewewanttoreturnaspecificviewinsteadofthestaticHTMLcontent,weachievethisbyusingExpressviewengines,whichwillbediscussednext.
Expressviewengines
Expresshasvariousviewengines,althoughJadeandEJSarethemostwidelyused.Wewillgothroughtheseonebyoneandseewhatthedifferencesare.
EJSviewengine
IntheEJSviewengine,viewsareHTMLpagesandthemodelpropertiescanbeboundusingscriptlets:<%%>.
TostartusingEJS,weneedtoaddtheEJSpackagethroughtheNPMpackagemanageroptioninVisualStudio,orbyexecutingthenpminstallejs–savecommand:
Oncethisisadded,wecansettheviewenginetoejs,asshowninthefollowingcodesnippet:
//Initializedhttpobject
varhttp=require('http');
//addingexpressdependency
varexpress=require('express');
//creatingexpressapplication
varexpressApp=express();
//SetjadeforNode.jsapplication
expressApp.set('viewengine','ejs')
Setthepathoftheejsviewbycallingtheresponseobjectrender()method,asshowninthefollowingcode:
//Configuringrootcallwhere'/'representsrootpathoftheURL
expressApp.get("/",function(req,res){
res.render("ejsviews/home/index");
});
Addtheindex.ejsfileinsidethehomefolder.AlltheviewsshouldresideundertherootViewsfolder,otherwisetheywon'tbeloadedwhentheapplicationisrun.Therefore,theejsviewsfoldershouldbedefinedundertheViewsfolderandhomeinsidetheejsviewsfolder,asshowninthefollowingscreenshot:
ThefollowingisthecontentoftheEJSviewthatwillberenderedwhentheapplicationstarts:
<html>
<body>
<div><h1>ThisisEJSView</h1></div>
</body>
</html>
Addthecodeatthebottomoftheejsserver.jsfilethatcreatestheserverwhichlistensforrequestsonportnumber1337:
//declaredport
varport=process.env.port||1337;
//Initializedhttpserverobjectanduseres.write()tosendactualresponse
content
varhttpServer=http.createServer(expressApp);
//listeningforincomingrequest
httpServer.listen(port);
Whentheapplicationruns,index.ejswillbeloadedandwillrendertheHTMLcontentasshowninthefollowingfigure:
WecanalsopassthemodelintherepresentationoftheJSONobject.Supposeweneedtopasstheapplicationnameanddescription;wecanpassthesevalueswhencallingarender()
methodoftheresponseobject,asshowninthefollowingcode:
//Configuringrootcallwhere'/'representsrootpathoftheURL
expressApp.get("/",function(req,res){
res.render("ejsviews/home/index",{appName:"EJSDemo",message:"Thisisour
firstEJSviewengineexample!"});
});
Inindex.ejs,wecanuseandbindthesevalueswithHTMLcontrolsusingscriptlets:
<html>
<body>
<h1><%=appName%></h1>
<p><%=message%></p>
</body>
</html>
EJSalsosupportslayoutpagesthatcontainthestaticcontent,suchastheheaderandfooterofthewebapplication.So,adeveloperdoesn'tneedtodefinethemainlayoutcontentagainandagainoneverypage,andwecankeepitcentralized,justlikewedoinASP.NETMVCusing_layout.cshtmlandSite.masterinASP.NETwebforms.
Toworkwithmasterpages,weneedtoaddonemorepackage,knownasejs-local.ThispackagecanbeaddedusingtheNPMpackagemanagerwindowinVisualStudio,orbyrunningthenpmcommandasnpminstallejs-local--save:
Afteraddingthispackage,wecanaddejs-locals,asshowninthefollowingcode.Ithastobesetbeforesettingtheviewengine:
//Initializedhttpobject
varhttp=require('http');
//addingexpressdependency
varexpress=require('express');
varejsLocal=require('ejs-locals');
//creatingexpressapplication
varexpressApp=express();
//Addenginethatsupportsmasterpages
app.engine('ejs',ejsLocal);
Addthelayout.ejspageinthesameejsviewsfolderandspecifytheHTMLcontent:
<html>
<head>
<title><%=appName%></title>
</head>
<body>
<%=body%>
</body>
</html>
Thecontentofthechildpagewillberenderedonthelinewherethebodyisdefinedinthelayoutpage,asshownintheprecedingcode.Hereisthecodesnippetoftheindex.ejsfile:
<%layout('../layout.ejs')-%>
<h1><%=appName%></h1>
<p><%=message%></p>
Thefollowingoutputisgenerated:
Jadeviewengine
TheJadeviewengineisanotherNode.jsviewengineandthesyntaxisquitedifferent,whendefiningviews,thanwehaveseeninEJS.TostartwiththeJadeviewengine,weneedtoinstalltheJadeviewenginenodepackageusingNPM.WecaninstallthisfromtheVisualStudioNPMpackagemanagerorbyrunningthenpminstalljade–savecommand:
Whenyouinstallit,itwilladdtheJadepackageinthepackage.jsondependenciessection.WewillstartbysettingtheJadeviewengineintheapp.jsfile(themainentrypointtokickstarttheNode.jsproject).
HereisthecodetosetuptheJadeviewengineinapp.js:
//addingexpressdependency
varexpress=require('express');
//creatingexpressapplication
varexpressApp=express();
//SetjadeforNode.jsapplication
expressApp.set('viewengine','jade');
YouwillnoticethatwehavenotspecifiedtheJadereferencethroughtherequireobject.ThisisbecausewhentheExpressframeworkisloaded,itwillautomaticallyregisterthedependenciesofJade.ThefollowingcodesnippetloadstheJadeview:
//Configuringrootcallwhere'/'representsrootpathoftheURL
expressApp.get("/",function(req,res){
res.render("home/index",
{
appName:"JadeDemo",
message:"ThisisourfirstJadeviewengineexample!"
}
);
});
TheJadeviewsyntaxistypicallydifferentfromHTMLandalltheviewextensionsshouldbe.jade.Intheprecedingcode,wearepointingtoindex.jade,whereJadeisnotrequiredtobespecifiedexplicitly.Index.jadeshouldresideundertheviews/homefolder.Let'screateafoldercalledviewsandthenthehomefolderinsideit.AddanewJadefileandnameditindex.jade.HereisthecodethatdisplaystheappNameandmessageinHTMLelements:
doctype
html
body
h1=appName
p=message
WithJadesyntax,youdon'thavetodefinethecompleteHTMLtags,yousimplyspecifyitthroughtheirnames,followedbythevalueassignedtothem.Forexample,intheprecedingexample,wearesettingtheappNameandmessagevaluespassedasaJSONobjecttothisviewthroughtheresponserender()method.However,therearemanymoreattributesthatHTMLelementssupport,suchassettingcontrolwidth,fontcolor,fontstyle,andsoon.Inalatersection,wewillseehowwecanachievethisinJade.
Theequalto(=)operatorisonlyrequiredifyouarebindingtoanyvalueinjectingintotheview.Ifyouwanttospecifyahardcodedstaticvalue,thenitcaneasilybesetwithoutusingtheequaltooperator,asshowninthefollowingcode:
doctype
html
body
h1JadeApp
pThisisJadeView
HereareafewexamplesofusingJadesyntaxforHTML-specificscenarios:
Attributes Jade HTML
Textboxinput(type='text'
name='txtName')<inputtype='text'name='txtName'/>
Anchortaga(href='microsoft.com')
Microsoft<ahref="microsoft.com">Microsoft</a>
Checkboxinput(type='checkbox',
checked)
<inputtype="checkbox"
checked="checked"/>
Anchorwithstyleattributes
a(style={color:'green',
background:'black'})
<a
style="color:green;background:black">
</a>
Linkbuttoninput(type='button'
name='btn')<inputtype="button"name="btn"/>
YoucanlearnmoreabouttheJadelanguagehere:http://jade-lang.com/.
Jade'sframeworkalsosupportslayoutpages.Layoutpagesholdthestaticinformationofthewebsite,whichismostlyplacedintheheader,footer,orsidebars,andthecontentactuallychangesasperthepagerequested.InASP.Netwebforms,wedefinemasterpageswith<asp:ContentPlaceHolder>tags,whichrenderthecontentofthepagereferencetothatmasterpage.InASP.NETMVC,[email protected],wecandefinethecontentblockusingablockkeywordfollowedbythenameoftheblock.Forexample,thefollowingisthelayout.jadethatcontainstheblockcontentBlockstatement,whereblockrepresentswherethecontentofthechildpagerenders,andcontentBlockisthenameoftheblockthathastobedefinedinthechildpage.Multipleblockscanalsobedefinedinasingleview.
Thefollowingisthecontentofthelayoutpage:
doctypehtml
html
head
titleJadeApp
body
blockcontentBlock
Thelayoutpagecanbeusedwiththeextendskeywordfollowedbythelayoutpagename.TheJadeviewengineautomaticallysearchesthepagewiththatnameandifit'sfound,searchesfortheblocknameandplacesthecontentthere.Hereisthechildpageindex.jadethatusesthelayoutpagelayout.jade:
extendslayout
blockcontentBlock
h1=appName
p=message
Theoutputwillbeasfollows:
RoutingintheExpressapplication
WehavenowlearnedthebasicsoftheEJSandJadeviewengines.Bothoffersimilarfeatures,butthesyntaxisdifferent.Inthepreviousexamples,wesentaresponsethatpointstoaspecificpagewhichrendersthecontentontheclientside.
TheExpressframeworkprovidesseveralmethodsthatcorrespondtoHTTPmethods,suchasget,post,put,delete,andsoon.Wecanusethegetmethodtoretrievesomething,posttocreatearecord,puttoupdate,andsoon.PagescanresideanywherewithintheViewsfolder,buttheroutingactuallydefineswhichpagehastobeloadedwhentherequestismadeonaspecificURLpath.
Let'screateanEJSpageinsidetheViews/ejsviews/homefolderandnameitabout.ejs.
RoutingcanbedefinedusingtheExpressapplicationobject,asshowninthefollowingcode:
expressApp.get("/About",function(req,res){
res.render("ejsviews/home/about");
});
Whentheuserbrowsestohttp://localhost/About,itshowstheAboutpage.
MiddlewareNode.jsExpressalsoprovidesaspecialroutingmethod,all(),whichisnotmappedtoanyHTTPmethod.ButitisusedtoloadMiddlewareatapath,irrespectiveoftheHTTPmethodbeingrequested.Forexample,makingHTTPGETandPOSTrequestsathttp://localhost/middlewareexamplewillexecutethesameall()methodshowninthefollowingcode:
expressApp.all('/middlewareexample',function(req,res){
console.log('Accessingthesecret1section...');
});
Justlikein.NET,wehaveOWINmiddlewarethatcanbechainedintherequestpipeline.Inthesameway,Node.jsExpressmiddlewarecanalsobechainedandcanbeinvokedbycallingthenextmiddlewarewithalittlechangeinthefunctionsignature.Hereisthemodifiedversionthattakesthenextparameteraftertheresponseobjectwhichprovidesahandlertothenextmiddlewareinthepipeline,definedinasequenceforaparticularrequestpath:
expressApp.all('/middlewareexample',function(req,res,next){
console.log('Accessingthesecret1section...');
next();
});
Forexample,supposewehavetwomiddlewaresandthefirstmiddlewarejustlogstheinformationtotheconsolewindow,whereasthesecondreturnstheHTMLcontentbacktotheclient.Hereistheserver.jsfilethatcontainsthetwomiddlewaresintheEJSviewengine:
//Initializedhttpobject
varhttp=require('http');
//addingexpressdependency
varexpress=require('express');
//creatingexpressapplication
varexpressApp=express();
expressApp.all('/middlewareexample',function(req,res,next){
console.log('Middlewareexecutednowcallingnextmiddlewareinthe
pipeline');
next();//passcontroltothenexthandler
});
expressApp.all('/middlewareexample',function(req,res){
res.send("<html><body><div>Middlewareexecuted</div></body></html>");
});
//declaredport
varport=process.env.port||1337;
//Initializedhttpserverobjectanduseres.write()tosendactualresponse
content
varhttpServer=http.createServer(expressApp);
//listeningforincomingrequest
httpServer.listen(port);
NowwhenweaccesstheURLpathhttp://localhost/middlewareexample,themessagewillbeprintedontheconsoleandrenderstheHTMLcontentinthebrowser:
HereistheHTMLcontentthatwillrenderinthebrowser:
MVCwiththeExpressframeworkAlmosteveryapplicationconsistsofnumerouspages,anddefiningallthelogicandroutingonthemainserver.jsisnotpracticalormaintainable.Inthissection,wewillseehowtheModelViewController(MVC)patterncanbeimplementedwiththeExpressframework.Wewilldevelopasimpleapplicationtoseehowcontrollersanddataservicescanbedeveloped,andhowthecontrollerloadstheviewandinjectsthemodelusingtheExpressframework.
MVCpatternMVCisasoftwarearchitecturalpatternusedtoseparateanapplication'sconcerns.Themodelrepresentstheentitythatcontainspropertiestoholdinformation,whereasthecontrollerisusedtoinjectthemodelintotheviewandloadtheview.Thecontrollerisalsousedtostorethemodelinthedatabase,whereastheviewisthepagethatrendersthemodelinjectedbythecontrollerandusesitwhereverneeded.
Creatingacontroller
WewillstartbycreatingasimplehomeControllertorenderahomepage.Let'sextendtheEJSviewengineexampledevelopedabove,andcreateaControllersfolderattherootofyourproject.InsidetheControllersfolder,createaHomeController.jsfileandplacethefollowingcodesnippetthere:
(function(homeController){
homeController.load=function(expressApp){
expressApp.get('/',function(req,res){
res.render("ejsviews/home/index",{appName:"EJSApplication",
message:"EJSMVCImplementation"})
});
};
})(module.exports);
Intheprecedingcode,thereisananonymousJavaScriptfunctionthattakesthemodule.exportobjectandbindsittothehomeControllerwhenitisexecuted.ThebasicadvantageofimplementingitinthiswayisthateverymethodorpropertydefinedwiththehomeControllerobjectwillbeexportableandaccessiblebythecallingobject.Intheprecedingexample,wehavedefinedaload()methodthatdefinestheroutingattherootpath(/)andreturnstheIndexpagetotheclient.
Inthemainejsserver.jsfile,wecanusethecontrollerbyusingtherequireobjectasshowninthefollowingcode:
//Initializedhttpobject
varhttp=require('http');
//addingexpressdependency
varexpress=require('express');
//addingejslocals
varejsLocal=require('ejs-locals');
//creatingexpressapplication
varexpressApp=express();
//Addenginethatsupportsmasterpages
expressApp.engine('ejs',ejsLocal);
//SetjadeforNode.jsapplication
expressApp.set('viewengine','ejs');
//InitializingHomeController
varhomeController=require('./Controllers/HomeContoller.js');
homeController.load(expressApp);
//declaredport
varport=process.env.port||1337;
//Initializedhttpserverobjectanduseres.write()tosendactualresponse
content
varhttpServer=http.createServer(expressApp);
//listeningforincomingrequest
httpServer.listen(port);
Intheprecedingcode,wehaveaddedtheHomeControllerobjectusingtherequireobjectandcalledtheload()methodtodefinetheroutingthatnavigatestotheindexpagewhenthewebsiteruns.
Creatingdataservices
EverybusinessapplicationinvolveslotsofCRUD(create,read,update,anddelete)operations.Forabetterdesign,theseoperationscanbeseparatelyimplementedindataserviceobjects,soifmultiplecontrollerswantedtousethesameservice,theycanusethemwithoutwritingthesamecoderepeatedly.Inthissection,wewillcreateadataserviceJavaScriptfilethatreadsthedataandpassesitintheroutingfunction.Tostartwith,let'screateafoldernameDataServicesattherootoftheapplicationandcreateProductService.jsinsideit.HereisthecodeforProductService.js,whichreturnstheproductsarray:
(function(data){
data.getProducts=function(){
return[{
name:'Product1',
price:200,
},
{
name:'Product2',
price:500
},
{
name:'Product3',
price:1000
}
];
};
})(module.exports);
WecanusethisProductServiceinsideHomeControllerthroughtherequireobject:
(function(homeController){
varproductService=require('../DataServices/ProductService');
homeController.load=function(expressApp){
expressApp.get('/',function(req,res){
varproducts=productService.getProducts();
res.render("ejsviews/home/index",{appName:"EJSApplication",
message:"EJSMVCImplementation",data:products});
});
};
})(module.exports);
Andhereistheindex.ejsfile,whichloopsthroughtheproductsanddisplaystheproductnameandprice:
<%layout('../layout.ejs')-%>
<h1><%=appName%></h1>
<p><%=message%></p>
<div>
<%data.forEach(function(product){%>
<li><%=product.name%>-<%=product.price%></li>
<%});%>
</div>
Finally,theoutputlookslikethefollowing:
AccessingtheMicrosoftSQLserverinNode.jsNode.jsprovidesdifferentdatabasedriversthatcanbeaddedasnodepackages.TherearepackagesfortheMongoDBdriver,theMicrosoftSQLServerdriver,andmore.WewillusetheMSSQLdriverforNode.jstoconnectwithMicrosoftSQLserverdatabases.Toinstallmssqlyoucanrunthenpminstallmssql–savecommand,oradditfromtheNPMpackagemanagerwindow,asshowninthefollowingscreenshot:
Tip
WiththeMSSQLdriver,TCP/IPshouldbeenabledforacorrespondingSQLserverinstance.
ReadingarecordfromtheMicrosoftSQLserverdatabaseIntheDataService.jsfile,wewilladdthegetProducts()method,whichloadsthelistofproductsfromtheSQLServerdatabase.
ThefollowingisthegetProducts()method,whichacceptsthecallbackfunction,soassoontheproductslistisfetchedfromthedatabase,itwillbepassedinthecallbackfunctiononthecallerside:
(function(data){
data.getRecords=function(callbackFn){
//loadedSQLobject
varsql=require('mssql');
//databaseconfigurationattributestoconnect
varconfig={
user:'sa',
password:'123',
server:'ovais-pc',//Youcanuse'localhost\\instance'toconnect
tonamedinstance
database:'products'
}
varproducts=null;
//ConnecttoSQLServerreturnsapromiseandonsuccessfullconnection
executingaqueryusingRequestobject
sql.connect(config).then(function(){
newsql.Request().query('select*fromproducts',function(err,
recordset){
callbackFn(recordset);
});
});
};
})(module.exports);
Intheprecedingcode,weinitializedthesqlobjectusingtherequireobject.TheConfigvariablecontainstheconnectionattributes,suchasusername,password,server,anddatabase.Thisispassedwhilecallingthesqlconnect()method.TheConnect()methodreturnsathen()promise,throughwhichwecaninitiatetheSQLqueryrequestusingthesql.Request()method.Iftherequestissuccessful,wewillgettheresultsetintherecordsetobjectthatwillbereturnedtothecallerthroughitscallbackfunction.
HereisthemodifiedversionofHomeController.jsthatcallstheDataServicegetRecords()methodandpassestheproductslistretrievedasamodeltotheindexview:
(function(homeController){
varproductService=require('../DataServices/ProductService');
homeController.load=function(expressApp){
expressApp.get('/',function(req,res){
varproducts=productService.getRecords(function(products){
console.dir(products);
res.render("ejsviews/home/index",{appName:"EJSApplication",
message:"EJSMVCImplementation",data:products});
});
});
};
})(module.exports);
Thefollowingistheindex.jsfile,whichloopsthroughthelistofproductsanddisplaystheproductnameandprice:
<%layout('../layout.ejs')-%>
<h1><%=appName%></h1>
<p><%=message%></p>
<table>
<th>
<td>ProductName</td>
<td>Description</td>
<td>Price</td>
</th>
<%data.forEach(function(product){%>
<tr><td><%=product.Name%></td><td><%=product.Description%></td><td>
<%=product.Price%></td></tr>
<%});%>
</table>
CreatingarecordintheMicrosoftSQLserverdatabaseTocreatearecordinthedatabase,wecandefinetheHTMLinputelementswrappedwithintheHTMLformtag,andonsubmissionoftheform,wecanmakeapostrequestbydefiningapostmethodinourHomeController.jsfile.Whentheformissubmitted,thevaluescanberetrievedusingtherequest.bodyobject.ThisistheparserthatparsestheDOMandmakesalistofelementswrappedundertheformtag.Wecanaccessitlikereq.body.txtName,wheretxtNameistheHTMLinputelementandreqistherequestobject.
Express4.0unbundledthebody-parserobjectintoaseparatepackageandcanbedownloadedseparatelyusingthenpminstallbody-parser–savecommandorthroughtheNPMpackagemanagerwindow,asshowninthefollowingscreenshot:
Inyourmainejsserver.jsfile,addthebody-parserusingtherequireobjectandpassitintheexpressAppobjectbycallingtheexpressApp,use()method:
varbodyParser=require('body-parser');
expressApp.use(newbodyParser());
Oncethisisadded,wecanmodifyHomeController.jsanddefineaPOSTmethodthatwillbecalledoncetheformissubmitted:
expressApp.post('/',function(req,res){
console.log(req.body.txtName);
productService.saveProduct(req.body.txtName,req.body.txtDescription,
req.body.txtPrice,function(result){
res.send("Recordsavedsuccessfully");
});
});
TheprecedingmethodcallsthedataservicesaveProduct()methodandpassesthevaluesfilledbytheuserasparameters.Thelastparameteristhecallbackfunction,whichwillbeexecutediftherecordsavedsuccessfully.HereisthesaveProductcodesnippetoftheDataService.jsfile:
data.saveProduct=function(name,description,price,callbackFn){
//loadedSQLobject
varsql=require('mssql');
//databaseconfigurationattributestoconnect
varconfig={
user:'sa',
password:'123',
server:'ovais-pc',//Youcanuse'localhost\\instance'toconnect
tonamedinstance
database:'products'
}
//ConnecttoSQLServerreturnsapromiseandonsuccessfullconnection
executingaqueryusingRequestobject
sql.connect(config).then(function(){
newsql.Request().query("INSERTintoproducts(Name,Description,
Price)values('"+name+"','"+description+"',"+price+")",function(err,
recordset){
callbackFn(recordset);
});
});
};
Andfinally,hereistheIndex.ejsviewthatcontainsaformwiththeName,Description,andPricefields:
<formmethod="post">
<table>
<tr>
<td>ProductName:</td>
<td><inputtype='text'name='txtName'/></td>
</tr>
<tr>
<td>Description:</td>
<td><inputtype='text'name='txtDescription'/></td>
</tr>
<tr>
<td>Price:</td>
<td><inputtype='number'name='txtPrice'/></td>
</tr>
<tr>
<td> </td>
<td><inputtype="submit"value="Save"/></td>
</tr>
</table>
</form>
Tolearnmoreaboutthemssqlnodepackage,pleaseusethislink:https://www.npmjs.com/package/mssql.
SummaryThischapterfocusedonthebasicsofNode.jsandhowtousethemindevelopingserver-sideapplicationsusingJavaScript.Wehavelearnedabouttwoviewengines,EJSandJade,andhowtousethem.WehavealsolearnedtheusageofcontrollersandservicestoimplementanMVCpattern.Andfinally,wefinishedbylookingatexamplesofaccessingtheMicrosoftSQLserverdatabasetoperform,create,andretrieveoperationsonadatabase.Inthenextchapter,wewillfocusonbestpracticesforusingJavaScriptinlarge-scaleapplications.
Chapter9.UsingJavaScriptforLarge-ScaleProjectsLarge-scalewebapplicationprojectscompriseofseveralmodules.WithcontinuousimprovementsandadvancementsinthedevelopmentofvariousJavaScriptframeworks,developersuseJavaScriptfrequentlyinanapplication'spresentationorfrontendlayer,andserver-sideoperationsareonlyperformedwhenrequired.Forexample,whensavingorreadingthedatafromserverordoingsomeotherdatabaseorbackendoperations,anHTTPrequestismadetotheserverthatreturnstheplainJSONobjectandupdatestheDOMelements.Withthesedevelopmentsinplace,mostoftheapplicationfrontendcoderesidesontheclientside.However,whenJavaScriptwasfirstdeveloped,itwastargetedtobeusedfordoingsomebasicoperations,suchasupdatingtheDOMelementsorshowingconfirmdialogsandotherrelativeoperations.TheJavaScriptcodemostlyexistsonthepageitselfwithinthe<script>scriptingtag.However,large-scaleapplicationsconsistofmanylinesofcodeandneedproperattentionwhendesigningandarchitectingthefrontend.Inthischapter,wewilldiscussafewconceptsandbestpracticesthathelptomaketheapplicationfrontendmorescalableandmaintainable.
ThinkbeforeproceedingLarge-scaleapplicationsconsistofmanyJavaScriptfilesandproperstructuringofthesefilesbringsgreatervisibility.JavaScriptframeworkssuchasAngularJS,EmberJS,andothersalreadyprovideproperstructuringandguidelinestodefinecontrollers,factories,andotherobjects,aswellasprovidebestpracticesofusingthem.Theseframeworksareverypopularandalreadyadheretotheproblemofhigherscalabilityandmaintainability.However,therearecertainscenarioswherewewanttostrictlyrelyonplainJavaScriptfilesandmaydevelopourowncustomframeworktoremedyparticularrequirements.Toacknowledgethese,therearecertainbestindustry-widepracticesbeingused,whichmakeourJavaScript-basedfrontendmoremaintainableandscalable.
Whenworkingonlargescaleapplications,weneedtothinkaheadofwhatthescopeoftheapplicationisallabout.Weneedtothinkhoweasilyourapplicationcanbeextendedandhowquicklytheincorporationofothermodulesorfunctionalitycanbeachieved.Ifanymodulefails,woulditaffectthebehavioroftheapplicationorcrashothermodules?Forexample,ifweareusinganythird-partyJavaScriptlibrarythatmodifysomeoftheirmethod'ssignatures.Inthiscase,ifanythird-partylibraryisusedfrequentlyeverywhereinourapplication,wehavetomodifythemethodateachpoint,anditmaybeacumbersomeprocesstonotonlychange,butalsotest.Ontheotherhand,ifsomeFacadeorwrapperhasbeenimplemented,itwouldonlyrequireustochangeatoneplaceinsteadofupdatingiteverywhere.Therefore,designinganapplicationarchitectureorframeworkisathoughtfulprocess,butitmakestheapplicationmorerobustandhealthy.
DevelopinghighlyscalableandmaintainableapplicationsThefollowingarethefactorsthatweshouldconsidertomakehighlyscalableandmaintainablewebapplicationsthatrelyheavilyonJavaScript.
ModularizationWithbigapplications,writingeverythinginasingleJavaScriptfileisnotagoodpractice.Nonetheless,evenifyouhaveseparateJavaScriptfilesfordifferentmodulesandreferencingthemthroughthescripting<script>tagbloatstheglobalnamespace.AproperstructuringshouldbedonetokeepJavaScriptfilesintoseparatemodule-wisefolders.Forexample,anERPapplicationconsistsofseveralmodules.WecancreateseparatefoldersforeachmoduleandusespecificJavaScriptfilestoprovidecertainfunctionalityforspecificviewsorpages.However,thecommonfilescanresideinthecommonfolder.
HereisthesampleprojectstructuringtoarrangeJavaScriptfilesbasedonanERPmodule.Eachmodulehasaservicefolderthatcontainssomefilestodosomereadorwriteoperationsontheserverside,andtheViewsfoldertomanipulateDOMelementsofspecificviewsoncethedataisloadedoranycontroleventisinvoked.Thecommonfoldermaycontainallthehelperutilitiesandfunctionsthatareusedbyalltheothermodules.Forexample,tologmessagesonconsole,ormakeanHTTPrequestontheserverside,functionscanbedefinedinthecommonJavaScriptfilesandtheycanbeusedbytheservicesorviewJavaScriptfiles:
Intheprecedingstructure,theServicesfoldercanhavethefunctionsrelatedtoperformcreate,retrieve,update,anddelete(CRUD)operationsonthedatabasebycallingsomeWebAPIorwebservices,whereasaviewfilesuchasFIMain.jscontainspage-specific
function.
TomakeanHTMLpageclean,itisabetterapproachtokeepJavaScriptfilesseparatefromtheHTMLpage.Sointheprecedingscreenshot,FIMain.jscontainstheJavaScriptfunctioncorrespondingtothemainpage,whereasFIDashboard.jscontainstheJavaScriptfunctioncorrespondingtothedashboardpage,andsoon.
Thesefilescansimplybeaddedthroughthe<script>scriptingtag,butinJavaScriptworld,addingJavaScriptfilesdirectlyonthepageitselfisnotgoodpractice.ModulescanbedefinedinJavaScriptbyimplementingamodulepattern.However,mostlydeveloperspreferusingRequireJSAPItodefinemodulestomakemoduleloadingsimplerandprovideabetterscopingofvariablesandfunctionsdefinedinthismodule.ItisequivalenttotheCommonJSsystem,butitisrecommendedbecauseofanasynchronousbehavior.ItloadstheJavaScriptmodulesinanasynchronouswayandmakethepage-loadcyclefaster.
Implementingthemodulepattern
Amodulepatternisthemostprevailingdesignpatternusedforcreatingloose-coupledarchitectureandkeepingthepiecesofJavaScriptcodeindependentofothermodules.
Modulesarejustlike.NETclassesthatcanhaveprivate,protected,andpublicpropertiesandmethodsandprovidecontroltothedevelopertoexposeonlythosemethodsorpropertiesthatareneededbyotherclasses.
InJavaScript,amodulepatterncanbeimplementedusingimmediatelyinvokedfunctionexpression(IIFE)thatexecutesimmediatelyandreturnsaclosure.Closureactuallyhidesprivatevariablesandmethodsandreturnsanobjectthatcontainsonlythosemethodsorvariablesthatarepublicandareaccessiblebyothermodules.
HereistheLoggermodulethatexposedalogMessage()method,whichcallsoneprivateformatMessage()methodtoappenddateandreturnstheformattedmessagethatisthenprintedoutonabrowser'sConsolewindow:
<script>
varLogger=(function(){
//privatemethod
varformatMessage=function(message){
returnmessage+"loggedat:"+newDate();
}
return{
//publicmethod
logMessage:function(message){
console.log(formatMessage(message));
}
};
})();
Logger.logMessage("helloworld");
</script>
Intheprecedingcode,thelogMessage()methodreturnsanobjectthatisinvokedthroughaLoggernamespace.
Amodulecancontainmultiplemethodsandproperties,andtoimplementthisscenario,let'smodifytheprecedingexampleandaddonemoremethodtoshowalertmessageandapropertytoaccesstheloggernameandexposethemthroughanobjectliteralsyntax.Objectliteralisanotherrepresentationofbindingmethodsandpropertiesasnamevaluepairsseparatedbycommasandoffersacleanerrepresentation.Hereisthemodifiedcode:
<script>
varLogger=(function(){
//privatevariable
varloggerName="AppLogger";
//privatemethod
varformatMessage=function(message){
returnmessage+"loggedat:"+newDate();
}
//privatemethod
varlogMessage=function(message){
console.log(formatMessage(message));
}
//privatemethod
varshowAlert=function(message){
alert(formatMessage(message));
}
return{
//publicmethodsandvariable
logConsoleMessage:logMessage,
showAlertMessage:showAlert,
loggerName:loggerName
};
})();
Logger.logConsoleMessage("HelloWorld");
Logger.showAlertMessage("HelloWorld");
console.log(Logger.loggerName);
</script>
Intheprecedingcode,logMessage()andshowAlert()willbeaccessiblethroughlogConsoleMessage()andshowAlertMessage()methods.
ModularizingJavaScriptcodethroughRequireJS
ModulesinRequireJSareanextensionofthemodulepatternwiththebenefitofnotneedingglobalstorefertoothermodules.RequireJSisaJavaScriptAPItodefinemodulesandloadthemasynchronouslywhentheyarerequired.ItdownloadstheJavaScriptfilesasynchronouslyandreducesthetimetoloadthewholepage.
CreatingmodulesusingtheRequireJSAPI
AmoduleinRequireJScanbecreatedusingthedefine()methodandloadedusingtherequire()method.RequireJSprovidestwosyntaxstylestodefinemodulesthatareasfollows:
DefiningmoduleinCommonJSstyle:HereisthecodesnippettodefinethemoduleintheCommonJSstyle:
define(function(require,exports,module){
//requiretouseanyexistingmodule
varutility=require('utility');
//exportstoexportvalues
exports.example="CommonJS";
//moduletoexportvalues
module.exports.name="Largescaleapplications";
module.exports.showMessage=function(message){
alert(utility.formatMessage(message));
}
});
TheprecedingCommonJSstylesyntaxusesthedefine()methodofRequireJSAPIthattakesafunction.Thisfunctiontakesthreeparameters:require,exports,andmodule.Thelasttwoparameters,exportsandmodule,areoptional.However,theyhavetobedefinedinthesamesequence.Ifyouarenotusingrequire,andonlywantedtoexportsomefunctionalityusingtheexportsobject,therequireparameterneedstobeprovided.Therequireparameterisusedtoimportmodulesthatwereexportedusingexportsormodule.exportsinothermodules.Intheprecedingcode,wehaveaddedthedependencyoftheutilitymodulebyspecifyingthepathoftheutility.jsfileincallingtherequiremethod.Whenaddinganydependency,wejustneedtospecifythepathfollowedwiththefilenameoftheJavaScriptfileandnotthe.jsfileextension.ThefileisautomaticallypickedbytheRequireJSAPI.Exportinganyfunctionorvariablethatweneedtobeusedbyothermodulescanbedonethroughexportsormodule.exportsappropriately.DefiningmoduleinAMDstyle:HereisthecodesnippettodefinethemoduleinanAMD-stylesyntax:
define(['utility'],function(utility){
return{
example:"AMD",
name:"Largescaleapplications",
showMessage:function(){
alert(utility.formatMessage(message));
}
}
});
TheAMD-stylesyntaxtakesthedependenciesarrayasthefirstparameter.ToloadthemoduledependenciesinanAMD-stylesyntaxyouhavetodefinetheminanarray.Thesecondparametertakesthefunctionparameter,takingthemodulenamethatmapstothemoduledefinedinthedependenciesarraysothatitcanbeusedinthefunctionbody.Toexportthevariablesormethods,wecanexportthemthroughtheobjectliteralsyntax.
BootstrappingRequireJS
Let'sgothroughasimpleexampletounderstandtheconceptsofusingRequireJSinanASP.NETapplication.TousetheRequireJSAPIintheASP.NETCoreapplication,youhavetodownloadandplacetheRequire.jsfileinthewwwroot/jsfolder.Inthefollowingexample,wewillwritealoggingmodulethatcontainsafewmethodssuchaswritingtoconsole,showinganalert,andwritingontheserver.
Let'screatetheLogging.jsfileinthewwwroot/js/commonfolderandwritethefollowingcode:
define(function(){
return{
showMessage:function(message){
alert(message);
},
writeToConsole:function(message){
console.log(message);
},
writeToServer:function(message){
//writetoserverbydoingsomeAjaxrequest
varxhr=newXMLHttpRequest();
xhttp.open("POST","http://localhost:8081/Logging?message="+message,true);
xhttp.send();
}
}
});
ThefollowingisthecodefortheIndex.cshtmlpagethatdisplaysanalertmessagewhenthepageisloaded:
<scriptsrc="~/js/require.js"></script>
<script>
(function(){
require(["js/common/logging"],function(logging){
logging.showMessage("demo");
});
})();
</script>
Wecanalsowraptheprecedingfunctioninthemain.jsfileandbootstrapitthroughthe
scripting<script>tag.Thereisonespecialattributeknownasdata-main,whichisusedbyRequireJSastheentrypointoftheapplication.
Thefollowingisthecodeformain.jsthatresidesunderthewwwroot/JSfolder.Asmain.jsresidesunderthewwwroot/jsfolder,thepathwillbecommon/logging:
//Main.js
require(["common/logging"],function(logging){
logging.showMessage("demo");
});
Finally,wecanbootstrapmain.jsusingscriptingtag,asshowninthefollowingcode:
<scriptdata-main="~/js/main.js"src="~/js/require.js"></script>
ThefollowingisthesampleprojectstructurecontainingtheCommonfoldertohavecommonJavaScriptfiles;whereas,FIandHRfoldersformodule-specificJavaScriptfiles:
Supposewewanttomodifytheprecedingexampleandpassthemessagefromaninputcontrolonabutton'sclickevent.Thiscanbedonebydevelopingaviewmoduleforaparticularpageandinjectingtheloggingmoduleinsideit.
ThefollowingistheHTMLmarkupcontaininginputandbuttonelements:
<divid="myCarousel"class="carouselslide"data-ride="carousel"data-
interval="6000">
<inputtype="text"id="txtMessage"/>
<buttonid="btnSendMessage">SendMessage</button>
</div>
Thefollowingistheview.jsfilethattakestheloggingmoduleandcallitssendMessage()methodbyreadingthetxtMessageelementvalue:
define(['common/logging'],function(logging){
$('#btnSendMessage').on('click',function(e){
sendMessage();
e.preventDefault();
});
functionsendMessage(){
varmessage=document.getElementById('txtMessage').value;
logging.showMessage(message);
}
return{
sendMessage:sendMessage
};
});
Whenthebuttonisclicked,analertmessagewillbedisplayed.
Event-drivenmessagingIntheprecedingsection,weenabledthemodularizationsupportforJavaScriptfilesandconvertedthemintomodules.Inlargeapplications,wecannotjustrelyoninjectingthemodulesinothermodules,wemayneedsomeflexibilitytoinvokeeventsofcertainmodulesthroughsomethePub/Subpattern.WehavealreadyseenthePub/SubpatterninChapter7,JavaScriptDesignPatterns,whichmaintainsacentralizedlisttoregistereventsthatpointtosomecallbackfunctionsandareinvokedthroughapublisherobject.Thispatternisveryusefulwhenenablingevent-drivenmessagingbetweenmodules,butthereisanotherbetterpatternknownasamediatorpattern,whichisasupersetofthePub/Subpattern.Themediatorpatternisbetterasitallowspublishersormediatorstoaccessotherevents/methodsofthesubscribedobjectandallowsthemediatortodecidethemethodoreventthatisneededtobecalled.
Implementingmediatorpatternforcommunicationbetweenmodules
Mediatorencapsulatesobjectsinacentralizedlistandusesthembyinvokingtheirmethods.Thislistkeepsalltheobjects(ormodules)atcentrallocation,thusallowingimprovedcommunicationbetweenthem.
Let'sgothroughapracticalexampleofimplementingthemediatorpattern.Themediatoractsasacentralizedcontrollingobjectwheremodulescansubscribeorunsubscribe.Itprovidesabstractmethodsthatcanbeinvokedbyanyofthesourcesubscribermoduletocommunicatewiththetargetsubscribermodule.Themediatorholdsacentralizeddictionaryobjecttoholdsubscriberobjectsbasedonsomekey,ormostlyname,andinvokestargetmodulemethodbasedonthemodulenamepassedbythesubscriber.Inthefollowingexample,wehaveMediatorCore(mediator),EmployeeRepository(subscriber),andHRModule(subscriber)objects.WewillusetheRequireJSAPItoconvertJavaScriptfilesintomodules.
ThefollowingistheMediatorCoreJavaScriptfile:
//MediatorCore.js
define(function(){
return{
mediator:function(){
this.modules=[];
//Tosubscribemodule
this.subscribe=function(module){
//Checkifmoduleexistorinitializearray
this.modules[module.moduleName]=this.modules[module.moduleName]||[];
//Addthemoduleobjectbasedonitsmodulename
this.modules[module.moduleName].push(module);
module.mediator=this;
},
this.unsubscribe=function(module){
//Loopthroughthearrayandremovethemodule
if(this.modules[module.moduleName]){
for(i=0;i<this.modules[module.moduleName].length;i++){
if(this.modules[module.moduleName][i]===module){
this.modules[module.moduleName].splice(i,1);
break;
}
}
}
},
/*TocallthegetRecordsmethodofspecificmodulebasedonmodulename
*/
this.getRecords=function(moduleName){
if(this.modules[moduleName]){
//getthemodulebasedonmodulename
varfromModule=this.modules[moduleName][0];
returnfromModule.getRecords();
}
},
/*TocalltheinsertRecordmethodofspecificmodulebasedonmodulename
*/
this.insertRecord=function(record,moduleName){
if(this.modules[moduleName]){
//getthemodulebasedonmodulename
varfromModule=this.modules[moduleName][0];
fromModule.insertRecord(record);
}
},
/*TocallthedeleteRecordmethodofspecificmodulebasedonmodulename
*/
this.deleteRecord=function(record,moduleName){
if(this.modules[moduleName]){
//getthemodulebasedonmodulename
varfromModule=this.modules[moduleName][0];
fromModule.deleteRecord(record);
}
},
/*TocalltheupdateRecordmethodofspecificmodulebasedonmodulename
*/
this.updateRecord=function(record,moduleName){
if(this.modules[moduleName]){
//getthemodulebasedonmodulename
varfromModule=this.modules[moduleName][0];
fromModule.updateRecord(record);
}
}
}
}
});
ThismediatorexposesfourmethodstoperformCRUDoperations.ThisexampleshowcasesasimpleHRmodulethatusescertainrepositoriestodocertainoperations.Forexample,theHRmodulecanhavetheEmployeeRepositorymoduletosaverecordinemployee-specifictables,DepartmentRepositorytodooperationsspecifictodepartment,andsoon.
HereisthecodesnippetforEmployeeRepositorythatcontainstheconcreteimplementationoftheabstractmethodsdefinedinthemediator:
//EmployeeRepository.js
define(function(){
return{
//ConcreteImplementationofMediatorInterface
EmployeeRepository:function(uniqueName){
this.moduleName=uniqueName;
//thisreferencewillbeusedjustincasetocallsomeothermodule
methods
this.mediator=null;
//ConcreteImplementationofgetRecordsmethod
this.getRecords=function(){
//Callsomeservicetogetrecords
//SampletexttoreturndatawhengetRecordsmethodwillbeinvoked
return"Thisaretestrecords";
},
//ConcreteImplementationofinsertRecordmethod
this.insertRecord=function(record){
console.log("savingrecord");
//Callsomeservicetosaverecord.
},
//ConcreteImplementationofdeleteRecordmethod
this.deleteRecord=function(record){
console.log("deletingrecord");
//Callsomeservicetodeleterecord
}
//ConcreteImplementationofupdateRecordmethod
this.updateRecord=function(record){
console.log("updatingrecord");
//Callsomeservicetodeleterecord
}
}
}
});
EmployeeRepositorytakesanameparameteratinitializationanddefinesthemediatorvariablethatcanbesetwhenitisregisteredatmediator.ThisisprovidedincaseEmployeeRepositorywantstocallsomeothermoduleorrepositoryofasubscribermodule.Wecancreatemultiplerepositories,forexample,RecruitmentRepositoryand
AppraisalRepositoryforHRModuleandusethemwhenneeded.
HereisthecodeforHRModulethatcallsEmployeeRepositorythroughamediator:
//HRModule.js
define(function(){
return{
HRModule:function(uniqueName){
this.moduleName=uniqueName;
this.mediator=null;
this.repository="EmployeeRepository";
this.getRecords=function(){
returnthis.mediator.getRecords(this.repository);
},
this.insertRecord=function(record){
this.mediator.insertRecord(record,this.repository);
},
this.deleteRecord=function(record){
this.mediator.deleteRecord(record,this.repository);
}
this.updateRecord=function(record){
this.mediator.updateRecord(record,this.repository);
}
}
}
});
Now,wewillregisterHRModuleandEmployeeRepositorywiththemediatorandcalltheHRModulemethodstoperformCRUDoperations.
ThefollowingisthecodeforHRView.jsthatisusedtocapturethebutton'sclickeventontheformandcallsthegetRecords()methodwhenthebuttonisclicked:
//HRView.js
define(['hr/mediatorcore','hr/employeerepository','hr/hrmodule'],function
(mediatorCore,employeeRepository,hrModule){
$('#btnGetRecords').on('click',function(e){
getRecords();
e.preventDefault();
});
functiongetRecords(){
varmediator=newmediatorCore.mediator();
varempModule=newhrModule.HRModule("EmployeeModule");
mediator.subscribe(empModule);
varempRepo=new
employeeRepository.EmployeeRepository("EmployeeRepository");
mediator.subscribe(empRepo);
alert("Records:"+empModule.getRecords());
}
return{
getRecords:getRecords
};
});
Thefollowingisthemain.jsfilethatisusedtobootstrapstheHRView.jsfilethroughtheRequireJSAPI:
//main.js
require(["./hrview"],function(hr){
});
Finally,wecanusetheprecedingMain.jsmoduleontheIndex.cshtmlpageinASP.NET,asfollows:
//Index.cshtml
@{
ViewData["Title"]="HomePage";
}
<scriptdata-main="js/main.js"src="~/js/require.js"></script>
<divid="myCarousel"class="carouselslide"data-ride="carousel"data-
interval="6000">
<inputtype="text"id="txtMessage"/>
<buttonid="btnGetRecords">SendMessage</button>
</div>
Thefollowingisthelogicaldiagramthatshowshowthemodulescommunicatewitheachother:
EncapsulatingcomplexcodeAnothercoreprincipleofdevelopinghighlyscalableandmaintainableapplicationistousewrappersandencapsulatecomplexcodeintoasimplerinterface.ThiscanbeachievedbyimplementingaFacadepattern.
TheFacadepatternisusedtosimplifythecomplexcodebyexposingamethodandhidingallthecomplexcodeinsidetheFacadeobject.Forexample,therearemanywaysandAPIsavailabletoperformAjaxifiedoperations.AjaxrequestscanbemadeusingaplainXmlHttpRequestobject,orwithjQuery,itisquiteeasytouse$.post()and$.get()methods.InAngularJS,itcanbeachievedusingitsownhttpobjecttoinvokeservicesandsoon.ThesetypeofoperationscanbeencapsulatedandbenefitedinscenarioswhentheinternalAPIismodified,orwhenyoudecidedtousesomeotherbetterAPI;modificationneedstobedone,whichisfarlesserthanchangingitatalltheplaceswhereithasbeenused.WiththeFacadepattern,youcanonlymodifyitintheFacadeobjectandsavetimeonupdatingiteverywherewhereithasbeenused.
AnotheradvantageofusingFacadeisthatitreducesthedevelopmenteffortbyencapsulatingabunchofcodetoasimplemethodandmakeiteasyfortheconsumertouse.Facadereducesthedevelopmenteffortbyminimizingthelinesofcoderequiredtocallacertainfunctionality.TolearnmoreaboutFacade,refertoChapter7,JavaScriptDesignPatterns.
GeneratingdocumentationProperdocumentationincreasesthemaintainabilityofyourapplicationandmakesiteasierfordeveloperstoreferenceitwhenneededorcustomizingapplications.Therearemanydocumentationgeneratorsavailableinthemarket.JSDocandYUIDocareverypopularJavaScriptdocumentationgenerators,butinthissection,wewilluseJSDoc3thatnotonlygeneratesdocumentation,butalsoenablesintellisenseforyourcustomJavaScriptmodulestofacilitateduringdevelopment.
JSDocisanAPIsimilartoJavaDocandPHPDoc.CommentscanbeaddeddirectlytotheJavaScriptcode.ItalsoprovidesaJSDoctoolthroughwhichthedocumentationwebsitecanbecreated.
InstallingJSDoc3inASP.NETCore
JSDoc3canbeaddedasaNodepackageandwecanuseitwiththeGulptaskrunnertogeneratedocuments.ToaddJSDoc3toyourASP.NETCoreproject,youcanstartbyaddinganentrytothepackage.jsonfileusedbyNode.Thisentryhastobedoneinthedevelopmentdependencies:
ThefirstdevelopmentdependencydefinedinthepreviousscreenshotisGulpwhichisrequiredtocreatetasks,andgulp-jsdoc3istheactualdocumentationgeneratorthatgeneratestheHTMLwebsitewhenyourunthattask.
Thetaskcanbedefinedasfollows:
///<bindingClean='clean'/>
"usestrict";
vargulp=require("gulp"),
jsdoc=require("gulp-jsdoc3");
varpaths={
webroot:"./wwwroot/"
};
paths.appJs=paths.webroot+"app/**/*.js";
gulp.task("generatedoc",function(cb){
gulp.src(['Readme.md',paths.appJs],{read:false})
.pipe(jsdoc(cb));
});
Intheprecedingcodesnippet,wehaveonetasknamedgeneratedoc,inwhichwearereadingthefilesplacedatwwwroot/app/**/*.jsandgeneratingdocumentation.Thejsdocobjecttakestheconfigurationdefaultstogeneratedocumentation.Topassthedefaultconfigurationattributes,wecanjustspecifythecbparameterinjectedinthefunctionlevelbyGulp.WhenyourunthisgeneratedoctaskfromthetaskrunnerinVisualStudio,itwilladdadocsfolderattherootpathofyourwebapplicationproject.AsinASP.NETCore,wealreadyknowthatallstaticcontentshouldresideinthewwwrootfolder,andtoaccessitfrombrowser,simplydraganddropthisfolderinthewwwrootfolderandaccessitbyrunningyourwebsite.
Addingcomments
Togeneratedocumentation,weneedtoannotateourcodewithcomments.Themorethecommentsareprovided,thebetterthedocumentationwillbegenerated.Commentscanbeaddedthrough/**asthestartingtagand*/astheendingtag:
/**ThismethodisusedtosendHTTPGetRequest**/
functionGetData(path){
$.get(path,function(data){
returndata;
})
}
Ifthefunctionisaconstructor,youcanspecify@constructorinthecommentstogivemoremeaningtothereaders:
/**ThismethodisusedtosendHTTPGetRequest
@constructor
*/
functionGetData(path){
$.get(path,function(data){
returndata;
})
}
Afunctiontakesparametersaswell,andthiscanbeindicatedbyusing@paraminyourcomments.Hereisthesamefunctionthattakestheactualpathofsomeserviceasaparametertoretrieverecords:
/**ThismethodisusedtosendHTTPGetRequest
@constructor
@parampath–SpecifyURIoftheresourcethatreturnsdata
*/
functionGetData(path){
$.get(path,function(data){
returndata;
})
}
Whenyourunyourapplication,itwillshowthedocumentationasfollows:
WehaveseenhoweasyitiswithJSDoc3togeneratedocumentation.Thisnotonlyhelpstounderstandthecode,butalsohelpsthedeveloperduringdevelopmentbyprovidingintellisense.TolearnmoreaboutJSDoc3,refertohttp://usejsdoc.org/.
DeploymentoptimizationLarge-scaleapplicationconsistsoflargenumberofJavaScriptfiles.Whenthepageisdownloaded,itisparsedanditdownloadsalltheJavaScriptfilesdefinedwiththe<script>tag.OncetheJavaScriptfilesaredownloaded,theyareparsedandexecuted.So,itdependsonthenumberofJavaScriptfilesyouhavereferencedonthepage,followedbythelinesofcodeeachJavaScriptfilecontains.Tooptimizethepage-loadingcycle,itisrecommendedtocompressthemthroughaminificationprocess.ThismakestheJavaScriptfilesmallerinsizeandthepage-loadingcyclebecomesfaster.
InASP.NET,wecancompresstheJavaScriptfilesusingGruntandGulpmodules.TheseareNodemodulesandarehighlyintegratedwithASP.NETCore.InASP.NETCore,wecanaddthesemodulesbyaddingaNodepackagereferenceinthepackage.jsonfile,andeachmodulehasitsseparateconfigurationfileknownasGulpFile.jsorGruntFile.js.
Inthisexample,wewillusetheGulpmoduletominifyandcompressourJavaScriptfiles.InASP.NETCore,wecanenableGulpbyaddingtheGulpmoduletothepackage.jsonfile:
Theprecedingcodesnippetusesgulp,gulp-concat,gulp-cssmin,andgulp-uglify.Thefollowingisthedescriptionofeachmodule:
Module Description
Gulp Thisisusedtodefinetasksthatcanberunthroughtaskrunners
gulp-concat ThisisusedtoconcatenateJavaScriptfilesintoasinglefile
gulp-cssmin ThisisusedtocompressCSSfiles
gulp-uglify ThisisusedtocompressJavaScriptfiles
Thefollowingisthesamplegulpfile.jsthatcanbeusedtocompressJavaScriptandCSSfiles:
///<bindingClean='clean'/>
"usestrict";
//Addingreferencesofgulpmodules
vargulp=require("gulp"),
rimraf=require("rimraf"),
concat=require("gulp-concat"),
cssmin=require("gulp-cssmin"),
uglify=require("gulp-uglify");
//definerootpathwhereallJavaScriptandCSSfilesreside
varpaths={
webroot:"./wwwroot/"
};
/*Pathwhereallthenon-minifiedJavaScriptfileresides.JSisthefolderand
**isusedtohandleforsubfolders*/
paths.js=paths.webroot+"js/**/*.js";
/*PathwherealltheminifiedJavaScriptfileresides.JSisthefolderand**
isusedtohandleforsubfolders*/
paths.minJs=paths.webroot+"js/**/*.min.js";
/*Pathwhereallthenon-minifiedCSSfileresides.Cssisthemainfolderand
**isusedtohandleforsubfolder*/
paths.css=paths.webroot+"css/**/*.css";
/*PathwherealltheminifiedCSSfileresides.Cssisthemainfolderand**
isusedtohandleforsubfolder*/
paths.minCss=paths.webroot+"css/**/*.min.css";
/*NewJavaScriptfilesite.min.jsthatcontainsallthecompressedandmerged
JavaScriptfiles*/
paths.concatJsDest=paths.webroot+"js/site.min.js";
/*NewCSSfilesite.min.cssthatwillcontainallthecompressedandmergedCSS
files*/
paths.concatCssDest=paths.webroot+"css/site.min.css";
//todeletesite.min.jsfile
gulp.task("clean:js",function(cb){
rimraf(paths.concatJsDest,cb);
});
//todeletesite.min.cssfile
gulp.task("clean:css",function(cb){
rimraf(paths.concatCssDest,cb);
});
/*Tomerge,compressandplacetheJavaScriptfilesintoonesinglefile
site.min.js*/
gulp.task("min:js",function(){
returngulp.src([paths.js,"!"+paths.minJs],{base:"."})
.pipe(concat(paths.concatJsDest))
.pipe(uglify())
.pipe(gulp.dest("."));
});
/*tomerge,compressandplacetheCSSfilesintoonesinglefilesite.min.css
*/
gulp.task("min:css",function(){
returngulp.src([paths.css,"!"+paths.minCss])
.pipe(concat(paths.concatCssDest))
.pipe(cssmin())
.pipe(gulp.dest("."));
});
Intheprecedingcodesnippet,therearefourtasksandthefollowingistheirdescription:
clean:js:Thisremovesthesite.min.jsfileclean:css:Thisremovesthesite.min.cssfilemin:js:Thismergesallthefilesspecifiedinpaths.jsandpaths.minJs,minifiesthemusinguglify(),andfinallycreatesthesite.main.jsfilemin:css:Thismergesallthefilesspecifiedinpaths.cssandpaths.minCss,minifiesthemusingcssmin(),andfinallycreatesthesite.main.cssfile
InVisualStudio2015,youcanrunthesetasksusingTaskRunnerExplorer,andalsobindthemwiththebuildevents:
Thefollowingaretheoptionsthatyoucanhavetoassociatethemwithspecificbuildevents:
Theprecedingscreenshotshowsthestepstobindtheclean:jstaskwithaCleanbuildevent.So,wheneveryourcleanyourproject,itwillrunclean:jsandremovethesite.min.jsfile.
SummaryInthischapter,wediscussedafewconceptsofstructuringJavaScript-basedprojectsandsplittingthemintomodulestoincreasethescalabilityandmaintainability.Wealsosawhoweffectivelywecanusethemediatorpatterntoprovidecommunicationbetweenmodules.Documentationalsoplaysanimportantroleandincreasesthemaintainability,andweusedJSDoc3,whichisoneofthemostpopularJavaScriptdocumentationAPIsandhelpeddeveloperstoreferenceandunderstandthefunctionsofJavaScriptduringdevelopment.Lastly,wediscussedhowtooptimizetheloadtimeofyourapplicationbycompressingandmergingyourJavaScriptfilesintooneminifiedJavaScriptfiletoincreasetheperformance.Inthenextchapter,wewilldiscusstestinganddebuggingJavaScriptapplicationsandthetoolsthatareavailabletotroubleshootefficiently.
Chapter10.TestingandDebuggingJavaScriptIneverysoftwarelifecycle,testinganddebuggingplayanimportantrole.Thoroughtestingmakessoftwareflawlessandgooddebuggingtechniquesmakesiteasytonotonlytroubleshootproblems,butalsohelpstoidentifyandfixanyproblemsbyreachingouttotheexactpoint.
Testingisthecoreessenceofcreatinganyrobustapplication.However,therearedifferentpracticesandframeworksusedbytheapplicationtoserveparticularobjective,andthearchitecturevariesasperthenatureoftheapplication.Therefore,sometimesitbecomesdifficultforadevelopertotestclient-sidecode,forexample,ifanapplicationcontainssomeJavaScriptcodeonapageitself,suchasinlineeventhandlers,makeittightlycoupledwiththepage.Ontheotherhand,evenwhenmodularizingtheJavaScriptcodeintodifferentmodulesbringsometestsuitelimitationsandbecomehardertoexecutethetestingprocessofanapplication.
Debuggingistheprocessoffindingandfixingerrorsinanapplication.Itisoneofthemostimportantandcoreskillsetinsoftwaredevelopment.Ifdevelopershaveasolidgrasponthedebuggingtoolsandknowtheinsandoutsofdebugging,theycanquicklyidentifytherootcauseandstartfixingtheerrors.Debuggingisabasicprocessinanysoftwaredevelopmentlifecycle.Whetherapplicationisacomplexoneorasimpleone,totraceandrectifyerrorsdebuggingplaysanimportantrole.Ithelpsthedevelopertobreaktheprogramexecutionthroughbreakpointsandidentifytheprogramflowbysteppingintothechainofprogramexecution.Moreover,thereisotherusefulinformationalmostalldebuggingtoolsprovide,suchaswatchingthecurrentstateofthevariablesorobjectsbeingusedwithintheprogramandwatchingthemoneverystageofthedebugginglifecycle.
TestingtheJavaScriptcodeNormally,webapplicationsgothroughdifferenttypesoftesting,suchasuserinterface(UI)testing,whichchecksthefunctionalityoftheUIbymakingcertaininputstotheformandverifiesthebehaviorofanapplication.Thistypeoftestingismostlydonemanuallyorthroughautomatedtestingtools.Theothertypeoftestingisloadtesting,whichisusedmostlytochecktheperformanceofanapplicationandbyputtingupsomeloadontheapplication.Insimpleterms,itcanbeanexampleofsigningintoanapplicationwithmanynumberofusersordoingsomeoperationsthroughautomatedroutinestotesthowtheapplicationbehaves.Thereareafewmoretestingtypes,butthemostessentialtypeoftestingthatensuresthefunctionalityoftheapplicationandcertifieswhethertheapplicationcomplieswiththerequirementsisunittesting.Inthissection,wewilldiscussaboutunittestingJavaScriptcodeusingJasmine(apopularJavaScriptunittestframework)anduseitwithKarmaandGrunttoexecutetestcasesinanASP.NETapplicationusingVisualStudio2015IDE.
UnittestingUnittestingisamethodtotestindividualunitsofmodulestogetherwithassociateddataandprocedurestoverifytheapplication'sfunctionalitycompliancetotherequirements.Unittestingisdonebydevelopers,anditallowsdeveloperstotesteachusecaseoftheapplicationtoguaranteethatitmeetstherequirementandworksasexpected.
Thebasicadvantageofunittestingisthatitseparateseachpartoftheapplicationintoasmallerunitandhelpsdeveloperstofocusandidentifythebuginitiallyduringthedevelopmentcycle.Unittestingisthefirsttestinganyapplicationenduresandallowtestersanddeveloperstoreleasetheapplicationforuseracceptancetesting(UAT).
Writingunittests
TotestJavaScriptcode,therearemanytestingsuitesavailable.ThemostpopularonesareJasmine,Mocha,andQUnit.Inthischapter,wewilluseJasminewithKarmaandGrunt.
Jasmine
Jasmineisabehavior-drivendevelopmentframeworkfortestingJavaScriptcode.Thisprovidescertainfunctionssuchasit(),describe(),expect(),andsoontowritetestscriptsfortheJavaScriptcode.ThebasicadvantageofthisframeworkisthatitisveryeasytounderstandandhelpstowritethetestJavaScriptcodewithverysimplelinesofcode.
Forexample,considerthefollowingJavaScriptcodethatsumsuptwonumberspassedasparameters:
(function(){
varaddTwoNumbers=function(x,y){
returnx+y;
};
})();
Thetestcasefortheprecedingfunctionwilllooksimilartothefollowing:
describe('Calculator',function(){
it('Resultswillbe20for10+10',function(){
expect(addTwoNumbers(10,10)).toBe(20);
});
});
Karma
KarmaisatestrunnerforJavaScriptthatcanbeintegratedwithothertestingframeworkssuchasJasmine,Mocha,andsoon.ItexecutestestcasesdefinedthroughJasmineorothertestframeworksbyprovidingamocktestenvironmentandloadbrowsersthatexecutesthetestJavaScriptcodeaccordingtotheconfiguration.TheKarmaconfigurationfileisknownasKarma.config.js.Oncethetestsareexecuted,theresultsaredisplayedintheconsolewindow.
Grunt
GruntisequivalenttoGulp.ItisusedtoexecutetaskssuchasminificationofCSSfileorJavaScriptfiles,concatenationandmergingofmultipleJavaScriptfile,andsoon.Grunthashundredsofpluginsthatcanbeusedtoautomatespecifictasks.Unlikethepreviouschapters,whereweusedGulp,wewilluseGruntandseewhatitprovideswithKarma(testrunner)andJasmine(testingsuite).GruntandGulparebothrenownedtaskrunnersfordevelopment.ThereasonforusingGrunthereistogetanunderstandingofanothertaskrunnerofJavaScriptthatisequallyrenownedandsupportedbyVisualStudio2015anddiscussthepackagesthatitprovidestoperformtestingusingKarmaandJasmine.
DevelopingunittestusingJasmine,Karma,andGrunt
Inthissection,wewilldevelopasimpleunittesttoshowhowunittestingcanbedoneinanASP.NETCoreapplicationusingJasmine,Karma,andGruntframeworks.Tostartwith,createanASP.NETCoreapplicationfromVisualStudio2015.
Addingpackages
Openthepackage.jsonfileinyourASP.NETCoreapplicationandaddpackagessuchasgrunt,grunt-karma,karma,karma-phantomjs-launcher,karma-jasmine,karma-spec-reporter,andkarma-cli,asshowninthefollowing:
Thefollowingtableshowsthedescriptionofeachpackage:
PackageName Description
grunt Thisconfiguresandrunstasks
grunt-karma ThisistheGruntpluginfortheKarmatestrunner
karma ThisisthetestrunnerforJavaScript
karma-phantomjs-launcher ThisistheKarmaplugintolaunchthePhantomJSbrowser
karma-jasmine ThisistheKarmapluginfortheJasminetestsuite
karma-spec-reporter ThisistheKarmaplugintoreporttestresultstotheconsole
karma-cli ThisistheKarmacommand-lineinterface
AddingtheGruntfile
AddGruntfile.jsinyourASP.NETapplicationtodefineGrunttasks.Gruntfile.jsisthemainfilewhereallthetasksareconfigured.ConfiguredtaskscanbeseeninVisualStudiofromtheTaskRunnerExplorerwindow.AddingKarmaspecifications
TheGruntfile.jsfileprovidesthemaininitConfig()methodthatiscalledwhentheGruntisloaded.ThisisthestartingpointwherewedefinetheKarmaspecifications.
ThefollowingistheKarmaspecificationsdefinedwithintheinitConfig()method:
grunt.initConfig({
karma:{
unit:{
options:{
frameworks:['jasmine'],
singleRun:true,
browsers:['PhantomJS'],
files:[
'./wwwroot/js/**/*.js',
'./wwwroot/tests/**/*.test.js'
]
}
}
}
});
Intheprecedingscript,wefirststartedbyspecifyingthetargetplatformforKarma.Insidekarma,wewillspecifytheunitthatisusedtorununittests.Insideunit,wecandefinecertainconfigurationattributessuchasframeworks,singleRun,browsers,andfiles:
frameworks:Thisisanarrayoftestframeworksthatwewanttouse.Inthisexercise,we
usedJasmine.However,otherframeworkssuchasMochaandQUnitcanalsobeused.
Tip
PleasenotethatwhenusinganyframeworkinKarma,anadditionalplugin/libraryofthatframeworkhastobeseparatelyinstalledusingNodePackageManager(NPM).
singleRun:Ifthisissettotrue,Karmastartcapturingtheconfiguredbrowser(s)andexecutestestsonthem.Oncethetestsarecompleted,itexitssmoothly.browsers:Thisisanarraytodefinemultiplebrowsersinacomma-separatedvalue.WehaveusedPhantomJSinourexample,whichisaheadlessbrowserandrunsthetestinbackground.KarmasupportsotherbrowserssuchasChrome,Firefox,IE,andSafari,andthesecanbeconfiguredthroughthisproperty.files:Thiscontainsallthetestfiles,sourcefiles,anddependencies.Forexample,ifweareusingjQueryinourtestscripts,ororiginalsourcecode,wecanaddthepathtothislibraryaswell.Intheprecedingconfiguration,weusedwildcardcharacterstoloadallthesourcefilesdefinedunderthejsfolder,andtestsfilesunderthetestsfolderwithatest.jssuffix.
TherearemanymoreattributesthatcanbeusedintheKarmaconfigurationanditcanbereferredhere:
http://karma-runner.github.io/0.13/config/configuration-file.htmlLoadnpmtask
ToloadtheKarmatestrunnertool,weneedtospecifyitinGruntfile.jsaftertheKarmaconfiguration,asshowninthefollowing:
grunt.loadNpmTasks('grunt-karma');
Registertask
Finally,wewilladdtheGrunttasktoregistertasks.Thefirstparameteristhetaskname,whichwillbeavailableintheTaskRunnerExplorerinVisualStudio,andthesecondparametertakesanarraytoexecutemultipletasks:
grunt.registerTask('test',['karma']);
SourceJavaScriptfile
Inthisexample,wehaveaproduct.jsfilethatcontainsasaveProduct()method,whichwillbeinvokedontheSavebutton'sclickevent.
Addthisfiletothewwwroot/jsfolderpath:
window.product=window.product||{};
(function(){
varsaveProduct=function(){
varprodCode=document.getElementById('txtProdCode').value;
varprodUnitPrice=document.getElementById('txtProdUnitPrice').value;
varprodExpiry=document.getElementById('txtProdExpiry').value;
varprodQuantity=document.getElementById('txtProdQuantity').value;
vartotalPrice=prodUnitPrice*prodQuantity;
document.getElementById('totalAmount').innerHTML=totalPrice;
};
window.product.init=function(){
document.getElementById('save').addEventListener('click',saveProduct);
};
})();
Intheprecedingcodesnippet,wehaveasaveProduct()methodthatreadstheHTMLelementsandcalculatesthetotalpricebasedonthequantityandunitpriceentered.Onthepageinitialization,wewillregistertheSavebutton'sclickeventhandlerthatcallsthesaveProduct()methodandcalculatethetotalprice.
Tip
ItisarecommendedapproachtokeepyourJavaScriptcodeseparatefromyourHTMLmarkup.
Addingunittestscriptfile
Here,wewilladdanotherJavaScriptfileunderthewwwroot/testsfolderandnameditproduct.test.js.Whenwritingtests,youcanaddthe*.test.jssuffixtomakeituniquelyidentified,andseparatesitfromthesourceJavaScriptfiles.
Hereisthecodeforproduct.test.js:
describe('Product',function(){
//injecttheHTMLfixtureforthetests
beforeEach(function(){
varfixture='<divid="fixture">'+
'<inputid="txtProdCode"type="text">'+
'<inputid="txtProdExpiry"type="text">'+
'<inputid="txtProdUnitPrice"type="text">'+
'<inputid="txtProdQuantity"type="text">'+
'<inputid="save"type="button"value="Save">'+
'TotalAmount:<spanid="totalAmount"/></div>';
document.body.insertAdjacentHTML(
'afterbegin',
fixture);
});
//removethehtmlfixturefromtheDOM
afterEach(function(){
document.body.removeChild(document.getElementById('fixture'));
});
//calltheinitfunctionofcalculatortoregisterDOMelements
beforeEach(function(){
window.product.init();
});
it('Expectedresultshouldbe0iftheUnitpriceisnotvalid',function()
{
document.getElementById('txtProdUnitPrice').value='a';
document.getElementById('txtProdQuantity').value=2;
document.getElementById('save').click();
expect(document.getElementById('totalAmount').innerHTML).toBe('0');
});
it('Expectedresultshouldbe0iftheProductQuantityisnotvalid',
function(){
document.getElementById('txtProdUnitPrice').value=30;
document.getElementById('txtProdQuantity').value='zero';
document.getElementById('save').click();
expect(document.getElementById('totalAmount').innerHTML).toBe('0');
});
});
TheJasmineframeworkprovidescertainkeywordstodefinespecificblocksthatrunonspecificconditions,whichareasfollows:
describe():ThisisaglobalJasminefunctionthatcontainstwoparameters:astringandafunction.Thestringisthenameofthefunctionalitythatisgoingtobetested.ThefunctioncontainsthecodethatactuallyimplementstheJasminesuiteandcontainslogicofunittests.it():Here,specsaredefinedbycallingtheglobalJasminefunctionit().Thisalsotakesthestringandfunction,whereitcontainstheactualunittestnameandthefunctionblockcontainstheactuallogicofthecodetobeexecutedfollowedwiththeexpectedresults.expect():Theexpectedresultscanbespecifiedbyusingtheexpect()functionthattakessomevaluedefinedwithintheit()function.Thisisalsochainedwithamatcherfunction,suchastoBe()ornot.toBe(),tomatchorunmatchtheexpectedvalue.
In.NET,itisequivalenttotheArrange,Act,andAssertpattern.Here,Arrangeisusedtoinitializeobjectsandsetvaluesofthedatathatispassedtothemethodundertest.TheActpatternactuallyinvokesthemethodundertest,andAssertverifiesthatthemethodundertestbehavesasexpected.
Runningtesttask
Runningthesetasksisstraightforward,itcansimplyberunthroughtheTaskRunnerExplorerwindowinVisualStudio2015.HereisthescreenshotoftheTaskRunnerExplorerwindowthatshowsthetasksdefinedinGruntfile.js:
Whenwerunthetesttask,itwillshowsomethingsimilartothefollowingoutput:
Inourproduct.test.jstestscript,wehavetwotasks.OneistocheckwhetherpassingthestringvaluestooneofthetwoelementssuchastxtProdUnitPriceandtxtProdQuantitywillreturn0.Asourproduct.jsfiledoesnothandlethiscondition,itwillgiveanerror.
Tofixthis,wewillmodifyourproduct.jsandaddthesetwolinestohandlethislogictocheckwhetherthevalueisanumberornot:
prodUnitPrice=isNaN(prodUnitPrice)?0:prodUnitPrice;
prodQuantity=isNaN(prodQuantity)?0:prodQuantity;
Now,whenwerunourtestagain,wewillgetthefollowingoutput:
Intheprecedingexample,wedefinedtheHTMLmarkupwithinthebeforeEach()functionintheproduct.test.jsfile.Withsimpleapplications,thismaynotbeacumbersomeprocesstoredefinetheHTMLmarkupasfixturesandusethemtoexecutetests.However,mostwebapplicationsareusingsomeclient-sideframeworkssuchasKnockout,AngularJS,andsoon,thatseparatesthebindingofcontrolsspecifiedinanHTMLviewtoaViewModel,andthisViewModelisresponsibletoreadorwritecontrolvalues.
Inthefollowingexample,wewillusetheKnockoutJavaScriptlibrarythatimplementsanModel-View-ViewModelpatternandseehowunittestscanbewritteninthisway.
ImplementingModel-View-ViewModelusingKnockoutandRuntest
Model-View-ViewModel(MVVM)isadesignpatternforbuildinguserinterfaces.Itisdividedintothreeparts,asshowinthefollowingdiagram:
Thesethreepartsaredescribedasfollows:
Model:Thiscontainsthebackendlogictoinvokebackendservicesandsaveorretirevedatabycommunicatingwiththepersistantstorage.ViewModel:Thiscontainstheview-specificoperationsanddata.Itrepresentsthemodeloftheviewtowhichtheviewelementsbindsto.Forexample,aformthatcontainssomeHTMLelementswillhaveaViewModel,whichisanobjectcontainingsomepropertiestobindthesecontrolswiththedata.
View:Thisistheuserinterfacetowhichtheuserinteracts.ItdisplaysinformationfromtheViewModel,raiseseventsattheViewModel,andupdatesitwhentheViewModelchanges.
Let'simplementtheMVVMpatternusingtheKnockoutJavaScriptlibraryusingthefollowingsteps.
AddingtheKnockoutpackage
Tostartwith,let'saddKnockout.jsinyourASP.NETCoreapplicationthroughbower.json.Itcanbeaddedbymakinganentryinthedependenciessectionofthebower.jsonfile,andVisualStudioautomaticallydownloadsthepackageandplacesitinthewwwroot/lib/knockoutfolder.
Thefollowingstatementcanbeaddedinthebower.jsonfile:
"knockout":"3.4.0",
AddingProductViewModel
ProductViewModelcontainspropertiessuchastheproductcode,unitprice,quantity,expiry,andtotalamount.HereisthecodesnippetofProductViewModel.js:
varProductViewModel=function(){
this.prodCode=ko.observable('');
this.prodUnitPrice=ko.observable(0);
this.prodQuantity=ko.observable(0);
this.prodExpiry=ko.observable('');
this.prodTotalAmount=0;
ko.applyBindings(this);
this.saveProduct=function(){
varunitPrice=this.prodUnitPrice();
varquantity=this.prodQuantity();
vartotal=unitPrice*quantity;
this.prodTotalAmount=total;
//callsomeservicetosaveproduct
}
};
Intheprecedingcodesnippet,wehaveaProductViewModelclassthatcontainsafewproperties,eachpropertyisassignedtoko.observable().
koisbasicallytheKnockoutobjectthatprovidesacomplimentarywayoflinkinganobjectmodeltotheView,whereko.observable()isaKnockoutfunctionthatmakestheModelpropertiesobservableandsyncwiththeViewdata.ThismeansthatwhentheViewModelproperty'svaluechanges,Viewisupdated;andwhenthecontrolvalueismodified,the
ViewModelpropertyisupdated.
Valuesarealsopre-populatedasshowninthefollowingcodesnippet.Passing0inthefollowingstatementwillsetthecontrolvalue0whenthecontrolbindingisdone:
this.prodUnitPrice=ko.observable(0)
ko.applyBindings()actuallyactivatesKnockouttoperformthebindingoftheModelpropertieswiththeViewelements.
AddtheProductview
KnockoutprovidesaverydecentwayofbindingViewModelpropertiestothecontrolelements.Bindingconsistoftwoitems,nameandvalue,separatedbyacolon.TobindtheViewModelwiththeinputelements,wecanusethedata-bindattributeandspecifythevaluenamefollowedwith:andViewModel'spropertyname.Eachcontrolhasaspecificsetofpropertiesanditcanbeusedtobindelementsaccordingly.
Forexample,thespanelementcanbindtotheviewmodelpropertyusingthetextnameasshowninthefollowing:
Productcodeis:<spandata-bind="text:prodCode"></span>
HereisthemodifiedversionoftheProductview:
<body>
<div>
<label>ProductCode:</label>
<inputtype="text"data-bind="value:prodCode"/>
</div>
<div>
<label>ProductUnitPrice:</label>
<inputtype="text"data-bind="value:prodUnitPrice"/>
</div>
<div>
<label>ProductExpiry:</label>
<inputtype="text"data-bind="value:prodExpiry"/>
</div>
<div>
<label>ProductQuantity:</label>
<inputtype="text"data-bind="value:prodQuantity"/>
</div>
<div>
<inputid="btnSaveProduct"type="button"value="SaveProduct"/>
</div>
<scriptsrc="lib/knockout/dist/knockout.js"></script>
<scriptsrc="Js/ProductViewModel.js"></script>
<script>
(function(){
varprod=newProductViewModel();
document.getElementById("btnSaveProduct").onclick=function(){
prod.saveProduct();};
})();
</script>
</body>
ThisisallwhatweneedtoconfigureKnockoutintheProductview.WhenthebtnSaveProductbuttonisclicked,itwillcalculatethetotalamountandcalltheproductservicetosavetherecord.
Modifyingtestconfiguration
HereisthemodifiedversionofGruntfile.jscreatedearlier.WeaddedtheProductViewModel.jsandtheKnockoutdependencyinthefilesarray:
/*
Thisfileinthemainentrypointfordefininggrunttasksandusinggrunt
plugins.
*/
module.exports=function(grunt){
grunt.initConfig({
karma:{
unit:{
options:{
frameworks:['jasmine'],
singleRun:true,
browsers:['PhantomJS'],
files:[
'./wwwroot/lib/knockout/dist/knockout.js',
'./wwwroot/js/ProductViewModel.js',
'./wwwroot/test/**/product.test.js'
]
}
}
}
});
grunt.loadNpmTasks('grunt-karma');
grunt.registerTask('test',['karma']);
};
Modifyingtheproduct-testingscript
AswearenotdependentontheHTMLviewdirectly,wecantestourunittestcasesthroughtheProductviewmodel.Hereisthemodifiedversionofproduct.test.jsthatdoesnothaveanyofthefixturesdefined:
describe('Product',function(){
it('ExpectedTotalAmountshouldbe600',function(){
varproduct=newProductViewModel();
product.prodQuantity(3);
product.prodUnitPrice(200);
product.saveProduct();
expect(product.prodTotalAmount).toBe(600);
});
});
Thefollowingoutputwillbegeneratedwhenthetestisrun:
DebuggingJavaScriptJavaScriptrunsonclientbrowsers,andalmostallbrowsers,suchasInternetExplorer,MicrosoftEdge,Chrome,andFirefox,providetheintegratedJavaScriptdebuggerandDeveloperToolswindow.WithVisualStudio,wecanalsodebugtheJavaScriptcodebysettingInternetExplorerasthedefaultbrowser.Chromeisnotsupportedoutofthebox,butwithcertainsteps,itscanbeachieved.
DebuggingoptionsinVisualStudio2015VisualStudioprovidescertaindecentfeaturestodebugJavaScriptandtroubleshooterrors.JavaScriptdebugginginVisualStudioonlyworkswithInternetExplorer.DebuggingcanbestartedbystartingtheapplicationinadebugmodeandthenplacingsomebreakpointsintheJavaScriptcode.Whenthebreakpointishit,wecanuseallsortsofdebuggingoptionsinVisualStudiothatwealreadyknowofandusedindebuggingtheC#andVB.NETcode.OptionssuchasStepinto(F11),Stepover(F10),Stepout(Shift+F11),conditionalbreakpoints,andwatches,allworkwiththeJavaScriptcode.
DebuggingfromVisualStudiowithInternetExplorer
ThedefaultbrowserinVisualStudioforaparticularwebapplicationprojectcanbesetfromtheWebBrowser(InternetExplorer)|InternetExploreroption,asshowninthefollowingscreenshot:
DebuggingfromVisualStudiowithGoogleChromeVisualStudio2015doesnotprovideout-of-the-boxsupporttodebugJavaScriptapplications,exceptwithInternetExplorer.Alternatively,withNode.js,debuggingworksperfectlyfineinVisualStudio,andastechnically,bothNode.jsandGoogleChromearebasedontheV8engine,thereisnodrawback.
TostartdebuggingwithChromeinVisualStudio,wehavetoruntheGooglechrome.exefilewitharemote-debuggerargument.ThefollowingcommandrunsGoogleChromewithremotedebugging,andfromVisualStudio,itcanbeattachedbypointingtothesameChromeinstance:
chrome.exe–remote-debugging-port=9222
9222isthedefaultportwhereVisualStudioconnectsonattachingtoitsprocess.
FromtheVisualStudio,youcanattachtheprocessbyhittingCtrl+Alt+P,orbygoingtoDebug|AttachtoProcessinmenubarandselectingtheChromeinstance.
DeveloperToolsAlmostallthebrowserssupportbuilt-indevelopertoolsthathelpstodebugandtroubleshootJavaScripterrors.ThesetoolsarecommonlyknownasF12toolsandopensuptheDeveloperToolswindowbyhittingtheF12key.
DebuggingoptionsinMicrosoftEdge
MicrosoftEdgeisoneofthemostlightweightwebbrowserwiththelayoutenginebuiltaroundwebstandards.ItcontainssomenewfeaturessuchasCortana,annotationtools,andreadingmodethatgivesitanedgeoverotherbrowsers.
TheDeveloperToolswindowinMicrosoftEdgelooksimilartothefollowingimage:
Thefirstpaneinthetopleft-handcorneristheScriptpanethatshowsthecontentoftheJavaScriptcontainingthepage.
Thesecondpaneinthethetopright-handcornerisaWatchespanewherethevariablevaluesaredisplayed.
Thethirdpaneinthethebottomleft-handcorneristheconsolewindowwheremessagesaredisplayed,allthemessagesloggedusingconsole.log();callsareprintedontheconsolewindow.MicrosoftEdgeprovideserrors,warnings,andmessagesinthreedifferenttabsthat
giveaglimpseattheerrors,warnings,andmessages,anditalsohelpsthedevelopertodirectlyjumpintotheexactcodesnippetbyclickingontheerrorlinelink.
ThefourthpaneistheCallstackandBreakpoints.Callstackshowsthechainoffunctioncallsthatareexecutedanditishelpfultounderstandthecode-executionflow.Forexample,ifanA()methodcallsaB()method,andtheB()methodcallsaC()method,itshowsthecompleteflowofexecutionfromtheA()methodtotheC()method.
TheBreakpointstabshowsthelistofallthebreakpointsbeingusedinthescript,andtheuserscanmanagethesebreakpointsbyenablingordisablinganddeletingoraddingnewevents:
DebuggingcanonlystartiftheF12DevelopersToolswindowisopenedandthiscanbeopenedthroughthe…|F12DeveloperToolswindowoptionfrommenubarorbyhittingtheF12key.Oncethewindowisopened,youcansetbreakpointsontheJavaScriptcodeandtakespecificactionsonthepage.
Thefollowingtableshowssomeimportantoptionsavailableinthedebuggertoolbar:
Icon Option ShortcutKey Description
Continue F5orF8 Thisreleasesthebreakmodeandcontinuestillthenextbreakpoint.
Break Ctrl+Shift+B Thisbreaksonthenextstatement.
StepInto F11 Thisstepsintothefunctionbeingcalledorthenextstatement.
StepOver F10 Thisstepsoverthefunctionbeingcalledorthenextstatement.
StepOut Shift+F11
Thisstepsoutofthecurrentfunctionandintothecallingfunction.
Breakonnewworker
Ctrl+Shift+W Thisbreaksonthecreationofanewwebworker.
ExceptionControl
Ctrl+Shift+E
Thiscanbeusedtobreakonallexceptionsorunhandledexceptions.Bydefault,itissettoignoreexceptions.
DisconnectDebugger
Thisdisconnectsthedebuggerandnobreakpointsrun.
Debugjustmycode Ctrl+J Thisignoresthethird-partylibrariesfromdebugging.
Prettyprint Ctrl+Shift+P
ThissearchestheminifiedversionoftheJavaScriptblockandmakesitreadible.
Wordwrap Alt+W Thiswrapsthesentencetoadjustitasperthecontentpanesize.
MicrosoftEdgeprovidesthefollowingfivetypesofbreakpoints:
StandardConditionalTracepointsXHREvents
Standardbreakpoints
Thesebreakpointscanbesetbysimplyselectingthestatementfromthescriptcode:
Conditionalbreakpoints
Thesetypeofbreakpointsarehitwhenspecificconditionsaremetorwhenthevalueofthevariablereachesaspecificstate.Forexample,wecanusethiswithastatementinsidealoopandbreaktheexecutionwhenthecounterreachesavalueof10.
ItcanbesetbyclickingontheexistingbreakpointandselectingCondition...fromthecontextmenu:
ThisoptionopensuptheConditionalbreakpointwindowandtheconditioncanbesetasshowninthefollowingscreenshot:
Oncetheconditionisset,theiconchangesto
Tracepoints
Tracepointsareusedtowritethemessageontheconsolewhenitpassedthroughthestatementwherethetracepointisconfigured.ItcanbesetbyclickingontheInserttracepointoptionfromthecontextmenushowninthegutterbyright-clicking:
Oncethetracepointisset,theiconchanges,asfollows:
Whenthestatementisexecuted,itwillprintthemessageonaconsolewindowasshowninthefollowingscreenshot:
Event
MicrosoftEdgeprovidestheoptionofregisteringeventtracepointsandbreakpointsfromtheBreakpointspane.Aneventcouldbeamouseevent,keyboardevent,ortimerevent.Thisfeatureisheavilyusedinlargeorcomplexwebapplicationswheretheexactlocationof
specifyingthebreakpointisnotknown.Itisalsomoreusefulincaseswheretheeventhandlersarespecifiedatmultipleplaces.Forexample,ifapagecontainsfivebuttoncontrols,andweneedtobreaktheexecutionwheneveranybuttonraisestheclickevent,wecansimplyspecifythemouse-clickeventthroughthebreakpointevent;andwheneveranybuttoneventisraised,thebreakpointwillbeexecutedandwillfocusonthestatement.
Addeventtracepoint
Ausercanaddeventtracepointswiththehelpofthefollowingoption:
Thefollowingwindowshowstheregistrationofaneventtracepointwhenthemouseisclicked:
Addeventbreakpoints
Usercanaddeventbreakpointswiththehelpofthefollowingoption:
Thefollowingwindowshowstheregistrationofaneventbreakpointwhenthemouseisclicked:
XHR
Justlikeevents,XHReventscanalsoberegisteredfromtheBreakpointpaneofbrowser.TheseeventsareinvokedwhenanyoftheAjaxrequestisbeingmadefromtheJavaScriptcode.AusercanregistertheXHReventfromtheiconshowninthefollowingscreenshot:
Onceweclickonthisevent,itwillbeaddedintheBreakpointswindow,asshowninthefollowingscreenshot:
DebuggingTypeScriptInChapter5,DevelopinganASP.NETApplicationUsingAngular2andWebAPI,wealreadydiscussedTypeScriptandhowittranspilesintotheJavaScriptcodethateventuallyrunsonthebrowser.ThedeveloperswritecodeinTypeScript,butonthebrowser,ageneratedJavaScriptfileisrun.WhentheTypeScriptfileistranspiledtoaJavaScriptfile,amappingfileisgeneratedwitha*.map.jsextension.ThisfilecontainstheinformationabouttheactualTypeScriptfileandthegeneratedJavaScriptfile.Notonlythis,butthegeneratedJavaScriptfilealsocontainsoneentryaboutthemappingfilethatactuallytellsthebrowserstoloadthecorrespondingsourceTypeScriptfilebyreadingthemappingfile.
HereistheentrythateverygeneratedJavaScriptfilecontainswhenitistranspiledfromTypeScript:
//#
sourceMappingURL=http://localhost:12144/todosapp/apps/createTodo.component.js.map
ThiscanbeconfiguredfromtheTSConfig.jsonfilethroughthesourceMapproperty.IfthesourceMappropertyistrue,itgeneratesthemappingfileandmakesanentryinthegeneratedJavaScriptfile.Also,whenworkinginanASP.NETCoreapplication,allthestaticfileshavetobeinthewwwrootfolder.So,todebugthetypescripts,allthecorrespondingtypescript(.ts)fileshavetobemovedtoanyfolderunderthewwwrootfoldersothatitcanbeaccessiblefromthebrowser.
Hereisthedebuggerwindow,showingthelistofTypeScriptfilesontheleft-handsideandtheiconintheupper-rightcornertotogglebetweenthesourcefileandcompiledJavaScriptversion:
DebuggerkeywordsupportedbyallbrowsersWecanalsoexplicitlyforcetobreakthecontrolatsomepointthroughthedebuggerkeyword.Ifthebreakpointisnotset,butthedebuggerkeywordisspecified,thedebuggingwillbeenabledandbreaktheexecution.Itcanbesetfromcodeasshowninthefollowingscreenshot:
SummaryInthischapter,wediscussedhowJavaScriptapplicationscanbetestedanddebugged.FortestingJavaScriptapplications,wediscussedtheJasminetestingsuitethatcanbeeasilypluggedinwithKarma,whichisatestrunnerandcanbeusedwithGrunttobeexecutedfromVisualStudioTaskRunnerExplorerwindow.WealsodiscussedthebasicsoftheMVVMpatternandhowtoimplementitusingtheKnockoutJavaScriptlibrary.WethenmodifiedthetestcasetoworkwiththeViewmodel.Fordebugging,wediscussedsometipsandtechniquesofdebuggingJavaScriptwithVisualStudioandwhatMicrosoftEdgeoffersthroughtheDeveloperToolswindowtomakedebuggingeasy.Intheend,wealsolearnedaboutthebasictopicssuchashowMicrosoftEdgeenablesdebuggingforTypeScriptfilesandwhatconfigurationsarerequiredtoachieveit.
IndexA
abstractfactorypatternabout/Abstractfactorypattern
AbstractSyntaxTree(AST)/CompilationarchitectureofTypeScriptaccesscontrolkeys
Access-Control-Allow-Origin/CallingWCFservicesfromJavaScriptAccess-Control-Allow-Headers/CallingWCFservicesfromJavaScriptAccess-Control-Allow-Method/CallingWCFservicesfromJavaScriptAccess-Control-Max-Age/CallingWCFservicesfromJavaScript
adapterpatternabout/Adapterpattern
AJAXeventsabout/Ajaxevents
Ajaxeventslocalevents/Localeventsglobalevents/Globalevents
AJAXpropertiesaccepts/Ajaxpropertiesasync/Ajaxpropertiescache/Ajaxpropertiescontents/AjaxpropertiescontentType/AjaxpropertiescrossDomain/Ajaxpropertiesdata/AjaxpropertiesdataType/Ajaxproperties
AJAXrequestoptions,URL/Pre-filteringAjaxrequestsdefaultvalues,setting/SettingdefaultvaluesforallfutureAjaxrequests
AjaxrequestXHRobject,using/AjaxrequestsusingtheclassicXHRobjectcreating,jQueryused/MakinganAjaxrequestusingjQuery
AJAXrequest,objectsoptions/Pre-filteringAjaxrequestsoriginalOptions/Pre-filteringAjaxrequestsjqXHR/Pre-filteringAjaxrequests
Angular2about/IntroductiontoAngular2architecture/Angular2architecturecomponentlifecycleevents/Eventsofcomponentlifecyclemodules/Modulescomponents/Components
dependencyinjection/DependencyinjectioninAngularrouting/RoutinginAngular
ApplicationProgrammingInterfaces(APIs)/WebapplicationswithNode.jsarithmeticoperators
about/Arithmeticoperatorsarray
about/ArrayinJavaScriptASP.NETapplication
WinJSlibrary,adding/AddingtheWinJSlibraryintheASP.NETapplicationWinJS,using/UsingWinJSintheASP.NETapplication
ASP.NETCoreto-doapplication,developing/Developingato-doapplicationinASP.NETCoreJSDoc3,installing/InstallingJSDoc3inASP.NETCore
ASP.NETcoreapplicationcreating/CreatingtheASP.NETcoreapplication
assignmentoperatorsabout/Assignmentoperators
AsynchronousJavaScriptandXML(Ajax)about/IntroducingAjaxworking/HowAjaxworksproperties/AjaxpropertiesAJAXrequest,pre-filtering/Pre-filteringAjaxrequestsdataloading,throughgetfunctions/LoadingdatathroughthegetfunctionsinjQuerypostfunction,usedfordataposting/Postingdatatoserverusingthepostfunction
Bbehavioralpatterns
about/Behavioralpatternchainofresponsibility/Chainofresponsibilitypatternobserver/Observerpatternpub/sub/Pub/subpatternpromises/Promises
bitwiseoperatorsabout/BitwiseoperatorsbitwiseAND/BitwiseANDbitwiseOR/BitwiseORbitwiseNOT/BitwiseNOTbitwiseXOR/BitwiseXOR
bitwiseshiftoperatorsabout/Bitwiseshiftoperatorsbitwiseleftshift/Bitwiseleftshiftbitwiserightshift/Bitwiserightshift
blankNode.jsapplicationscreating/CreatingblankNode.jsapplications
BODMASrule/Groupingoperatorbridgepattern
about/Bridgepatternbrowserobjects
about/BrowserObjectModelsinJavaScriptwindow/Windowdocument/Documentnavigator/Navigatorscreen/Screenhistory/Historylocation/Location
built-indisplaymethods,JavaScriptabout/Built-indisplaymethodsinJavaScriptmessages,displaying/Displayingmessageswindow.alert()/Alertboxwindow.confirm()/Confirmboxdocument.write()method,forwritingonpage/Writingonapageconsole.log()method,forwritingintobrowser'sconsolewindow/Writingintothebrowser'sconsolewindow
CCDNlibrary/CDNchainofresponsibilitypattern
about/ChainofresponsibilitypatternCommonLanguageRuntime(CLR)/Comparingruntimescomparisonoperators
about/Comparisonoperatorsstrictequaloperator/Strictequaloperatorstrictnotequaloperator/Strictnotequaloperator
complextypesabout/Datatypes
componentlifecycleevents,Angular2ngOnInit()/EventsofcomponentlifecyclengOnDestroy()/EventsofcomponentlifecyclengDoCheck()/EventsofcomponentlifecyclengOnChanges(changes)/EventsofcomponentlifecyclengAfterContentInit()/EventsofcomponentlifecyclengAfterContentChecked()/EventsofcomponentlifecyclengAfterViewInit()/EventsofcomponentlifecyclengAfterViewChecked()/Eventsofcomponentlifecycle
conditionalbreakpointsabout/Conditionalbreakpoints
configurationattributes,unittestsframeworks/AddingKarmaspecificationssingleRun/AddingKarmaspecificationsbrowsers/AddingKarmaspecificationsfiles/AddingKarmaspecifications
containsselectorabout/ThecontainsselectorinjQuery
contentdeliverynetwork(CDN)using/Usingacontentdeliverynetwork,TheuseofCDNabout/Usingacontentdeliverynetwork
corebenefits,TypeScriptabout/AdvantagesofTypeScriptsupersetofJavaScript/SupersetofJavaScriptclassesandmodules,supportfor/Supportforclassesandmodulesstatictypechecking/StatictypecheckingECMAScript6featuresupport/ECMAScript6featuresupportoptionaltyping/Optionaltypingtypes,declaring/DeclaringtypesinTypeScript
coreelements,TypeScriptabout/CoreelementsofTypeScriptvariabledeclaration/Declaringvariables
types/Typesinterfaces/Classesandinterfacesclasses/Classesandinterfacesinterfaces,defining/Defininginterfacesclassesandinterfaces,deriving/Derivingclassesandinterfacesgenericclasses/Genericclassesfunctions/Functionsgenericfunctions/Genericfunctionsiterators/Iteratorsmodules/Modulesandnamespacesnamespaces/Modulesandnamespaces
corefundamentals,ofJavaScriptabout/CorefundamentalsofJavaScriptJavaScript,addingtoHTMLpage/AddingJavaScripttoanHTMLpagestatements/StatementsinJavaScriptliterals/Literalsandvariablesvariables/Literalsandvariablesdatatypes/DatatypesJavaScriptObjectNotation(JSON)/WhatisJSON?
coreproperties,Angular2componentsabout/CorepropertiesofAngular2componentstemplates/Templatesandselectorsselectors/Templatesandselectorsinputs/Inputsandoutputsoutputs/Inputsandoutputsinputs,using/Usinginputsoutputs,using/Usingoutputsdirectives/Directivesproviders/Providers
CORSabout/CORSpolicy,specifyingatserviceslevel/SpecifyingtheCORSpolicyatserviceslevelenabling,atConfiguremethod/EnableCORSattheConfiguremethod
Create,Read,UpdateandDelete(CRUD)operations/CreatingaTodoServiceAppprojectcreationalpatterns
about/Creationalpatternssingleton/Singletondesignpatternfactory/Factorypatternabstractfactory/Abstractfactorypatternprototype/Prototypepattern
cross-originrequestsabout/Cross-originrequestsJavaScriptObjectNotationPadding(JSON-P)/JSON-P
CORS/CORSCRUD(create,retrieve,update,anddelete)operations
about/Modularization
Ddata
posting,postfunctionused/Postingdatatoserverusingthepostfunctiondatabinding
about/Databindingonetimedatabinding/Onetimedatabindingonewaydatabinding/Onewaydatabindingtwowaydatabinding/Twowaydatabindingworkingmodel/Adatabindingworkingmodel
dataloadingthroughgetfunctions,injQuery/LoadingdatathroughthegetfunctionsinjQuery
datatypesabout/Datatypesprimitivetypes/Datatypescomplextypes/Datatypesconversions/Conversionsindatatypes
debuggerkeywordabout/Debuggerkeywordsupportedbyallbrowsers
debuggingJavaScript/DebuggingJavaScriptinVisualStudio2015/DebuggingoptionsinVisualStudio2015fromVisualStudio,withInternetExplorer/DebuggingfromVisualStudiowithInternetExplorerfromVisualStudio,withGoogleChrome/DebuggingfromVisualStudiowithGoogleChromewithDevelopersTools/DeveloperToolsinMicrosoftEdge/DebuggingoptionsinMicrosoftEdgewithTypeScript/DebuggingTypeScript
decoratorpatternabout/Decoratorpattern
deleteoperatorabout/Thedeleteoperator
dependencies,TodoWebAppprojectabout/Dependenciesangular2/Dependenciessystemjs/Dependenciesreflect-metadata/Dependenciesrxjs/Dependencieszone.js/Dependenciesdevelopmentdependencies/Developmentdependencies,ConfiguringTypeScriptGulp,configuring/ConfiguringGulp
dependencyinjection,inAngularabout/DependencyinjectioninAngular
DevelopersToolsabout/DeveloperToolsdebuggingoptions,inMicrosoftEdge/DebuggingoptionsinMicrosoftEdge
directivesabout/Directivescomponents/Directivesstructuraldirective/Directives,Structuraldirectivesattributedirective/Directives,AttributedirectivesimpleHelloWorlddirective,creating/CreatingasimpleHelloWorlddirective
documentationgenerating/GeneratingdocumentationJSDoc3,installinginASP.NETCore/InstallingJSDoc3inASP.NETCore
documentobjectabout/Document
DocumentObjectModel(DOM)about/ImportanceofJavaScript
documentobjectmodel(DOM)/UsingWinJSintheASP.NETapplicationdocumentreadyevent
about/ThedocumentreadyeventDOMelements
selecting,IDused/SelectingtheDOMelementsusingtheIDselecting,TagNameused/SelectingtheDOMelementsusingTagNameselecting,byattributevalue/Selectingbytheattributevaluemanipulating/ManipulatingDOMproperties,modifying/Modifyinganelement'spropertiesURL,formethods/Modifyinganelement'spropertiesgetmethod/Modifyinganelement'spropertiessetmethod/Modifyinganelement'spropertiescreating/Creatingnewelementsattributes,removing/Removingelementsandattributesremoving/Removingelementsandattributes
DOMelements,methodsappend()/CreatingnewelementsappendTo()/Creatingnewelementsbefore()/Creatingnewelementsafter()/CreatingnewelementsinsertAfter()/CreatingnewelementsinsertBefore()/Creatingnewelementsprepend()/Creatingnewelementsdetach()/Removingelementsandattributesremove()/RemovingelementsandattributesremoveAttr()/RemovingelementsandattributesremoveClass()/RemovingelementsandattributesremoveProp()/Removingelementsandattributes
DOMelements,methodsempty()/Removingelementsandattributes
EEJSviewengine
about/EJSviewengineelements,ofJavaScript
about/ElementsofJavaScriptconstants/ConstantsinJavaScriptcomments/Commentscasesensitivity/Casesensitivitycharacterset/Characterset
entity-relationshipmodel(ERD)about/Inheritance
environmentsettingup/Settingupyourenvironment
event,MicrosoftEdgeeventtracepoint,adding/Addeventtracepointeventbreakpoints,adding/Addeventbreakpoints
event-drivenmessagingabout/Event-drivenmessagingmediatorpattern,implementing/Implementingmediatorpatternforcommunicationbetweenmodulescomplexcode,encapsulating/Encapsulatingcomplexcode
eventsabout/EventsinJavaScripthandling,injQuery/EventhandlinginjQueryregistering,injQuery/RegisteringeventsinjQueryclick()/RegisteringeventsinjQuerydblclick()/RegisteringeventsinjQuerymousedown()/RegisteringeventsinjQuerymouseup()/RegisteringeventsinjQuerymouseenter()/RegisteringeventsinjQuerymouseleave()/RegisteringeventsinjQuerykeydown()/RegisteringeventsinjQuerykeyup()/RegisteringeventsinjQueryfocus()/RegisteringeventsinjQueryblur()/RegisteringeventsinjQuerychange()/RegisteringeventsinjQueryURL/RegisteringeventsinjQuerybinding,onandoffused/Bindingeventsusingonandoffhoverevents,using/Usingthehoverevents
/EventsinWinJSexceptionhandling,OOP
about/ExceptionhandlingError/Error
RangeError/RangeErrorReferenceError/ReferenceErrorTypeError/TypeErrorURIError/URIError
Expressframeworkforwebapplications,inNode.jsusing/UsingtheExpressframeworkforwebapplicationsinNode.jssimpleNode.js,extending/ExtendsimpleNode.jstouseExpressExpressviewengines/Expressviewengines
expressionsabout/Expressionsthiskeyword/Thethiskeywordsequence,ofcodeexecution/SequenceofcodeexecutioninJavaScriptthiskeyword,usingoncallingmethod/Usingthethiskeywordonacallingmethodfunctionstatement/Thefunctionstatementandexpressionfunctionexpression/Thefunctionstatementandexpressionclassstatement/Classstatementandexpressionclassexpression/Classstatementandexpressiongroupingoperator/Groupingoperatornewkeyword/newsuperkeyword/super
Expressviewenginesabout/ExpressviewenginesEJSviewengine/EJSviewengineJadeviewengine/Jadeviewenginerouting,inExpressapplication/RoutingintheExpressapplication
Ffactorypattern
about/Factorypatternfaçadepattern
about/Facadepatternfunctionarguments
about/Functionarguments
Ggetfunctions
used,fordataloading/LoadingdatathroughthegetfunctionsinjQueryjQuery.get(),using/UsingjQuery.get()jQuery.getJSON(),using/UsingjQuery.getJSON()jQuery.getScript(),using/UsingjQuery.getScript()
globalevents,AJAXajaxStart/GlobaleventsajaxSend/GlobaleventsajaxSuccess/GlobaleventsajaxError/GlobaleventsajaxComplete/Globalevents
GoogleChromeused,fordebuggingfromVisualStudio/DebuggingfromVisualStudiowithGoogleChrome
Gruntused,forwritingunittests/Grunt
Gruntfileadding/AddingtheGruntfileKarmaspecifications,adding/AddingKarmaspecificationsnpmtask,loading/Loadnpmtasktask,registering/Registertask
GulpmoduleGulp/Deploymentoptimizationgulp-concat/Deploymentoptimizationgulp-cssmin/Deploymentoptimizationgulp-uglify/Deploymentoptimization
Hhistoryobject
about/Historymethods/Methods
hovereventsusing/Usingthehoverevents
IID
used,forselectingDOMelements/SelectingtheDOMelementsusingtheIDIIFE(immediatelyinvokedfunctionexpression)
about/ImplementingthemodulepatternindexOf()method
about/ArrayinJavaScriptinheritance
about/Inheritanceconstructors,chaininginJavaScript/ChainingconstructorsinJavaScriptObject.create()used/InheritanceusingObject.create()defining,classused/Defininginheritanceusingclass
inputelementsselecting/Selectinginputelements
IntegratedDevelopmentEnvironment(IDE)/UsingNode.jswithVisualStudio2015IntermediateLanguage(IL)code/ComparingruntimesInternetExplorer
used,fordebuggingfromVisualStudio/DebuggingfromVisualStudiowithInternetExplorer
JJadelanguage
URL/JadeviewengineJadeviewengine
about/JadeviewengineJasmine
used,forwritingunittests/Jasminedescribe()function/Addingunittestscriptfileit()function/Addingunittestscriptfileexpect()function/Addingunittestscriptfile
JavaScriptimportance/ImportanceofJavaScriptabout/WhatisJavaScript?runtimes,comparing/Comparingruntimesprogramming/ProgramminginJavaScriptWCFservices,calling/CallingWCFservicesfromJavaScriptdebugging/DebuggingJavaScript
JavaScriptcodemodularizing,throughRequireJS/ModularizingJavaScriptcodethroughRequireJStesting/TestingtheJavaScriptcode
JavaScriptdesignpatternscreational/Creationalpatternsstructural/Structuralpatternsbehavioral/Behavioralpattern
JavaScriptObjectNotationPadding(JSON-P)about/JSON-Pusing/UsingJSON-P
JavaScriptpropertydescriptorsabout/JavaScriptpropertydescriptorswritable/JavaScriptpropertydescriptorsenumerable/JavaScriptpropertydescriptorsconfigurable/JavaScriptpropertydescriptorsdisplaying/Displaypropertydescriptorsmanaging/Managingpropertydescriptorsgettersandsetters,using/Usinggettersandsetters
JavaScripttypedarraysabout/JavaScripttypedarraysarchitecture/Typedarrayarchitecturearraybuffer/Thearraybufferbuffer,creating/Creatingabuffer
jQueryURL/GettingstartedwithjQuery
about/GettingstartedwithjQuerycontentdeliverynetwork(CDN),using/Usingacontentdeliverynetworkdocumentreadyevent/Thedocumentreadyeventselectors/ThejQueryselectorsevents,handling/EventhandlinginjQueryevents,registering/RegisteringeventsinjQueryused,forcreatingAjaxrequest/MakinganAjaxrequestusingjQuery.ajax()/jQuery.ajax()dataloading,throughgetfunctions/LoadingdatathroughthegetfunctionsinjQuery
jQuery.get()functionusing/UsingjQuery.get()
jQuery.getJSON()functionusing/UsingjQuery.getJSON()
jQuery.getScript()functionusing/UsingjQuery.getScript()
jQueryselectorsabout/ThejQueryselectorsDOMelements,selectingIDused/SelectingtheDOMelementsusingtheIDDOMelements,selectingTagNameused/SelectingtheDOMelementsusingTagNamenodes,selectingbyclassname/SelectingnodesbytheclassnameDOMelements,selectingbyattributevalue/Selectingbytheattributevalueinputelements,selecting/Selectinginputelementselements,selecting/Selectingalltheelementsfirstchildelements,selecting/Selectingthefirstandlastchildelementslastchildelements,selecting/Selectingthefirstandlastchildelementscontainsselector/ThecontainsselectorinjQueryoddrowsselector,selecting/Selectingtheevenandoddrowsselectorsevenrowsselector,selecting/Selectingtheevenandoddrowsselectors
JSDoc3installing,inASP.NETCore/InstallingJSDoc3inASP.NETCorecomments,adding/AddingcommentsURL/Addingcomments
JSONabout/WhatisJSON?simpleobjects/SimpleobjectsinJSONarrays,declaring/DeclaringarraysinJSONdata,nesting/NestingdatainJSON
just-in-time(JIT)compilation/Comparingruntimes
KKarma
used,forwritingunittests/KarmaURL,forconfigurationfile/AddingKarmaspecifications
Knockoutused,forimplementingModel-View-ViewModel(MVVM)/ImplementingModel-View-ViewModelusingKnockoutandRuntestpackage,adding/AddingtheKnockoutpackageProductViewModel,adding/AddingProductViewModelProductview,adding/AddtheProductviewtestconfiguration,modifying/Modifyingtestconfigurationproduct-testingscript,modifying/Modifyingtheproduct-testingscript
Llargescaleapplication
designing/ThinkbeforeproceedinglastIndexOf()method
about/ArrayinJavaScriptlet
used,fordeclaring/Declaringletconditions/Conditionswhereletisefficienttousefunctionsinloops/Functionsinloops
loadtestingabout/TestingtheJavaScriptcode
localevents,AJAXbeforeSend/Localeventssuccess/Localeventserror/Localeventscomplete/Localevents
locationobjectabout/Locationproperties/Propertiesmethods/Methods
logicaloperatorsabout/LogicaloperatorsLogicalAND/LogicalANDLogicalOR/LogicalORLogicalNOT/LogicalNOT
Mmainto-dopage,TodoWebAppproject
creating/Creatingthemainto-dopagecustomto-dotaghelper,creating/Creatingacustomto-dotaghelper-doMVCcontroller,adding/Addingato-doMVCcontrollerviews,generatingforTodoControlleractionmethods/GeneratingviewsfortheTodoControlleractionmethodsCreateTodocomponent,developing/DevelopingtheCreateTodocomponent
mediatorpatternimplementing,forcommunicationbetweenmodules/Implementingmediatorpatternforcommunicationbetweenmodules
messages,displayingalertbox/Alertboxconfirmbox/Confirmboxpromptbox/Promptbox
methods,OOPabout/Methodsdefining,objectliteralnotationused/Definingmethodsthroughobjectliteralnotationapproachdefining,constructorfunctionused/Definingobjectsusingtheconstructorfunctionapproachextending/Extendingpropertiesandmethods
MicrosoftEdgedebuggingoptions/DebuggingoptionsinMicrosoftEdgestandardbreakpoints/Standardbreakpointsconditionalbreakpoints/Conditionalbreakpointstracepoints/Tracepointsevent/EventXHR/XHR
MicrosoftSQLserver,accessinginNode.jsabout/AccessingtheMicrosoftSQLserverinNode.jsrecord,reading/ReadingarecordfromtheMicrosoftSQLserverdatabaserecord,creating/CreatingarecordintheMicrosoftSQLserverdatabase
Middlewareabout/Middleware
miscellaneousoperatorsconditionaloperators/Conditionaloperatorsspreadoperator/Spreadoperator
mixin/MixinModel-View-ViewModel(MVVM)
implementing,Knockoutused/ImplementingModel-View-ViewModelusingKnockoutandRuntest
modularization
about/Modularizationmodulepattern,implementing/ImplementingthemodulepatternJavaScriptcode,modularizingthroughRequireJS/ModularizingJavaScriptcodethroughRequireJS
modulescreating,RequireJSAPIused/CreatingmodulesusingtheRequireJSAPIdefining,inCommonJSstyle/CreatingmodulesusingtheRequireJSAPIdefining,inAMDstyle/CreatingmodulesusingtheRequireJSAPI
mssqlnodepackagereference/CreatingarecordintheMicrosoftSQLserverdatabase
MVC,withExpressframeworkabout/MVCwiththeExpressframeworkMVCpattern/MVCpatterncontroller,creating/Creatingacontrollerdataservices,creating/Creatingdataservices
Nnamespace/NamespacesinWinJSnavigatorobject
about/Navigatorproperties/Properties
Node.jsabout/IntroductiontoNode.jsversus.NET/ComparisonofNode.jswith.NETNodePackageManager(NPM)/NPMinstalling/InstallingNode.jsURL/InstallingNode.jsusing,withVisualStudio2015/UsingNode.jswithVisualStudio2015forsimpleconsoleapplication/SimpleconsoleapplicationusingNode.jsblankNode.jsapplications,creating/CreatingblankNode.jsapplicationsExpressframework,usingforwebapplications/UsingtheExpressframeworkforwebapplicationsinNode.jsMicrosoftSQLserver,accessing/AccessingtheMicrosoftSQLserverinNode.js
Node.jswebserverrequest,processingby/RequestprocessingbytheNode.jswebserver
nodepackagemanager(npm)about/AddingKarmaspecifications
NodePackageManager(NPM)/AddingtheWinJSlibraryintheASP.NETapplicationnodes
selecting,byclassname/SelectingnodesbytheclassnameNPM/NPMNuGet/NuGet
Oobject
about/Datatypesobject-orientedprogramming(OOP)
about/Object-orientedprogramminginJavaScriptobjects,creating/Creatingobjectsprivateandpublicmembers/Privateandpublicmembersinheritance/Inheritanceencapsulation/Encapsulationabstraction/Abstractionnew.targetproperty/new.targetnamespace/Namespaceexceptionhandling/Exceptionhandlingclosures/Closuresclosures,practicaluse/PracticaluseJavaScripttypedarrays/JavaScripttypedarraysmaps/Maps,sets,weakmaps,andweaksetsmapsandweakmaps/Mapsandweakmapssetsandweaksets/Setsandweaksetsstrictmode/Thestrictmode
Object.create()used,forinheritance/InheritanceusingObject.create()predefinedproperties/PredefinedpropertiesofObject.create()
objects,OOPcreating/Creatingobjectsdefining,objectliteralnotationused/Definingobjectsusingobjectliteralnotationdefining,constructorpatternused/Definingobjectsusingaconstructorpatternclasskeyword,using/Usingtheclasskeyword
observerpatternabout/Observerpattern
onetimedatabinding/Onetimedatabindingonewaydatabinding/Onewaydatabindingopenauthentication(OAuth)/Mapsandweakmapsoperators
about/Operatorsassignmentoperators/Assignmentoperatorsarithmeticoperators/Arithmeticoperatorsunaryoperators/Unaryoperatorscomparisonoperators/Comparisonoperatorslogicaloperators/Logicaloperatorsbitwiseoperators/Bitwiseoperatorsbitwiseshiftoperators/Bitwiseshiftoperatorstypeofoperator/Thetypeofoperator
voidoperator/Thevoidoperatordeleteoperator/Thedeleteoperatormiscellaneousoperators/Miscellaneousoperators
Ppop()method
about/ArrayinJavaScriptpostfunction
used,fordataposting/Postingdatatoserverusingthepostfunctionprimitivetypes
Boolean/Datatypesnull/Datatypesundefined/Datatypesnumber/Datatypesstring/Datatypes
programming,inJavaScriptabout/ProgramminginJavaScriptcorefundamentals/CorefundamentalsofJavaScriptelements/ElementsofJavaScriptexpressions/Expressionsoperators/Operatorsbuilt-indisplaymethods/Built-indisplaymethodsinJavaScriptBrowserObjectModels/BrowserObjectModelsinJavaScript
promisesabout/Promises,Promisesoperations/Otheroperationsofpromiseschaining/Chainingpromisesandhandlingerrorserrors,handling/Chainingpromisesandhandlingerrorscancelling/Cancelingpromisesjoining/Joiningpromiseschecking/Checkingpromisenon-promise,wrappingintopromise/Wrappingnon-promiseintopromise
properties,OOPabout/Propertiesdefining,objectliteralnotationused/Definingpropertiesusingobjectliteralnotationdefining,constructorpatternused/Definingpropertiesusingaconstructorpatterndefining,setters/gettersused/Definingpropertiesusingsetters/gettersinECMAScript6JavaScriptpropertydescriptors/JavaScriptpropertydescriptorsextending/Extendingpropertiesandmethods
prototypepatternabout/Prototypepattern
pub/subpatternabout/Pub/subpattern
push()methodabout/ArrayinJavaScript
RRequireJS
JavaScriptcode,modularizing/ModularizingJavaScriptcodethroughRequireJSAPI,usedforcreatingmodules/CreatingmodulesusingtheRequireJSAPIbootstrapping/BootstrappingRequireJS
reverse()methodabout/ArrayinJavaScript
routing,inAngularabout/RoutinginAngular
Sscalableandmaintainableapplications
developing/Developinghighlyscalableandmaintainableapplicationsmodularization/Modularizationevent-drivenmessaging/Event-drivenmessagingdocumentation,generating/Generatingdocumentationdeploymentoptimization/Deploymentoptimization
screenobjectabout/Screenproperties/Properties
shift()methodabout/ArrayinJavaScript
simpleconsoleapplicationNode.jsused/SimpleconsoleapplicationusingNode.js
singletondesignpatternabout/Singletondesignpattern
splice()methodabout/ArrayinJavaScript
standardbreakpointsabout/Standardbreakpoints
structuralpatternsabout/Structuralpatternsadapter/Adapterpatterndecorator/Decoratorpatternfaçade/Facadepatternbridge/Bridgepattern
TTagName
used,forselectingDOMelements/SelectingtheDOMelementsusingTagNametesting
unittesting/Unittestingto-doapplication,developinginASP.NETCore
about/Developingato-doapplicationinASP.NETCoreCommonproject,creating/CreatingaCommonprojectTodoServiceAppproject,creating/CreatingaTodoServiceAppprojectTodoWebAppproject,creating/CreatingaTodoWebAppproject
TodoServiceAppprojectcreating/CreatingaTodoServiceAppprojectMVC,enabling/EnablingMVCinaWebAPIprojectEntityFramework,installing/InstallingEntityFrameworkAppSettings,addingforstoringconnectionstring/AddingAppSettingstostoreaconnectionstringAppSettings,configuringinStartupclass/ConfiguringAppSettingsintheStartupclassdataaccess,addinginWebAPI/AddingdataaccessinWebAPICORS,enablinginASP.NETWebAPI/EnablingCORSintheASP.NETWebAPIdatabasemigration,running/Runningdatabasemigrationcontroller,creating/Creatingacontroller
TodoWebAppprojectcreating/CreatingaTodoWebAppprojectAngular2,configuring/ConfiguringAngular2intheTodoWebAppprojectdependencies/DependenciesAngularcomponents,adding/AddingAngularcomponents-doservicecomponent,adding/Addingtheto-doservicecomponentto-doviewcomponent,adding/Addingato-doviewcomponentmainto-dopage,creating/Creatingthemainto-dopage
toString()methodabout/ArrayinJavaScript
tracepointsabout/Tracepoints
twowaydatabinding/Twowaydatabindingtypeofoperator
about/Thetypeofoperatorvalues/Thetypeofoperator
TypeScriptabout/TypeScriptcompilationarchitecture/CompilationarchitectureofTypeScriptbenefits/AdvantagesofTypeScriptcoreelements/CoreelementsofTypeScript
reference/Modulesandnamespacesdebugging/DebuggingTypeScript
Uunaryoperators
about/Unaryoperatorsdecrement/Unaryoperatorsincrement/Unaryoperatorslogicalcomplementoperator/Unaryoperators
unittestingabout/Unittesting
unittestswriting/Writingunittestswriting,withJasmine/Jasminewriting,withKarma/Karmawriting,withGrunt/Gruntdeveloping/DevelopingunittestusingJasmine,Karma,andGruntpackages,adding/AddingpackagesGruntfile,adding/AddingtheGruntfilesourceJavaScriptfile,using/SourceJavaScriptfilescriptfile,adding/Addingunittestscriptfiletesttask,executing/RunningtesttaskModel-View-ViewModel(MVVM),implementing/ImplementingModel-View-ViewModelusingKnockoutandRuntestexecuting/ImplementingModel-View-ViewModelusingKnockoutandRuntest
UniversalAppPlatform(UAP)/UsageofWinJSUniversalWindowsPlatform(UWP)/UsageofWinJSunshift()method
about/ArrayinJavaScriptuseracceptancetesting(UAT)
about/Unittestinguserinterface(UI)/ImportanceofJavaScript,IntroductiontoWinJSuserinterface(UI)testing/TestingtheJavaScriptcode
Vvariables
about/Variables–scopeandhoistinghoisting/Variables–scopeandhoistinglet,fordeclaring/Declaringlet
variablesscope/Variables–scopeandhoistingVisualStudio
existingWindowsapptemplate/ExistingWindowsapptemplateinVisualStudiodebuggingfrom,withInternetExplorer/DebuggingfromVisualStudiowithInternetExplorerdebugging,withGoogleChrome/DebuggingfromVisualStudiowithGoogleChrome
VisualStudio2015debuggingoptions/DebuggingoptionsinVisualStudio2015
VisualStudio2015communityeditionURL/Settingupyourenvironment
VisualStudio2015IDEneweditingexperience,ofJavaScript/NeweditingexperienceofJavaScriptinVisualStudio2015IDEimprovements/NeweditingexperienceofJavaScriptinVisualStudio2015IDE
voidoperatorabout/Thevoidoperator
WWCFservices
calling,fromJavaScript/CallingWCFservicesfromJavaScriptwindowobject
about/WindowWindowsruntimefeatures
using/UsingWindowsruntimefeatureshostedapps/Hostedappsandaccessingthecameracamera,accessing/HostedappsandaccessingthecameraASP.NETcoreapplication,creating/CreatingtheASP.NETcoreapplicationASP.NETapplicationconvertingintowindowsapplication,Hostedappconceptused/ConvertinganASP.NETapplicationintoWindowsapplicationusingtheHostedappconcept
WinJSabout/IntroductiontoWinJSuses/UsageofWinJSstarting/GettingstartedwithWinJSusing,inASP.NETapplication/UsingWinJSintheASP.NETapplicationexistingWindowsapptemplate,inVisualStudio/ExistingWindowsapptemplateinVisualStudiocontrols,adding/AddingWinJScontrolscontrols,propertiessetting/SettingpropertiesofWinJScontrols
WinJS,corefundamentalsexploring/ExploringWinJScorefundamentalsclasses/Classesandnamespacesnamespaces/Classesandnamespaces,NamespacesinWinJSclasses,defininginWinJS/DefiningclassesinWinJSclasses,derivinginWinJS/DerivingclassesinWinJSmixin/Mixinevents/EventsinWinJSDatabinding/Databinding
WinJS,featuresabout/WinJSfeaturesJavaScriptcodingandlanguagepatterns/JavaScriptcodingandlanguagepatternsstylesheets/StylesheetsWindowsruntimeaccess/Windowsruntimeaccesssecurity/SecurityAppmodel/AppmodelDatabinding/Databindingcontrols/Controlsutilities/Utilities
WinJSlibrary,addinginASP.NETapplicationabout/AddingtheWinJSlibraryintheASP.NETapplication
CDNlibrary/CDNNPM/NPMNuGet/NuGet
XXHR
about/XHRXHRobject
used,forAJAXrequest/AjaxrequestsusingtheclassicXHRobjectmethods/XHRmethodsevents/XHReventsproperties/XHRproperties
XHRobject,headersCookie/XHRmethodsHost/XHRmethodsConnection/XHRmethodsAccept/XHRmethodsAccept-charset/XHRmethodsAccept-encoding/XHRmethodsAccept-language/XHRmethodsUser-Agent/XHRmethodsReferer/XHRmethods
XHRobject,methodsrequest,sending/XHRmethodsvoidsend()/XHRmethodsvoidsend(DOMString?Data)/XHRmethodsvoidsend(Documentdata)/XHRmethodsvoidsend(Blobdata)/XHRmethodsvoidsend(FormDatadata)/XHRmethodsrequest,aborting/XHRmethodsrequestheaders,setting/XHRmethodsresponseheaders,obtaining/XHRmethods
XHRobject,propertiesrequeststate,obtaining/XHRpropertiesresponsestate,obtaining/XHRpropertiesresponsestatus,obtaining/XHRproperties
XMLHttpRequest(XHR)about/HowAjaxworks