延續上一篇的 ViewModel 結構
現在假設使用者提出要動態 新增/刪除 Collection 項目的需求
就可能碰到的問題拆成多個 Case 來看
另外,接下來開始著重於之前強調的 Model Binding,所以文章內就不提 render 後的 html !
這篇先從大家常用的手法來做新增/刪除 Collection 項目:
在 js 產生 html 來新增 以及 刪除指定的 Html dom !
加入新增
的功能 !
修改 Controller 內的 Action 內容如下:
Get Case01 時,不給定 ViewModel 的預設值
[HttpGet]
public IActionResult Case01()
{
return View();
}
修改 View 內容如下:
加入新增
的功能 !
Table thead 最右邊加上一欄,放上新增
按鈕
<button type="button" onclick="AddItem(event)">新增</button>
Table tbody 最右邊加上一欄
<td></td>
最下面加上 Section Script
@section Scripts {
<script>
window.ItemsCount = @(Model?.Items?.Length ?? 0);
window.ItemHtml = `
<tr>
<td>
<input type="text"
name="Items[i].Name" />
</td>
<td>
<input type="number"
step=1
min=0
name="Items[i].Quantity" />
</td>
<td>
</td>
</tr>
`;
window.AddItem = function () {
const itemHtml = ItemHtml..replaceAll('[i]', `[${ItemsCount}]`);
ItemsCount++;
$('#Items').append(itemHtml);
}
</script>
}
網站執行後,在該頁面新增訂單項目及輸入資料
submit 後,可以看出資料可以如預期的傳遞 !
接下來,加入刪除
的功能 !
從 Case01.cshtml 複製為 Case02.cshtml
修改 Case02.cshtml
把 Table tbody 最後一列改為以下語法
<td>
<button type="button" onclick="DeleteItem(event)">刪除</button>
</td>
把 js script 內的 window.ItemHtml 變數中,最後一欄,改為上述的語法
js script 加上以下刪除的 function
window.DeleteItem = function (e) {
const btnDom = e.target;
const trDom = $(btnDom).parent().parent();
trDom.remove();
};
重新編譯並執行網站,開啟剛才新增的頁面
送出
按鈕在上述第 2 步驟送出前,訂單項目當下的 html 會長這樣
可以發現
第一個項目是 Items[0]
而
第二個項目是 Items[2]
這個 index 不連續的情況,送到後端後,就會發生 Model Binding
部份失敗 !
<tbody id="Items">
<tr>
<td>
<input type="text" name="Items[0].Name" />
</td>
<td>
<input type="number" step="1" min="0" name="Items[0].Quantity" />
</td>
<td>
<button type="button" onclick="DeleteItem(event)">刪除</button>
</td>
</tr>
<tr>
<td>
<input type="text" name="Items[2].Name" />
</td>
<td>
<input type="number" step="1" min="0" name="Items[2].Quantity" />
</td>
<td>
<button type="button" onclick="DeleteItem(event)">刪除</button>
</td>
</tr>
</tbody>
查看 Log ,會發現找不到 Items[1] 後
Could not bind to model of type 'Project.Models.Day13.OrderItem' as there were no values in the request for any of the properties.
就等於完成該 Property 的 Binding 了 !
Done attempting to bind property
2021-05-25 16:32:49.5225 | 25 | DEBUG | Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexObjectModelBinder | Done attempting to bind model of type 'Project.Models.Day13.OrderItem' using the name 'Items[0]'.
2021-05-25 16:32:49.5225 | 24 | DEBUG | Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexObjectModelBinder | Attempting to bind model of type 'Project.Models.Day13.OrderItem' using the name 'Items[1]' in request data ...
2021-05-25 16:32:49.5387 | 18 | DEBUG | Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexObjectModelBinder | Could not bind to model of type 'Project.Models.Day13.OrderItem' as there were no values in the request for any of the properties.
2021-05-25 16:32:49.5387 | 14 | DEBUG | Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ArrayModelBinder | Done attempting to bind property 'Project.Models.Day13.ViewModel.Items' of type 'Project.Models.Day13.OrderItem[]'.
調整方式,請看 Case03
主要就是參考 Day05.Case03 的格式來調整
在每個 tbody tr 中加上
<input type="hidden" name="Items.index" value="@(i)" />
js script 的 ItemHtml 變數也加上類似上面的語法
<input type="hidden" name="Items.index" value="i" />
js script 的 AddItem 改為以下
window.AddItem = function () {
const itemHtml = ItemHtml.replaceAll('[i]', `[${ItemsCount}]`)
.replaceAll('value="i"', `value="${ItemsCount}"`);
ItemsCount++;
$('#Items').append(itemHtml);
}
讓原本的 html 及動態新增的 html 都能加上 name 為 Items.index & value 為指定值 的項目
就可以解決 Case02 因 index 不連續而導致 Model Binding 部份失敗 !
以 Case03 的 Code 來說,因為以下二點原因:
接下來,我會改用另一個做法來做 !
這篇先到這裡,下一篇來看 動態 新增/刪除 Collection 項目 (二)
!