[译]asp-net-core-mvc-ajax-form-requests-using-jquery-unobtrusive
开始项目
项目使用了package.json'文件,添加需要的前端package到项目中。在这我们添加了
jquery-ajax-unobstrusive`。
{
"version": "1.0.0",
"name": "asp.net",
"private": true,
"devDependencies": {
"bootstrap": "4.1.3",
"jquery": "3.3.1",
"jquery-validation": "1.17.0",
"jquery-validation-unobtrusive": "3.2.10",
"jquery-ajax-unobtrusive": "3.2.4"
}
}
bundleconfig.json
文件用来打包js文件和css文件。问了使用这个打包功能,先安装BuildBundlerMinifer
包。
js类库被打包成了两个不同的文件, vendor-min.js
和vendor-validation-min.js
。
// Vendor JS
{
"outputFileName": "wwwroot/js/vendor.min.js",
"inputFiles": [
"node_modules/jquery/dist/jquery.min.js",
"node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"
],
"minify": {
"enabled": true,
"renameLocals": true
},
"sourceMap": false
},
// Vendor Validation JS
{
"outputFileName": "wwwroot/js/vendor-validation.min.js",
"inputFiles": [
"node_modules/jquery-validation/dist/jquery.validate.min.js",
"node_modules/jquery-validation/dist/additional-methods.js",
"node_modules/jquery-validation-unobtrusive/dist/jquery.validate.unobtrusive.min.js",
"node_modules//jquery-ajax-unobtrusive/jquery.unobtrusive-ajax.min.js"
],
"minify": {
"enabled": true,
"renameLocals": true
},
"sourceMap": false
}
全局打包文件可以添加到_Layout.cshtml
文件中。
<script src="~/js/vendor.min.js" asp-append-version="true"></script>
<script src="~/js/site.min.js" asp-append-version="true"></script>
@RenderSection("scripts", required: false)
</body>
将validation打包文件添加到_ValidationScriptsPartial.cshtml
中。
<script src="~/js/vendor-validation.min.js" asp-append-version="true"></script>
然后就可以将其添加到需要的视图中去。
@section Scripts {
@await Html.PartialAsync("_ValidationScriptsPartial")
}
简单的AJAX的表单请求
通过添加一些html attribute到表单元素上可以将一个表单请求作为一个ajax请求发送。当请求结束,id为data-ajax-update
指定的div将被替换为相应结果。Html.PartialAsync
调用初始的视图。
@{
ViewData["Title"] = "Ajax Test Page";
}
<h4>Ajax Test</h4>
<form asp-action="Index" asp-controller="AjaxTest"
data-ajax="true"
data-ajax-method="POST"
data-ajax-mode="replace"
data-ajax-update="#ajaxresult" >
<div id="ajaxresult">
@await Html.PartialAsync("_partialAjaxForm")
</div>
</form>
@section Scripts {
@await Html.PartialAsync("_ValidationScriptsPartial")
}
_partialAjaxForm.cshtml
视图实现了表单的一些内容。
@model AspNetCoreBootstrap4Validation.ViewModels.AjaxValidationModel
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label for="name">Name</label>
<input type="text" class="form-control" asp-for="Name"
id="AjaxValidationModelName" aria-describedby="nameHelp"
placeholder="Enter name">
<small id="nameHelp" class="form-text text-muted">
We'll never share your name ...
</small>
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<div class="form-group">
<label for="age">Age</label>
<input type="number" class="form-control"
id="AjaxValidationModelAge" asp-for="Age" placeholder="0">
<span asp-validation-for="Age" class="text-danger"></span>
</div>
<div class="form-check ten_px_bottom">
<input type="checkbox" class="form-check-input big_checkbox"
asp-for="IsCool" id="AjaxValidationModelIsCool">
<label class="form-check-label ten_px_left" for="IsCool">IsCool</label>
<span asp-validation-for="IsCool" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
下面例子中的第一个Index
方法,仅用来相应HTTP GET请求。
第二个Index
方法接收一个POST请求,其中包括了每次都会发送的Anti-Forgery token。成功后,返回一个部分视图,并清空model state,否则validation messages不会被重置。
public class AjaxTestController : Controller
{
public IActionResult Index()
{
return View(new AjaxValidationModel());
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Index(AjaxValidationModel model)
{
if (!ModelState.IsValid)
{
return PartialView("_partialAjaxForm", model);
}
// the client could validate this, but allowed for testing server errors
if(model.Name.Length < 3)
{
ModelState.AddModelError("name", "Name should be longer than 2 chars");
return PartialView("_partialAjaxForm", model);
}
ModelState.Clear();
return PartialView("_partialAjaxForm");
}
}
复杂的ajax表单请求
这个例子中会返回一个数据集合到视图,每个数据子项都有一个表单用来更新这个子项的数据,通过checkbox的onchange事件,文本框的oninput事件来触发请求。
因为是一个数据集合,因此每个子项所用的div元素都必须有一个唯一的id。可以通过为每一个子项生成一个GUID来实现,用它作为data-ajax-update
的一部分。
@using AspNetCoreBootstrap4Validation.ViewModels
@model AjaxValidationListModel
@{
ViewData["Title"] = "Ajax Test Page";
}
<h4>Ajax Test</h4>
@foreach (var item in Model.Items)
{
string guid = Guid.NewGuid().ToString();
<form asp-action="Index" asp-controller="AjaxComplexList"
data-ajax="true"
data-ajax-method="POST"
data-ajax-mode="replace"
data-ajax-update="#complex-ajax-@guid">
<div id="complex-ajax-@guid">
@await Html.PartialAsync("_partialComplexAjaxForm", item)
</div>
</form>
}
@section Scripts {
@await Html.PartialAsync("_ValidationScriptsPartial")
}
@model AspNetCoreBootstrap4Validation.ViewModels.AjaxValidationModel
@{
string guid = Guid.NewGuid().ToString();
}
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label for="name">Name</label>
<input type="text" class="form-control" asp-for="Name"
id="AjaxValidationModelName" aria-describedby="nameHelp" placeholder="Enter name"
oninput="$('#submit-@guid').trigger('submit');">
<small id="nameHelp" class="form-text text-muted">We'll never share your name ...</small>
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<div class="form-group">
<label for="age">Age</label>
<input type="number" asp-for="Age"
class="form-control" id="AjaxValidationModelAge" placeholder="0"
oninput="$('#submit-@guid').trigger('submit');">
<span asp-validation-for="Age" class="text-danger"></span>
</div>
<div class="form-check ten_px_bottom">
@Html.CheckBox("IsCool", Model.IsCool,
new { onchange = "$('#submit-" + @guid + "').trigger('submit');", @class = "big_checkbox" })
<label class="form-check-label ten_px_left" >Check the checkbox to send a request</label>
</div>
<button style="display: none" id="submit-@guid" type="submit">Submit</button>
控制器的代码和之前的一样。
using AspNetCoreBootstrap4Validation.ViewModels;
namespace AspNetCoreBootstrap4Validation.Controllers
{
public class AjaxComplexListController : Controller
{
public IActionResult Index()
{
return View(new AjaxValidationListModel {
Items = new List<AjaxValidationModel> {
new AjaxValidationModel(),
new AjaxValidationModel()
}
});
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Index(AjaxValidationModel model)
{
if (!ModelState.IsValid)
{
return PartialView("_partialComplexAjaxForm", model);
}
// the client could validate this, but allowed for testing server errors
if(model.Name.Length < 3)
{
ModelState.AddModelError("name", "Name should be longer than 2 chars");
return PartialView("_partialComplexAjaxForm", model);
}
ModelState.Clear();
return PartialView("_partialComplexAjaxForm", model);
}
}
}