创建People
本节介绍创建一个model,然后向电话薄中添加一条记录。
在PersonAppService中添加CreatePerson方法
我们首先在IPersonAppService接口中定义CreatePerson方法:
Task CreatePerson(CreatePersonInput input);
然后我们创建 CreatePersonInput DTO 作为这个方法的参数
[AutoMapTo(typeof(Entities.Person))]
public class CreatePersonInput
{
[Required]
[MaxLength(Entities.Person.MaxNameLength)]
public string Name { get; set; }
[Required]
[MaxLength(Entities.Person.MaxSurnameLength)]
public string Surname { get; set; }
[EmailAddress]
[MaxLength(Entities.Person.MaxEmailAddressLength)]
public string EmailAddress { get; set; }
}
CreatePersonInput和Person实体类做了映射(下面我们会用到映射)。为了自动化验证,所有的属性都加上了Data annotation的特性。注意,这里MaxLength属性我们使用的是Person实体类中定义的常量。
下面是 CreatePerson 方法的具体实现:
public async Task CreatePerson(CreatePersonInput input)
{
var person = input.MapTo<Entities.Person>();
await this._personRepository.InsertAsync(person);
}
Person 实体根据Input参数创建,然后插入到数据库。这里使用了 async/await 模式。所有ASP.NET Zero的最初创建的方法都是 async,这是建议尽可能的使用 async/await 模式。
测试CreatePerson方法
如果你对自动化测试不感兴趣可以跳过。
我们可以通过以下代码创建单元测试来测试CreatePerson方法:
[Fact]
public async Task Should_Create_Person_With_Valid_Arguments()
{
//Act
await _personAppService.CreatePerson(
new CreatePersonInput
{
Name = "John",
Surname = "Nash",
EmailAddress = "john.nash@abc.com"
});
//Assert
UsingDbContext(
context =>
{
var john = context.Persons.FirstOrDefault(p => p.EmailAddress == "john.nash@abc.com");
john.ShouldNotBe(null);
john.Name.ShouldBe("John");
});
}
测试方法也使用了 async/await 模式。我们调用CreatePerson方法后,检查参数中的Person是否在数据库中。UsingDbContext是AppTestBase的帮助方法,它用来获取DbContext并可以执行数据库操作。
由于所有必要的字段都已提供,所以这个测试会通过。下面我们尝试创建一个参数无效的单元测试:
[Fact]
public async Task Should_Not_Create_Person_With_Invalid_Arguments()
{
//Act and Assert
await Assert.ThrowsAsync<AbpValidationException>(
async () =>
{
await _personAppService.CreatePerson(
new CreatePersonInput
{
Name = "John"
});
});
}
我们没有设置必需的属性Surname,所以,它会自动的抛出 AbpValidationException 异常。另外我们不能再验证时传null给CreatePerson方法。这个单元测试是为了检查参数不完整会抛出异常。
创建Modal
我们将新建一个Bootstrap Modal来创建Person。首先,你可以按照你知道的和喜欢的方式来创建Modal,但是ASP.NET Zero提供了更为简单的方式。
从 Areas/Mpa/Views/Common/Modals/Empty文件夹下拷贝 cshtml 和 js 文件。
如下图所示对视图代码(_CreatePersonModal.cshtml)进行修改:
@using MyCompanyName.AbpZeroTemplate.Entities
@using MyCompanyName.AbpZeroTemplate.Web.Areas.Mpa.Models.Common.Modals
@Html.Partial("~/Areas/Mpa/Views/Common/Modals/_ModalHeader.cshtml", new ModalHeaderViewModel(L("CreateNewPerson")))
<div class="modal-body">
<form role="form" novalidate class="form-validation">
<div class="form-group form-md-line-input form-md-floating-label no-hint">
<input class="form-control" type="text" name="Name" required maxlength="@Person.MaxNameLength" />
<label>@L("Name")</label>
</div>
<div class="form-group form-md-line-input form-md-floating-label no-hint">
<input type="text" name="Surname" class="form-control" required maxlength="@Person.MaxSurnameLength" />
<label>@L("Surname")</label>
</div>
<div class="form-group form-md-line-input form-md-floating-label no-hint">
<input type="email" name="EmailAddress" class="form-control" maxlength="@Person.MaxEmailAddressLength" />
<label>@L("EmailAddress")</label>
</div>
</form>
</div>
@Html.Partial("~/Areas/Mpa/Views/Common/Modals/_ModalFooterWithSaveAndCancel.cshtml")
Modal的header 和 footer代码来自模板,这里我们只需要修改modal-body代码就行了。
表单包含三个输入框(name, surname和email),我们使用Person实体的maxlength常量来控制输入。
同样,如下所示修改modal js代码(_CreatePersonModal.js):
(function($) {
app.modals.CreatePersonModal = function () {
var _modalManager;
this.init = function(modalManager) {
_modalManager = modalManager;
//Initialize your modal here...
};
this.save = function () {
//Save your modal here...
};
};
})(jQuery);
这里只是将modal命名为CreatePersonModal,稍后会完善其余部分。
打开模态窗口
我们需要在人员列表页面里放置’添加’按钮,并且添加脚本代码使得点击该按钮后弹出对话框。
所以,修改index.cshtml视图header:
@using Abp.Web.Mvc.Extensions
@using MyCompanyName.AbpZeroTemplate.Web.Navigation
@model MyCompanyName.AbpZeroTemplate.Web.Areas.Mpa.Models.PhoneBook.IndexViewModel
@{
ViewBag.CurrentPageName = PageNames.App.Tenant.PhoneBook;
}
@section Scripts
{
@Html.IncludeScript("~/Areas/Mpa/Views/PhoneBook/_CreatePersonModal.js")
@Html.IncludeScript("~/Areas/Mpa/Views/PhoneBook/Index.js")
}
<div class="row margin-bottom-5">
<div class="col-xs-12">
<div class="page-head">
<div class="page-title">
<h1>
<span>@L("PhoneBook")</span>
</h1>
</div>
</div>
</div>
<div class="col-xs-6 text-right">
<button id="CreateNewPersonButton" class="btn btn-primary blue">
<i class="fa fa-plus"></i>@L("CreateNewPerson")
</button>
</div>
</div>
Index.js 修改如下:
(function () {
$(function () {
var _createPersonModal = new app.ModalManager({
viewUrl: abp.appPath + 'Mpa/PhoneBook/CreatePersonModal',
scriptUrl: abp.appPath + 'Areas/Mpa/Views/PhoneBook/_CreatePersonModal.js',
modalClass: 'CreatePersonModal'
});
$('#CreateNewPersonButton').click(function (e) {
e.preventDefault();
_createPersonModal.open();
});
});
})();
ModalManager是ASP.NET Zero的一个Modal辅助类,它接受一个viewUrl(实际上是加载视图的action),一个scriptUrl(modal的脚本文件)和一个modalClass(之前我们定义的)
ModalManager的Open方法用来加载视图和脚本(如果需要)并且打开模态窗口。
最后,我们需要创建一个action。打开PhoneBookController 并添加以下代码:
public PartialViewResult CreatePersonModal()
{
return PartialView("_CreatePersonModal");
}
现在,我们可以运行并且通过点击’添加人员’按钮来打开弹出对话框。
保存Person
最后,我们可以点击‘保存’按钮来保存人员信息。我们在 _CreatePersonModal.js 文件里实现:
(function($) {
app.modals.CreatePersonModal = function () {
var _modalManager;
var _personService = abp.services.app.person;
var _$form = null;
this.init = function (modalManager) {
_modalManager = modalManager;
_$form = _modalManager.getModal().find('form');
_$form.validate();
};
this.save = function () {
if (!_$form.valid()) {
return;
}
var person = _$form.serializeFormToObject();
_modalManager.setBusy(true);
_personService.createPerson(person).done(function () {
_modalManager.close();
location.reload();
}).always(function () {
_modalManager.setBusy(false);
});
};
};
})(jQuery);
在init方法里,引用了form并且启用了验证。
在save方法里,我没首先验证这个form。然后我们将form转换成脚本对象,再使用PersonAppService的createPerson 方法来保存人员信息。setBusy 方法自动禁用保存和取消按钮。在done方法里,我们重新加载新增的人员信息并显示在页面上(在真实应用中,我们可以使用JQuery来动态添加人员信息至页面上)。
注意:我们通过脚本直接使用 PersonAppService’的 createPerson 方法。