zoukankan      html  css  js  c++  java
  • Tapestry-Again

    一旦你已经开始使用Tapestry了,那么你就走上了一条不归路。。。

    Once you work in Tapestry there's no going back!

    Page或者Component里面的元素都是private类型的。

    @InjectPage,Tapestry是一个被管理的环境,我们不直接去创建一个对象,而是让Tapestry去管理所有对象的生命周期。如果我们需要一个Page对象,那么只需要用@InjectPage这个注解。

    When creating your own applications, make sure that the objects stored in final variables are thread safe.

    当创建我们的应用时,确保我们声明的final类型的变量是线程安全的。

    Tapestry uses an approach based on the Post/Redirect/Get pattern.

    Tapestry使用的是一个基于POST/REDIRECT/GET的方式来获取服务器信息的。

    <t:actionLink t:id="makeGuess" context="1">1</t:actionLink> -> <a href="/bootcss/guess.actionlink/1">1</a>

    <t:actionLink id="makeGuess" context="1">1</t:actionLink> -> <a id="makeGuess" href="/bootcss/guess.actionlink/1">1</a>

    @Persist

    Tapestry的持久化默认是session级别的。

    Flash级别的话,只会保持一个请求,下一次请求发出的时候就会消失。

    <img src="${request.contextPath}/images/catalog/product_${productId}.png"/>

    犯了一个超级2的毛病,

    <t:loop source="1..10" value="var:index">${var:index}</t:loop>

    这种写法可以说是没问题的,小猿想不明白了就,为啥他就能够循环着把1到10打印出来呢,渲染的过程就是如何把控件中的值写成html的过程。

    当afterrender返回true的时候可能就完成渲染了,如果返回的是false的话会继续渲染。

    下图中的各个阶段,据官方介绍,SetupRender, BeginRender, AfterRender, CleanupRender是经常被使用的。其它的貌似是给mixin专门设计的。

    @SetupRender

    This is a good place to read component parameters and use them to set temporary instance variables.

    这是一个读取组件参数,并且设定临时变量的好地方。

    The general rule is, only use the ${...} syntax in non-Tapestry-controlled locations in your template, such as in attributes of ordinary HTML elements and in plain-text areas of your template.

    不要随意地使用${...}语法,基本准则是我们仅仅在非tapestry控制的地方使用,例如在标准的HTML元素的属性处,或者是一个基本的描述处。。。

    <t:textfield t:id="color" value="${color}"/> => <t:textfield t:id="color" value="color"/>

    <img src="context:images/banner.png"/> => <img src="${context:images/banner.png}"/>

    看如下的tml文件,其中有一个<html>标签,并且有一个t:type,其实这个<html>标签是必须的,在Tapestry渲染的时候,会把页面中的<h1>和<p>放到layout.tml的<t:body />处,然后把<html>标签去掉。

    <html t:type="layout" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd">
       <h1>Welcome to the Nifty Web Application!</h1>
       <p>
            Would you like to <t:pagelink page="login">Log In</t:pagelink>?
       </p>
    </html>

    Page Render Requests

    1 Pages may have an activation context. The activation context represents persistent information about the state of the page.

    页面可以有些激活内容(表现为URL上的参数)。它是用来显示页面内容的一个决定因素,例如ID什么的。

    2 When no explicit activation context is provided, the page itself is queried for its activation context.

    然而并不是所有的页面都有参数,如果没有提供参数的时候,页面就会自己去问(请求)这个参数。

    The event name is "passivate" . The return value of the method is used as the context. 

    这个请求触发的方法就是onPassivate(),他会返回一个参数,这个参数就会被当做页面请求的参数。

    1 一个页面中如果存在一个pagelink的话,如果这个pagelink没有参数(context)的话,那么这个pagelink的page所指定的页面java类的onPassivate就会被触发,以获取相应的context;

    2 如果这个页面的pagelink存在参数的话,那么就不会触发java类的onPassivate方法。

    3 进入到一个页面的时候,首先会触发它的onActivate方法,@SetupRender之后,@AfterRender之前,触发onPassivate方法

    下面的这个tml代码:

    <t:ActionLink context="${literal:actionLink}">ActionLink Test</t:ActionLink>
    被render之后形成:
    <a href="/bootcss/action/index.actionlink/actionLink">ActionLink Test</a>

    <t:EventLink context="${literal:eventLink}" event="testEvent">EventLink Test</t:EventLink>
    被render之后形成:
    <a href="/bootcss/action/index:testevent/eventLink">EventLink Test</a>

    @OnEvent注解的使用

    1 Form或者ActionLink的时候,

    @OnEvent(value="action", component="componentId")

    其中value的值固定,EventConstants.ACTION/action,component的值是actionLink的t:id指定的值。并且,t:ActionLink的id不要指定。

    INSTEAD: void onActionFromComponentId() {...}

    2 EventLink的时候,

    @OnEvent的value的值是eventLink的event指定的值。component的值不用指定也可以。

    INSTEAD: void onEventName() {...}

    当有多个方法满足条件时,按照下面的顺序执行:

    • Base class methods before sub-class methods.
    • 基类早于子类
    • Matching methods within a class in alphabetical order.
    • 一个类中有多个方法满足时,按照字母顺序执行
    • For a single method name with multiple overrides, by number of parameters, descending.
    • 如果一个方法被重载了,按照参数个数的降序执行。

    如果想要阻止其继续寻找合适的方法的话,可以让方法返回一个true。

    Tapestry持久化

    针对于单个page而言

    Tapestry有一个@Persist注解,它有三种类型,Session,Flash,Client(不推荐)。

    Session,默认级别,经过多次请求,它会保存一个页面上的数据。

    Flash,(PersistenceConstants.FLASH),信息仍然是储存在session里面,但是它不会被保存太长时间,一旦它在页面上显示过一次了就会被从session中删除。

    Tapestry的请求时Post-Redirect-Get类型的,应该说它会在一个请求完成之后就删除。

    凡是被@Persist标注的变量是不允许设定默认值的,无论直接赋值或者在构造器中设定值。

    针对多个multi-page而言

    Session State Objects for complex objects, and Session Attributes for simple types.

    Session State Object(SSO),是一个被@SessionState注解标记的变量,这个变量储存的内容被一个用户在所有的page中使用,别的用户不可以。

    如果一个页面中定义了一个@SessionState类型的变量,那么在别的页面中定义这么一个类型相同的,同样被@SessionState标准的变量,那么这两个变量指向的就是同一个变量。

    注意点: 不要把@SessionState用到一个简单类型上。因为Tapestry在@SessionState这个事儿上是认类型不认名字的,可能会因为存在相同类型的两个变量而把前面的变量给覆盖掉了。

    当Tapestry第一次发现一个SSO的时候,就会自动的创建这个对象。所以这个对象需要有一个无参构造器。

    如果给一个SSO设定了null的话,这个SSO就会被销毁。

    @SessionState(create=false)注解决定这个变量不会被自动创建。

        public void contributeApplicationStateManager(MappedConfiguration<Class, ApplicationStateContribution> configuration)
        {
            ApplicationStateCreator<ShoppingCart> creator = new ApplicationStateCreator<ShoppingCart>()
            {
                public ShoppingCart create()
                {
                    return new ShoppingCart(new Date());
                }
            };
    
            configuration.add(ShoppingCart.class, new ApplicationStateContribution("session", creator));
        }

    Session Attribute, 是一个被@SessionAttribute标记的变量,他同样是被所有的页面共享的一个seesion级别的变量。

    他跟SSO不同,并不是靠类型来获取一个对象,而是靠名称来获取的,这也帮助了在一个大型的项目中工程之间调用session中的内容。

    以下两个代码是等价的:

    public class Page {
        @SessionAttribute
        private User loggedInUserName;
    }
    public class Page {
        @SessionAttribute("loggedInUserName")
        private User userName;
    }

    在使用这个SSO或者SessionAttribute的时候,被共享的session是存在于多个module之间的。所以我们最好定义一个与包名相关的变量,然后赋予它,如下:

    public static final String USER_NAME_SESSION_ATTRIBUTE = "com.example.shoppingapp.username";
     
    public class Page {
        @SessionAttribute(USER_NAME_SESSION_ATTRIBUTE)
        private User userName;
    }

    @Inject

    1 Block Injection,推荐方式

    @Inject
    @Id("bar")
    private Block barBlock;

    2 Resource Injection

    • java.lang.String – The complete id of the component, which incorporates the complete class name of the containing page and the nested id of the component within the page.

    java.util.Locale – The locale for the component (all components within a page use the same locale).

    org.slf4j.Logger – A Logger instance configured for the component, based on the component's class name. SLF4J is a wrapper around Log4J or other logging toolkits.

    org.apache.tapestry5.ComponentResources – The resources for the component, often used to generate links related to the component.

    org.apache.tapestry5.ioc.Messages – The component message catalog for the component, from which localized messages can be generated.

    3 Asset Injection

    @Inject
    @Path("context:images/top_banner.png")
    private Asset banner;

    4 Service Injection

    Ajax and Zone

    Zones是Tapestry用来实现刷新部分页面的方式。Zone组件会被渲染成一个HTML元素,基本上是DIV。

    Zone可以被EventLink,ActionLink,Select,或者是一个Form来实现更新。

    有点需要说明的是,如果我们需要使用jquery,那么我们需要引入一个新的包Tapestry-jquery.jar,这样我们就不需要再引入jquery.js了

    <dependencies>
    ...
            <dependency>
                <groupId>org.got5</groupId>
                <artifactId>tapestry5-jquery</artifactId>
                <version>3.3.7</version>
            </dependency>
    </dependencies>
    
        <repositories>
          <repository>
              <id>devlab722-repo</id>
              <url>http://nexus.devlab722.net/nexus/content/repositories/releases
              </url>
              <snapshots>
                  <enabled>false</enabled>
              </snapshots>
          </repository>
      
          <repository>
              <id>devlab722-snapshot-repo</id>
              <url>http://nexus.devlab722.net/nexus/content/repositories/snapshots
              </url>
              <releases>
                  <enabled>false</enabled>
              </releases>
          </repository>
        </repositories>

    Zone是一个被@InjectComponent注解的变量,它的名称跟页面中的t:zone的id相同

    @Inject private Request request;是用来解析一个请求是否是ajax请求的,如:request.isXHR() ? /* YES */ someZone.getBody() : /* NO, reture page itself */ null;

    t:zone标签有一个visible属性,用来定义是否直接显示该组件;

    有一个update属性,用来定义更新的时候的样式,默认为highlight,show,slidedown,slideup,fade可选。

    Form and Validation

    @Component private Form form;

    这样引入一个form组件,这个组件的一个用途就是在上面追加错误信息。

    如: form.recordError("Check your information buddy~");

    另外还可以指定到某一个特定的控件,这个控件也需要被引入:

    @InjectComponent private TextField username;

    然后,form.recordError(username, "Check your information buddy~");

    java类中可能会有一个方法,名称是这样的:onValidateFromSomeForm() {...};其中somefrom指的是页面上的form的id。

    当完成了验证之后,java类中的onSuccess()方法就会被触发。。。

    t:textfield控件,我们需要给它指定t:id,这个t:id会生成html中input的name和id,我们也可以给它指定一个value,但是这没有什么必要,Tapestry会自动去绑定具有相同id的变量。

    FormValidation,输入控件可以有一个validate属性,这个属性可以指定这些值:required, min, max, minlength, maxlength, none, email, regexp,这些都是Tapestry内置的客户端验证。

    修改默认的错误信息,只需要追加相应的properties信息即可。
    app.properties中key的形式如:formid-componentid-checkname-message;
    pagename.properties中key的形式如:componentid-checkname-message;

    File upload

    在一个t:form中,<t:FileUpload t:id="file" t:value="file" validate="required"/>就会形成一个文件上传控件。

    java端代码:

        @Persist(PersistenceConstants.FLASH)
        @Property
        private String message;
        
        @Property
        private UploadedFile file;
        
        public void onSuccess() {
            File copied = new File("c:/" + file.getFileName());
            
            file.write(copied);
        }
        
        
        Object onUploadException(FileUploadException ex)
        {
            message = "Upload exception: " + ex.getMessage();
        
            return this;
        }

    Tapestry Hibernate

    Tapestry Hibernate管理的entity们,会被自动的创建一个valueEncoder。不明白encode和decode的区别。

    总之Tapestry会自动的通过一个entity的id来确定一个entity实体。并且这个id会被强转成string类型。

    所以,我们想要根据一个entity的id获取一个entity只需要用:

    void onActivate(SomeEntity entity) {
        this.entity = entity;
    }
    
    SomeEntity onPassivate() {
        return this.entity;
    }

    然而 ,Tapestry提供了另外一种方式,即注解@PageActivationContext,

    /**
        Annotation for a field for which the page activation context handlers (onActivate and onPassivate) should be created.
        In order to use this annotation you must contribute a org.apache.tapestry5.ValueEncoder for the class of the annotated property.
        You should not use this annotation within a class that already has an onActivate() or onPassivate() method; doing so will result in a runtime exception.
    */

    由于ValueEncoder已经被Tapestry Hibernate创建了,所以就无需费心了^.^, 另外,这个注解是不能跟onPassivate和onAcitivate一起使用的。

    如果给一个entity追加一个@Persist("entity")注解的话,那么这个变量就会被保存在session中,包括类型和该类型的id。

    这个时候是不需要onPassivate()方法的,Tapestry会直接去session当中去获取这个变量类型的id,onActivate()还是需要的。

    @CommitAfter

    所有的Hibernate操作都发生在一个事务当中,只有这个request请求完成之后事务才会提交。因此我们需要@CommitAfter这个注解来帮忙。这个注解放到哪一个方法上面,执行哪一个方法的时候事务就会被提交。

     

  • 相关阅读:
    随机六位数验证码生成
    泛型反反射方法显示
    前台分页控件用法
    asp.net api后台
    项目开发基础概念
    认证Authentication、权限Permissions、限流Throttling、过滤Filtering、排序、分页Pagination、异常处理Exceptions、自动生成接口文档、Xadmin
    视图基类、视图扩展类、GenericAPIView的视图子类、视图集基类ViewSet、action属性、路由Routers
    序列化组件的使用、反序列化、全局钩子和局部钩子的使用、raise_exception参数、modelserializer进行数据保存时的问题
    day61 Linux:权限管理、rpm软件包管理、yum工具
    day60 Linux压缩与打包、用户管理、用户提权sudo、grep、sed、awk、sort、uniq
  • 原文地址:https://www.cnblogs.com/voctrals/p/3714955.html
Copyright © 2011-2022 走看看