本文是ABP官方文檔翻譯版,翻譯基於 3.2.5 版本 轉載請註明出處:http://www.cnblogs.com/yabu007/ 謝謝javascript
官方文檔分四部分html
1、 教程文檔前端
2、ABP 框架java
3、zero 模塊git
4、其餘(中文翻譯資源)github
本篇是第一部分的第一篇。web
第一部分分三篇數據庫
1-1 手把手引進門後端
1-2 進階設計模式
1-3 雜項 (相關理論知識)
第一篇含兩個步驟。
1-1-1 ASP.NET Core & Entity Framework Core 後端(內核)含兩篇 ( 第一篇連接 第二篇連接)
1-1-2 ASP.NET MVC, Web API, EntityFramework & AngularJs 前端
如今進入正文
譯者注:本文的最新更新時間是2016年10月,文章的內容與實際最新的樣例模版已經不一樣。請讀者注意區別。
翻譯末尾我會再加一個欄目,提供推薦的 Angular 學習資料,畢竟2017年9月已經發布 Angular5 了。
土牛語錄:
使用 AngularJs , ASP.NET MVC , Web API , EntityFramework 和 ASP.NET Boileplate 建立一個 N 層的,本地化的,良好架構的單頁面 Web 應用。
樣例程序的截圖如上。
介紹
用模板建立應用
建立實體 entities
建立數據庫上下文 DbContext
建立數據庫遷移
定義倉儲 repositories
實現倉儲
建立應用服務
建立 Web API 服務
開發單頁程序 SPA
本地化
單元測試
總結
文章更改歷史
引用
版權全部
Angular 學習資料
在這篇文章中,咱們會展現如何用如下工具從底層到頂層逐步的開發一個單頁面 Web 應用程序(SPA)
ASP.NET Boilerplate [1] 是一個開源的應用程序框架,它把以上全部的框架和類庫合併到一塊兒,讓咱們能夠輕鬆的開始開發咱們的應用程序。這是一個最佳實踐的基礎框架,咱們能夠用它來開發應用程序。它自然的支持依賴注射 Dependency Injection ,領域模型設計 Domain Driven Design 和 分層架構 Layered Architecture 。樣例應用程序夜實現了驗證 Validation , 異常處理 exception handling , 本地化 localization 和 響應式設計 responsive design。
ASP.NET Boilerplate 會生成模板,模板綁定並配置好不少搭建企業級 web 應用最好的工具,這讓咱們在開始建立一個新應用程序時能夠節約不少時間。
讓咱們從 aspnetboilerplate.com/Templates 開始生成模板建立咱們的應用程序吧。
譯者注:友情提示:上面的頁面是舊版本的。新版本請參考 ASP.NET Core & Entity Framework Core 第一篇連接
在上圖中,咱們選擇 SPA (單頁面程序)AngularJs 和 EntityFramework 。依然使用 SimpleTaskSystem 做爲咱們的項目名字。點擊建立後會生成並下載咱們的解決方案。
解決方案中有5個項目。 .core 項目是領域(業務)層, Application 項目是應用層, WebApi 項目實現 Web Api 控制器, Web 項目是展示層,最後 EntityFramework 項目是 EntityFramework 數據庫的實現,是基礎設施層。
友情提示:若是你下載本文的樣例解決方案(譯者注:文章開頭的樣例下載連接),你會看到解決方案有7個項目。那是由於咱們將樣例修改成支持 NHibernate 和 Durandal 。若是你對 NHibernate 或者 Durandal 不感興趣,請忽略。
(譯者注:若是從官網下載,.Net Core 必須選擇帶 zero 模塊才能下載 Angular 版本,不選擇 zero 模塊必須使用 .Net MVC 5.X,如圖)
咱們建立一個簡單的應用服務,這個服務建立任務並把任務指派給責任人。因此,咱們須要任務 Task 和責任人 People 實體。
任務 Task 實體定義很簡單,包含任務的描述 Description , 建立時間 CreationTime ,狀態 State 。還有一個可選的責任人 Person (責任人 AssignedPerson)引用:
代碼以下
1 public class Task : Entity<long> 2 { 3 [ForeignKey("AssignedPersonId")] 4 public virtual Person AssignedPerson { get; set; } 5 6 public virtual int? AssignedPersonId { get; set; } 7 8 public virtual string Description { get; set; } 9 10 public virtual DateTime CreationTime { get; set; } 11 12 public virtual TaskState State { get; set; } 13 14 public Task() 15 { 16 CreationTime = DateTime.Now; 17 State = TaskState.Active; 18 } 19 }
責任人 Person 實體僅僅定義了責任人的名字 :
代碼以下
1 public class Person : Entity 2 { 3 public virtual string Name { get; set; } 4 }
ASP.NET Bolierplate 提供預約了 Id 屬性的 Entity 類。咱們的實體從 Entity 類繼承。任務 Task 類從 Entity<long> 繼承,因此它的 Id 類型是 long 。責任人 Person 類的 Id 類型是 int 。由於咱們沒有指定其餘類型,因此責任人類使用了默認的主鍵類型 int 。
因爲實體屬於領域層/業務層,因此咱們在 Core 項目下定義實體。
總所周知,EntityFramework 經過 DbContext 類與數據庫鏈接。咱們首先來定義 DbContext 。 ASP.NET Boilerplate 模版已經爲咱們建立了 DbContext 模板。咱們只須要把實體 Task 和 Person 的 IDbSets 加上去就能夠。這是咱們的 DbContext 類:
代碼以下
1 public class SimpleTaskSystemDbContext : AbpDbContext 2 { 3 public virtual IDbSet<Task> Tasks { get; set; } 4 5 public virtual IDbSet<Person> People { get; set; } 6 7 public SimpleTaskSystemDbContext() 8 : base("Default") 9 { 10 11 } 12 13 public SimpleTaskSystemDbContext(string nameOrConnectionString) 14 : base(nameOrConnectionString) 15 { 16 17 } 18 }
它將會使用 web.config 中的默認 Default 鏈接字符串。 它定義以下:
代碼以下
1 <add name="Default" connectionString="Server=localhost; Database=SimpleTaskSystem; Trusted_Connection=True;" providerName="System.Data.SqlClient" />
咱們使用 EntityFramework 的 代碼優先模式 Code First 遷移來建立和維護數據庫結構。 ASP.NET Boilerplate 模版支持默認遷移並使用配置 Configuration 類。
代碼以下
1 internalinternal sealed class Configuration : DbMigrationsConfiguration<SimpleTaskSystem.EntityFramework.SimpleTaskSystemDbContext> 2 { 3 public Configuration() 4 { 5 AutomaticMigrationsEnabled = false; 6 } 7 8 protected override void Seed(SimpleTaskSystem.EntityFramework.SimpleTaskSystemDbContext context) 9 { 10 context.People.AddOrUpdate( 11 p => p.Name, 12 new Person {Name = "Isaac Asimov"}, 13 new Person {Name = "Thomas More"}, 14 new Person {Name = "George Orwell"}, 15 new Person {Name = "Douglas Adams"} 16 ); 17 } 18 }
在 Seed 方法中,咱們加入4我的做爲初始數據。 而後,咱們開始建立 初始遷移 initial migration 。 咱們打開程序包管理控制檯 Package Manager Console 並輸入如下命令,如圖:
(譯者注:若是這部分有問題,請參照ASP.NET Core MVC 的第一篇,默認項目必須選 EntityFramework 項目)
輸入命令 Add-Migration "InitialCreate" 建立一個名爲 InitialCreate 的類。
代碼以下
1 public partial class InitialCreate : DbMigration 2 { 3 public override void Up() 4 { 5 CreateTable( 6 "dbo.StsPeople", 7 c => new 8 { 9 Id = c.Int(nullable: false, identity: true), 10 Name = c.String(), 11 }) 12 .PrimaryKey(t => t.Id); 13 14 CreateTable( 15 "dbo.StsTasks", 16 c => new 17 { 18 Id = c.Long(nullable: false, identity: true), 19 AssignedPersonId = c.Int(), 20 Description = c.String(), 21 CreationTime = c.DateTime(nullable: false), 22 State = c.Byte(nullable: false), 23 }) 24 .PrimaryKey(t => t.Id) 25 .ForeignKey("dbo.StsPeople", t => t.AssignedPersonId) 26 .Index(t => t.AssignedPersonId); 27 } 28 29 public override void Down() 30 { 31 DropForeignKey("dbo.StsTasks", "AssignedPersonId", "dbo.StsPeople"); 32 DropIndex("dbo.StsTasks", new[] { "AssignedPersonId" }); 33 DropTable("dbo.StsTasks"); 34 DropTable("dbo.StsPeople"); 35 } 36 }
咱們已經完成了建立數據庫所必需的類了,可是咱們還沒建立數據庫。建立數據庫必需輸入如下的命令:
代碼以下
1 PM> Update-Database
(譯者注:命令在剛纔的程序包管理控制檯裏輸入。即輸入 Add-Migration 的地方)
這個命令執行遷移,建立數據庫並填充初始數據。
當咱們修改實體類後,咱們能夠很容易的建立新的遷移類,輸入 Add-Migration 命令而後再輸入 Update-Database 命令來更新數據庫。 若是對數據庫遷移感興趣的,能夠參考 entity framework 文檔。
在領域設計模式中,倉儲是用於實現數據庫操做的指定代碼。 ASP.NETBoilerplate 定義了範型的 IRepository 接口, 它爲每一個實體建立了自動化的倉儲。 IRepository 定義了不少公用方法,好比 select ,insert ,update , delete 等等,如圖:
在咱們須要的時候咱們能夠擴展這些倉儲 。 咱們來擴展它並建立一個任務 Task 倉儲。依據接口實現分離約定,咱們首先聲明倉儲的藉口, 任務 Task 的倉儲接口以下:
代碼以下
1 public interface ITaskRepository : IRepository<Task, long> 2 { 3 List<Task> GetAllWithPeople(int? assignedPersonId, TaskState? state); 4 }
代碼拓展了 ASP.NETBoilerplate 的範型 IRepository 接口。因此, ITaskRepository 默認就包含全部這些方法的定義。它只須要編碼自定義的方法 GetAllWithPeople(...).
至於責任人 Person 倉儲就無需再建立了,由於默認的方法咱們就夠用了。 ASP.NET boilerplate 無需建立倉儲類,經過反射範型倉儲就能夠用了。咱們將在建立應用服務章節的任務應用服務 TaskAppService 類進行展現。
倉儲接口是領域層/應用層的一部分,因此咱們在 Core 項目下進行定義。
咱們來實現剛纔定義的 ITaskRepository 接口。咱們將在 EntityFramework 項目實現倉儲類。 這樣,領域層將徹底獨立於基礎設施層 EntityFramework 。
當咱們建立模版時, ASP.NET Boilerplate 在咱們的項目中自動建立了倉儲範型類 : SimpleTaskSystemRepositoryBase 。 建立這個基類是一種最佳實踐的作法,咱們能夠在之後爲咱們的倉儲類添加一些公用的方法。咱們能夠在代碼裏看到這個基類的定義。咱們實現的 TaskRepository 就從這個基類繼承。
代碼以下
1 public class TaskRepository : SimpleTaskSystemRepositoryBase<Task, long>, ITaskRepository 2 { 3 public List<Task> GetAllWithPeople(int? assignedPersonId, TaskState? state) 4 { 5 //In repository methods, we do not deal with create/dispose DB connections, DbContexes and transactions. ABP handles it. 6 7 var query = GetAll(); //GetAll() returns IQueryable<T>, so we can query over it. 8 //var query = Context.Tasks.AsQueryable(); //Alternatively, we can directly use EF's DbContext object. 9 //var query = Table.AsQueryable(); //Another alternative: We can directly use 'Table' property instead of 'Context.Tasks', they are identical. 10 11 //Add some Where conditions... 12 13 if (assignedPersonId.HasValue) 14 { 15 query = query.Where(task => task.AssignedPerson.Id == assignedPersonId.Value); 16 } 17 18 if (state.HasValue) 19 { 20 query = query.Where(task => task.State == state); 21 } 22 23 return query 24 .OrderByDescending(task => task.CreationTime) 25 .Include(task => task.AssignedPerson) //Include assigned person in a single query 26 .ToList(); 27 } 28 }
任務倉儲 TaskRepository 繼承於 SimpleTaskSystemRepositoryBase 並實現了 ITaskRepository 接口。
GetAllWithPeople 是咱們自定義的方法,該方法獲取任務,並附帶任務的責任人(預先綁定),該任務可經過設定條件進行過濾。咱們能夠在倉儲裏自由的使用數據庫和數據庫上下文 Context ( EF 的 DbContext )對象。ASP.NET Boilerplate 爲咱們管理數據庫鏈接,事務,建立和銷燬數據庫上下文 DbContext (詳情參見 documentation)
應用服務經過分層方法把展現層和領域層分開。 咱們在項目的應用程序集裏定義了應用服務。首先,咱們爲任務應用服務定義接口:
代碼以下
1 public interface ITaskAppService : IApplicationService 2 { 3 GetTasksOutput GetTasks(GetTasksInput input); 4 void UpdateTask(UpdateTaskInput input); 5 void CreateTask(CreateTaskInput input); 6 }
(譯者注:在.net core 系列裏, input 和 output 都已經替換爲 ResultDto 了。 建議官網下載最新的版本)
接口 ITaskAppService 拓展了 IApplicationService 。 ASP.NET Boilerplate 自動爲這個類提供了一些特性 (好比依賴注入 dependency injection 和 驗證 validation )。如今,讓咱們來實現 ITaskAppService 接口
代碼以下
1 public class TaskAppService : ApplicationService, ITaskAppService 2 { 3 //These members set in constructor using constructor injection. 4 5 private readonly ITaskRepository _taskRepository; 6 private readonly IRepository<Person> _personRepository; 7 8 /// <summary> 9 ///In constructor, we can get needed classes/interfaces. 10 ///They are sent here by dependency injection system automatically. 11 /// </summary> 12 public TaskAppService(ITaskRepository taskRepository, IRepository<Person> personRepository) 13 { 14 _taskRepository = taskRepository; 15 _personRepository = personRepository; 16 } 17 18 public GetTasksOutput GetTasks(GetTasksInput input) 19 { 20 //Called specific GetAllWithPeople method of task repository. 21 var tasks = _taskRepository.GetAllWithPeople(input.AssignedPersonId, input.State); 22 23 //Used AutoMapper to automatically convert List<Task> to List<TaskDto>. 24 return new GetTasksOutput 25 { 26 Tasks = Mapper.Map<List<TaskDto>>(tasks) 27 }; 28 } 29 30 public void UpdateTask(UpdateTaskInput input) 31 { 32 //We can use Logger, it's defined in ApplicationService base class. 33 Logger.Info("Updating a task for input: " + input); 34 35 //Retrieving a task entity with given id using standard Get method of repositories. 36 var task = _taskRepository.Get(input.TaskId); 37 38 //Updating changed properties of the retrieved task entity. 39 40 if (input.State.HasValue) 41 { 42 task.State = input.State.Value; 43 } 44 45 if (input.AssignedPersonId.HasValue) 46 { 47 task.AssignedPerson = _personRepository.Load(input.AssignedPersonId.Value); 48 } 49 50 //We even do not call Update method of the repository. 51 //Because an application service method is a 'unit of work' scope as default. 52 //ABP automatically saves all changes when a 'unit of work' scope ends (without any exception). 53 } 54 55 public void CreateTask(CreateTaskInput input) 56 { 57 //We can use Logger, it's defined in ApplicationService class. 58 Logger.Info("Creating a task for input: " + input); 59 60 //Creating a new Task entity with given input's properties 61 var task = new Task { Description = input.Description }; 62 63 if (input.AssignedPersonId.HasValue) 64 { 65 task.AssignedPersonId = input.AssignedPersonId.Value; 66 } 67 68 //Saving entity with standard Insert method of repositories. 69 _taskRepository.Insert(task); 70 } 71 }
任務應用服務 TaskAppService 使用倉儲來操做數據庫。它經過構造函數注入模式從構造函數得到引用。 ASP.NET Boilerplate 自然實現依賴注入,因此咱們能夠自由的使用構造函數注入和屬性注入(更多依賴注入詳情請參照 ASP.NET Boilerplate documentation 文檔)
友情提示,咱們經過 反射 IRepository<Person> 使用責任人倉儲 PersonRepository 。 ASP.NET Boilerplate 自動爲咱們的實體建立了倉儲。咱們無需建立倉儲類由於默認的 IRepository 接口的方法已經足夠咱們用了。
應用服務方法使用數據傳輸對象 Data Transfer Objects (DTOs)。 這是最佳實踐的一種。咱們強烈推薦使用這種方法。若是你在將實體暴露給展現層這個問題上有本身的處理方式的話,請按你本身的方式來,無需必定使用 DTO。
在 GetTasks 方法裏, 咱們使用 GetAllWithPeople 方法。它會返回一個 LIst<Task> , 但我須要返回給展現層的是 ListMTaskDto> 。AutoMapper 能夠幫咱們自動的將 Task 對象轉換爲 TaskDto 對象。 GetTasksInput 和 GetTasksOutput 是專門爲 GetTasks 方法定義的特殊 DTOs 。
在 CreateTask 方法裏, 咱們簡單的建立了一個新的任務並使用 IRepository 的插入方法將它插入了數據庫。
ASP.NET Boilerplate 的 ApplicationService 類有一些方法可讓咱們更容易的開發應用服務。例如,它定義了記錄日誌的 Logger 屬性。因此,因爲咱們的 TaskAppService 是從 ApplicationService 繼承的,咱們能夠直接使用 Logger 屬性。是否從這個類繼承是可選的,但必需實現 IApplicationService (友情提示,ITaskAppService 拓展了 IApplicationService ,因爲這個類實現了 ITaskAppService 也就實現了 IApplicationService )
ASP.NET Boilerplate 自動驗證應用服務方法的輸入參數。 CreateTask 方法將 CreateTaskInput 做爲輸入參數
代碼以下
1 public class CreateTaskInput 2 { 3 public int? AssignedPersonId { get; set; } 4 5 [Required] 6 public string Description { get; set; } 7 }
在這,描述 Description 被標記爲必需的 Required 。 你可使用更多的數據註釋屬性,請參考 Data Annotation attributes 。 若是你想作一些定製驗證,你能夠實現 ICustomValidate , 就像我在 UpdateTaskInput 裏實現的
代碼以下
1 public class UpdateTaskInput : ICustomValidate 2 { 3 [Range(1, long.MaxValue)] 4 public long TaskId { get; set; } 5 6 public int? AssignedPersonId { get; set; } 7 8 public TaskState? State { get; set; } 9 10 public void AddValidationErrors(List<ValidationResult> results) 11 { 12 if (AssignedPersonId == null && State == null) 13 { 14 results.Add(new ValidationResult("Both of AssignedPersonId and State can not be null in order to update a Task!", new[] { "AssignedPersonId", "State" })); 15 } 16 } 17 18 public override string ToString() 19 { 20 return string.Format("[UpdateTask > TaskId = {0}, AssignedPersonId = {1}, State = {2}]", TaskId, AssignedPersonId, State); 21 } 22 }
AddValidationErrors 方法是你編寫你本身的定製驗證代碼的地方。
友情提示,咱們不處理任何異常。 ASP.NET Boilerplate 自動處理異常,日誌並返回一個恰當的錯誤信息給客戶端。同理,在客戶端,自動的處理這些錯誤信息並展現給客戶。實際上,這對 ASP.NET MVC 和 Web API 控制器操做來講是合理的。 咱們將使用 Web API 來暴露任務管理服務 TaskAppService , 咱們無需處理異常。 細節請參考 exception handling 文檔。
咱們將咱們的應用服務暴露給遠程客戶端。這樣,咱們的 Angularjs 應用程序可使用 AJAX 輕鬆的調用這些服務方法。
ASP.NET Boilerplate 提供了自動化方法將咱們的應用服務方法暴露爲 ASP.NET Web API 。咱們使用 DynamicApiControllerBuilder
代碼以下
1 DynamicApiControllerBuilder 2 .ForAll<IApplicationService>(Assembly.GetAssembly(typeof (SimpleTaskSystemApplicationModule)), "tasksystem") 3 .Build();
在這個例子裏, ASP.NET Boilerplate 在應用層程序集裏查找全部繼承了 IApplicationService 接口的接口,而後爲每一個應用服務類建立一個 web api 控制器。這是精細控制的替代語法。咱們來看看怎麼使用 AJAX 調用這些服務。
咱們來實現一個單頁面(SPA) Web 應用來做爲咱們項目的用戶界面。 AngularJS (Google 出品)是 SPA 框架最有用的一個(也許是最好的一個)。
ASP.NET Boilerplate 提供一個模版,使咱們能夠輕鬆的開始使用 AngularJs 。 模版有能夠平滑切換的兩個頁面(主頁和關於頁)。 使用 Twitter 的 Bootstrap 做爲 HTML/CSS 框架。(因此,這是基於響應模式的。)。 固然,ASP.NET Boilerplate 的本地化系統可讓咱們自由切換英語和土耳其語 (咱們也能夠很容易的添加其餘語言或者刪除掉)
咱們首先修改模版的路由。 ASP.NET Boilerplate 模版使用 AngularUI-Router 路由器。這是 AngularJs 的標準路由器 de-facto 。 它是基於狀態的路由模式。咱們將有兩個視圖: 任務列表 task list 和新任務 new task 。 因此, 咱們須要修改 app.js 中定義的路由。
代碼以下
1 app.config([ 2 '$stateProvider', '$urlRouterProvider', 3 function ($stateProvider, $urlRouterProvider) { 4 $urlRouterProvider.otherwise('/'); 5 $stateProvider 6 .state('tasklist', { 7 url: '/', 8 templateUrl: '/App/Main/views/task/list.cshtml', 9 menu: 'TaskList' //Matches to name of 'TaskList' menu in SimpleTaskSystemNavigationProvider 10 }) 11 .state('newtask', { 12 url: '/new', 13 templateUrl: '/App/Main/views/task/new.cshtml', 14 menu: 'NewTask' //Matches to name of 'NewTask' menu in SimpleTaskSystemNavigationProvider 15 }); 16 } 17 ]);
app.js 是主要的 javascript 文件, 它配置和啓動咱們的 SPA 。友情提示, 咱們使用 cshtml 文件做爲視圖。一般,在 AngularJs 中,咱們使用 html 文件做爲視圖。 ASP.NET Boilerplate 讓咱們可使用 cshtml 文件。因此咱們可使用 razor 引擎來生成 HTML 。
ASP.NET Boilerplate 基礎架構支持在應用程序裏建立和展現菜單 menus ,並且這很簡單。你可使用 C# 定義菜單,使用 C# 和 javascript 來使用菜單。請查看 SimpleTaskSystemNavigationProvide 類的代碼,這裏咱們建立了菜單,而後請查看 header.js/header.cshtml 的代碼, 這裏咱們展現瞭如何使用 angular 來展現菜單。
首先,咱們爲 任務列表 task list 視圖建立一個 Angular 控制器 controller
代碼以下
1 (function() { 2 var app = angular.module('app'); 3 4 var controllerId = 'sts.views.task.list'; 5 app.controller(controllerId, [ 6 '$scope', 'abp.services.tasksystem.task', 7 function($scope, taskService) { 8 var vm = this; 9 10 vm.localize = abp.localization.getSource('SimpleTaskSystem'); 11 12 vm.tasks = []; 13 14 $scope.selectedTaskState = 0; 15 16 $scope.$watch('selectedTaskState', function(value) { 17 vm.refreshTasks(); 18 }); 19 20 vm.refreshTasks = function() { 21 abp.ui.setBusy( //Set whole page busy until getTasks complete 22 null, 23 taskService.getTasks({ //Call application service method directly from javascript 24 state: $scope.selectedTaskState > 0 ? $scope.selectedTaskState : null 25 }).success(function(data) { 26 vm.tasks = data.tasks; 27 }) 28 ); 29 }; 30 31 vm.changeTaskState = function(task) { 32 var newState; 33 if (task.state == 1) { 34 newState = 2; //Completed 35 } else { 36 newState = 1; //Active 37 } 38 39 taskService.updateTask({ 40 taskId: task.id, 41 state: newState 42 }).success(function() { 43 task.state = newState; 44 abp.notify.info(vm.localize('TaskUpdatedMessage')); 45 }); 46 }; 47 48 vm.getTaskCountText = function() { 49 return abp.utils.formatString(vm.localize('Xtasks'), vm.tasks.length); 50 }; 51 } 52 ]); 53 })();
咱們將控制器 controller 命名爲 ‘sts.views.task.list’ 。這麼命名是個人習慣(方便拓展或重構代碼),但你能夠簡單的命名爲 ‘ListController’ 。 AngularJs 同樣可使用依賴注入。 咱們在這反射 ‘$scope’ 和 ‘abp.services.tasksystem.task’ 。 第一個是 Angular 的 scope 變量,第二個是自動生成的 ITaskAppService (咱們在建立 Web API 服務章節建立了這個接口) 的 javascript 服務代理 。
ASP.NET Boilerplate 的基礎架構支持在服務端和客戶端都使用相同的本地化 localization 文本 (詳情請參照 localization 文檔)
vm.tasks 是即將在視圖裏展現的任務列表。 vm.refreshTasks 方法調用 taskService 獲取任務並填充到隊列裏。當 selectedTaskState 改變的時候(使用 $scope.$watch 監控)就會調用 vm.refreshTasks 方法。
就像你所看到的。調用應用服務方法是如此的容易和直接!這是 ASP.NET Boilerplate 的特性之一。它生成 Web API 層和與 Web API 層通信的 Javascript 代理層。因此,咱們調用應用服務方法就想調用 javascript 方法同樣簡單。這是與 AngularJs 的完整集成 (經過使用 Angular 的 $http 服務)。
讓咱們看下任務列表的視圖代碼。
代碼以下
1 <div class="panel panel-default" ng-controller="sts.views.task.list as vm"> 2 3 <div class="panel-heading" style="position: relative;"> 4 <div class="row"> 5 6 <!-- Title --> 7 <h3 class="panel-title col-xs-6"> 8 @L("TaskList") - <span>{{vm.getTaskCountText()}}</span> 9 </h3> 10 11 <!-- Task state combobox --> 12 <div class="col-xs-6 text-right"> 13 <select ng-model="selectedTaskState"> 14 <option value="0">@L("AllTasks")</option> 15 <option value="1">@L("ActiveTasks")</option> 16 <option value="2">@L("CompletedTasks")</option> 17 </select> 18 </div> 19 </div> 20 </div> 21 22 <!-- Task list --> 23 <ul class="list-group" ng-repeat="task in vm.tasks"> 24 <div class="list-group-item"> 25 <span class="task-state-icon glyphicon" ng-click="vm.changeTaskState(task)" ng-class="{'glyphicon-minus': task.state == 1, 'glyphicon-ok': task.state == 2}"></span> 26 <span ng-class="{'task-description-active': task.state == 1, 'task-description-completed': task.state == 2 }">{{task.description}}</span> 27 <br /> 28 <span ng-show="task.assignedPersonId > 0"> 29 <span class="task-assignedto">{{task.assignedPersonName}}</span> 30 </span> 31 <span class="task-creationtime">{{task.creationTime}}</span> 32 </div> 33 </ul> 34 35 </div>
ng-controller 特性(參見第一行)將視圖和控制器綁在了一塊兒。 @L("TaskList") 用於將 「任務列表 task list」進行本地化 (在服務端對 HTML 進行渲染)。這是 cshtml 文件能夠作到的。
ng-model 將下拉框和 javascript 變量綁定在了一塊兒。當變量改變時,下拉框就會更新。當下拉框改變了,變量也就更新了。這是 AngularJs 的雙向綁定。
ng-repeat 是 Angular 中的另外一個「指令」,用於傳送相同的 HTML 給隊列裏的每一個值。當隊列改變時(好比添加了一個項目),它會自動反射到視圖裏。這是 AngularJs 另外一個很強大的特性。
友情提示,當你添加一個 javascript 文件時(例如,爲 ‘任務列表’控制器),你應該把它加到你的頁面上。你能夠在模版裏的 Home\Index.cshtml 裏添加它。
ASP.NET Boilerplate 擁有一個靈活且強大的本地化系統。你能夠 XML 文件或資源文件做爲本地化源文件。你也能夠自定義本地化源文件。詳情請參加 documentation 文檔。在這個樣例應用程序裏,咱們使用 XML 文件(路徑是 web 應用程序的本地化文件夾裏 Localization):
代碼以下
1 <?xml version="1.0" encoding="utf-8" ?> 2 <localizationDictionary culture="en"> 3 <texts> 4 <text name="TaskSystem" value="Task System" /> 5 <text name="TaskList" value="Task List" /> 6 <text name="NewTask" value="New Task" /> 7 <text name="Xtasks" value="{0} tasks" /> 8 <text name="AllTasks" value="All tasks" /> 9 <text name="ActiveTasks" value="Active tasks" /> 10 <text name="CompletedTasks" value="Completed tasks" /> 11 <text name="TaskDescription" value="Task description" /> 12 <text name="EnterDescriptionHere" value="Task description" /> 13 <text name="AssignTo" value="Assign to" /> 14 <text name="SelectPerson" value="Select person" /> 15 <text name="CreateTheTask" value="Create the task" /> 16 <text name="TaskUpdatedMessage" value="Task has been successfully updated." /> 17 <text name="TaskCreatedMessage" value="Task {0} has been created successfully." /> 18 </texts> 19 </localizationDictionary>
ASP.NET Boilerplate 的設計是可測試的。咱們編寫了一篇文章來展現 ABP 基本項目的單元測試和集成測試。文章請參見 Unit testing in C# using xUnit, Entity Framework, Effort and ASP.NET Boilerplate
在這片文章裏,咱們展現瞭如何開發一個 N 層單頁面的 SPA 基於響應式用戶界面的 ASP.NET MVC web 應用程序。咱們使用 ASP.NET Boilerplate 做爲基礎架構,由於它是基於最佳實踐的,咱們不止很容易就能夠開發應用程序,並且至關的節約時間。更多信息請參考如下連接:
[1] ABP.NET Boilerplate 官網 : http://www.aspnetboilerplate.com
該文章和其中的任何源代碼和文件的版權均歸 The Code Project Open License (CPOL) 全部
此部分信息爲譯者學習中使用的資料,與本文及做者無關。請按需使用。
Angular 英文官網 : https://angular.io/
Angular 中文官網 :https://angular.cn/ 中文官網在大版本上與英文官網同步,小版本上會落後一些。
阿里的 Angular 組件NG-ZORRO:https://ng.ant.design/#/docs/angular/introduce 分爲 NG5 和 NG4 兩個版本
百度的 Echarts 數據可視化組件 : http://echarts.baidu.com/
書籍推薦看官方的英文或中文文檔:
推薦看 ng-book 最新版本,英文的 https://www.ng-book.com/2/
中文的只有 ng-book2 的翻譯版 《Angular 權威教程》
最後推薦看 雪狼的 《AngularJS深度剖析與最佳實踐》這本書寫的是 Angular 1.0 ,感興趣的能夠重溫一下
羣和博客請參考