本文章是由機器翻譯。

技術最前線

ASP.NET AJAX 4.0 中的條件式轉譯

Dino Esposito

用戶端呈現是刺激目前為止最,而您在 ASP.NET AJAX 4 中找到的長時間 awaited、 新功能。 用戶端呈現可讓您使用 HTML 範本來定義您想要的版面配置,並提供執行階段資料的預留位置文字基礎的語法。 所有它看起來像是伺服器端的資料繫結不同之處在於花與下載透過 Web 服務的外部資料在用戶端瀏覽器中的位置。

上個月我會涵蓋新的 DataView 用戶端控制項和繫結技巧,最常使用的基本概念。 在這篇文章我移一個步驟進一步和封面條件式範本呈現。

條件式範本呈現 

在 ASP.NET AJAX 4 HTML 範本進行資料繫結 」 是標記的一段可能包含 ASP.NET 標記、 HTML 常值和執行階段資料的某些預留位置。 呈現演算法是相當簡單:繫結至這類範本,DataView 控制項擷取一些資料,並使用的填滿該範本。 產生的標記與實際的資料取代預留位置,接著會顯示代替原始的 HTML 範本。

有兩種方式,您可在其中建立 DataView 控制項的執行個體:以宣告方式或以程式設計的方式。 不過,用來產生標記演算法保持不變。 這是其中我們在幾留在上個月 ’s 發行項。

往,問題 springs 自然。 如果需要一些邏輯呈現範本? 如果您需要產生不同的標記根據不同的執行階段條件的條件式呈現吗? 某些用戶端程式碼必須糾結與標記,以檢查以及其他通用的 JavaScript 物件的狀態進行繫結的資料項目的值。

ASP.NET AJAX 4 定義一組特殊 namespaced 屬性透過它您必須附加自訂行為至 HTML 範本。 這些行為都 JavaScript 運算式來評估並執行特定階段呈現程序。 圖 1 列出預先定義的程式碼屬性的條件式範本呈現。

程式碼屬性辨識的範本產生器,而且其內容適當地用於呈現程序。 的 圖 1 中的屬性可以附加至 ASP.NET AJAX 範本中使用任何 DOM 項目。

請注意 的 圖 1 中的屬性已在程式碼範圍:往上直到預覽 4 ASP.NET AJAX 4 程式庫與 Visual Studio 2010 Beta 1 的命名空間。 開始預覽 5 ASP.NET 團隊消除程式碼:命名空間,和它使用只 sys:命名空間的所有項目。

圖 1 屬性條件式呈現的 DOM 項目,在 「 範本

條件式呈現作用中

布林 (Boolean) 運算式,會指派 sys:if 屬性。 如果運算式傳回 true,然後項目則會呈現 ; 否則演算法會繼續進行下一個步驟。 這裡 ’s 一個普通的範例只是為了快速說明此點:

<div sys:if="false">
:
</div>

在處理這個標記時建立幫手只會忽略 DIV 標記和所有它的內容。 以特定方式 sys:if 屬性可用來在開發階段註解部分的範本。 所有在所有指定常值為 false sys:if 屬性是不多了不同於執行下列 C# 中:

if (false)
{
  :
}

設定為 false 值 sys:if,doesn’t 完全隱藏 HTML 項目。 請注意任何 ASP.NET AJAX 範本一開始視為一般的 HTML 瀏覽器。 這表示任何範本完全處理文件物件模型 (DOM) 樹狀結構。 不過,為 ASP.NET AJAX 範本是以裝飾 sys 範本] 屬性沒有任何 DOM 樹狀架構會顯示當顯示網頁。 在實際上 sys 範本屬性是包含下列的 CSS 類別:

.sys-template { display:none; visibility:hidden; }

sys:if 屬性保留 HTML 項目範本的實際標記關閉。 如果附加到任何 ASP.NET AJAX 範本以外的 HTML 項目,則會忽略 sys:if 屬性。 sys:if 屬性未與其他分支目前關聯。

如果定義,sys:codebefore 和 sys:codeafter 屬性執行之前和之後的 HTML 項目呈現。 兩個屬性可以用於一起或個別如它最適合您。 屬性的內容必須是可執行的 JavaScript 程式碼片段。

完全,程式碼屬性提供您足夠的電力來應付幾乎每個可能的情況即使雖然不一定有簡單的解決方案。 let’s 考慮較不 trivial 範例 sys:if 及 sys:codebefore。

藉由到最您可能已經注意到一些怪異 $ 前置字元變數在上述程式碼中。 讓我簡短地介紹它們之前引入範例。

