在ASP.NET MVC中使用Razor語法可以在視圖中方便地展示數(shù)組,如果要進(jìn)行數(shù)組模型綁定,會遇到索引斷裂問題,如下示例:
input type="text" name="[0].Name" />
input type="text" name="[1].Name" />
input type="text" name="[2].Name" />
input type="text" name="[4].Name" />
input type="text" name="[5].Name" />
數(shù)組Name在索引3處斷裂,在模型綁定器解析完成后,會丟棄后面的4和5,只有0、1、2會被正確解析到對應(yīng)模型中。
這種斷裂在進(jìn)行動態(tài)數(shù)組綁定時會經(jīng)常發(fā)生。
下面,以一個案例來探討如何進(jìn)行動態(tài)數(shù)組綁定。假設(shè)有以下應(yīng)用場景:
要求能夠動態(tài)地添加和刪除乘機(jī)人,最終提交表單后乘機(jī)人信息要填充到視圖模型中的一個數(shù)組或集合屬性中,以方便我們進(jìn)行后續(xù)業(yè)務(wù)處理。
方式一:使用占位符替換
第一種方式我稱之為”占位符替換“,使用的是ASP.NET MVC默認(rèn)的模型綁定器(DefaultModelBinder)并結(jié)合前端處理。
首先,第一步,根據(jù)業(yè)務(wù)場景設(shè)計視圖模型:
public class OrderModel
{
/// summary>
/// 航班號
/// /summary>
public string FlightNo { get; set; }
/// summary>
/// 乘機(jī)人
/// /summary>
public ListPassenger> Passengers { get; set; }
}
public class Passenger
{
public string Name { get; set; }
public string IdNo { get; set; }
}
其次,將此視圖模型傳遞給視圖:
public ActionResult New()
{
Models.OrderModel orderModel = new Models.OrderModel();
ListModels.Passenger> passenger = new ListModels.Passenger>();
passenger.Add(new Models.Passenger());
orderModel.Passengers = passenger;
return View(orderModel);
}
再在視圖文件中進(jìn)行展示:
div style="width:680px">
div class="form-group">
label>航班/label>br/>
@Html.TextBoxFor(p => p.FlightNo, new { placeholder = "航班號" })
/div>
div class="form-group">
label>乘機(jī)人/label>
table class="passenger" >
tbody>
@if (Model.Passengers != null Model.Passengers.Count > 0) {
for(int i = 0; i Model.Passengers.Count; i++) {
tr>
td>姓名:/td>
td>@Html.TextBoxFor(p => Model.Passengers[i].Name)/td>
td>身份證號:/td>
td>@Html.TextBoxFor(p => Model.Passengers[i].IdNo)/td>
td>
a href="javascript:;" onclick="removePassenger(this)" >刪除/a>
/td>
/tr>
}
}
/tbody>
/table>
div style="margin-top:10px">
a href="javascript:;" onclick="addPassenger()">添加乘機(jī)人/a>
/div>
/div>
/div>
由于ASP.NET MVC的模型綁定器(DefaultModelBinder)具備自動解析形如"[0].屬性名"、"[1].屬性名"的能力,所以可以在模板文件中以占位符的形式來表示數(shù)組下標(biāo):
!-- 乘機(jī)人模板 -->
script type="text/html" id="passengerTemplate">
tr>
td>姓名:/td>
td>input id="Passengers_{}__Name" name="Passengers[{}].Name" type="text" value="">/td>
td>身份證號:/td>
td>input id="Passengers_{}__IdNo" name="Passengers[{}].IdNo" type="text" value="">/td>
td>
a href="javascript:;" onclick="removePassenger(this)">刪除/a>
/td>
/tr>
/script>
以上代碼中的"{}"是數(shù)組下標(biāo)占位符。當(dāng)添加乘機(jī)人時,可預(yù)先計算已有乘機(jī)人個數(shù),然后再使用JavaScript替換”{}“為數(shù)組下標(biāo)。
// 添加乘機(jī)人
function addPassenger() {
// 當(dāng)前添加行數(shù)組元素下標(biāo)
var index = $(".passenger").find("tbody").find("tr").length;
//{}是數(shù)組元素下標(biāo)占位符
var passengerHTML = $('#passengerTemplate').html().replace(/{}/g, index);
$(".passenger").find("tbody").append(passengerHTML);
}
當(dāng)刪除乘機(jī)人時,注意如果刪除的不是最后一個,會發(fā)生索引斷裂問題,需要重新調(diào)整數(shù)組下標(biāo):
// 刪除乘機(jī)人
function removePassenger(e) {
$(e).parents("tr").remove();
// 依次遍歷表格的每行,重新調(diào)整數(shù)組下標(biāo)
var tb = $(".passenger").first();
var count = tb.find("tbody").find("tr").length;
for (var i = 0; i count; i++) {
var newTR = tb.find("tr").eq(i).formhtml().replace(/\[\d+\]/g, '[' + i + ']');//重新調(diào)整數(shù)組元素下標(biāo)
tb.find("tr").eq(i).html(newTR);
}
}
這樣,當(dāng)我們提交表單時,乘機(jī)人信息就會自動填充到模型的Passengers屬性中。
方式二:使用Vue.js
使用第一種方式需要編寫大量前端代碼,包括模板文件,添加刪除事件,還需要處理重新調(diào)整順序時的插值問題。
如果使用前端MVVM框架會讓這一流程變得簡單,目前比較流行的前端MVVM框架有AngularJS,有老古董KnockoutJS,也有新興小眾框架Vue.js。
AngularJS比較龐大,這么簡單的一個模型綁定用Anuglar有一種殺雞用牛刀的感覺;Knockout和Vue都是輕量級的MVVM框架,但Knockout需要包裹原生數(shù)據(jù)來制造可觀察對象,取值和賦值時需要采用函數(shù)調(diào)用的形式,使用起來不是很方便,所以我選擇了Vue.js。Vue.js是一個輕量高效的庫,它沒有像Angular的module、controller、scope、factory、service這種API,核心就是一個模型綁定功能。大小只有70kb,gzip壓縮后只有25kb,非常輕量化。
這種方式的基本原理是前端使用Vue.js聲明視圖模型并進(jìn)行綁定,然后提交表單時把模型序列化為json字符串傳遞到后臺,后臺再使用Json.net反序列化為C#對象。
由于Vue.js的綁定特點,我們只需要操作數(shù)組元素即可,不需要額外關(guān)注DOM操作,節(jié)省了不少工作量。
首先,需要聲明視圖模型,并使用Vue.js進(jìn)行綁定:
script src="~/Scripts/vue.js">/script>
script type="text/javascript">
// 視圖模型
var viewModel = {
FlightNo: '',
Passengers: [
{ ElementId: 'passenger_1', Name: '', IdNo: '' }
]
}
// 模型綁定
new Vue({
el: '#app',
data: viewModel,
methods: {
removePassenger: function (elementId) {
for (var i = 0; i viewModel.Passengers.length; i++) {
if (viewModel.Passengers[i].ElementId == elementId) {
viewModel.Passengers.splice(i, 1);
}
}
},
addPassenger: function () {
var tb = document.getElementsByTagName('table')[0];
var index = tb.rows[tb.rows.length - 1].getElementsByTagName('input')[0].getAttribute("id").split('_')[1];
viewModel.Passengers.push({ Name: '', IdNo: '', ElementId: 'passenger_' + (index + 1) });
},
submitForm: function () {
var jsonString = JSON.stringify(viewModel);
document.getElementById("viewModel").value = jsonString;
return true;
}
}
});
/script>
然后,在視圖中使用Vue.js綁定:
form action="/Order2/NewPost" method="post">
div id="app" style="width:680px">
div class="form-group">
label>航班/label>br />
input v-model="FlightNo" type="text" placeholder="航班號" />
/div>
div class="form-group">
label>乘機(jī)人/label>
table class="passenger">
tbody>
tr v-for="passenger in Passengers">
td>姓名:/td>
td>input v-model="passenger.Name" v-bind:id="passenger.ElementId" type="text" />/td>
td>身份證號:/td>
td>input v-model="passenger.IdNo" type="text" />/td>
td>
a href="javascript:;" v-on:click="removePassenger(passenger.ElementId)">刪除/a>
/td>
/tr>
/tbody>
/table>
div style="margin-top:10px">
a href="javascript:;" v-on:click="addPassenger">添加乘機(jī)人/a>
/div>
div style="margin-top:10px">
input type="submit" class="btn btn-default" v-on:click="submitForm" />
/div>
/div>
/div>
input type="hidden" id="viewModel" name="viewModel" />
/form>
最后在Controller里,我們反序列化即可得到對應(yīng)的C#強(qiáng)類型模型:
[HttpPost]
public ActionResult NewPost()
{
var jsonString = Request.Form["viewModel"];
Models.OrderModel model = Newtonsoft.Json.JsonConvert.DeserializeObjectModels.OrderModel>(jsonString);
if (model != null) {
// our code here...
}
return RedirectToAction("Index", "Home");
}
這兩種方式均可以實現(xiàn)動態(tài)數(shù)組綁定,方式一使用js進(jìn)行占位符替換,表單中的元素都以[index].屬性名的方式命名,然后由MVC默認(rèn)的模型綁定器來轉(zhuǎn)化模型;
方式二使用Vue.js來直接進(jìn)行模型綁定,提交表單時將模型序列化為json字符串,然后后端再反序列化,最終得到強(qiáng)類型模型。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助。
您可能感興趣的文章:- asp.net 字符串、二進(jìn)制、編碼數(shù)組轉(zhuǎn)換函數(shù)
- asp.net 判斷數(shù)組是否存在某個值的方法
- asp.net(c#) 使用Rex正則來生成字符串?dāng)?shù)組的代碼
- asp.net通過js實現(xiàn)Cookie創(chuàng)建以及清除Cookie數(shù)組的代碼
- asp.net 數(shù)組中字符串替換的幾種方式
- vb.net 數(shù)組參與SQL語句的查詢范例
- ASP.NET數(shù)組刪除重復(fù)值實現(xiàn)代碼
- .NET數(shù)組使用中的注意事項小結(jié)
- .NET下模擬數(shù)組越界的方法詳解