table of contents - add docshare01.docshare.tips to...
TRANSCRIPT
1. GettingStarted2. Overview3. Introduction4. Preparation5. CreateWebService6. CreateWebClient7. Customization8. Conclusion
TableofContents
HowToCreateSinglePageApplicationinminutes!
2
ThisisademoandatutorialshowinghowtodevelopanapplicationusingYii2.0forcreatingRESTAPIandthenusingitfromUIbuiltwithAngularJS.
Tutorialisavailablehere.Bookisavailablehere
Inordertoinstalldemoclonethisrepository:
gitclonehttps://github.com/hscstudio/angular1-yii2angular1-yii2
cdangular1-yii2
Thenimportdatabaseangular_spa.sql.
Afterit'sdonerunthefollowing:
cdweb-service
composerupdate--prefer-dist
Setdatabaseconfiginweb-service\config\db.php.
Setuptwohostsinyourwebserver.Oneshouldpointtoweb-client,aothertoweb-service/web.
ThensetserviceBasevariableinweb-client\app.jstopointtoweb-serviceURL.
Freeopensource
HowToCreateSinglePageApplicationinminutes!withAngularJs1.3andYii2.0
Introduction
Installingdemo
License
HowToCreateSinglePageApplicationinminutes!
3GettingStarted
0.1(Alpha-11/05/2015)0.2(Beta-12/05/2015)
Changelog
HowToCreateSinglePageApplicationinminutes!
4GettingStarted
IntroductionPreparationCreatingWebServiceCreatingWebClientCustomizationConclusion
ThisguideisanOpenSourceprojectsoyourcontributionisverywelcome.
Inordertogetstarted:
Clonethisrepository.CheckREADME.md.Readeverythingindocumentation.
Sendpullrequests.
Asidefromcontributingviapullrequestsyoumaysubmitissues.
OfficialYii2.0guide.Yii2.0sourcecode.ExampleofAngularJS+Yii2.0bygithubjeka.
HafidMukhlasin-ProjectLeader/IndonesianYiideveloper.AlexanderMakarov-Editorial/Yiicoreteam.
HowToCreateSinglePageApplicationinminutes!withAngularJs1.3andYii2.0
TableOfContents
Development
Reference
OurTeam
HowToCreateSinglePageApplicationinminutes!
5Overview
We'dliketothankourcontributorsforimprovingtheguide.Thankyou!
HowToCreateSinglePageApplicationinminutes!
6Overview
Fromthisguideyou'lllearnhowtocreatesinglepageapplication(SPA)inminutesusingAngularJsandYiiFramework2.0.TheapplicationwillimplementaCreateReadUpdateDelete(CRUD)dataprocessing.UserinterfaceofthisapplicationwillbeimplementedusingAngularJs.DatawillbeprovidedbyAPIcreatedusingYiiFramework2.0.
Therewillbetwoapplications:
WebClientApplication.TheoneprovidingUI.WebServiceApplication.Theonedealingwithdata.
Note:ForeasiermaintenanceitisrecommendedthatyoudevelopyourRESTfulAPIsasaseparateapplicationwhichisseparatedfrombothwebsiteandadminpartsoftheapplication.
Sinceclientandserviceareseparated,technologystackusedineachcasevaries.
ForclientapplicationweuseHTML,JS,andCSS.Atleastsomeknowledgeofallthreeismandatorytofollowthistutorial.
AngularJs1.3
AngularJsisapopularJavaScriptframework.Itdoesnotmatterifyoudonotknowtoomuchaboutityet.Itisrelativelyeasytoundestandifyou'refamiliarwithJavaScriptandYii.Ifyou'veusedjQuerybefore,forgetaboutitforawhilesinceconceptsofAngularJsaredifferent.
CSSBootstrap3
InitiallydevelopedbyTwitterteam,CSSBootstrapiswidelyusedfrontendUIframework.ItisacollectionofreadytouseJavaScriptandCSSthatallowsustodesignabeautifuluserinterfacequickly.
Introduction
Structure
TechnologyBehindtheScenes
WebClientApplication
HowToCreateSinglePageApplicationinminutes!
7Introduction
Forservicepartwe'llusePHPandMySQL.AsaPHPframeworkwe'lluseYiiFramework2.0
BackToIndex01.Introduction02.Preparation03.CreateWebService04.CreateWebClient05.Customization06.Conclusion
WebServiceApplication
HowToCreateSinglePageApplicationinminutes!
8Introduction
It'stimetostartpreparingapplications.Thefollowingareseparatestepsforclientandserviceapplications.
Createawebrootdirectory(usuallyit'sweb,htdocs,public_html,wwworalikename).Insideadd
directoriesnamedassets,controllers,modelsandviews.
Thestructurewe'vejustcreatedissimilartothestructureusedbyYii.Youcanadjustitasyoulikebutforthistutorialwe'llsticktoYiiconventioninordertomakeiteasiertounderstand.
Let'sexplainallthesedirectoriesabit:
assetscontainsAngularJsandCSSBootstraplibraries.
controllersisforAngularJscontrollers.
modelsisforserviceswhichdealwithRESTfulCRUDAPIwe'regoingtobuild.
viewsisforpartialpages.MuchlikeviewsinYii.
Downloadalibraryfromhttp://getbootstrap.com/andextractittoassetsdirectorylikethefollowing:
assets
bootstrap
js
css
font
Downloadalibraryfromhttp://angularjs.org/,andextractitintoassetsdirectorylikethefollowing:
assets
bootstrap
angular
angular.js
angular.min.js
...
InordertouseAngularJsandCSSBootstrapweneedtocreateanHTMLfilehtmlfilewhichwillusebothlibraries.Createindex.htmlandputitintoyourwebrootdirectory:
Preparations
WebClientApplication
GetCSSBootstrap
GetAngularJs
IncludeAngularJsandCSSBootstrapintoHTML
HowToCreateSinglePageApplicationinminutes!
9Preparation
<!DOCTYPEhtml>
<html>
<head>
<!--CSS-->
<linkrel="stylesheet"href="assets/twitter-bootstrap/css/bootstrap.min.css"/>
<linkrel="stylesheet"href="assets/twitter-bootstrap/css/bootstrap-theme.min.css"/>
</head>
<body>
<!--JS-->
<scriptsrc="assets/angular/angular.min.js"></script>
<scriptsrc="assets/angular/angular-route.min.js"></script>
</body>
</html>
InstallYii2.0BasicprojecttemplateasdescribedinYiiguide.ItispreferredtouseComposerlikethefollowing:
composerglobalrequire"fxp/composer-asset-plugin:1.0.0"
composercreate-project--prefer-distyiisoft/yii2-app-basicweb-service
Alternativelyyoucandoitmanuallydownloadingandextractingarchiveasdescribedatthesameguidepage.
BackToIndex01.Introduction02.Preparation03.CreateWebService04.CreateWebClient05.Customization06.Conclusion
WebServiceApplication
HowToCreateSinglePageApplicationinminutes!
10Preparation
Nowthatprepartionsdonewe'llstartwithservicepartofourapplicationusinghighperformanceYii2.0PHPframework.It'sagreatfitsinceithasbuiltinfunctionalitythatallowskickstartingwithRESTfulAPIseasily.Basicsarealreadydoneforyouensuringmanydetailstobesetupcorrectlywhilemoreadvancedthingscouldbeimplementedasneeded.
We'llusedatabasenamedangular_spasocreateit.Thenaddbooktablewithfollowingstructure:
Youcanuseangular_spa.sqlinordertoimportit.
Insertsomedatainthetable,youcancreatemoretablesbutforthesakeofsimplicity,inthistutorialwe'lluseonlybook.
Openconfig/db.phpintherootofYiiwebserviceapplication.Modifydbconfigurationtospecifyyour
MySQLsettings:
return[
'class'=>'yii\db\Connection',
'dsn'=>'mysql:host=localhost;dbname=angular_spa',
'username'=>'root',//specifyyourusernamehere
'password'=>'',//specifyyourpasswordhere
'charset'=>'utf8',
];
UseGiitogeneratemodelclassfordatabasetables(wehaveonlyonesofar).RefertoYii'sguidefordetailsonhowtodoit.
CreatingWebService
CreateDatabaseStructure
ConfigureDatabaseConnection
CreateModels
HowToCreateSinglePageApplicationinminutes!
11CreateWebService
Youshouldgetmodels/Book.phpwiththefollowingcontent:
namespaceapp\models;
useYii;
classBookextends\yii\db\ActiveRecord
{
publicstaticfunctiontableName()
{
return'book';
}
publicfunctionrules()
{
return[
[['title','author','publisher','year'],'required'],
[['id','year'],'integer'],
[['title'],'string','max'=>255],
[['description'],'string'],
[['author','publisher'],'string','max'=>50]
];
}
}
IfyouarenotfamiliarwithRESTsupportinYii2.0,youcangetandideaaboutitbyreadingcorrespondingguidesection.
There'snoabilitytogenerateRESTCRUDinYii'sGiicodegeneratorbutdoingitwithoutitiseasyandfun.
First,createBookController.phpinthecontrollersdirectory.
namespaceapp\controllers;
useyii\rest\ActiveController;
classBookControllerextendsActiveController
{
//adjustthemodelclasstomatchyourmodel
public$modelClass='app\models\Book';
publicfunctionbehaviors()
{
return
\yii\helpers\ArrayHelper::merge(parent::behaviors(),[
'corsFilter'=>[
'class'=>\yii\filters\Cors::className(),
],
]);
SetupYiiRESTfulApplication
CreateRESTController
HowToCreateSinglePageApplicationinminutes!
12CreateWebService
}
}
Thecontrollerclassextendsfromyii\rest\ActiveController.ByspecifyingmodelClassas
app\models\Book,thecontrollerknowswhichmodelcanbeusedforfetchingandmanipulatingdata.
We'readdingCORSbehaviorinordertograntaccesstothirdpartycode(AJAXcallsfromexternaldomain).
Ifyouhavemoremodels,createalikecontrollersforeachone.
Modifyapplicationconfiguration,urlManagercomponentinweb.phpintheconfigdirectory:
'urlManager'=>[
'enablePrettyUrl'=>true,
'enableStrictParsing'=>true,
'showScriptName'=>false,
'rules'=>[
['class'=>'yii\rest\UrlRule','controller'=>'book'],
],
]
TheaboveconfigurationmainlyaddsaURLruleforthebookcontrollersothatthebookdatacanbeaccessedandmanipulatedwithprettyURLsandmeaningfulHTTPverbs.Ifyouhavemorecontrollers,specifythemasarray:
'rules'=>[
['class'=>'yii\rest\UrlRule','controller'=>['book','user','employee','etc']],
],
Note:IfyouareusingApacheasawebserver,youneedtoadda.htaccessfiletoyourwebroot.Incaseofnginxyoudon'tneedtodoit.
TolettheAPIacceptinputdatainJSONformat,configuretheparserspropertyoftherequest
applicationcomponenttousetheyii\web\JsonParser:
'request'=>[
'parsers'=>[
'application/json'=>'yii\web\JsonParser',
]
]
ConfiguringURLRules
EnableJSONInput
HowToCreateSinglePageApplicationinminutes!
13CreateWebService
Info:Theaboveconfigurationisoptional.Withoutit,theAPIwouldonlyrecognizeapplication/x-
www-form-urlencodedandmultipart/form-datainputformats.
Notit'stimetocheckwhatwe'vecreatedsofarwiththatlittleeffort.WealreadyhaveRESTfulAPIforgettingandmanagingbookdata:
GET/books:listallbookspagebypage;
HEAD/books:showtheoverviewinformationofbooklisting;
POST/books:createanewbook;
GET/books/123:returnthedetailsofthebook123;
HEAD/books/123:showtheoverviewinformationofbook123;
PATCH/books/123andPUT/books/123:updatethebook123;
DELETE/books/123:deletethebook123;
OPTIONS/books:showthesupportedverbsregardingendpoint/books;
OPTIONS/books/123:showthesupportedverbsregardingendpoint/books/123.
Info:Yiiwillautomaticallypluralizecontrollernamesforuseinendpoints.Youcanconfigurethisusingtheyii\rest\UrlRule::$pluralize.
YoumayalsoaccessyourAPIviaWebbrowserbyenteringhttp://localhost/web-service/web/books.However,youmayneedsomebrowserpluginstosendspecificrequestheaders.Forexample,PostmanChromeExtension:
BackToIndex01.Introduction02.Preparation03.CreateWebService04.CreateWebClient05.Customization06.Conclusion
Summary
HowToCreateSinglePageApplicationinminutes!
14CreateWebService
Itistimetocreateawebclientor,inotherwords,theuserinterfacefortheapplicationservicewe'vecreatedpreviously.WewilluseAngularJsasJavaScriptframeworkandCSSbootstraptostyleourUI.
Entryscriptisascriptthathandlesallapplicationrequests.Inourcaseitwillbeindex.htmlfilecontainedinthewebdirectory.
Addng-appattributetohtmltag.Asanexample,we'llusespaAppasthevalue.
<!DOCTYPEhtml>
<!--defineangularapp-->
<htmlng-app="spaApp">
<head>
<!--CSS-->
Addng-controllerattributetobodytag.Controllernameisindex.
</head>
<!--defineangularcontroller-->
<bodyng-controller="index">
Addthefollowingcontentinsidebodytag:
<bodyng-controller="index">
<navclass="navbarnavbar-default">
<divclass="container">
<divclass="navbar-header">
<aclass="navbar-brand"href="#/">SinglePageApplication</a>
</div>
<ulclass="navnavbar-navnavbar-right">
<li><ahref="#/"><iclass="glyphiconglyphicon-home"></i>Home</a></li>
<li><ahref="#/site/about"><iclass="glyphiconglyphicon-tag"></i>About</a></li>
<li><ahref="#/site/contact"><iclass="glyphiconglyphicon-envelope"></i>Contact
</ul>
</div>
</nav>
<divid="main"class="container">
CreatingWebClient
SetupApplicationEntryScript
DefineAngularApp
DefineDefaultAngularController
CreateMainMenu
HowToCreateSinglePageApplicationinminutes!
15CreateWebClient
<!--angulartemplating-->
<!--thisiswherecontentwillbeinjected-->
<divng-view></div>
</div>
<footerclass="text-center">
<p>Yii2.0.3+AngularJs1.3.15</p>
</footer>
Navbarisusedformenu,mainispagecontainer.Footeris,obviously,afooter.
Importantthinghereisanidofindivwithcontainerclass:
<divid="main"class="container">
<!--angulartemplating-->
<!--thisiswherecontentwillbeinjected-->
<divng-view></div>
</div>
Dynamiccontentfromotherfilesorpageviewswillbeplacedinto<divng-view></div>.
Themainmoduleisintendedtocontrolotherscriptssuchassubmodule.Wenameitapp.jsandplaceitintothewebrootofthewebclient:
'usestrict';
//adjusttotheyoururlofwebservice
varserviceBase='http://127.0.0.1/web-service/web/'
//declareapplevelmodulewhichdependsonviews,andcomponents
varspaApp=angular.module('spaApp',[
'ngRoute',
'spaApp.site',
]);
//submoduledeclaration
varspaApp_site=angular.module('spaApp.site',['ngRoute'])
spaApp.config(['$routeProvider',function($routeProvider){
//configdefaultroute
$routeProvider.otherwise({redirectTo:'/site/index'});
}]);
Defaultrouteis/site/index.ThisroutewillhandledbyspaApp.sitesubmodule.
AftercreatingspaApp.sitesubmoduleweneedtodefinewhatthatsubmoduledoes.Createafile
site.jsincontrollersdirectory.
CreateMainModuleandSubModule
CreateSubModuleDefinition
HowToCreateSinglePageApplicationinminutes!
16CreateWebClient
'usestrict';
spaApp_site.config(['$routeProvider',function($routeProvider){
$routeProvider
.when('/site/index',{
templateUrl:'views/site/index.html',
controller:'index'
})
.when('/site/about',{
templateUrl:'views/site/about.html',
controller:'about'
})
.when('/site/contact',{
templateUrl:'views/site/contact.html',
controller:'contact'
})
.otherwise({
redirectTo:'/site/index'
});
}])
.controller('index',['$scope','$http',function($scope,$http){
//createamessagetodisplayinourview
$scope.message='EveryonecomeandseehowgoodIlook!';
}])
.controller('about',['$scope','$http',function($scope,$http){
//createamessagetodisplayinourview
$scope.message='Look!Iamanaboutpage.';
}])
.controller('contact',['$scope','$http',function($scope,$http){
//createamessagetodisplayinourview
$scope.message='Contactus!JK.Thisisjustademo.';
}]);
Thisfileissubmoduletohandlesiteviews.ItisverysimilartoYii'sSiteController:
spaApp_site.config(['$routeProvider',function($routeProvider){
$routeProvider
.when('/site/index',{
templateUrl:'views/site/index.html',
controller:'index'
})
...
...
.otherwise({
redirectTo:'/site/index'
});
}])
Thisisroutingconfigurationofthissubmoduleonly.EveryroutehasmaytemplateUrland
controller.
templateUrlisanexternalHTMLfilethatisusedaspartialcontent.QuitesimilartoYiiviews.
controllerisanameofcontrollerthatpreparestemplatedatasuchasvariables.Itissimlarto
whatYiicontrollerdoes.
HowToCreateSinglePageApplicationinminutes!
17CreateWebClient
.otherwisetellstheapplicationwhattodoifnoroutematches.
.controller('index',['$scope','$http',function($scope,$http){
//createamessagetodisplayinourview
$scope.message='EveryonecomeandseehowgoodIlook!';
}])
$scopeisascopethatcanbehandledbytheangularappinthiscaseisallthetagsunderthetagwhich
ismarkedwithng-app
$scope.message,messageisvariabelinfiletemplateUrl,letsayviews/site/index.html,pointto
Aftercreatingmainmoduleapp.jsandsubmodulesite.js,wemustincludeitinanentryscriptof
appindex.html:
<scriptsrc="assets/angular/angular-animate.min.js"></script>
<!--Includethisjs-->
<scriptsrc="app.js"></script>
<scriptsrc="controllers/site.js"></script>
</body>
Createatemplatefiletobeusedbycontrollerinviewsdirectory.Thefilenamewouldbesite/index.html:
<divclass="jumbotrontext-center">
<h1>HomePage</h1>
<p>{{message}}</p>
</div>
Createsite/contact.html:
<divclass="jumbotrontext-center">
<h1>ContactPage</h1>
<p>{{message}}</p>
</div>
Createsite/about.html:
<divclass="jumbotrontext-center">
<h1>AboutPage</h1>
<p>{{message}}</p>
IncludeMainModuleandSubModule
CreateTemplateFile
HowToCreateSinglePageApplicationinminutes!
18CreateWebClient
</div>
Theseviewsaresimpleplaceholdersfornow.
http://localhost/web-client
AddglobalJavaScriptvarableserviceBasethatreferstoyourYii2.0webservice.Thenaddasub
modulecalledspaApp.book:
'usestrict';
varserviceBase='http://127.0.0.1/web-service/web/'
//Declareapplevelmodulewhichdependsonviews,andcomponents
varspaApp=angular.module('spaApp',[
'ngRoute',
'spaApp.site',
'spaApp.book',
]);
varspaApp_site=angular.module('spaApp.site',['ngRoute'])
varspaApp_book=angular.module('spaApp.book',['ngRoute']);
spaApp.config(['$routeProvider',function($routeProvider){
$routeProvider.otherwise({redirectTo:'/site/index'});
}]);
book.jswillhandleCRUDdataprovidedbyRESTserviceisisprettymuchwhatmodelsaredoingin
Yii.
'usestrict';
spaApp_book.factory("services",['$http','$location','$route',
function($http,$location,$route){
varobj={};
obj.getBooks=function(){
return$http.get(serviceBase+'books');
Testyourapplication
Modifyapp.js
Createbook.jsinmodelsdirectory
HowToCreateSinglePageApplicationinminutes!
19CreateWebClient
}
obj.createBook=function(book){
return$http.post(serviceBase+'books',book)
.then(successHandler)
.catch(errorHandler);
functionsuccessHandler(result){
$location.path('/book/index');
}
functionerrorHandler(result){
alert("Errordata")
$location.path('/book/create')
}
};
obj.getBook=function(bookID){
return$http.get(serviceBase+'books/'+bookID);
}
obj.updateBook=function(book){
return$http.put(serviceBase+'books/'+book.id,book)
.then(successHandler)
.catch(errorHandler);
functionsuccessHandler(result){
$location.path('/book/index');
}
functionerrorHandler(result){
alert("Errordata")
$location.path('/book/update/'+book.id)
}
};
obj.deleteBook=function(bookID){
return$http.delete(serviceBase+'books/'+bookID)
.then(successHandler)
.catch(errorHandler);
functionsuccessHandler(result){
$route.reload();
}
functionerrorHandler(result){
alert("Errordata")
$route.reload();
}
};
returnobj;
}]);
Therearemultiplefunctionssuchasobj.getBooks,obj.createBook,etc.whicharepassingdatato
RESTfulendpoints.Forexample,thefollowingwillgetalistofthebooksusingGET.Seethisguide.
obj.getBooks=function(){
return$http.get(serviceBase+'books');
}
CreateabookusingPOST:
obj.createBook=function(book){
return$http.post(serviceBase+'books',book)
HowToCreateSinglePageApplicationinminutes!
20CreateWebClient
UpdateabookusingPUT:
obj.updateBook=function(book){
return$http.put(serviceBase+'books/'+book.id,book)
Createbook.jsincontrollersdirectory.ItwillhandlebookviewslikeYiicontrollerdoes:
'usestrict';
spaApp_book.config(['$routeProvider',function($routeProvider){
$routeProvider
.when('/book/index',{
templateUrl:'views/book/index.html',
controller:'index'
})
.when('/book/create',{
templateUrl:'views/book/create.html',
controller:'create',
resolve:{
book:function(services,$route){
returnservices.getBooks();
}
}
})
.when('/book/update/:bookId',{
templateUrl:'views/book/update.html',
controller:'update',
resolve:{
book:function(services,$route){
varbookId=$route.current.params.bookId;
returnservices.getBook(bookId);
}
}
})
.when('/book/delete/:bookId',{
templateUrl:'views/book/index.html',
controller:'delete',
})
.otherwise({
redirectTo:'/book/index'
});
}]);
spaApp_book.controller('index',['$scope','$http','services',
function($scope,$http,services){
$scope.message='EveryonecomeandseehowgoodIlook!';
services.getBooks().then(function(data){
$scope.books=data.data;
});
$scope.deleteBook=function(bookID){
if(confirm("Areyousuretodeletebooknumber:"+bookID)==true&&bookID>0){
services.deleteBook(bookID);
CreateacontrollerforSiteSubModule
HowToCreateSinglePageApplicationinminutes!
21CreateWebClient
$route.reload();
}
};
}])
.controller('create',['$scope','$http','services','$location','book',
function($scope,$http,services,$location,book){
$scope.message='Look!Iamanaboutpage.';
$scope.createBook=function(book){
varresults=services.createBook(book);
}
}])
.controller('update',['$scope','$http','$routeParams','services','$location','book',
function($scope,$http,$routeParams,services,$location,book){
$scope.message='Contactus!JK.Thisisjustademo.';
varoriginal=book.data;
$scope.book=angular.copy(original);
$scope.isClean=function(){
returnangular.equals(original,$scope.book);
}
$scope.updateBook=function(book){
varresults=services.updateBook(book);
}
}]);
Createtemplatefilethatispointedbycontrollerinbooksubmoduleinviewsdirectory.Thenameis
book/index.html:
<div>
<h1>BOOKCRUD</h1>
<p>{{message}}</p>
<divng-show="books.length>0">
<aclass="btnbtn-primary"href="#/book/create">
<iclass="glyphiconglyphicon-plus"></i>Create
</a>
<tableclass="tabletable-stripedtable-hover">
<thead>
<th>Title</th>
<th>Author</th>
<th>Publisher</th>
<th>Year</th>
<thstyle="width:80px;">Action </th>
</thead>
<tbody>
<trng-repeat="datainbooks">
<td>{{data.title}}</td>
<td>{{data.author}}</td>
<td>{{data.publisher}}</td>
<td>{{data.year}}</td>
<td>
<aclass="btnbtn-primarybtn-xs"href="#/book/update/{{data.id}}">
<iclass="glyphiconglyphicon-pencil"></i>
</a>
<aclass="btnbtn-dangerbtn-xs"ng-click="deleteBook(data.id)">
CreateTemplateFileforBookSubModule
HowToCreateSinglePageApplicationinminutes!
22CreateWebClient
<iclass="glyphiconglyphicon-trash"></i>
</a>
</td>
</tr>
</tbody>
</table>
</div>
<divng-show="books.length==0">
Empty
</div>
</div>
Createbook/create.html:
<div>
<h1>BOOKCRUD</h1>
<p>{{message}}</p>
<formrole="form"name="myForm">
<divclass="form-group"ng-class="{error:myForm.title.$invalid}">
<label>Title</label>
<div>
<inputname="title"ng-model="book.title"type="text"class="form-control"
<spanng-show="myForm.title.$dirty&&myForm.title.$invalid"class="help-inline"
</div>
</div>
<divclass="form-group">
<label>Description</label>
<div>
<textareaname="description"ng-model="book.description"class="form-control
</div>
</div>
<divclass="form-group"ng-class="{error:myForm.author.$invalid}">
<label>Author</label>
<div>
<inputname="author"ng-model="book.author"type="text"class="form-control
<spanng-show="myForm.author.$dirty&&myForm.author.$invalid"class="help-inline"
</div>
</div>
<divclass="form-group"ng-class="{error:myForm.publisher.$invalid}">
<label>Publisher</label>
<div>
<inputname="publisher"ng-model="book.publisher"type="text"class="form-control
<spanng-show="myForm.publisher.$dirty&&myForm.publisher.$invalid"class="help-inline"
</div>
</div>
<divclass="form-group"ng-class="{error:myForm.year.$invalid}">
<label>Year</label>
<div>
<inputname="year"ng-model="book.year"type="text"class="form-control"placeholder
<spanng-show="myForm.year.$dirty&&myForm.year.$invalid"class="help-inline"
</div>
</div>
<ahref="#/book/index"class="btnbtn-default">Cancel</a>
<buttonng-click="createBook(book);"
ng-disabled="myForm.$invalid"
HowToCreateSinglePageApplicationinminutes!
23CreateWebClient
type="submit"class="btnbtn-default">Submit</button>
</form>
</div>
Createbook/update.html:
<div>
<h1>BOOKCRUD</h1>
<p>{{message}}</p>
<formrole="form"name="myForm">
<divclass="form-group"ng-class="{error:myForm.title.$invalid}">
<label>Title</label>
<div>
<inputname="title"ng-model="book.title"type="text"class="form-control"
<spanng-show="myForm.title.$dirty&&myForm.title.$invalid"class="help-inline"
</div>
</div>
<divclass="form-group">
<label>Description</label>
<div>
<textareaname="description"ng-model="book.description"class="form-control
</div>
</div>
<divclass="form-group"ng-class="{error:myForm.author.$invalid}">
<label>Author</label>
<div>
<inputname="author"ng-model="book.author"type="text"class="form-control
<spanng-show="myForm.author.$dirty&&myForm.author.$invalid"class="help-inline"
</div>
</div>
<divclass="form-group"ng-class="{error:myForm.publisher.$invalid}">
<label>Publisher</label>
<div>
<inputname="publisher"ng-model="book.publisher"type="text"class="form-control
<spanng-show="myForm.publisher.$dirty&&myForm.publisher.$invalid"class="help-inline"
</div>
</div>
<divclass="form-group"ng-class="{error:myForm.year.$invalid}">
<label>Year</label>
<div>
<inputname="year"ng-model="book.year"type="text"class="form-control"placeholder
<spanng-show="myForm.year.$dirty&&myForm.year.$invalid"class="help-inline"
</div>
</div>
<ahref="#/book/index"class="btnbtn-default">Cancel</a>
<buttonng-click="updateBook(book);"
ng-disabled="isClean()||myForm.$invalid"
type="submit"class="btnbtn-default">Submit</button>
</form>
</div>
ModifyMainMenu
HowToCreateSinglePageApplicationinminutes!
24CreateWebClient
Don'tforgettoaddlinktobookCRUD.
<li><ahref="#/book/index"><iclass="glyphiconglyphicon-book"></i>Book</a></li>
BackToIndex01.Introduction02.Preparation03.CreateWebService04.CreateWebClient05.Customization06.Conclusion
Testit
HowToCreateSinglePageApplicationinminutes!
25CreateWebClient
Thispagestillediting.You'reverywelcometocontribute.
Forinstallationassets(javascriptandcss),wecanusebower.Bowerisapackagemanagerforthe
web.It'squitewithcomposerinPHP.
Youshoulddropallfoldersandfilesinsideassetsfolderbeforeusingbower,becausebowerwilldownloadlibrariesforYoubyspecialstructure.
Beforeinstallationbower,wemustinstall:
nodeJs&npmNode.jsisaplatformbuiltonChrome'sJavaScriptruntimeforeasilybuildingfast,scalablenetworkapplications.NPMispackagemanagerforjavascriptGitafreeandopensourcedistributedversioncontrolsystemdesignedtohandleeverythingfromsmalltoverylargeprojectswithspeedandefficiency.
Bowerisacommandlineutility.Installitwithnpm.
npminstall-gbower
Createfilebower.jsoninfolderweb-client
{
"name":"Angular1-Yii2",
"version":"1.0.0",
"homepage":"https://github.com/hscstudio/angular1-yii2",
"description":"AngularJS1.3andYiiFramework2.0",
"dependencies":{
"angular":"~1.3.0",
"bootstrap":"~3.1.1",
"angular-route":"~1.3.0"
}
}
Andthencreatefile.bowerrc,thisfilecontainsconfigurationofbower,addparameterdirectorytospecifytargetfolderinstallation.
Customization
Miscellaneous
Installation
InstallationBower
Usage
HowToCreateSinglePageApplicationinminutes!
26Customization
{
"directory":"assets"
}
Incommandline,dothis
cdweb-client2
bowerinstall
Afterinstallationfinished,Youcanseefolderassetshavecontainedlibraryangular,bootstrap,etc.
Becausestructurefolderthatdownloadedbybowerisdifferent,weshouldadjustlinksjsandcssinfileindex.html.
<!--CSS-->
<linkrel="stylesheet"href="assets/bootstrap/dist/css/bootstrap.min.css"/>
<linkrel="stylesheet"href="assets/bootstrap/dist/css/bootstrap-theme.min.css"/>
<!--JS-->
<scriptsrc="assets/angular/angular.min.js"></script>
<scriptsrc="assets/angular-route/angular-route.min.js"></script>
Thereareseveraltypesofanimationtechniquesthatwecanapplyinourangularjsapplication.ButinthistutorialIwilldiscussabouttheanimationattheturnofthepage.Todothat,weneedthengAnimatemoduletoenableanimationsthroughouttheapplication.
Addmoduleangular-animateinbower.json
{
...
...
"dependencies":{
...
...
"angular-animate":"~1.3.0"
}
}
Andthendo
IncludeinIndex
EnhanceUserInterface
AngularAnimation
HowToCreateSinglePageApplicationinminutes!
27Customization
bowerupdate
Createstyle.cssfordefineanimation,forexample:
.animate.ng-leave{
}
.animate.ng-enter{
-webkit-animation:scaleUp0.5sbothease-in;
-moz-animation:scaleUp0.5sbothease-in;
animation:scaleUp0.5sbothease-in;
}
/*scaleup*/
@keyframesscaleUp{
from{opacity:0.3;transform:scale(0.8);}
}
@-moz-keyframesscaleUp{
from{opacity:0.3;-moz-transform:scale(0.8);}
}
@-webkit-keyframesscaleUp{
from{opacity:0.3;-webkit-transform:scale(0.8);}
}
ng-enter:willattachwhenenteringviewng-leave:whenattachwhenleavingview
Includecssanimationinindex.html:
<linkrel="stylesheet"href="style.css"/>
Addclassanimateinng-view
<divid="main"class="container">
<!--angulartemplating-->
<!--thisiswherecontentwillbeinjected-->
<divng-viewclass="animate"></div>
</div>
IncludemodulengAnimateinapp.js:
varspaApp=angular.module('spaApp',[
'ngRoute',
'spaApp.site',
'spaApp.book',
'ngAnimate'//addmodulengAnimate
]);
HowToCreateSinglePageApplicationinminutes!
28Customization
Forfurtherinformations,readthis:
https://docs.angularjs.org/tutorial/step_12https://docs.angularjs.org/guide/animationshttps://docs.angularjs.org/api/ngAnimate
https://oclazyload.readme.io/v1.0/docs/getting-started
Whenweaddsomemodule,wemustaddincludescriptforsubmoduleinmain.ByusemoduleocLazyLoadwecanmakelazyloadinangular,includeonlywhenneeded.
DownloadocLazyLoad.js(youcaninstallitwithbowerinstalloclazyloadornpminstalloclazyload)andaddthefiletoyourproject.Addthemoduleoc.lazyLoadtoyourapplication:
varspaApp=angular.module('spaApp',[
'ngRoute',
'ngAnimate',
'spaApp.site',
'spaApp.book',
'oc.lazyLoad',//addthismodulelazyLoader
]);
Loadondemand:
spaApp.controller("MyCtrl",function($ocLazyLoad){
$ocLazyLoad.load('testModule.js');
});
With$ocLazyLoadyoucanloadangularmodules,butifyouwanttoloadanycomponent(controllers/services/filters/...)withoutdefininganewmoduleit'sentirelypossible(justmakesurethatyoudefinethiscomponentwithinanexistingmodule).
Therearemultiplewaystouse$ocLazyLoadtoloadyourfiles,justchoosetheonethatyouprefer.
Alsodon'tforgetthatifyouwanttogetstartedandthedocsarenotenough,seetheexamplesinthe'examples'folder!
FlashMessage
AngularLazyLoader
CustomizeRESTfulAPI
Versioning
CustomizeURL
HowToCreateSinglePageApplicationinminutes!
29Customization
Beforewedeployourapplication,weneeddosomethings.
Readofficialguideforapplicationproductionclickhere.
BydefaultAngularJSattachesinformationaboutbindingandscopestoDOMnodes,andaddsCSSclassestodata-boundelements,butwecandisablethisinproductionforasignificantperformanceboostwith:
spaApp.config(['$compileProvider',function($compileProvider){
$compileProvider.debugInfoEnabled(false);
}]);
Usingstrictdimodeinyourproductionapplicationwillthrowerrorswhenainjectablefunctionisnotannotatedexplicitly.Strictdimodeisintendedtohelpyoumakesurethatyourcodewillworkwhenminified.However,italsowillforceyoutomakesurethatyourinjectablefunctionsareexplicitlyannotatedwhichwillimproveangular'sperformancewheninjectingdependenciesinyourinjectablefunctionsbecauseitdoesn'thavetodynamicallydiscoverafunction'sdependencies.Itisrecommendedtoautomatetheexplicitannotationviaatoollikeng-annotatewhenyoudeploytoproduction(andenablestrictdimode)
Toenablestrictdimode,youhavetwooptions:
<divng-app="spaApp"ng-strict-di>
<!--yourapphere-->
</div>
or
angular.bootstrap(document,['spaApp'],{
strictDi:true
});
ErrorHandling
Authorization
HandlingFileUpload
DeployingApplication
WebClient
DisablingDebugData
StrictDIMode
HowToCreateSinglePageApplicationinminutes!
30Customization
SamewithAngularJs,inproductionenvironments,weshoulddisabledebugmode.Itmayhaveasignificantandadverseperformanceeffect,besidesthatthedebugmodemayexposesensitiveinformationtoendusers.
Modifyfileweb/index.phpinweb-service,setYii_DEBUGconsttobefalse,andthenYII_ENVtobe
prod.
defined('YII_DEBUG')ordefine('YII_DEBUG',false);
defined('YII_ENV')ordefine('YII_ENV','prod');
BackToIndex01.Introduction02.Preparation03.CreateWebService04.CreateWebClient05.Customization06.Conclusion
WebService
HowToCreateSinglePageApplicationinminutes!
31Customization
Yii2.0makesitmucheasierandfastertoimplementawebserviceaswellasteachingushowtodoitproperly.TheAPIcreatedcouldbeusedeasilyfromAngularJsoranyotherclient.Overall,AngularJSandYii2.0areagoodmatch.
Happycoding!
Jakarta,IndonesiaMay,122015
HafidMukhlasin
BackToIndex01.Introduction02.Preparation03.CreateWebService04.CreateWebClient05.Customization06.Conclusion
Conclusion
http://www.hafidmukhlasin.com
HowToCreateSinglePageApplicationinminutes!
32Conclusion