[Vue.js] 表格列動態新增、編輯與刪除 jQuery 對決 Vue.js

Table add row,edit row,delete row 

jQuery  v.s. Vue.js

前言

有時候工作會碰到這種刁鑽的需求,只要遇到jQuery需要組大量html字串時,考慮使用Vue.js就沒錯

實作

後端網頁環境ASP.net Core 2.1

先看jQuery版本,ASP.net Core 的 Controller

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;

namespace WebApp_jQueryTableCRUD.Controllers
{
    //定義一筆資料當ViewModel 
    public class User
    {
        public long v_id { get; set; }
        public string userName { get; set; }
        public string userJob { get; set; }
        public string userSex { get; set; }
        public bool isOver18 { get; set; }
    }


    public class HomeController : Controller
    {
        public IActionResult Index()
        {
            return View();
        }

        [HttpPost]
        public IActionResult Index(List<long> v_id, List<string> userName, List<string> userJob, IFormCollection form)
        {
            List<User> users = new List<User>();
            int index = 0;
            foreach (long uuid in v_id)
            {
                User user = new User() { v_id=uuid};
                user.userName = userName[index] ?? "";
                user.userJob = userJob[index] ?? "-1";
                if (form.ContainsKey($@"userSex_{uuid}"))
                {//使用者有勾
                    user.userSex = form[$@"userSex_{uuid}"];//取 男 或 女
                }
                else
                {
                    user.userSex = "";
                }

                user.isOver18 = form.ContainsKey($@"isOver18_{uuid}") ? true : false;
                users.Add(user);
                index++;
            }

            ViewData["result"] = JsonConvert.SerializeObject(users);
            //回傳剛剛輸入的資料
            return View(users);
        }

    }


}

View

@using WebApp_jQueryTableCRUD.Controllers
@model List<User>

