[ASP.net MVC 4] Html Table使用Ajax刪除後的資料列更新(手動打造)
前言
從MVP Open Day睡眠不足回來,我為啥還一直拼命發文Orz……
之前文章提到:[ASP.net MVC 4] Controller如何取得Html Table裡各個Cell格子的值
既然在Html Table 的每個Cell偷塞hidden欄位,在Controller就可以取得每個Cell的值
如果在View對該筆資料列做編輯或刪除後,再refresh該Html Table,把全部hidden欄位更新,下一次表單post到Controller時
接到的資料集合順序才是正確
以下展示
1. post網頁刷新
2. Ajax方式
當刪除某一筆資料列時,Html Table該如何Refresh更新
舉刪除做例子,是因為現在我很累Orz…,刪除比較簡單省事
實作
範例從這篇開始接:[ASP.net MVC 4] 多張上傳圖片預覽,不使用Session和js套件的方式
剛好變成三部曲(?)
使用者上傳圖檔除了預覽外,應該也有需求把預覽圖刪除掉
ViewModel
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace MvcApplicationHtmlTableRefresh.Models
{
public class MemberViewModel
{
/// <summary>
/// 會員姓名
/// </summary>
public string MemberName { get; set; }
/// <summary>
/// 加這個,當MemberViewModel new出來時,存取PhotoFileNames才不會發生NULL例外
/// </summary>
private List<string> _PhotoFileNames = new List<string>();
/// <summary>
/// 多張大頭照的檔案名稱
/// </summary>
public List<string> PhotoFileNames { get { return this._PhotoFileNames; } set { this._PhotoFileNames = value; } }
}
}
HomeController
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MvcApplicationHtmlTableRefresh.Models;
namespace MvcApplicationHtmlTableBind.Controllers
{
public class HomeController : Controller
{
/// <summary>
/// 一開始進入表單
/// </summary>
/// <returns></returns>
[HttpGet]
public ActionResult Index()
{
MemberViewModel vm = new MemberViewModel();
return View(vm);
}
/// <summary>
/// 表單提交
/// </summary>
/// <param name="act"></param>
/// <param name="vm"></param>
/// <param name="myFile"></param>
/// <returns></returns>
[HttpPost]
public ActionResult Index(string act, MemberViewModel vm, HttpPostedFileBase myFile,FormCollection form)
{
switch (act)
{
case "upload"://上傳照片
this.UploadPhoto(vm, myFile);
break;
case "delete"://刪除單張照片
ModelState.Clear();//沒加這段,畫面上有可能會出現舊資料
//從ViewModel刪除該筆照片
vm.PhotoFileNames.Remove(
vm.PhotoFileNames.Where(x => x == form["DelFileName"]).FirstOrDefault()
);
//從DB或檔案系統刪除該筆照片
System.IO.File.Delete(Server.MapPath("~/UploadFiles/" + form["DelFileName"]));
break;
case "post"://存檔,寫DB
//照片已在剛剛就上傳到Server
this.LogWrite(vm);
break;
}
return View(vm);
}
/// <summary>
/// 寫Log查看表單post的結果
/// </summary>
/// <param name="vm"></param>
private void LogWrite(MemberViewModel vm)
{
//寫Log - 檔案名稱(知道上傳的檔案名稱,就可以寫DB了)
using (FileStream fs = new FileStream(@"D:\myLog.txt", FileMode.Append, FileAccess.Write, FileShare.ReadWrite))
{
StreamWriter sw = new StreamWriter(fs);
foreach (string strPhotoFileName in vm.PhotoFileNames)
{
sw.WriteLine(strPhotoFileName);
}
sw.Close();
}
}
/// <summary>
/// 上傳單一照片
/// </summary>
/// <param name="vm"></param>
/// <param name="myFile"></param>
private void UploadPhoto(MemberViewModel vm, HttpPostedFileBase myFile)
{
if (myFile != null && myFile.ContentLength > 0)//使用者有選擇照片檔案
{
//新的檔案名稱
string strFileName = Guid.NewGuid().ToString() + Path.GetExtension(myFile.FileName);
//存放檔案路徑
string strFilePath = Server.MapPath("~/UploadFiles/" + strFileName);
//檔案存放在Server
myFile.SaveAs(strFilePath);
//ViewModel的PhotoFileNames累加一張圖片名稱
vm.PhotoFileNames.Add(strFileName);
}
}
}
}
Index.cshtml
@model MvcApplicationHtmlTableRefresh.Models.MemberViewModel
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
<script type="text/javascript" src="@Url.Content("~/Scripts/jquery-2.0.0.min.js")"></script>
<!--加入一些自己手寫的簡單jQuery語法-->
<script type="text/javascript">
//上傳照片
function uploadPhoto() {
$("input[name='act']").attr("value", "upload");
//表單提交
$("form[name='myForm']").submit();
}
//提交表單
function postForm() {
//更改flag
$("input[name='act']").val("post");
}
//刪除單張照片
function deletePhoto(strFilename)
{
$("input[name='DelFileName']").val(strFilename);
$("input[name='act']").val("delete");
//表單提交
$("form[name='myForm']").submit();
}
</script>
</head>
<body>
@using (Html.BeginForm("Index", "Home", FormMethod.Post, new { enctype = "multipart/form-data", name = "myForm" }))
{
<!--紀錄是要上傳照片還是資料存檔-->
<input type="hidden" value="" name="act" />
<!--刪除照片的檔案名稱-->
<input type="hidden" name="DelFileName" value=""/>
<!--選擇檔案後,表單自動提交-->
<input type="file" value="選擇大頭照" name="myFile" onchange="uploadPhoto();" />
<table border="1" >
@for (int i = 0; i < Model.PhotoFileNames.Count; i++)
{
<tr>
<td>
<!--預覽圖-->
<img src="@(Url.Content("~/UploadFiles/")+Model.PhotoFileNames[i])" width="100" />
@Html.HiddenFor(m=>m.PhotoFileNames[i])
<a href="javascript:deletePhoto('@Model.PhotoFileNames[i]');">刪除</a>
</td>
</tr>
}
</table>
<hr /><!--我是分隔線-->
<input type="submit" value="提交" onclick="postForm();" />
}
</body>
</html>
說明都在註解裡,注意刪除的部份即可
執行結果:
一開始的畫面
選擇檔案後自動上傳
對著第二張圖片按下「刪除」
這個範例因為每次刪除都會post網頁刷新,以下再貢獻一個Ajax刪除方式
用到技術:
1. jQuery Ajax傳遞ViewModel (jQuery Ajax pass ViewModel)
2. jQuery Ajax傳遞ViewModel時,可使用hidden再追加傳遞表單參數
3. 因為要在callback function更新整個Html Table,我把上面的for-loop部份抽成Partial View,不然就得在Delete的Action拼接出那些奇奇怪怪的hidden name等等一堆Html字串再return Content(HtmlString);很麻煩
為了方便範例檔下載後可直接執行,所以我另外新寫了Controller和View
HomeAjaxController
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MvcApplicationHtmlTableRefresh.Models;
namespace MvcApplicationHtmlTableRefresh.Controllers
{
public class HomeAjaxController : Controller
{
/// <summary>
/// 一開始進入表單
/// </summary>
/// <returns></returns>
[HttpGet]
public ActionResult IndexAjax()
{
MemberViewModel vm = new MemberViewModel();
return View(vm);
}
/// <summary>
/// 表單提交
/// </summary>
/// <param name="act"></param>
/// <param name="vm"></param>
/// <param name="myFile"></param>
/// <returns></returns>
[HttpPost]
public ActionResult IndexAjax(string act, MemberViewModel vm, HttpPostedFileBase myFile, FormCollection form)
{
switch (act)
{
case "upload"://上傳照片
this.UploadPhoto(vm, myFile);
break;
case "post"://存檔,寫DB
//照片已在剛剛就上傳到Server
this.LogWrite(vm);
break;
}
return View(vm);
}
/// <summary>
/// Ajax刪除單張照片
/// </summary>
/// <param name="vm"></param>
/// <param name="form"></param>
/// <returns></returns>
[HttpDelete]
public ActionResult PhotoDelete(MemberViewModel vm,FormCollection form)
{
//加這段,原因見其他網友解釋:http://www.dotblogs.com.tw/constancy/archive/2013/04/02/99764.aspx
//沒加的話,畫面上會出現舊的資料
ModelState.Clear();
//從ViewModel刪除該筆照片
vm.PhotoFileNames.Remove(form["DelFileName"]);
//從DB或檔案系統刪除該筆照片
System.IO.File.Delete(Server.MapPath("~/UploadFiles/" + form["DelFileName"]));
//到部份檢視
return View("HtmlTable",vm);
}
/// <summary>
/// 寫Log查看表單post的結果
/// </summary>
/// <param name="vm"></param>
private void LogWrite(MemberViewModel vm)
{
//寫Log - 檔案名稱(知道上傳的檔案名稱,就可以寫DB了)
using (FileStream fs = new FileStream(@"D:\myLog.txt", FileMode.Append, FileAccess.Write, FileShare.ReadWrite))
{
StreamWriter sw = new StreamWriter(fs);
foreach (string strPhotoFileName in vm.PhotoFileNames)
{
sw.WriteLine(strPhotoFileName);
}
sw.Close();
}
}
/// <summary>
/// 上傳單一照片
/// </summary>
/// <param name="vm"></param>
/// <param name="myFile"></param>
private void UploadPhoto(MemberViewModel vm, HttpPostedFileBase myFile)
{
if (myFile != null && myFile.ContentLength > 0)//使用者有選擇照片檔案
{
//新的檔案名稱
string strFileName = Guid.NewGuid().ToString() + Path.GetExtension(myFile.FileName);
//存放檔案路徑
string strFilePath = Server.MapPath("~/UploadFiles/" + strFileName);
//檔案存放在Server
myFile.SaveAs(strFilePath);
//ViewModel的PhotoFileNames累加一張圖片名稱
vm.PhotoFileNames.Add(strFileName);
}
}
}
}
部份檢視HtmlTable.cshtml
@model MvcApplicationHtmlTableRefresh.Models.MemberViewModel
@for (int i = 0; i < Model.PhotoFileNames.Count; i++)
{
<tr>
<td>
<!--預覽圖-->
<img src="@(Url.Content("~/UploadFiles/")+Model.PhotoFileNames[i])" width="100" />
@Html.HiddenFor(m=>m.PhotoFileNames[i])
<a href="javascript:deletePhoto('@Model.PhotoFileNames[i]');">刪除</a>
</td>
</tr>
}
View的IndexAjax.cshtml
@model MvcApplicationHtmlTableRefresh.Models.MemberViewModel
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
<script type="text/javascript" src="@Url.Content("~/Scripts/jquery-2.0.0.min.js")"></script>
<!--加入一些自己手寫的簡單jQuery語法-->
<script type="text/javascript">
//上傳照片
function uploadPhoto() {
$("input[name='act']").val("upload");
//表單提交
$("form[name='myForm']").submit();
}
//提交表單
function postForm() {
//更改flag
$("input[name='act']").val("post");
}
//刪除單張照片
function deletePhoto(strFilename) {
if (confirm("確定刪除?")) {
//改變hidden,記下是哪個檔名要被刪除
$("input[name='DelFileName']").val(strFilename);
var viewModel = $("form[name='myForm']").serialize();
$.ajax({
url: "@Url.Action("PhotoDelete", "HomeAjax")",
type: "Delete",
async: false,
data: viewModel,
success: function (result) {
//置換更新整個Html Table
$("#tableContent").html(result);
},
error: function (xhr) {
//顯示錯誤訊息
alert(xhr.responseText);
}
});
}
}
</script>
</head>
<body>
@using (Html.BeginForm("IndexAjax", "HomeAjax", FormMethod.Post, new { enctype = "multipart/form-data", name = "myForm" }))
{
<!--紀錄是要上傳照片還是資料存檔-->
<input type="hidden" value="" name="act" />
<!--刪除照片的檔案名稱,從Controller的FormCollection參數可以接到此值-->
<input type="hidden" name="DelFileName" value=""/>
<!--選擇檔案後,表單自動提交-->
<input type="file" value="選擇大頭照" name="myFile" onchange="uploadPhoto();" />
<table border="1" id="tableContent" >
@Html.Partial("HtmlTable", Model)
</table>
<hr /><!--我是分隔線-->
<input type="submit" value="提交" onclick="postForm();" />
}
</body>
</html>
執行結果:
一開始的畫面
選擇圖片後,網頁自動post上傳
刪除第二張
按下「提交」鈕寫Log
結語
睡覺去Orz…………
本文範例檔下載,放到MSDN時間到