[ASP.net MVC 4] Controller如何取得Html Table裡各個Cell格子的值

[ASP.net MVC 4] Controller如何取得Html Table裡各個Cell格子的值

前言

ASP.net MVC新手上路\(^O^)/

過沒多久就發現到相較以往寫WebForm時,畫面上拉一個GridView控制項

postback後,就可以在GridView的RowDataBound事件裡取得GridView裡各個Cell的值(可能用 .FindControl()方式)

那ASP.net MVC的Html Table呢?

把集合資料顯示在View的Html Table之後,按下submit,表單提交

在Controller想要對Table裡各個Cell的值做更動後再丟回View顯示Table更動後的結果…如此的需求

話說試出辦法前,我還不知道ViewModel裡的參考型別屬性該怎麼跟View Binding (逃~

經過這關,自己的ViewModel和View的繫結又更進一步認識

回到主題,要解決「在Controller的Action內取得Html Table裡各個Cell格子的值」

重點就是:每個Cell塞一個hidden欄位和注意name的命名(用索引方式)

聽起來基本,但一開始我還有點小卡關XD”

 

 

範例實作

在Models資料夾裡加入MemberViewModel.cs、Book.cs、Company.cs

因為我要順便紀錄ViewModel的參考型別屬性如何Binding網頁標籤,所以才會有Book和Company類別

MemberViewModel


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace MvcApplicationHtmlTable.Models
{
    public class MemberViewModel
    {
        /// <summary>
        /// 會員名稱
        /// </summary>
        public string MemberName { get; set; }

        private List<string> _NickNames = new List<string>();
        /// <summary>
        /// 多個暱稱
        /// </summary>
        public List<string> NickNames { get { return this._NickNames; } set { this._NickNames = value; } }
        /// <summary>
        /// 擁有多本書
        /// </summary>
        public List<Book> Books { get; set; }
        /// <summary>
        /// 屬於哪一家公司
        /// </summary>
        public Company Company { get; set; }
        

    }
}

Book


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace MvcApplicationHtmlTable.Models
{
    public class Book
    {
        /// <summary>
        /// 書名
        /// </summary>
        public string BookName { get; set; }
        /// <summary>
        /// 書本大小
        /// </summary>
        public string BookSize { get; set; }
    }
}

Company


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace MvcApplicationHtmlTable.Models
{
    public class Company
    {
        /// <summary>
        /// 公司人數
        /// </summary>
        public int PeopleNumber { get; set; }
        /// <summary>
        /// 是否為硬工作
        /// </summary>
        public bool IsHardWork { get; set; }

    }
}

Controller加入HomeController.cs


using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MvcApplicationHtmlTable.Models;

namespace MvcApplicationHtmlTableBind.Controllers
{
    public class HomeController : Controller
    {

        /// <summary>
        /// 一開始進入表單
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public ActionResult Index()
        {
            MemberViewModel vm = new MemberViewModel();
            InitData(vm);
            return View(vm);
        }
        /// <summary>
        /// 初始化資料(假裝這是從DB撈出來的資料)
        /// </summary>
        /// <param name="vm"></param>
        private static void InitData(MemberViewModel vm)
        {
            vm.MemberName = "王大明";
            vm.NickNames = new List<string>() { 
            "小明","明兄","明明"
            };
            vm.Books = new List<Book>(){
            new Book() {  BookName="ASP.NET MVC 4開發實戰" , BookSize="1000x1000"},
            new Book() { BookName = "ASP.NET MVC4網站開發美學", BookSize = "200x200" }
            };
            vm.Company = new Company() { PeopleNumber = 800, IsHardWork = true };

        }
        /// <summary>
        /// 表單提交
        /// </summary>
        /// <param name="act"></param>
        /// <param name="vm"></param>
        /// <param name="myFile"></param>
        /// <returns></returns>
        [HttpPost]
        public ActionResult Index(MemberViewModel vm)
        {
           
            this.LogWrite(vm);
                    

            return View(vm);
        }
        /// <summary>
        /// 寫Log查看表單post的結果
        /// </summary>
        /// <param name="vm"></param>
        private void LogWrite(MemberViewModel vm)
        {
            //寫Log - 暱稱
            using (FileStream fs = new FileStream(@"D:\myLog.txt", FileMode.Append, FileAccess.Write, FileShare.ReadWrite))
            {
                StreamWriter sw = new StreamWriter(fs);

                foreach (string strNickName in vm.NickNames)
                {
                    sw.WriteLine("暱稱:" + strNickName);
                }
                sw.Close();
            }

            //寫Log - 書藉
            using (FileStream fs = new FileStream(@"D:\myLog.txt", FileMode.Append, FileAccess.Write, FileShare.ReadWrite))
            {
                StreamWriter sw = new StreamWriter(fs);

                foreach (Book objBook in vm.Books)
                {
                    sw.WriteLine(objBook.BookName + "," + objBook.BookSize);
                }
                 
                sw.Close();
            }
            //寫Log - 公司
            using (FileStream fs = new FileStream(@"D:\myLog.txt", FileMode.Append, FileAccess.Write, FileShare.ReadWrite))
            {
                StreamWriter sw = new StreamWriter(fs);

                sw.WriteLine("公司人數:" + vm.Company.PeopleNumber);
                sw.WriteLine("是否為硬工作:" + vm.Company.IsHardWork);
                sw.Close();
            }
        }
       

    }
}

重點的View完整代碼:

Index.cshtml


@model MvcApplicationHtmlTable.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>
</head>
<body>
    @using (Html.BeginForm("Index", "Home", FormMethod.Post, new { enctype = "multipart/form-data", name = "myForm" }))
    {
        <div>
            會員姓名:@Html.TextBoxFor(m => m.MemberName)
            <br />
            會員暱稱:
            <table border="1">
                @*input系列的欄位才會自動Bind ViewModel,可以使用hidden欄位,把要傳給Controller的值隱藏起來*@
                <!--最好使用for loop,有index變數可用-->
                @for (int i = 0; i < Model.NickNames.Count; i++)
                {
                    <tr>
                        <td>@Model.NickNames[i] &nbsp; @Html.HiddenFor(m => m.NickNames[i])</td>
                    </tr>
                }
            </table>
            <br />
            書籍:
            <table border="1">
                @for (int i = 0; i < Model.Books.Count; i++)
                {
                    <tr>
                        <td>@Model.Books[i].BookName @Html.HiddenFor(m => m.Books[i].BookName)</td>
                        <td>@Model.Books[i].BookSize @Html.HiddenFor(m => m.Books[i].BookSize)</td>
                    </tr>
                }
            </table>
            <br />
            公司人數:@Html.TextBoxFor(Model => Model.Company.PeopleNumber)<br />
            是否為硬工作:
            @Html.RadioButtonFor(Model => Model.Company.IsHardWork, true)  是&nbsp;
            @Html.RadioButtonFor(Model => Model.Company.IsHardWork, false) 否
        </div>
        <input type="submit" value="提交" />
    }
</body>
</html>

圖解說明:

image

執行結果:

image

執行的網頁原始碼:集合的name都變成陣列型式,物件的name則為該物件.屬性名

image

按下提交按鈕後,透過寫出來的Log觀察,也確實抓到Html Table裡各Cell的值

image

 

結語

由於Table裡每個Cell偷塞了hidden欄位

到Controller那邊除了用ViewModel抓值外也可以使用FormCollection的GetValue(string name)或GetValues(string name)來抓值

不過還是用ViewModel才可以享受到強型別的好處

另外,Table若要整合 編輯或刪除 連結,可以在點選後用Ajax向Server進行異動後,callback function裡refresh整個table,讓每個Cell裡的hidden欄位值和name都更新,這樣表單post後

Controller接到的才會是正確的集合順序
 

參考文章:Getting values from the html to the controller

文章範例專案檔下載,放到MSDN期滿

 

以上

先有這樣的初步認識,下一篇介紹進階用法:[ASP.net MVC 4] 多張上傳圖片預覽,不使用Session和js套件的方式