内置组件<t:pageLink/>、<t:actionlink/>和<t:eventlink/>

第一部分:源码
AbstractLink.java

// Copyright 2007, 2008, 2009 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package org.apache.tapestry5.corelib.base;

import org.apache.tapestry5.*;
import org.apache.tapestry5.annotations.Parameter;
import org.apache.tapestry5.annotations.SetupRender;
import org.apache.tapestry5.annotations.SupportsInformalParameters;
import org.apache.tapestry5.dom.Element;
import org.apache.tapestry5.ioc.annotations.Inject;
import org.apache.tapestry5.services.javascript.JavaScriptSupport;

/**
 * Provides base utilities for classes that generate clickable links.
 */
@SupportsInformalParameters
public abstract class AbstractLink implements ClientElement
{
    /**
     * An anchor value to append to the generated URL (the hash separator will be added automatically).
     */
    @Parameter(defaultPrefix = BindingConstants.LITERAL)
    private String anchor;

    /**
     * If true, then then no link element is rendered (and no informal parameters as well). The body is, however, still
     * rendered.
     */
    @Parameter("false")
    private boolean disabled;

    @Inject
    private ComponentResources resources;

    @Inject
    private JavaScriptSupport jsSupport;

    private Link link;

    private Element element;

    private String clientId;

    private String buildHref(Link link)
    {
        String href = link.toURI();

        if (anchor == null)
            return href;

        return href + "#" + anchor;
    }

    @SetupRender
    void resetElementAndClientId()
    {
        element = null;
        clientId = null;
    }

    /**
     * Writes an &lt;a&gt; element with the provided link as the href attribute. A call to
     * {@link org.apache.tapestry5.MarkupWriter#end()} is <em>not</em> provided. Automatically appends an anchor if
     * the component's anchor parameter is non-null. Informal parameters are rendered as well.
     * 
     * @param writer
     *            to write markup to
     * @param link
     *            the link that will form the href
     * @param namesAndValues
     *            additional attributes to write
     */
    protected final void writeLink(MarkupWriter writer, Link link, Object... namesAndValues)
    {
        element = writer.element("a", "href", buildHref(link));

        writer.attributes(namesAndValues);

        resources.renderInformalParameters(writer);

        this.link = link;
    }

    /**
     * Returns the most recently rendered {@link org.apache.tapestry5.Link} for this component. Subclasses calculate
     * their link value as they render, and the value is valid until the end of the request, or the next time the same
     * component renders itself (if inside a loop).
     * 
     * @return the most recent link, or null
     */
    public Link getLink()
    {
        return link;
    }

    /**
     * Returns the unique client id for this element. This is valid only after the component has rendered (its start
     * tag). A client id is generated the first time this method is invoked, after the link renders its start tag.
     */
    public final String getClientId()
    {
        if (clientId == null)
        {
            if (element == null)
                throw new IllegalStateException(String.format(
                        "Client id for %s is not available as it did not render yet (or was disabled).",
                        resources.getCompleteId()));

            clientId = jsSupport.allocateClientId(resources);

            element.forceAttributes("id", clientId);
        }

        return clientId;
    }

    /**
     * Returns true if the component is disabled (as per its disabled parameter). Disabled link components should not
     * render a tag, but should still render their body.
     */
    public boolean isDisabled()
    {
        return disabled;
    }

    /**
     * Used for testing.
     */
    final void inject(String anchor, ComponentResources resources)
    {
        this.anchor = anchor;
        this.resources = resources;
    }
}

PageLink.java

// Copyright 2007, 2008 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package org.apache.tapestry5.corelib.components;

import org.apache.tapestry5.BindingConstants;
import org.apache.tapestry5.ComponentResources;
import org.apache.tapestry5.Link;
import org.apache.tapestry5.MarkupWriter;
import org.apache.tapestry5.annotations.Parameter;
import org.apache.tapestry5.corelib.base.AbstractLink;
import org.apache.tapestry5.ioc.annotations.Inject;

