AngularJS中的$resource服務相比$http服務更適合與RESTful服務進行交互。本篇后端使用ASP.NET Web API, 前端使用$resource,實現增刪改查。
本系列包括:
1、前端使用AngularJS的$resource,后端ASP.NET Web API,實現增刪改查
2、前端使用AngularJS的$resource,后端ASP.NET Web API,實現分頁、過濾
領域和上下文
首先領域先行。
public class StudentVm { [Key] public int Id { get; set; } public string Name { get; set; } public string Age { get; set; } }
上下文。
public class StudentContext : DbContext { public StudentContext() : base("conn") { Database.SetInitializer(new StudentInitializer()); } public DbSet<StudentVm> Students { get; set; } }
上下文的構造函數中,StudentIntializer類對數據進行了初始化。
public class StudentInitializer : CreateDatabaseIfNotExists<StudentContext> { protected override void Seed(StudentContext context) { IList<StudentVm> students = new List<StudentVm>(); students.Add(new StudentVm() { Name = "aa", Age = "20" }); students.Add(new StudentVm() { Name = "bb", Age = "18" }); students.Add(new StudentVm() { Name = "cc", Age = "22" }); students.Add(new StudentVm() { Name = "dd", Age = "23" }); students.Add(new StudentVm() { Name = "ee", Age = "20" }); students.Add(new StudentVm() { Name = "ff", Age = "21" }); students.Add(new StudentVm() { Name = "gg", Age = "28" }); foreach(StudentVm student in students) { context.Students.Add(student); } base.Seed(context); } }
對於EF Code First來說,Web.config中需要配置連接字符串。
<connectionStrings> <add name="conn" connectionString="Data Source=.;User=yourname;Password=yourpassword;Initial Catalog=StudentsDemo;Integrated Security=True" providerName="System.Data.SqlClient"/> </connectionStrings>
Repository
在這里使用上下文類,實現增刪改查。
public class StudentsReop { private StudentContext _db = new StudentContext(); public IEnumerable<StudentVm> Query() { return _db.Students; } public StudentVm Get(int id) { return _db.Students.SingleOrDefault(s => s.Id == id); } //更新 public void Put(int id, StudentVm student) { var stu = _db.Students.SingleOrDefault(s => s.Id == id); _db.Students.Attach(stu); _db.Entry(stu).State = System.Data.Entity.EntityState.Modified; _db.Entry(stu).CurrentValues.SetValues(student); _db.SaveChanges(); } //添加 public void Post(StudentVm student) { _db.Students.Add(student); _db.SaveChanges(); } public void Delete(int id) { var student = _db.Students.SingleOrDefault(s => s.Id.Equals(id)); _db.Students.Remove(student); bool saveFailed; do { saveFailed = false; try { _db.SaveChanges(); } catch (DbUpdateConcurrencyException ex) { saveFailed = true; //重新加載數據庫中的實體,使之處於unchanged的狀態 ex.Entries.Single().Reload(); } } while (saveFailed); } }
API控制器
public class StudentsController : ApiController { private StudentsReop _reop = new StudentsReop(); //GET api/Students public HttpResponseMessage Get() { var students = _reop.Query().ToList(); return Request.CreateResponse(HttpStatusCode.OK, students); } //GET api/Students/5 public HttpResponseMessage Get(int id) { var student = _reop.Get(id); return Request.CreateResponse(HttpStatusCode.OK, student); } //POST api/Students public void Post([FromBody]StudentVm student) { _reop.Post(student); } //PUT api/Students/5 public void Put(int id, [FromBody]StudentVm student) { _reop.Put(id, student); } //DELETE api/Students public void Delete(int id) { _reop.Delete(id); } }
允許跨域訪問
默認情況下,ASP.NET Web API是不支持跨域訪問的。為了支持,需要安裝Microsoft.AspNet.WebApi.Cors。安裝之后,需要在全局配置生效。在WepApiConfig.cs中配置如下:
public static class WebApiConfig { public static void Register(HttpConfiguration config) { // Web API 配置和服務 // Web API 路由 config.MapHttpAttributeRoutes(); config.EnableCors(new EnableCorsAttribute("*", "*", "*")); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); } }
在本地,瀏覽器中:http://localhost:49621/api/Students
前端准備
后端完成,前端在WebStorm下安裝先安裝需要的幾個插件:
npm install angular
npm install angular-route
npm install angular-resource
npm install angular-cookies
npm install alertify
再來了解下前端的文件結構:
app.js 主module,路由都在這里配置
index.html 主視圖,引用所有的css,js文件,提供讓其它部分視圖呈現的一塊區域<div ng-view></div>
.....service/ 自定義服務,$resouce的核心就封裝在這里
..........studentService.js
.....controller/
..........studentsCtrl.js 列表
..........studentUpdateCtrl.js 更新
..........studentCreateCtrl.js 添加
.....views/
..........Students.html 列表
..........StudentInfo.html 更新
..........StudentCreate.html 添加
index.html
<!DOCTYPE html> <html lang="en" ng-app="studentManagement"> <head> <meta charset="UTF-8"> <title>{{title}}</title> <link rel="stylesheet" href="node_modules/alertify/themes/alertify.core.css"/> </head> <body> <div> <p> <a href="#/">Students</a> <a href="#/Create">Create Student</a> </p> </div> <div ng-view></div> <script src="node_modules/angular/angular.min.js"></script> <script src="node_modules/angular-route/angular-route.min.js"></script> <script src="node_modules/angular-resource/angular-resource.min.js"></script> <script src="node_modules/angular-cookies/angular-cookies.min.js"></script> <script src="node_modules/alertify/lib/alertify.min.js"></script> <script src="app.js"></script> <script src="service/studentService.js"></script> <script src="controller/studentUpdateCtrl.js"></script> <script src="controller/studentsCtrl.js"></script> <script src="controller/studentCreateCtrl.js"></script> </body> </html>
以上,主視圖中,需要注意引用js文件的順序,一般angualr相關方在最上面,然后app對應js文件,最后是各種服務和控制器相關js文件。
app.js
在這里,當然首先要定義一個module,定義module的時候要把所有用到的module依賴寫在module方法的第二個實參里。還有一個主項工作就是定義設置路由,而且,如果想讓以后視同從controller中拿數據更快,我們還可以利用路由的resolve機制,把數據從某處讀取出來,先放到路由中,然后在controller中把resolve機制下的數據讀出來。
"use strict"; var studentsManagement = angular.module("studentManagement",["ngResource","ngCookies","ngRoute"]) .run(function($rootScope){ $rootScope.title = "Home"; }) .config(["$routeProvider","$locationProvider", function($routeProvider, $locationProvider){ //關於url的基本配置 //$locationProvider.html5Mode({ // enabled: true, // requireBase: false //}); //配置路由 $routeProvider.when("/", { templateUrl: "views/Students.html", controller: "studentsCtrl", resolve: { students: function($q,studentDataService){ //$q異步執行方法 var deferred = $q.defer(); studentDataService.query(function(data){ deferred.resolve(data); }); return deferred.promise; } } }).when("/Student/:id",{ templateUrl: "views/StudentInfo.html", controller: "studentUpdateCtrl", resolve: { student: function($q, studentDataService, $route){ var defered = $q.defer(); //從路由中獲取id的值 var id = $route.current.params.id; studentDataService.get({id: id}, function(data){ defered.resolve(data); }); return defered.promise; } } }).when("/Create",{ templateUrl: "views/CreateStudent.html", controller: "studentCreateCtrl" }); }]);
● 使用$routeProvider配置路由的過程就是讓一對對view和controller結婚的過程
● 顯示列表的時候通過路由的resolve機制把數據先放在了路由中
● 顯示某個Sudent的時候也通過路由的resolve機制把數據先放在了路由中
●/Student/:id這個路由格式中的id代表變量,可借助$route服務從路由中取出來var id = $route.current.params.id;
studentService.js
在這里,封裝了對API的所有請求。
而$resource服務是位於angular-resource中,大致按如下調用:
$resource(url,{paramDefaults},{actions},{options});
其中,第一個參數是必須的,其它都optional。
angular.module('studentManagement').factory("studentDataService",["$resource", function($resource){ var baseUrl = "http://localhost:49621/api/Students"; return $resource("http://localhost:49621/api/Students",{},{ query: {method: "GET", isArray: true }, create: {method: "POST"}, get: {method: "GET", url: baseUrl + "?id=:id"}, remove: {method: "DELETE", url: baseUrl + "?id=:id"}, update: {method: "PUT", url: baseUrl + "?id=:id"} }) }]);
以上,在"?id=:id"中,冒號后面的id是一個變量,在controller中通過對象傳遞到這里來,比如 studentDataService.remove({id: id}).$promise.then(...)
列表,studentsCtr.j和views/Students.html這對戀人
studentsCtr.js:
angular.module('studentManagement').controller("studentsCtrl",['$scope','$route','$rootScope','studentDataService', function($scope,$route, $rootScope, studentDataService){ $rootScope.title = "Students"; $scope.students = $route.current.locals.students;//students在路由resolve中定義 $scope.removeStudent = function(id, student){ studentDataService.remove({id: id}).$promise.then(function(){ //獲取student在當前集合中的索引 var index = $scope.students.indexOf(student); $scope.students.splice(index, 1); alertify.log(student.Name + ' is removed'); }); }; }]);
以上,students的數據並沒有向那個源頭發出請求獲取,而是直接使用$route服務,把路由resolve機制中的變量值取了出來。刪除數據實際是做2件事,一件是刪除服務端的數據,一件是刪除model中的數據。
Students.html:
<table> <thead> <tr> <th>Name</th><th>Age</th><th>Actions</th> </tr> </thead> <tbody> <tr ng-repeat="student in students"> <td>{{student.Name}}</td> <td>{{student.Age}}</td> <td> <a href="#/Student/{{student.Id}}">更新</a> <a href="javascript:void(0)" ng-click="$parent.removeStudent(student.Id, student)">移除</a> </td> </tr> </tbody> </table>
添加,studentCreateCtrl.js和views/CreateStudent.html這對戀人
studentCreateCtrl.js:
angular.module('studentManagement').controller("studentCreateCtrl", ["$scope", "studentDataService", '$rootScope', "$location", function ($scope, studentDataService, $rootScope, $location) { $rootScope.title = "Create student"; $scope.saveStudent = function (student) { studentDataService.create(student).$promise.then(function (res) { $location.path('/'); }); }; }]);
CreateStudent.html:
<form> <input id="userName" ng-model="student.Name" /> <br/> <input id="userAge" ng-model="student.Age" /> <br/> <button ng-click="saveStudent(student)">Save</button> <button type="reset">Cancel</button> </form>
更新,studentUpdateCtrl.js和views/StudentInfo.html這對戀人
studentUpdateCtrl.js:
angular.module('studentManagement').controller("studentUpdateCtrl",["$scope","$route","$rootScope","studentDataService","$location", function($scope,$route, $rootScope, studentDataService, $location){ //student是在resolve中定義的 $scope.student = $route.current.locals.student; $rootScope.title = "Student Info -" + $scope.student.Name; $scope.updateInfo = function(student){ studentDataService.update({id: student.Id}, student).$promise.then(function(){ $location.url("/"); alertify.log("Updated Student Scucess"); }); }; }]);
StudentInfo.html:
<form> <input type="text" id="userName" ng-model="student.Name"/> <br/> <input type="text" id="userAge" ng-model="student.Age"/> <br/> <button ng-click="updateInfo(student)">Update</button> <button type="reset">Reset</button> </form>
待續~~