Sometimes, you may want to specify a component parameter in tapestry's markup language (TML) and instead of rendering the HTML directly to the response output stream, you may wish to use it for some other purpose (eg use it in a javascript).

Example 1

The code below demonstrates a component that does the following:

  • Accepts a count parameter and a RenderCommand parameter
  • Adds a wrapper div to the markup writer
  • Loops through each count and updates a the "current" property which may be referenced in the RenderCommand parameter
  • Renders the RenderCommand for each iteration through the loop
  • Uses @AfterRender to get the resultant HTML from the wrapper div
  • Removes the wrapper div from the markup so that it is not rendered to the response directly

Page.tml

<t:tmlToString t:count="5" t:id="tmlToString">
   <p:renderMe>
      <div>foo ${tmlToString.current} bar</div>
   </p:renderMe>
</t:tmlToString> 

Page.java

@InjectComponent
@Property
private TmlToString tmlToString; 

TmlToString.java

public class TmlToString {
   @Parameter
   @Property
   private RenderCommand renderMe;

   @Property
   @Parameter(defaultPrefix=BindingConstants.LITERAL, required=true)
   private int count;

   @Property
   private int current;

   @Inject
   private JavaScriptSupport javaScriptSupport;

   private Element wrappingDiv;

   @BeginRender
   RenderCommand beginRender() {
      return new RenderCommand() {
         public void render(MarkupWriter writer, RenderQueue queue) {
            wrappingDiv = writer.element("div");
            List<RenderCommand> commands = new ArrayList<RenderCommand>();
            for (int i = 0; i < count; ++ i) {
               final int finalI = i;
               commands.add(new RenderCommand() {
                  public void render(MarkupWriter writer2, RenderQueue queue2) {
                     current = finalI;
                     queue2.push(renderMe);
                  }
               });
            }
            Collections.reverse(commands); // render commands are pushed to the front of the queue
            for (RenderCommand command : commands) {
               queue.push(command);
            }
         }
      };
   }

   @AfterRender
   void afterRender(MarkupWriter writer) {
      writer.end();
      String html = wrappingDiv.getChildMarkup();
      wrappingDiv.remove();
      javaScriptSupport.addScript("alert('%s')", html);
   }
}

Result

alert('<div>foo 0 bar</div><div>foo 1 bar</div><div>foo 2 bar</div><div>foo 3 bar</div><div>foo 4 bar</div>');

Example 2

Here is a slightly different example which uses the component's body block instead of a render command parameter. Note that tapestry's TypeCoercer can coerce between Block and RenderCommand

Page.tml

   <t:alertBody t:count="5" t:id="alertBody">
      <div>${alertBody.current}</div>
   </t:alertBody>

Page.java

   @InjectComponent
   @Property
   private AlertBody alertBody;

AlertBody.java

public class AlertBody {
   @Property
   @Parameter(defaultPrefix=BindingConstants.LITERAL, required=true)
   private int count;
   
   @Property
   private int current;

   @Inject
   private JavaScriptSupport javaScriptSupport;
   
   @Inject
   private ComponentResources componentResources;
   
   @Inject
   private TypeCoercer typeCoercer;
   
   private Element wrappingDiv;
   
   @BeginRender
   RenderCommand beginRender() {
      return new RenderCommand() {
         public void render(MarkupWriter writer, RenderQueue queue) {
            wrappingDiv = writer.element("div");
            List<RenderCommand> commands = new ArrayList<RenderCommand>();
            for (int i = 0; i < count; ++ i) {
               final int finalI = i;
               commands.add(new RenderCommand() {
                  public void render(MarkupWriter writer2, RenderQueue queue2) {
                     current = finalI;
                     Block body = componentResources.getBody();
                     RenderCommand bodyRenderCommand = typeCoercer.coerce(body, RenderCommand.class);
                     queue2.push(bodyRenderCommand);
                  }
               });
            }
            // commands are pushed to the front of the queue
            Collections.reverse(commands);
            for (RenderCommand command : commands) {
               queue.push(command);
            }
         }
      };
   }
   
   @BeforeRenderBody
   boolean beforeRenderBody() {
      // we are explicitly rendering the body in beginRender so do not render it implicitly
      return false;
   }
   
   @AfterRender
   void afterRender(MarkupWriter writer) {
      writer.end();
      String html = wrappingDiv.getChildMarkup();
      wrappingDiv.remove();
      javaScriptSupport.addScript("alert('%s')", html);
   }
}

Result

alert('<div>0</div><div>1</div><div>2</div><div>3</div><div>4</div>');
  • No labels