[ASP.net MVC 4] Html Table使用Ajax刪除後的資料列更新(手動打造)

[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" /> 
                        &nbsp;
                        @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>

說明都在註解裡,注意刪除的部份即可

執行結果:

一開始的畫面

image

選擇檔案後自動上傳

image

對著第二張圖片按下「刪除」

image

這個範例因為每次刪除都會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" /> 
                        &nbsp;
                        @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>

執行結果:

一開始的畫面

image

選擇圖片後,網頁自動post上傳

image

刪除第二張

image

image

按下「提交」鈕寫Log

image

 

結語

睡覺去Orz…………

 

本文範例檔下載,放到MSDN時間到