/**
 * Generates a render request link to some other page in the application. If an activation context is supplied (as the
 * context parameter), then the context values will be encoded into the URL. If no context is supplied, then the target
 * page itself will supply the context via a passivate event.
 * <p/>
 * Pages are not required to have an activation context. When a page does have an activation context, the value
 * typically represents the identity of some object displayed or otherwise manipulated by the page.
 */
public class PageLink extends AbstractLink
{
    /**
     * The logical name of the page to link to.
     */
    @Parameter(required = true, allowNull = false, defaultPrefix = BindingConstants.LITERAL)
    private String page;

    @Inject
    private ComponentResources resources;

    /**
     * If provided, this is the activation context for the target page (the information will be encoded into the URL).
     * If not provided, then the target page will provide its own activation context.
     */
    @Parameter
    private Object[] context;

    void beginRender(MarkupWriter writer)
    {
        if (isDisabled()) return;

        Link link = resources.createPageLink(page, resources.isBound("context"), context);

        writeLink(writer, link);
    }

    void afterRender(MarkupWriter writer)
    {
        if (isDisabled()) return;

        writer.end(); // <a>
    }
}

AbstractComponentEventLink.java

// Copyright 2008, 2009 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package org.apache.tapestry5.corelib.base;

import org.apache.tapestry5.BindingConstants;
import org.apache.tapestry5.Link;
import org.apache.tapestry5.MarkupConstants;
import org.apache.tapestry5.MarkupWriter;
import org.apache.tapestry5.annotations.Environmental;
import org.apache.tapestry5.annotations.Parameter;
import org.apache.tapestry5.ioc.annotations.Inject;
import org.apache.tapestry5.services.ClientBehaviorSupport;
import org.apache.tapestry5.services.Request;

/**
 * Base class for link-generating components that are based on a component event request. Such events have an event
 * context and may also update a {@link org.apache.tapestry5.corelib.components.Zone}.
 */
public abstract class AbstractComponentEventLink extends AbstractLink
{
    /**
     * The context for the link (optional parameter). This list of values will be converted into strings and included in
     * the URI. The strings will be coerced back to whatever their values are and made available to event handler
     * methods.
     */
    @Parameter
    private Object[] context;

    /**
     * Binding the zone parameter turns the link into a an Ajax control that causes the related zone to be updated.
     */
    @Parameter(defaultPrefix = BindingConstants.LITERAL)
    private String zone;

    @Environmental
    private ClientBehaviorSupport clientBehaviorSupport;

    @Inject
    private Request request;

    void beginRender(MarkupWriter writer)
    {
        if (isDisabled()) return;

        Link link = createLink(context);

        writeLink(writer, link);

        if (zone != null)
        {
            if (!request.isXHR())
                writer.getElement().forceAttributes(MarkupConstants.ONCLICK, MarkupConstants.WAIT_FOR_PAGE);

            clientBehaviorSupport.linkZone(getClientId(), zone, link);
        }
    }

    /**
     * Invoked to create the Link that will become the href attribute of the output.
     *
     * @param eventContext the context as an object array, possibly null
     */
    protected abstract Link createLink(Object[] eventContext);

    void afterRender(MarkupWriter writer)
    {
        if (isDisabled()) return;

        writer.end(); // <a>
    }
}

ActionLink.java

// Copyright 2006, 2007, 2008, 2009 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package org.apache.tapestry5.corelib.components;

import org.apache.tapestry5.ComponentResources;
import org.apache.tapestry5.EventConstants;
import org.apache.tapestry5.Link;
import org.apache.tapestry5.annotations.Events;
import org.apache.tapestry5.corelib.base.AbstractComponentEventLink;
import org.apache.tapestry5.ioc.annotations.Inject;

/**
 * Component that triggers an action on the server with a subsequent full page refresh.
 */
@Events(EventConstants.ACTION)
public class ActionLink extends AbstractComponentEventLink
{
    @Inject
    private ComponentResources resources;

