範例4已展示KO完全掌握observableArray()陣列元素增減,即時反應在UI的能力。但如果我們希望在陣列增加或移除元素時加上自訂邏輯,要怎麼做?

foreach提供了afterAdd及beforeRemove兩個額外的事件繫結設定,允許在陣列新增、移除元素時執行特定邏輯。在此繼續沿用先前的使用者清單呈現範例,加上兩個效果:

  • 新增資料時,將最新加入的資料標為暗紅字
  • 刪除資料時,加上資料淡出後消失的特效

而在ViewModel裡我們加上兩個函數:

            //第一次foreach產生時不會觸發,只有事後加入才會
            self.afterAddEvent = function (elems, idx, data) {
                //Template中的Text Node與Element Node會分別觸發多次
                //在本例中會因Text, Element, Text共觸發三次
                //透過nodeType過濾,只處理Element Node
                if (elems.nodeType == 1) //Element Node
                {
                    $(".new").removeClass("new");
                    $(elems).addClass("new");
                }
            };
            //注意: 掛載beforeRemove事件後,要自已負責移除被刪除元素
            self.b4RemoveEvent = function (elems, idx, data) {
                if (elems.nodeType == 1) {
                    $(elems)
                    .css("background-color", "red")
                    .animate({ opacity: 0.2 }, 500, function () {
                        $(this).remove();
                    })
 
                }
            };

afterAdd及beforeRemove函數會固定收到三個參數,elems、index及data,其中elems為範本容器中的各元素,在本範例中,就是以下黃底紅字部分:

<tbody data-bind="foreach: { data: users, afterAdd: afterAddEvent, beforeRemove: b4RemoveEvent }">
    <tr>
        <td><span data-bind="text: id"></span></td>
        <td><span data-bind="text: name"></span></td>
        <td><span data-bind="text: score" style='text-align: right'></span></td>
        <td><a href='#' data-bind="click: $root.removeUser">移除</a></td>
    </tr>

</tbody>

但此處有個小訣竅,實際運作時afterAdd/beforeRemove會收到不同的elems被呼叫三次,原因是除了<tr>之外,<tbody>到<tr>之間的空白、</tr>到</tbody>間的空白也各算一個Element,其nodeType為3,代表TEXT_NODE。因此三次傳入的elems分別為TEXT_NODE、ELEMENT_NODE、TEXT_NODE,而第二次傳入的ELEMENT_NODE是<tr>…</tr>間的內容,才是我們需要處理的對象,故加入if (elems.nodeType == 1)的判斷式。

最新加入資料的特別標示,在此透過addClass("new")來達成;至於移除資料的特效,則先為<tr>加上不透明度(opacity)由100%降到20%的動畫,然後在動畫完成後,將其自<tbody>中移除。要注意,一旦掛載了beforeRemove,KO就不再自動幫你移除該筆資料在網頁對應的元素,必須自行處理,但這也提供開發人員絕對的控制權,可自由安排HTML元素要怎麼從網頁上退場。

線上展示


[KO系列]
http://www.darkthread.net/kolab/labs/default.aspx?m=post

Comments

# by 李政忠

你好,我想在template裡使用afterAdd事件時,卻一直無法成功,還請指點迷津,謝謝。 ... <div data-bind="template: { name: 'contactTmpl', foreach: members, afterAdd: afterAddEvent }"></div> ... function MyViewModel() { ... self.afterAddEvent = function(element, index, data){ console.log(index); }; }

# by Jeffrey

to 李政忠,經測試應該可行,請參考範例: http://jsfiddle.net/darkthread/8HpMy/1/ 看看哪邊有差異

# by Andy

Dear 黑暗大 您好 我設計一個網頁,裡面有一些TextBox,其中還有如此範例的動態表格(可新增/刪除),當我按下儲存按鈕時,會把相對應的TextBox內容丟到我已建好的資料庫其指定的資料表裡面,我的問題如下: 我也想把count行數的那個值丟到資料表某個欄位(此範例圖片中共4筆==>的那個"4"這個值),asp.net要如何去抓它呢 還請大大指教,感謝

# by Jeffrey

to Andy, 如果你 ASP.NET 端是用 PostBack 方式取值,最簡單的做法是將 4 放入 <input type="hidden" name="fldCount" data-bind="value: ..." />,後端用 Request["fldCount"] 接回數字。

# by Andy

Dear 黑暗大 感謝您,已成功將count後的數值寫入資料庫 利用您提供的方法,也將動態表格內user所填/選的資料成功寫入資料庫了

# by Andy

Dear 黑暗大 您好 之前有參考您「KO範例6 - 陣列元素的新增/移除事件」及「KO範例19 - 下拉選單連動效果」的內容,成功在所寫的網頁(系統)上讓【新增】(button)同時呈現兩種功能。不過,我遇到一個問題,當網頁用chrome(版本 72.0.3626.119 (正式版本) (32 位元))開啟時→功能正常;當網頁用IE(版本:8.0.7601.24000)開啟時→功能失效,其實這個問題我一直沒去理會它,以為只要用chrome開啟就沒問題,最近試了5~6台與我電腦規格一樣、chrome版本一樣的其它電腦,用chrome開啟我所做的網頁,也遇到"""功能失效"""的問題。 目前:查過瀏覽器版本→一樣;安全性設定→一樣,尚找不到解決此問題的辦法,所以想請問黑暗大是否有類似的經驗?或知道我可能遇到的問題在哪兒? 還請大大指教,感謝

# by Jeffrey

to Andy, 沒看到實體很難揣測原因,能用 jsbin 或 jsfiddler 重現你說的問題嗎?

# by Andy

Dear 黑暗大 實際頁面範如右===> https://output.jsbin.com/laruhiciki "新增樣品(Add)"這個【按鈕】功能失效 另外,需有一個AnalysisProject.js檔案(for 分析儀器 與 分析項目 下拉式選單連動 )===>檔案內容如下 var AnalysisProjectData = { "設備1": { "分析A": "001", "分析B": "002" }, "設備2": { "分析AA": "101", "分析BB": "102" }, "設備3": { "分析CCC": "201" } } ;

# by Jeffrey

to Andy, 看到一個問題,你的程式碼是從 www.darkthread.net / kolab / Scripts / 取用 jquery-1.7.2.js 及 knockout-2.1.0.debug.js (這讓我有點意外,因為該網站只是個範例,維護方面比較隨便,並不適合做為正式的 js 下載來源),前陣子網站搬家後該連結已失效,要修復需要點時間。建議你改從 CND 引用這兩個 js 或將它們放進自己專案裡。 至於你說 Chrome 72.0正常,IE 跟其他 Chrome 失效的問題,猜想可能是那台機器在網站失效前曾 Cache 住 jquery 及 knockout,才意外可以繼續使用。

# by Andy

Dear 黑暗大 非常感謝您點出了我的問題,修改程式碼取用 jquery-1.7.2.js 及 knockout-2.1.0.debug.js 的路徑後, 的確沒問題了(不管是用IE、還是chrome)。 不好意思,讓黑暗大吃驚了!,還請原諒算是新手階段的所我犯下的錯XD

Post a comment