本文是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 ,感兴趣的能够重温一下
群和博客请参考