    @Override
    protected Link createLink(Object[] contextArray)
    {
        return resources.createEventLink(EventConstants.ACTION, contextArray);
    }
}

EventLink.java

// Copyright 2008 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package org.apache.tapestry5.corelib.components;

import org.apache.tapestry5.BindingConstants;
import org.apache.tapestry5.ComponentResources;
import org.apache.tapestry5.Link;
import org.apache.tapestry5.annotations.Parameter;
import org.apache.tapestry5.corelib.base.AbstractComponentEventLink;
import org.apache.tapestry5.ioc.annotations.Inject;

/**
 * A close relative of {@link org.apache.tapestry5.corelib.components.ActionLink} except in two ways.
 * <p/>
 * First, the event that it triggers is explicitly controlled, rather than always "action".
 * <p/>
 * Second, the event is triggered in its container.
 * <p/>
 * This allows slightly shorter URLs but also allows multiple components within the same container to generate identical
 * URLs for common actions.
 */
public class EventLink extends AbstractComponentEventLink
{
    /**
     * The name of the event to be triggered in the parent component. Defaults to the id of the component. An {@link
     * org.apache.tapestry5.corelib.components.ActionLink} triggers an "action" event on itself, and EventLink component
     * triggers any arbitrary event on <em>its container</em>.
     */
    @Parameter(defaultPrefix = BindingConstants.LITERAL)
    private String event;

    @Inject
    private ComponentResources resources;

    String defaultEvent()
    {
        return resources.getId();
    }

    @Override
    protected Link createLink(Object[] eventContext)
    {
        ComponentResources containerResources = resources.getContainerResources();

        return containerResources.createEventLink(event, eventContext);
    }
}

第二部分:简介
从AbstractLink抽象类继承的子类都是实现链接的组件。
<t:pageLink/>提供了页面跳转的link组件。
<t:actionLink/>提供了一个执行action方法事件的组件。
<t:eventlink/>提供了执行一个任意方法事件的组件。
三个组件类都是从AbstractLink继承而来,在AbstractLink中声明了anchor和disabled两个组件参数,以上三个组件全部集成了这两个组件参数。
anchor参数的作用是增加一个锚点,HTML中的锚点应该很熟悉吧!不说了。
disabled参数的作用看名字就知道了,就是禁用不禁用的意思,如果值为true则不渲染任何内容,直接输出组件内部文本,如果为false(默认)则渲染一个a标签出来,然后输出到HTML。
<t:pageLink/>组件类PageLink中声明了一个page的参数,用来指定要跳转到的page名。
<t:actionlink/>和<t:eventlink/>两个组件类ActionLink和EventLink都继承了AbstractComponentEventLink,而AbstractComponentEventLink又是AbstractLink的实现子类。
其中在AbstractComponentEventLink中声明了context和zone两个组件参数,context用来做参数传递的,而zone用来进行ajax更新的,涉及<t:zone/>的使用,详细介绍以后会讲解。
context参数的作用是用来传递参数,被跳转到的页面的onActivate方法接收,如果<t:actionlink/>和<t:eventlink/>未指定context属性,那么则使用跳转到的页面的页面类的onPassivate方法进行参数传递,如果指定了context,则onPassivate不执行,具体看示例代码。
<t:actionlink/>组件是<t:eventlink/>的一个简化实现,可以类似于<t:eventlink/>组件指定event属性为action。其中的event是需要在页面类触发的事件方法名,方法名的格式是:on+eventName。其中eventName是event的参数值,方法名中的eventName第一个字母大写。

第三部分:测试
以下是测试用的最终代码
Start.tml

<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
	<head>
		<title>link component</title>
	</head>
    <body>
    	<t:pagelink page="another" context="param">another</t:pagelink>
    	<t:pagelink page="another" anchor="pre2">another_pre2</t:pagelink>
    	<t:actionlink t:id="actionId" zone="actionlinkzone">actionlinkzone</t:actionlink>
    	<t:eventlink event="testevent" zone="eventlinkzone">eventlinkzone</t:eventlink>
    	
    	<t:zone t:id="actionlinkzone" id="actionlinkzone">
    		actionlinkzone count:${count}
    	</t:zone>
    	<t:zone t:id="eventlinkzone" id="eventlinkzone">
    		eventlinkzone count:${count}
    	</t:zone>
    </body>
</html>

Start.java

package jumpstart.web.pages;

import org.apache.tapestry5.annotations.InjectComponent;
import org.apache.tapestry5.annotations.Persist;
import org.apache.tapestry5.annotations.Property;
import org.apache.tapestry5.corelib.components.Zone;

public class Start {
	@Property
	private String param="context param";
	@Property
	@Persist
	private int count;
	@InjectComponent
	private Zone actionlinkzone;
	@InjectComponent
	private Zone eventlinkzone;

	Object onActionFromActionId() {
		count--;
		return actionlinkzone.getBody();
	}

	Object onTestevent() {
		count++;
		return eventlinkzone.getBody();
	}

}

Another.tml

<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
	<head>
		<title>page another</title>
		<style>
			.red{color:red;}		
		</style>
	</head>
    <body>
    	<div class="red">${message}</div>
    	<pre id="pre1">
    		pre1 pre1 pre1 pre1 pre1 pre1 pre1 pre1 pre1 pre1 pre1 pre1 pre1 pre1 pre1 pre1 
适当复制内容才可以看到锚点的效果。
    	<pre id="pre2">
    		pre2 pre2 pre2 pre2 pre2 pre2 pre2 pre2 pre2 pre2 pre2 pre2 pre2 pre2 pre2 pre2 
适当复制内容才可以看到锚点的效果。
    	</pre>
    </body>
</html>

Another.java

package jumpstart.web.pages;

import org.apache.tapestry5.annotations.Property;

public class Another {
	@Property
	private String message = "default message";

	void onActivate(String message) {
		this.message = message;
	}

	String onPassivate() {
		return "from passivate";
	}
}

最终的效果

第四部分:分析
上述测试代码最终效果不完整或者与你看到的效果有些出入,根据简介内容自己研究吧!
这里我说一下我的理解:
1.eventlink和actionlink两个组件的重要区别就是各自组件类中的createLink方法内容,其中的actionlink组件类中的createLink方法内容是
return resources.createEventLink(EventConstants.ACTION, contextArray);
其中EventConstants.ACTION的常量值就是action,contextArray就是context的属性值,这就是简介中提到<t:actionlink/>组件是<t:eventlink/>的一个简化实现,可以类似于<t:eventlink/>组件指定event属性为action。为了验证这一点,我们看看eventlink组件类中的createLink方法内容
        ComponentResources containerResources = resources.getContainerResources();
        return containerResources.createEventLink(event, eventContext);
第一行的内容是获得当前组件的容器组件,我的理解应该就是主组件,因为组件有可能是一个mixin,而这个mixin是附着在另外一个组件的,这句就是这个作用。(源码中写的)
第二行的内容就和actionlink的类似了,只不过EventConstants.ACTION参数换成了event,而event就是eventlink的一个属性。
2.Zone参数的功能实现涉及到org.apache.tapestry5.services.ClientBehaviorSupport类,这个以后再研究。
3.pagelink组件类中的重要代码是resources.createPageLink(page, resources.isBound("context"), context);该句代码创建了一个跳转到page参数的org.apache.tapestry5.Link,该方法已经被声明为废弃的了,被org.apache.tapestry5.services.PageRenderLinkSource#createPageRenderLink(String)和org.apache.tapestry5.services.PageRenderLinkSource#createPageRenderLinkWithContext(String, Object[])代替,一个方法被两个方法分解,岂是就是将resources.isBound("context")的作用进行分解了,createPageRenderLink方法是一个不带context(就是组件中context未指定)参数的实现,createPageRenderLinkWithContext方法是一个带context参数的实现。

以上是我对这三个组件的源码研究,如果不正确的,请大家指出。

转载于:https://my.oschina.net/namespace/blog/15156

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值