1、<vt:template>與<vt:include>標簽的不同
<vt:template>和<vt:include> 標簽都包含file屬性,如果這兩個標簽都設置file屬性,那這兩個標簽看起來很相似,並且最終效果都是將文件的內容包含進來。但是對於模版引擎來說它們之間的差別卻是非常的大。
<vt:template>標簽是“模版塊”標簽,它能擁有自己的“變量”,它會成為它內部的標簽的“宿主模版”(OwnerTemplate)。而<vt:include>則只是簡單的將文件內容包含進來,它內部的標簽的“宿主模版”與它相同。
現假如有一個VT模版文件: inc_content.html
我是包含文件裡的變量 {$:#.var1}。
我是包含文件裡的foreach標簽:
<vt:foreach from=”#.names” item=”name” index=”i”>
包含文件的第{$:#.i}個名字叫{$:#.name}。
</vt:foreach>
現分別用<vt:template> 和 <vt:include>標簽去包含上面的文件,如下:
A、<vt:template>包含:
我是外部的變量{$:#.var1}。
我是外部的foreach標簽:
<vt:foreach from=”#.names” item=”name” index=”i”>
外部的第{$:#.i}個名字叫{$:#.name}。
</vt:foreach>
<vt:template id=”inc” file=”inc_content.html” />
B、<vt:include>包含:
我是外部的變量{$:#.var1}。
我是外部的foreach標簽:
<vt:foreach from=”#.names” item=”name” index=”i”>
外部的第{$:#.i}個名字叫{$:#.name}。
</vt:foreach>
<vt:include id=”inc” file=”inc_content.html” />
上面兩塊的VT模版代碼看起來很相似,但是經解析後A中的var1與inc這個<vt:template>模版塊下的變量var1分別獨立存在,互不影響!而B中的變量var1與inc這個<vt:include>的變量var1相等,都是引用同一個變量(其它變量類似)。
現假如A、B兩塊的VT模版代碼都通過過下面的程序來處理:
this.Document.Variables.SetValue("var1", 1);
this.Document.Variables.SetValue("names", new string[] { "張三", "李四", "王五" });
也即是只對外部變量var1、names賦值,最終經模版引擎解析輸出後,它們的輸出結果如下:
從圖可知,<vt:template>包含的沒有數據輸出,而用<vt:include>包含則有數據輸出並且和外部數據一模一樣!所以可把<vt:template>標簽看成是程序語言裡的類,它能擁有它自己的變量,改變外部變量的值不會影響到其內部的同名變量,並且外部標簽可通過其id獲取其內部變量!
具體的示例代碼,請參考:http://net-vtemplate.googlecode.com/svn/src/VTemplate.WebTester/template_include_test.ashx.cs
2、使用變量表達式
變量表達式可用在標簽屬性,也可用在變量元素中。它的作用就是用於獲取變量中某個字段、屬性、函數方法或索引的結果值。比如上例中的#.var1就是說明獲取var1變量的值,也即是數值“1”。
對於變量值類型中真實存在的字段、屬性或函數方法,VT模版引擎將通過反射獲取其結果值,例如以下VT模版代碼:
我叫{$:user.name},今年{$:user.age}歲,我來自{$:user.location.getcity()}
假如對user變量賦以下類實例的值,那麼上面模版代碼解析時模版引擎將能正確解析出各個變量表達式的最終值。
class Location
{
public string GetCity(){
//code here
}
}
class User
{
public string Name { get; set; }
public int Age { get; set; }
public Location Location { get; set; }
}
但在某些情況下,我們需要獲取的“值”並不簡單地存在變量值的類型中,而是需要經過其它處理運算得出來的值。比如上面的獲取個人資料裡,我們還要獲取用戶的個人財產總額,但從上面的代碼裡可看出個人財產總額項並不存在於User類裡,所以導致VT模版引擎根本無法獲取此項的值。那我們要如何做才能獲取此項數據呢?VT模版引擎提供了一個手動設置變量表達式的值的方法,而我們要做的就是根據此方法手動設置變量表達式的值!例如上面的VT模版代碼改為如下:
我叫{$:user.name},今年{$:user.age}歲,我來自{$:user.location.getcity()},我的個人財產總共有{$:user.totalmoney}元。
從上面的類實例代碼中可知道totalmoney這個項是不存在User的屬性/字段列表裡的,所以我們就要手動設置{$:user.totalmoney}的值,示例代碼如下:
/// <summary>
/// 返回某個用戶的個人財產總額
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
static int GetUserTotalMoney(User user)
{
//code here
}
//------------------------使用代碼----------------------------------------//
//獲取user變量
Variable userVar = this.Document.Variables["user"];
//生成User實例
User user = new User();
//…………其它代碼略去…………//
//設置user變量的值為User實例
userVar.Value = user;
//手動設置totalmoney的值(注意,這行和上面那行的順序不能搞亂)
userVar.SetExpValue("totalmoney", GetUserTotalMoney(user));
3、有條件的控制數據的輸出
在輸出數據時,我們並不是簡單的輸出所有數據,而是要根據外部的許多條件組合獲取其中的部分數據。而對於這些外部條件,如果可固定的則我們可以在設計VT模版時將其寫入到標簽(建議是<vt:template>標簽)的屬性裡,這樣我們就能在程序代碼裡獲取到這些外部條件並加以處理數據。
例如博客園的新聞頻道裡右邊的“相關新聞”、“熱點新聞”兩欄數據,如下圖:
假設“相關新聞”裡獲取的新聞是屬於"relating”類型的新聞,而“熱點新聞”則是獲取屬於"hoting”類型的新聞,則我們可以設計其VT模版如下:
<div class="side_block">
<h3 class="title_blue">相關新聞</h3>
<vt:template name="topnews" type="relating" file="cnblogs_newsdata.html" />
</div>
<div class="side_block">
<h3 class="title_yellow">熱點新聞</h3>
<vt:template name="topnews" type="hoting" file="cnblogs_newsdata.html" />
</div>
在上面的VT模版中,定義了兩個name為"topnews”的<vt:template>標簽,這是為了便於在代碼裡對這兩個<vt:template>進行統一處理(因為它們要處理的數據都是相同,只是獲取數據條件不同)而定義的名稱。並且分別定義了自定義屬性type用於做數據獲取條件。其中包含文件cnblogs_newsdata.html的VT模版如下:
<ul class="topnews block_list bt">
<vt:foreach from="#.newsdata" item="news" index="i" id="newslist">
<li>
<a href="{$:#.news.url}" title="{$:#.news.title htmlencode='true'}">{$:#.news.title htmlencode='true'}...</a>
</li>
</vt:foreach>
</ul>
在此文件的VT模版中,定義了一個id為"newslist"的<vt:foreach>標簽,定義此id是為了在程序代碼裡控制新聞的輸出和處理每條新聞的訪問地址,也即是"{$:#.news.url}”變量表達式的值。
示例代碼:
//獲取所有名稱為topnews的模版塊
ElementCollection<Template> templates = this.Document.GetChildTemplatesByName("topnews");
foreach (Template template in templates)
{
//根據模版塊裡定義的type屬性條件取得新聞數據
List<News> newsData = GetNewsData(template.Attributes.GetValue("type"));
//設置變量newsdata的值
template.Variables.SetValue("newsdata", newsData);
//取得模版塊下Id為newslist的標簽(也即是在cnblogs_newsdata.html文件中定義的foreach標簽)
Tag tag = template.GetChildTagById("newslist");
if (tag is ForEachTag)
{
//如果標簽為foreach標簽則設置其BeforeRender事件用於設置變量表達式{$:#.news.url}的值
tag.BeforeRender += (sender, e) =>
{
ForEachTag t = (ForEachTag)sender;
//取得當前項的值(因為foreach標簽的數據源是List<News>集合,所以當前項的值類型為News實體)
News news = (News)t.Item.Value;
//設置當前項的變量表達式的值.也即是"{$:#.news.url}"變量表達式
t.Item.SetExpValue("url", GetNewsUrl(news));
};
}
}
在上面代碼中使用了BeforeRender事件,此事件是在標簽元素的數據呈現之前觸發。對於循環元素<vt:foreach>和<vt:for>,因為每次循環時都會呈現數據,也就導致每次循環時都會觸發此事件(包括AfterRender事件),所以我們就可通過此事件方法獲取到循環當前項的值。
具體的示例代碼,請參考:http://net-vtemplate.googlecode.com/svn/src/VTemplate.WebTester/cnblogs_newslist.ashx.cs