範本 pseudo-variables

在程式碼屬性的範本中使用自訂程式碼中,,您可以完整的存取之資料項目的公用屬性的集合。 在另外您可以存取某些額外變數定義和公開的範本建立幫手,為了方便起見。

目前,文件作為 「 虛擬資料行 」 指向它們,但我個人喜歡詞彙 「 pseudo-variable 」。圖 2 列出全部。

這些 pseudo-variables 提供多樣的轉譯引擎的內部狀態的概略檢視其正常運作。 您可以使用任何這類變數會在 ASP.NET AJAX 範本中使用 JavaScript 的任何變數。

圖 2 Pseudo-Variables 支援 ASP.NET AJAX 範本產生器的

資料表中著色相符的資料列

做為範例 let’s 考慮顯示一份客戶清單再加上一個下拉式清單中挑選一個國家/地區的網頁。 只要選取新國家/地區的客戶清單,重新整理呈現客戶從該國家/地區以不同的樣式 (請參閱 的 圖 3)。


圖 3 與設定格式化的條件的樣板的範例 ASP.NET AJAX 網頁呈現

圖 4 顯示範例頁面的標記。 您可以看到資料頁就是內容頁面與主版頁面相關聯。 在主版頁面中定義的主體標記中宣告必要的系統命名空間。

圖 4 的 動作] 中的程式碼屬性

<asp:Content ContentPlaceHolderID="PH_Body" runat="server">
<asp:ScriptManagerProxy runat="server" ID="ScriptManagerProxy1">
<Scripts>
<asp:ScriptReference Name="MicrosoftAjax.js"
Path="~/MicrosoftAjax.js" />
<asp:ScriptReference Path="~/MicrosoftAjaxTemplates.js" />
</Scripts>
</asp:ScriptManagerProxy>
<div>
<asp:DropDownList ID="listOfCountries" runat="server"
ClientIDMode="Static"
onchange="listOfCountries_onchange()">
</asp:DropDownList>
<table id="gridLayout">
<tr>
<th>ID</th>
<th>COMPANY</th>
<th>COUNTRY</th>
</tr>
<tbody id="grid" class="sys-template">
<tr sys:if="$dataItem.Country != currentCountry">
<td align="left">{{ ID }}</td>
<td align="right">{{ CompanyName }}</td>
<td align="right">{{ Country }}</td>
</tr>
<tr sys:if="$dataItem.Country == currentCountry"
class="highlight">
<td align="left"
sys:codebefore="if($dataItem.Country == 'USA') {
$element.style.color = 'orange';
$element.style.fontWeight=700;
}">
{{ ID }}
</td>
<td align="right">{{ CompanyName }}</td>
<td align="right">{{ Country }}</td>
</tr>
</tbody>
</table>
</div>
</asp:Content>

您應該注意的 ASP.NET AJAX 預覽 5 要求您提供 ScriptManager 控制項和 Beta 1 許多 MicrosoftAjax.js 檔案會覆寫。 這是暫時的修正程式,將不再是必要的組件更新以 Beta 2,然後放開製造。

老人與 ASP.NET AJAX 範本 grips 讓我之前專注於標記程式碼的下拉式清單控制項。

設定下拉式清單

下拉式清單中的國家/地區的程式碼是,如下所示:

<asp:DropDownList ID="listOfCountries" runat="server" 
     ClientIDMode="Static" 
     onchange="listOfCountries_onchange()">
</asp:DropDownList>

您可以看到此控制項將值指派給新 ClientIDMode 屬性,並提供用戶端處理常式的 DOM 層級 onchange 事件。 控制項已繫結至其資料精確地在傳統的 Page_Load 方法在伺服器上中:

protected void Page_Load(object sender, EventArgs e)
{
   if (!IsPostBack)
   {
      // Fetch data
      string[] countries = new string [] {"[All]", "USA", ... "};

      // Serialize data to a string
      string countriesAsString = "’[All]’, ‘USA’, ...’";

      // Emit the array in the page
      this.ClientScript.RegisterArrayDeclaration(
           "theCountries", countriesAsString);

      // Bind data to the drop-down list
      listOfCountries.DataSource = countries;
      listOfCountries.DataBind();
   }
}

繫結程序鉸在兩個步驟。 第一次,JavaScript 陣列發出包含相同的資料,DropDownList 控制項以程式設計的方式繫結的回應中。 接下來,傳統伺服器端的資料繫結會發生。

這種技巧稱為雙側樣板化,是標準的用戶端資料繫結模式的一種變化。 差異都包含事實資料,以繫結會擷取在伺服器第一次網頁存取並提供服務給用戶端做為內嵌的 JavaScript 陣列中。

進一步證明有必要的用戶端資料繫結可以再進行使用內嵌的陣列。 以此方式基本上儲存來取得資料的額外往返。 顯示靜態 don’t 變更使用者互動期間的資料時,這樣的傳統用戶端資料繫結的變化就特別有用。 在範例中,我這項技術只能用於取得國家/地區清單 ; 從使用 Web 服務的 Web 伺服器讀取改,客戶清單]。