<!doctype html>
<html>
<head> 
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css"
          integrity="sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS" crossorigin="anonymous">
    <title>jQuery 操作Table新增、刪除並儲存</title>
    <style type="text/css">
        body {
            padding-top: 30px;
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="row">
            <div class="col-12">

                <div class="clearfix"> 
                    <button class="btn btn-info float-right" id="btnAdd">新增</button>
                </div>
                <hr />

                <form method="post" action="@Url.Action("Index","Home")">
                    <table class="table table-striped table-bordered table-hover">
                        <thead class="thead-light">
                            <tr>
                                <th>姓名</th>
                                <th>職業</th>
                                <th>性別</th>
                                <th>是否成年</th>
                                <th>操作</th>
                            </tr>
                        </thead>
                        <tbody id="tbody">
                            @if (Model != null)
                            {
                                foreach (User user in Model)
                                {
                                    long v_id = user.v_id; 

                                    string trID = $@"tr_{v_id}";
                                    <tr id="@trID"> <!--jQuery刪除 tr 使用這個id-->
                                        <td>
                                            @*給後端識別資料用*@
                                            <input type="hidden" name="v_id" value="@v_id" />
                                            <input type="text" class="form-control" name="userName" placeholder="請輸入姓名" value="@user.userName" />

                                        </td>
                                        <td>
                                            <select class="form-control" name="userJob">
                                                <option value='-1' @(user.userJob == "-1" ? "selected" : "")>請選擇</option>
                                                <option @(user.userJob == "打字員" ? "selected" : "")>打字員</option>
                                                <option @(user.userJob == "工程師" ? "selected" : "")>工程師</option>
                                            </select>

                                        </td>
                                        <td class="text-center align-middle">
                                            <div class="custom-control custom-radio">
                                                <input type="radio" class="custom-control-input" id="userSexMale_@v_id" name="userSex_@v_id" value="男" @(user.userSex == "男" ? "checked" : "")>
                                                <label class="custom-control-label" for="userSexMale_@v_id">男</label>
                                            </div>
                                            <div class="custom-control custom-radio">
                                                <input type="radio" class="custom-control-input" id="userSexFemale_@v_id" name="userSex_@v_id" value="女" @(user.userSex == "女" ? "checked" : "") />
                                                <label class="custom-control-label" for="userSexFemale_@v_id">女</label>
                                            </div>

                                        </td>
                                        <td class="text-center align-middle">
                                            <div class="custom-control custom-checkbox">
                                                <input type="checkbox" class="custom-control-input" name="isOver18_@v_id" id="isOver18_@v_id" @(user.isOver18 == true ? "checked" : "") />
                                                <label class="custom-control-label" for="isOver18_@v_id"></label>
                                            </div>
                                        </td>
                                        <td>
                                            <button class="btn btn-danger" onclick="DeleteRow('@trID');">刪除</button>

                                        </td>
                                    </tr>


                                }//end foreach
                            }
                        </tbody>
                    </table>
                    <hr />

                    <div class="clearfix">
                        <button class="btn btn-primary float-right" id="btnSubmit" type="submit">儲存</button>
                    </div>
                    <div>
                        <pre>
                         @ViewData["result"]
                         </pre>
                    </div>
                </form>
            </div>

        </div>
    </div>


    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="https://code.jquery.com/jquery-3.3.1.min.js" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.6/umd/popper.min.js" integrity="sha384-wHAiFfRlMFy6i5SRaxvfOCifBUQy1xHdJ/yoi7FRNXMRBu5WHdZYu1hA6ZOblgut" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/js/bootstrap.min.js" integrity="sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k" crossorigin="anonymous"></script>
    <script type="text/javascript">
        /*jQuery都是操作畫面DOM元素為主*/

        $(function () {
            $("#btnAdd").on("click", function () {

                let v_id = Date.now();//產生一個畫面用的不重複id
               
                //讓新增的欄位id不重複
                let trID = `tr_${v_id}`;
                let trHtml = `"<tr id="${trID}">
                                        <td>
                                             <input type="hidden" name="v_id" value="${v_id}" />
                                             <input type="text" class="form-control" name="userName" placeholder="請輸入姓名" />

                                        </td>
                                        <td>  <select class="form-control" name="userJob">
                                                <option value='-1'>請選擇</option>
                                                <option>打字員</option>
                                                <option>工程師</option>
                                              </select>

                                        </td>
                                       <td class="text-center align-middle">
                                                <div class="custom-control custom-radio">
                            <input type="radio" class="custom-control-input" id="userSexMale_${v_id}" name="userSex_${v_id}" value="男"  >
                            <label class="custom-control-label" for="userSexMale_${v_id}">男</label>
                              </div>
                              <div class="custom-control custom-radio">
                                <input type="radio" class="custom-control-input" id="userSexFemale_${v_id}" name="userSex_${v_id}" value="女" >
                                <label class="custom-control-label" for="userSexFemale_${v_id}">女</label>
                              </div>

                                 </td>
                                        <td class="text-center align-middle">
                                            <div class="custom-control custom-checkbox">
                                                <input type="checkbox" class="custom-control-input" name="isOver18_${v_id}" id="isOver18_${v_id}">
                                                <label class="custom-control-label" for="isOver18_${v_id}"></label>
                                            </div>
                     </td>
                    <td>
                      <button class="btn btn-danger" onclick="DeleteRow('${trID}');">刪除</button>

                    </td>
                                   </tr >`;
                $("#tbody").append(trHtml);

            });
        });
        function DeleteRow(trID) {
            //移除table的一筆項目
            $(`#${trID}`).remove();
        }
    </script>
</body>
</html>

可以看到jQuery版本最大缺點,在新增時,要組html字串,而且相同html在畫面上寫了兩遍

線上Demo:https://jsfiddle.net/ShadowKao/kLth5mwc/

接著看Vue.js版本

Controller

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks; 
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;

namespace WebApp_jQueryTableCRUD.Controllers
{
    //定義一筆資料當ViewModel
    public class User
    {
        public long v_id { get; set; }   
        public string userName { get; set; }
        public string userJob { get; set; }
        public string userSex { get; set; }
        public bool isOver18 { get; set; }
    }


    public class HomeController : Controller
    {
        public IActionResult Index()
        {  
            return View(new List<User>());
        } 

        [HttpPost]
        public IActionResult Index(List<User> users)
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("<pre>後端接收到:");
            foreach (User user in users)
            {
                sb.AppendLine($@"user.v_id:{user.v_id} ,user.userName:{user.userName},user.userJob:{user.userJob},user.userSex:{user.userSex},user.isOver18:{user.isOver18}");
            }
            sb.AppendLine("</pre>");
            return Content(sb.ToString());
        }
    }
      

}

View

@using WebApp_jQueryTableCRUD.Controllers
@using Newtonsoft.Json
@model List<User>

<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css"
          integrity="sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS" crossorigin="anonymous">
    <title>Vue.js 操作Table新增、刪除並Ajax儲存</title>
    <style type="text/css">
        body {
            padding-top: 30px;
        }
        [v-cloak] {
          display:none !important;
        }
    </style>
