<div>{$title}</div> <select> {foreach $l in $list} <option value="{$l.Age}">{$l.Name}</option> {/foreach} </select>
生成的aspx頁面: javascript
<%@ Page Language="C#" %> <% if (HttpContext.Current.Items["SMARTY_TEMPLATE_DIR"]==null) { Response.Write("no direct access allowed"); Response.End(); } %> <div><%=DotSmarty.Smarty.GetTemplateArg("title") %></div> <% var list = DotSmarty.Smarty.GetTemplateArg("list") as System.Collections.Generic.IList<SmartyTest.User>; %> <select> <% foreach (var l in list){ %> <option value="<%=l.Age %>"><%=l.Name %></option> <%}%> </select>調用如
Smarty smarty = new Smarty(); List<User> list = new List<User>(); list.Add(new User() { Age=1, Name="name111"}); list.Add(new User() { Age = 2, Name = "name222" }); smarty.Assign("title", "標題"); smarty.Assign("list", list, TemplateArgType.List); smarty.Display("user/userInfo.htm");看起來很像Smarty,可越寫難度越大!唉,能力有限,未來有能力再說吧,如今只能放棄。
前幾天幸運地據說了DotLiquid,網址是:http://dotliquidmarkup.org。上面曰:「DotLiquid is a templating system ported to the .net framework from Ruby’s Liquid Markup.It’s easy to learn, fast and safe"。我想我終於找到了asp.net中的smarty了,更有圖說明: css
這裏先介紹一下她的幾個主要的概念: html
Filter:"Filters are simple methods(過濾器是一些簡單的方法)" java
如標準Filter中的upcase,{{ "looGn" | upcase }} 值爲"LOOGN"。 jquery
Tag:"Tags are used for the logic in your template(標籤用於實現模板中的邏輯)"。 git
如標準Tag中的assign,{% assign freestyle = false %} 定義值爲false的freestyle變量。 github
Block:其實block也是tag,如if..else,for..in, 能夠說Block是有endtag的Tag。 數據庫
如:{% for i in (1..5) %} 編程
{{ i }} c#
{% endfor %}
下面跟你們分享一下這幾天對她的理解及代碼實現:
下載過發佈的壓縮包DotLiquid v1.5.zip裏會有v3.5和v4.0兩個版,引用DotLiquid.dll的對應版本到本身項目便可。其實她真的是easy to learn!看一個handler的代碼:
public void ProcessRequest(HttpContext context) { context.Response.ContentType = "text/plain"; Template template = Template.Parse("模板內容:{{hw}}");//用模板內容作爲參數解析獲得Template對象 string result = template.Render(Hash.FromAnonymousObject(new { hw = "Hello World!" }));//用模板所需的元素作爲參數呈現處理後的結果 context.Response.Write(result); }一切的一切,其實就這兩步!關鍵是!咱們如何應用!"技術不是祕密,祕密是如何善用技術!" 不記得在哪看得一句話,不過無所謂啦!
第一步: 從文件讀入模板內容。模板大多數不是程序中的一個字符串變量,可能存在文件、數據庫裏等,這裏說的是html文件模板。TemplateHelper以下
public static class TemplateHelper { #region Template路徑 static TemplateHelper() { //可用文件配置,例子中用了字典 _map = new Dictionary<string, string>(50); _map.Add("master", "~/template/master.htm"); _map.Add("index_content_main", "~/template/index_content_main.htm"); _map.Add("list_content_main", "~/template/list_content_main.htm"); _map.Add("list_content_script", "~/template/list_content_script.htm"); _map.Add("detail", "~/template/uc/detail.htm"); } #endregion private static Dictionary<string, string> _map; public static bool ContainsKey(string key) { return _map.ContainsKey(key); } public static string GetTemplateURL(string key) { try { return _map[key]; } catch (KeyNotFoundException e) { KeyNotFoundException ne = new KeyNotFoundException(e.Message + "key:" + key); throw ne; } catch (Exception e) { throw e; } } /*這個方法限制了文件模板路徑必要配置,若是須要能夠添加直接以文件路徑爲參數的方法, *不過感受這裏配置起來是個好的習慣 */ public static Template GetFileTemplate(string templateKey, Encoding encoding) { Template template = HttpContext.Current.Cache[templateKey] as Template; if (template == null) { string path = HttpContext.Current.Server.MapPath(GetTemplateURL(templateKey)); template = Template.Parse(File.ReadAllText(path, encoding)); CacheDependency dependency = new CacheDependency(path); HttpContext.Current.Cache.Add(templateKey, template, dependency, Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration, CacheItemPriority.Default, null);//把模板緩存起來 } return template; } public static Template GetFileTemplate(string templateKey) { return GetFileTemplate(templateKey, Encoding.UTF8); } }
第二步:include文件。DotLiquid的include是一個標準Tag,如{% include top %}。調用include須要給Template.FileSystem賦值,它是一個IFileSystem接口,在IFileSystem裏只有一個方法:
public interface IFileSystem { string ReadTemplateFile(Context context, string templateName); }
很顯然,解析到include top時會把top作爲templateName調用Template.FileSystem.ReadTemplateFile方法,以此來實現include。因此實現IFileSystem的類要以templateName參數返回對應的內容:
public class IncludeFileSystem : IFileSystem { private Encoding _encoding = Encoding.Default; public IncludeFileSystem(){} public IncludeFileSystem(Encoding encoding) { _encoding = encoding; } public string ReadTemplateFile(Context context, string templateName) { bool isOptional = false; //是否可選 string templateKey = templateName; if (templateName.EndsWith("_optional")) { isOptional = true; templateKey = templateKey.Replace("_optional", ""); } if (templateKey.StartsWith("content_")) { object ns = context.Environments[0]["ns"]; if (ns == null) { ns = Path.GetFileNameWithoutExtension(HttpContext.Current.Request.RawUrl); } templateKey = ns + "_" + templateKey; } object result = HttpContext.Current.Cache[templateKey]; if (result == null) { if (isOptional && !TemplateHelper.ContainsKey(templateKey)) { return string.Empty; } string path = HttpContext.Current.Server.MapPath(TemplateHelper.GetTemplateURL(templateKey)); result = File.ReadAllText(path, Encoding.UTF8); CacheDependency dependency = new CacheDependency(path); HttpContext.Current.Cache.Add(templateKey, result, dependency, Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration, CacheItemPriority.Default, null); } return result.ToString(); } }這裏的 ReadTemplateFile 有 點複雜,其實就是根據templateName來讀文件,緩存,至於開始的判斷後面再說。在適當的地方設置一下Template.FileSystem 可。Template.FileSystem = new IncludeFileSystem();我寫在了Application_Start事件處理程序裏。
第三步:提供測試數據。下面爲了簡單,沒從數據庫讀取:
public static class WebHelper { public static Family GetFamily() { Family family = new Family() { Host = "孫悟空", Address = "大佛山小石村", Count = 4, Desc = "快樂的一家人!" }; return family; } public static List<Member> GetMembers() { List<Member> members = new List<Member>() { new Member(){ ID=1, Name="孫悟空", Age=42, Sex=true, Relation="本人", Desc="下等戰士,重情重義、毫不欺騙朋友、喜歡幫助人,就算對着敵人也會幫助他。 屢次救了地球和全人類。" }, new Member(){ ID=2, Name="牛琪琪", Age=40, Sex=false, Relation="妻子", Desc="孫悟空和琪琪結婚了,孫悟空沒老可琪琪老了!"}, new Member(){ ID=3, Name="孫悟飯", Age=22, Sex=true , Relation="長子", Desc="擁有極高的潛力,每次遇到危險時都會發揮出強大力量來保護本身。"}, new Member(){ ID=4, Name="孫悟天", Age=15, Sex=true , Relation="次子", Desc="天賦極高,可老想着泡妞!"}, }; return members; } public static Member GetMemberInfo(int id) { return GetMembers().Single(m => m.ID == id); } } public class Family:Drop { public string Host { get; set; } public string Address { get; set; } public int Count { get; set; } public string Desc { get; set; } } public class Member : Drop { public int ID { get; set; } public string Name { get; set; } public int Age { get; set; } public bool Sex { get; set; } public string Relation { get; set; } public string Desc { get; set; } }
第四步:實現母版編程 。一直感受Asp.net中母版很不錯,Asp.net頁面的對象模型來實際母版和用戶控件也是瓜熟蒂落的事。要用DotLiquid的include標籤來實現母版應該怎麼作呢?這裏說說個人解決方法。先看母版模板文件master.htm
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>{{title}}</title> <link href="/css/style.css" rel="stylesheet" type="text/css" /> <script type="text/javascript" src="/js/jquery-1.6.1.min.js"></script> {% include content_script_optional %} </head> <body> <div id="root"> <div class="top"> <h2> Top in master</h2> </div> {% include content_main %} <div class="bottom"> <h2> Bottom in master </h2> </div> </div> </body> </html>
對,關鍵仍是在templateName上,"content_main"和"content_script_optional" 中的"content"和"optional"是兩個約定,還記得IncludeFileSystem.ReadTemplateFile方法吧,就是有點複雜的那個!content指明這裏是包含"內容模板(名詞來自內容窗體)",optional指明這個include能夠沒有,模板裏到這就能夠了,再看ReadTemplateFile方法有個object ns;默認是請求文件名,由於a頁面能夠用master.htm,b頁面也能夠用master.htm,ns默認就是"a"或"b",這樣作只是爲了找到a或b的模板。假如index_content_main.htm首頁模板:
<div class="center"> <table class="tb" style="width:400px;"> <caption>家庭:{{f.Host}} <a href="/list.ashx">查當作員</a> </caption> <tr> <th>戶主:</th><td>{{f.Host}}</td> </tr> <tr> <th>地址:</th><td>{{f.Address}}</td> </tr> <tr> <th>家庭成員數:</th><td>{{f.Count}}</td> </tr> <tr> <th>描述:</th><td>{{f.Desc}}</td> </tr> </table> </div>
index.ashx爲訪問接口:
public void ProcessRequest(HttpContext context) { context.Response.ContentType = "text/plain"; Template template = TemplateHelper.GetFileTemplate("master"); string html = template.Render(Hash.FromAnonymousObject(new { title = "dotliquid demo index",f=WebHelper.GetFamily()})); context.Response.Write(html); }嘿嘿,這裏有點怪吧,代碼裏看不出來和index頁面有半點關係,template是根據master.htm獲得的。這主要歸功於那兩個約定和一個ns默認值,若是把index.ashx更名爲abc.ashx,上面呈現的代碼就要這樣寫了:
string html = template.Render(Hash.FromAnonymousObject(new {ns="index" , title = "dotliquid demo index",f=WebHelper.GetFamily()}));
這幾步到些完結!完整例子會附下載!
擴展:還記得代碼圖右邊的幾個主要概念吧,下面是三個例子,我寫的一個,他們的兩個!
public class TemplateFilters { public static string Text(bool? input, string trueText, string falseText, string nullText) { if (input == null) return nullText; return input == true ? trueText : falseText; } public static string Text(bool input, string trueText, string falseText) { return input ? trueText : falseText; } }這 個是本身的Filters類,裏面的每一個方法均可以是一個Filter,{% true | text: "男" , "女" %} 此值爲"男" ,調用第二個重載,text爲方法名稱(注意大小寫),true是第一個參數input,"男"是第二個參數trueText,"女"是第三個參數 falseText。Filter要註冊纔可用,Template.RegisterFilter(typeof(TemplateFilters)); 這個我也寫在Application_Start。
public class Rd : Tag //隨機數 { int _max; public override void Initialize(string tagName, string markup, List<string> tokens) { base.Initialize(tagName, markup, tokens); _max = Convert.ToInt32(markup); } public override void Render(Context context, StreamWriter result) { result.Write(new Random().Next(_max).ToString()); } } public class Scale : Block//機率出現,用的單詞應該不當!~ { int _max; public override void Initialize(string tagName, string markup, List<string> tokens) { base.Initialize(tagName, markup, tokens); _max = Convert.ToInt32(markup); } public override void Render(Context context, StreamWriter result) { if (new Random().Next(_max) == 0) base.Render(context, result); } }
仍是要先註冊,Template.RegisterTag<Rd>("rd");Template.RegisterTag<Scale>("scale");
用法分別是 {% rd 10 %} 值爲0到10任意一個數字, {% scale 10 %}這裏有10%的機率出現{% endscale %}。