编辑模式与持久化
通常来说,iWidget 定义文件相当于给出了一个 iWidget 模板,从这个模板出发可以在页面上实例化出多个 iWidget 。每个 iWidget 实例都允许进行一定的配置,从而满足用户的需求。比如一个订阅源(feed)阅读器 iWidget 允许用户配置订阅源 URL,一个显示地图的 iWidget 允许用户配置默认的地图中心位置等。本章主要讨论如何通过编辑模式来允许用户配置 iWidget 并通过 ItemSet 将配置保存下来。
iWidget 模式
根据 iWidget 规范,每个 iWidget 可以支持多种模式(mode)。每种模式对应的 HTML 片断的内容有所不同。iWidget 运行环境应该提供界面上的操作来允许用户切换 iWidget 的模式,与此同时,iWidget 本身也需要 API 的支持来切换自身的模式。
清单 2 中 iwidget 元素的属性 supportedModes 和 mode 分别用来声明 iWidget 支持的模式以及默认的模式。目前 iWidget 可以支持的模式有:
典型的应用场景是 iWidget 默认显示查看模式,用户通过界面操作切换到编辑模式,完成配置修改之后,再切换回查看模式并反映配置的修改。对于每个 iWidget 支持的模式,都需要提供与之对应的 HTML 片断。这是通过多个 content 元素来实现的。content 元素的 mode 属性声明了其对应的模式。下面说明如何为 HelloWorld iWidget 添加编辑模式。
为 HelloWorld 添加编辑模式
当前的 HelloWorld iWidget 只能发出英语的问候。下面要做的是让用户可以选择问候所使用的语言,这样就能发出中文、法文和日文等的问候。首先需要添加编辑模式的 HTML 片断内容。清单 5 中给出了 HTML 片断的内容。
清单 5. HelloWorld 编辑模式的 HTML 片断
<iw:iwidget
name="HelloWorld"
xmlns:iw="http://www.ibm.com/xmlns/prod/iWidget"
supportedModes="view edit"
mode="view"
iScope="HelloWorld"
lang="en">
<iw:content mode="edit">
<![CDATA[
<div class="hello_world">
<div>
<label for="_IWID_languages"> 请选择语言 </label>
<select id="_IWID_languages">
<option value="en"> 英语 </option>
<option value="fr"> 法语 </option>
<option value="zh"> 中文 </option>
<option value="ja"> 日语 </option>
</select>
</div>
<div>
<input type="button" id="_IWID_save" value=" 保存 " >
<input type="button" id="_IWID_cancel" value=" 取消 " >
</div>
</div>
]]>
</iw:content>
</iw:iwidget> |
清单 5 中使用一个 HTML select 元素来列出可选的语言。清单 6 中给出了相应需要添加的 JavaScript 代码。
清单 6. HelloWorld 编辑模式的 JavaScript 代码
dojo.declare("HelloWorld", null, {
HELLOS : {
"en" : "Hello",
"zh" : " 你好 ",
"fr" : "bonjour",
"ja" : " こんにちは "
},
byId : function(id) {
var idPrefix = this.iContext.widgetId;
return document.getElementById("_" + idPrefix + "_" + id);
},
onedit : function() {
var saveBtn = this.byId("save"),
cancelBtn = this.byId("cancel"),
messageDom = this.byId("message"),
languageSelect = this.byId("languages");
var self = this;
saveBtn.onclick = function() {
messageDom.innerHTML = self.HELLOS[languageSelect.value] + " World";
self.iContext.iEvents.fireEvent("onModeChanged",null,{newMode:'view'});
};
cancelBtn.onclick = function() {
self.iContext.iEvents.fireEvent("onModeChanged",null,{newMode:'view'});
};
}
}); |
清单 6 中使用了一个方法 byId 来根据 ID 获取 HTML 元素。该方法使用不带 _IWID_ 前缀的 ID 作为参数,可以避免使用 document.getElementById 时繁琐的增加前缀的操作。onedit 是一个特殊的方法,当 iWidget 切换到编辑模式时,该方法会被自动调用。相应的,也有一个 onview 方法,在切换到查看模式时会被自动调用。在 onedit 方法中的主要逻辑是为“保存”和“取消”两个按钮增加响应事件。其中 iContext.iEvents.fireEvent("onModeChanged",null,{newMode:'view'} 这行代码是利用运行环境提供的 API 来进行模式的切换,即从编辑模式切换回查看模式。这里用到了 iContext 中的 iEvents 对象的 fireEvent 方法,该方法用来发出某种事件。onModeChanged 事件用来切换 iWidget 的模式。关于 iWidget 中的事件,在下一章中会详细说明。图 4 给出了 HelloWorld 的编辑模式的运行效果图。
图 4. HelloWorld iWidget 的编辑模式的运行效果图
使用 ItemSet 进行持久化
在上一节中提到用户可以通过 iWidget 的编辑模式来修改其配置。不过这种修改并没有被持久化下来。当页面刷新之后,iWidget 就会恢复其默认的配置。为了持久化 iWidget 的配置,需要利用本节所介绍的 iWidget 的 ItemSet(数据条目集)机制。
ItemSet 和 ManagedItemSet
ItemSet 是 iWidget 的持久化存储机制。iWidget 运行环境提供相应的 API 来对 ItemSet 进行操作。一个 ItemSet 由多个数据条目(Item)组成,每个数据条目有名称和对应的值。ItemSet 可以简单的看成是名值对的列表。在 ItemSet 中,相同名称的数据条目只能有一个值。ItemSet 可以类比于关系数据库中的表,其中的数据条目可以类比于表中的列,只是这个表中只有一行数据。 ItemSet 有一系列的方法用来对其进行操作,具体见表 2。
表 2. ItemSet 的方法说明
| 方法 | 说明 |
|---|
| setItemValue() | 该方法用来设置 ItemSet 中某个数据条目的值。该方法有三个参数,分别是数据条目的名称,值以及是否是只读的。比如代码 setItemValue("name", "Alex", false) 的作用是在 ItemSet 中增加或修改数据条目 name,使得其值为“ Alex ”,并且是非只读的。 | | getItemValue() | 该方法用来获取 ItemSet 中某个数据条目的值。该方法只有一个参数,就是数据条目的名称。该方法的返回值是该条目的值。比如代码 getItemValue("name") 的作用是获取 ItemSet 中条目 name 的值。 | | removeItem() | 该方法用来删除 ItemSet 中的某个数据条目。该方法只有一个参数,就是数据条目的名称。 | | isReadOnly() | 该方法用来判断 ItemSet 中的某个数据条目是否是只读的。该方法只有一个参数,就是数据条目的名称。 | | getAllNames() | 该方法用来获取 ItemSet 中所有数据条目的名称。使用该方法可以遍历 ItemSet 中的所有条目。 | | clone() | 该方法用来对当前 ItemSet 进行复制。 | | addListener() 和 removeListener() | 这两个方法用来管理 ItemSet 的变化监听器(listener)。对 ItemSet 可以添加监听器,当 ItemSet 发生变化的时候,监听器方法会被调用。 |
上面提到的 ItemSet 是一个通用的 iWidget 持久化接口。实际上,在 iWidget 的 JavaScript 代码中经常用到的是由 iContext 管理的 ItemSet,也就是 ManagedItemSet(受控 ItemSet)。 ManagedItemSet 的持久化和生命周期是由 iContext 来管理的。 ManagedItemSet 的接口扩展自 ItemSet,因此它有 表 2 中列出的绝大部分方法(addListener() 和 removeListener() 除外);同时它还有额外的方法,见 表 3。
表 3. ManagedItemSet 的方法说明
| 方法 | 说明 |
|---|
| save() | 该方法用来请求 iContext 持久化 ManagedItemSet 。由于 iContext 的持久化操作可能是异步的,该方法有一个可选的参数,允许传入一个回调方法。当持久化完成的时候,该回调方法会被调用。 |
在 iWidget 中,有三个比较特殊的 ManagedItemSet,可以通过 iContext 对象的方法来获取它们,再访问其中保存的数据。这三个特殊的 ManagedItemSet 的说明见 表 4。
表 4. 特殊的 ManagedItemSet 说明
| iContext 对象的方法 | 说明 |
|---|
| getiWidgetAttributes() | 该方法返回的 ManagedItemSet 可以访问 iWidget 的可供配置的属性。 | | getUserProfile() | 该方法返回的 ManagedItemSet 可以访问当前用户的概要信息。 | | getiDescriptor() | 该方法返回的 ManagedItemSet 可以访问 iContext 必须理解的属性。这是由于 iWidget 开发人员可以添加自定义的属性,为了避免命名冲突,iWidget 规范要求把一些常用的属性存放在这个 ManagedItemSet 中。这些属性包括标题、名称、描述和作者姓名等。 |
为 HelloWorld 增加持久化
在介绍完 ManagedItemSet 之后,下面介绍 iWidget 规范中定义的每个 iWidget 都具有的一个名称为 attributes 的 ManagedItemSet 。iWidget 规范允许对 iWidget 进行一定的定制。每个 iWidget 中可定制的属性组成了一个 ManagedItemSet,由 iContext 进行管理。每个 iWidget 可以自由确定该 ManagedItemSet 中包含的数据条目。如 表 4 中所述,iContext 对象中的 getiWidgetAttributes方法用来获取该 ManagedItemSet。
对于 HelloWorld iWidget 来说,希望用户可以配置问候所使用的语言,因此需要把该属性添加到 attributes 这一 ManagedItemSet 中。在 iWidget 定义文件中,可以通过 <itemSet> 元素来声明 ItemSet 及其包含的数据条目。对于名称为 attributes 这一特殊的 ManagedItemSet 来说,在定义文件中添加声明是可选的。不过进行声明的好处是可以为数据条目提供默认值。声明 ItemSet 的方式见 清单 7。
清单 7. 声明 ItemSet
<iw:itemSet id="attributes">
<iw:item id="language" value="en" readOnly="false"/>
</iw:itemSet> |
当该 iWidget 加载的时候,应该读取配置信息并选择对应的语言来显示;当用户在编辑模式修改了配置之后,需要保存配置信息;编辑模式的 HTML 内容应该反映当前的配置信息。清单 8 中给出了相应的 JavaScript 代码并省略了部分无关代码。
清单 8. HelloWorld 配置的持久化
saveSettings : function(settingsObj) {
var att = this.iContext.getiWidgetAttributes();
for (key in settingsObj) {
att.setItemValue(key, settingsObj[key], false);
}
att.save();
},
loadSettings : function() {
var att = this.iContext.getiWidgetAttributes();
var attNames = att.getAllNames() || [];
var settingsObj = {};
for (var i = 0, n = attNames.length; i < n; i++) {
settingsObj[attNames[i]] = att.getItemValue(attNames[i]);
}
return settingsObj;
},
onedit : function() {
var saveBtn = this.byId("save"),
cancelBtn = this.byId("cancel"),
messageDom = this.byId("message"),
languageSelect = this.byId("languages");
var self = this;
var settingsObj = this.loadSettings();
for (var i = 0, n = languageSelect.options.length; i < n; i++) {
if (languageSelect.options[i].value == settingsObj["language"]) {
languageSelect.options[i].selected = true;
}
}
saveBtn.onclick = function() {
messageDom.innerHTML = self.HELLOS[languageSelect.value] + " World";
var settingsObj = {
"language" : languageSelect.value
};
self.saveSettings(settingsObj);
self.iContext.iEvents.fireEvent("onModeChanged",null,{newMode:'view'});
};
}; |
清单 8 中,saveSettings 方法用来把一个 JSON 对象表示的名值对持久化到 attributes 这个 ManagedItemSet 中;loadSettings 方法用来获取该 ManagedItemSet 中的全部数据条目,并返回一个 JSON 对象。原来的 onLoad 和 onedit 方法也做了相应的修改,以使用该 ManagedItemSet 中保存的配置信息。
在增加了上面的代码之后,当 HelloWorld iWidget 加载的时候,会根据用户上次配置的语言来显示问候信息。需要注意的是,在 iWidget 的定义文件中可以通过 <itemSet> 元素来添加自定义的 ItemSet,并通过 iContext.getItemSet 方法来获取它。不过由于它并不是 ManagedItemSet,因此并不能支持持久化。
小结
本章中介绍了如何为 iWidget 添加编辑模式,以及如何将 iWidget 中可定制的配置信息用 ItemSet 持久化下来。下一章将介绍如何在 iWidget 中发布和响应事件。
|