Template
通过使用Template类,可以定义一组html的字符串去渲染ModelData或者Params。其生成的html 字符串里面可以设置占位符,要来作为变量可以设置参数。
一个占位符参数的定义,需要通过{}给包裹起来。一个使用firstName和lastName定义的Template如下:
Template template = new Template("My full name is {firstName} {lastName}.");
接下来,我们需要Params给占位符参数设值
Params data = new Params();
data.set("firstName", "Daniel");
data.set("lastName", "Vaughan");
最后,让template应用data(Params)的值域,需要通过applyTemplate方法
template.applyTemplate(data);
Template可以被预编译,这样就可以节约头期使用正规表达式的时间。实现此功能需要调用Template.compile方法。
在RSSReader项目里,我们要自定义组件——ItemPanel 用来使用Template将一个对象渲染成html。
- 在com.danielvaughan.rssreader.client.components包下,新建ItemPanel extends ContentPanel
package com.danielvaughan.rssreader.client.components;
import com.extjs.gxt.ui.client.widget.ContentPanel;
public class ItemPanel extends ContentPanel {
}
- 新建一个属性——GWT的HTML widget
private final HTML html = new HTML();
- 当然要override onRender方法——设置标题,并将HTMl加入到ContentPanel里
@Override
protected void onRender(Element parent, int index) {
super.onRender(parent, index);
setHeading("Item");
add(html);
}
- 继续在onRender方法里——因为我们希望让ContentPanel能够自适应HTML widget的大小,所以要设置成FitLayout布局方式。当然我们同样的希望设置HTML widget应用一个CSS style,这样我们就可以编辑其样式,来设置HTML的显示方式:
@Override
protected void onRender(Element parent, int index) {
super.onRender(parent, index);
setHeading("Item");
setLayout(new FitLayout());
html.setStylePrimaryName("item");
add(html);
}
- 接下来,我们构造template的字符串——Template接收的是一个String类型的字符串,我们可以使用java标准的StringBuilder类来生成String。我们将占位符参数通过大括号包裹起来。
private String getTemplate() {
StringBuilder sb = new StringBuilder();
sb.append("<h1>{title}</h1>");
sb.append("<p><i>{pubDate}</i></p>");
sb.append("<hr/>");
sb.append("<img src=\"{thumbnailUrl}\"/>");
sb.append("<p>{description}</p>");
return sb.toString();
}
- 新建一个方法displayItem——它会有一个参数为Item。将Item对象里面属性存储的值,赋值给template的占位符,根据item的属性名称和template里占位符的属性名称一一对应。此操作GXT已经给我们提供了Util类,具体实现如下:
public void displayItem(Item item) {
setHeading(item.getTitle());
Template template = new Template(getTemplate());
html.setHTML(template.applyTemplate(Util.getJsObject(item, 1)));
}
- 下面在程序的入口html文件里(RSSReader.html),引用新建的css文件(css/item.css)
<link type="text/css" rel="stylesheet" href="css/item.css">
- 在item.css样式文件里,定义相关其template设置的html标记的样式。
.item h1 {
font-size: 1.5em;
}
.item img {
border: 1px solid #000;
float: left;
margin-right: 10px;
}
.item hr {
border-bottom: 1px solid #000;
}
- 我们现在需要新建一个测试类去实验,让ItemPanel显示item对象的template。在com.danielvaughan.rssreader.client包下,新建TestObjects类,具体实现如下:
package com.danielvaughan.rssreader.client;
import java.util.Date;
import com.danielvaughan.rssreader.shared.model.Item;
public class TestObjects {
public static Item getTestItem() {
Item testItem = new Item();
testItem.setTitle("Computers get more powerful");
testItem
.setDescription("New computers are more powerful "
+ "than the computers that were around a year ago. "
+ "They are also much more powerful than the computers from five years ago. "
+ "If you were to compare current computers with the computers of twenty "
+ "years ago you would fine they are far more powerful.");
testItem.setLink("http://www.example.com/item573.html");
testItem.setPubDate(new Date());
testItem.setCategory("Category");
testItem
.setThumbnailUrl("http://localhost:8888/computers.jpg");
return testItem;
}
}
- 在RSSReader类里,将ItemPanel替换RssMainPanel
//RssMainPanel mainPanel = new RssMainPanel();
ItemPanel mainPanel = new ItemPanel();
mainPanel.displayItem(TestObjects.getTestItem());
- 显示效果如下:
![](http://hi.csdn.net/attachment/201202/9/0_13287654067P65.gif)
在其他components里使用Templates
不单单HTML widget可以使用Template,其他的components也可以使用Template去定义HTML。具体来说,ListField,ComboBox,ToolTipConfig都可以使用Template。
当我们使用ListField或ComboBox的时候,Template的使用是去定义其里面的列表item。例如,使用Template定义多个fields的组合显示,去替换ListField里面的单一的field显示。但是要注意,因为ListField或ComboBox里面都是使用ModelData的结果集,因此需要针对每一个item设置其Template。
Template有一个专门的标签<tpl>,此标记提供一个方法去迭代list组件里面每一个item的,并赋予其template。在了解XTemplate之前,我们会详细了解<tpl>标记的使用。
随着时间的推移,我们会不断的修改RSSReader项目里面FeedList类,让其显示Feed对象里name和部分description内容,取代之前仅仅是显示name字段。虽然我们使用ListField举例,但是其操作过程同样应用与ComboBox component。
- 我们在FeedList类里面,定义一个getTemplate方法,用来返回Template形式的字符串
- 在此方法里,我们需要使用<tpl>用于加工store里面的每个ModelData。
private String getTemplate() {
StringBuilder sb = new StringBuilder();
sb.append("<tpl for=\".\">");
sb.append("</tpl>");
return sb.toString();
}
- 在<tpl>标记之间,我们使用<div>标签来包裹这每一行的显示效果,其css style设置成“x-combo-list-item”
private String getTemplate() {
StringBuilder sb = new StringBuilder();
sb.append("<tpl for=\".\">");
sb.append("<div class='x-combo-list-item'><b>{title}</b> -{description}</div>");
sb.append("</tpl>");
return sb.toString();
}
- ListField同样提供setTemplate方法——在FeedList类中,onRender方法内,替换setDisplayField方法。
//feedList.setDisplayField("title");
feedList.setTemplate(getTemplate());
- 修改后的整个FeedList类如下:
package com.danielvaughan.rssreader.client.lists;
import java.util.List;
import com.danielvaughan.rssreader.client.RSSReaderConstants;
import com.danielvaughan.rssreader.client.services.FeedServiceAsync;
import com.danielvaughan.rssreader.shared.model.Feed;
import com.extjs.gxt.ui.client.Registry;
import com.extjs.gxt.ui.client.data.BaseListLoader;
import com.extjs.gxt.ui.client.data.BeanModel;
import com.extjs.gxt.ui.client.data.BeanModelReader;
import com.extjs.gxt.ui.client.data.ListLoadResult;
import com.extjs.gxt.ui.client.data.ListLoader;
import com.extjs.gxt.ui.client.data.LoadEvent;
import com.extjs.gxt.ui.client.data.RpcProxy;
import com.extjs.gxt.ui.client.event.LoadListener;
import com.extjs.gxt.ui.client.store.ListStore;
import com.extjs.gxt.ui.client.widget.LayoutContainer;
import com.extjs.gxt.ui.client.widget.form.ListField;
import com.extjs.gxt.ui.client.widget.layout.FitLayout;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.rpc.AsyncCallback;
public class FeedList extends LayoutContainer {
private final ListStore<BeanModel> feedStoreR = Registry
.get(RSSReaderConstants.FEED_STORE);
public FeedList() {
setLayout(new FitLayout());
}
private String getTemplate() {
StringBuilder sb = new StringBuilder();
sb.append("<tpl for=\".\">");
sb.append("<div class='x-combo-list-item'><b>{title}</b> -{description}</div>");
sb.append("</tpl>");
return sb.toString();
}
@Override
protected void onRender(Element parent, int index) {
super.onRender(parent, index);
final ListField<BeanModel> feedList = new ListField<BeanModel>();
// 0:从Registry里获得Service
final FeedServiceAsync feedService = (FeedServiceAsync) Registry
.get(RSSReaderConstants.FEED_SERVICE);
// 1:定义proxy在load方法里掉用Serivce里的方法
RpcProxy<List<Feed>> proxy = new RpcProxy<List<Feed>>() {
@Override
protected void load(Object loadConfig,
AsyncCallback<List<Feed>> callback) {
feedService.loadFeedList(false, callback);
}
};
// 2:定义Reader
BeanModelReader reader = new BeanModelReader();
// 3:将proxy和reader传入,定义loader
ListLoader<ListLoadResult<BeanModel>> loader = new BaseListLoader<ListLoadResult<BeanModel>>(
proxy, reader);
// 4:传入loader,生成store,此时还没有load数据
final ListStore<BeanModel> feedStore = new ListStore<BeanModel>(loader);
// 5:将stroe绑定到data-backed component身上
feedList.setStore(feedStoreR);
//feedList.setDisplayField("title");
feedList.setTemplate(getTemplate());
// 6:真正的load数据,load成功之后,data-backed component会自动的显示出来。
loader.load();
loader.addLoadListener(new LoadListener() {
@Override
public void loaderLoad(LoadEvent le) {
feedStoreR.add(feedStore.getModels());
}
});
add(feedList);
}
}
- 运行效果如下: