如何在OperaMasks 3.0中自定义原生UI构件 —渲染器(下)
2010-04-26 由 蜜汁肥叉烧 发表   评论(2条)   有3461人浏览
 

1. 页面资源引入

在现实开发中编写基于Ajax框架的构件时,我们不可避免地需要在页面中渲染出引入外部的JavaScript脚本文件与CSS样式表文件的代码。比如说,在页面中放一个<w:button>,就应该在响应流中渲染出以下代码:

<script type="text/javascript" src="/ear1/_global/resource/ext/om/ajax.js" charset="UTF-8"></script>
<script type="text/javascript" src="/ear1/_global/resource/ext/ext-base.js" charset="UTF-8"></script>
<script type="text/javascript" src="/ear1/_global/resource/ext/ext-core.js" charset="UTF-8"></script>
<script type="text/javascript" src="/ear1/_global/resource/ext/package/util.js" charset="UTF-8"></script>
<script type="text/javascript" src="/ear1/_global/resource/ext/package/widget-core.js" charset="UTF-8"></script>
<script type="text/javascript" src="/ear1/_global/resource/ext/om/ButtonPlugin.js" charset="UTF-8"></script>
<script type="text/javascript" src="/ear1/_global/resource/ext/package/button.js" charset="UTF-8"></script>
 
<link class="x-skin" rel="stylesheet" type="text/css" href="/ear1/_global/resource/ext/skin/default/yuiext/css/ext-all.css"/>
<link class="x-skin" rel="stylesheet" type="text/css" href="/ear1/_global/resource/ext/skin/default/yuiext/css/ext-extra.css"/>

渲染这种引入页面资源的代码,需要考虑以下几个问题:

  • 按照HTML的编写惯例,这类引入资源代码的代码应该集中渲染在header部分,而不是分别渲染在构件出现的位置上。

  • 外部资源不能重复引入,JavaScript脚本文件在引入时会执行初始化动作,如果重复引入会引起冲突。并且,重复引入资源会耗用额外的网络流量,延长页面首次打开的时间。

  • 一个比较大型的Ajax框架,如果把它的代码全部引入,会占用大量的资源。比如说Ext-JS的一个包含全部框架脚本代码的文件ext-all.js大小是900多kb。然而在一些简单的页面中,我们可能只使用其中的TextField与Button对象,这时如果直接引入ext-all.js会造成额外的资源浪费。为此,我们往往会对框架脚本文件进行拆分,然后由构件的渲染器去决定需要引入哪部分资源(通常成熟的Ajax框架会提供拆分好的版本)。

  • 同一个构件库中的构件所需要引入的外部资源往往带有逻辑上的依赖关系。比如说,AOM官方构件库的<w:dateField>构件继承了<w:textField>构件,并采用了与<w:calendar>构件一样的日期选择器。我们在编写<w:dateField>构件时就希望能直接先引入<w:dateField>与<w:calendar>构件所引入的资源,而不是重新去为<w:dateField>构件编写一个所有外部资源的列表。

  • JavaScript脚本文件在生产环境中,往往使用经过压缩的格式,但这种格式是难以阅读,无法调试的。因此在开发环境中,应用开发者会希望引入未经压缩的格式。渲染引用代码时需要考虑这种需求。

  • 构件打包部署后,外部资源文件与构件代码一起位于项目的源代码目录中,正常情况下不能通过URL直接访问。

  • 在Ajax响应中,由于构件状态发生改变,可能需要引入额外的外部资源,这时需要通过修改DOM树的方式来进行引入。

为此,渲染引入页面外部资源需要一种独特的机制来支撑。在AOM中,使用了一种称为“资源描述符”的机制来管理页面资源的引入。下面我们就来看一下,在AOM中如何开发一个需要引入外部资源的构件。

1.1. 代码高亮构件示例