使用伺服器端控制項發出的 HTML 標記時您可能可以少控制實際的識別碼,如果使用主版頁面。 在 ASP.NET AJAX 4 新 ClientIDMode 屬性會提供更有彈性的方式處理問題。

在特別如果您在此範例中設定靜態的 ClientIDMode,然後用戶端 ID 的 HTML 項目會符合完全伺服器識別碼。 你們重複的資料繫結範本化控制項內容中該伺服器控制項時,無法有用這一輪。

下列的指令碼處理下拉式清單中選取事件的變更:

<script language="javascript" type="text/javascript">
    var currentCountry = "";

    function listOfCountries_onchange() {
        // Pick up the currently selected item
        var dd = $get("listOfCountries");
        currentCountry = dd.options[dd.selectedIndex].value;
        
        // Refresh the template
        refreshTemplate();
    }

    function refreshTemplate() {
        var theDataView = $find("grid");
        theDataView.refresh();
    }
</script>

請注意這段程式碼會引發一個 JavaScript 錯誤是否 don’t 將 DropDownList 控制項 ClientIDMode 屬性設定為靜態。 這是因為識別碼改變的工時,ASP.NET 通常不會以確保使用主版頁面時每個產生的 HTML 項目具有唯一的識別碼。

前述 onchange 事件處理常式會將目前選取的國家/地區的名稱儲存到全域的 JavaScript 變數,然後重設 ASP.NET AJAX 範本。 let’s 現在專注於範本。

條件式範本

範本建立並填入以程式設計方式如下:

