Important Announcement
PubHTML5 Scheduled Server Maintenance on (GMT) Sunday, June 26th, 2:00 am - 8:00 am.
PubHTML5 site will be inoperative during the times indicated!

Home Explore Front -End Web Development

Front -End Web Development

Published by Angarag, 2022-12-06 17:43:43

Description: Front -End Web Development

Search

Read the Text Version

["Figure\t12.1\t\tErrors\twhen\trequired\tfields\tare\tblank Also,\tnotice\tthat\tthere\tare\tno\tconsole\tmessages\tfrom\tyour\tsubmit\thandlers.\tThe\tsubmit event\tonly\tfires\tafter\tthe\tbrowser\tvalidates\tyour\tform\t(Figure\t12.2). Figure\t12.2\t\tTwo\tpossible\tsequences\tof\tevents\twhen\ta\tform\tis\tvalidated","Validating\twith\tRegular\tExpressions Making\ta\tfield\trequired\tis\tan\teasy\tway\tto\tensure\tthat\tthe\tuser\tdoes\tnot\tleave\tthe\tfield blank.\tBut\twhat\tif\tyou\twant\tto\tbe\tspecific\tabout\twhat\tshould\tgo\tinto\ta\tfield?\tThis\tkind\tof validation\tcalls\tfor\tthe\tpattern\tattribute. After\tthe\trequired\tattribute\ton\tyour\torder\t<input>,\tadd\ta\tpattern\tattribute.\tAssign\tit\ta specially\tformatted\tstring\tcalled\ta\tregular\texpression,\twhich\twe\twill\texplain\tin\ta\tmoment. ... \t\t\t\t\t\t\t\t\t\t\t\t<div\tclass=\\\"form-group\\\"> \t\t\t\t\t\t\t\t\t\t\t\t\t\t<label\tfor=\\\"coffeeOrder\\\">Order<\/label> \t\t\t\t\t\t\t\t\t\t\t\t\t\t<input\tclass=\\\"form-control\\\"\tname=\\\"coffee\\\"\tid=\\\"coffeeOrder\\\" \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tautofocus\trequired\tpattern=\\\"[a-zA-Z\\\\s]+\\\"\t\/> \t\t\t\t\t\t\t\t\t\t\t\t<\/div> ... A\tregular\texpression\tis\ta\tsequence\tof\tcharacters\tfor\tpattern\tmatching.\tThe\tregular expression\t[a-zA-Z\\\\s]+\tmatches\tany\tcharacter\tfrom\tthe\tset\tconsisting\tof\tlowercase\tletters (a-z),\tuppercase\tletters\t(A-Z),\tand\twhitespace\tcharacters\t(\\\\s),\trepeated\tone\tor\tmore\ttimes (+). In\tshort,\twhen\tyou\tsubmit\tthe\tform\tthis\tfield\twill\tonly\tbe\tvalid\tif\tit\tcontains\tletters\tor spaces. Save\tand\treload.\tSee\twhat\thappens\tif\tyou\tput\tsymbols\tor\tnumbers\tinto\tthe\torder\tfield\tand try\tto\tsubmit\tthe\tform.","Constraint\tValidation\tAPI The\tmost\trobust\tway\tto\tvalidate\ta\tform\tfield\tin\tthe\tbrowser\tis\tto\twrite\ta\tvalidation function.\tYou\tcan\tuse\tvalidation\tfunctions\tin\tconjunction\twith\tthe\tConstraint\tValidation API\tto\ttrigger\tbuilt-in\tvalidation\tbehavior. But\tthere\tis\ta\tcatch,\tand\tit\tis\tnot\ta\tsmall\tone:\tThe\tConstraint\tValidation\tAPI\thas\tpoor support\tin\tApple\u2019s\tSafari\tbrowser. Despite\tthis\toversight,\tit\tis\timportant\tto\twrite\tcode\tthat\ttargets\tstandard\tbehavior\tand\tthen add\ta\tJavaScript\tlibrary\tthat\tadds\tsupport\tfor\tnoncompliant\tbrowsers.\t(You\tcan\tread\tmore about\tthis\tin\tthe\tsection\tcalled\tFor\tthe\tMore\tCurious:\tThe\tWebshims\tLibrary\tat\tthe\tend\tof this\tchapter.) Suppose\tyour\tcoffee\ttruck\tis\tonly\tfor\temployees\tof\tyour\tcompany,\tso\tyou\twant\tto\tmake sure\tthat\tthe\tcustomer\tis\tan\temployee.\tOne\tway\tto\tdo\tthis\twould\tbe\tto\tensure\tthat\tthe email\taddress\tthat\tis\tsubmitted\tis\tfrom\tyour\tcompany\u2019s\tdomain. You\tcould\tuse\ta\tpattern\tattribute\tfor\tyour\temailAddress\tfield.\tBut\tthis\tproblem\tis\ta\tgood one\tfor\tlearning\tthe\tConstraint\tValidation\tAPI.\t(Also,\tafter\tyou\twork\tthrough\tthe\tnext chapter,\tyou\tcould\texpand\tbeyond\ta\tsimple\temail\tdomain\tcheck\tand\tquery\ta\tremote\tserver to\tfind\tout\twhether\tthe\temail\taddress\tactually\texists.) Create\ta\tnew\tfile,\tscripts\/validation.js,\tto\thold\tyour\tvalidation\tfunctions.\tAdd a\t<script>\ttag\tin\tindex.html\tfor\tyour\tnew\tmodule. ... \t\t\t\t\t\t<\/div> \t\t\t\t<\/section> \t\t\t\t<script\tsrc=\\\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/jquery\/2.1.4\/jquery.js\\\" \t\t\t\t\t\tcharset=\\\"utf-8\\\"><\/script> \t\t\t\t<script\tsrc=\\\"scripts\/validation.js\\\"\tcharset=\\\"utf-8\\\"><\/script> \t\t\t\t<script\tsrc=\\\"scripts\/checklist.js\\\"\tcharset=\\\"utf-8\\\"><\/script> \t\t\t\t<script\tsrc=\\\"scripts\/formhandler.js\\\"\tcharset=\\\"utf-8\\\"><\/script> \t\t\t\t<script\tsrc=\\\"scripts\/datastore.js\\\"\tcharset=\\\"utf-8\\\"><\/script> \t\t\t\t<script\tsrc=\\\"scripts\/truck.js\\\"\tcharset=\\\"utf-8\\\"><\/script> \t\t\t\t<script\tsrc=\\\"scripts\/main.js\\\"\tcharset=\\\"utf-8\\\"><\/script> \t\t<\/body> <\/html> Save\tindex.html.\tIn\tvalidation.js,\tadd\tan\tIIFE\tmodule\tthat\tcreates\tan\tempty object\tliteral,\tassigns\tit\tto\ta\tvariable\tnamed\tValidation,\tand\tthen\texports\tthat\tvariable to\tthe\tApp\tnamespace. (function\t(window)\t{ \t\t'use\tstrict'; \t\tvar\tApp\t=\twindow.App\t||\t{}; \t\tvar\tValidation\t=\t{ \t\t}; \t\tApp.Validation\t=\tValidation; \t\twindow.App\t=\tApp; })(window); Your\tnew\tValidation\tmodule\twill\tonly\tbe\tused\tfor\torganizing\tfunctions,\tso\tit\tdoes\tnot need\tto\tbe\ta\tconstructor. Add\ta\tmethod\tcalled\tisCompanyEmail.\tThis\tmethod\twill\ttest\tan\temail\taddress\tagainst a\tregular\texpression\tand\treturn\ttrue\tor\tfalse.\t(Feel\tfree\tto\tchange\tthe\temail\tdomain specified.)","(function\t()\t{ \t\t'use\tstrict'; \t\tvar\tApp\t=\twindow.App\t||\t{}; \t\tvar\tValidation\t=\t{ \t\t\t\tisCompanyEmail:\tfunction\t(email)\t{ \t\t\t\t\t\treturn\t\/.+@bignerdranch\\\\.com$\/.test(email); \t\t\t\t} \t\t}; \t\tApp.Validation\t=\tValidation; \t\twindow.App\t=\tApp; })(window); You\tcreated\ta\tliteral\tregular\texpression\tby\tputting\ta\tpattern\tbetween\tthe\tforward\tslashes, \/\/.\tInside\tthe\tslashes,\tyou\tspecify\ta\tstring\tthat\tconsists\tof\tone\tor\tmore\tcharacters\t(.+), followed\tby\t\[email protected]\u201d\t\u2013\tyou\talso\tused\ta\tbackslash\tto\tindicate\tthat\tthe\tperiod in\tbignerdranch.com\tshould\tbe\ttreated\tas\ta\tliteral\tperiod.\t(Normally,\ta\tperiod\tin\ta regular\texpression\tis\ta\twildcard\tthat\tmatches\tany\tcharacter.)\tThe\t\u201c$\u201d\tat\tthe\tend\tof\tregular expression\tmeans\tthat\t\[email protected]\u201d\tshould\tbe\tat\tthe\tend\tof\tthe\tstring\t\u2013\tthere should\tbe\tno\tmore\tcharacters\tthat\tappear\tafter\tit. This\tregular\texpression\tis\tan\tobject\tand\thas\ta\ttest\tmethod.\tYou\tcan\tpass\ta\tstring\tto\tthe test\tmethod,\tand\tit\twill\treturn\ta\tBoolean\t\u2013\ttrue\tif\tthe\tregular\texpression\tmatches\tthe string\tand\tfalse\tif\tnot.\t(For\ta\tlist\tof\tother\tregular\texpression\tmethods,\tsee developer.mozilla.org\/\u200ben-US\/d\u200b ocs\/W\u200b eb\/\u200bJavaScript\/R\u200b eference\/\u200b Global_Objects\/R\u200b egExp.) Test\tyour\tApp.Validation.isCompanyEmail\tfunction\ton\tthe\tconsole (Figure\t12.3). Figure\t12.3\t\tTesting\tApp.Validation.isCompanyEmail\ton\tthe\tconsole You\tnow\thave\ta\tfunction\tthat\tcan\tcheck\tfor\tvalid\temail\taddresses.\tThe\tnext\tthing\tto\tdo\tis to\tconnect\tit\tto\tthe\tform. Listening\tfor\tthe\tinput\tevent When\tshould\tyou\tuse\tthis\tfunction?\tThere\tare\tseveral\tevents\tthat\tthe\tinput\tfield\tcould trigger\twhile\ta\tuser\tis\tfilling\tout\tthe\tform.\tOne\toccurs\tas\tthe\tuser\ttypes\teach\tcharacter. Another\tis\twhen\tthe\tuser\tremoves\tfocus\tfrom\tthe\tfield.\tOr\tyou\tcould\trun\tthe\tfunction when\tthe\tform\tis\tsubmitted. The\tConstraint\tValidation\tAPI\trequires\tthat\tinvalid\tfields\tbe\tmarked\tprior\tto\tsubmission.","If\tany\tfields\tare\tinvalid,\tthe\tbrowser\tstops\tshort\tof\ttriggering\tthe\tsubmit\tevent.\tSo\tdoing the\tcheck\ton\tsubmit\tis\ttoo\tlate. The\tevent\ttriggered\twhen\tthe\tuser\tremoves\tfocus\tfrom\ta\tfield\tis\tknown\tas\ta\tblur\tevent.\tIt is\tnot\ta\tgood\tchoice\tfor\tvalidation,\teither.\tSuppose\tthe\tuser\u2019s\tcursor\tis\tin\tthe\temail\tinput, so\tthat\tfield\thas\tfocus.\tIf\tthe\tuser\tthen\tpresses\tthe\tReturn\tkey,\tthis\twould\ttrigger\tform submission,\tbut\tthe\tblur\tevent\twould\tnot\tbe\ttriggered\tand\tany\tvalidation\ttied\tto\tit\twould not\tbe\tperformed. So\tthe\tvalidation\tcheck\twill\tneed\tto\thappen\tas\tthe\tuser\tenters\teach\tcharacter.\tUpdate\tthe FormHandler\tmodule\tin\tformhandler.js\twith\tan\taddInputHandler\tprototype method.\tIt\tshould\tadd\ta\tlistener\tfor\tthe\tinput\tevent\tof\tthe\tform.\tLike\tthe addSubmitHandler\tmethod,\tit\tshould\taccept\ta\tfunction\targument. ... \t\tFormHandler.prototype.addSubmitHandler\t=\tfunction\t(fn)\t{ \t\t\t\t... \t\t}; \t\tFormHandler.prototype.addInputHandler\t=\tfunction\t(fn)\t{ \t\t\t\tconsole.log('Setting\tinput\thandler\tfor\tform'); \t\t}; \t\tApp.FormHandler\t=\tFormHandler; \t\twindow.App\t=\tApp; ... Attach\tthe\tlistener\tfor\tthe\tinput\tevent\tusing\tjQuery\u2019s\ton\tmethod.\tMake\tsure\tto\tuse\tthe event\tdelegation\tpattern\tto\tfilter\tout\tevents\tcreated\tby\tanything\tbut\tthe [name=\\\"emailAddress\\\"]\tfield. ... \t\tFormHandler.prototype.addInputHandler\t=\tfunction\t(fn)\t{ \t\t\t\tconsole.log('Setting\tinput\thandler\tfor\tform'); \t\t\t\tthis.$formElement.on('input',\t'[name=\\\"emailAddress\\\"]',\tfunction\t(event)\t{ \t\t\t\t\t\t\/\/\tEvent\thandler\tcode\twill\tgo\there \t\t\t\t}); \t\t}; \t\tApp.FormHandler\t=\tFormHandler; \t\twindow.App\t=\tApp; ... Inside\tthe\tevent\thandler,\textract\tthe\tvalue\tof\tthe\temail\tfield\tfrom\tthe\tevent.target object.\tThen\tconsole.log\tthe\tresult\tof\trunning\taddInputHandler\u2019s\tfunction argument\tfn\tand\tpassing\tit\tthe\tvalue\tof\tthe\temail\tfield. ... \t\tFormHandler.prototype.addInputHandler\t=\tfunction\t(fn)\t{ \t\t\t\tconsole.log('Setting\tinput\thandler\tfor\tform'); \t\t\t\tthis.$formElement.on('input',\t'[name=\\\"emailAddress\\\"]',\tfunction\t(event)\t{ \t\t\t\t\t\t\/\/\tEvent\thandler\tcode\twill\tgo\there \t\t\t\t\t\tvar\temailAddress\t=\tevent.target.value; \t\t\t\t\t\tconsole.log(fn(emailAddress)); \t\t\t\t}); \t\t}; \t\tApp.FormHandler\t=\tFormHandler; \t\twindow.App\t=\tApp; ... Save\tformhandler.js. Associating\tthe\tvalidation\tcheck\twith\tthe\tinput\tevent In\tmain.js,\timport\tValidation\tfrom\tthe\tApp\tnamespace\tand\tassign\tit\tto\ta\tlocal variable. ... \t\tvar\tTruck\t=\tApp.Truck; \t\tvar\tDataStore\t=\tApp.DataStore;","var\tFormHandler\t=\tApp.FormHandler; \t\tvar\tValidation\t=\tApp.Validation; \t\tvar\tCheckList\t=\tApp.CheckList; \t\tvar\tmyTruck\t=\tnew\tTruck('ncc-1701',\tnew\tDataStore()); ... With\tthe\tValidation\tobject\timported,\tyou\tcan\tconnect\tit\tto\tFormHandler\u2019s\tnew addInputHandler\tmethod. At\tthe\tend\tof\tmain.js,\tpass\tValidation.isCompanyEmail\tto\tthe addInputHandler\tmethod\tof\tthe\tformHandler\tinstance: ... \t\tformHandler.addSubmitHandler(function\t(data)\t{ \t\t\t\tmyTruck.createOrder.call(myTruck,\tdata); \t\t\t\tcheckList.addRow.call(checkList,\tdata); \t\t}); \t\tformHandler.addInputHandler(Validation.isCompanyEmail); })(window); Save\tand\treload.\tFill\tout\tthe\temail\tfield\tand\tsee\twhat\tappears\ton\tthe\tconsole.\tAs\tyou\ttype a\tvalid\temail\taddress,\tthe\tconsole\twill\tshow\ta\tnumber\tof\tfalse\tresults,\tprinted\tby\tthe console.log(fn(emailAddress));\tline\tin FormHandler.prototype.addInputHandler.\tWhen\tyou\thave\tfinished\ttyping\ta valid\temail\taddress,\tyou\twill\tsee\ttrue\tprinted\tin\tthe\tconsole\t(Figure\t12.4). Figure\t12.4\t\tLogging\tthe\temail\tvalidation\tcheck Your\tvalidation\tfunction\tis\trun\teach\ttime\ta\tcharacter\tis\tentered\t(or\tremoved)\tfrom\tthe email\tfield.\tWhen\tyou\thave\tconfirmed\tthat\tit\tis\tcorrectly\tchecking\tyour\tinput,\tyou\tcan\tuse it\tto\tshow\ta\tcustom\terror\tmessage. Triggering\tthe\tvalidity\tcheck Now\tthat\tyou\tcan\treliably\ttest\tthat\tthe\temail\taddress\tis\tfrom\tyour\tcompany\u2019s\tdomain,\tyou should\tnotify\tthe\tuser\tif\tthe\tvalidation\tfails.\tYou\twill\tuse\tthe\tsetCustomValidity method\tfor\tthe\tevent.target\tto\tmark\tit\tas\tinvalid. In\tformhandler.js,\tremove\tthe\tconsole.log\tstatement\tand\treplace\tit\twith\ta\tvariable for\ta\twarning\tmessage\tand\tan\tif\/else\tclause.\tIf\tthe\tfn(emailAddress)\tcall\treturns\ttrue, clear\tthe\tcustom\tvalidity\tof\tthe\tfield.\tIf\tit\treturns\tfalse,\tassign\tthe\tmessage\tvariable\tto\ta","string\twith\tthe\twarning\tmessage\tand\tset\tthe\tcustom\tvalidity\tto\tmessage. ... \t\tFormHandler.prototype.addInputHandler\t=\tfunction\t(fn)\t{ \t\t\t\tconsole.log('Setting\tinput\thandler\tfor\tform'); \t\t\t\tthis.$formElement.on('input',\t'[name=\\\"emailAddress\\\"]',\tfunction\t(event)\t{ \t\t\t\t\t\tvar\temailAddress\t=\tevent.target.value; \t\t\t\t\t\tconsole.log(fn(emailAddress)); \t\t\t\t\t\tvar\tmessage\t=\t''; \t\t\t\t\t\tif\t(fn(emailAddress))\t{ \t\t\t\t\t\t\t\tevent.target.setCustomValidity(''); \t\t\t\t\t\t}\telse\t{ \t\t\t\t\t\t\t\tmessage\t=\temailAddress\t+\t'\tis\tnot\tan\tauthorized\temail\taddress!' \t\t\t\t\t\t\t\tevent.target.setCustomValidity(message); \t\t\t\t\t\t} \t\t\t\t}); \t\t}; ... You\tpassed\tin\tthe\terror\tmessage\tthat\tshould\tbe\tshown\tto\tthe\tuser.\tIf\tthere\tis\tno\terror,\tyou still\thave\tto\tcall\tsetCustomValidity,\tbut\twith\tan\tempty\tstring\tas\tthe\targument.\tThis has\tthe\teffect\tof\tmarking\tthe\tfield\tas\tvalid. The\tvalidation\tcheck\tthat\toccurs\tas\tyou\ttype\tonly\tmarks\tthe\tfield\tas\tvalid\tor\tinvalid.\tIt does\tnot\tdisplay\tthe\terror\tmessage.\tWhen\tyou\tpress\tthe\tSubmit\tbutton,\tthe\tbrowser\tchecks for\tinvalid\tfields\tand\tdisplays\tthe\tvalidation\tmessage\tif\tit\tfinds\tany. To\ttry\tit\tout,\tsubmit\tthe\tform\tafter\tentering\tan\temail\taddress\tthat\tdoes\tnot\tmatch\tthe domain.\tRight\tafter\tyou\tpress\tthe\tSubmit\tbutton,\tyou\tshould\tsee\tyour\tcustom\tvalidation message\tappear\tas\ta\twarning\tnext\tto\tthe\tfield\t(Figure\t12.5). Figure\t12.5\t\tOnly\tvalid\temail\taddresses\tallowed!","Styling\tValid\tand\tInvalid\tElements CoffeeRun\tnow\tvalidates\tboth\tthe\torder\tfield\tand\tthe\temail\taddress.\tNow\tit\tis\ttime\tto enhance\tthe\tUI\tby\tvisually\tmarking\tinvalid\tfields.\tFor\tthis\tvery\tshort\tpiece\tof\tCSS,\tyou will\tadd\tone\truleset\tin\ta\t<style>\ttag\tto\tthe\t<head>\tin\tindex.html. ... \t\t<head> \t\t\t\t<meta\tcharset=\\\"utf-8\\\"> \t\t\t\t<title>coffeerun<\/title> \t\t\t\t<link\trel=\\\"stylesheet\\\"\thref=\\\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/twitter-bootst rap\/3.3.6\/css\/bootstrap.min.css\\\"> \t\t\t\t<style> \t\t\t\t\t\tform\t:invalid\t{ \t\t\t\t\t\t\t\tborder-color:\t#a94442; \t\t\t\t\t\t} \t\t\t\t<\/style> \t\t<\/head> ... This\tadds\ta\tborder\tto\tany\tfield\tinside\tyour\tform\tthat\thas\tthe\tpseudo-class\t:invalid.\tThis pseudo-class\tis\tautomatically\tadded\tby\tthe\tbrowser\twhen\tthe\tform\truns\tits\tvalidation checks. Save\tand\treturn\tto\tthe\tbrowser.\tPress\tthe\tTab\tkey\ta\tfew\ttimes\t(or\tclick\toutside\tof\tthe\ttext- entry\tfields)\tto\tfocus\ton\ta\tform\telement\tother\tthan\tthe\torder\tor\temail\tfields.\tThe\ttwo required\tfields\twill\thave\ta\treddish\tborder\tcolor\t(Figure\t12.6). Figure\t12.6\t\tTrust\tus:\tThese\tborders\tare\tred It\twould\tbe\tmore\tappropriate\tfor\tthe\tborder\tto\tonly\tappear\ton\tan\tinvalid\tfield\tthat\tis required\tand\thas\tfocus.\tAdd\ttwo\tmore\tpseudo-classes\tto\tyour\tselector\tin\tindex.html: \t\t<head> \t\t\t\t<meta\tcharset=\\\"utf-8\\\"> \t\t\t\t<title>coffeerun<\/title> \t\t\t\t<link\trel=\\\"stylesheet\\\"\thref=\\\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/twitter-bootst rap\/3.3.6\/css\/bootstrap.min.css\\\"> \t\t\t\t<style> \t\t\t\t\t\tform\t:focus:required:invalid\t{ \t\t\t\t\t\t\t\tborder-color:\t#a94442; \t\t\t\t\t\t} \t\t\t\t<\/style> \t\t<\/head> ... You\thave\tspecified\tthat\tfields\tthat\thave\tthe\tthree\tpseudo-classes\t:focus,\t:required,\tand :invalid\twill\tget\tthe\tnew\tborder\tcolor\t(Figure\t12.7).","Figure\t12.7\t\t:invalid\tborder\tcolor\tonly\tfor\tfield\twith\tfocus CoffeeRun\tis\tdeveloping\tinto\ta\tfully\tfeatured\tweb\tapp.\tIn\tthe\tnext\ttwo\tchapters,\tyou\twill sync\tthe\tdata\tto\ta\tremote\tserver\tusing\tAjax.","Silver\tChallenge:\tCustom\tValidation\tfor\tDecaf Add\tanother\tfunction\tto\tyour\tValidation\tmodule.\tIt\tshould\taccept\ttwo\targuments:\ta string\tand\tan\tinteger.\tIf\tthe\tstring\tcontains\tthe\tword\t\u201cdecaf\u201d\tand\tthe\tinteger\tis\tgreater\tthan 20,\tthe\tfunction\tshould\treturn\tfalse. Add\tlisteners\tfor\tthe\tcoffee\torder\ttext\tfield\tand\tfor\tthe\tcaffeine\tstrength\tslider.\tTrigger\tthe custom\tvalidation\tfor\twhichever\tfield\tis\tcurrently\tbeing\tedited\tand\tcaused\tthe\tvalidation failure.","For\tthe\tMore\tCurious:\tThe\tWebshims\tLibrary As\tmentioned\tearlier,\tone\tnotable\tbrowser\tthat\tdoes\tnot\tsupport\tthe\tConstraint\tValidation API\tis\tApple\u2019s\tSafari\tbrowser.\tShould\tyou\tneed\tto\tsupport\tSafari,\tyou\tcan\tuse\ta\tlibrary,\tor polyfill,\tthat\tsimulates\tthe\tAPI\tfor\tbrowsers\tthat\tdo\tnot\timplement\tit. One\tlibrary\tthat\twill\tprovide\tConstraint\tValidation\tin\tSafari\tis\tthe\tWebshims\tLib,\twhich you\tcan\tdownload\tfrom\tgithub.com\/\u200baFarkas\/\u200bwebshim. Actually,\tthe\tWebshims\tlibrary\tcan\tact\tas\ta\tpolyfill\tfor\tmany,\tmany\tfeatures.\tSetting\tit\tup and\tusing\tit\tis\tstraightforward.\t(However,\tit\tdoes\tdo\ta\tlot\tof\tdifferent\tthings,\tand\tit\tis\teasy to\tget\tlost\tin\tthe\tdocumentation.) Here\tis\thow\tyou\tuse\tit\twith\tCoffeeRun\tso\tthat\tSafari\tworks\twith\tyour\tValidation module.\tFirst,\tdownload\ta\tzip\tfile\tfrom\tthe\tproject\tpage:\tgithub.com\/\u200baFarkas\/\u200b webshim\/r\u200b eleases\/\u200blatest.\tUnzip\tthe\tfile\tand\tput\tthe\tjs-webshim\/webshim folder\tin\tyour\tcoffeerun\tdirectory\t(next\tto\tindex.html\tand\tyour\tscripts\tfolder). Add\ta\t<script>\ttag\tin\tindex.html\tfor\tthe\twebshim\/polyfiller.js\tfile. ... \t\t\t\t\t\t<\/div> \t\t\t\t<\/section> \t\t\t\t<script\tsrc=\\\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/jquery\/2.1.4\/jquery.js\\\" \t\t\t\t\t\tcharset=\\\"utf-8\\\"><\/script> \t\t\t\t<script\tsrc=\\\"webshim\/polyfiller.js\\\"\tcharset=\\\"utf-8\\\"><\/script> \t\t\t\t<script\tsrc=\\\"scripts\/validation.js\\\"\tcharset=\\\"utf-8\\\"><\/script> \t\t\t\t<script\tsrc=\\\"scripts\/checklist.js\\\"\tcharset=\\\"utf-8\\\"><\/script> \t\t\t\t<script\tsrc=\\\"scripts\/formhandler.js\\\"\tcharset=\\\"utf-8\\\"><\/script> \t\t\t\t<script\tsrc=\\\"scripts\/datastore.js\\\"\tcharset=\\\"utf-8\\\"><\/script> \t\t\t\t<script\tsrc=\\\"scripts\/truck.js\\\"\tcharset=\\\"utf-8\\\"><\/script> \t\t\t\t<script\tsrc=\\\"scripts\/main.js\\\"\tcharset=\\\"utf-8\\\"><\/script> \t\t<\/body> <\/html> Then,\tadd\tthese\tlines\tto\tmain.js: ... \t\tvar\tValidation\t=\tApp.Validation; \t\tvar\tCheckList\t=\tApp.CheckList; \t\tvar\twebshim\t=\twindow.webshim; \t\tvar\tmyTruck\t=\tnew\tTruck('ncc-1701',\tnew\tDataStore()); \t\t... \t\tformHandler.addInputHandler(Validation.isCompanyEmail); \t\twebshim.polyfill('forms\tforms-ext'); \t\twebshim.setOptions('forms',\t{\taddValidators:\ttrue,\tlazyCustomMessages:\ttrue\t}); }(window)); This\timports\tthe\twebshim\tlibrary\tand\tthen\tconfigures\tit\tfor\tuse\twith\tforms. Finally,\tthere\tis\tone\tquirk\twith\tthe\tlibrary\tthat\tyou\tneed\tto\tknow.\tAnywhere\tyou\tuse setCustomValidity,\tyou\tmust\twrap\tthe\tobjects\twith\tjQuery.\tFor\tCoffeeRun,\tyou need\tto\twrap\tthe\tevent.target\tobjects\tof\tthe\taddInputHandler\tfunction\tin formhandler.js: ... \t\tFormHandler.prototype.addInputHandler\t=\tfunction\t(fn)\t{ \t\t\t\tconsole.log('Setting\tinput\thandler\tfor\tform'); \t\t\t\tthis.$formElement.on('input',\t'[name=\\\"emailAddress\\\"]',\tfunction\t(event)\t{ \t\t\t\t\t\tvar\temailAddress\t=\tevent.target.value; \t\t\t\t\t\tvar\tmessage\t=\t''; \t\t\t\t\t\tif\t(fn(emailAddress))\t{ \t\t\t\t\t\t\t\t$(event.target).setCustomValidity(''); \t\t\t\t\t\t}\telse\t{ \t\t\t\t\t\t\t\tmessage\t=\temailAddress\t+\t'\tis\tnot\tan\tauthorized\temail\taddress!' \t\t\t\t\t\t\t\t$(event.target).setCustomValidity(message);","} \t\t\t\t}); \t\t}; ... The\tauthors\tof\tWebshim\tchose\tto\timplement\tthe\tpolyfill\tfunctionality\tentirely\tas\tan extension\tof\tjQuery.\tOther\tthan\tthis\twrapping,\tyou\tdo\tnot\tneed\tto\tmodify\tyour\tcode. After\tyou\tsave\tyour\tchanges,\tyou\tcan\ttest\tyour\tvalidation\tin\tSafari.\tYou\tshould\tsee\tthat\tit also\treports\tan\tissue\tif\tyou\tforget\tto\tfill\tout\tthe\tcoffee\torder\tor\tif\tyou\tenter\tan\tinvalid email\taddress\t(Figure\t12.8). Figure\t12.8\t\tUsing\tWebshim\tas\ta\tSafari\tpolyfill Webshim\tgoes\twell\tbeyond\tproviding\tform\tvalidation.\tYou\tshould\tbrowse\tthrough\tthe documentation\tto\tsee\twhat\telse\tit\tcan\tdo\tfor\tyou\ton\tyour\tprojects.","13\t Ajax In\tthe\tlast\tchapter,\tyou\tused\tthe\tbrowser\u2019s\tbuilt-in\tvalidation\tto\tensure\tthat\tthe\tuser entered\tdata\tthat\tfit\tCoffeeRun\u2019s\tparameters.\tAfter\tdoing\tthose\tchecks,\tyou\tcan\tfeel confident\tabout\tsending\tthat\tdata\tto\tthe\tserver. At\tthe\tmoment,\tthe\tFormHandler.prototype.addSubmitHandler\tmethod\tcalls the\tevent\tobject\u2019s\tpreventDefault\tmethod\tto\tkeep\tthe\tbrowser\tfrom\tsending\ta request\tto\tthe\tserver.\tNormally,\tthe\tserver\tsends\tback\ta\tresponse\tthat\tcauses\tthe\tpage\tto reload.\tInstead,\tyou\tare\textracting\tthe\tdata\tthat\tthe\tuser\tenters\tinto\tthe\tform\tand\tupdating the\tform\tand\tthe\tchecklist\twith\tJavaScript. In\tthis\tchapter,\tyou\twill\tcreate\ta\tRemoteDataStore\tmodule\tthat\tsends\ta\trequest\tto\tthe server\tand\thandles\tthe\tresponse\t(Figure\t13.1).\tBut\tit\twill\tdo\tthis\tin\tthe\tbackground\tusing Ajax,\twithout\tcausing\tthe\tbrowser\tto\treload. Figure\t13.1\t\tCoffeeRun\tat\tthe\tend\tof\tthe\tchapter Ajax\tis\ta\ttechnique\tfor\tcommunicating\twith\ta\tremote\tserver\tvia\tJavaScript.\tThe JavaScript\tusually\tchanges\tthe\tcontents\tof\ta\tweb\tpage\tusing\tdata\treturned\tby\tthe\tserver without\treloading\tthe\tbrowser.\tThis\tcan\timprove\tthe\texperience\tof\tusing\ta\tweb application. Originally,\tthe\tterm\t\u201cAjax\u201d\twas\tan\tacronym\tfor\t\u201casynchronous\tJavaScript\tand\tXML,\u201d\tbut","it\tis\tnow\tused\tgenerically\tfor\tthis\tstyle\tof\tasynchronous\tdata\tcommunication,\tregardless\tof what\ttechnologies\tare\tactually\tinvolved.\t(Asynchronous\tcommunication\tmeans\tthat\tthe app,\thaving\tsent\ta\trequest,\tdoes\tnot\thave\tto\twait\tfor\ta\tresponse\tfrom\tthe\tserver\tbefore continuing\twith\tother\ttasks.)\tAjax\tis\tnow\tthe\tstandard\tmechanism\tfor\tsending\tand receiving\tdata\tin\tthe\tbackground. XMLHttpRequest\tObjects At\tthe\tcore\tof\tAjax\tis\tthe\tXMLHttpRequest\tAPI.\tIn\tmodern\tbrowsers,\tyou\tcan\tinstantiate new\tXMLHttpRequest\tobjects,\twhich\tallow\tyou\tto\tsend\trequests\tto\tservers\twithout\tcausing a\tpage\treload.\tThey\tperform\ttheir\twork\tin\tthe\tbackground. Using\tXMLHttpRequest\tobjects,\tyou\tcan\tattach\tcallbacks\tto\tdifferent\tstages\tof\tthe request\/response\tcycle,\tmuch\tin\tthe\tsame\tway\tthat\tyou\tlisten\tto\tevents\ton\tDOM\tobjects. You\tcan\talso\tinspect\tthe\tXMLHttpRequest\tobject\u2019s\tproperties\tto\taccess\tinformation\tabout the\tstatus\tof\tthe\trequest\/response\tcycle.\tTwo\tuseful\tproperties\tare\tresponse\tand\tstatus, which\tare\tupdated\tas\tsoon\tas\tany\tchanges\toccur.\tThe\tresponse\tproperty\tcontains\tthe\tdata (such\tas\tHTML,\tXML,\tJSON,\tor\tanother\tformat)\tsent\tback\tby\tthe\tserver.\tThe\tstatus\tis\ta numeric\tcode\tthat\ttells\tyou\twhether\tthe\tHTTP\tresponse\twas\tsuccessful\tor\tnot.\tThese\tare officially\tknown\tas\tHTTP\tStatus\tCodes. Status\tcodes\tare\tgrouped\tin\tranges,\tand\tthese\tranges\thave\tbasic\tmeanings.\tFor\texample, the\tstatus\tcodes\tin\tthe\t200-299\trange\tare\tsuccess\tcodes,\twhile\tstatus\tcodes\tin\tthe\t500-599 range\tmean\tthat\tthere\twas\ta\tserver\terror.\tYou\twill\toften\tsee\tthese\tranges\treferred\tto generically,\tas\tin\t\u201c2xx\u201d\tor\t\u201c3xx\u201d\tstatuses. Table\t13.1\tshows\tsome\tof\tthe\tmore\tcommon\tcodes. Table\t13.1\t\tCommon\tHTTP\tstatus\tcodes Status Status\tText Description Code 200 OK The\trequest\twas\tsuccessful. 400 Bad The\tserver\tdid\tnot\tunderstand\tthe\trequest. Request 404 Not\tFound The\tresource\tcould\tnot\tbe\tfound,\toften\tbecause\tthe\tfile\tor\tpath name\tdid\tnot\tmatch\tanything\ton\tthe\tserver. 500 Internal The\tserver\tencountered\tan\terror,\tsuch\tas\tan\tunhandled\texception Server in\tthe\tserver-side\tcode. Error 503 Service The\tserver\tcould\tnot\thandle\tthe\trequest,\toften\tbecause\tit\tis Unavailable overloaded\tor\tdown\tfor\tmaintenance. jQuery\thas\ta\tnumber\tof\tmethods\tthat\tcreate\tand\tmanage\tXMLHttpRequest\tobjects\tand provide\ta\tconcise,\tbackward-compatible,\tcross-browser\tAPI.\tIt\tis\tnot\tthe\tonly\tlibrary","available\tfor\tmanaging\tAjax\trequests,\tbut\tmany\tother\tlibraries\tsimply\tfollow\tjQuery\u2019s lead.\tYou\twill\tbe\tusing\tjQuery\u2019s\tget\tand\tpost\tmethods\tto\twork\twith\tAjax\tGET\tand\tPOST requests\tand\tthe\tjQuery\tajax\tmethod\tto\thandle\tDELETE\trequests.","RESTful\tWeb\tServices You\tare\tgoing\tto\tenhance\tCoffeeRun\tby\tusing\ta\tremote\tweb\tservice\tto\tstore\tyour application\tdata.\tThe\tserver\tyou\twill\tuse\thas\tbeen\tcreated\tspecifically\tfor\tthis\tbook. The\tCoffeeRun\tserver\tprovides\ta\tRESTful\tweb\tservice.\t\u201cREST\u201d\tstands\tfor \u201crepresentational\tstate\ttransfer,\u201d\twhich\tis\ta\tstyle\tof\tweb\tservice\tthat\trelies\ton\tHTTP\tverbs (GET,\tPOST,\tPUT,\tDELETE)\tand\tURLs\tthat\tidentify\tresources\ton\tthe\tserver. Frequently,\tthe\tURL\tpath\t(the\tpart\tthat\tcomes\tafter\tthe\tserver\tname)\twill\trefer\teither\tto\ta collection\tof\tthings\t(such\tas\t\/coffeeorders)\tor\tto\tindividual\tthings,\tspecified\tby\tan\tID (such\tas\t\/coffeeorders\/[customer\temail]). This\tdifference\taffects\thow\tthe\tHTTP\tverbs\tapply.\tFor\texample,\twhen\tworking\twith\ta collection,\ta\tGET\trequest\tretrieves\ta\tlist\tof\tall\titems\tin\tthe\tcollection.\tWith\tan\tindividual item,\ta\tGET\trequest\tretrieves\tall\tof\tthe\tdetails\tfor\tthat\titem. The\tURL\tand\tHTTP\tverb\tpatterns\tare\tsummarized\tin\tTable\t13.2. Table\t13.2\t\tExample\tof\tRESTful\tURL\tand\tHTTP\tverb\tpatterns URL\tPath GET POST PUT DELETE \/coffeeorders list\tall create\tone - delete\tall records record records \/coffeeorders\/[email protected] get\tthe - update\tthe delete\tthe record record record","The\tRemoteDataStore\tModule In\ta\tmoment,\tyou\twill\tcreate\ta\tnew\tmodule\tcalled\tRemoteDataStore.\tThe\tjob\tof RemoteDataStore\twill\tbe\tto\ttalk\tto\ta\tserver\ton\tbehalf\tof\tthe\trest\tof\tthe\tapplication.\tIt will\thave\tall\tthe\tsame\tmethods\tas\tDataStore\t\u2013\tadd,\tget,\tgetAll,\tand\tremove\t\u2013 which\tit\twill\tuse\tto\tcommunicate\twith\tthe\tserver\t(Figure\t13.2). Figure\t13.2\t\tDataStore\tvs\tRemoteDataStore You\twill\tbe\table\tto\tuse\tan\tinstance\tof\tRemoteDataStore\tin\tplace\tof\tDataStore without\thaving\tto\tchange\tyour\tTruck,\tFormHandler,\tor\tCheckList\tmodules.\t(You will\tnot\tbe\tdeleting\tyour\tDataStore\tmodule,\tthough.\tYou\tmight\twant\tto\tcreate\ta\tfuture enhancement\tallowing\tCoffeeRun\tto\tswitch\tbetween\tthe\ttwo\tstorage\tmodules\tbased\ton whether\tthe\tapp\tis\trunning\tonline\tor\toffline.) RemoteDataStore\u2019s\tmethods\twill\tcommunicate\tasynchronously\twith\tthe\tserver\tby sending\ta\tnetwork\trequest\tin\tthe\tbackground.\tWhen\tthe\tbrowser\treceives\tthe\tresponse from\tthe\tserver,\tit\thas\tan\topportunity\tto\tinvoke\ta\tcallback. Each\tof\tRemoteDataStore\u2019s\tmethods\twill\taccept\ta\tfunction\targument\tthat\twill\tbe invoked\tafter\tthe\tresponse\tarrives\twith\tany\tdata\tfrom\tthe\tserver. Create\ta\tnew\tscripts\/remotedatastore.js\tfile\tand\tadd\ta\t<script>\ttag\tfor\tit\tin index.html. ... \t\t\t\t<script\tsrc=\\\"scripts\/validation.js\\\"\tcharset=\\\"utf-8\\\"><\/script> \t\t\t\t<script\tsrc=\\\"scripts\/checklist.js\\\"\tcharset=\\\"utf-8\\\"><\/script> \t\t\t\t<script\tsrc=\\\"scripts\/formhandler.js\\\"\tcharset=\\\"utf-8\\\"><\/script> \t\t\t\t<script\tsrc=\\\"scripts\/remotedatastore.js\\\"\tcharset=\\\"utf-8\\\"><\/script> \t\t\t\t<script\tsrc=\\\"scripts\/datastore.js\\\"\tcharset=\\\"utf-8\\\"><\/script> \t\t\t\t<script\tsrc=\\\"scripts\/truck.js\\\"\tcharset=\\\"utf-8\\\"><\/script> \t\t\t\t<script\tsrc=\\\"scripts\/main.js\\\"\tcharset=\\\"utf-8\\\"><\/script> \t\t<\/body> <\/html> Save\tindex.html.\tIn\tremotedatastore.js,\timport\tthe\tApp\tnamespace\tand jQuery,\tthen\tcreate\tan\tIIFE\tmodule\twith\ta\tconstructor\tnamed\tRemoteDataStore.\tThe constructor\tshould\taccept\tan\targument\tfor\ta\tremote\tserver\tURL\tand\tthrow\tan\terror\tif\ta","URL\tis\tnot\tpassed\tin.\tAt\tthe\tend\tof\tthe\tmodule\tdefinition,\texport\tthe RemoteDataStore\tto\tthe\tApp\tnamespace: (function\t(window)\t{ \t\t'use\tstrict'; \t\tvar\tApp\t=\twindow.App\t||\t{}; \t\tvar\t$\t=\twindow.jQuery; \t\tfunction\tRemoteDataStore(url)\t{ \t\t\t\tif\t(!url)\t{ \t\t\t\t\t\tthrow\tnew\tError('No\tremote\tURL\tsupplied.'); \t\t\t\t} \t\t\t\tthis.serverUrl\t=\turl; \t\t} \t\tApp.RemoteDataStore\t=\tRemoteDataStore; \t\twindow.App\t=\tApp; })(window);","Sending\tData\tto\tthe\tServer The\tfirst\tmethod\tyou\twill\tcreate\tis\tthe\tadd\tmethod\tto\tstore\tcustomer\torder\tdata\ton\tthe remote\tweb\tservice. Add\ta\tprototype\tmethod\tto\tRemoteDataStore.\tLike\tDataStore\u2019s\tadd\tmethod,\tit will\taccept\targuments\tcalled\tkey\tand\tval.\tNote\tthat\tit\tis\tnot\trequired\tfor\tyou\tto\tuse\tthe same\tparameter\tnames,\tbut\tit\tis\tgood\tpractice\tto\tkeep\tthem\tconsistent. ... \t\tfunction\tRemoteDataStore(url)\t{ \t\t\t\t... \t\t} \t\tRemoteDataStore.prototype.add\t=\tfunction\t(key,\tval)\t{ \t\t\t\t\/\/\tCode\twill\tgo\there \t\t}; \t\tApp.RemoteDataStore\t=\tRemoteDataStore; \t\twindow.App\t=\tApp; ... Using\tjQuery\u2019s\t$.post\tmethod Inside\tthe\tRemoteDataStore\tmodule,\tyou\twill\tuse\tjQuery\u2019s\t$.post\tmethod.\tThis method\tsends\ta\tPOST\trequest\tin\tthe\tbackground\tas\tan\tXMLHttpRequest\tobject (Figure\t13.3). Figure\t13.3\t\tRemoteDataStore\tuses\tjQuery\tfor\tAjax The\t$.post\tmethod\tonly\trequires\ttwo\tpieces\tof\tinformation:\tthe\tURL\tof\tthe\tserver\tto send\tthe\trequest\tto\tand\twhat\tdata\tto\tinclude. In\tremotedatastore.js,\tupdate\tthe\tbody\tof\tthe\tadd\tmethod\tso\tthat\tit\tcalls $.post,\tpassing\tit\tthis.serverUrl\tand\tthe\tval. ... \t\tRemoteDataStore.prototype.add\t=\tfunction\t(key,\tval)\t{ \t\t\/\/\tCode\twill\tgo\there \t\t\t\t$.post(this.serverUrl,\tval); \t\t}; \t\tApp.RemoteDataStore\t=\tRemoteDataStore; \t\twindow.App\t=\tApp; ... Notice\tthat\tthe\tkey\targument\tis\tnot\tused.\tIt\tis\tkept\tas\tpart\tof\tthe\tmethod\tdeclaration\tso","that\tthe\tadd\tmethod\tof\tRemoteDataStore\tis\tidentical\tto\tthe\tadd\tmethod\tof DataStore.\tBoth\ttake\tthe\tcoffee\torder\tinformation\tas\tthe\tsecond\targument.\tFor\tthe RemoteDataStore,\tthis\tis\tthe\tcrucial\tpart. Adding\ta\tcallback Like\tmany\tjQuery\tmethods,\t$.post\tcan\taccept\tadditional,\toptional\targuments.\tYou\tare going\tto\tpass\tit\ta\tcallback\tfunction\tas\ta\tthird\targument.\tWhen\tthe\tresponse\tarrives\tfrom the\tserver,\tthis\tfunction\twill\tbe\tcalled\tand\tthe\tdata\tin\tthe\tresponse\twill\tbe\tpassed\tto\tit. This\tis\tsimilar\tto\tthe\tevent\thandling\tcode\tyou\thave\twritten\t\u2013\tyou\tregister\ta\tfunction\tto\trun at\tsome\tpoint\tin\tthe\tfuture.\tWhen\thandling\tevents,\tthis\tpoint\tis\tsomething\tlike\ta\tmouse click\tor\ta\tform\tsubmission.\tWhen\thandling\tremote\tdata,\tit\tis\tthe\tthe\tarrival\tof\ta\tresponse from\tthe\tserver. Add\tan\tanonymous\tfunction\tas\tthe\tthird\targument\tto\t$.post.\tThis\tanonymous\tfunction should\texpect\tan\targument,\twhich\tyou\twill\tlabel\tserverResponse.\tPrint\tthis serverResponse\tto\tthe\tconsole\tusing\tconsole.log. ... \t\tRemoteDataStore.prototype.add\t=\tfunction\t(key,\tval)\t{ \t\t\t\t$.post(this.serverUrl,\tval,\tfunction\t(serverResponse)\t{ \t\t\t\t\t\tconsole.log(serverResponse); \t\t\t\t}); \t\t}; \t\tApp.RemoteDataStore\t=\tRemoteDataStore; \t\twindow.App\t=\tApp; ... Now\t$.post\tknows\tthree\tthings:\twho\tto\ttalk\tto,\twhat\tto\tsay,\tand\twhat\tto\tdo\twith\tthe information\tit\tgets\tback\tin\tthe\tresponse. After\tyou\tsave\tthese\tchanges\tto\tremotedatastore.js,\tstart\tbrowser-sync\tand\topen the\tconsole\tin\tyour\tbrowser.\tInstantiate\ta\tRemoteDataStore\tobject\twith\tthe\tURL shown\tbelow,\twhich\tis\tthe\taddress\tof\tthe\ttest\tserver\tcreated\tfor\tthis\tbook.\t(Once\tagain, this\tline\tis\tbroken\tin\torder\tto\tfit\ton\tthe\tpage;\tenter\tit\ton\tone\tline.) var\tremoteDS\t=\tnew\tApp.RemoteDataStore \t\t\t\t(\\\"http:\/\/coffeerun-v2-rest-api.herokuapp.com\/api\/coffeeorders\\\"); Now\tinvoke\tits\tadd\tmethod,\tpassing\tit\tsome\ttest\tdata: remoteDS.add('[email protected]',\t{emailAddress:\t'[email protected]',\tcoffee:\t'espresso'}); In\tthe\tconsole,\tlook\tat\twhat\twas\tprinted\tfrom\tyour\tconsole.log\tstatement (Figure\t13.4). Figure\t13.4\t\tConsole\tshowing\tresult\tof\tcalling\tRemoteDataStore.add","The\tobject\tin\tthe\tconsole\u2019s\tlog\tstatement\tcontains\tsome\tinformation\tsent\tback\tby\tthe server\tin\tits\tresponse:\tthe\tcoffee\tand\temailAddress\tinformation,\tin\taddition\tto\tsome bookkeeping\tdata\tthat\twill\tvary\tfrom\tserver\tto\tserver. Inspecting\tthe\tAjax\trequest\tand\tresponse In\tthe\tDevTools,\topen\tthe\tnetwork\tpanel\tby\tclicking\tNetwork\tin\tthe\tmenu\tat\tthe\ttop (between\tSources\tand\tTimeline).\tThis\tpanel\tshows\ta\tlist\tof\trequests\tthat\tyour\tbrowser\thas made\tand\tlets\tyou\tinspect\teach\tof\tthem\tfor\tmore\tinformation\t(Figure\t13.5). Figure\t13.5\t\tViewing\tAjax\trequests\tin\tthe\tnetwork\tpanel Your\tnetwork\tpanel\twill\tlikely\thave\tmany\tnetwork\trequests\tin\tthe\tlist.\tClear\tit\tout\tby clicking\tthe\t \ticon\tnear\tthe\tupper\tleft\tof\tthe\tDevTools.\tThen\tactivate\tthe\tconsole\tdrawer at\tthe\tbottom\tso\tyou\tcan\tsee\tthe\tconsole\tas\twell\tas\tthe\tnetwork\tpanel.\tYou\tcan\tdo\tthis\tby pressing\tthe\tEscape\tkey\ton\tyour\tkeyboard\tor\tby\tclicking\tthe\t \ticon\tin\tthe\tupper\tright. This\twill\topen\ta\tmenu\twith\tthe\toption\tShow\tconsole. The\tconsole\tdrawer\twill\tappear\tat\tthe\tbottom\tof\tthe\tDevTools\t(Figure\t13.6).","Figure\t13.6\t\tConsole\tdrawer\topen\tbelow\tthe\tnetwork\tpanel In\tthe\tconsole,\tenter\tthe\tfollowing: var\tremoteDS\t=\tnew\tApp.RemoteDataStore \t\t\t\t(\\\"http:\/\/coffeerun-v2-rest-api.herokuapp.com\/api\/coffeeorders\\\"); remoteDS.add('[email protected]',\t{emailAddress:\t'[email protected]',\tcoffee:\t'espresso'}); You\twill\tsee\ta\tnew\tentry\tin\tthe\tnetwork\tpanel\t(Figure\t13.7). Figure\t13.7\t\tAjax\trequest\tin\tnetwork\tpanel To\tfind\tout\tmore\tabout\tthe\trequest,\tclick\ton\tits\tentry\t(Figure\t13.8).\tYou\tmay\tfind\tit\teasier","to\tview\tthe\tdetails\tif\tyou\thide\tthe\tconsole\tdrawer. Figure\t13.8\t\tRequest\tdetails The\tdetails\tinclude\tsome\tgeneral\tinformation\tabout\tthe\trequest\tat\tthe\ttop\tand\tthe\tform data\tat\tthe\tbottom.\tIn\tthe\tmiddle\tare\tpanes\tthat\tshow\tthe\trequest\tand\tresponse\theaders. Headers\tare\tmetadata\tand\toptions\tthat\thave\tbeen\tspecified\tfor\tthe\trequest\tand\tresponse. Of\tall\tthis\tdata,\tthe\tstatus\tcode\t(in\tthe\tGeneral\tpane)\tand\tthe\tForm\tData\tpane\tare\tusually\tthe most\tuseful\twhile\tdeveloping\tand\tdebugging\tAjax\trequests.","Retrieving\tData\tfrom\tthe\tServer Your\tRemoteDataStore\tmodule\tcan\tsave\tindividual\tcoffee\torders\tto\tthe\tserver.\tThe next\tthing\tto\tdo\tis\tadd\ta\tgetAll\tprototype\tmethod\tso\tthat\tit\tcan\tretrieve\tall\torders\tfrom the\tserver.\tGet\tit\tstarted\tin\tremotedatastore.js: ... \t\tRemoteDataStore.prototype.add\t=\tfunction\t(key,\tval)\t{ \t\t\t\t... \t\t}; \t\tRemoteDataStore.prototype.getAll\t=\tfunction\t()\t{ \t\t\t\t\/\/\tCode\twill\tgo\there \t\t}; \t\tApp.RemoteDataStore\t=\tRemoteDataStore; \t\twindow.App\t=\tApp; ... Next\tyou\twill\tadd\ta\tcall\tto\tjQuery\u2019s\t$.get\tmethod.\tLike\t$.post,\tyou\twill\tpass\tit\tthe server\tURL.\tBut\tyou\twill\tnot\tpass\tit\tany\tdata,\tbecause\tyou\tare\tretrieving\tinformation instead\tof\tsaving\tinformation.\tYou\twill\tneed\tto\tpass\tit\ta\tfunction\targument\tso\tthat\tit knows\twhat\tto\tdo\twith\tthe\tdata\twhen\tit\tcomes\tback\tfrom\tthe\tserver. Call\t$.get\tin\tRemoteDataStore.prototype.getAll. ... \t\tRemoteDataStore.prototype.getAll\t=\tfunction\t()\t{ \t\t\t\t\/\/\tCode\twill\tgo\there \t\t\t\t$.get(this.serverUrl,\tfunction\t(serverResponse)\t{ \t\t\t\t\t\tconsole.log(serverResponse); \t\t\t\t}); \t\t}; \t\tApp.RemoteDataStore\t=\tRemoteDataStore; \t\twindow.App\t=\tApp; ... Save\tand\treturn\tto\tthe\tDevTools\tin\tthe\tbrowser. Inspecting\tthe\tresponse\tdata In\tthe\tconsole,\tinstantiate\ta\tRemoteDataStore\twith\tthe\tsame\tURL\tas\tbefore.\t(Pro\ttip: Instead\tof\tre-typing\tthe\tvery\tlong\tline\twith\tthe\tURL,\tyou\tcan\tuse\tthe\tup\tand\tdown\tarrow keys\tto\tcycle\tthrough\tstatements\tyou\thave\tentered\tinto\tthe\tconsole.)\tThen\tcall\tits\tgetAll method: var\tremoteDS\t=\tnew\tApp.RemoteDataStore \t\t\t\t(\\\"http:\/\/coffeerun-v2-rest-api.herokuapp.com\/api\/coffeeorders\\\"); remoteDS.getAll(); In\tthe\tnetwork\tpanel\tof\tthe\tDevTools,\tyou\twill\tsee\tthat\tthe\tGET\trequest\twent\tout.\tIt\tshould return\twithin\ta\tfew\tdozen\tmilliseconds.\tSome\tcoffee\torder\tinfo\t(from\tdata\tpreloaded\ton the\tserver)\twill\tappear\tin\tthe\tconsole\t(Figure\t13.9).","Figure\t13.9\t\tInspecting\tthe\tresponse\tfrom\tgetAll You\tmay\tsee\tslightly\tdifferent\tresults,\tdepending\ton\twhat\thas\tbeen\tadded\tto\tthe\tserver. However,\tgetting\tany\tresult\tshows\tthat\tyou\tare\tsuccessfully\tretrieving\tdata\tfrom\tthe server. Adding\ta\tcallback\targument You\tcan\tretrieve\tthe\tdata\tfrom\tthe\tserver,\tbut\tyou\tcannot\treturn\tit\tfrom\tgetAll.\tThis\tis because\tgetAll\tonly\tmakes\tthe\tinitial\tAjax\trequest;\tit\tdoes\tnot\thandle\tthe\tresponse. Instead,\tyou\tpass\ta\tresponse\thandling\tcallback\tto\t$.get.\tYour\tresponse\thandling callback\twill\twork\tlike\tthe\tevent\thandling\tcallbacks\tyou\thave\twritten\t\u2013\tin\tboth\tcases,\tthe callback\tshould\texpect\tto\treceive\tan\targument.\tThis\tmeans\tthat\tthe\tresponse\tdata\tis\tonly available\tinside\tthe\tbody\tof\tthe\tcallback.\tHow\twill\tyou\taccess\tit\toutside\tof\tthat\tcallback? If\tyou\tpass\tgetAll\ta\tfunction\targument,\tyou\tcan\tcall\tthat\tfunction\tinside\tthe\t$.get callback.\tThere,\tyou\thave\taccess\tto\tboth\tthe\tfunction\targument\tand\tthe\tserver\tresponse. Add\tthe\tfunction\targument\tand\tcall\tin\tremotedatastore.js: ... \t\tRemoteDataStore.prototype.getAll\t=\tfunction\t(cb)\t{ \t\t\t\t$.get(this.serverUrl,\tfunction\t(serverResponse)\t{ \t\t\t\t\t\tconsole.log(serverResponse); \t\t\t\t\t\tcb(serverResponse); \t\t\t\t}); \t\t}; \t\tApp.RemoteDataStore\t=\tRemoteDataStore; \t\twindow.App\t=\tApp; ... The\tgetAll\tmethod\tretrieves\tall\tthe\tcoffee\torders\ton\tthe\tremote\tserver\tand\tpasses\tthem","to\tthe\tcallback\tcb\tfunction\tthat\tis\tpassed\tin. You\talso\tneed\tto\timplement\tthe\tget\tmethod,\twhich\tretrieves\ta\tsingle\tcoffee\torder\tby\tthe customer\temail\taddress.\tLike\tgetAll,\tit\twill\taccept\ta\tfunction\targument,\twhich\tit\twill call\tand\tpass\tthe\tretrieved\tcoffee\torder. Add\tthis\timplementation\tof\tget\tto\tremotedatastore.js: ... \t\tRemoteDataStore.prototype.getAll\t=\tfunction\t(cb)\t{ \t\t\t\t... \t\t}; \t\tRemoteDataStore.prototype.get\t=\tfunction\t(key,\tcb)\t{ \t\t\t\t$.get(this.serverUrl\t+\t'\/'\t+\tkey,\tfunction\t(serverResponse)\t{ \t\t\t\t\t\tconsole.log(serverResponse); \t\t\t\t\t\tcb(serverResponse); \t\t\t\t}); \t\t}; \t\tApp.RemoteDataStore\t=\tRemoteDataStore; \t\twindow.App\t=\tApp; ... Save\tyour\tchanges\tto\tremotedatastore.js.\tEnter\tthe\tfollowing\tcode\tin\tthe\tconsole, passing\tan\tempty\tanonymous\tfunction\tto\tremoteDS.get.\t(It\tis\texpecting\ta\tfunction argument,\tbut\tyou\tonly\twant\tto\ttake\tit\tfor\ta\tquick\ttest.) var\tremoteDS\t=\tnew\tApp.RemoteDataStore \t\t\t\t(\\\"http:\/\/coffeerun-v2-rest-api.herokuapp.com\/api\/coffeeorders\\\"); remoteDS.get('[email protected]',\tfunction\t()\t{}); Your\tconsole\twill\tlook\tsomething\tlike\tFigure\t13.10. Figure\t13.10\t\tTesting\tRemoteDataStore.prototype.get","Deleting\tData\tfrom\tthe\tServer Using\tAjax,\tyou\tcan\tnow\tsave\torders\tto\tthe\tserver\tand\tretrieve\torders\tfrom\tthe\tserver.\tThe last\tthing\tto\tdo\tis\tto\tdelete\torders\tfrom\tthe\tserver\twhen\tthey\tare\tdelivered. To\tdo\tthis,\tyou\twill\tsend\tan\tHTTP\trequest\tto\tthe\tURL\tfor\tan\tindividual\torder.\tAs\tyou\tdid with\tRemoteDataStore.prototype.get,\tyou\twill\tuse\tthe\tserver\tURL,\tbut\tyou will\tappend\ta\tslash\tand\tthe\tcustomer\u2019s\temail\taddress. You\twill\tbe\tsending\tthe\tserver\ta\tDELETE\trequest.\tDELETE\tis\tone\tof\tthe\tHTTP\tverbs.\tThe server\tknows\tto\tremove\tthe\tdata\tassociated\twith\tthat\tcustomer\u2019s\temail\taddress\tif\tit receives\ta\tDELETE\trequest\tat\tthat\tURL. Using\tjQuery\u2019s\t$.ajax\tmethod jQuery\tprovides\tthe\t$.get\tand\t$.post\tmethods\tas\ta\tconvenience\tbecause\tthese\tare\tthe two\tmost\tcommon\tHTTP\tverbs\tused.\tFor\texample,\ta\tGET\trequest\tis\tused\twhenever\tthe browser\tasks\tfor\tan\tHTML,\tCSS,\tJavaScript,\tor\timage\tfile\t(among\tothers).\tA\tPOST\trequest is\tused\tmost\toften\twhen\ta\tform\tis\tsubmitted. jQuery\tdoes\tnot\tprovide\ta\tconvenience\tmethod\tfor\tsending\tDELETE\trequests\tvia\tAjax. Instead,\tyou\twill\tneed\tto\tuse\tthe\t$.ajax\tmethod.\t($.get\tand\t$.post\tactually\tcall $.ajax\tfor\tyou,\tspecifying\tGET\tand\tPOST\tas\tthe\tHTTP\tverbs.) In\tremotedatastore.js,\tadd\tthe\tremove\tprototype\tmethod.\tIn\tit,\tcall\tthe\t$.ajax method,\tpassing\tit\ttwo\targuments.\tThe\tfirst\targument\tis\tan\tindividual\tcoffee\torder\u2019s\tURL, made\tup\tof\tthe\tserver\tURL,\ta\tslash,\tand\tthe\tkey\t(the\tcustomer\u2019s\temail\taddress).\tThe second\targument\tis\tan\tobject\tthat\tcontains\tthe\toptions,\tor\tsettings,\tfor\tthe\tAjax\trequest. The\tonly\toption\tyou\tneed\tto\tspecify\tfor\tthe\tremove\tmethod\tis\tthat\tthe\ttype\tis\tDELETE. ... \t\tRemoteDataStore.prototype.get\t=\tfunction\t(key,\tcb)\t{ \t\t\t\t... \t\t}; \t\tRemoteDataStore.prototype.remove\t=\tfunction\t(key)\t{ \t\t\t\t$.ajax(this.serverUrl\t+\t'\/'\t+\tkey,\t{ \t\t\t\t\t\ttype:\t'DELETE' \t\t\t\t}); \t\t}; \t\tApp.RemoteDataStore\t=\tRemoteDataStore; \t\twindow.App\t=\tApp; ... (There\tare\tmany\toptions\tavailable\tfor\tAjax\trequests,\twhich\tyou\tcan\tread\tabout\tat api.jquery.com\/\u200bjquery.ajax.) Save\tand\treturn\tto\tthe\tconsole.\tInstantiate\ta\tnew\tRemoteDataStore\tand\tinvoke\tthe remove\tmethod.\tPass\tit\tthe\temail\taddress\tfor\tthe\ttest\torders\tyou\thave\tcreated.\tFinally, call\tgetAll\tto\tconfirm\tthat\tit\tis\tno\tlonger\tincluded\tin\tthe\torders\treturned\tfrom\tthe\tserver. var\tremoteDS\t=\tnew\tApp.RemoteDataStore \t\t\t\t(\\\"http:\/\/coffeerun-v2-rest-api.herokuapp.com\/api\/coffeeorders\\\"); remoteDS.remove('[email protected]'); remoteDS.getAll(); If\tyou\tinspect\tthe\tresponse\tfrom\tthe\tserver\tfor\tthe\tDELETE\trequest,\tyou\tcan\tsee\tthat\tthe","server\tsends\tback\tinformation\tabout\twhat\tit\tdid\t(Figure\t13.11).\t(As\twe\tmentioned\tearlier, other\tservers\tmay\tprovide\tdifferent\tinformation\tin\tthe\tresponse.) Figure\t13.11\t\tInspecting\tthe\tresponse\tfrom\ta\tDELETE\trequest","Replacing\tDataStore\twith\tRemoteDataStore Your\tRemoteDataStore\tmodule\tis\tcomplete.\tIt\tis\ttime\tto\treplace\tyour\tDataStore instance\twith\ta\tRemoteDataStore\tinstance. Open\tmain.js.\tBegin\tby\timporting\tRemoteDataStore\tfrom\tthe\tApp\tnamespace. (function\t(window)\t{ \t\t'use\tstrict'; \t\tvar\tFORM_SELECTOR\t=\t'[data-coffee-order=\\\"form\\\"]'; \t\tvar\tCHECKLIST_SELECTOR\t=\t'[data-coffee-order=\\\"checklist\\\"]'; \t\tvar\tApp\t=\twindow.App; \t\tvar\tTruck\t=\tApp.Truck; \t\tvar\tDataStore\t=\tApp.DataStore; \t\tvar\tRemoteDataStore\t=\tApp.RemoteDataStore; \t\tvar\tFormHandler\t=\tApp.FormHandler; ... Also,\tadd\ta\tnew\tvariable\tcalled\tSERVER_URL\tand\tassign\tit\ta\tstring\twith\tthe\tURL\tof\tthe CoffeeRun\ttest\tserver. (function\t(window)\t{ \t\t'use\tstrict'; \t\tvar\tFORM_SELECTOR\t=\t'[data-coffee-order=\\\"form\\\"]'; \t\tvar\tCHECKLIST_SELECTOR\t=\t'[data-coffee-order=\\\"checklist\\\"]'; \t\tvar\tSERVER_URL\t=\t'http:\/\/coffeerun-v2-rest-api.herokuapp.com\/api\/coffeeorders'; \t\tvar\tApp\t=\twindow.App; \t\tvar\tTruck\t=\tApp.Truck; \t\tvar\tDataStore\t=\tApp.DataStore; \t\tvar\tRemoteDataStore\t=\tApp.RemoteDataStore; ... Next,\tcreate\ta\tnew\tinstance\tof\tRemoteDataStore,\tpassing\tit\tSERVER_URL. ... \t\tvar\tRemoteDataStore\t=\tApp.RemoteDataStore; \t\tvar\tFormHandler\t=\tApp.FormHandler; \t\tvar\tValidation\t=\tApp.Validation; \t\tvar\tCheckList\t=\tApp.CheckList; \t\tvar\tremoteDS\t=\tnew\tRemoteDataStore(SERVER_URL); \t\tvar\twebshim\t=\twindow.webshim; \t\tvar\tmyTruck\t=\tnew\tTruck('ncc-1701',\tnew\tDataStore()); \t\twindow.myTruck\t=\tmyTruck; ... Finally,\tinstead\tof\tpassing\tthe\tTruck\tconstructor\ta\tnew\tinstance\tof\tDataStore,\tpass\tit remoteDS.\tBecause\tDataStore\tand\tRemoteDataStore\thave\tmethods\twith\tthe same\tnames\tand\ttake\t(mostly)\tthe\tsame\targuments,\tthis\tchange\twill\twork\tseamlessly. ... \t\tvar\tRemoteDataStore\t=\tApp.RemoteDataStore; \t\tvar\tFormHandler\t=\tApp.FormHandler; \t\tvar\tValidation\t=\tApp.Validation; \t\tvar\tCheckList\t=\tApp.CheckList; \t\tvar\tremoteDS\t=\tnew\tRemoteDataStore(SERVER_URL); \t\tvar\twebshim\t=\twindow.webshim; \t\tvar\tmyTruck\t=\tnew\tTruck('ncc-1701',\tnew\tDataStore());\tremoteDS); \t\twindow.myTruck\t=\tmyTruck; ... Save\tyour\tchanges\tand\tgo\tback\tto\tthe\tbrowser.\tEnter\tsome\tcoffee\torder\tinformation\tand submit\tthe\tform.\tKeep\tthe\tnetwork\tpanel\topen\twhile\tyou\tdo\tso.\tYou\tshould\tsee\tnetwork transactions\tfor\tevery\tcoffee\torder\tyou\tadd\tthrough\tthe\tform\tor\tdeliver\tvia\tthe\tchecklist (Figure\t13.12).","Figure\t13.12\t\tSaving\tcoffee\torders\tto\tthe\tremote\tserver Congratulations!\tCoffeeRun\tis\tfully\tfunctional\tand\tis\tintegrated\twith\ta\tremote\tweb service. The\tnext\tchapter\tis\tthe\tfinal\tone\tfor\tCoffeeRun.\tIt\tdoes\tnot\tadd\tany\tnew\tfeatures.\tInstead, it\tfocuses\ton\trefactoring\tyour\texisting\tcode\tso\tyou\tcan\tlearn\ta\tnew\tpattern\tfor\tworking with\tasynchronous\tcode.","Silver\tChallenge:\tValidating\tAgainst\tthe\tRemote Server Your\tvalidation\tcode\tcurrently\tdoes\ta\tsimple\tdomain\tcheck.\tUpdate\tyour\tvalidation\tcode so\tthat\tit\talso\tchecks\twhether\tan\temail\taddress\thas\talready\tbeen\tused\tfor\tan\torder\tthat exists\ton\tthe\tserver.\tPrevent\tthe\tform\tfrom\tbeing\tsubmitted\tif\tthat\taddress\thas\tbeen\tused and\tprovide\tan\tappropriate\tvalidation\twarning. You\tmay\twant\tto\topen\ta\tsecond\tbrowser\twindow\tfor\tCoffeeRun\tand\tenter\tdifferent\tcoffee orders\tin\tthe\ttwo\twindows. Pay\tattention\tto\thow\toften\ta\trequest\tis\tsent\tto\tthe\tserver\twhen\tdoing\tthis\tvalidation\tcheck. (You\tcan\tsee\tthese\tin\tthe\tDevTools\tnetwork\tpanel.)\tCan\tyou\tfind\ta\tgood\tway\tto\tminimize the\tnumber\tof\trequests?","For\tthe\tMore\tCurious:\tPostman One\tof\tthe\tbest\ttools\tfor\tsending\ttest\trequests\tto\ta\tserver\tis\tPostman,\ta\tfree\tplug-in\tfor Chrome.\tIt\tlets\tyou\tbuild\tHTTP\trequests\tand\tspecify\tthe\tHTTP\tverbs,\tform\tdata,\theaders, and\tuser\tcredentials\t(Figure\t13.13). Figure\t13.13\t\tThe\tPostman Postman\tis\tan\tindispensable\ttool\tfor\texploring\tan\tAPI\tbefore\tyou\twrite\tyour\tserver communication\tcode.\tDownload\tit\tfrom\tthe\tChrome\tweb\tstore,\tchrome.google.com\/\u200b webstore.\t(Search\tfor\t\u201cPostman\u201d\tto\tfind\tit.)","14\t Deferreds\tand\tPromises In\tCoffeeRun,\tyour\tmodular\tcode\thas\thelped\tyou\tavoid\tthe\tdreaded\t\u201cspaghetti\tcode\u201d\tthat can\teasily\thappen\twhen\tyou\tmix\tevent-handling\t(UI)\tcode\twith\tyour\tapplication\u2019s\tinternal logic. Your\tmodules\tinteract\tvia\tfunction\targuments,\talso\tknown\tas\tcallbacks.\tCallbacks\tare\ta fine\tsolution\tfor\tsituations\tin\twhich\tyou\thave\tcode\tthat\tonly\tdepends\ton\ta\tsingle, asynchronous\tstep.\tFigure\t14.1\tshows\ta\tsimplified\tversion\tof\tone\tasynchronous\tflow\tfrom CoffeeRun. Figure\t14.1\t\tAsynchronous\tflow\tfor\tadding\ta\tcoffee\torder What\thappens\twhen\tyou\thave\tmany\tdependent\tasynchronous\tsteps?\tOne\toption\tis\tto\tnest lots\tof\tcallbacks,\tbut\tthis\tquickly\tbecomes\tunwieldy\tand\tdangerous.\tWith\ta\tsimplified version\tof\tyour\tsubmit\thandler\tcode\tthat\tdoes\textra\terror\tchecking,\tthat\tapproach\tmight look\tlike\tthis: formHandler.addSubmitHandler(function\t(data)\t{ \t\ttry\t{ \t\t\t\tmyTruck.createOrder(function\t(error)\t{ \t\t\t\t\t\tif\t(error)\t{ \t\t\t\t\t\t\t\tthrow\tnew\tException(error) \t\t\t\t\t\t}\telse\t{ \t\t\t\t\t\t\t\ttry\t{ \t\t\t\t\t\t\t\t\t\tsaveOnServer(function\t(error)\t{ \t\t\t\t\t\t\t\t\t\t\t\tif\t(error)\t{ \t\t\t\t\t\t\t\t\t\t\t\t\t\tthrow\tnew\tException({message:\t'server\terror'}); \t\t\t\t\t\t\t\t\t\t\t\t}\telse\t{ \t\t\t\t\t\t\t\t\t\t\t\t\t\ttry\t{ \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcheckList.addRow(); \t\t\t\t\t\t\t\t\t\t\t\t\t\t}\tcatch\t(e2)\t{ \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\thandleDomError(e2); \t\t\t\t\t\t\t\t\t\t\t\t\t\t} \t\t\t\t\t\t\t\t\t\t\t\t} \t\t\t\t\t\t\t\t\t\t}) \t\t\t\t\t\t\t\t}\tcatch\t(e)\t{ \t\t\t\t\t\t\t\t\t\thandleServerError(e,\tfunction\t()\t{ \t\t\t\t\t\t\t\t\t\t\t\t\/\/\tTry\tadding\tthe\trow\tagain \t\t\t\t\t\t\t\t\t\t\t\ttry\t{ \t\t\t\t\t\t\t\t\t\t\t\t\t\tcheckList.addRow(); \t\t\t\t\t\t\t\t\t\t\t\t}\tcatch\t(e3)\t{ \t\t\t\t\t\t\t\t\t\t\t\t\t\thandleDomError(e3); \t\t\t\t\t\t\t\t\t\t\t\t} \t\t\t\t\t\t\t\t\t\t}); \t\t\t\t\t\t\t\t} \t\t\t\t\t\t} \t\t\t\t}); \t\t}\tcatch\t(e)\t{ \t\t\t\talert('Something\tbad\thappened.'); \t\t} }); Promises,\twhich\tyou\twill\tlearn\tabout\tin\tthis\tchapter,\tare\ta\tbetter\tsolution.\tThat\tsame series\tof\tsteps\tmight\tbe\texpressed\tas\ta\tchain\tof\tPromises\tlike\tthis: formHandler.addSubmitHandler() \t\t.then(myTruck.createOrder) \t\t.then(saveOnServer)",".catch(handleServerError) \t\t.then(checkList.addRow) \t\t.catch(handleDomError); Promises\tprovide\ta\tway\tto\tarchitect\tvery\tcomplex\tasynchronous\tcode\tin\ta\tmanageable way,\tand\tin\tthis\tchapter\tyou\twill\tuse\tthem\tto\tsimplify\tthe\tarchitecture\tof\tCoffeeRun. Promises\tare\ta\trelatively\tnew\tfeature,\tbut\tthey\tare\twell\tsupported\tin\trecent\tbrowsers, including\tChrome. In\tCoffeeRun,\tyou\tare\tmainly\tinterested\tin\tperforming\tthe\tnext\tstep\tif\tthe\tcurrent\tone succeeds\twithout\terrors.\tPromises\tmake\tthis\tsimple.\tInstead\tof\trelying\ton\tcallback arguments,\tyou\twill\treturn\tPromise\tobjects,\twhich\twill\tlet\tyou\tdecouple\tyour\tmodules even\tfurther. Promises\tand\tDeferreds Promise\tobjects\tare\talways\tin\tone\tof\tthree\tstates:\tpending,\tfulfilled,\tor\trejected (Figure\t14.2). Figure\t14.2\t\tThree\tstates\tof\ta\tPromise\tobject Every\tPromise\tobject\thas\ta\tthen\tmethod\tthat\tis\ttriggered\twhen\tthe\tPromise\tbecomes fulfilled.\tYou\tcan\tcall\tthen\tand\tpass\tit\ta\tcallback;\twhen\tthe\tPromise\tis\tfulfilled,\tthe callback\tis\tinvoked\tand\tpassed\twhatever\tvalue\tthe\tPromise\treceived\twhen\tdoing\tits asynchronous\twork. You\tcan\talso\tchain\tmultiple\tthen\tcalls\ttogether.\tInstead\tof\twriting\tfunctions\tthat\taccept and\tthen\tinvoke\tcallbacks,\tit\tis\tbetter\tto\treturn\tPromise\tobjects\tand\tlet\tthe\tcaller\tchain\ta then\toff\tof\tthat\tPromise. You\tare\tgoing\tto\tstart\twith\tjQuery\u2019s\tDeferred\tobject,\twhich\tworks\tsimilarly\tto\ta\tPromise for\tsimple\tuse\tcases. jQuery\u2019s\t$.ajax\tmethods\t(including\t$.post\tand\t$.get)\treturn\ta\tDeferred. Deferred\tobjects\thave\tmethods\tthat\tlet\tyou\tregister\tcallbacks\tfor\ttwo\tof\ttheir\tstates:","fulfilled\tand\trejected.\tYou\tare\tgoing\tto\tstart\tby\tupdating\tRemoteDataStore\tso\tthat\tit returns\tthe\tDeferreds\tproduced\tby\tjQuery\u2019s\tAjax\tmethods.\tLater,\tyou\twill\tmodify\tyour other\tmodules\tto\tregister\tcallbacks\twith\tthe\tDeferreds.","Returning\tDeferred Take\tadvantage\tof\tthe\tDeferred\tobjects\treturned\tby\tjQuery\u2019s\t$.ajax\tmethods.\tIn remotedatastore.js,\tupdate\tthe\tprototype\tmethods\tso\tthat\tthey\treturn\tthe\tresult\tof calling\t$.get,\t$.post,\tand\t$.ajax. ... \t\tRemoteDataStore.prototype.add\t=\tfunction\t(key,\tval)\t{ \t\t\t\treturn\t$.post(this.serverUrl,\tval,\tfunction\t(serverResponse)\t{ \t\t\t\t\t\tconsole.log(serverResponse); \t\t\t\t}); \t\t}; \t\tRemoteDataStore.prototype.getAll\t=\tfunction\t(cb)\t{ \t\t\t\treturn\t$.get(this.serverUrl,\tfunction\t(serverResponse)\t{ \t\t\t\t\t\tconsole.log(serverResponse); \t\t\t\t\t\tcb(serverResponse); \t\t\t\t}); \t\t}; \t\tRemoteDataStore.prototype.get\t=\tfunction\t(key,\tcb)\t{ \t\t\t\treturn\t$.get(this.serverUrl\t+\t'\/'\t+\tkey,\tfunction\t(serverResponse)\t{ \t\t\t\t\t\tconsole.log(serverResponse); \t\t\t\t\t\tcb(serverResponse); \t\t\t\t}); \t\t}; \t\tRemoteDataStore.prototype.remove\t=\tfunction\t(key)\t{ \t\t\t\treturn\t$.ajax(this.serverUrl\t+\t'\/'\t+\tkey,\t{ \t\t\t\t\t\ttype:\t'DELETE' \t\t\t\t}); \t\t}; ... Because\tthey\tnow\treturn\tthe\tDeferred\tproduced\tby\tjQuery\u2019s\tAjax\tmethods,\tit\tis\tnot absolutely\tnecessary\tfor\tget\tand\tgetAll\tto\taccept\tcallbacks.\tTo\taccount\tfor\tthe possibility\tof\tno\tcallback,\tadd\tan\tif\tstatement\tto\tcheck\tthat\tcb\twas\tpassed\tin\tbefore invoking\tit. ... \t\tRemoteDataStore.prototype.getAll\t=\tfunction\t(cb)\t{ \t\t\t\treturn\t$.get(this.serverUrl,\tfunction\t(serverResponse)\t{ \t\t\t\t\t\tif\t(cb)\t{ \t\t\t\t\t\t\t\tconsole.log(serverResponse); \t\t\t\t\t\t\t\tcb(serverResponse); \t\t\t\t\t\t} \t\t\t\t}); \t\t}; \t\tRemoteDataStore.prototype.get\t=\tfunction\t(key,\tcb)\t{ \t\t\t\treturn\t$.get(this.serverUrl\t+\t'\/'\t+\tkey,\tfunction\t(serverResponse)\t{ \t\t\t\t\t\tif\t(cb)\t{ \t\t\t\t\t\t\t\tconsole.log(serverResponse); \t\t\t\t\t\t\t\tcb(serverResponse); \t\t\t\t\t\t} \t\t\t\t}); \t\t}; ... Save\tremotedatastore.js.\tSince\tthe\tRemoteDataStore\tmethods\treturn Deferreds,\tyou\twill\tneed\tto\tupdate\tthe\tTruck\tmethods\tto\tdo\tthe\tsame.\tFor\tnow,\tyou\twill focus\ton\tcreateOrder\tand\tdeliverOrder. Open\ttruck.js\tand\tadd\ta\treturn\tto\tthese\ttwo\tmethods\twhere\tyou\tcall\tthem\ton this.db. ... \t\tTruck.prototype.createOrder\t=\tfunction\t(order)\t{ \t\t\t\tconsole.log('Adding\torder\tfor\t'\t+\torder.emailAddress); \t\t\t\treturn\tthis.db.add(order.emailAddress,\torder); \t\t}; \t\tTruck.prototype.deliverOrder\t=\tfunction\t(customerId)\t{ \t\t\t\tconsole.log('Delivering\torder\tfor\t'\t+\tcustomerId); \t\t\t\treturn\tthis.db.remove(customerId); \t\t}; ...","Save\ttruck.js.\tTruck\tnow\treturns\tthe\tDeferreds\tthat\tRemoteDataStore produces.\tWhen\tusing\tPromises\tand\tDeferreds,\tit\tis\ta\tbest\tpractice\tto\treturn\tthem\tfrom your\tfunctions.\tReturning\tthem\tlets\tany\tobject\tthat\tcalls\tcreateOrder\tor deliverOrder\tregister\tcallbacks\tthat\tare\ttriggered\twhen\tthe\tasynchronous\twork\tis finished. In\tthe\tnext\tsection,\tyou\twill\tdo\tjust\tthat.","Registering\tCallbacks\twith\tthen $.ajax\treturns\ta\tDeferred,\twhich\thas\ta\tthen\tmethod.\tThe\tthen\tmethod\tregisters\ta callback\tthat\tis\trun\twhen\tthe\tDeferred\tis\tresolved.\tWhen\tthe\tcallback\tis\tinvoked,\tit\tis passed\tthe\tvalue\tsent\tback\tin\tthe\tserver\tresponse\t(Figure\t14.3). Figure\t14.3\t\tDeferred\tobject\tinvokes\tcallbacks\tregistered\twith\tthen Start\twith\ta\tsimple\tusage\tof\tthen.\tIn\tmain.js,\tyour\tsubmit\thandler\tcalls createOrder\tand\taddRow.\tChange\tthis\tso\tthat\taddRow\tis\tregistered\tas\ta\tcallback\tof createOrder. Open\tmain.js\tand\tupdate\tthe\tcall\tto\tformHandler.addSubmitHandler.\tChain\ta .then\tto\tthe\tinvocation\tof\tcreateOrder.\tPass\tit\ta\tcallback\tthat\truns checkList.addRow. ... \t\tformHandler.addSubmitHandler(function\t(data)\t{ \t\t\t\tmyTruck.createOrder.call(myTruck,\tdata); \t\t\t\t\t\t.then(function\t()\t{ \t\t\t\t\t\t\t\tcheckList.addRow.call(checkList,\tdata); \t\t\t\t\t\t}); \t\t}); ... Instead\tof\tinvoking\taddRow\timmediately\tafter\tcreateOrder,\tyou\tare\tmaking addRow\tdependent\ton\tcreateOrder\tcompleting\twithout\terrors\tor\texceptions.","Handling\tFailures\twith\tthen then\taccepts\ta\tsecond\targument,\twhich\tis\tinvoked\twhen\tthe\tDeferred\tshifts\tto\tthe rejected\tstate.\tTo\tsee\tthis\tin\taction,\tadd\ta\tsecond\tfunction\targument\t(making\tsure\tto\tadd\ta comma\tbetween\tthe\ttwo\tfunction\targuments)\tto\tformHandler.addSubmitHandler in\tmain.js.\tInside\tof\tthis\tfunction,\tshow\tan\talert\twith\ta\tsimple\terror\tmessage. ... \t\tformHandler.addSubmitHandler(function\t(data)\t{ \t\t\t\tmyTruck.createOrder.call(myTruck,\tdata) \t\t\t\t\t\t.then(function\t()\t{ \t\t\t\t\t\t\t\tcheckList.addRow.call(checkList,\tdata); \t\t\t\t\t\t}, \t\t\t\t\t\tfunction\t()\t{ \t\t\t\t\t\t\t\talert('Server\tunreachable.\tTry\tagain\tlater.'); \t\t\t\t\t\t} \t\t\t\t\t\t); \t\t}); ... At\tthe\ttop\tof\tmain.js,\tmisspell\tthe\tserver\tname\tso\tthat\tAjax\trequests\tfail.\t(This\tchange will\tonly\tbe\ttemporary,\tso\tyou\tmay\twant\tto\tsimply\tcut\ta\tsection\tout\tof\tthe\tURL\tthat\tyou can\tpaste\tback\tin\tlater.) (function\t(window)\t{ \t\t'use\tstrict'; \t\tvar\tFORM_SELECTOR\t=\t'[data-coffee-order=\\\"form\\\"]'; \t\tvar\tCHECKLIST_SELECTOR\t=\t'[data-coffee-order=\\\"checklist\\\"]'; \t\tvar\tSERVER_URL\t=\t'http:\/\/coffeerun-v2-rest-api.herokuapp.com\/api\/coffeeorders\/'; \t\tvar\tApp\t=\twindow.App; ... Save\tyour\tchanges,\tmake\tsure\tbrowser-sync\tis\trunning,\tand\topen\tCoffeeRun\tin\tthe browser.\tFill\tout\tthe\tform.\tYou\tshould\tsee\tan\talert\tpop\tup\twhen\tyou\tsubmit\tit (Figure\t14.4).","Figure\t14.4\t\tAlert\tshown\twhen\tAjax\tcall\tfails Restore\tthe\tSERVER_URL\tto\thttp:\/\/coffeerun-v2-rest- api.herokuapp.com\/api\/coffeeorders\/.\tYou\tcan\talso\tdelete\tthe\tfunction argument\tthat\tshows\tthe\talert. (function\t(window)\t{ \t\t'use\tstrict'; \t\tvar\tFORM_SELECTOR\t=\t'[data-coffee-order=\\\"form\\\"]'; \t\tvar\tCHECKLIST_SELECTOR\t=\t'[data-coffee-order=\\\"checklist\\\"]'; \t\tvar\tSERVER_URL\t=\t'http:\/\/coffeerun-v2-rest-api.herokuapp.com\/api\/coffeeorders\/'; \t\t... \t\tformHandler.addSubmitHandler(function\t(data)\t{ \t\t\t\tmyTruck.createOrder.call(myTruck,\tdata) \t\t\t\t\t\t.then(function\t()\t{ \t\t\t\t\t\t\t\tcheckList.addRow.call(checkList,\tdata); \t\t\t\t\t\t}, \t\t\t\t\t\tfunction\t()\t{ \t\t\t\t\t\t\t\talert('Server\tunreachable.\tTry\tagain\tlater.'); \t\t\t\t\t\t} \t\t\t\t\t\t); \t\t}); ... Using\tthen\tto\tregister\tcallbacks\tmaps\tonto\tthe\tway\tPromises\twork.\tIf\tthe\tPromise changes\tstate\tto\tfulfilled,\tone\tset\tof\tcallbacks\tis\trun.\tIf\tthe\tPromise\tchanges\tstate\tto rejected,\tthe\tother\tset\tof\tcallbacks\tis\trun.","Using\tDeferreds\twith\tCallback-Only\tAPIs Sometimes\tyou\twill\tneed\tto\tcoordinate\tyour\tDeferred-based\tcode\twith\tcallback-only APIs,\tsuch\tas\tevent\tlisteners. Currently,\tformHandler.addSubmitHandler\tresets\tthe\tform\tand\tfocuses\ton\tthe first\telement\t\u2013\tno\tmatter\twhat\thappens\twith\tthe\tAjax\trequest.\tHowever,\tyou\tonly\twant those\tthings\tto\thappen\tif\tthe\tAjax\trequest\tis\tsuccessful.\tPut\tanother\tway,\tyou\tonly\twant those\tthings\tto\thappen\tif\tthe\tDeferred\tis\tfulfilled. How\tcan\tyou\tknow\twhether\tthe\tDeferred\tis\tfulfilled?\tYour\tfunction\targument\tto addSubmitHandler\tcan\treturn\tthe\tDeferred,\tand\tinside\tof\taddSubmitHandler you\tcan\tchain\ta\t.then\tcall\tto\tthe\tDeferred. In\tmain.js,\tadd\ta\treturn\tkeyword\tto\tthe\tcallback. ... \t\tformHandler.addSubmitHandler(function\t(data)\t{ \t\t\t\treturn\tmyTruck.createOrder.call(myTruck,\tdata) \t\t\t\t\t\t.then(function\t()\t{ \t\t\t\t\t\t\t\tcheckList.addRow.call(checkList,\tdata); \t\t\t\t\t\t}); \t\t}); ... Save\tmain.js.\tNext,\topen\tformhandler.js,\tfind\tthe\taddSubmitHandler method,\tand\tlocate\tthe\tcall\tto\tthe\tanonymous\tfunction,\tfn.\tBecause\tthat\tanonymous function\tnow\treturns\ta\tDeferred,\tyou\tcan\tchain\ta\tcall\tto\t.then\ton\tthe\tend\tof\tit.\tUse .then\tto\tregister\ta\tcallback\tthat\tresets\tthe\tform\tand\tfocuses\ton\tthe\tfirst\telement. ... \t\tFormHandler.prototype.addSubmitHandler\t=\tfunction\t(fn)\t{ \t\t\t\tconsole.log('Setting\tsubmit\thandler\tfor\tform'); \t\t\t\tthis.$formElement.on('submit',\tfunction\t(event)\t{ \t\t\t\t\t\tevent.preventDefault(); \t\t\t\t\t\tvar\tdata\t=\t{}; \t\t\t\t\t\t$(this).serializeArray().forEach(function\t(item)\t{ \t\t\t\t\t\t\t\tdata[item.name]\t=\titem.value; \t\t\t\t\t\t\t\tconsole.log(item.name\t+\t'\tis\t'\t+\titem.value); \t\t\t\t\t\t}); \t\t\t\t\t\tconsole.log(data); \t\t\t\t\t\tfn(data); \t\t\t\t\t\t.then(function\t()\t{ \t\t\t\t\t\t\t\tthis.reset(); \t\t\t\t\t\t\t\tthis.elements[0].focus(); \t\t\t\t\t\t}); \t\t\t\t}); \t\t}; ... Before,\tyou\thad\tthree\tsequential\tstatements:\tinvoke\tthe\tcallback,\treset\tthe\tform,\tand\tfocus on\tthe\tfirst\tform\telement.\tNow,\tyou\thave\tone\tstatement\tthat\tdepends\ton\tthe\tresult\tof\tthe previous\tstatement.\tYou\tinvoke\tthe\tcallback\tand\t\u2013\tif\tthe\tpromised\twork\tfinishes\texecution normally,\twithout\tencountering\tan\texception\t\u2013\tthen\tyou\treset\tthe\tform\tand\tfocus\ton\tthe first\tform\telement. There\tis\tjust\tone\tconcern.\tWhen\tyou\tregister\ta\tcallback\tfunction\twith\t.then,\tthat callback\tfunction\thas\ta\tnew\tscope.\tYou\tneed\tto\t.bind\tthat\tanonymous\tfunction\tso\tthat the\tvalue\tof\tthis\tis\tset\tto\tthe\tFormHandler\tinstance. Make\tthis\tchange\tin\tformhandler.js. ... \t\tFormHandler.prototype.addSubmitHandler\t=\tfunction\t(fn)\t{ \t\t\t\t...","fn(data) \t\t\t\t\t\t\t\t.then(function\t()\t{ \t\t\t\t\t\t\t\t\t\tthis.reset(); \t\t\t\t\t\t\t\t\t\tthis.elements[0].focus(); \t\t\t\t\t\t\t\t}.bind(this)); \t\t\t\t}); \t\t}; ... Save\tformhandler.js. Similarly,\tyou\tonly\twant\tto\tremove\tan\titem\tfrom\tthe\tchecklist\tif\tthe\tcall\tto Truck.prototype.deliverOrder\tis\tsuccessful. Chain\ta\tcall\tto\t.then\toff\tthe\tfunction\tpassed\tto\taddClickHandler\tin checklist.js.\tRemember\tto\t.bind\tthe\tvalue\tof\tthis\tfor\tthe\tanonymous\tfunction. ... \t\tCheckList.prototype.addClickHandler\t=\tfunction\t(fn)\t{ \t\t\t\tthis.$element.on('click',\t'input',\tfunction\t(event)\t{ \t\t\t\t\t\tvar\temail\t=\tevent.target.value; \t\t\t\t\t\tthis.removeRow(email); \t\t\t\t\t\tfn(email); \t\t\t\t\t\t\t\t.then(function\t()\t{ \t\t\t\t\t\t\t\t\t\tthis.removeRow(email); \t\t\t\t\t\t\t\t}.bind(this)); \t\t\t\t}.bind(this)); \t\t}; ... Save\tchecklist.js.\tRecall\tthat\tyou\tinvoke\taddClickHandler\tin\tmain.js: checkList.addClickHandler(myTruck.deliverOrder.bind(myTruck); There\tis\tno\tneed\tto\tmake\tany\tchanges\tto\tthis\tmethod\tcall.\tBecause Truck.prototype.deliverOrder\tis\treturning\tthe\tDeferred, addClickHandler\twill\twork\tas\twritten. All\tof\tyour\tdata\tis\tremote,\twhich\tmeans\tyou\tneed\tto\tload\tit\tand\tdraw\tchecklist\titems\tfor each\tcoffee\torder.\tYou\tcan\tuse\tthe\tTruck.prototype.printOrders\tmethod\talong with\tthe\tCheckList.prototype.addRow\tmethod\tto\tdo\tthis. You\twill\tmake\ttwo\tchanges\tto\tprintOrders.\tFirst,\tyou\twill\tupdate\tprintOrders\tto work\twith\tDeferreds.\tThen,\tyou\twill\tadd\ta\tfunction\targument\tto\tprintOrders\twhich\tit will\tcall\tas\tit\titerates\tthrough\tthe\tdata\tto\tprint. In\ttruck.js,\tyour\tcode\tfor\tTruck.prototype.printOrders\tcurrently\tlooks\tlike this: ... \t\tTruck.prototype.printOrders\t=\tfunction\t()\t{ \t\t\t\tvar\tcustomerIdArray\t=\tObject.keys(this.db.getAll()); \t\t\t\tconsole.log('Truck\t#'\t+\tthis.truckId\t+\t'\thas\tpending\torders:'); \t\t\t\tcustomerIdArray.forEach(function\t(id)\t{ \t\t\t\t\t\tconsole.log(this.db.get(id)); \t\t\t\t}.bind(this)); \t\t}; ... Update\tthis\timplementation\tto\tcall\tand\treturn\tthis.db.getAll,\tchaining\ta\t.then\tto it.\tPass\tan\tanonymous\tfunction\tto\t.then\tand\tset\tits\tthis\tkeyword\tusing\t.bind: ... \t\tTruck.prototype.printOrders\t=\tfunction\t()\t{ \t\t\t\treturn\tthis.db.getAll() \t\t\t\t\t\t.then(function\t(orders)\t{ \t\t\t\t\t\t\t\tvar\tcustomerIdArray\t=\tObject.keys(this.db.getAll()); \t\t\t\t\t\t\t\tconsole.log('Truck\t#'\t+\tthis.truckId\t+\t'\thas\tpending\torders:'); \t\t\t\t\t\t\t\tcustomerIdArray.forEach(function\t(id)\t{ \t\t\t\t\t\t\t\t\t\tconsole.log(this.db.get(id)); \t\t\t\t\t\t\t\t}.bind(this)); \t\t\t\t\t\t}.bind(this)); \t\t}; ...","Your\tanonymous\tfunction\texpects\tto\treceive\tan\tobject\tcontaining\tall\tof\tthe\tcoffee\torder data\tretrieved\tfrom\tthe\tserver.\tExtract\tthe\tkeys\tfrom\tthat\tobject\tand\tassign\tthem\tto\tthe variable\tnamed\tcustomerIdArray. ... \t\tTruck.prototype.printOrders\t=\tfunction\t()\t{ \t\t\t\treturn\tthis.db.getAll() \t\t\t\t\t\t.then(function\t(orders)\t{ \t\t\t\t\t\t\t\tvar\tcustomerIdArray\t=\tObject.keys(this.db.getAll());\torders); \t\t\t\t\t\t\t\tconsole.log('Truck\t#'\t+\tthis.truckId\t+\t'\thas\tpending\torders:'); \t\t\t\t\t\t\t\tcustomerIdArray.forEach(function\t(id)\t{ \t\t\t\t\t\t\t\t\t\tconsole.log(this.db.get(id)); \t\t\t\t\t\t\t\t}.bind(this)); \t\t\t\t\t\t}.bind(this)); \t\t}; ... Likewise,\tchange\tthe\tconsole.log\tstatement\tso\tthat\tit\tdoes\tnot\tcall\tthis.db.get(id). It\tshould\tuse\tthe\tallData\tobject,\twhich\talready\thas\tall\tof\tthe\tcoffee\torders.\tYou\tshould not\tmake\tan\textra\tAjax\tcall\tfor\teach\titem\tthat\tneeds\tto\tbe\tprinted. ... \t\tTruck.prototype.printOrders\t=\tfunction\t()\t{ \t\t\t\treturn\tthis.db.getAll() \t\t\t\t\t\t.then(function\t(orders)\t{ \t\t\t\t\t\t\t\tvar\tcustomerIdArray\t=\tObject.keys(orders); \t\t\t\t\t\t\t\tconsole.log('Truck\t#'\t+\tthis.truckId\t+\t'\thas\tpending\torders:'); \t\t\t\t\t\t\t\tcustomerIdArray.forEach(function\t(id)\t{ \t\t\t\t\t\t\t\t\t\tconsole.log(this.db.get(id));\torders[id]); \t\t\t\t\t\t\t\t}.bind(this)); \t\t\t\t\t\t}.bind(this)); \t\t}; ... printOrders\tshould\ttake\tan\toptional\tfunction\targument.\tYou\tneed\tto\tcheck\twhether\tit was\tpassed\tin\tand,\tif\tit\twas,\tinvoke\tit.\tWhen\tyou\tinvoke\tit,\tyou\twill\tpass\tit\tthe\tcurrent coffee\torder\tallData[id]. ... \t\tTruck.prototype.printOrders\t=\tfunction\t(printFn)\t{ \t\t\t\treturn\tthis.db.getAll() \t\t\t\t\t\t.then(function\t(orders)\t{ \t\t\t\t\t\t\t\tvar\tcustomerIdArray\t=\tObject.keys(orders); \t\t\t\t\t\t\t\tconsole.log('Truck\t#'\t+\tthis.truckId\t+\t'\thas\tpending\torders:'); \t\t\t\t\t\t\t\tcustomerIdArray.forEach(function\t(id)\t{ \t\t\t\t\t\t\t\t\t\tconsole.log(orders[id]); \t\t\t\t\t\t\t\t\t\tif\t(printFn)\t{ \t\t\t\t\t\t\t\t\t\t\t\tprintFn(orders[id]); \t\t\t\t\t\t\t\t\t\t} \t\t\t\t\t\t\t\t}.bind(this)); \t\t\t\t\t\t}.bind(this)); \t\t}; ... Save\ttruck.js.\tIn\tmain.js,\tinvoke\tprintOrders\tand\tpass\tit checkList.addRow.\tMake\tsure\tthat\taddRow\tis\tbound\tto\tthe\tCheckList\tinstance. ... \t\tformHandler.addInputHandler(Validation.isCompanyEmail); \t\tmyTruck.printOrders(checkList.addRow.bind(checkList)); \t\twebshim.polyfill('forms\tforms-ext'); \t\twebshim.setOptions('forms',\t{\taddValidators:\ttrue,\tlazyCustomMessages:\ttrue\t}); })(window); Save\tand\treturn\tto\tthe\tbrowser.\tCoffeeRun\tshould\tshow\tthe\texisting\tcoffee\torders\tin\tthe checklist.\tManually\treload\tto\tconfirm\tthat\tthe\tchecklist\tis\trepopulated\teach\ttime.\tInspect the\tnetwork\tpanel\tand\tsee\tthat\tAjax\trequests\tare\ttaking\tplace\t(Figure\t14.5).","Figure\t14.5\t\tDrawing\torders\ton\tpage\tload","Giving\tDataStore\ta\tPromise By\treturning\tDeferreds\tfrom\tRemoteDataStore\u2019s\tmethods,\tyou\thave\ta\tflexible\tway to\tuse\tthe\tdata\tsent\tback\tfrom\tthe\tserver. But\tyou\tmay\thave\tnoticed\tthat\tRemoteDataStore\u2019s\tmethods\tnow\tstray\tfar\taway\tfrom how\tDataStore\u2019s\tmethods\twork.\tIf\tyou\twere\tto\tswap\ta\tregular\tDataStore\tback\tin, you\twould\tsee\tthat\tit\tno\tlonger\tworks\twith\tyour\tapplication\t(Figure\t14.6). Figure\t14.6\t\tUh\toh.\tDataStore\tis\tno\tlonger\tcompatible In\tFigure\t14.6,\tyou\tcan\tsee\tthat\tinstantiating\ta\tTruck\twith\ta\tregular\tDataStore\tthrows errors\tand\tfails\tto\twork\tcorrectly\twith\tthe\tUI.\tCoffeeRun\texpects\ta\tPromise-based DataStore\tin\torder\tto\tfunction\tcorrectly. To\tremedy\tthis\tsituation,\tyou\tare\tgoing\tto\tchange\tDataStore\u2019s\tfour\tmethods\tso\tthat they\treturn\tPromises. jQuery\u2019s\tDeferred\tobjects\thave\ttreated\tyou\twell.\tHowever,\tbecause\tDataStore\tis\tnot using\tthe\tjQuery\t$.ajax\tmethods\tyou\thave\tbeen\tusing\tto\taccess\tDeferreds,\tyou\twill need\tto\tuse\tthe\tnative\tPromise\tconstructor\tto\tcreate\tand\treturn\tPromises. Creating\tand\treturning\tPromises In\tdatastore.js,\tyou\tare\tgoing\tto\tupdate\tthe\tadd\tmethod.\tBut\tfirst,\tcreate\ta","Promise\tvariable\tand\tassign\tit\tthe\tvalue\twindow.Promise.\tWhile\tnot\tabsolutely necessary,\tit\tis\ta\tgood\tidea\tto\tcontinue\tthis\tpattern\tof\timporting\tanything\tfrom\tthe\tglobal scope\tthat\tyou\twill\tneed\tinside\tof\tyour\tmodule. Inside\tthe\tadd\tmethod,\tcreate\ta\tnew\tvariable\tcalled\tpromise.\tAssign\tit\ta\tnew\tinstance of\tPromise.\tMake\tsure\tto\treturn\tthe\tpromise\tvariable\tat\tthe\tend\tof\tadd. (function\t(window)\t{ \t\t'use\tstrict'; \t\tvar\tApp\t=\twindow.App\t||\t{}; \t\tvar\tPromise\t=\twindow.Promise; \t\tfunction\tDataStore()\t{ \t\t\t\tthis.data\t=\t{}; \t\t} \t\tDataStore.prototype.add\t=\tfunction\t(key,\tval)\t{ \t\t\t\tthis.data[key]\t=\tval; \t\t\t\tvar\tpromise\t=\tnew\tPromise(); \t\t\t\treturn\tpromise; \t\t}; ... The\tPromise\tconstructor\tneeds\ta\tfunction\targument.\tPass\tit\tan\tanonymous\tfunction\tthat accepts\ttwo\tfunction\targuments,\tresolve\tand\treject. ... \t\tDataStore.prototype.add\t=\tfunction\t(key,\tval)\t{ \t\t\t\tthis.data[key]\t=\tval; \t\t\t\tvar\tpromise\t=\tnew\tPromise(function\t(resolve,\treject)\t{ \t\t\t\t}); \t\t\t\treturn\tpromise; \t\t}; ... When\tthe\tPromise\tdoes\tits\twork,\tit\twill\tinvoke\tthe\tanonymous\tfunction\targument\tand pass\tit\ttwo\tvalues:\tresolve\tand\treject.\tThe\tresolve\tfunction\tis\tinvoked\tto\tchange the\tstate\tof\tthe\tPromise\tobject\tto\tfulfilled.\tThe\treject\tfunction\tis\tinvoked\tto\tchange\tthe state\tof\tthe\tPromise\tobject\tto\trejected. Next,\tmove\tthe\tdata\tstorage\tline\t(this.data[key]\t=\tval;)\tdown\tinto\tthe\tbody\tof\tthe anonymous\tfunction.\tTo\tmake\tsure\tthat\tthis.data\tcorrectly\trefers\tto\tthe\tDataStore\u2019s data\tinstance\tvariable,\tbind\tthe\tanonymous\tfunction\tto\tthis. ... \t\tDataStore.prototype.add\t=\tfunction\t(key,\tval)\t{ \t\t\t\tthis.data[key]\t=\tval; \t\t\t\tvar\tpromise\t=\tnew\tPromise(function\t(resolve,\treject)\t{ \t\t\t\t\t\tthis.data[key]\t=\tval; \t\t\t\t}.bind(this)); \t\t\t\treturn\tpromise; \t\t}; ... Resolving\ta\tPromise At\tthe\tvery\tend\tof\tthe\tanonymous\tfunction,\tinvoke\tresolve\twith\tno\targument. ... \t\tDataStore.prototype.add\t=\tfunction\t(key,\tval)\t{ \t\t\t\tvar\tpromise\t=\tnew\tPromise(function\t(resolve,\treject)\t{ \t\t\t\t\t\tthis.data[key]\t=\tval; \t\t\t\t\t\tresolve(null); \t\t\t\t}.bind(this)); \t\t\t\treturn\tpromise; \t\t}; ... Why\tuse\tnull\tas\tthe\targument?\tadding\ta\tvalue\tto\tthe\tDataStore\tdoes\tnot\tproduce\ta value,\tso\tthere\tis\tnothing\tfor\tit\tto\tresolve\tto.\tWhen\tyou\tneed\tto\texplicitly\treturn\ta\tnon-","value\tyou\tshould\tuse\tnull.\t(You\tcould\talso\tuse\tresolve(val)\tto\tgive\tthe\tnext\tfunction\tin the\tchain\taccess\tto\tthe\tfreshly\tstored\tvalue.\tFor\tCoffeeRun,\tthis\tis\tnot\tnecessary,\tand therefore\tnot\tincluded\tas\tpart\tof\tthe\texample.) Promise-ifying\tthe\tother\tDataStore\tmethods You\tcould\tmanually\tupdate\tthe\tother\tthree\tmethods\tusing\tthis\tsame\tpattern\tof\tcode.\tBut instead\tof\tretyping\tall\tthat\tcode,\tcreate\ta\thelper\tfunction\tcalled promiseResolvedWith\tto\tcreate\ta\tPromise,\tresolve\tit,\tand\treturn\tit.\tUpdate DataStore.prototype.add\tto\tuse\tthis\thelper. ... \t\tfunction\tDataStore()\t{ \t\t\t\tthis.data\t=\t{}; \t\t} \t\tfunction\tpromiseResolvedWith(value)\t{ \t\t\t\tvar\tpromise\t=\tnew\tPromise(function\t(resolve,\treject)\t{ \t\t\t\t\t\tresolve(value); \t\t\t\t}); \t\t\t\treturn\tpromise; \t\t} \t\tDataStore.prototype.add\t=\tfunction\t(key,\tval)\t{ \t\t\t\tvar\tpromise\t=\tnew\tPromise(function\t(resolve,\treject)\t{ \t\t\t\t\t\tthis.data[key]\t=\tval; \t\t\t\t\t\tresolve(null); \t\t\t\t}.bind(this)); \t\t\t\treturn\tpromise; \t\t\t\treturn\tpromiseResolvedWith(null); \t\t}; ... promiseResolvedWith\tis\ta\treusable\tform\tof\tthe\tPromise\tcode\tyou\twrote\tin\tthe\tadd method.\tIt\taccepts\ta\tparameter\tcalled\tvalue,\tcreates\ta\tnew\tvariable\tnamed\tpromise, and\tassigns\tit\tto\ta\tnew\tinstance\tof\tPromise.\tIt\tpasses\tan\tanonymous\tfunction\tto\tthe Promise\tconstructor\tthat\taccepts\ttwo\targuments:\tresolve\tand\treject.\tInside\tthe anonymous\tfunction,\tyou\tinvoke\tresolve\tand\tpass\tit\tthe\tvalue\targument. In\tpromiseResolvedWith,\tyou\tdo\tnot\tneed\tto\tbind\tthe\tfunction\targument\tto\tthis, as\tthere\tare\tno\treferences\tto\tthis\tthat\tneed\tto\tbe\tmaintained. Update\tthe\tother\tmethods\tto\tuse\tpromiseResolvedWith.\tPass\tget\tand\tgetAll\tthe value\tyou\twere\treturning\tin\tthe\tnon-Promise\tversion.\tPass\tnull\tto\tremove. ... \t\tDataStore.prototype.get\t=\tfunction\t(key)\t{ \t\t\t\treturn\tthis.data[key]; \t\t\t\treturn\tpromiseResolvedWith(this.data[key]); \t\t}; \t\tDataStore.prototype.getAll\t=\tfunction\t()\t{ \t\t\t\treturn\tthis.data; \t\t\t\treturn\tpromiseResolvedWith(this.data); \t\t}; \t\tDataStore.prototype.remove\t=\tfunction\t(key)\t{ \t\t\t\tdelete\tthis.data[key]; \t\t\t\treturn\tpromiseResolvedWith(null); \t\t}; ... Finally,\tupdate\tmain.js\tto\tuse\ta\tDataStore\tinstead\tof\ta\tRemoteDataStore. ... \t\tvar\tremoteDS\t=\tnew\tRemoteDataStore(SERVER_URL); \t\tvar\twebshim\t=\twindow.webshim; \t\tvar\tmyTruck\t=\tnew\tTruck('ncc-1701',\tremoteDS);\tnew\tDataStore()); \t\twindow.myTruck\t=\tmyTruck; ... After\tmaking\tthese\tchanges,\tsave\tyour\tcode\tand\ttake\tCoffeeRun\tfor\tanother\tspin.\tYou","should\tsee\tthat\tit\tworks\tcorrectly\tusing\tDataStore,\tbut\tmakes\tno\tAjax\trequests (Figure\t14.7). Figure\t14.7\t\tCoffeeRun\tis\tdone! CoffeeRun\thas\ttaken\tyou\ton\tquite\ta\tjourney!\tAlong\tthe\tway,\tyou\twrote\tsome\tpretty serious\tJavaScript\tusing\tIIFEs,\tcallbacks,\tand\tPromises.\tYou\talso\tgot\ta\ttaste\tof\tjQuery, which\tyou\tused\tto\tmanipulate\tDOM\telements\tand\tcommunicate\twith\ta\tRESTful\tweb service. It\tis\ttime\tto\tpart\tways\twith\tCoffeeRun\tand\tmove\ton.\tThe\tnext\tapp,\tChattrbox,\tis\ta\tfull- stack\tchat\tapplication.\tYou\twill\tnot\tonly\tcreate\tthe\tfront-end\tcode\tbut\talso\twrite\tthe server.\tDo\tnot\tworry\tif\tthis\tis\tyour\tfirst\tserver\tapplication.\tYou\twill\tstill\tbe\tusing JavaScript,\tjust\tnot\tfor\tthe\tbrowser.\tGet\tready\tto\twork\twith\tNode.js!","Silver\tChallenge:\tFallback\tto\tDataStore If\tyou\tare\tlucky,\tyou\thave\ta\tnice,\tstable\tnetwork\tconnection\tall\tthe\ttime.\tBut\tyou\tshould be\tprepared\tin\tcase\tyour\tconnection\tgoes\tdown\twhile\tusing\tCoffeeRun. Update\tCoffeeRun\tso\tthat\tit\tuses\ta\tDataStore\twhen\tits\tAjax\trequests\tcannot\treach\tthe server. To\tmake\tsure\tthat\tthis\tis\tworking,\tturn\toff\tyour\tcomputer\u2019s\tnetwork\tconnection\twhile loading\tand\tsaving\tcoffee\torders.","Part\tIII\t Real-Time\tData"]


Like this book? You can publish your book online for free in a few minutes!
Create your own flipbook