</head>
<body>
    <div class="container" id="myApp">
        <div class="row">
            <div class="col-12">

                <div class="clearfix"> 
                    <button class="btn btn-info float-right" id="btnAdd" v-on:click="AddRow">新增</button>
                </div>
                <hr />

                <form method="post" action="#">
                    <table class="table table-striped table-bordered table-hover">
                        <thead class="thead-light">
                            <tr>
                                <th>姓名</th>
                                <th>職業</th>
                                <th>性別</th>
                                <th>是否成年</th>
                                <th>操作</th>
                            </tr>
                        </thead>
                        <tbody>



                            <!--通常迴圈中會處理複雜邏輯,必須小心 v-for不足以應付,複雜邏輯須事先在Controller就處理完畢*/-->
                            <tr v-for="(user,index) in users" v-cloak>
                                <td>
                                    <!--後端使用v_id資料識別資料-->
                                    <input type="text" class="form-control" name="userName" placeholder="請輸入姓名" v-model="user.userName" />

                                </td>
                                <td>
                                    <select class="form-control" name="userJob" v-model="user.userJob">
                                        <option value='-1'>請選擇</option>
                                        <option>打字員</option>
                                        <option>工程師</option>
                                    </select>

                                </td>
                                <td class="text-center align-middle">
                                    <div class="custom-control custom-radio">
                                        <input type="radio" class="custom-control-input"  v-bind:id="('userSexMale_'+user.v_id)"  v-bind:name="('userSex_'+user.v_id)" value="男" v-model="user.userSex">
                                        <label class="custom-control-label" v-bind:for="'userSexMale_'+user.v_id">男</label>
                                    </div>
                                    <div class="custom-control custom-radio">
                                        <input type="radio" class="custom-control-input"  v-bind:id="('userSexFemale_'+user.v_id)"  v-bind:name="('userSex_'+user.v_id)" value="女" v-model="user.userSex" />
                                        <label class="custom-control-label" v-bind:for="'userSexFemale_'+user.v_id">女</label>
                                    </div>

                                </td>
                                <td class="text-center align-middle">
                                    <div class="custom-control custom-checkbox">
                                        <input type="checkbox" class="custom-control-input"  v-bind:id="('isOver18_'+user.v_id)"  v-bind:name="('isOver18_'+ user.v_id)"  v-model="user.isOver18" />
                                        <label class="custom-control-label" v-bind:for="'isOver18_'+user.v_id"></label>
                                    </div>
                                </td>
                                <td>
                                    <button class="btn btn-danger" v-on:click="DeleteRow(user,index)" type="button">刪除</button>

                                </td>
                            </tr>




                        </tbody>
                    </table>
                    <hr />

                    <div class="clearfix">
                        <!--資料都存放在JS裡了,所以使用Ajax送出資料,不仰賴表單提交,因為表單提交動作和畫面的輸入欄位有很大藕合性,但Vue.js就是要讓開發者不用去管畫面的處理-->
                        <button class="btn btn-primary float-right" id="btnSubmit" type="button" v-on:click="AjaxSubmit">Ajax儲存</button>
                    </div>
                    <div v-html="showResult"> 
                    </div>
                </form>
            </div>

        </div>
    </div>
     


    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="https://code.jquery.com/jquery-3.3.1.min.js" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.6/umd/popper.min.js" integrity="sha384-wHAiFfRlMFy6i5SRaxvfOCifBUQy1xHdJ/yoi7FRNXMRBu5WHdZYu1hA6ZOblgut" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/js/bootstrap.min.js" integrity="sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k" crossorigin="anonymous"></script>
    <!--引用Vue.js-->
    <script type="text/javascript" src="https://vuejs.org/js/vue.js"></script>
    <script type="text/javascript">
        /*Vue.js以操作資料為主*/
        let users = @Html.Raw(JsonConvert.SerializeObject(Model));
        let myApp = new Vue({
            el: "#myApp", /*必填*/
            data: {
                users: users,
                showResult:""
            },
            //事件處理
            methods: {
                AddRow: function () {
                    let v_id = Date.now();//產生一個畫面用的不重複id
                    this.users.push({ v_id: v_id, userName: "", userJob: "-1", userSex: "", isOver18: false });//新增資料到陣列就好,不必組html字串
                },
                DeleteRow: function (user,index) {
                    let vm = this;
                    //要刪除的陣列索引
                    //let delIndex = vm.users.findIndex(function (item, index) {
                      //  return user.v_id === item.v_id;
                   // });
                      let delIndex=index;
                    //從集合中刪除物件
                    vm.users.splice(delIndex, 1);//刪除資料,畫面會跟著變動

                },
                AjaxSubmit: function () {
                    let vm = this;

                    $.ajax({
                        url: "@Url.Action("Index","Home")",
                        data:  { users: vm.users } ,
                        method: "post", 
                        success: function (result) {
                            vm.showResult = result;
                        }
                    });
                    //vm.showResult =  JSON.stringify(vm.users);
                }
            } 
        });

    </script>
</body>
</html>

Vue.js不用再組一次Html字串讓程式碼簡潔不少

線上Demo:https://jsfiddle.net/ShadowKao/g0b2xar6/

結語

透過範例比較一下 jQuery 和 Vue.js 兩者處理複雜畫面需求時,誰能讓程式碼簡潔好懂,加快開發速度。