原文地址:https://www.sitepoint.com/creating-crud-app-minutes-angulars-resourcejavascript
大量的單頁應用都有CRUD操做。若是你使用angularjs
建立一個CRUD操做,那麼你能夠利用$resource服務的優點。$resource
服務是創建在$http
服務之上,而且可使先後端用RESTful標準進行交互變得簡單的一個factory。因此,咱們一塊兒探索一下$resource
,而且在angular
中實現CRUD的操做。css
$resource
服務沒有打包到angularjs中。你須要下載一個獨立的文件叫angular-resource.js
而且把它引入到HTML頁面中。這個文件能夠從這裏下載:http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.16/angular-resource.min.js。html
另外,爲了使用$resource
你須要在主模塊中注入ngResource
模塊。示例:前端
angular.module('mainApp',['ngResource']); //mainApp is our main module
$resource
和 RESTful API工做。這意味着你的 URLs 應該相似於下面的模式:java
你能夠建立接口使用你選擇的服務器端語言。我使用的是Node + Express + MongoDB爲這個示例應用設計RESTful接口。建立好了這些URLs之後,你就能夠藉助於$resource
與這些URLs交互。咱們來看一下$resource
其實是怎麼工做的。node
在controller/service中使用$resource
須要先聲明依賴。接下來就像下面的示例同樣,在REST端調用$resource()
方法。調用方法以後返回一個$resource
一個可用於與REST後端交互的資源類:git
angular.module('myApp.services').factory('Entry', function($resource) { return $resource('/api/entries/:id'); // Note the full endpoint address });
方法返回的是一個資源類對象,默認包含下面5種方法:angularjs
get()
github
query()
ajax
save()
remove()
delete()
下面咱們看一下如何在controller中使用get()
,query()
和save()
方法:
angular.module('myApp.controllers',[]); angular.module('myApp.controllers').controller('ResourceController',function($scope, Entry) { var entry = Entry.get({ id: $scope.id }, function() { console.log(entry); }); // get() 返回單個entry var entries = Entry.query(function() { console.log(entries); }); //query() 返回有的entry $scope.entry = new Entry(); //實例化一個資源類 $scope.entry.data = 'some data'; Entry.save($scope.entry, function() { //data saved. do something here. }); //saves an entry. Assuming $scope.entry is the Entry object });
在上面的代碼片斷中 get()
方法發起/api/entries/:id
的 get 請求。參數:id
在被替換爲$scope.id
。注意get()
方法返回一個空對象,當實際數據從服務器返回的時候自動填充到這個空對象。get()
方法的第二個參數是回調方法,當服務器端返回數據的時候執行該回調。這是一個很是有用的技巧,由於你能夠設置get()
返回一個空對象,掛載到$scope
,綁定視圖。當真實的數據返回且填充到對象,雙向數據綁定觸發,而且更新視圖。
query()
方法發起/api/entries
(注意這裏沒有:id)的 get請求 並返回一個空數組。當服務器端返回數據時候填充到該數組。將該數組繼續掛在到$scope
的模型上,而且在視圖上使用ng-repeat
綁定。你也能夠給query()
傳遞一個回調方法,在數據從服務前端返回的時候執行。
save()
方法發起/api/entries
的 post 請求, 第一個參數是 post body。第二個參數爲回調方法,當數據保存成功後執行。你應該記得$resource()
方法返回的是一個資源類。因此,在咱們的用例中,咱們能夠調用new Entry()
實例化一個真正的對象,設置若干個屬性給它,最後將該對象保存到後端。
假設你只使用get()
和query()
在resource類上(在咱們的例子中resource類是Entry)。全部非 get 請求的方法,例如save()
和delete()
在new Entry()
實例中都能用(在這裏稱爲$resource
實例)。不一樣的是這些方法都以$
的開頭。因此這些有用的方法爲:
$save()
$delete()
$remove
例如,$save()
方法像下面同樣使用:
$scope.entry = new Entry(); //這個對象有個$save()方法 $scope.entry.$save(function() { //$scope.entry序列化爲json做爲post body 發送 });
咱們已經實踐CRUD中的了增長,查找和刪除,剩下的最後一個修改。爲了支持修改操做,咱們須要像下面這樣修改咱們的Entry
factory:
angular.module('myApp.services').factory('Entry', function($resource) { return $resource('/api/entries/:id', { id: '@_id' }, { update: { method: 'PUT' // this method issues a PUT request } }); });
$resource
的第二個 argument 明確標識須要url中:id
參數的值是什麼。這裏將其設置爲@_id
,這意味着在$resource
的實例中無論何時調用方法例如$update()
和$delete()
,:id
的都會被設置爲實例中_id
的屬性值。這個是爲 PUT 和 DELETE 請求使用的。注意第三個 argument,它容許咱們給資源類添加自定義方法。若是該方法是一個非 get 請求,在$resource
會有一個以$
的同名方法。咱們看一下如何使用$update
方法。假設咱們在controller中使用:
$scope.entry = Movie.get({ id: $scope.id }, function() { // $scope.entry 是 $scope.entry.data = 'something else'; $scope.entry.$update(function() { //updated in the backend }); });
當$update()
方法調用的時候,過程以下:
Angularjs 知道$update()
方法會觸發 URL 爲 /api/entries/:id
的 PUT 請求。
讀取$scope.entry._id
值,將此值賦給:id
而且生成 URL。
發起一個請求體爲 $scope.entity
的 PUT 請求。
一樣,若是你想刪除一個entry能夠像下面這麼作:
$scope.entry = Movie.get({ id: $scope.id }, function() { // $scope.entry 是服務器端返回來的,是一個 Entry 的實例 $scope.entry.data = 'something else'; $scope.entry.$delete(function() { //gone forever! }); });
它和 update 有着一樣的步驟,只是使用 DELETE 替換了 PUT請求類型。
咱們已經覆蓋了CRUD的全部操做,可是還有一小點。$resource
方法還有第4個可選擇的參數。這是一個自定義設置的值。目前只有一個 stripTrailingSlashes
能夠設置。它的默認值是false,這意味着它會自動刪除 URL 的最後一個 /,若是你不須要能夠像下面這麼作:
angular.module('myApp.services').factory('Entry', function($resource) { return $resource('/api/entries/:id', { id: '@_id' }, { update: { method: 'PUT' // this method issues a PUT request } }, { stripTrailingSlashes: false }); });
順便說一句,我沒有覆蓋$resource
相關的每個東西。咱們這裏介紹的是最基本的,這將幫助您很快的開始一個應用的 CRUD。若是你仔細研究$resource
,你能夠閱讀這篇文章。
爲了增強$resource
的概念咱們爲電影愛好者建立一個應用。這是一個單頁應用,用戶能夠新增一個電影,修改一個已存在的,最後還能夠刪除。咱們將使用$resource
與後端 REST API 進行交互。你能夠查看這個在線例子,咱們將它部署在這裏。
注意API容許CROS訪問(跨域資源共享),因此你能夠建立一個獨立的 Angular 應用,可使用 http://movieapp-sitepointdemo... 地址爲API。你能夠開發 Angular 應用不用擔憂沒有後端服務。
我已經使用 Node 和 Express建立了一個 RESTful 後端服務。看下面的截圖認識一下API接口:
咱們的 AngularJS 應用採用下面的目錄結構:
movieApp /css bootstrap.css app.css /js app.js controllers.js services.js /lib angular.min.js angular-resource.min.js angular-ui-router.min.js /partials _form.html movie-add.html movie-edit.html movie-view.html movies.html index.html
注意到咱們的路由使用Angular UI Router實現
像上面部分討論的同樣,咱們建立一個標準的 service 使用$resource
與後端 REST API 交互。這個服務定義在js/services.js
中。
services.js
angular.module('movieApp.services', []).factory('Movie', function($resource) { return $resource('http://movieapp-sitepointdemos.rhcloud.com/api/movies/:id', { id: '@_id' }, { update: { method: 'PUT' } }); });
這個 factory 的名字是 Movie. 咱們使用的是 MongoDB,每一個 movie 實例都有一個_id
的屬性。rest是簡單而直接的。
如今咱們已經開發好了 service,咱們接下來開發 views 和 controllers。
index.html
:建立應用的入口頁面index.html
是咱們應用的入口。咱們須要將全部依賴的 scripts 和 css所有引入到頁面。咱們使用 Bootstrap 來快速的開始佈局。下面是index.html
的內容:
<!DOCTYPE html> <html data-ng-app="movieApp"> <head lang="en"> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <base href="/"/> <title>The Movie App</title> <link rel="stylesheet" type="text/css" href="css/bootstrap.min.css"/> <link rel="stylesheet" type="text/css" href="css/app.css"/> </head> <body> <nav class="navbar navbar-default" role="navigation"> <div class="container-fluid"> <div class="navbar-header"> <a class="navbar-brand" ui-sref="movies">The Movie App</a> </div> <div class="collapse navbar-collapse"> <ul class="nav navbar-nav"> <li class="active"><a ui-sref="movies">Home</a></li> </ul> </div> </div> </nav> <div class="container"> <div class="row top-buffer"> <div class="col-xs-8 col-xs-offset-2"> <div ui-view></div> <!-- This is where our views will load --> </div> </div> </div> <script type="text/javascript" src="lib/angular.min.js"></script> <script type="text/javascript" src="js/app.js"></script> <script type="text/javascript" src="js/controllers.js"></script> <script type="text/javascript" src="js/services.js"></script> <script type="text/javascript" src="lib/angular-ui-router.min.js"></script> <script type="text/javascript" src="lib/angular-resource.min.js"></script> </body> </html>
標籤沒什麼特別須要說明的。須要注意的是<div ui-view></div>
,ui-view
指令來源於 UI Router
模塊,而且做爲咱們視圖的容器。
咱們主module和 states定義在js/app.js
中:
app.js
:
angular.module('movieApp', ['ui.router', 'ngResource', 'movieApp.controllers', 'movieApp.services']); angular.module('movieApp').config(function($stateProvider) { $stateProvider.state('movies', { // 展現全部movie路由 url: '/movies', templateUrl: 'partials/movies.html', controller: 'MovieListController' }).state('viewMovie', { //展現單個 movie 路由 url: '/movies/:id/view', templateUrl: 'partials/movie-view.html', controller: 'MovieViewController' }).state('newMovie', { //添加一個新 movie 路由 url: '/movies/new', templateUrl: 'partials/movie-add.html', controller: 'MovieCreateController' }).state('editMovie', { //修改一個movie路由 url: '/movies/:id/edit', templateUrl: 'partials/movie-edit.html', controller: 'MovieEditController' }); }).run(function($state) { $state.go('movies'); //當程序啓動時候默認跳轉路由 });
因此,咱們的應用有如下4種狀態:
movies
viewMovie
newMovie
editMovie
每個state由url
,templateUrl
和controller
組成。注意到當主模塊加載的時候路由轉向 movies
去展現咱們系統中全部的movies。下面的截圖看一看出每一個路由對應的url是什麼。
全部的模板都在partials
目錄下,咱們挨個來看一下他們的內容。
_form.html
:_form.html
是一個讓用戶錄入數據的的簡單表單。注意到這個表單會被movie-add.html
和movie-edit.html
引入,由於它們都須要用戶輸入。
下面是_form.html
的內容:
<div class="form-group"> <label for="title" class="col-sm-2 control-label">Title</label> <div class="col-sm-10"> <input type="text" ng-model="movie.title" class="form-control" id="title" placeholder="Movie Title Here"/> </div> </div> <div class="form-group"> <label for="year" class="col-sm-2 control-label">Release Year</label> <div class="col-sm-10"> <input type="text" ng-model="movie.releaseYear" class="form-control" id="year" placeholder="When was the movie released?"/> </div> </div> <div class="form-group"> <label for="director" class="col-sm-2 control-label">Director</label> <div class="col-sm-10"> <input type="text" ng-model="movie.director" class="form-control" id="director" placeholder="Who directed the movie?"/> </div> </div> <div class="form-group"> <label for="plot" class="col-sm-2 control-label">Movie Genre</label> <div class="col-sm-10"> <input type="text" ng-model="movie.genre" class="form-control" id="plot" placeholder="Movie genre here"/> </div> </div> <div class="form-group"> <div class="col-sm-offset-2 col-sm-10"> <input type="submit" class="btn btn-primary" value="Save"/> </div> </div>
模板中使用ng-model
綁定不一樣的movie變量屬性到不一樣的scope的movie模型。
movie-add.html
:這個模板接收用戶輸入而且在咱們的系統中保存一個新的movie,下面是具體內容:
<form class="form-horizontal" role="form" ng-submit="addMovie()"> <div ng-include="'partials/_form.html'"></div> </form>
當表單提交的時候,會觸發scope 的 addMovie()
方法向後端發送一個建立movie的POST請求。
movie-edit.html
:這個模板用於接收用戶收入,而且修改系統在已存在的一個movie。
<form class="form-horizontal" role="form" ng-submit="updateMovie()"> <div ng-include="'partials/_form.html'"></div> </form>
這個表單一旦提交,就會觸發scope的 updateMovie()
方法像向後端發送一個 PUT 請求去修改movie信息。
movie-view.html:
:這個模板用於展現一個movie的詳細信息。內容以下:
<table class="table movietable"> <tr> <td><h3>Details for {{movie.title}}</h3></td> <td></td> </tr> <tr> <td>Movie Title</td> <td>{{movie.title}}</td> </tr> <tr> <td>Director</td> <td>{{movie.director}}</td> </tr> <tr> <td>Release Year</td> <td>{{movie.releaseYear}}</td> </tr> <tr> <td>Movie Genre</td> <td>{{movie.genre}}</td> </tr> </table> <div> <a class="btn btn-primary" ui-sref="editMovie({id:movie._id})">Edit</a> </div>
模板最後是一個編輯按鈕。點擊按鈕路由會轉向編輯狀態,而且當前編輯的movie id 會包含在$stateParams
中。
movies.html
:這個模板用於展現系統中全部的 movie。
<a ui-sref="newMovie" class="btn-primary btn-lg nodecoration">Add New Movie</a> <table class="table movietable"> <tr> <td><h3>All Movies</h3></td> <td></td> </tr> <tr ng-repeat="movie in movies"> <td>{{movie.title}}</td> <td> <a class="btn btn-primary" ui-sref="viewMovie({id:movie._id})">View</a> <a class="btn btn-danger" ng-click="deleteMovie(movie)">Delete</a> </td> </tr> </table>
經過循環展現後端返回的每一個movie對象的詳細詳細。這個也有一個添加按鈕能夠將路由轉向添加狀態,觸發它能夠轉向一個新的頁面而且添加一個新的movie。
在每一個movie後面都有兩個操做按鈕,分別是查看和刪除。查看能夠轉向movie的詳細信息頁面。刪除會將movie永久的刪除掉。
每個路由都對應一個controller。因此,咱們總共有4個controller對應4個路由。全部的controller都在js/controllers.js
中。全部的controllers都是調用咱們上面討論的Movie service服務。下面看一下controllers如何調用:
controllers.js
:
angular.module('movieApp.controllers', []).controller('MovieListController', function($scope, $state, popupService, $window, Movie) { $scope.movies = Movie.query(); //fetch all movies. Issues a GET to /api/movies $scope.deleteMovie = function(movie) { // Delete a movie. Issues a DELETE to /api/movies/:id if (popupService.showPopup('Really delete this?')) { movie.$delete(function() { $window.location.href = ''; //redirect to home }); } }; }).controller('MovieViewController', function($scope, $stateParams, Movie) { $scope.movie = Movie.get({ id: $stateParams.id }); //Get a single movie.Issues a GET to /api/movies/:id }).controller('MovieCreateController', function($scope, $state, $stateParams, Movie) { $scope.movie = new Movie(); //create new movie instance. Properties will be set via ng-model on UI $scope.addMovie = function() { //create a new movie. Issues a POST to /api/movies $scope.movie.$save(function() { $state.go('movies'); // on success go back to home i.e. movies state. }); }; }).controller('MovieEditController', function($scope, $state, $stateParams, Movie) { $scope.updateMovie = function() { //Update the edited movie. Issues a PUT to /api/movies/:id $scope.movie.$update(function() { $state.go('movies'); // on success go back to home i.e. movies state. }); }; $scope.loadMovie = function() { //Issues a GET request to /api/movies/:id to get a movie to update $scope.movie = Movie.get({ id: $stateParams.id }); }; $scope.loadMovie(); // Load a movie which can be edited on UI });
假設應用部署在localhost/movieApp
,你能夠經過http://localhost/movieApp/index.html
訪問。若是你是一個電影愛好者,你也能夠在裏面添加你喜好的電影。部署和這篇文章中用到的源碼能夠在GitHub進行下載。