在本节的描述中,我们会使用一个新的示例:编写一个代码高亮构件。在互联网上有很多用JavaScript实现的代码高亮开源项目,我们可以选用其中任意一种来作为我们这个新构件的客户端实现。在这里,我使用在百度中用“syntax highlighter javascript”关键字搜索的第一个结果 http://alexgorbatchev.com/wiki/SyntaxHighlighter。

利用第三方框架封装构件,首要的事情是确认License,在项目网站中我们可以看到这个项目使用的是LGPL 3协议,我们可以自由免费地使用。然后,我们需要了解这个框架的用法,下载(http://alexgorbatchev.com/downloads/grab.php?name=sh)后,通过阅读文档(http://alexgorbatchev.com/wiki/SyntaxHighlighter#For_Users),我们可以总结出这个框架的基本用法:

1<script type="text/javascript" src="js/shCore.js"></script>
2<script type="text/javascript" src="css/shBrushJScript.js"></script>
 
3<link href="css/shCore.css" _fcksavedurl="css/shCore.css" _fcksavedurl="css/shCore.css" rel="stylesheet" type="text/css" />
4<link type="text/css" rel="Stylesheet" href="/styles/shThemeMidnight.css" _fcksavedurl="/styles/shThemeMidnight.css" _fcksavedurl="/styles/shThemeMidnight.css"/>
 
<script type="text/javascript">
    5SyntaxHighlighter.all();
</script>
 
6<pre class="brush: js">
  function foo()
    {some boys ...
    }
</pre>
 
 
1

引入核心脚本文件,必须。

2

根据需要高亮的语言引入该语言的高亮脚本,必须但内容根据具体场景会有所变动。

3

引入核心CSS样式文件,必须。

4

引入主题样式文件,必须但内容根据具体场景会有所变动。

5

触发框架去执行代码高亮动作,必须。

6

在HTML的body中使用<pre>标签来标记需要高亮的代码,并使用class属性来指定初始化参数。具体参数设置可参考 http://alexgorbatchev.com/wiki/SyntaxHighlighter:Configuration

明白了基本用法之后,现在我们就可以开始编写新的构件了。在这里,我们仍然使用前面文章所使用的AOM构件工程,双击打开http://org.operamasks.demo.customercomponents命名空间下的myComponent.taglib.xml文件,在tags分类下添加一个新的标签:标签名称为code,component-type为org.operamasks.demo.Code。先不要发布,我们可以看到在component-type的输入框下是dependJSPackages与dependCSSPackages两个配置框,下面,就来看看这两个配置框的含义与用法。

1.2. 资源描述符,resource-dependences.xml

前面已经提过,在许多场景下,不同构件的引入资源之间存在依赖关系。虽然我们可以不理会这种依赖关系,在每个构件中都直接渲染出它要引入的所有资源,但对于大型的构件库,这无疑是非常繁琐的。 在AOM中,使用资源描述符配置文件来描述这种依赖关系,来简化构件引入外部资源的声明。下面,我们结合示例来看看如何在AOM构件工程中配置资源依赖项目。

首先,我们需要把外部资源复制到构件工程中。请注意,构件资源文件都应该存放在AOM构件工程的"META-INF/resource"目录中。因此我们先在构件工程的“META-INF/resource”目录下,新建一个highlighter目录,然后把下载的syntaxhighlighter文件解压后复制到该目录。

下面开始定义外部资源的依赖项,我们首先双击打开构件工程“资源描述符”项目。在左边的“目录”中,选择“JS”分类,按“添加”按钮加入一条依赖项,用来引入核心脚本,资源名为highlight_core(可以随便起)。在资源文件中选择META-INF/resource目录下的highlighter/scripts/shCore.js文件。

接下来,再添加一条用于引入Java语言高亮脚本的依赖项。继续在JS分类下添加一条依赖项,资源名为highlight_java,在资源文件中选择META-INF/resource目录下的highlighter/scripts/shBrushJava.js文件。并且,在“目录”窗口中,在新增的“highlight_java"依赖项上按右键,在右键菜单中选择”添加依赖资源“,在弹出窗口中选择在上一步中创建的highlight_core依赖项。

我们可以继续添加需要支持高亮的语言脚本,每个依赖项都应该选择相关的脚本文件,并依赖于”highlight_core“项目。

同理,我们可以加入对应的CSS样式依赖项。其中,highlight_css_core依赖项指向文件”META-INF/resource/highlighter/styles/shCore.css“,其他项目指向同名的主题样式,并依赖于highlight_css_core项目。

存盘后,我们就定义好了所需要的资源描述符。总结一下,在资源描述符中,我们可以定义若干条资源依赖项,每条依赖项可以引入一个资源文件,并依赖于一个或多个其他依赖项。在编写渲染器时,只需要按照依赖项名称来引入资源,AOM引擎就会自动按照依赖关系渲染出该构件所需要的所有外部资源。

关闭资源描述符编辑窗口,回到myComponent.taglib.xml的编辑窗口。在默认情况下,我们希望这个代码高亮构件对Java语言进行高亮,并使用eclipse高亮风格。因此在dependJSPackages中指定highlight_java,在dependCSSPackage中指定highlight_css_eclipse。

1.3. 在渲染器中注册依赖资源

现在,我们可以点击“发布”按钮,生成相关的java类。

构件基类的编写方法在这里不再重复,可以参考《如何在OperaMasks 3.0中自定义原生UI构件 — 渲染器(上)》中的描述。代码如下:

@ComponentMeta(tagName="code", 
               family="org.operamasks.demo.Code", 
               rendererType="org.operamasks.demo.Code")
public abstract class UICodeBase extends UIComponentBase {
    /**
     * 语法高亮所使用的语言。缺省值为java。
     * 可选取值为:java, css, xml, javascript。
     */
    protected String lang;
    
    /**
     * 语法高亮的主题风格。缺省值为eclipse。
     * 可选值为:eclipse, default, midnight。
     */
    protected String theme;
    
    /**
     * 是否显示行号。缺省值为true。
     */
    protected Boolean showLineNumber;
    
    /**
     * 高亮行号。缺省值为空。
     * 多个高亮行之间用逗号分隔。例如“2,4,5”
     */
    protected String highlightLines;
}

下面我们开始编写渲染器,双击打开自动创建的CodeAjaxRenderer,可以看到已经自动生成了基本依赖代码:

package org.operamasks.demo;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
 
import org.operamasks.faces.render.common.AjaxRendererBase;
 
public class CodeAjaxRenderer extends AjaxRendererBase {
 
    @Override
    public String[] getDependedCSSPackages(FacesContext context, UIComponent component) {
 
        return new String[]{"highlight_css_eclipse"};
 
    }
 
    @Override
    public String[] getDependedJSPackages(FacesContext context, UIComponent component) {
 
        return new String[]{"highlight_java"};
 
    }
 
}

这里的getDependedCSSPackages与getDependedJSPackages方法继承自ResourceRenderer接口,渲染器覆盖这两个方法,返回一个字符串数组,通知引擎首次渲染该构件时需要引入的资源依赖项目。从前面的代码高亮脚本基本用法,我们知道根据应用开发者所设置的高亮语言与主题风格,我们可能需要引入不同的资源依赖项目,因此需要对注册依赖项目的代码进行修改,根据构件对象的lang属性与theme属性取值来确定所注册的依赖项目,修改后的代码如下:

package org.operamasks.demo;
import java.util.HashMap;
import java.util.Map;
 
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
 
import org.operamasks.faces.render.common.AjaxRendererBase;
 
public class CodeAjaxRenderer extends AjaxRendererBase {
    private static final Map<String, String> SUPPORTED_LANGS = new HashMap<String, String>() {{
        put("java", "highlight_java");
        put("css", "highlight_css");
        put("xml", "highlight_xml");
        put("javascript", "highlight_javascript");
    }};
 
    private static final Map<String, String> SUPPORTED_THEMES = new HashMap<String, String>() {{
        put("eclipse", "highlight_css_eclipse");
        put("default", "highlight_css_default");
        put("midnight", "highlight_css_midnight");
    }};
 
    @Override
    public String[] getDependedCSSPackages(FacesContext context, UIComponent component) {
        UICode code = (UICode) component;
        String lang = getLang(code);
        if (SUPPORTED_LANGS.containsKey(lang)) {
            return new String[] {SUPPORTED_LANGS.get(lang)};
        } else {
            throw new IllegalStateException("不支持的高亮语言类型:" + lang);
        }
    }
 
    @Override
    public String[] getDependedJSPackages(FacesContext context, UIComponent component) {
        UICode code = (UICode) component;
        String theme = getTheme(code);
        if (SUPPORTED_THEMES.containsKey(theme)) {
            return new String[] {SUPPORTED_THEMES.get(theme)};
        } else {
            throw new IllegalStateException("不支持的高亮主题风格:" + theme);
        }
    }
 
    private String getLang(UICode code) {
        String lang = code.getLang();
        if (lang == null) {
            lang = "java";
        }
        return lang;
    }
    
    private String getTheme(UICode code) {
        String theme = code.getTheme();
        if (theme == null) {
            theme = "eclipse";
        }
        return theme;
    }
}

然后,我们继续在渲染器中加入以下方法,在响应流中渲染出所需要的代码:

    private static final String HIGHLIGHTALL_FLAG_KEY = "org.operamasks.demo.highlightall";
 
    @Override
    public void encodeHtmlBegin(FacesContext context, UIComponent component)
            throws IOException {
        UICode code = (UICode) component;
        1String text = RendererUtils.encodeComponentChildren(context, component);
        if (text != null && text.length() > 0) {
            AjaxHtmlResponseWriter writer = (AjaxHtmlResponseWriter) context.getResponseWriter();
 
            //SyntaxHighlighter要求在第一个代码块出现之前调用SyntaxHighlighter.all()方法
            Map<String, Object> reqMap = context.getExternalContext().getRequestMap();
            2if (!reqMap.containsKey(HIGHLIGHTALL_FLAG_KEY)) {
                writer.startElement("script", component);
                writer.writeText("SyntaxHighlighter.all();", null);
                writer.endElement("script");
            }
 
            writer.startElement("pre", component);
 
            StringBuilder sb = new StringBuilder();
            Formatter fmt = new Formatter(sb);
            fmt.format("brush: %s; gutter: %s; highlight: %s", getLang(code), getShowLineNumber(code), getHighlightLines(code));
            writer.writeAttribute("class", sb.toString(), null);
            writer.writeText(text, null);
            writer.endElement("pre");
        }
    }
 
    @Override
    3public boolean getEncodeHtmlChildren(UIComponent component) {
        return true;
    }
 
    private boolean getShowLineNumber(UICode code) {
        Boolean showLines = code.getShowLineNumber();
        if (showLines == null) {
            showLines = true;
        }
        return showLines;
    }
 
    private static final String HIGHLIGHTLINES_PATTERN = "\\d+\\s*(,\\s*\\d+\\s*)*";
    private String getHighlightLines(UICode code) {
        String lines = code.getHighlightLines();
        if (lines == null) {
            return "null";
        } else if (lines.matches(HIGHLIGHTLINES_PATTERN)) {
            return "[" + lines + "]";
        } else {
            throw new IllegalStateException("非法的hightlightLines属性格式:" + lines);
        }
    }
1

RendererUtils.encodeComponentChildren()方法用于一个构件标签的内部文本,包括所有子构件渲染出来的响应文本,适用于允许在起始标签与结束标签之间包含文本的构件。此方法已经包含了递归调用子构件渲染器的算法,通常情况下使用此方法的构件渲染器会覆盖相应。关于RendererUtils工具类的详细描述可参考《如何在OperaMasks 3.0中自定义原生UI构件 — 渲染器(上)》一文。

2

编写渲染器时需要考虑到页面中可能同时存在多个类型相同,使用相同渲染器的构件,如果需要渲染出多个同类构件公用的响应文本。比如说这里调用"SyntaxHighlighter.all()"的脚本,即使页面中存在多个<my:code>构件时也只应该出现一次。应该采用在RequestMap中打标志的方式保证只渲染一次。

3

覆盖渲染器的getEncodeHtmlChildren()方法返回true,将通知AOM引擎不要递归调用子构件的渲染器。因为这里子构件的响应文本已经在encodeHtmlBegin中调用RendererUtils.encodeComponentChildren()方法处理了。

重新打包发布构件工程后,就可以在测试应用中测试这个新构件:

<w:page title="Insert title here">
    <my:code lang="java" highlightLines="2,3">
public class myClass {
    private int i;
 
    public int getI() {
        return i;
    }
}
    </my:code>
 
    <my:code lang="css" >
.p {
    width: 10px;
}
    </my:code>
</w:page>

运行页面就看到代码高亮的效果。在浏览器中查看HTML源码,我们可以发现正确渲染出来了如下资源代码:

<script type="text/javascript" src="/ear1/_global/resource/highlighter/scripts/shCore.js" charset="UTF-8"></script>
<script type="text/javascript" src="/ear1/_global/resource/highlighter/scripts/shBrushJava.js" charset="UTF-8"></script>
<script type="text/javascript" src="/ear1/_global/resource/highlighter/scripts/shBrushCss.js" charset="UTF-8"></script>
...
<link class="x-skin" rel="stylesheet" type="text/css" href="/ear1/_global/resource/highlighter/styles/shCore.css"/>
<link class="x-skin" rel="stylesheet" type="text/css" href="/ear1/_global/resource/highlighter/styles/shThemeEclipse.css"/>

在开发中,还有一些场景需要我们在渲染过程中(encodeResourceBegin方法中)才能确定需要引入哪些资源。这时,我们可以调用ComponentResource上的API来加入依赖资源,例如:

public void encodeResourceBegin(FacesContext context,ResourceManager rm, UIComponent component) 
throws IOException {
    ComponentResource resource = ComponentResource.getResourceInstance(rm);
    resource.addJSPackageDependency("highlight_java");
    resource.addCSSPackageDependency("highlight_css_eclipse");
    ......
}

关于ComponentResource的详细介绍,可参考《如何在OperaMasks 3.0中自定义原生UI构件 — 渲染器(上)》。

(以上的渲染器写法存在一个小bug,由于SyntaxHighlighter项目的脚本本来就被设计为通过引入不同的css样式文件来改变主题,因此一个页面只能使用一种主题。如果在页面中存在多个<my:code>构件,而且分别指定了不同的theme属性的话,只有一种主题能生效且整个页面都使用同一主题。修正这个bug需要对该项目的css样式文件做小量修改,但与本文主题无关,在这里就不展开讨论了。)

2. 处理客户端提交的请求信息

渲染器除了渲染代码外,还担负着另一个重要职责,对客户端提交上来的请求信息进行解码,并同步到服务器端构件模型体系中。构件开发者可以覆盖渲染器的decode方法,编写处理请求内容的逻辑,这个方法将会在完成构件树创建之后被调用,也就是说,这时我们可以通过引擎或构件API——例如getParent()方法与getChildren()方法等——获取到构件之间的组织关系。在decode方法中,开发者可以随时通过FacesContext.getExternalContext().getRequestParameterMap()方法获取到请求参数表,其中包含了所有从客户端提交的信息。由于提交的内容必然是由上一次请求渲染出来的响应代码所决定的,因此一个渲染器应该负责处理请求参数中的哪些参数,也应该由其渲染出来的响应代码而定。通常来说,decode方法中需要处理以下的具体任务。

2.1. 从请求信息中解码出构件提交值

《如何在OperaMasks 3.0中自定义原生UI构件 — 基本概念》中我们提到,对于允许用户输入的构件(构件类实现了EditableValueHolder接口的构件),value属性是一个特殊的属性,它是被设计用来在客户端供用户修改的。因此它的值会通过请求参数来提交,而构件的其他属性值通常不支持在客户端直接修改,是通过视图状态(ViewState)进行跨请求传递的。下面我们以AOM官方构件w:textField渲染器为例来简单说明应该如何对请求参数中的构件提交值进行解码(为了突出重点,对代码进行了简化和合并):

/**
 * w:textField组件的AJAX渲染器
 */
public class AjaxTextFieldRenderer extends AbstractFieldRenderer {
...
    @Override
    public void decode(FacesContext context, UIComponent component) {
        ...
        // save submitted value
        1String clientId = component.getClientId(context);
        2Map<String, String> requestMap = context.getExternalContext().getRequestParameterMap();
        UITextField textField = (UITextField) component;
        ...
        3String newValue = requestMap.get(clientId);
        if (newValue != null) {
            4((EditableValueHolder) component).setSubmittedValue(newValue);
        }
    }
 
    @Override
    public void encodeHtmlBegin(FacesContext context, UIComponent component) throws IOException {
        ...
        String clientId = component.getClientId(context);
        ...
        out.startElement("input", component);
        out.writeAttribute("id", clientId, "clientId");
        out.writeAttribute("name", clientId, "clientId");
        ...
    }
...
}
1

任何构件都可以通过getCliendId()方法获取一个唯一且在跨请求环境下保持一致的clientId值。输入构件的渲染器在首次渲染页面时,应该保证本构件的值被提交时采用clientId作为键值。例如,在HTML场景下,应该保证在encodeHtmlBegin方法中渲染输入构件的客户端DOM元素时(比如说HTML的<input>标签)设置该元素的name属性值为clientId。

2

通过FacesContext.getExternalContext().getRequestParameterMap()获取请求参数表。

3

从请求参数表中根据clientId获取本构件的提交值。

4

调用EditableValueHolder接口上的setSubmittedValue方法将解码后的提交值同步到构件模型中。

简单总结,需要处理请求参数的渲染器中,必然存在首次渲染与decode方法的合作关系,分别负责渲染提交代码与解码。特别地,在编写一个渲染输入构件的渲染器时,需要考虑以下几点:

  • 在首次渲染时应保证构件的输入值会以构件的clientId为键值提交。(例如在HTML响应中渲染出<input name="构件clientId">的代码)

  • 在decode方法中通过构件的clientId在请求参数中获取到字符串类型的提交值。

  • 调用构件上的setSubmittedValue方法(构件应该实现了EditableValueHolder接口),将字符串类型的提交值保存到构件类中。

至于字符串类型的提交值是否合法,如何转换,构件值发生改变后如何调用ValueChangeListener,如何把构件值更新到绑定的ManagedBean属性中等问题,在这里不需要关心,AOM引擎会在后续的处理中自动进行。

2.2. 从请求信息中解码出事件源

对于动作构件(实现了ActionSource或ActionSource2接口的构件),渲染器上的decode方法则担负了确定发起提交的动作事件源的职责。在一个页面中往往存在了多个动作构件,例如说多个<w:button>,当用户按下了其中一个按钮发起提交,AOM框架是怎么知道用户按下的是哪一个,又是如何去调用对应的Action方法的呢?玄机就在渲染器的decode方法中。下面我们以简化的<w:button>渲染器代码来说明:

public class AjaxButtonRenderer extends AjaxRendererBase implements ToolBarItemRenderer {
    ...
    @Override
    public void encodeResourceBegin(FacesContext context, ResourceManager manager, UIComponent component) {
        ...
        1buf.append(jsvar).append(".on('click', function(){");
 
        2String onclick = (String)component.getAttributes().get("onclick");
        if (onclick != null) {
            fmt.format("if (function(){%s}.apply(this)==false)return;", onclick);
        }
 
        3buf.append(RendererUtils.encodeAjaxSubmit(context, component, 
             HtmlEncoder.enquote(clientId), HtmlEncoder.enquote("")));
        buf.append("});\n");
        ...
    }
 
    @Override
    public void decode(FacesContext context, UIComponent component) {
        Map<String, String> paramMap = context.getExternalContext().getRequestParameterMap();
        String clientId = component.getClientId(context);
        4if (paramMap.containsKey(clientId)) {
            5ActionEvent event = new ActionEvent(component);
            6PartialUpdateCandidates.addLinkedUpdate(component);
            7component.queueEvent(event);
        }
    }
    ...
}
1

js.on('click', function(){事件处理代码}); 是在Ext中定义客户端构件事件的方式。这里的代码本质上是渲染出了一段button在客户端onclick事件的处理代码。

2

在渲染出来的onclick处理代码中,首先执行在构件onclick属性上定义的脚本。并且当onclick属性上定义的脚本返回false的话,中止整个onclick事件的处理,不再执行下面的提交脚本。

3

渲染出发起提交的脚本代码。在这里,使用了RendererUtils工具类的encodeAjaxSubmit方法,这个方法返回一段用来发起AJAX提交的脚本。其中,前两个参数是固定的,分别是当前的请求处理上下文FacesContext实例与构件类实例。后面可以继续添加个数不定的参数(总数必须是偶数),指定发起AJAX提交时需要额外添加的请求参数,按“键,取值,键,取值...”的顺序排列。在这里,加入了一个参数,键值为当前构件的clientId,取值为空。那么,在button的onclick事件被触发,发起提交的时候,button构件的clientId会作为键值包含在请求参数中。

4

在button构件的decode方法中,判断请求参数中是否包含本构件clientId的键值。如果是,则说明本次提交是由当前正在解码的构件发起的。

5

新建一个动作事件(ActionEvent)实例,并传入当前构件实例作为事件源。

6

对于允许添加<ajax:linkedUpdate>的构件,调用PartialUpdateCandidates.addLinkedUpdate静态方法处理<ajax:linkedUpdate>子构件。

7

调用构件上的queueEvent方法,将新建的动作事件实例加入事件队列中。

同样的,动作构件的渲染器中也体现了首次渲染与decode方法的合作关系。首先,在首次渲染的encodeResourceBegin方法(或encodeHtmlBegin方法,视具体情况而定)中渲染出构件触发提交的客户端事件脚本,通常我们会选用onclick事件。在渲染出来的脚本中,通常会先处理应用开发者在构件上定义的事件脚本。然后执行提交脚本,提交脚本中应该保证在提交的请求参数中包含提交构件的clientId作为键值,通常可以调用RendererUtils工具类的encodeAjaxSubmit方法来实现。

这样,在decode方法中就可以判断当前正在解码的构件的clientId是否在请求参数中,从而确定当前构件是否事件源。如是,则为当前构件新建一个动作事件实例,并加入构件的事件队列。至于加入事件队列之后,何时触发,如何调用ManagedBean上的绑定方法等问题,AOM引擎会自动处理,在这里不用关心。

所有评论
Nari Chiboo 2010-06-03 评论道:
弱弱的问一下:syntaxhighlighter文件在哪里下载?给个url,谢谢!
rory2008 2010-05-29 评论道:
例子代码中 getDependedCSSPackages 与getDependedJSPackages方法体反了
1   共1页
您还没有登录,请登录后发表评论