【ASP.NET AJAX Templates系列】


先前的ASP.NET AJAX Templates介紹都集中在如何將資料反應到顯示元素上,記得嗎? 在Server Control Template中,我們可以寫Eval也可以寫Bind,當使用Bind時,更改Template裡的資料,會反應回原始的資料來源上,這在ASP.NET AJAX Client Templates也做得到!

進一步來說,ASP.NET AJAX Client Templates的Binding寫法有三種:

  1. One-Way, One-Time:
    如同先前講過的{{ propName }}寫法,set_data時會將資料值寫入,之後就不再有任何互動。
  2. One-Way:
    要寫成{binding propName},set_data後,若當初提供資料來源的Javascript物件變動,該值亦會自動更新。 最主要應用於Master-Detail的情境。
  3. Two-Way:
    一樣是寫成{binding propName},但若同一屬性用在兩個HTML元素上,則更改其中一個,另一個也會跟著變化。

接下來,我們就將前述宣告式AJAX Templates的例子,做一些修改,改裝成Master-Detail形式,並透過Two-Way Binding加上酷炫的編輯功能。

首先,原本的Table維持不變,但要多加一個dataview:sys-key="dataViewObjName"的屬性,如此,便可利用這個變數名稱存取背後的Sys.UI.DataView物件。而這個物件,也將會是Detail View要Bind的對象。

我們建立一個<div>內包<fieldset>做為簡陋的Detail View展示:

    <div
    sys:attach="dataview"
    dataview:data="{binding selectedData, source={{ master }}}"
    class="sys-template" style="font-size: 9pt; width:300px;"
    >
    <fieldset>
    Person: {{Id}} {{Name}} <br />
    Score=<input type="text" value="{binding Score}" />
    </fieldset>
    </div>

值得注意的是,Score分數的部分,我們用了<input>,value則寫成{binding Score} [Updated: 目前版本的解析邏輯不佳,大括號內名稱前後不要插入空白],如此,當數字內容修改,就會反映回來源物件的Score屬性。但原先Table中我們寫的是<td style="text-align: right;">{{Score.format("N2")}}</td>,這是One-Time式Binding,不會即時反應值的變化,因此得改成{binding Score}的格式,但這衍生另一個問題,原來我們對數字做了格式化,加上千位號以及限定小數兩位,binding表示法裡必須直指Score,而不能用Score.format("N2")。要解決這個問題,要造convert函數,其表示法為{binding propName, convert=convertFuncName}。在我們的範例裡,我們新增一個函數fmtNum:

    <script type="text/javascript">
        function fmtNum(value) 
        { return parseFloat(value).format("N2"); }
    </script>

並改寫成<td style="text-align: right;">{binding Score, convert=fmtNum}</td>

 

如上圖所示,按了Id欄位,下方Detail View就會即時顯示所選取項目的內容,而修改Score後,修改後的分數會立即回饋到上方的Master View。

很酷吧!!


Comments

# by ChrisTorng

是否能貼出此段完成後完整的程式碼? 前一個我有做出來,但這個怎麼弄都弄不出來...寫 {{ Score }} 時可以,但只是單向,這 ok。改成 { binding Score } 時就一定出錯,更別說 format...另在修改當中,有時會整個表格都沒顯示,不知道是怎麼回事...覺得偵錯好難喔...:(

# by Jeffrey

to ChrisTorng, 我發現我有個地方寫得不清楚。Detail裡寫了dataview:data="{binding selectedData, source={{ master }}}",則Master <tbody>要加上 dataview:sys-key="master",二者都取一樣的名字(master)才會相互對應。你先試試看是否問題出在這裡?

# by ChrisTorng

這一點我第一次就注意到了...如果這個寫錯的話根本完全不能動... 我現在的狀況是寫 {{ Score }} 可以執行,點上面連結下面就出現內容,修改值後上面不會更新,但點上面其他連結再點回來,下面會看到最新的值,可見該值已經成功改掉了,只差上面沒有更新,也就是沒寫 { binding Score } 的結果,看來是正常的。 一改成 { binding Score } 後,執行出現 js 錯誤:「Microsoft JScript 執行階段錯誤: Sys.InvalidOperationException: A markup extension with the name '' could not be found.」,錯誤點是 MicrosoftAjaxTemplates.debug.js 第 161 行: throw Error.invalidOperation(String.format(Sys.TemplateRes.cannotFindMarkupExtension, name)); 剛剛定下心來追蹤 js 程式,終於發現問題了。 第 377 行的 spaceIndex = extension.indexOf(' '),其中 extension = " binding Score ",因此 spaceIndex = 0。我想到我是寫 { binding Score },改成 {binding Score} 去掉前後空白後,執行即正確了...原來良好的空白分隔習慣也不行啊 :( 我覺得它應該要先 trim() 之後再做 indexOf() 吧... 而下一行寫 if (spaceIndex !== -1),我本來以為是這裡寫錯了,但自己試過結果發現 !== 與 != 的結果是一模一樣的...不知 !== 的寫法有什麼其他意圖? 或者的確是寫錯了,只是 js engine 的執行結果恰好一樣 ? 剛剛才查到,確實有 !== 的語法...可能是為了效率吧...該 js 裡都這麼寫...

# by ChrisTorng

靜態宣告 people 如本文已經可以了,但 WCF 版一直改不出來,想說您接下來應該還會有一篇,會講如何將修改過的值回存 Server 吧...靜待下篇文章...

# by Jeffrey

to ChrisTorng,原來是空白問題,相信嗎? 我跟你做過同樣的事,一樣多加了空白,一樣Trace js才找出問題來。整理文章時一晃神便忘了加註進去,謝謝你的回饋,我想在這個Bug被改好前還會有千千萬萬的人中計,已把這點加註在本文中了。謝謝! 至於Update to WCF,有此計劃,但最近事多,可能會遲到一陣子,多多包涵。

Post a comment