<script language="javascript" type="text/javascript">
   function pageLoad()
   {
      dv = $create(Sys.UI.DataView,
              {
                 autoFetch: true,
                 dataProvider: “/ajax40/mydataservice.asmx",
                 fetchOperation: “LookupAllCustomers"
              },
              {},
              {},
              $get(“grid")
       );
    }
</script>

DataView 用戶端控制會指定的 Web 服務呼叫、 執行給定的提取運算,並使用任何傳回的資料來填滿 ASP.NET AJAX 範本生根在 DOM 元素名為 「 格線 」。

使用範例 Web 服務上繫結至 LookupAllCustomers 方法的傳回值是 DataView 執行個體呈現整體的範本。 此方法會傳回屬性如識別碼]、 [供應商] 和 [國家地區的客戶物件的集合。

範本會保持整個存留期 (Lifetime) 無論可能會發生資料變更頁面的繫結至其資料。 該怎麼辦如果改,您只是要修改的範本呈現 — 沒有資料重新整理侷 — 如特定的執行階段條件變更嗎? 若要取得這,您需要在範本中插入程式碼屬性。

您真的需要什麼這裡是不會驗證呈現一種方式,如果驗證指定的條件的範本及其否則如果條件為真的條件式呈現。 如所述之 sys:如果屬性 doesn’t 支援 「 如果-然後-其他 」 語意,以及所有它會呈現,或忽略根據布林守衛值其父項目。

可能的因應措施,來模擬兩個分支的條件包含在使用範本的兩個互斥的部份,每一個由不同的布林 (Boolean) 運算式來控制。 的 圖 4 中還顯示,程式碼會依照以下結構描述:

<tr sys:if="$dataItem.Country != currentCountry">
  :
</tr>
<tr sys:if="$dataItem.Country == currentCountry" 
    class="highlight">
  :
</tr>

變數 currentCountry 是全域的 JavaScript 變數包含目前選取的國家/地區名稱。 變數會更新每次 onchange 事件被引發的伺服器端 DropDownList 控制項的 HTML 標記。

在前述的程式碼片段中前者的 TR 項目會呈現有條件地根據進行繫結的資料項目的國家/地區屬性的值。 如果變數符合選取的國家/地區,前者 TR 會略過。 這種行為會依賴全域變數初始化為空字串,而且後續 doesn’t 符合任何值。 結果資料表資料列範本一開始會呈現任何傳回的客戶。

當使用者在下拉式清單中進行選擇,全域 currentCountry 變數會更新。 不過,這個動作 doesn’t 自動觸發範本上的任何重新整理如 的 圖 3。 重新整理的範本必須明確命令 onchange 事件處理常式中。 這裡 ’s 執行操作之的可能方法:

var theDataView = $find("grid");
theDataView.refresh();

$ 尋找函式是速記查閱函式可在 Microsoft AJAX Library中擷取的元件執行個體。 使用 $ 尋找 (或 $ get) 您必須有 ScriptManager 控制項在工作和參考 MicrosoftAjax.js 核心程式庫的方式來設定。  一旦您有擷取與 「 格線 」 範本相關的 DataView 例項,您只會叫用其重新整理方法。 在內部,方法只是 recompiles 範本並更新 [DOM. 請注意 don’t 嚴格需要從已註冊的元件清單擷取 DataView 執行個體。 您也可以將 DataView 執行個體儲存全域變數建立在載入頁面時:

var theDataView = null;
function pageLoad()
{
   theDataView = $create(Sys.UI.DataView, ...); 
   :
}

接下來,onchange 處理常式中您只需重新整理方法呼叫全域執行個體上:

theDataView.refresh();

在此第一個範例我可以使用下拉式清單來呈現的使用者介面部分負責觸發其餘的頁面上所做的變更。 下拉式清單項目是特定的因為它會併入邏輯,以引發變更事件,其元素之一選取時。

ASP.NET AJAX 不過,提供更一般的機制,導致網頁特定作業的觸發程序變更/通知事件。 let’s 重設先前的範例使用一般的手做清單,而不下拉式清單。

sys:command 屬性

國家/地區清單現在會產生使用 HTML 標記為未排序的項目符號清單。 範本是,如下所示:

<fieldset>
   <legend><b>Countries</b></legend>
   <ul id="listOfCountries" class="sys-template">
       <li>
           {{ $dataItem }}
       </li>
   </ul>
</fieldset>

範本以程式設計的方式會附加至 DataView 控制項來呈現之用。 要填滿範本資料提供透過內嵌的 JavaScript 陣列。 從 Page 類別上使用 ClientScript 物件服務伺服器發出 JavaScript 陣列,包含國家/地區的清單。 與前一個範例不同 Page_Load 程式碼 doesn’t 包括伺服器端繫結作業:

protected void Page_Load(object sender, EventArgs e)
{
   if (!IsPostBack)
   {
      string[] countries = new string [] {"[All]", "USA", ... "};
      string countriesAsString = "’[All]’, ‘USA’, ...’";
      this.ClientScript.RegisterArrayDeclaration(
           "theCountries", countriesAsString);
   }
}

當載入頁面會在用戶端上第二個 DataView 控制項執行個體化。 的 [圖 5] 所示,這裡 ’s JavaScript pageLoad 函式修改過的程式碼。

圖 5 的 JavaScript pageLoad 函式

<script language="javascript" type="text/javascript">
    function pageLoad()
    {
        $create(Sys.UI.DataView,
            {
                autoFetch: true,
                dataProvider: "/ajax40/mydataservice.asmx",
                fetchOperation: "LookupAllCustomers"
            },
            {},
            {},
            $get("grid")
        );
        $create(Sys.UI.DataView,
            {
                autoFetch: true,
                initialSelectedIndex: 0,
                selectedItemClass:"selectedItem",
                onCommand: refreshTemplate,
                data:theCountries
            },
            {},
            {},
            $get("listOfCountries")
        );
    }
</script>

您可以看到用來將國家/地區繫結至 UL 架構的樣板的第二個 DataView 有比另一個相當不同結構。

第一個不同的是 [資料] 屬性用來匯入資料。 當使用內嵌的資料時,這會是正確的程序。

當資料來源的使用者定義的物件陣列,您執行透過 {{運算式}} 語法的繫結。 運算式的內容通常是資料項所公開的公用屬性的名稱。 在這個範例來代替,資料繫結的來源是] 一般字串陣列。 接下來,資料項目是一個包含沒有公用的屬性,以在繫結運算式中參考之字串。 在這種情況下您依靠所示:

<ul>
  <li>{{ $dataItem }}</li>
</ul>

[initialSelectedIndex] 和 [selectedItemClass] 屬性設定 [DataView 預期的行為,如重點在於顯示的項目選取到目前為止。

DataView 可以將附加範本的選取範圍內建的行為。 以 initialSelectedIndex 所指定位置項目將樣式根據至透過 selectedItemClass 屬性設定的 CSS 類別。 如果您 don’t 想在第一個顯示上進行任何選取項目,您可以將設為-1 的 initialSelectedIndex。

將範本從結果清單是純 UL 清單,且為等它 doesn’t 原本就加入任何邏輯來處理選取項目,如下所示:

<ul>
  <li>[All]</li>
  <li>USA</li> 
  :
</ul>

使用範本內的 HTML 項目上的 [sys:command 屬性],來指示範本產生器,動態地將附加事件處理常式是一堆到項目,如下所示:

<ul id="listOfCountries" class="sys-template">
    <li sys:command="select">
        {{ $dataItem }}
    </li>
</ul>

圖 6 顯示如何修改過的 LI 同類項出現在網際網路總管 8 的 [開發 o 人 h 員 û 工 u 具 ã] 視窗。 sys:command 屬性採用代表觸發命令的名稱的字串值。 名稱實際上是給您。 命令所按一下項目上觸發。 常用的命令會選取、 刪除、 插入和更新。 觸發命令時 [DataView 就會引發 onCommand 事件。 這裡 ’s 處理 onCommand 事件並重新整理範本之程式碼:

<script type="text/javascript">
    var currentCountry = "";
    function refreshTemplate(args) 
    {
      if (args.get_commandName() == "select") 
      {
        // Pick up the currently selected item
        currentCountry = args.get_commandSource().innerHTML.trim();
                
        // Refresh
        var theDataView = $find("grid");
        theDataView.refresh();
      }
    }
</script>


圖 6 事件處理常式動態新增為 sys:command 效果

相同的方法,只要發出直接在為下列的 HTML 中可用於下拉式清單:

<select>
  <option sys:command="select"> {{ $dataItem }} </option>
</select>

不過,注意 Bug 會防止如預期般在 Beta 1 中執行前面的程式碼。 (圖 7在 範例中顯示網頁的動作)。


圖 7 指令用來處理選取範圍

HTML 屬性

在 ASP.NET AJAX 4,特殊堆 sys:屬性指定 HTML 屬性的臨機操作繫的結。 功能上說話時,這些屬性就像是 HTML 屬性,並 won’t 注意不同的行為。 圖 8 列出 namespaced HTML 屬性。

在範本中的所有項目屬性可以作為前置詞 「 sys:命名空間。 因此什麼 ’s 最終理由使用對應屬性嗎? 為什麼會只有幾個它們會列在 的 圖 8

圖 8 對應的 HTML 屬性

通常您想要繫結至 HTML 屬性,但 don’t 要將本身的屬性設定為 {...} 繫結運算式。 DOM 觀點繫結運算式是只要值指派給屬性,因此處理]。 這的當然可能有一些差的副作用。 比方說如果您繫結至值屬性的輸入項目或項目的內容,繫結字串可能會出現給使用者的第二個如載入頁面。 如果您使用的範本 (亦即,即時繫結或雙向繫結) 之外的繫結運算式,會發生相同情況。 在另外有一大堆 HTML 屬性 — 那些 的 圖 8 中所列 — 繫結運算式的使用可能會產生不想要的效果。 例如,請考量下列標記:

<img src="{{ URL }}" />

它會觸發字串 「 URL 」 的要求而非屬性 URL 資料項目上的值。

其他您可能面對的問題包括瀏覽器 XHTML 驗證問題和錯誤的一般屬性解析度。 如果您做前置字元這類重要屬性 sys 命名空間會解決問題。

因此最好的作法是永遠與 sys 命名空間首碼正在指定繫結運算式的任何屬性。 DOM doesn’t 在意 namespaced 屬性,所以屬性保留其繫結運算式與沒有副作用,直到處理由範本產生器。

即使它們不是以外的地方它們可以節省您的錯誤 HTML 剖析效果的情況下肯定強制,Namespaced 屬性,建議您在用戶端呈現。

全新的世界

範本和資料繫結打開全新的世界的 ASP.NET AJAX 的開發人員的可能性。 下個月我會回到涵蓋各種類型的繫結包括即時的繫結和主要/詳細檢視。

Dino Esposito 是架構設計人員,在 IDesign 和 co-author 的 「 Microsoft.NET:架構企業的應用程式 」 (Microsoft 按,2008年)。 目前居住在義大利,並常在世界各地的產業活動與會議發表演說。 您可以加入他的部落格,網址是:weblogs.asp.net/despos