PhoneGap and AngularJS for Cross-platform Development · Simon Basset is a cross-platform mobile...
Transcript of PhoneGap and AngularJS for Cross-platform Development · Simon Basset is a cross-platform mobile...
PhoneGapandAngularJSforCross-platformDevelopment
TableofContents
PhoneGapandAngularJSforCross-platformDevelopment
Credits
AbouttheAuthor
AbouttheReviewers
www.PacktPub.com
Supportfiles,eBooks,discountoffers,andmore
Whysubscribe?
FreeaccessforPacktaccountholders
Preface
Whatthisbookcovers
Whatyouneedforthisbook
Whothisbookisfor
Conventions
Readerfeedback
Customersupport
Downloadingtheexamplecode
Errata
Piracy
Questions
1.IntroductiontoAngularJS
AbriefoverviewofAngularJS
Coreconcepts
Controllers
Data-binding
Directives
Aconceptualexample
Asimpleto-dolistusingAngularJS
Preparingyourcodestructure
HTMLforourto-dolist
AddinginJavaScriptwithAngularJS
Summary
2.GettingReadyforPhoneGap
PreparingforPhoneGapdevelopment
InstallingAndroid
InstallingiOS
Command-lineinterfaceforbothAndroidandiOS
Runningonrealdevices
AngularJSonPhoneGap
Whatjusthappened?
Creatingato-dolistappusingAngularJSonPhoneGap
Abasicversionofato-dolistusingAngularJSonPhoneGap
Summary
3.FromaSimpleTo-doListtoanAdvancedTo-doList
Rewritingthesimpleto-dolistapp
Splittingindex.htmlintomultiplefiles
Splittingtodo.jsintomultiplefiles
Checkpoint
Wiringupabackendserver
Codingourserver
ChangingAngularJStoperformRESTfulrequests
Usingthe$httpmoduleofAngularJS
Rewritingcontrollerstomakeuseofthe$httpmodule
Checkingourcode
PreparingforPhoneGap
TestingourcodeoniOS
TestingourcodeonAndroid
Summary
4.AddingAuthenticationCapabilitiesUsingPhoneGapPlugins
AddingFacebookConnecttotheto-dolistapp
InitializingandpreparingforFacebookConnect
Writingtheusercontroller
Addingaloginpage
Addingalogoutfunction
Checkingtheloginstatus
FacebookloginforPhoneGap
InstallingtheFacebookplugin
TestingoutFacebookLoginonPhoneGap
FromwebtoPhoneGap
ImportingFacebookandPhoneGapplugins
ChangingFBtofacebookConnectPlugin
Theto-dolistappwithFacebookLoginonPhoneGap
Summary
5.SprucingUptheAppUsingAnimationsandMobileDesign
Addinganimationstoyourwebapp
AddingmobileCSSstylestoyourapp
PortingyourwebapptoPhoneGap
TestingyourapponiOS
TestingyourapponAndroid
Summary
6.GettingReadytoLaunch
Deployingserver.py
Usingphonegap.com
PreparingyourPhoneGapappforanAndroidrelease
Testingyourapponrealdevices
Exportingyourapptoinstallonotherdevices
Preparingpromotionalartworkforrelease
Buildingyourappforrelease
Signingtheapp
iOS
RunningyourapponaniOSdevice
Othertechniques
UsingXcode
Summary
A.References
AngularJSandrelatedlibraries
PhoneGapandrelatedreferences
Others
Othertutorials
Index
PhoneGapandAngularJSforCross-platformDevelopment
PhoneGapandAngularJSforCross-platformDevelopmentCopyright©2014PacktPublishing
Allrightsreserved.Nopartofthisbookmaybereproduced,storedinaretrievalsystem,ortransmittedinanyformorbyanymeans,withoutthepriorwrittenpermissionofthepublisher,exceptinthecaseofbriefquotationsembeddedincriticalarticlesorreviews.
Everyefforthasbeenmadeinthepreparationofthisbooktoensuretheaccuracyoftheinformationpresented.However,theinformationcontainedinthisbookissoldwithoutwarranty,eitherexpressorimplied.Neithertheauthor,norPacktPublishing,anditsdealersanddistributorswillbeheldliableforanydamagescausedorallegedtobecauseddirectlyorindirectlybythisbook.
PacktPublishinghasendeavoredtoprovidetrademarkinformationaboutallofthecompaniesandproductsmentionedinthisbookbytheappropriateuseofcapitals.However,PacktPublishingcannotguaranteetheaccuracyofthisinformation.
Firstpublished:October2014
Productionreference:1241014
PublishedbyPacktPublishingLtd.
LiveryPlace
35LiveryStreet
BirminghamB32PB,UK.
ISBN978-1-78398-892-1
www.packtpub.com
CoverimagebyAniketSawant(<[email protected]>)
CreditsAuthor
Yuxian,EugeneLiang
Reviewers
SimonBasset
RaziMahmood
CommissioningEditor
KunalParikh
AcquisitionEditor
MeetaRajani
ContentDevelopmentEditor
PriyankaShah
TechnicalEditors
VeronicaFernandes
AnandSingh
CopyEditors
RoshniBanerjee
AdithiShetty
ProjectCoordinator
KartikVedam
Proofreaders
MariaGould
ElinorPerry-Smith
Indexers
MonicaAjmeraMehta
TejalSoni
ProductionCoordinators
KyleAlbuquerque
NileshR.Mohite
CoverWork
NileshR.Mohite
AbouttheAuthorYuxian,EugeneLiangisafrontendengineerwithworkingknowledgeofUXanddatamining/machinelearning.HebuildsapplicationspredominantlyusingJavaScript/Python,andrelatedframeworkssuchasAngularJS,ReactJS,Node.js,Tornado,andDjango.Heledateamoftwo(includinghimself)towinStartupWeekendatTaiwanrecently.Hehasalsocompletedacademicresearchonsocialnetworkanalysis(linkprediction)usingmachinelearningtechniques,whileinterningasafrontendengineeratYahoo!.Toknowmoreabouthim,visithttp://www.liangeugene.com.
IamgratefulforthisopportunityandIwanttothankthefollowingpeopleatPacktPublishing:PriyankaShah,KartikVedam,SageerParkar,MeetaRajani,andAnandSingh.
SpecialthankstoProfessorDaphneYuan,ProfessorTsai-YenLi,andProfessorPailinChenoftheNationalChengChiUniversity,Taipei,Taiwan,forprovidingmewithtimelyandpracticaladviceonhowtocarryoutgreatresearchandhowtodealwithlife.
TothegoodpeopleofServiceScienceResearchCenter,IntelligentMediaLab,andtheresearchteamoftheFloodandFireresearchproject,thankyouforhelpingmeoutwhenIneededitthemost.
IalsowanttothankXJLforhelpingmeoutandstayingputwhenImostneededsupport.
Lastbutnotleast,Iwanttothankmyfamilymembersandfriendsfortheircontinuedsupport.
AbouttheReviewersSimonBassetisacross-platformmobileandfrontendengineerlivinginParis.Heworkshardeverydaytocreateattractivemobileandwebapps.
HeworkedforyearsatSmileOpenSourceSolutions,technicallyleadingateamspecializinginmobiledevelopment,andhasrecentlyjoinedthefrontendexpertteamofAXAFrance.
Heisatechnologyenthusiast.Helikestotryandusecutting-edgetechnologiesandlovestheWebandopensource.Healsolovesanimals,hastwocats,andisavegetarian.
RaziMahmoodhasaMaster’sdegreeinITwith14yearsofworkingexperience,andisanaccomplishedandexperiencedsoftwaretrainingconsultant.Hisinterestintechnologyneverfadesandhealwayskeepshimselfupdatedwiththelatesttechnology.Asaresult,hehassucceededinmanyareasinhiscareer.Heismotivatedandisaquicklearner,andhastheabilitytohandleprojectswithminimumsupervision;thesearehispersonalstrengthsineveryachievement.
RazistartedhiscareerasanexecutiveinanengineeringfirminKualaLumpur.Overtheyears,hehasdevelopedcustomsoftwaresolutionstoexpediteworkinaccounting,humanresources,andprojectmanagementreporting.ThesesolutionswereeventuallydocumentedandpresentedaspartofhisprojectthesisforhisMaster’sdegree.UponcompletionofhisMaster’sdegree,hejoinedtheSchoolOfTechnologyManagement,BinaryUniversityasalecturerinSoftwareEngineeringandAccountingInformationSystem.Sincethen,hehassupervisedvariousapplicationdevelopmentprojectsundertakenbystudentsusingvariousplatformssuchasWindows,Linux,OSX,Android,andiOS.In2008,hewasappointedasamemberofthePanelofAssessorsofMalaysianQualityAssuranceProgramme,specializingindatabases.Heisalsoaco-developerforaneducation-basedmobileappsprojectendorsedbytheMalaysianMinistryOfEducationtohelpstudentslearnlocalhistorysubjects.ThisappisnowfeaturedonGoogle’sPlaystore(https://play.google.com/store/apps/details?id=com.fiziazezan2gmail.com.ism2&hl=en).
www.PacktPub.com
Supportfiles,eBooks,discountoffers,andmoreYoumightwanttovisitwww.PacktPub.comforsupportfilesanddownloadsrelatedtoyourbook.
DidyouknowthatPacktofferseBookversionsofeverybookpublished,withPDFandePubfilesavailable?YoucanupgradetotheeBookversionatwww.PacktPub.comandasaprintbookcustomer,youareentitledtoadiscountontheeBookcopy.Getintouchwithusat<[email protected]>formoredetails.
Atwww.PacktPub.com,youcanalsoreadacollectionoffreetechnicalarticles,signupforarangeoffreenewslettersandreceiveexclusivediscountsandoffersonPacktbooksandeBooks.
http://PacktLib.PacktPub.com
DoyouneedinstantsolutionstoyourITquestions?PacktLibisPackt’sonlinedigitalbooklibrary.Here,youcanaccess,readandsearchacrossPackt’sentirelibraryofbooks.
Whysubscribe?FullysearchableacrosseverybookpublishedbyPacktCopyandpaste,printandbookmarkcontentOndemandandaccessibleviawebbrowser
FreeaccessforPacktaccountholdersIfyouhaveanaccountwithPacktatwww.PacktPub.com,youcanusethistoaccessPacktLibtodayandviewnineentirelyfreebooks.Simplyuseyourlogincredentialsforimmediateaccess.
PrefaceWelcometoAngularJSwithPhoneGap!Inthisbook,youwillreceivepracticalknowledgeaboutAngularJSandPhoneGap.Inparticular,youwilllearnhowtobuildacomplete,workablewebappusingAngularJS,afterwhichyouwillconvertvariousversionsofthiswebapptoaPhoneGapapp.YoushouldalsopickupsomethingnewregardingPhoneGapinparticular:howtousethecommand-lineinterfacetogeneratePhoneGapapps.
WhatthisbookcoversChapter1,IntroductiontoAngularJS,willteachyoutheabsolutebasicsofbuildinganAngularJSapp.
Chapter2,GettingReadyforPhoneGap,willcoverthePhoneGapcommand-lineinterface.Byendofthischapter,youwillhavelearnedthatthecommand-lineinterfaceisoneofthebestthingsaboutPhoneGap3.x.TheexampleyoucodedinChapter1,IntroductiontoAngularJS,willbeputtouseinthischapter.
Chapter3,FromaSimpleTo-doListtoanAdvancedTo-doList,willcoversomeoftheslightlymoreadvancedconceptsofAngularJS,suchascodeorganization,makingRESTfulcalls,andmore.ThisadvancedappwillthenbeconvertedtoaPhoneGapapp.
Chapter4,AddingAuthenticationCapabilitiesUsingPhoneGapPlugins,willaddFacebookauthenticationcapabilitiesviaPhoneGapplugins.Onceagain,youwillseehowwecanaddtheFacebookpluginusingthecommand-lineinterface.
Chapter5,SprucingUptheAppUsingAnimationsandMobileDesign,willcoveraslightlymoreadvancedAngularJStopic:animations.
Chapter6,GettingReadytoLaunch,willteachyouhowtolaunchtheapp,bothinAndroidandiOSdevices.
Appendix,References,hasalistofreferencesthatyoushouldfinduseful.
WhatyouneedforthisbookThisbookassumesthatyouhaveabasiccodeeditor.YouwillneedaMacifyouintendtodevelopiOSversionsofthePhoneGapapp.YouwillmostdefinitelyrequireanInternetconnectionandtheGoogleChromebrowser.
WhothisbookisforThisbookisintendedforpeoplewhoarenotfamiliarwithAngularJSbuthavebeginnerexperienceinPhoneGap,andwhomightwanttoimprovetheirPhoneGapskillsbylearningthecommand-lineinterfaceforPhoneGap3.x,anddevelopPhoneGapappsusingAngularJS.
ConventionsInthisbook,youwillfindanumberofstylesoftextthatdistinguishbetweendifferentkindsofinformation.Herearesomeexamplesofthesestyles,andanexplanationoftheirmeaning.
Codewordsintext,databasetablenames,foldernames,filenames,fileextensions,pathnames,dummyURLs,userinput,andTwitterhandlesareshownasfollows:“Feelfreetocopythecodeandsaveitasconcepts.html.”
Ablockofcodeissetasfollows:
project/
css/
js/
controllers/
todo.js
services/
todo.js
app.js
partials/
detail.html
list.html
index.html
Whenwewishtodrawyourattentiontoaparticularpartofacodeblock,therelevantlinesoritemsaresetinbold:
<ling-repeat="todointodos">
<inputtype="checkbox"ng-model="todo.done">
<spanclass="done-{{todo.done}}">{{todo.text}}</span>
<buttonng-click="showDetail(todo.text)">Detail</button>
</li>
Anycommand-lineinputoroutputiswrittenasfollows:
cordovaemulateandroid
Newtermsandimportantwordsareshowninbold.Wordsthatyouseeonthescreen,inmenusordialogboxesforexample,appearinthetextlikethis:“ClickonEdit/DetailsfortheAndroiditemandstartediting.”
NoteWarningsorimportantnotesappearinaboxlikethis.
TipTipsandtricksappearlikethis.
ReaderfeedbackFeedbackfromourreadersisalwayswelcome.Letusknowwhatyouthinkaboutthisbook—whatyoulikedormayhavedisliked.Readerfeedbackisimportantforustodeveloptitlesthatyoureallygetthemostoutof.
Tosendusgeneralfeedback,simplysendane-mailto<[email protected]>,andmentionthebooktitleviathesubjectofyourmessage.
Ifthereisatopicthatyouhaveexpertiseinandyouareinterestedineitherwritingorcontributingtoabook,seeourauthorguideonwww.packtpub.com/authors.
CustomersupportNowthatyouaretheproudownerofaPacktbook,wehaveanumberofthingstohelpyoutogetthemostfromyourpurchase.
DownloadingtheexamplecodeYoucandownloadtheexamplecodefilesforallPacktbooksyouhavepurchasedfromyouraccountathttp://www.packtpub.com.Ifyoupurchasedthisbookelsewhere,youcanvisithttp://www.packtpub.com/supportandregistertohavethefilese-maileddirectlytoyou.
ErrataAlthoughwehavetakeneverycaretoensuretheaccuracyofourcontent,mistakesdohappen.Ifyoufindamistakeinoneofourbooks—maybeamistakeinthetextorthecode—wewouldbegratefulifyouwouldreportthistous.Bydoingso,youcansaveotherreadersfromfrustrationandhelpusimprovesubsequentversionsofthisbook.Ifyoufindanyerrata,pleasereportthembyvisitinghttp://www.packtpub.com/submit-errata,selectingyourbook,clickingontheerratasubmissionformlink,andenteringthedetailsofyourerrata.Onceyourerrataareverified,yoursubmissionwillbeacceptedandtheerratawillbeuploadedonourwebsite,oraddedtoanylistofexistingerrata,undertheErratasectionofthattitle.Anyexistingerratacanbeviewedbyselectingyourtitlefromhttp://www.packtpub.com/support.
PiracyPiracyofcopyrightmaterialontheInternetisanongoingproblemacrossallmedia.AtPackt,wetaketheprotectionofourcopyrightandlicensesveryseriously.Ifyoucomeacrossanyillegalcopiesofourworks,inanyform,ontheInternet,pleaseprovideuswiththelocationaddressorwebsitenameimmediatelysothatwecanpursuearemedy.
Pleasecontactusat<[email protected]>withalinktothesuspectedpiratedmaterial.
Weappreciateyourhelpinprotectingourauthors,andourabilitytobringyouvaluablecontent.
QuestionsYoucancontactusat<[email protected]>ifyouarehavingaproblemwithanyaspectofthebook,andwewilldoourbesttoaddressit.
Chapter1.IntroductiontoAngularJSWelcometotheworldofAngularJSwithPhoneGap!Inthisbook,youwilllearnhowtomergetwoveryexcitingtechnologies,namelyAngularJSandPhoneGap.Bytheendofthisbook,youwillhaveaworkingmobileappthatworksacrossiOSandAndroid,basedonAngularJSandPhoneGap.Asmentionedpreviously,thisbookistargetedatprogrammerswhohaveknowledgeofPhoneGap,butmayormaynothaveknowledgeregardingAngularJS.YoushouldhavesomeideaaboutJavaScriptthough,foryoutogetmaximumbenefitoutofthisbook.Thatsaid,letusbeginwithAngularJS.
AbriefoverviewofAngularJSAngularJS(https://angularjs.org/)isasuperheroicJavaScriptMVCframework,whichismaintainedbyGoogle.Itisopensourceanditsmaingoalistoassistwithcreatingsinglepageapplications.Thesearetypicallyone-pagewebapplicationsthatonlyrequireHTML,CSS,andJavaScriptontheclientside.
Whileonemayarguethattherearealreadymanyframeworksoutthereinthemarketthathelpwiththisissue,IwouldliketosaythatAngularJSisdifferentinafewways.Andinquiteafewoftheseinstances,itmakesyourlifemucheasierasafrontendprogrammer.
CoreconceptsTherearemanyconceptsrelatedtoAngularJS,butIwillcoverthemostcommonlyusedonesforthesakeofprogressingthroughthischapter.Aswegoalonginthisbook,I’lltouchonotherconcepts,suchastheuseofself-defineddirectivesandperformingRESTfulrequestsviaAngularJS.Themainconceptsthatyoushouldunderstandinthissectionaredirectives,controllers,anddatabinding.
ControllersIfyouhavealreadyusedJavaScriptframeworks,suchasBackBone.js,Can.js,Ember.js,orKnockOut.js,youshouldbefamiliarwiththisconcept.ControllersarethebehaviorbehindtheDOMelements.AngularJSletsyouexpressthebehaviorinacleanreadableformwithouttheusualboilerplateofupdatingtheDOM,registeringcallbacks,orwatchingmodelchanges.
Data-bindingData-bindingisanautomaticwaytoupdatetheviewwheneverthemodelchanges,aswellasupdatingthemodelwhenevertheviewchanges.Thecoolestaspectofthisconceptisthatitisatwowaydata-bindingprocess.Usedintandemwithcontrollers,thiscansaveyoualotofcode,asthereisnoneedforyoutowritetheusualupdatingoftheDOMelements.
DirectivesDirectivesareanotherawesomeconceptinAngularJS.WhattheydoisteachyourapplicationnewHTMLsyntaxandnewthingsspecifictoyourapplication.Directivescanbeself-definedandpredefined.Someofthemorenotablepredefineddirectivesinclude:
ng-app:Thisdeclaresanelementasarootelementoftheapplication,allowingitsbehaviortobemodifiedthroughcustomHTMLtags.ng-bind:ThisautomaticallychangesthetextofanHTMLelementtothevalueofagivenexpression.ng-model:Thisissimilartong-bind,butallowstwo-waybindingbetweentheviewandscope.ng-controller:ThisspecifiesaJavaScriptcontrollerclass,whichevaluatesHTMLexpressions.Inlayman’sterms,whatng-controllerdoesisthatitappliesaJavaScriptfunctiontothisblockofHTMLsothatthisparticularJavaScriptfunction(includingitsaccompanyinglogic,expressions,andmore)canonlyoperateinthisblockofHTML.ng-repeat:Youcanseethisasaloopthroughacollection.
AconceptualexampleNow,let’stakealookathowsomeofthepreviousconceptsplaytogether.Considerthefollowingpieceofcode:
<!doctypehtml>
<htmlng-app>
<head>
<script
src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.12/angular.min.js"
></script>
</head>
<body>
<div>
<label>SayHelloWorld</label>
<inputtype="text"ng-model="yourHelloWorld"placeholder="Type
anythinghere.">
<hr>
<h1>Hello{{yourHelloWorld}}!</h1>
</div>
</body>
</html>
TipDownloadingtheexamplecode
YoucandownloadtheexamplecodefilesforallPacktbooksyouhavepurchasedfromyouraccountathttp://www.packtpub.com.Ifyoupurchasedthisbookelsewhere,youcanvisithttp://www.packtpub.com/supportandregistertohavethefilese-maileddirectlytoyou.
Let’sgothroughthecode.
WedefinedanHTML5HTMLdocumentinthiscase,asseeninthefirstlineNext,noticeng-appinthesecondlineofthecode;thisisanAngularJSdirective,whichtellsAngularJSthatthisistherootoftheAngularJSapplicationInordertouseAngularJS,weobviouslyhavetoinstallthescriptonthiswebpage,asshowninthe<script>tagWithinthebodytag,weseealabel,aninput,andanh1tag.Takenoteoftheinputtag,thereisang-modeldirective,whichismappedtoh1tag's{{yourHelloWorld}}
Whatthepreviouspieceofcodedoesisthatanythingthatistypedintotheinputbox,willbeshowninplaceof{{yourHelloWorld}}
Takenoteoftheversionofthecodeweareusinginthischapter,version1.2.12;shouldyoubeusingnewerversionsofAngularJS,thereisnoguaranteethatthecodewillwork.
Nowthatwehavebrieflywalkedthroughthecode,letuscopythecodeandrunitonourwebbrowser.Feelfreetocopythecodeandsaveitasconcepts.html.Thesourcecodeforthischaptercanbefoundintheconcepts.htmlfileintheChapter1folder.
Copiedthecode?Ifso,openthefileinyourfavoritewebbrowser.Youshouldseethe
followingscreenshotinyourbrowser:
Asampleconceptwebpage
Gotthepreviouscode?Okgreat!SonowyoucantrytypingintotheinputboxandseenewtextbeingappendedtoHelloandbefore!inthescreen.
Forinstance,whenwetypeworld,wewillseethenewcharactersbeingappendedtothescreenasIcontinuetotype.Bytheendoftypingtheword“World”,weshouldseethefollowingscreenshot:
AftertypingWorld
NowthatwehaveabriefideaastohowasimpleAngularJSappworks,letusmovetoamorecomplicatedapp.
Asimpleto-dolistusingAngularJSInthisexample,wewillcoverindetailastohowtowritecodeforaslightlymorecomplicatedAngularJSapp.Thisappismodifiedfromtheofficialexamplefoundatangularjs.org.ThisexamplewillbeusedasabasewhenweconvertitfromawebapplicationtoaPhoneGapapplication.
PreparingyourcodestructureForstarters,createtheindex.htmlandtodo.jsfiles.Justforyourinformation,thecodefoundinthissectioncanbefoundinthetodofolderinChapter1.
HTMLforourto-dolistWeneedtoprepareourHTMLfilesothatwecanmakeuseofAngularJS.Similartothepreviousconcepts.htmlfile,youwillseethatwehaveincludedtheuseofAngularJSviascript.Openupyourindex.htmlfileinyourfavoriteeditorandyoucanstartbyaddingthefollowingcode:
<!doctypehtml>
<htmlng-app>
<head>
<script
src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.12/angular.min.js"
></script>
<scriptsrc="todo.js"></script>
<linkrel="stylesheet"
href="http://netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css">
<style>
body{
padding:40px;
}
#todoDetails{
visibility:hidden;
}
</style>
</head>
<body>
<divclass="row"ng-controller="todoCtrl">
<divclass="col-md-6">
<h2>Todo</h2>
<div>
<span>{{getRemaining()}}of{{todos.length}}remaining</span>
[<buttonng-click="archive()">archive</button>]
<ulclass="unstyled">
<ling-repeat="todointodos">
<inputtype="checkbox"ng-model="todo.done">
<spanclass="done-{{todo.done}}">{{todo.text}}</span>
<buttonng-click="showDetail(todo.text)">Detail</button>
</li>
</ul>
<formng-submit="addTodo()">
<inputtype="text"ng-model="todoText"size="30"
placeholder="addnewtodohere">
<inputclass="btn-primary"type="submit"value="add">
</form>
</div>
</div>
<divid="todoDetails"class="col-md-6">
<h2>Details</h2>
Title:<spanid="title">{{currentText}}</span>
<br>
AddDetails:
<formng-submit="addDetails()">
<textareaid="details"ng-model="currentDetails">
{{currentDetails}}</textarea>
<p>
<inputclass="btn-primary"type="submit"value="AddDetails">
<inputclass="btn-primary"type="submit"value="Cancel"ng-
click="closeThis()">
</p>
</form>
</div>
</div>
</body>
</html>
Now,tomakesurethatyouareonthesamepageasIam,Iwantyoutoopenthisfileinyourfavoritebrowser.Youshouldseesomethinglikethefollowingscreenshot:
OurHTMLtemplate
Gotthepreviouscode?ItlooksweirdnowduetothefactthatwehavenotaddedthemainJavaScriptfunctionalities.Wewillbeworkingonitinthenextsection.
Now,letmeexplainthecode;noticethatIhavehighlightedafewlinesofit.Thesearethemostimportantlinesofthecodethatyoushouldtakenoteofinthisexample.TheremaininglinesarejusttheusualHTMLcode.
ThefirsttwolinesofthehighlightedcodesimplyinstallAngularJSandincludeBootStrap3’sCSSforstylingpurposes.Withoutboth,theprojectwillnotworkandmaynotlookgood.Theng-controllerdirectiveiswhatwecoveredbrieflyearlieroninthischapter.WeareapplyingtodoCtrltothisblockofHTML.Theng-clickdirectiveisanotherdirectivethatwedidnottouchonintheprevioussection.Whatng-clickdoesisthatitexecuteswhateverfunctionisdefinedforthisdirective.Inourexample,ng-click="archive()"meansthatonclickingit,archive()willbeexecuted.TheJavaScriptfunctionarchive()iswritteninourtodo.jsfile,whichwewillcoverlater.Theng-repeatdirectiveisadirectivethatloopsthroughacollection.Noticehowwe
implementedng-repeatinourHTMLcode:
<ling-repeat="todointodos">
<inputtype="checkbox"ng-model="todo.done">
<spanclass="done-{{todo.done}}">{{todo.text}}</span>
<buttonng-click="showDetail(todo.text)">Detail</button>
</li>
Anythingthatiswithin<li>isdependentonthetodoobject,whichispartofthetodoscollection.
Theng-submitdirectiveisgenerallyusedinforms.Thisisadirectivewhichcontrolswhatisbeingdoneonthesubmitform.Inthiscase,onthesubmitform,wewillexecutetheJavaScriptfunctionaddToDo().The[]optionencapsulates<buttonng-click="archive()">archive</button>,whichsimplyaddsasquarebracketaroundthebutton.
AddinginJavaScriptwithAngularJSNowwewillopenourtodo.jsfile,whichwecreatedintheprevioussection.Opentodo.jsinyourfavoritetexteditor.Letusbeginbycodingthefollowing:
functiontodoCtrl($scope){
}
Wearefirstgoingtodefineacontroller,whichwewillbeusingforourapp.NoticethatwehavenamedittodoCtrl,whichismappedontong-controllerintheHTMLfile(index.html),whereng-controller="todoCtrl"meansthattodoCtrlwillbecontrollingthisportionofthewebpage.
Also,noticetheuseof$scope,whichisanobjectthatreferstotheapplicationmodel;itistheexecutioncontextforrelatedexpressions,suchasng-click,ng-model,andsoon.Anysuchexpressionsofapredefineddirectiveoutsidethisscopewillnotbeexecuted.
Let’sstartbyinitializingourto-dolist.WithintodoCtrl,addthefollowingcode:
$scope.todos=[
{text:'hereismyfirsttodo',done:true,details:''},
{text:'continuewritingchapter1forthisbook',done:false,
details:''},
{text:'workonchapter2examples',done:false,details:''}
];
$scope.currentText='';//makethetextempty
$scope.currentDetails='';//makethetextempty
What$scope.todosdoesisthatitsimplycreatesalistofobjects,whichcontainsthetext,details,andwhetherthisto-doisexecutedornot(trueorfalse).Noticethattodoshereismappedtothecollectiontodosasseeninindex.html,whereng-repeatisbeingused.
Let’smoveonbyaddingmorefunctionalities.After$scope.currentDetails,addthefollowingthreeJavaScriptfunctions:
$scope.addTodo=function(){
$scope.todos.push({text:$scope.todoText,done:false,details:''});
$scope.todoText='';
};
$scope.remaining=function(){
varcount=0;
angular.forEach($scope.todos,function(todo){
count+=todo.done?0:1;
});
returncount;
};
$scope.archive=function(){
varoldTodos=$scope.todos;
$scope.todos=[];
angular.forEach(oldTodos,function(todo){
if(!todo.done)$scope.todos.push(todo);
});
};
The$scope.todoTextfunctionresetstodoTextafterithasbeenpushedintothearray.The$scope.addTodofunctiondoeswhatitissupposetodo,simplyaddinganewto-dotothelistoftodosasdefinedearlier.ThebeautyofAngularJSisthatitusesstandardJavaScriptdatastructuresthatmakemanipulationsomucheasier.
The$scope.getRemainingfunctionsimplycalculatesthenumberofitemsthatarenotdoneyet.Here,wecanseetwo-waydata-bindinginactionasthisfunctionexecuteswheneverthereisachangeinthelengthoftodos.
The$scope.archivefunctionmarksato-doasdone:trueinstandardJavaScriptmanner.
Bynow,youshouldhavenoticedthatalltheJavaScriptfunctionsdefinedherearebeingusedinindex.htmlunderng-controller="todoCtrl".
Let’snowaddthreemoreJavaScriptfunctionstocompletethefinishingtouchforthissampleapplication.
Afterthe$scope.archivefunction,addthefollowingfunctions:
$scope.showDetail=function(text){
varresult=$scope.todos.filter(function(obj){
returnobj.text==text;
})
$scope.currentText=result[0].text;
$scope.currentDetails=result[0].details;
document.getElementById('todoDetails').style.visibility='visible';
}
$scope.closeThis=function(){
$scope.currentText='';
$scope.currentDetails='';
document.getElementById('todoDetails').style.visibility='hidden';
}
$scope.addDetails=function(text){
varresult=$scope.todos.filter(function(obj){
returnobj.text==text;
})
angular.forEach($scope.todos,function(todo){
if(todo.text==text){
todo.details=$scope.currentDetails;
}
});
document.getElementById('todoDetails').style.visibility='hidden';
}
The$scope.showDetailfunctionsimplyretrievesthecurrentto-dobeingclickedonandshowsitonthedivwithID#todoDetails.Thevisibilityofthe#todoDetailsfunctionisthensettovisible.
The$scope.closefunctionsimplychangesthevisibilityof#todoDetailstohidden.
Finally,$scope.addDetailsaddsthedetailsofthetodoitem,andchangesthevisibilityof#todoDetailstohiddenoncedone.
Okay,sotoseeifweareonthesamepage,wenowneedtocheckourcode.Savethisfileastodo.js.Refreshyourbrowserandyoushouldstillseethesameuserinterfaceasperthepreviousscreenshot.
Now,tryclickingontheDetailbuttoninfrontofworkonchapter2examples,andyoushouldseethefollowingscreenshot:
DetailsoftheToDoitemshowsonclickingonthecorrespondingdetailbutton
Youwillseethedetailsofaparticularto-doitem.YoucantrytoaddsomedetailsforthisitemandclickonAddDetails.Youcanthenclickonotheritemsandcomebacktothisitemlater(withoutrefreshingthebrowser),andyoushouldstillseethedetailsinthetextarea.
Youcanalsocheckoffanyoftheitemsandyouwillseethatthenumberofremainingto-doitemdecreases:
Numberofitemschangesdynamicallyasyoucheckoffitems
Andofcourse,youcanaddnewitemsbysimplytypingintheinputboxandclickingontheaddbutton.Youshouldnoticethatthenumberofitemsnowincreases:
Addingnewto-doschangesthenumberofitemsdynamicallyandalsoshowsonthescreenimmediately
SummaryTosummarizewhatwehavedoneinthischapter;wehavewalkedthroughthebasicsofbuildinganAngularJSappandfamiliarizedourselveswiththebasicconceptsusedinAngularJS.Wehavemadeuseofng-app,ng-controller,ng-click,ng-repeat,andng-submitingeneral.Theseexpressions,suchasng-clickandng-submitaretypicallymappedontoJavaScriptfunctionsdefinedinAngularJScontrollers,asseenintodo.jsinourexample.NoticehowlittlecodewehavewritteninordertoachievesuchspeedyUXthroughtheconceptoftwo-waydata-bindinganditscontrollers.
Inthenextchapter,wewillstarttoportthisappinamoreorganizedmannertoPhoneGap.
Chapter2.GettingReadyforPhoneGapAsyoumightalreadyknow,PhoneGap(http://phonegap.com/)isareallycoolopensourceproject(nowownedbyAdobe),thatallowsyoutocreatecrossplatformmobileappsusingJavaScript/CSS/HTML.
Thismeansthatyoucanreadilyuseyourwebdevelopmentskillstodevelopingmobileapps.SincethisbookassumesbasicfamiliaritywithPhoneGap,IwilladvancetohowtoinstallPhoneGap.YouwillprimarilyseeexamplesrelatedtoAndroidandiOSsincewearegoingtocreatemobilesappsthatonlysupportAndroidandiOS.
NotethatwearefocusingonusingPhoneGapVersion3.3.0and,asmuchaspossible,wewillbebuildingtheappsviathelatestcommand-lineinterfaceprovidedbyPhoneGap.
Justforyourinformation,allsourcecodefoundinthischapter—whetherautomaticallygeneratedbyPhoneGaporcodedbyus—canbefoundinthesourcecodefolder,chapter2.
PreparingforPhoneGapdevelopmentWewillnowgoquicklythroughtheinstallationprocessforAndroidandiOSplatforms.Thebasicinstructionsforthissectioncanbefoundathttp://docs.phonegap.com/en/3.3.0/guide_platforms_index.md.html#Platform%20Guides.
InstallingAndroidTheinstructionstoinstallAndroidSDKcanbefoundathttp://docs.phonegap.com/en/3.3.0/guide_platforms_android_index.md.html#Android%20Platform%20Guide
Inordertobenefitfromthischapter,youneedtofollowtheinstructionstillthepointwhereyoucanruntheHelloWorldexampleinyourAndroidemulator.ThiswillincludethingslikeinstallingtheAndroidSDK,EclipseTools,andsoon.
InstallingiOSIfyouareusingMacandwanttodevelopanappforiOS,thenyouwillneedtoinstalltheSDKforiOSaswell.Ingeneral,youwillneedtoinstallXcodefromtheAppStoreandyouwillneedtoregisterasanAppleDeveloperinordertodeploytheappintheAppStore.
Youcanfollowtheinstructionsgivenathttp://docs.phonegap.com/en/3.3.0/guide_platforms_ios_index.md.html#iOS%20Platform%20Guide
PleasemakesurethatyoucanatleastrunthePhoneGapHelloWorldexampleinyouriOSsimulatorinordertobenefitfromthischapter.
Command-lineinterfaceforbothAndroidandiOSOnceyouhavefinishedinstallingtheindividualplatforms,it’stimetomoveontothecommand-lineinterface.Thissectioncontainsthemostimportantcommandsforthecommand-lineinterface.Tostartoff,youneedtoinstallNode.js(www.nodejs.org).Onceyouhaveinstallednode.js,performthefollowingsteps:
1. Runthenpm–ginstallcordovacommand.Thisinstallsthecommand-lineinterfaceonyourcomputer.
2. Changethedirectorytotheplacewhereyouwillbesavingyourprojectfilesforthischapter.
3. Onceinthedirectory,issuethecordovacreatetodocom.project.todoToDocommand.ThiswillcreateafoldercontainingyourbasicfilesforPhoneGap.
4. Now,changedirectoryto/todo.5. Onceinthedirectory,weneedtoinstallthevariousplatformswewillbesupporting:
ForiOS,usethecordovaplatformaddioscommandForAndroid,usethecordovaplatformaddandroidcommand
6. Now,let’strytoruntheHelloWorldexampleinAndroidusingthecordovaemulateandroidcommand.IfyouseeanareawhereyouhavenotdefinedanAVD,runtheandroidcreateavd–nametodo–target1command.
7. Then,runthecordovaemulateandroidagaincommand.Ifeverythingworkscorrectly,youshouldseethefollowingscreenshot:
HelloWorldworkinginandroid
Cool,sonowlet’strytoruntheHelloWorldexampleinaniOSsimulator.Forastart,issuethenode–ginstallios-simcommand.Now,runthecordovaemulateioscommand.Ifeverythingrunscorrectly,youshouldseethefollowingscreenshot:
RunningonrealdevicesSofar,wehavelearnedhowtoruntheHelloWorldapponemulators.However,whatifwewanttorunourcodeonourdevices?It’seasy;forandroid,runthefollowingcommands:
cordovabuildandroid
cordovarunandroid
ForiOS,runthefollowingcommands:
cordovabuildios
cordovarunios
Gotthepreviousexamplerunning?Ifso,great!Let’snowmoveontothenextsectionwhereweimplementAngularJSonPhoneGap.
AngularJSonPhoneGapBeforewebeginthissection,let’stakealookathowmuchmagicthePhoneGapcommand-lineinterfacehas.Navigatetothedirectorywhereyousavedyourcode;youshouldseesomethinglikethefollowingscreenshot:
Directorylayoutofthecode
ThepreviousscreenshotishowmycodedirectorylooksafterissuingthePhoneGapcommandsoftheprevioussection.NoticethatI’vecreatedthefolderphonegap/,andthePhoneGapcommandlinehelpeduscreatethetodo/projectfolderwithotherfolderssuchashooks/,merges/,platforms/,plugins/,andsoon.Ourplatform-specificcommandscreatedthefoldersandroid/andios/andtheyarefoundundermerges/andplatforms/respectively.
NoteTheautomaticallycreatedfoldersaremeanttoholdimportantfilesthatbelongtodifferentplatforms.Forexample,youwillfindAndroid-relatedfilesinandroid,whileyouwillfindiOSrelatedfilesinios/.Intheplugins/folder,youwillfindthevariouspluginsthatyouhaveinstalled.Laterinthisbook,youwillseeandexperiencetheFacebookconnect
plugin.
Inmyopinion,thecommand-lineinterfacesavesusalotofwork.Mostimportantly,wearenottieddowntoanyparticularcodeeditor;wecanjustusetheterminalandanycodeeditorthatweprefer(IuseSublimeText).
Now,goingbacktoyoursourcecode,navigatetothewww/folderundertodo/.Asyoumightalreadyknow,www/containsoursourcecodefortheJavaScript,CSS,andHTMLfiles.
Lookfortheindex.htmlfileandrenameitindex_backup.html.Now,createanewindex.htmlfileunderthewww/directory.
Next,copyandpastethecontentsfromconcepts.html,butmakeafewchangestoit.Foryourconvenience,thecodethatwewilluseisasfollows:
<!doctypehtml>
<htmlng-app>
<head>
<scripttype="text/javascript"src="cordova.js"></script>
<script
src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.12/angular.min.js"
></script>
<style>
body{
padding:40px;
}
#holder{
border:2pxsolidred;
}
</style>
</head>
<body>
<divid="holder">
<label>SayHelloWorld</label>
<inputtype="text"ng-model="yourHelloWorld"placeholder="Type
anythinghere.">
<hr>
<h1>Hello{{yourHelloWorld}}!</h1>
</div>
</body>
</html>
Thecodeisgenerallythesameastheconcepts.htmlfileasseeninthechapter1folder,butwithafewchangesasshowninthehighlightedlinesofcode:
Inthe<header>tag,weinstalledPhoneGapbyusing<scripttype="text/javascript"src="cordova.js">
Next,weaddedsimplestylessothatwecanseewhatweareconcernedwithboundedinaredbox
Now,saveyourcodeasindex.html.Makesureitissavedunderthewww/folder.Afteryouhavesavedit,weneedtotestthecodeandmakesurethatthecodeisworkingasperwhatwehaveseeninthechapter1folder.Weshouldexpecttoseethataswetypeany
textintotheinputbox,itshouldbeappendedaftertheHello!text.
Sonow,let’sstarttestingourcode.Let’sstartwithiOS.Gotoyourterminalandmakesureyouchangethedirectorytotodo/.Onceinthedirectory,issuethecommandcordovaemulateios.Onceyouremulatorhasstarted,youshouldseethefollowingscreenshot:
AngularJSHelloWorldexampleonPhoneGapiOSSimulator
Allisgood.Nowlet’stestbytypingintothetextinputandseeifitworksasintended.Inmycase,Ityped,hey,andIgotthefollowingscreenshot:
Typingheytomakesurethatthecodeworks
Understoodtheexample?Makesurethatthecharactersgetprintedoutasyoutypealong!IfiOSisworkingoutfine,weshouldnothaveanyproblemwithAndroid.However,forsafety’ssake,let’sfireupAndroid’semulatortomakesurethingsaregoingasintended.
QuityouriOSsimulatorifyouwantto.Now,returntoyourterminalandissuethecommandcordovaemulateandroid.OnceAndroid’semulatorhasstarted,youshouldseethefollowingscreenshot:
AngularJSHelloWorldonAndroidemulator
Ifyougottheoutput,that’sgood.Let’stestbytypingintothetextinputbox.Thistime,ItypedworldandIgotthefollowingscreenshot:
AngularJSHelloWorldoniOSsimulator
Whatjusthappened?Bynow,youshouldhavenoticedthatenablingAngularJSonPhoneGapappsareprettystraightforward,justcodeasifyouarewritinganAngularJSapp.ToportitovertoPhoneGap,justmakesureyourunthecommandsshownintheprevioussectionsandinstallthecordova.jsscriptinyourAngularJSapp.
NowthatwehavemadesurethatAngularJSworksinPhoneGap,it’stimetomoveontothemaintopicofthischapter:buildingaTodoapp.
Creatingato-dolistappusingAngularJSonPhoneGapForthissection,we’llstartoffbytransferringtheto-dolistappfromthechapter1foldertoPhoneGap.Asyoumayhavealreadyguessed,shiftingtheto-dolistapptoaPhoneGapversionsimplyrequirestheinstallationofcordova.js.Let’sseehowthisisdoneinthenextsection.
Abasicversionofato-dolistusingAngularJSonPhoneGapLet’squicklygetstartedbyshiftingtheto-dolistappfromchapter1toPhoneGap.Performthefollowingsteps:
1. Changethedirectorytochapter2andnavigatetowww/whereyourPhoneGapfilesarelocated.
2. Changeindex.htmltoindex_concepts.html.3. Now,copythecontentsfromindex.htmlfromchapter1(wherethebasicHTML
structurefortodoappresides)toournewindex.htmlfile.4. Copytodo.jsfromtodo/inchapter1tojs/inwww/inthechapter2folder.
Yourdirectoryshouldlooklikethisfortodoappofchapter2:
Thecodedirectory
5. So,asofnow,yourindex.htmlfileforthischaptershouldlooklikethefollowingcode:
<!doctypehtml>
<htmlng-app>
<head>
<script
src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.12/angular.min
.js"></script>
<scriptsrc="todo.js"></script>
<linkrel="stylesheet"
href="http://netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.
css">
<style>
body{
padding:40px;
}
#todoDetails{
visibility:hidden;
}
</style>
</head>
<body>
<divclass="row"ng-controller="todoCtrl">
<divclass="col-md-6">
<h2>Todo</h2>
<div>
<span>{{remaining()}}of{{todos.length}}remaining</span>
[<ahref=""ng-click="archive()">archive</a>]
<ulclass="unstyled">
<ling-repeat="todointodos">
<inputtype="checkbox"ng-model="todo.done">
<spanclass="done-{{todo.done}}">{{todo.text}}</span>
<buttonng-click="showDetail(todo.text)">Detail</button>
</li>
</ul>
<formng-submit="addTodo()">
<inputtype="text"ng-model="todoText"size="30"
placeholder="addnewtodohere">
<inputclass="btn-primary"type="submit"value="add">
</form>
</div>
</div>
<divid="todoDetails"class="col-md-6">
<h2>Details</h2>
Title:<spanid="title">{{currentText}}</span>
<br>
AddDetails:
<formng-submit="addDetails(currentText)">
<textareaid="details"ng-model="currentDetails">
{{currentDetails}}</textarea>
<p>
<inputclass="btn-primary"type="submit"value="AddDetails">
<inputclass="btn-primary"type="submit"value="Cancel"ng-
click="closeThis()">
</p>
</form>
</div>
</div>
</body>
</html>
Let’smakesomeCSSchangestothecodethatyouaregoingtouseforindex.html;weneedtochangethehighlightedlineofcodetopadding:40px20px020px;.
6. Now,weneedtofireupourcodetomakesurethatitisworkingasintended.We’lltestitoutoniOSfirst.Gotoyourterminal,changethedirectorytotodo/andissuethecommandcordovaemulateios.OncetheiOSsimulatorisfiredup,youshouldseethefollowingscreenshot:
TodoApponiOS
7. Nowtestthefollowingtomakesurethatitisworkingasperwhatweseeinchapter1:
TickoffthecheckboxestoseeifthenumberoftasksremainingandtotaltasksarecorrectTryaddinganewtodoitemandseeifitaddstothelistoftodosClickonDetailandseeiftheitemwillshowupbelowtheinputbox.Forinstance,youshouldseesomethinglikethefollowingscreenshot:
Detailsforeachtodoitem
Ifyouaregettingtheprecedingtestsright,thencongratulations;allisworkingwellandgood.NowweneedtotestthecodeonAndroid.Goingbacktoyourterminal,issuethecommandcordovaemulateandroid.Aftertheemulatorisfiredup,youshouldseethefollowing:
TodoapponAndroid
Now,asusual,carryoutthetestthatyouhavedoneforiOS.Similarly,whenyouclickontheDetailbutton,youshouldseethefollowingscreenshot:
TododetailsforeachtodoonAndroid
SummaryLet’squicklydiscusswhatwehavedoneinthischapter.WehavepreparedourselvesforPhoneGapdevelopmentbyinstallingSDKsforbothAndroidandiOS.Next,wetouchedonhowwecanmakeuseofPhoneGapcommand-lineinterfacetosetupourapp,install,andpreparefordifferentplatformsandrunourappsoniOSandAndroidemulators.We’vealsolearnedthecommandstorunourappsonrealdevices.
However,wearestillfarawayfromadecentmobileapp.WhatwehavenowisjustabasicversionofthetodoapponPhoneGap;weneedtoimproveonit.Specifically,weneedtomakeitlookmorelikeamobileapp.Forinstance,canwedesignthelookandfeeloftheto-dolistsothatitwhenwetaponit,weareshowntheindividualtodoitemonasinglepage,insteadofjustappendingittothebottomofthepage?We’lldothisandmoreinthenextchapter.
Chapter3.FromaSimpleTo-doListtoanAdvancedTo-doListNowthatwehaveourbasicsinplace,let’smoveontosomethingmoreadvanced.InChapter1,IntroductiontoAngularJS,andChapter2,GettingReadyforPhoneGap,webuiltasimpleto-dolist,whichworkswell,butthecodeorganizationisamateurishatbest.Thecodearrangementinthepreviouschapterspreventsusfromwritinglargeappsshouldwewantto.Hence,inthischapter,wewillstartoffbyreorganizingthecodefirst,beforewiringupwithabackendserver.
WewillbebuildingonthetopicscoveredinChapter1,IntroductiontoAngularJS,andChapter2,GettingReadyforPhoneGap.Specifically,wearegoingtocoverthreemainareas:
ReorganizingthecodeWritingourserverusingtheTornadowebserverWiringourappwiththebackendserver
Rewritingthesimpleto-dolistappInChapter2,GettingReadyforPhoneGap,wewrotearathersimplisticversionoftheto-dolistapp.Asyoumayhavealreadynoticed,manythingsweremissing;therewasnobackendservertosaveyourto-dolists,andtherewasnocodeorganization,aseverythingwasjustwrittenwithinafolderandwithminimalbreakupofthecode.
Inthissection,wearegoingtodojustthat;rewritethecodesothattherearesomelevelsofcodeorganization.
Let’sreviewthecodeorganizationfirst.TheappwebuiltinChapter1,IntroductiontoAngularJS,lookslikethefollowing:
todo/
todo.js
index.html
Wearegoingtobreakupthecodesothatthecodeorganizationlooksasfollows:
project/
css/
js/
controllers/
todo.js
services/
todo.js
app.js
partials/
detail.html
list.html
index.html
Sowhatisgoingtohappenisthatproject.jsfromChapter2,GettingReadyforPhoneGap,willbebrokenupintotodo.js,controllers/todo.js,services/todo.js,andapp.js.Wewillalsobreakupindex.htmlintomultipleHTMLsnippetsandplacethemunderthepartials/folder.
Sobeforeyoustartwiththenextsection,youmightwanttocreatethedirectoriesandemptyfilesbasedonthecodeorganization.
Splittingindex.htmlintomultiplefilesWewillstartworkonindex.htmlfirst.Tostartoff,thisiswhatindex.htmlwilllooklikeinthischapter:
<!doctypehtml>
<htmlng-app="todoApp">
<head>
<linkrel="stylesheet"
href="http://netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css"
>
<style>
body{
padding:40px20px020px;
}
</style>
<script
src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.12/angular.min.js"
></script>
<script
src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.12/angular-
resource.min.js">
</script>
<script
src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.12/angular-
route.min.js">
</script>
<scriptsrc="js/controllers/todo.js"></script>
<scriptsrc="js/services/todo.js"></script>
<scriptsrc="js/app.js"></script>
</head>
<body>
<h2>Todos</h2>
<divng-view></div>
</body>
</html>
TheprecedingcodestructureisessentiallythesameaswhatwesawinChapter2,GettingReadyforPhoneGap.Themaindifferenceisthatmuchofthedetails(suchaslistingtheto-dolistsandeditview)arenowbeingabstractedaway.
Themainthingtonoteistheuseofng-viewinindex.html;youcanthinkofng-viewasacontainerthatwillholddifferentsnippetsofHTMLbasedonthecurrentURL.
Nowlet’sbegincoding:
1. Let’sworkonpartials/detail.html:
<formname="myForm">
<divclass="control-group">
<label>Name</label>
<inputtype="text"name="text"ng-model="todoText">
</div>
<divclass="control-group">
<label>Details</label>
<textareaname="description"ng-model="todoDetails"></textarea>
</div>
<br>
<ahref="#/"class="btn">Cancel</a>
<buttonng-click="save()"
class="btnbtn-primary">Save</button>
<buttonng-click="destroy()"
ng-show="project.$remove"class="btnbtn-
danger">Delete</button>
</form>
2. Youwillseesomeofthedetailsofthedetail.htmlfileinthepreviouschapter;thehighlightedlinesofcodearethenewadditions:
<ahref="#/"class="btn">Cancel</a>simplyperformsachangeintheURLroutewhenweperformacancelaction.Noticethatweareusing#/insteadof/.Insteadofusingng-submittoaddnewto-dolists,wenowuseng-click="save()"toeithercreateoreditthecurrentto-doitem,shouldtherebeany.
3. Onceyou’redonewiththeprecedingstep,let’smovetopartials/list.html:
<span>{{remaining()}}of{{todos.length}}remaining</span>
<table>
<thead>
<tr>
<th>Todo</th>
<th>Done</th>
<th>Details</th>
<th><ahref="#/new"><iclass="icon-plus-sign">NEW</i></a></th>
</tr>
</thead>
<tbody>
<trng-repeat="todointodos">
<td>{{todo.text}}</td>
<td><inputtype="checkbox"ng-model="todo.done"></td>
<td>
<ahref="#/edit/{{todo.text}}"><iclass="icon-
pencil">Edit/Details</i></a>
</td>
</tr>
</tbody>
</table>
4. Thelist.htmlfileissimilartowhatwehavedoneinChapter2,GettingReadyforPhoneGap;theonlychangewasthatwesplitthepartwhereweloopthroughthelistofto-dosintoaseparateHTMLfile.
NowthatwearedonewiththeHTMLportionofthecode,let’smoveontothemeatofthisrewrite:rearrangingtheJavaScriptcode.
Splittingtodo.jsintomultiplefilesWewillfirstworkonapp.jsfoundundertheproject/jsfolder.Bearinmindthatthisisthefirsttimewewritethecodeforapp.js.ItcontainstheroutestodifferentviewsoftheappbymakinguseofanAngularJSmodulecalledngRoute.
Nowlet’sseehowwecanmakeuseofngRoute:
1. Tostartoff,here’swhatapp.jslookslikenow:
angular.module('todoApp',[
'ngRoute',
'todoApp.controllers',
'todoApp.services'
])
.config(function($routeProvider){
$routeProvider
.when('/',{
controller:'ListCtrl',
templateUrl:'partials/list.html'
})
.when('/edit/:todoText',{
controller:'EditCtrl',
templateUrl:'partials/detail.html'
})
.when('/new',{
controller:'CreateCtrl',
templateUrl:'partials/detail.html'
})
.otherwise({
redirectTo:'/'
});
})
2. Let’sgothroughwhat’shappeningherelinebyline:
WefirstdefinetodoApp;somethingwehavedoneinChapter1,IntroductiontoAngularJS,andChapter2,GettingReadyforPhoneGap.Next,weincludedthemodulesandcodethatwewanttouse,namelyngRoute,todoApp.controller,andtodoApp.services.TakenotethatwehavenotcreatedtodoApp.controllerandtodoApp.servicesyet.Finally,wemakeuseofthengRoutemoduletodefinetherouteswewanttouse,anditsassociatedcontrollerandtemplate.when('/'meansthatwhentheURLlocationis'/',wewillbeusingListCtrlandthepartials/list.htmltemplate.ControllersineachoftheroutearedefinedastodoApp.controllers(whichisthecontroller.jsfilewewillbeworkingoninthenextsection).templateurlissimplytheHTMLsnippetthatwewouldliketoshowforeachdifferentURL.
3. Nowlet’screatethecontrollerfoundatcontroller/todo.js.Thecodeisthesame
asinChapter2,GettingReadyforPhoneGap:
angular.module('todoApp.controllers',[])
.controller('ListCtrl',function($scope,$http,Todos){
$scope.todos=Todos;
$scope.remaining=function(){
varcount=0;
angular.forEach($scope.todos,function(todo){
count+=todo.done?0:1;
});
returncount;
};
})
.controller('CreateCtrl',function($scope,$location,$timeout,
Todos){
$scope.todoText="";
$scope.todoDetails=""
$scope.save=function(){
Todos.push({text:$scope.todoText,done:false,
details:$scope.todoDetails});
$location.path('/');
};
})
.controller('EditCtrl',
function($scope,$location,$routeParams,Todos){
$scope.todos=Todos;
varresult=$scope.todos.filter(function(obj){
returnobj.text==$routeParams.todoText;
});
$scope.todoText=result[0].text;
$scope.todoDetails=result[0].details;
$scope.save=function(){
vartext=$scope.todoText;
vardetails=$scope.todoDetails;
vardone=$scope.todoDone;
alert(text);
angular.forEach($scope.todos,function(todo){
if(todo.text==text){
todo.text=text;
todo.details=details;
}
});
$location.path('/');
};
$scope.destroy=function(){
$scope.project.$remove();
$location.path('/');
};
});
4. Theonlylineofcodeyouneedtotakenoteofisthefirstline;wehavedefinedtodoApp.controllers,whichwasreferencedfromjs/app.jsearlieron.
5. Wecannowmoveontojs/services/todo.js.Asusual,wefirstdeclaretodoApp.services,followedbyTodos.Wesimplyreturnalistofto-doitemshere.Thecodelooksasfollows:
angular.module('todoApp.services',[])
.factory('Todos',function(){
varitems=[
{text:'hereismyfirsttodo',done:true,details:''},
{text:'continuewritingchapter1forthisbook',done:false,
details:''},
{text:'workonchapter2examples',done:false,details:''}
]
returnitems;
})
Nowthatwehavemoreorlessreorganizedourcode,weshouldcheckthatourcodeisworking.Saveyourcodeandopenupindex.htmlinyourwebbrowser.YoushouldexpecttoseethatthecodeisworkingasperChapter2,GettingReadyforPhoneGap.Theinterfacewilllookthesamewiththeexceptionthattheunderlyingcode’sorganizationhaschanged.
Isitallworkingwell?Ifso,great!YoucanproceedtocreateaPhoneGapversionofthiscodebycopyingandpastingthecontentsoftodos_advance/intothewww/folderfoundinyourPhoneGapproject.
Next,youcantestyourcodeintheAndroidandiOSemulatorsbyrunningcordovaemulatorandroidandcordovaemulatorios,respectively,andmakingsurethatthecodeisworkingfine.
CheckpointNowthatwehaverewrittenthecodeandtransferredittoPhoneGap,checkifthewww/directoryinyourPhoneGapapplookslikethefollowing:
www/
css/
js/
controllers/
todo.js
services/
todo.js
partials/
detail.html
list.html
index.html
Mostimportantly,makesurethatyourcodeinPhoneGapisworkingasperChapter2,GettingReadyforPhoneGap.Also,yourappshouldlookandworksimilarlytotheversionwehavecodedwehaveinChapter2,GettingReadyforPhoneGap.Ifthecodeisconfirmedandworkingcorrectly,let’smoveontothenextsectionwherewewireabackendserverandcreateanadvancedversionoftheto-dolistapp.
WiringupabackendserverInthissection,wewillmakeuseofthe$httpmoduleofAngularJStomakeRESTfulcallstoasimplebackendserver.ThebackendserverhereisbasedonFacebook’sTornadoFramework(https://github.com/facebook/tornado/),butthefactisthatyoucanmakeRESTfulcallsusingExpress.js(http://expressjs.com/)oranyotherframeworkthatyoulike.
Beforeyougetstartedwiththissection,youwillneedtohaveMongoDB(http://www.mongodb.org/),Python2.7.x,andtheTornadowebserverinstalled.YouwillalsoneedtoinstallaPythonlibrarycalledtornado-cor(https://github.com/globocom/tornado-cors),whichfacilitatestheuseofcross-originresourcesbetweenyourAngularJSappandserver.
CodingourserverThemainideaofthePythonTornadoserverisasfollows:
Wehaveoneendpoint,wherethisendpointwillreceiveaGETorPOSTrequestfromourAngularJSapp.DependingontheURLargumentreceived,thecorrespondinghandlerwillperformGETonallto-doitemsoroneto-doitem.IftherequestisaPOSTrequest,itwilleithereditorcreateanewto-doitem.
SincetheTornadowebserverusesclass-basedviews,weonlyneedtodefineoneclass,whichacceptsaGETorPOSTrequest.Youcanrefertothesourcecodefoundatchapter3/server/server.py.Thefullcodeforourserverisasfollows:
importtornado.httpserver
importtornado.ioloop
importtornado.options
importtornado.web
importpymongo
frombson.objectidimportObjectId
fromtornado_corsimportCorsMixin
fromtornado.optionsimportdefine,options
importjson
define("port",default=8000,help="runonthegivenport",type=int)
classApplication(tornado.web.Application):
def__init__(self):
handlers=[(r"/todos",Todos)]
conn=pymongo.Connection("localhost")
self.db=conn["todos"]
settings=dict(
xsrf_cookies=False,
debug=True
)
tornado.web.Application.__init__(self,handlers,**settings)
classTodos(CorsMixin,tornado.web.RequestHandler):
CORS_ORIGIN='*'
CORS_METHODS='POST,GET,OPTIONS'
CORS_HEADERS='Origin,X-Requested-With,Content-Type,Accept,
content-type'
CORS_MAX_AGE=1728000
CORS_CREDENTIALS=False
defget(self):
Todos=self.application.db.todos
todo_id=self.get_argument("id",None)
iftodo_id:
todo=Todos.find_one({"_id":ObjectId(todo_id)})
todo["_id"]=str(todo['_id'])
self.write(todo)
else:
todos=Todos.find()
result=[]
data={}
fortodointodos:
todo["_id"]=str(todo['_id'])
result.append(todo)
data['todos']=result
self.write(data)
defoptions(self):
todo_id=self.get_argument("id",None)
Todos=self.application.db.todos
#ifself.request['Access-Control-Request-Method']=='POST':
self.set_header("Access-Control-Allow-Headers","content-type")
defpost(self):
data=json.loads(self.request.body)
Todos=self.application.db.todos
todo_id=self.get_argument("id",None)
iftodo_id:
#performanedit
todo=Todos.find_one({"_id":ObjectId(todo_id)})
#hereshouldperformtheupdate…
todo['text']=data['text']
todo['details']=data['details']
todo['done']=data['done']
Todos.save(todo)
#cos_idisnotJSONserializable.
todo["_id"]=str(todo['_id'])
self.write(todo)
else:
data=json.loads(self.request.body)
todo={
'text':data['text'],
'details':data['details'],
'done':data['done']
}
a=Todos.insert(todo)
#cos_idisnotJSONserializable.
todo['_id']=str(a)
self.write(todo)
defmain():
tornado.options.parse_command_line()
http_server=tornado.httpserver.HTTPServer(Application())
http_server.listen(options.port)
tornado.ioloop.IOLoop.instance().start()
if__name__=="__main__":
main()
Here’swhat’shappeninginourcode:
Rightatthetopofserver.py,wesimplyimportvariouslibrariesrequiredforoursever.Next,wehavetheApplicationclassdefined,whereweinitializethehandlersrequiredforourserver.HandlersaresimplyURLsthataremappedtoclass-basedviewsintheTornadowebserver.TheTodosclasscontainsthreefunctions:
get:ThisfunctionsupportstheGEToperationsoptions:ThisfunctionsupportstheCORSOPTIONSstringpost:ThisfunctionsupportsthePOSToperations
Finally,wedefineamain()function,whichissupposedtorunourTornadoserverwhencalled.
TomakesurethattheTornadoPythonserverisworking,youmustfirstrunMongoDBonyourcomputer,navigatetothefolderwhereserver.pyresides,andthenrunthefollowingcommand:
pythonserver.js
Onceyouissuetheprecedingcommand,openupyourwebbrowserandnavigatetohttp:/localhost:8000;youwillseethefollowingscreen:
GettinganerrorsincewehavenotdefinedahandlerforthisURL
WegetanerrormessagebecausewedidnotdefineanyhandlersfortheURLathttp://localhost:8000.Solet’snowchangeourURLtohttp://localhost:8000/todos.Youshouldtechnicallyreceiveanemptypage,butbecausewealreadyhavesometestdatasavedinMongoDB,youwillseethefollowingscreen:
Asamplelistofdatareturned
SowhenweperformaGETrequestat/todos,wesimplyretrieveafulllistofto-dos.Similarly,whenweperformaGETrequestwithanID,youwillseethatonlyoneto-doitemisbeingreturned.
Oneto-doitemreturned
ChangingAngularJStoperformRESTfulrequestsNowthatourserverisready,weneedtostartcodingourAngularJSapptomakeitreadyforRESTfuloperations.We’llbemakingchangestothecodefoundinjs/controllersandjs/services,ingeneral.We’llstartwithjs/services/todo.jsfirst.
Usingthe$httpmoduleofAngularJSLet’sgetbacktojs/services/todo.js.Wearegoingtoincludefourbasicoperationsinthismodule,namelygettingallto-doitems,gettingoneto-doitem,savingto-dolists,andfinally,editingto-doitemoperations.
ThecodethatwillconsumetheRESTfulAPIsisasfollows:
angular.module('todoApp.services',[])
.config(function($httpProvider){
$httpProvider.defaults.useXDomain=true;
delete$httpProvider.defaults.headers.common['X-Requested-With'];
})
.factory('Todos',function($http){
return{
getAll:function(){
//return$http.get('http://10.0.2.2:8000/todos');//ifusing
android
return$http.get('http://localhost:8000/todos');
},
getTodo:function(id){
//return$http.get('http://10.0.2.2:8000/todos?id='+id);//ifusing
android
return$http.get('http://localhost:8000/todos?id='+id);
},
save:function(todoData){
//return$http.post('http://10.0.2.2:8000/todos',todoData);//if
usingandroid
return$http.post('http://localhost:8000/todos',todoData);
},
edit:function(id,todoData){
//return$http.post('http://10.0.2.2:8000/todos',todoData);//if
usingandroid
return$http.post('http://localhost:8000/todos?id='+id,
todoData);
},
delete:function(id){
console.log("idontthinkIhaveadeletehere.")
}
}
})
Firstandforemost,noticethatwehavea.configfilewhereweused$httpProviderandmadesomechangestothedefaultbehavioroftheAngularJS$httprequests.Thefirsttwohighlightedlineswith.configaretheretoensurethatcross-domainrequestscanbedone,sinceourAngularJSappresidesinadifferentlocationasourserver.
Secondly,noticethattheservicesmodulesimplyreturnstheoperationsweneed,withtherelevantRESTfulendpoints.Forexample,getAllisafunctionthatreturnstheendpointhttp://localhost:8000/todosusingaGETrequest.
Rewritingcontrollerstomakeuseofthe$httpmoduleIntheprevioussection,werewroteservices/todo.jssothatitnowperformsRESTfulrequests.Howdoweconsumetheseservicesinthecontroller?WecansimplydosobyincludingTodounderthecontrollersthatwewanttousetheservicesprovidedforbyTodo.Takeforinstance,ListCtrl:
.controller('ListCtrl',function($scope,$rootScope,$http,Todos){
Todos.getAll().success(function(data){
$rootScope.todos=data['todos'];
})
$scope.remaining=function(){
varcount=0;
angular.forEach($scope.todos,function(todo){
count+=todo.done?false:true;
});
returncount;
};
})
Inthehighlightedlineintheprecedingcode,noticethatwehaveincludedTodos.Next,inordertoretrieveallto-doitems,wesimplymakea.getAlI()callbydoingTodos.getAll().Ifthecallissuccessful,wereturntheJSONdataandassignitto$rootScope.todos.
Weuse$rootScopeinsteadof$scope,becauseIwantedallthecontrollerstobeabletoaccessthecurrentstateoftodoswithoutmakinganothercalltothebackendserver.
Next,forCreateCtrl,wesimplymakea.save()tothebackendwithourto-dodata:
.controller('CreateCtrl',function($scope,$rootScope,$location,$timeout,
Todos){
$scope.todoText="";
$scope.todoDetails="";
$scope.save=function(){
vartodo={
text:$scope.todoText,
done:false,
details:$scope.todoDetails
};
console.log($rootScope.todos);
$rootScope.todos.push(todo);
console.log($rootScope.todos);
Todos.save(todo);
$location.path('/');
};
})
NoticethatwesimplymakeaTodos.save()calltosaveourdatatoourbackendserver.
Finally,let’stakealookatEditCtrl.Thistimearound,wesimplygettheto-doitembyitsID,andperformedit()whenwehavemadechangestotheitem.Thisisshownbythehighlightedlineinthefollowingcode:
.controller('EditCtrl',
function($scope,$location,$routeParams,Todos){
//$scope.todos=Todos;
console.log($location.$$path.split("/"));
varid=$location.$$path.split("/")[2];
varresult=Todos.getTodo(id).success(function(data){
console.log("andthereturneddatais");
console.log(data);
$scope.todoText=data.text;
$scope.todoDetails=data.details;
returndata;
})
$scope.save=function(){
vartodo={
id:$location.$$path.split("/")[2],
text:$scope.todoText,
details:$scope.todoDetails,
done:true
}
Todos.edit(id,todo);
}
});
CheckingourcodeNowthatwehaverewrittenourAngularJSapp,it’stimetocheckifitworkscorrectly.Asusual,fireupyourserverbyissuingthePythonserver.pyandstartyourAngularJSappusingalocalserver.WhenyoufirstloadyourAngularJSapp,youwillseeaGETrequestonyourserverinthebackend.Here’swhatitlookslikeonmyterminal:
AGETrequest
Let’strycreatinganewto-doitembyclickingonNEW.Asusual,youshouldseethefollowingscreen:
Ourinputfieldstoaddato-dolist
Nowtypeinthenameanddetails.I’mgoingtojusttypehelloworldasthenameandhelloworlddescriptionasthedetails.Oncedone,clickonSave.Youshouldseethatyouarenowredirectedtothepagecontainingthelistoftodoswiththenewto-doitematthebottom.
Ifyoulookatyourterminal,youwillalsoseethefollowingscreen:
AseriesofHTTPrequestscomingfromourAngularJSapp
TheOPTIONSrequestissentfromAngularJS.Youarethenredirectedbacktothehomepage(that’swheretheGETrequestoccurs),andfinally,thePOSTactioniscompleted,asshownbythelastlineinthisterminal.
Nowyoucanattempttoperformaneditoperation.Wewilledittheitemthatwehavejustadded:
Addingato-doitem
Wewillnowaddnewdetailstotheto-doitem:
Makingchangestotheto-doitem
NowclickonSave.Wewillseethefollowingscreenwhenweareredirectedbacktothehomepage:
Theto-doitemisbeingeditedandisreflectedonthelistofto-doitems
Ifeverythingworksasexpected,wecannowtestourcodeonAndroidandiOS.
PreparingforPhoneGapAsusual,wewillneedtotransferthecodethatwehavewrittenunder/www.MakesureyoutransferthecodecorrectlytoPhoneGap.Ifyou’vedoneitcorrectly,yourPhoneGapfoldershouldlookasfollows:
Codearrangementatthispointintime
Ifyouhaveperformedtheprevioussteps,let’stestitoutoniOSandAndroid.
TestingourcodeoniOSTotestourcodeoniOS,wesimplynavigatetoourPhoneGapproject(phonegap/todo)andissuethefollowingcommand:
cordovaemulateios
RemembertoturnonyourserverandMongoDBaswell.YoushouldseethefollowingscreenonyourAndroidemulator:
Yourto-dolistappshouldlookandworkasexpected
Thisissimplysomeofthedatathatwecreatedbeforehand.Let’sgoaheadandcreateanewto-doitem:
Yourto-dolistappshouldlookandworkasexpected
Nowgoaheadandsaveit.Youwillseethefollowingscreen:
Yourto-dolistappshouldlookandworkasexpected;creatingato-doitemworks
I’mjustgoingtogoaheadandeditthelatestto-doitem:
Editingato-doitemshouldworkasexpected
NowclickonSave.Youshouldseethelatestitemsavedasshowninthefollowingscreenshot:
Aftereditingandsavingtheto-doitem,theappworksasexpected
TestingourcodeonAndroidWeneedtomakesomechangesinordertotestourcodeonAndroid.Wealsoneedtomakechangestotheendpointthatwearecalling;http://localhost:8000willneedtobechangedtohttp://10.0.2.2.
ForthisAndroidversion,yourjs/services/todo.jswilllookasfollows:
angular.module('todoApp.services',[])
.config(function($httpProvider){
$httpProvider.defaults.useXDomain=true;
delete$httpProvider.defaults.headers.common['X-Requested-With'];
})
.factory('Todos',function($http){
return{
getAll:function(){
return$http.get('http://10.0.2.2:8000/todos');//ifusingandroid
//return$http.get('http://localhost:8000/todos');
},
getTodo:function(id){
return$http.get('http://10.0.2.2:8000/todos?id='+id);//ifusing
android
//return$http.get('http://localhost:8000/todos?id='+id);
},
save:function(todoData){
return$http.post('http://10.0.2.2:8000/todos',todoData);//if
usingandroid
//return$http.post('http://localhost:8000/todos',todoData);
},
edit:function(id,todoData){
return$http.post('http://10.0.2.2:8000/todos',todoData);//if
usingandroid
//return$http.post('http://localhost:8000/todos?id='+id,todoData);
},
delete:function(id){
console.log("idontthinkIhaveadeletehere.")
}
}
})
Noticethatwearecommentingoutthehttp://localhost:8000versionandusingthehttp://10.0.2.2version.
Next,wesimplynavigatetoourPhoneGapprojectphonegap/todoandissuethefollowingcommand:
cordovaemulateandroid
RemembertoturnonyourserverandMongoDBaswell.YoushouldseethefollowingscreenonyourAndroidemulator:
Onthefirstload,theto-doappshowsalistofitemswehaveinthedatabase
Thisissimplyjustsomeofthedatathatwehavecreatedbeforehand.Asyoucansee,theitemcreatedoniOSispresent.SoI’mjustgoingtogoaheadandcreateanothernewto-doitem:
Addinganewto-doitemtoyourAndroidapp
Let’sgoaheadandsaveit.Youshouldseethenewitemonourto-dolist.
Addinganewitemworksasexpected
Finally,I’mgoingtomakesomeeditstomakesurethatourcodeisworkingfine.ClickonEdit/DetailsfortheAndroiditemontheemulatorandstarteditingit:
Editingato-doitem
Onceyou’vesavedtheitem,youwillseethattheAndroiditemisnowedited.
SummaryThat’sit!Wewentthroughquiteabitofdetailinthischapter.BynowyoushouldseethatshiftinganAngularJSapptoPhoneGapisverystraightforward;allyouneedtodoisplaceyourfilesinthewww/folder.YoualsolearnedhowtocreateRESTfulappsontopofAnguarJSandtheTornadowebserver.
Inthenextchapter,wewillcoveradvanced-leveltopics,suchasoptimizingourAngularJSappfortouchdevices,usingPhoneGappluginssuchastheFacebookplugin,andhowtocreatedirectivesforourAngularJSapp.Seeyouthere.
Chapter4.AddingAuthenticationCapabilitiesUsingPhoneGapPluginsInthischapter,wewillbeaddingauthenticationcapabilitiestoourto-dolistapp.Tobespecific,wearegoingtoaddFacebookLogincapabilitiestoourapp.Wewillstartworkingontheweb-basedversionofourappbeforeportingthecodeovertothePhoneGapversion.InthePhoneGapversion,wewillbeleveragingonthePhoneGapplugininordertoachievewhatwehavedonefortheweb-basedversion.TheportingofcodewillbeslightlylessstraightforwardduetotherecentchangestoPhoneGap’sFacebookplugin.Sosittightandseehowwecanaddlogincapabilitiesinthischapter.
AddingFacebookConnecttotheto-dolistappAddingFacebookConnecttoourweb-basedversionoftheappisstraightforward.Youwillneedtosignupforanewapp(orusethecurrentone)fromhttps://developers.facebook.com/andtakenoteoftheappIDandapp’ssecretkey.Then,asusual,youwillneedtoinitiateyourappusingsomeFacebook-specificJavaScriptandlibrary“namely”Facebook’sJavaScriptSDK.
Incaseyouarewondering,theFacebookConnectpluginisusedtoperformFacebooklogininPhoneGapapps.
InitializingandpreparingforFacebookConnectLet’squicklydiveintothesourcecodeforindex.html:
<!doctypehtml>
<htmlng-app="todoApp">
<head>
<linkrel="stylesheet"
href="http://netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css"
>
<style>
body{
padding:40px20px020px;
}
</style>
</head>
<body>
<divid="fb-root"></div>
<scriptsrc="http://connect.facebook.net/en_US/all.js"></script>
<script>
FB.init({
appId:XXX',
xfbml:true,
version:'v1.0'
});
</script>
<h2>Todos</h2>
<divng-view></div>
<scriptsrc="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-
beta.7/angular.min.js"></script>
<scriptsrc="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-
beta.7/angular-resource.min.js"></script>
<scriptsrc="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-
beta.7/angular-route.min.js"></script>
<scriptsrc="js/controllers/todo.js"></script>
<scriptsrc="js/controllers/user.js"></script>
<scriptsrc="js/services/todo.js"></script>
<scriptsrc="js/app.js"></script>
</body>
</html>
Takenoteofthefollowinghighlightedlinesofcode:
<divid="fb-root"></div>
<scriptsrc="http://connect.facebook.net/en_US/all.js"></script>
<script>
FB.init({
appId:'XXX',
xfbml:true,
version:'v1.0'
});
</script>
TheprecedingcodeisbasicallyFacebook-specificandisrequiredinorderforyoutouseFacebookLoginforyourwebapplication.YouwillneedtoreplaceXXXwithyourownappID,andmakesurethatthesiteURL(foundunderyourappsettingsonFacebookDevelopers)issettoyourloginlocation.
Next,wehave<scriptsrc="js/controllers/user.js"></script>.Thiswillbeasmallsnippetofcode,wherewewilladdthelogincapabilitiesforourapp.
WritingtheusercontrollerWe’llnowstartwiththecontrolleraspectoftheapp.Solet’sstartbycreatinganewfilecalleduser.jsinjs/controllers/user.js.Asusual,wewillneedtodefineangular.moduleandwearegoingtonamethiscontrollerUserLoginCtrl.WecansimplymakecallstoFacebookusingtheFBobject,whichwasinitiatedearlierinindex.html.Sohere’swhatyouruser.jsshouldlooklike:
angular.module('todoApp.userControllers',[])
.controller('UserLoginCtrl',function($scope,$rootScope,$http,
$location,Todos){
$scope.login=function(){
//sothisisfordesktoptesting
FB.login(function(response){
if(response.authResponse){
console.log('Welcome!Fetchingyourinformation….');
FB.api('/me',function(response){
console.log('Goodtoseeyou,'+response.name+'.');
$location.path('/');
if(!$scope.$$phase)$scope.$apply();
});
}else{
console.log('Usercancelledloginordidnotfullyauthorize.');
}
});
};
Themainfunctionhereis$scope.login,whichsimplywrapsaroundtheFB.logincallwhereweattempttologintheuser.TheAngularJS-specificstuffisfoundinthefollowingcode:
$location.path('/');
if(!$scope.$$phase)$scope.$apply();
Thismeansthatwewillredirecttheuserbacktotheindexpageafterhe/shehasloggedinsuccessfully.
NoteThe$apply()optionismeanttostarta$digestcycle.Agreattutorialthatexplainsthisoperationisavailableathttp://www.sitepoint.com/understanding-angulars-apply-digest/.
Nowyoumaybewonderingifthere’sanypagewherelogintakesplace.Yes,ofcourse,andthisisexactlywhatwearegoingtodointhenextsection.
AddingaloginpageWearegoingtocreateanewfilecalledlogin.html.Thisfilewillresideinthepartialsfolder,whereallHTMLsnippetsarefound.However,first,weneedtodefinetheroutewherethisHTMLpartialwillbeloaded.Solet’stakealookatapp.js:
angular.module('todoApp',[
'ngRoute',
'todoApp.controllers',
'todoApp.userControllers',
'todoApp.services'
])
.config(function($routeProvider){
$routeProvider
.when('/',{
controller:'ListCtrl',
templateUrl:'partials/list.html'
})
.when('/edit/:id',{
controller:'EditCtrl',
templateUrl:'partials/detail.html'
})
.when('/new',{
controller:'CreateCtrl',
templateUrl:'partials/detail.html'
})
.when('/login',{
controller:'UserLoginCtrl',
templateUrl:'partials/login.html'
})
.otherwise({
redirectTo:'/'
});
})
Therearetwohighlightedpartsinourcode,todoApp.userControllers,whichmeansthatweareloadingthiscontrollersoitcanbeusedinourapp.
Thesecondinstanceisthefollowingcode:
.when('/login',{
controller:'UserLoginCtrl',
templateUrl:'partials/login.html'
})
Theprecedingcodemeansthatwhentherouteis/login,wewillbeusingUserLoginCtrlandlogin.html.NowthatwehavedefinedtherouteandcodedUserLoginCtrl,it’stimetoworkonlogin.html.Thelogin.htmlfileissimpleandstraightforward;itcontainsatitleandLoginbutton:
<h3>Pleaselogin</h3>
<buttonng-click="login()">Login</button>
Yup,that’sright,simpleandstraightforward.Soifyousaveyourfilesandvisityourappat
/login,youwillgetthefollowingscreenshot:
LoginScreen
Sorightnow,wecantryoutbyclickingonLogin.Ifeverythingworkscorrectly,youwillberedirectedbacktoyourindexpage.ThispagewillshowyouacompletelistofTodos,asshowninthefollowingscreenshot:
ListofTodos
Alright,prettycoolyeah?However,wearemissingoutsomestuff.Forexample,whathappensiftheuserwantstologout?Orwhathappenswhentheusergoesstraighttotheindexpagewithoutlogginginfirst?Wewilldealwiththatinthenexttwosections.
AddingalogoutfunctionPerformthefollowingstepstoaddalogoutfunction:
1. Toaddalogoutfunctionality,wewillfirstneedtohavealogoutbutton.So,wecanaddthisfunctionalityintheindexpagebyplacingthelogoutbuttoninpartials/list.html:
<buttonng-click="logout()"style="float:right">Logout</button>
<span>{{remaining()}}of{{todos.length}}remaining</span>
<table>
<thead>
<tr>
<th>Todo</th>
<th>Done</th>
<th>Details</th>
<th><ahref="#/new"><iclass="icon-plus-sign">NEW</i></a></th>
</tr>
</thead>
<tbody>
<trng-repeat="todointodos">
<td>{{todo.text}}</td>
<td><inputtype="checkbox"ng-model="todo.done"value="todo._id">
</td>
<td>
<ahref="#/edit/{{todo._id}}"><iclass="icon-
pencil">Edit/Details</i></a>
</td>
</tr>
</tbody>
</table>
2. Theentirepartials/list.htmlfileisthesameasbefore,exceptforthehighlightedlinewherethelogoutbuttonisadded.
3. Next,sincethelogoutbuttonisfoundinpartials/list.html,thismeansthatweneedtoaddafunctiontologoutoftodo.js.Now,goingtocontrollers/todo.js,prependthe$scope.logoutfunctionjustbeforeTodos.getAll():
$scope.logout=function(){
alert('loggingout')
//thisisthedesktopversion
FB.logout(function(response){
alert('loggedout');
$location.path('/login');
if(!$scope.$$phase)$scope.$apply();
});
}
Todos.getAll().success(function(data){
$rootScope.todos=data['todos'];
})
4. Thehighlightedlinesofcodearethelinesofcodethatwillbehandlingthelogout.So
basically,whathappensisthatonclickingtheLogoutbutton,youwillseeanalertboxthatsaysloggedout,afterwhichyouwillberedirectedtotheloginpage.
5. Saveyourfilesandrefreshyourbrowser.Youshouldseethefollowingscreenshot:
ListoftodoswiththeLogoutbutton
6. NoticethatwehaveaLogoutbuttontotherightofthepage.Nowclickonitandyoushouldseethefollowingscreenshot,ifeverythingisworkingcorrectly:
7. AfterclickingonOK,youwillberedirectedbacktotheloginpage.
Now,let’smoveontothelastrequirement,whichischeckingfortheloginstatus.
CheckingtheloginstatusCheckingoftheloginstatuswillbedoneintheindexpage,forsimplicity.Soweneedtoaddafunctionincontrollers/todo.js.Weneedtoprependthefunctionsusedinthefollowingcode,beforethe$scope.logoutfunctionthatweaddedintheprevioussection:
$scope.checkLogin=function(){
FB.getLoginStatus(function(response){
if(response.status=='connected'){
//alert('loggedin');
console.log("loggedinbro");
}
else{
//alert('notloggedin');
$location.path('/login');
if(!$scope.$$phase)$scope.$apply();
}
});
}
$scope.checkLogin();
Nowsavethefileandrefreshyourbrowser.Ifyouarestillloggedout,youshouldberedirectedtothe/loginpage,whereyouwillbeaskedtologin.Forexample,asIamstillloggedoutfromtheprevioussection,Iamredirectedtotheloginpage.AfterclickingontheLoginbutton,Iampromptedtologin,asshowninthefollowingscreenshot:
FacebookLogin
Afterenteringmycredentials,Iamloggedinsuccessfully.Feelfreetotryoutotherfunctionalitiesthatwehavecodedintheprevioussection,justtomakesurethatthingsareworkingasexpected.
Ifeverythingisgoodtogo,it’stimetomoveonandportourcodetoPhoneGap.Itwillbeslightlydifferentfromwhatwehavedoneintheprevioussections,astherearesomeextrastepsandprecautionsthatwehavetotake.Youmightwanttotakeashortbreakbeforecontinuingtothenextsection.
FacebookloginforPhoneGapPhoneGaphasgonethroughquiteabitofchangesnotonlyforthemainlibrary,butalsothepluginsystem.Inthissection,youwillseethatwecanquicklyinstallPhoneGappluginsusingthecommand-linetool,withouttheusualmultiplestepsthatwehavetofollowifwewanttoinstallitmanually.
InstallingtheFacebookpluginSincewehavealreadyaddediOSandAndroidplatforms,thisplugininstallationwilladdpluginsforbothiOSandAndroid.ToinstalltheFacebookplugin,youwillneedtonavigatetoyourtodoappproject.Next,issuethefollowingcommand:
cordovapluginadd
https://github.com/phonegap/phonegap-facebook-plugin,--variable
APP_ID="XXXXX"--variableAPP_NAME="AngularPhoneGapTest".
YouwillneedtoreplaceXXXXwithyourappIDandyoucannameyourapp_nameappanynameyouwant.
Theinstallationprocessisnowcomplete.ForAndroid,youwillneedthreemoresteps:
1. ImportyourtodoappintoyourEclipsedevelopmentenvironmentasanAndroidproject.
2. WhilestillinyourEclipseeditor,intheleft-handcolumnwhereallyourprojectsarelisted,rightclickonProperties.
3. MakesurethatyouimportFacebookLibandCordovaLibundertheLibrarysection(atthebottomofthenextscreenshot):
Oncethisisdone,wehavecompletedinstallationforbothiOSandAndroid.Now,it’stimetotestthecode.MostoftheexamplesrunoniOS,butbearinmindthatthesamepieceofcodewillrunproperlyandcorrectlyonAndroidaswell.
TestingoutFacebookLoginonPhoneGapInordertotestifourFacebookLoginpluginisinstalledcorrectlyonPhoneGap,weneedtowriteasimpleexamplejusttoseeiftheappisworkingcorrectly.TherearemanyexamplesoutthereontheInternet,butI’vewrittenasimpleoneherethatshouldquicklyshowifourappisworking.Atthesametime,youwillalsoseethatcallingtheFBSDKisalsoslightlydifferent.Let’sjumpstraightintothecodebywritinganewindex.htmlpageforourPhoneGapapp:
<!DOCTYPEhtml>
<html>
<head>
</head>
<body>
<buttononclick="login()">Login</button>
<divid="data">loading…</div>
<divid="fb-root"></div>
<!--cordova-->
<scriptsrc="cordova.js"></script>
<!--cordovafacebookplugin-->
<scriptsrc="cdv-plugin-fb-connect.js"></script>
<!--facebookjssdk-->
<scriptsrc="facebook-js-sdk.js"></script>
<script>
if((typeofcordova=='undefined')&&(typeofCordova=='undefined'))
alert('Cordovavariabledoesnotexist.Checkthatyouhaveincluded
cordova.jscorrectly');
if(typeofCDV=='undefined')alert('CDVvariabledoesnotexist.
Checkthatyouhaveincludedcdv-plugin-fb-connect.jscorrectly');
if(typeofFB=='undefined')alert('FBvariabledoesnotexist.Check
thatyouhaveincludedtheFacebookJSSDKfile.');
FB.Event.subscribe('auth.login',function(response){
alert('auth.loginevent');
});
FB.Event.subscribe('auth.logout',function(response){
alert('auth.logoutevent');
});
FB.Event.subscribe('auth.sessionChange',function(response){
alert('auth.sessionChangeevent');
});
FB.Event.subscribe('auth.statusChange',function(response){
alert('auth.statusChangeevent');
});
varfbLoginSuccess=function(userData){
alert("UserInfo:"+JSON.stringify(userData));
}
functionlogin(){
facebookConnectPlugin.login(["basic_info"],
fbLoginSuccess,
function(error){alert(""+error)}
);
}
document.addEventListener('deviceready',function(){
try{
alert('Deviceisready!Makesureyousetyourapp_idbelowthis
alert.');
FB.init({appId:"XXXX",nativeInterface:CDV.FB,useCachedDialogs:
false});
document.getElementById('data').innerHTML="";
}catch(e){
alert(e);
}
},false);
</script>
<divid="log"></div>
</body>
</html>
Theprecedingcodeissomewhatsimilartotheofficialexamples,butwiththreesubtledifferences,asshownbythehighlightedlines:
<buttononclick="login()">Login</button>
Weareonlyfocusedonlogin()here,sowewillremovetheremainingfunctionalitiessuchaspostingtothewallandsoon
Thesecondsectionofthecodeisasfollows:
<scriptsrc="cordova.js"></script>
<!--cordovafacebookplugin-->
<scriptsrc="cdv-plugin-fb-connect.js"></script>
<!--facebookjssdk-->
<scriptsrc="facebook-js-sdk.js"></script>
WearestillrequiredtoinstallallPhoneGap-relatedfiles,includingthoserelatedtotheFacebookplugins
Thethirdandmostimportantsectionisasfollows:
varfbLoginSuccess=function(userData){
alert("UserInfo:"+JSON.stringify(userData));
}
functionlogin(){
facebookConnectPlugin.login(["basic_info"],
fbLoginSuccess,
function(error){alert(""+error)}
);
}
NoticethatwearemakingalogincalltoFacebookusingfacebookConnectPlugin.logininsteadofFB.login.ThisisduetothemajorchangeinpluginsbythePhoneGapteam.
WhenweareportingourcodetoPhoneGapfromthewebversion,ourFacebookcallswillbechangedtoreflectthis.Fornow,followonwiththissection.Oncethecodeiswritten,savethefileandruntheiOSemulator.Issuethefollowingcommand:
cordovaemulateios
YouwillthenseeyouriOSemulatorgetfiredup,andyouwillbegreetedwiththefollowingscreenshot:
Onenteringtheapp
AfterclickingonOK,youwillseethattheloading…messagehasvanished.Next,youcanclickonLoginandyouwillbepresentedwithFacebook’sloginpage:
FacebookLoginScreen
Sincewehavealreadyauthorizedtheapp,there’snoneedtologinorauthorizetheappagain.ClickonOKandyoushouldseeanewscreenwithanalertboxshowingaJSONrepresentationofyourdata:
Ifyougetthepreviousscreenshot,thismeansthateverythingyouhavedonetillnowisrightandshouldworkwellforAndroidtoo.Ifso,timetoportourweb-basedversionofthecodetothePhoneGapversion.
FromwebtoPhoneGapIfyouremember,intheprevioussection,IbrieflymentionedthatmakingcallstoFacebookusingtheJavaScriptSDKisnowslightlydifferentinPhoneGap,comparedtotheweb-basedversion.InsteadofasimplisticFB.login()callinthewebversion,weneedtomakeafacebookConnectPlugin.login()callinthePhoneGapversion.Inthissection,we’lltakecarefulstepstoportourcodefromtheweb-basedversiontoaPhoneGapversion.
ImportingFacebookandPhoneGappluginsFirst,weneedtoimportFacebook-andPhoneGap-relatedplugins.WealsoneedtoslightlychangehowweinitiatetheFBobject.Sogoingbacktoyourindex.htmlfile,here’swhatyouneedtodo:
<!doctypehtml>
<htmlng-app="todoApp">
<head>
<linkrel="stylesheet"
href="http://netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css"
>
<style>
body{
padding:40px20px020px;
}
</style>
</head>
<body>
<divid="fb-root"></div>
<h2>Todos</h2>
<divng-view></div>
<!--thisisforphonegap-->
<scriptsrc="cordova.js"></script>
<!--cordovafacebookplugin-->
<scriptsrc="cdv-plugin-fb-connect.js"></script>
<!--facebookjssdk-->
<scriptsrc="facebook-js-sdk.js"></script>
<script>
document.addEventListener('deviceready',function(){
try{
alert('Deviceisready!Makesureyousetyourapp_idbelowthis
alert.');
FB.init({appId:"135542699836039",nativeInterface:CDV.FB,
useCachedDialogs:false});
}catch(e){
alert(e);
}
},false);
</script>
<scriptsrc="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-
beta.7/angular.min.js"></script>
<scriptsrc="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-
beta.7/angular-resource.min.js"></script>
<scriptsrc="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-
beta.7/angular-route.min.js"></script>
<!--
<scriptsrc="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-
beta.7/angular-touch.min.js"></script>
-->
<scriptsrc="js/controllers/todo.js"></script>
<scriptsrc="js/controllers/user.js"></script>
<scriptsrc="js/services/todo.js"></script>
<scriptsrc="js/app.js"></script>
</body>
</html>
Youwillfirstneedtoimportcordova.js,cdv-plugin-fb-connect.js,andfaceboo-js-sdk.jsasshownhere:
<!--thisisforphonegap-->
<scriptsrc="cordova.js"></script>
<!--cordovafacebookplugin-->
<scriptsrc="cdv-plugin-fb-connect.js"></script>
<!--facebookjssdk-->
<scriptsrc="facebook-js-sdk.js"></script>
Next,weinitiatetheFBobjectbywaitingforthedevicereadyeventspecifictoPhoneGap:
<script>
document.addEventListener('deviceready',function(){
try{
alert('Deviceisready!Makesureyousetyourapp_idbelowthis
alert.');
FB.init({appId:"XXXXX",nativeInterface:CDV.FB,useCachedDialogs:
false});
}catch(e){
alert(e);
}
},false);
</script>
Asusual,remembertoreplacetheXXXXXwithyourownappIDfromFacebooksothatyourloginredirectscorrectly,andhastherightcredentials.
ChangingFBtofacebookConnectPluginThenextthingthatweneedtodoischangeFBtofacebookConnectPlugin.Wewillstartwithcontroller/user.js.Thecodeshouldlooklikethefollowing,afterthechange:
angular.module('todoApp.userControllers',[])
.controller('UserLoginCtrl',function($scope,$rootScope,$http,
$location,Todos){
$scope.login=function(){
//placewheretheuserjustclickandlogin
varfbLoginSuccess=function(userData){
alert("UserInfo:"+JSON.stringify(userData));
$location.path('/');
if(!$scope.$$phase)$scope.$apply();
}
//thisisusedforPhoneGaPver
facebookConnectPlugin.login(["basic_info"],
fbLoginSuccess,
function(error){alert(""+error)}
);
};
})
WehaveanewvariablenamedfbLoginSuccess,whichiscalledaftertheuserlogsinsuccessfully.Next,wehavefacebookConnectPlugin.login,whichmakesacalltologintoFacebookviaPhoneGap’splugin.
Next,weneedtoworkoncontroller.js.Thetwofunctionsthatyouneedtochangeare$scope.checkLoginand$scope.logout.WesimplyreplaceFBwithfacebookConnectPlugin.Sohere’swhatthecodelookslikenow:
$scope.checkLogin=function(){
facebookConnectPlugin.getLoginStatus(function(response){
alert(response);
if(response.status=='connected'){
//alert('loggedin');
console.log("loggedinbro");
$location.path('/');
if(!$scope.$$phase)$scope.$apply();
}
else{
//alert('notloggedin');
$location.path('/login');
if(!$scope.$$phase)$scope.$apply();
}
});
}
$scope.logout=function(){
alert('loggingout')
//thisisthePhoneGapversion
facebookConnectPlugin.logout(function(response){
alert('loggedout');
$location.path('/login');
if(!$scope.$$phase)$scope.$apply();
});
}
Nowthatwehavemadetherequiredchanges,it’stimethatwetestthefunctionalities.
Theto-dolistappwithFacebookLoginonPhoneGapAsusual,weneedtorunourcodeusingPhoneGap’scommand-lineinterface.Sotochangeyourcurrentdirectorybacktothetodolistapp,issuethefollowingcommand:
cordovabuildios
cordovaemulateios
Onceyouhaveissuedthecommand,youshouldseeyouriOSemulatorfiredup.Next,youwillseethefollowingscreenshot:
OnEnteringtheApp
Aswearestillloggedinfromtheprevioussections,wegettoseeourto-dolistitemsevenafterclickingonOK.Atthispoint,ifyouarestillloggedin,feelfreetoclickonLogout,afterwhichyoushouldseethefollowingscreenshot:
LoginScreen
Asusual,youcanloginandbegreetedbythepreviousscreenshot.AfterclickingonOK,youcanstarttoplayaroundwithyourapp.
Successfullogin
Feelfreetoaddnewitems,editthem,andloginandoutjusttoseeifthecodeisworkingcorrectly.Ifallgoeswell,congratulations!YounowhaveaworkingPhoneGapappmakingRESTfulcallscoupledwithFacebookLogincapabilities.
SummaryTosummarize,weworkedourwayfromaverysimpleAngularJSapptoonewhichcanmakeRESTfulcallscoupledwithFacebookLogincapabilities.Ateachstage,wealsoportedthecodetoPhoneGapandmadesureitworks.Theimportanttakeawayhere,isthatusingacommand-lineinterfacecandrasticallyreducethenumberofstepsrequiredtosetupourPhoneGapproject.Inthenextchapter,wewillbeworkingonanimations;variousanimationtechniqueswillbeusedforourPhoneGapapp.
Chapter5.SprucingUptheAppUsingAnimationsandMobileDesignWelcometothischapter!Inthischapter,wewillspruceupourappusinganimationandstylesthatmimicthemobileuserinterface.Thiswillbeashortyetusefulchapter.Asusual,thecodeshowninthischaptercanbeappliedacrossiOSandAndroidapps.
Inthischapter,we’lllearnabout:
PerforminganimationsusingngAnimatePitfallstoavoidwhenperforminganimationsinPhoneGap
AddinganimationstoyourwebappAddinganimationsissurprisinglyeasyusingAngularJS.ThengAnimatemoduleofAngularJSwilltakeuserstherewithCSSanimations.
We’llstartbyaddinganimationtoourwebappbeforeportingitovertoPhoneGap.Todothis,headbacktoyourweb-basedversionofthecodeandopenindex.html.Therearethreechangesthatyouneedtomake:
1. Addanewindex.cssfile.2. Addclass="todos"inyour<divng-view></div>.3. Add<scriptsrc="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-
beta.7/angular-animate.min.js"></script>tothelistofimportedJavaScript.4. Theendresultofyourcodeinindex.htmlshouldlooklikethis:
<!doctypehtml>
<htmlng-app="todoApp">
<head>
<linkrel="stylesheet"
href="http://netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.
css">
<linkrel="stylesheet"href="css/index.css">
<style>
body{
padding:40px20px020px;
}
</style>
</head>
<body>
<divid="fb-root"></div>
<scriptsrc="http://connect.facebook.net/en_US/all.js"></script>
<script>
FB.init({
appId:'XXXXX',
xfbml:true,
version:'v1.0'
});
</script>
<h2>Todos</h2>
<divng-viewclass="todos"></div>
<scriptsrc="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-
beta.7/angular.min.js"></script>
<scriptsrc="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-
beta.7/angular-resource.min.js"></script>
<scriptsrc="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-
beta.7/angular-route.min.js"></script>
<scriptsrc="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-
beta.7/angular-animate.min.js"></script>
<scriptsrc="js/controllers/todo.js"></script>
<scriptsrc="js/controllers/user.js"></script>
<scriptsrc="js/services/todo.js"></script>
<scriptsrc="js/app.js"></script>
</body>
</html>
Thehighlightedlinesofcodearetheonesthatyouneedtoaddtoindex.html.Youmayhavenoticedthatwehavenotcreatedindex.css;that’swhatwearegoingtodorightnow:
1. Openyoureditor,createanewfileinthecssfolder,andnameitindex.css.Wearegoingtocreatetheinitialstateoftodosanddefinehowtheanimationwilllook.Ourgoalistomakeourappslideinfromtheleftoninitialization.ClickingonEdit/DetailsorNewshouldshiftittowardstheright.ByclickingonCancel,thescreenshouldshiftleftwards.Ourindex.cssfileshouldlooklikethefollowing:
.todos{
position:absolute;
background:coral;
display:block;
width:90%;
border-left:1pxsolidblack;
}
.todos.ng-enter,.todos.ng-leave{
transition:500msease-inall;
}
.todos.ng-enter.ng-enter-active,.todos.ng-leave{
left:0;
}
.todos.ng-leave.ng-leave-active,.todos.ng-enter{
left:200%;
}
The.todosclasssimplydefinestheinitialstateofthetodosclass.Next,wehave.todos.ng-enterand.todos.ng-leave,wherewedefineatransitiontimeof500msandanease-ineffect.
2. Nextcomesthefunpart..todos.ng-enter.ng-enter-active,.todos.ng-leaveisdefinedasleft:0,whichmeansthatonenteringanewroute,thepartialinquestionwillshiftinfromrighttolefttillthedistancefromtheleftsideoftheparentdivis0px.
3. Similarly,onleaving,itisdefinedby.todos.ng-leave.ng-leave-active,.todos.ng-enteratleft:200%.
Sowhatarewedoinghere?Ifyounoticed,wefirstdefined<divng-view>withclass="todos",andthenweattachedAngularJSspecificanimationsbyusingng-enter,ng-leave,ng-enter-active,andng-leave-active.Thesearethefourbasicstatesthatyoucandefineforananimation.
4. Nowsavethefileandopenindex.htmlonyourfavoritewebbrowser.PleaseremembertorunyourserverandtheMongoDBserverinorderforthisapptowork.Youshouldbegreetedwiththefollowingscreen:
TheTodoslistapp
5. ClickonNEWorEdit/Detailsandyoushouldseeanimationsshiftinginfromrightorleft,dependingonwhetheryoucanceltheactionornot.Forinstance,youcanclickonEdit/Detailsfirstandmakesurethatyouaremovingtowardstherightwithanewscreenasfollows:
Makingchangestoouritemsandshouldworkasexpectedwithanimationeffects
6. IfyouclickonCancel,youwillseeyourscreenshiftleftwards,whichisadirectionthatyouwouldexpect.
Assoonasyougettheprecedingresult,youknowthatallisworkingright.It’stimetoaddinCSSstylesthatmimictheusualmobileuserinterface.
AddingmobileCSSstylestoyourappInordertofurtherspruceupourapp,wewillbeleveragingontheCSSlibrariesofTopCoat.YoucangetTopCoatCSSlibrariesfromhttp://topcoat.io/.Wealsoneedtochangeourindex.htmlfileabitmoreinordertoleverageonthestylesprovidedbyTopCoat.
1. Forastart,replacethestylesheetwhichpointstoBootstrapCDNwiththefollowingcode:
<linkrel="stylesheet"
href="//cdnjs.cloudflare.com/ajax/libs/topcoat/0.8.0/css/topcoat-
mobile-light.css">
2. Afterthis,justbefore<divng-viewclass="todos"></div>,prependthefollowingcode:
<divclass="topcoat-navigation-bar">
<divclass="topcoat-navigation-bar__itemcenterfull">
<h1class="topcoat-navigation-bar__title">Todos</h1>
</div>
</div>
Thisistogiveauniversalheaderthatwecommonlyseeinmobileapps.
Afterthesechanges,yourindex.htmlfileshouldlooklikethis:
<!doctypehtml>
<htmlng-app="todoApp">
<head>
<linkrel="stylesheet"
href="//cdnjs.cloudflare.com/ajax/libs/topcoat/0.8.0/css/topcoat-
mobile-light.css">
<linkrel="stylesheet"href="css/index.css">
</head>
<body>
<divid="fb-root"></div>
<scriptsrc="http://connect.facebook.net/en_US/all.js"></script>
<script>
FB.init({
appId:'XXX',
xfbml:true,
version:'v1.0'
});
</script>
<divclass="topcoat-navigation-bar">
<divclass="topcoat-navigation-bar__itemcenterfull">
<h1class="topcoat-navigation-bar__title">Todos</h1>
</div>
</div>
<divng-viewclass="todos"></div>
<scriptsrc="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-
beta.7/angular.min.js"></script>
<scriptsrc="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-
beta.7/angular-resource.min.js"></script>
<scriptsrc="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-
beta.7/angular-route.min.js"></script>
<scriptsrc="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-
beta.7/angular-animate.min.js"></script>
<scriptsrc="https://ajax.googleapis.com/ajax/libs/
angularjs/1.3.0-beta.7/angular-touch.min.js"></script>
<scriptsrc="js/controllers/todo.js"></script>
<scriptsrc="js/controllers/user.js"></script>
<scriptsrc="js/services/todo.js"></script>
<scriptsrc="js/app.js"></script>
</body>
</html>
3. There’sstillonemorethingweneedtochange,andthatisthelistingoftodos.So,inpartials/list.html,weneedtochangethestylisticelementssothatitappearscorrectlywhenshownonamobiledevice.WearesimplyapplyingTopCoatCSSclasseshere:
<divclass="topcoat-list__containerscroller">
<astyle="padding-left:10px"href="#/new"><iclass="icon-plus-
sign">NEW</i></a>
<spanstyle="padding-left:10px">
{{remaining()}}of{{todos.length}}remaining
</span>
<buttonng-click="logout()"
style="position:absolute;right:10px;">Logout</button>
<ulclass="topcoat-listlist">
<ling-repeat="todointodos"class="topcoat-list__item">
<astyle="display:block;padding-left:10px"
href="#/edit/{{todo._id}}">{{todo.text}}</a>
</li>
</ul>
</div>>
4. Thenextandthefinalstepbeforewetestourapponthebrowseristoremovecertainpartsofindex.csssothatourself-definedstylesdonotgetconfusedwithTopCoatCSSstyles.
5. Inyourindex.cssfile,lookfor.todosandremovebackground:coralandborder-left:1pxsolidblack.So,yourfinalindex.cssfileshouldlooklikethis:
.todos{
position:absolute;
display:block;
width:100%;
}
.todos.ng-enter,.todos.ng-leave{
transition:500msease-inall;
}
.todos.ng-enter.ng-enter-active,.todos.ng-leave{
left:0;
}
.todos.ng-leave.ng-leave-active,.todos.ng-enter{
left:200%;
}
6. Now,openyournewindex.htmlfileinyourbrowser.Youshouldseesomethinglikethefollowingscreenshot:
Amobileappwithuniversalheader
Onceyouhavethisresult,youmightwanttoplayaroundwithittoseeifitisworkingoutasexpected.
PortingyourwebapptoPhoneGapNowweareatthefinalstep,whichisportingourapptoPhoneGap.Thestepsareverysimilartothepreviouschapters:weneedtoincludetherequiredPhoneGapandFacebookplugins.Mostimportantly,weneedtoincludethengTouchmoduleofAngularJS.Here’showourindex.htmlfileshouldlookforourPhoneGap’sversion:
<!doctypehtml>
<htmlng-app="todoApp">
<head>
<linkrel="stylesheet"
href="http://cdnjs.cloudflare.com/ajax/libs/topcoat/0.8.0/css/topcoat-
mobile-light.css">
<linkrel="stylesheet"href="css/index.css">
</head>
<body>
<divid="fb-root"></div>
<divclass="topcoat-navigation-bar">
<divclass="topcoat-navigation-bar__itemcenterfull">
<h1class="topcoat-navigation-bar__title">Todos</h1>
</div>
</div>
<divng-viewclass="todos"></div>
<!--thisisforphonegap-->
<scriptsrc="cordova.js"></script>
<!--cordovafacebookplugin-->
<scriptsrc="cdv-plugin-fb-connect.js"></script>
<!--facebookjssdk-->
<scriptsrc="facebook-js-sdk.js"></script>
<script>
document.addEventListener('deviceready',function(){
try{
alert('Deviceisready!Makesureyousetyourapp_idbelowthis
alert.');
FB.init({appId:"XXXX",nativeInterface:CDV.FB,useCachedDialogs:
false});
}catch(e){
alert(e);
}
},false);
</script>
<scriptsrc="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-
beta.7/angular.min.js"></script>
<scriptsrc="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-
beta.7/angular-resource.min.js"></script>
<scriptsrc="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-
beta.7/angular-route.min.js"></script>
<scriptsrc="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-
beta.7/angular-animate.min.js"></script>
<scriptsrc="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-
beta.7/angular-touch.min.js"></script>
<scriptsrc="js/controllers/todo.js"></script>
<scriptsrc="js/controllers/user.js"></script>
<scriptsrc="js/services/todo.js"></script>
<scriptsrc="js/app.js"></script>
</body>
</html>
It’sgenerallythefinalversionaspertheprevioussection;buttheinitializationforPhoneGapandFacebookisincludedinthisversion.Asusual,remembertouseyourownappIDforthisfile.
WealsoneedtoconvertourcontrollerstousethePhoneGapversionforFacebookAPIcallsthatwepreviouslycodedfor.Inthesourcecodeprovidedwiththisbook,youwillseethattherearetwocodeblocksforeachofthefunctions:oneforthedesktop/webversionandtheotherisforthePhoneGapversion.
AsmentionedinChapter4,AddingAuthenticationCapabilitiesUsingPhoneGapPlugins,thePhoneGapversionconnectstoFacebookusingthefacebookConnectPluginnamespaceinsteadoftheusualFBnamespace(comparedtopreviousversions).
TestingyourapponiOSTotestyourapponiOS,runthefollowingcommandsfromtherootofyourapp’sdirectory:
cordovabuildios
cordovaemulateios
YoushouldseethefollowingoutputinyouriOSemulator:
Theto-dolistapponiOS
Inmycase,Ihavesomeitemsleftinmydatabaseandhencethat’swhatIgotinmyemulator.Feelfreetoclickaround,addnewitems,editthem,orloginandout.Itshouldworkasexpected,withanimationsbuiltin.
TestingyourapponAndroidNowthatwehavetestedoniOS,it’stimetogetyourapptestedonAndroidaswell.NotethatsinceAndroidreadsthehttp://localhostaddressdifferently,youwillneedtochangehttp://localhost:8000tohttp://10.0.2.2:8000atservices/todo.jsinorderforthecodetowork.
Also,remembertochangeyourfacebookConnectPluginnamespacetotheusualFBnamespaceinorderfortheFacebookConnectplugintowork.
Oncethatisdone,youshouldissuethefollowingcommand:
cordovabuildandroid
cordovaemulateandroid
Yourappshouldbeworkingasexpected.
SummaryTosumup,wecreatedanimationsforourappandalsomadeuseofTopCoatCSSskinstogiveourappamobilelook.Therearemanyotherareasthatwecanimproveon,suchasit’sdesignorevenuseotherframeworkssuchastheIonicframework;noticethatwedidnotmakeuseofthepopularjQueryMobile,sincewewantedtomakeuseofAngularJSasmuchaspossible.Mostimportantly,bynowyoushouldseethatthecodebasesforbothiOSandAndroidarealmostthesame,withtheexceptionofchangingtheURLofourserver’slocation.Inthenextandfinalchapter,youwilllearnaboutdistributingandgettingreadytolaunchourmobileapps.
Chapter6.GettingReadytoLaunchInthisfinalchapter,wewillrunthroughsomeofthestuffthatyoushouldbedoingbeforelaunchingyourapptotheworld,whetherit’sthroughAppleAppStoreorGoogleAndroidPlayStore.
Wewillbecoveringthefollowingtopics:
TestingyourapponyourdeviceforrealHowtochangetheartworkforyourappDeployingserver.py
Wewillalsodiscussotherusefultipsbeforeyoulaunchtheappintherealworld,andwe’llstartbylaunchingserver.py.
Deployingserver.pyTodeployserver.py,youwillneedaccesstoanactualserver.ItcanberentedfromAmazonEC2,Linode,orDigitalOcean.TheoperationsystemIamusinghereisUbuntu12.04,althougholdervariantsofUbuntucanworktoo,whichinclude10.04and11.04.SincetheserverisessentiallyaTornadoapp,youwillneedtoprependsudotoeverycommandthatfollowsinthissection,ifyouarenotrunningitasroot.
1. Tostartoff,youwillneedtoputSSHintoyourserverandstartinstallingtherequiredtoolsanddependencies:
apt-getinstallpython-setuptools
easy_installpip
pipinstalltornado
2. YouwillalsoneedtoinstallMongoDB:
sudoapt-keyadv--keyserverhkp://keyserver.ubuntu.com:80--recv
7F0CEB10
echo'debhttp://downloads-distro.mongodb.org/repo/ubuntu-upstartdist
10gen'|sudotee/etc/apt/sources.list.d/mongodb.list
3. Reloadthelocalpackagedatabase:
apt-getupdate
4. InstalltheMongoDBpackages:
apt-getinstallmongodb-org
ForthelatestinstructionsoninstallingMongoDB,refertohttp://docs.mongodb.org/manual/tutorial/install-mongodb-on-ubuntu/.
5. Next,makeadirectoryforyoutohostyourapp:
mkdir/srv/www/
mkdir/srv/www/app_name
6. Now,youcansimplycreateafilecalledserver.pyunder/srv/www/app_nameandcopythecontentsofserver.py,whichwecodedintheprevioussections.However,beforewerunserver.pyweneedtoinstallonemorepackage:
pipinstalltornado-cors
7. Nowthatyouhaveinstalledourrequiredpackages,youcanruntheserverbyissuingthefollowingcommand:
pythonserver.py–host=80
8. Tocheckiftheserverisworking,gotohttp://XXX.XX.XX/todosandseeifyougetanemptylist.YourservershouldalsoshowaGETresponse.
Atthispoint,shouldyouwanttotestyourwebapporPhoneGapversionoftheapp,you
havetoupdatejs/services/todo.jssothattheURLsreflectthenewIPaddressordomainnameoftheapp.
Thereareotherwaystosetupserver.py,suchasusingnginxandsupervisor.Formoredetails,feelfreetocheckouttheguidesprovidedbyDigitalOcean(https://www.digitalocean.com/)andLinode(https://www.linode.com/)formoredetails.
Nowthatwearedonewithserver.py,it’stimetomoveontotheapps.
Usingphonegap.comTheservicesonhttps://build.phonegap.com/areastraightforwardwayforyoutogetyourappcompiledforvariousdevices.Whilethisisapaidservice,thereisafreeplanifyouonlyhaveoneappthatyouwanttoworkon.Thiswouldbefineinourcase,forthischapter.
ChooseaplanfromPhoneGap
YouwillneedtohaveanAdobeIDinordertousePhoneGapservices.Ifnot,feelfreetocreateone.SincetheprocessforgeneratingcompiledappsfromPhoneGapmaychange,it’sthatyouvisithttps://build.phonegap.com/andsignupfortheirservicesandfollowtheirinstructions.
PreparingyourPhoneGapappforanAndroidreleaseThissectiongenerallyfocusesonthingsthatarespecificfortheAndroidplatform.Thisisbynomeansacomprehensivechecklist,butsomeofthecommontasksthatyoushouldgothroughbeforereleasingyourapptotheAndroidworld.
TestingyourapponrealdevicesFormostofthisbook,wetestedourappontheAndroidemulator.Itisalwaysgoodtorunyourapponanactualhandsettoseehowtheappisworking.TorunyourPhoneGapapponarealdevice,issuethefollowingcommandafteryouplugyourhandsetintoyourcomputer:
cordovarunandroid
Youwillseethatyourappnowrunsonyourhandset.
ExportingyourapptoinstallonotherdevicesIntheprevioussectionwetalkedaboutinstallingyourapponyourdevice.WhatifyouwanttoexporttheAPKsothatyoucantesttheapponotherdevices?Here’swhatyoucando:
Asusual,buildyourappusingcordovabuildandroidAlternatively,ifyoucan,runcordovabuildrelease
ThepreviousstepwillcreateanunsignedreleaseAPKat/path_to_your_project/platforms/android/ant-build.ThisappiscalledYourAppName-release-unsigned.apk.
Now,youcansimplycopyYourAppName-release-unsigned.apkandinstallitonanyandroidbaseddeviceyouwant.
PreparingpromotionalartworkforreleaseIngeneral,youwillneedtoincludescreenshotsofyourappforuploadtoGooglePlay.Incaseyourdevicedoesnotallowyoutotakescreenshots,here’swhatyoucando:
Thefirsttechniquethatyoucanuseistosimplyrunyourappintheemulatorandtakescreenshotsoffit.Thesizeofthescreenshotmaybesubstantiallylarger,soyoucancropitusingGIMPorsomeotheronlineimageresizer.Alternatively,usethewebappversionandopenitinyourGoogleChromeBrowser.Resizeyourbrowserwindowsothatitisnarrowenoughtoresemblethewidthofmobiledevices.
BuildingyourappforreleaseTobuildyourappforrelease,youwillneedEclipseIDE.
1. TostartyourEclipseIDE,navigatetoFile|New|Project.2. Next,navigatetoExistingCode|Android|AndroidProject.3. ClickonBrowseandselecttherootdirectoryofyourapp.TheProjecttoImport
windowshouldshowplatforms/android.4. Now,selectCopyprojectsintoworkspaceifyouwantandthenclickonFinish.
SigningtheappWehavepreviouslyexportedtheapp(unsigned)sothatwecantestitondevicesotherthanthosepluggedintoourcomputer.However,toreleaseyourapptothePlayStore,youneedtosignthemwithkeys.Thestepsherearethegeneralstepsthatyouneedtofollowinordertogenerate“signed”APKappstouploadyourapptothePlayStore.
1. Right-clickontheprojectthatyouhaveimportedintheprevioussection,andthennavigatetoAndroidTools|ExportSignedApplicationPackage.YouwillseetheProjectChecksdialog.
2. IntheProjectChecksdialog,youwillseeifyourprojecthasanyerrorsornot.3. Next,youshouldseetheKeystoreselectiondialog.Youwillnowcreatethekey
usingtheappname(withoutspace)andtheextension.keystore.Sincethisappisthefirstversion,thereisnopriororiginalnametouse.Now,youcanbrowsetothelocationandsavethekeystore,andinthesamebox,givethenameofthekeystore.IntheKeystoreelectiondialog,addyourdesiredpasswordtwiceandclickonNext.YouwillnowseetheKeyCreationdialog.
4. IntheKeyCreationdialog,useapp_nameasyouralias(withoutanyspaces)andgivethepasswordofyourkeystore.Feelfreetoenter50forvalidity(whichmeansthepasswordisvalidfor50years).Theremainingfieldssuchasnames,organization,andsoonareprettystraightforward,soyoucanjustgoaheadandfillthemin.
5. Finally,selecttheDestinationAPKfile,whichisthelocationtowhichyouwillexportyour.apkfile.
Bearinmindthattheprecedingstepsarenotacomprehensivelistofinstructions.Fortheofficialdocumentation,feelfreetovisithttp://developer.android.com/tools/publishing/app-signing.html.
NowthatwearedonewithAndroid,it’stimetoprepareourappforiOS.
iOSAsyoumightalreadyknow,preparingyourPhoneGapappforAppleAppStorerequiressimilarlevels,ifnotmore,ascomparedtoyourusualAndroiddeployment.Inthissection,IwillnotbecoveringthingslikemakingsureyourappisintandemwithAppleUserInterfaceguidelines,butrather,howtoimproveyourappbeforeitreachestheAppStore.Beforewegetstarted,therearesomebasicrequirements:
AppleDeveloperMembership(ifyouultimatelywanttodeploytotheAppStore)Xcode
RunningyourapponaniOSdeviceIfyoualreadyhaveaniOSdevice,allyouneedtodoistoplugyouriOSdevicetoyourcomputerandissuethefollowingcommand:
cordovarunios
YoushouldseethatyourPhoneGapappwillbuildandlaunchonyourdevice.Notethatbeforerunningtheprecedingcommand,youwillneedtoinstalltheios-deploypackage.Youcaninstallitusingthefollowingcommand:
sudonpminstall–gios-deploy
OthertechniquesThereareotherwaystotestanddeployyourapps.Thesemethodscanbeusefulifyouwanttodeployyourapptoyourowndevicesorevenforexternaldevicetesting.
UsingXcodeNowlet’sgetstartedwithXcode:
1. Afterstartingyourprojectusingthecommand-linetoolandafteraddinginiOSplatformsupport,youmayactuallystartdevelopingusingXcode.YoucanstartyourXcodeandclickonOpenOther,asshowninthefollowingscreenshot:
2. OnceyouhaveclickedonOpenOther,youwillneedtobrowsetoyourToDoappfolder.
3. DrilldownuntilyouseeToDo.xcodeproj(navigatetoplatforms|ios).Selectandopenthisfile.
4. YouwillseeyourXcodedeviceimportingthefiles.Afterit’salldone,youshouldseesomethinglikethefollowingscreenshot:
FilesimportedintoXcode
5. NoticethatallthefilesarenowimportedtoyourXcode,andyoucanstartworkingfromhere.Youcanalsodeployyourappeithertodevicesorsimulators:
Deployonyourdeviceoronsimulators
SummaryInthischapter,wewentthroughthebasicsofpackagingyourappbeforesubmissiontotherespectiveappstores.That’sitforthisbook.Wehavecoveredquiteabitinthisbookinsixchapters.Ingeneral,youshouldhaveagoodideaofhowtodevelopAngularJSappsandapplymobileskinsonthemsothatitcanbeusedonPhoneGap.YoushouldalsonoticethatdevelopingforPhoneGapappstypicallytakesthepatternofcreatingawebappfirst,beforeconvertingittoaPhoneGapversion.Ofcourse,youmaystructureyourprojectsothatyoucanbuildaPhoneGapversionfromdayone,butitmaymaketestingmoredifficult.Anyway,Ihopethatyouenjoyedthisbookandfeelfreetofollowmeathttp://www.liangeugene.comandhttp://growthsnippets.com.
AppendixA.ReferencesThefollowingaresomecommonresourcesthatyoucanuseforreference.
AngularJSandrelatedlibrariesThemainAngularJSisavailableathttps://angularjs.org/;hereyoucanlearnaboutthebasicfunctionalitiesofAngularJSAlistofpopularadd-ons,modules,andpluginsforAngularJSisavailableathttp://ngmodules.org/CanJS,aframeworkthatmakesdevelopingcomplexapplicationssimpleandfastisavailableathttp://canjs.com/Ember.js,aframeworkthatincorporatescommonidiomssothatdeveloperscanfocusonwhatmakesyourappspecial,isavailableathttp://emberjs.com/Knockoutisaframeworkthatdeveloperscanusetobuildsinglepageapplications,custombindings,andsoitisavailableathttp://knockoutjs.com/
PhoneGapandrelatedreferencesThePhoneGapmainwebsiteisavailableathttp://phonegap.com/PhoneGapPluginsareavailableathttps://build.phonegap.com/pluginsTheApacheCordovamainwebsiteisavailableathttp://cordova.apache.org/TheAndroidDevelopersmainwebsiteisavailableathttp://developer.android.com/index.html
OthersTheiOSmainwebsiteisavailableathttps://developer.apple.com/devcenter/ios/index.actionTheFacebookloginpageisavailableathttps://developers.facebook.com/docs/facebook-login/v2.0FacebookPhoneGappluginisavailableathttps://github.com/phonegap/phonegap-facebook-pluginTheIonicframeworkisavailableathttp://ionicframework.com/
OthertutorialsInadditiontothepreviouslinks,herearesomeusefultutorialsregardingtheuseofAngularJSandPhoneGap.Thoughsomeofthemareslightlyoutdated(especiallyforPhoneGap),itisstillgenerallyusefulforyoutounderstandhowbothPhoneGapandAngularJSworktogetherbyreferringtothefollowinglinks:
HowtousePhoneGapandAngularJStogetherisavailableathttp://tech.pro/tutorial/1336/phonegap-and-angularjs-the-startSampleMobileApplicationwithAngularJSandPhoneGapisavailableathttp://coenraets.org/blog/2013/11/sample-mobile-application-with-angularjs/SampleMobileApplicationwithIonicFrameworkandPhoneGapisavailableathttp://coenraets.org/blog/2014/02/sample-mobile-application-with-ionic-and-angularjs/
IndexA
$apply()option/WritingtheusercontrollerAndroid
installing/InstallingAndroidinstalling,URL/InstallingAndroidcommand-lineinterface/Command-lineinterfaceforbothAndroidandiOScode,testingon/TestingourcodeonAndroidapp,testingon/TestingyourapponAndroid
AndroidDevelopersURL/PhoneGapandrelatedreferences
AngularJSabout/AbriefoverviewofAngularJSURL/AbriefoverviewofAngularJScoreconcepts/Coreconceptsconceptualexample/Aconceptualexampleto-dolist/Asimpleto-dolistusingAngularJSchanging,toperformRESTfulrequests/ChangingAngularJStoperformRESTfulrequests$httpmodule,using/Usingthe$httpmoduleofAngularJSreferences/AngularJSandrelatedlibrariesURL,formodules/AngularJSandrelatedlibrariesreferences,fortutorials/Othertutorials
AngularJSonPhoneGapabout/AngularJSonPhoneGapused,forcreatingtodolistapp/Creatingato-dolistappusingAngularJSonPhoneGapused,forbasicversiontodolistapp/Abasicversionofato-dolistusingAngularJSonPhoneGap
animationsadding,towebapp/Addinganimationstoyourwebapp
ApacheCordovaURL/PhoneGapandrelatedreferences
appmobileCSSstyles,adding/AddingmobileCSSstylestoyourapp
app,PhoneGaptesting,onrealdevices/Testingyourapponrealdevicesexporting,toinstallonotherdevices/Exportingyourapptoinstallonotherdevicesbuilding,forrelease/Buildingyourappforreleasesigning/Signingtheapp
Bbackend
about/Wiringupabackendserverserver,coding/Codingourservercode,checking/Checkingourcode
CCanJS
about/AngularJSandrelatedlibrariesURL/AngularJSandrelatedlibraries
codetesting,oniOS/TestingourcodeoniOStesting,onAndroid/TestingourcodeonAndroid
code,backendchecking/Checkingourcode
command-lineinterfaceforAndroid/Command-lineinterfaceforbothAndroidandiOSforiOS/Command-lineinterfaceforbothAndroidandiOSrealdevices,runningon/Runningonrealdevices
conceptualexample,AngularJS/Aconceptualexamplecontrollers
about/Controllersrewriting,touse$httpmodule/Rewritingcontrollerstomakeuseofthe$httpmodule
coreconcepts,AngularJSabout/Coreconceptscontrollers/Controllersdata-binding/Data-bindingdirectives/Directives
D$digest
URL/Writingtheusercontrollerdata-binding
about/Data-bindingDigitalOcean
URL/Deployingserver.pydirectives
about/Directivesng-app/Directivesng-bind/Directivesng-model/Directivesng-controller/Directivesng-repeat/Directives
EEmber.js
about/AngularJSandrelatedlibrariesURL/AngularJSandrelatedlibraries
ExpressURL/Wiringupabackendserver
FFacebookConnect
adding,totodolistapp/AddingFacebookConnecttotheto-dolistappURL/AddingFacebookConnecttotheto-dolistapppreparingfor/InitializingandpreparingforFacebookConnectinitializing/InitializingandpreparingforFacebookConnectusercontroller,writing/Writingtheusercontrollerloginpage,adding/Addingaloginpagelogoutfunction,adding/Addingalogoutfunctionloginstatus,checking/Checkingtheloginstatus
facebookConnectPluginFB,changingto/ChangingFBtofacebookConnectPlugin
FacebookloginforPhoneGap/FacebookloginforPhoneGapinstalling/InstallingtheFacebookplugintesting,forPhoneGap/TestingoutFacebookLoginonPhoneGap
FacebookloginpageURL/Others
FacebookPhoneGappluginURL/Others
Facebookpluginsimporting/ImportingFacebookandPhoneGapplugins
FBchanging,tofacebookConnectPlugin/ChangingFBtofacebookConnectPlugin
H$httpmodule
ofAngularJS,using/Usingthe$httpmoduleofAngularJSusing,controllersrewritingfor/Rewritingcontrollerstomakeuseofthe$httpmodule
Iindex.html
splitting,intomultiplefiles/Splittingindex.htmlintomultiplefilesIonicframework
URL/OthersiOS
installing/InstallingiOSinstalling,URL/InstallingiOScommand-lineinterface/Command-lineinterfaceforbothAndroidandiOScode,testingon/TestingourcodeoniOSapp,testingon/TestingyourapponiOSabout/iOSapp,running/RunningyourapponaniOSdeviceURL/Others
KKnockout
about/AngularJSandrelatedlibrariesURL/AngularJSandrelatedlibraries
LLinode
URL/Deployingserver.pyloginpage,FacebookConnect
writing/Addingaloginpageloginstatus,FacebookConnect
checking/Checkingtheloginstatuslogoutfunction,FacebookConnect
adding/Addingalogoutfunction
MmobileCSSstyles
adding,toapp/AddingmobileCSSstylestoyourappMongoDB
URL/Wiringupabackendserverinstalling,URL/Deployingserver.py
multiplefilesindex.html,splittinginto/Splittingindex.htmlintomultiplefilestodo.js,splittinginto/Splittingtodo.jsintomultiplefiles
Nng-appdirective/Directivesng-binddirective/Directivesng-clickdirective/HTMLforourto-dolistng-controllerdirective/Directives,HTMLforourto-dolistng-modeldirective/Directivesng-repeatdirective/Directives,HTMLforourto-dolistng-submitdirective/HTMLforourto-dolistngAnimatemodule/Addinganimationstoyourwebapp
PPhoneGap
development,preparingfor/PreparingforPhoneGapdevelopmentURL/PreparingforPhoneGapdevelopmentAndroid,installing/InstallingAndroidiOS,installing/InstallingiOSAngularJS/AngularJSonPhoneGap,Whatjusthappened?preparingfor/PreparingforPhoneGapFacebooklogin/FacebookloginforPhoneGapFacebooklogin,testingout/TestingoutFacebookLoginonPhoneGapfromWebto/FromwebtoPhoneGapwebapp,portingto/PortingyourwebapptoPhoneGapreferences/PhoneGapandrelatedreferencesreferences,fortutorials/Othertutorials
phonegap.comusing/Usingphonegap.com
PhoneGapapppreparing,forAndroidrelease/PreparingyourPhoneGapappforanAndroidreleasetesting,onrealdevices/Testingyourapponrealdevicesexporting,toinstallonotherdevices/Exportingyourapptoinstallonotherdevicespromotionalartwork,preparingforrelease/Preparingpromotionalartworkforreleasebuilding,forrelease/Buildingyourappforreleasesigning/Signingtheappsigning,URL/Signingtheapp
PhoneGappluginsimporting/ImportingFacebookandPhoneGapplugins
PhoneGapPluginsURL/PhoneGapandrelatedreferences
promotionalartwork,PhoneGappreparing,forrelease/Preparingpromotionalartworkforrelease
Rrealdevices
runningon/Runningonrealdevicesapp,testingon/Testingyourapponrealdevices
RESTfulrequestsperforming,AngularJSchanged/ChangingAngularJStoperformRESTfulrequests
S$scope.addDetailsfunction/AddinginJavaScriptwithAngularJS$scope.addTodofunction/AddinginJavaScriptwithAngularJS$scope.archivefunction/AddinginJavaScriptwithAngularJS$scope.closefunction/AddinginJavaScriptwithAngularJS$scope.getRemainingfunction/AddinginJavaScriptwithAngularJS$scope.showDetailfunction/AddinginJavaScriptwithAngularJS$scope.todoTextfunction/AddinginJavaScriptwithAngularJSserver,backend
coding/Codingourserverserver.py
deploying/Deployingserver.pysimpleto-dolistapp
rewriting/Rewritingthesimpleto-dolistappindex.html,splittingintomultiplefiles/Splittingindex.htmlintomultiplefilestodo.js,splittingintomultiplefiles/Splittingtodo.jsintomultiplefilesCheckpoint/Checkpoint
Tto-doList,AngularJS
HTML/HTMLforourto-dolistto-dolist,AngularJS
about/Asimpleto-dolistusingAngularJScodestructure,preparing/Preparingyourcodestructureadding,inJavaScript/AddinginJavaScriptwithAngularJS
todo.jssplitting,intomultiplefiles/Splittingtodo.jsintomultiplefiles
todolistappcreating,AngularJSonPhoneGapused/Creatingato-dolistappusingAngularJSonPhoneGapbasicversion,AngularJSonPhoneGapused/Abasicversionofato-dolistusingAngularJSonPhoneGapFacebookConnect,adding/AddingFacebookConnecttotheto-dolistappwithFacebookLogin/Theto-dolistappwithFacebookLoginonPhoneGapwithPhoneGap/Theto-dolistappwithFacebookLoginonPhoneGap
Todosclass/CodingourserverTopCoatCSSlibraries
URL/AddingmobileCSSstylestoyourapptornado-cor
URL/WiringupabackendserverTornadoFramework
URL/Wiringupabackendserver
Uusercontroller,FacebookConnect
writing/Writingtheusercontroller
WWeb
toPhoneGap/FromwebtoPhoneGapwebapp
animations,adding/Addinganimationstoyourwebappporting,toPhoneGap/PortingyourwebapptoPhoneGaptesting,oniOS/TestingyourapponiOStesting,onAndroid/TestingyourapponAndroid
XXcode
using/UsingXcode