You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@tapestry.apache.org by Christian Riedel <ch...@gmx.net> on 2009/12/02 18:41:12 UTC

How to insert in a Grid

Hi,

I'd like to put a <tfoot> tag inside my table, but it seems the Tapestry 
Grid component is not able to do that... or can somebody point out a 
solution?

I was thinking of something like that:

<t:grid>
    ....

    <p:foot>
         the footer row!
    </p:foot>
</t:grid>

But the Grid.tml only seems to care about header and body:
   
(Grid.tml)
<table t:id="table">
        <thead t:id="columns"/>
        <tbody>
            <tr t:id="rows"/>
        </tbody>
</table>

The current alternative for me is to put another table or div below the 
actual grid, but that's not satisfying ;)

Thanks,
Christian


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
For additional commands, e-mail: users-help@tapestry.apache.org


Re: How to insert in a Grid

Posted by Christian Riedel <ch...@gmx.net>.
Thanks for the example, Thiago.

I modified your code it to make it work with my example.
Now, to make my tml[1] work I edit the mixin[2] to render the foot-block 
and then move it to the right place.
I had to use two less beautifull workarounds. The first one was that 
marker tag. Although the documentation sais the "MarkupWriter is more 
like a cursor into the DOM tree" I didn't find out how to get the 
cursor's position. So that's why there is a marker tag. Now I can find 
the previously rendered tfoot-block and grid-table, which are relative 
positioned to the marker.

Then comes my second workaround. Initially I wanted to call 
tfoot.moveToBottom(gridTable); but that causes a stack overflow because 
this move-method does not delete Node#nextSibling, only the previous 
lastChild's nextSibling - which was null. The nextSibling of the Element 
"tfoot" in my code points to the grid-div! So this causes some 
endless-loop. Is this worth a Jira?

Anyway, as my code is not perfect, yet, I'd appreciate any suggestions 
for improvement :)


[1] example.tml snippet


    <t:grid source="source" t:mixins="GridFooter">
        <p:foot>
            <tfoot>
                <tr>
                    <td colspan="3">
                        Some Footer
                    </td>
                </tr>
            </tfoot>
        </p:foot>
    </t:grid>


[2] GridFooter.java


@MixinAfter
public class GridFooter {
   
    @Parameter(name = "foot", defaultPrefix = BindingConstants.LITERAL)
    private Block foot;

    Object beginRender(MarkupWriter writer) {
        return foot;
    }

    @AfterRender
    void rewriteDOM(MarkupWriter writer) {

        // first workaround caused by the lack of xpath knowledge
        Element marker = writer.element("marker");
        writer.end();

        final Element tfoot = findNextElementByTag("tfoot", 
childElements(marker.getContainer()), 0);
        final Element gridDiv = firstSibling("div", tfoot);
        final Element gridTable = firstChild("table", gridDiv);
       
        // 2dn workaround: copy tfoot because moving it directly causes 
a stack overflow
        Element e = writer.element("tfoot");
        e.raw(tfoot.toString().replace("<tfoot>", 
"").replace("</tfoot>", ""));
        writer.end();
       
        e.moveToBottom(gridTable);
       
        tfoot.remove();
        marker.remove();
    }

    private List<Element> childElements(Element element) {

        List<Element> elements = new ArrayList<Element>();

        for (Node node : element.getChildren()) {

            if (node instanceof Element) {
                elements.add((Element) node);
            }

        }

        return elements;

    }

    private Element firstChild(String tag, Element element) {

        Element firstChild = null;

        List<Element> children = childElements(element);

        for (Element child : children) {

            if (child.getName().equals(tag)) {
                firstChild = child;
            } else {
                firstChild = firstChild(tag, child);
            }

            if (firstChild != null) {
                break;
            }

        }

        return firstChild;

    }

    private Element firstSibling(String tag, Element element) {
        Element firstSibling = null;

        List<Element> siblings = childElements(element.getContainer());

        Element sibling = null;
        for (int i = 0; i < siblings.size(); i++) {
            sibling = siblings.get(i);
           
            if (sibling.equals(element)) {
                firstSibling = findNextElementByTag(tag, siblings, i++);
                break;
            }
        }

        return firstSibling;
    }

    private Element findNextElementByTag(String tag, List<Element> 
siblings, int start) {
        if(siblings.size() <= start) return null;

        Element e = siblings.get(start);
       
        if(e.getName().equals(tag)) return e;
       
        return findNextElementByTag(tag, siblings, ++start);
    }

}


Howard Lewis Ship schrieb:
> Remember that the Grid can sometimes only take you so far; if you look
> at its implementation, you'll see that much of it is driven by
> reusable sub-components.
>
> I expect, in 5.2, to create an AbstractGrid that will be easier to
> subclass and extend.
>
>   
I know, but there is so much nice functionality in the Grid that I don't 
want have copy-pasted into some custom component. I rather write some 
Frankenstein-Mixin than copy-paste your work ;)
An AbstractGrid would be nice!

Christian
 

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
For additional commands, e-mail: users-help@tapestry.apache.org


Re: How to insert in a Grid

Posted by Howard Lewis Ship <hl...@gmail.com>.
Remember that the Grid can sometimes only take you so far; if you look
at its implementation, you'll see that much of it is driven by
reusable sub-components.

I expect, in 5.2, to create an AbstractGrid that will be easier to
subclass and extend.

On Thu, Dec 3, 2009 at 3:44 AM, Thiago H. de Paula Figueiredo
<th...@gmail.com> wrote:
> Em Thu, 03 Dec 2009 07:41:57 -0200, Christian Riedel
> <ch...@gmx.net> escreveu:
>
>> Rewriting the generated DOM sounds so easy but... How can I accomplish
>> that?
>
> I'll copy here a slighlty modified mixin I've made that adds a select tag in
> each header column. It would be simplified if I used XPath, I guess.
> I hope it'll help you.
>
> @MixinAfter // this is very important.
> @IncludeJavaScriptLibrary("classpath:/deselectColumnOptions.js")
> public class ColumnSelect {
>
>        @AfterRender
>        void rewriteDOM(MarkupWriter writer) {
>
>                Element element = writer.getElement();
>
>                final Element table = firstChild("table", element);
>                final Element thead = firstChild("thead", table);
>                final Element th = thead.find("tr");
>                final List<Element> headerCells = childElements(th);
>
>                for (Element headerCell : headerCells) {
>                        addColumnField(headerCell);
>                }
>                ...
>
>        }
>
>
>        private List<Element> childElements(Element element) {
>
>                List<Element> elements = new ArrayList<Element>();
>
>                for (Node node : element.getChildren()) {
>
>                        if (node instanceof Element) {
>                                elements.add((Element) node);
>                        }
>
>                }
>
>                return elements;
>
>        }
>
>        private Element firstChild(String tag, Element element) {
>
>                Element firstChild = null;
>
>                List<Element> children = childElements(element);
>
>                for (Element child : children) {
>
>                        if (child.getName().equals(tag)) {
>                                firstChild = child;
>                        }
>                        else {
>                                firstChild = firstChild(tag, child);
>                        }
>
>                        if (firstChild != null) {
>                                break;
>                        }
>
>                }
>
>                return firstChild;
>
>        }
>
>        private Element firstChild(String tag, String className, Element
> element) {
>
>                Element firstChild = null;
>
>                List<Element> children = childElements(element);
>
>                for (Element child : children) {
>
>                        if (child.getName().equals(tag) &&
> child.getAttribute("class").contains(className)) {
>                                firstChild = child;
>                        }
>                        else {
>                                firstChild = firstChild(tag, child);
>                        }
>
>                        if (firstChild != null) {
>                                break;
>                        }
>
>                }
>
>                return firstChild;
>
>        }
>
> }
>
>> <t:grid ... t:mixins="GridFooter" >
>>    <p:foot>
>>       <tr><td>my footer</td></tr>
>>    </p:foot>
>> </t:grid>
>> First problem was to get a <tfoot> tag rendered around the block "foot"
>> that I specified as a parameter for the mixin. I tried several things, but I
>> think I need a little help here.
>
> I don't know if you can get block parameters in mixins. If not, you cannot
> use <p:foot> in your template. You'd need to add the footer programatically.
>
> Your mixin and your methods should be @AfterRender.
>
>> Instead of the desired markup this will render <tfoot></tfoot> followed by
>> the contents of the "foot"-block although the render methods are called in
>> the right order. I cannot end <tfoot> in the afterRender method because it
>> would wrap around the whole t5-grid, and moving it in the dom would be even
>> more difficult. So how can I wrap some markup around a block?
>
> Use the Element methods do to that.
>
>> Second, I didn't make it to get the table element of the grid. I hope
>> afterRender is the right place for that.
>
> I think that the best way to understand a DOM-rewriting mixin is that it
> always works after the component is rendered completely.
>
>> I don't know if that xpath command would work if I'd find the gridElement
>> in the first place, but apart from that it should work like that, right? How
>> can I get the gridElement?
>
> If your mixin works after the grid is rendered, the current element
> (markupWriter.getElement()) is the parent element of the grid.
>
>> The autocompleter mixin and some others I've seen are injecting their
>> containers (@InjectContainer ... someField) and calling
>> someField.getClientId, which would maybe a solution if grid contained a
>> getClientId method...
>
> You would only need that if you needed to hook some JavaScript to the grid.
>
> --
> Thiago H. de Paula Figueiredo
> Independent Java, Apache Tapestry 5 and Hibernate consultant, developer, and
> instructor
> Owner, software architect and developer, Ars Machina Tecnologia da
> Informação Ltda.
> http://www.arsmachina.com.br
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
> For additional commands, e-mail: users-help@tapestry.apache.org
>
>



-- 
Howard M. Lewis Ship

Creator of Apache Tapestry

The source for Tapestry training, mentoring and support. Contact me to
learn how I can get you up and productive in Tapestry fast!

(971) 678-5210
http://howardlewisship.com

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
For additional commands, e-mail: users-help@tapestry.apache.org


Re: How to insert in a Grid

Posted by "Thiago H. de Paula Figueiredo" <th...@gmail.com>.
Em Thu, 03 Dec 2009 07:41:57 -0200, Christian Riedel  
<ch...@gmx.net> escreveu:

> Rewriting the generated DOM sounds so easy but... How can I accomplish  
> that?

I'll copy here a slighlty modified mixin I've made that adds a select tag  
in each header column. It would be simplified if I used XPath, I guess.
I hope it'll help you.

@MixinAfter // this is very important.
@IncludeJavaScriptLibrary("classpath:/deselectColumnOptions.js")
public class ColumnSelect {

	@AfterRender
	void rewriteDOM(MarkupWriter writer) {

		Element element = writer.getElement();
	
		final Element table = firstChild("table", element);
		final Element thead = firstChild("thead", table);
		final Element th = thead.find("tr");
		final List<Element> headerCells = childElements(th);
	
		for (Element headerCell : headerCells) {
			addColumnField(headerCell);
		}
		...

	}


	private List<Element> childElements(Element element) {

		List<Element> elements = new ArrayList<Element>();

		for (Node node : element.getChildren()) {

			if (node instanceof Element) {
				elements.add((Element) node);
			}

		}

		return elements;

	}

	private Element firstChild(String tag, Element element) {

		Element firstChild = null;

		List<Element> children = childElements(element);

		for (Element child : children) {

			if (child.getName().equals(tag)) {
				firstChild = child;
			}
			else {
				firstChild = firstChild(tag, child);
			}

			if (firstChild != null) {
				break;
			}

		}

		return firstChild;

	}

	private Element firstChild(String tag, String className, Element element)  
{

		Element firstChild = null;

		List<Element> children = childElements(element);

		for (Element child : children) {

			if (child.getName().equals(tag) &&  
child.getAttribute("class").contains(className)) {
				firstChild = child;
			}
			else {
				firstChild = firstChild(tag, child);
			}

			if (firstChild != null) {
				break;
			}

		}

		return firstChild;

	}

}

> <t:grid ... t:mixins="GridFooter" >
>     <p:foot>
>        <tr><td>my footer</td></tr>
>     </p:foot>
> </t:grid>
> First problem was to get a <tfoot> tag rendered around the block "foot"  
> that I specified as a parameter for the mixin. I tried several things,  
> but I think I need a little help here.

I don't know if you can get block parameters in mixins. If not, you cannot  
use <p:foot> in your template. You'd need to add the footer  
programatically.

Your mixin and your methods should be @AfterRender.

> Instead of the desired markup this will render <tfoot></tfoot> followed  
> by the contents of the "foot"-block although the render methods are  
> called in the right order. I cannot end <tfoot> in the afterRender  
> method because it would wrap around the whole t5-grid, and moving it in  
> the dom would be even more difficult. So how can I wrap some markup  
> around a block?

Use the Element methods do to that.

> Second, I didn't make it to get the table element of the grid. I hope  
> afterRender is the right place for that.

I think that the best way to understand a DOM-rewriting mixin is that it  
always works after the component is rendered completely.

> I don't know if that xpath command would work if I'd find the  
> gridElement in the first place, but apart from that it should work like  
> that, right? How can I get the gridElement?

If your mixin works after the grid is rendered, the current element  
(markupWriter.getElement()) is the parent element of the grid.

> The autocompleter mixin and some others I've seen are injecting their  
> containers (@InjectContainer ... someField) and calling  
> someField.getClientId, which would maybe a solution if grid contained a  
> getClientId method...

You would only need that if you needed to hook some JavaScript to the grid.

-- 
Thiago H. de Paula Figueiredo
Independent Java, Apache Tapestry 5 and Hibernate consultant, developer,  
and instructor
Owner, software architect and developer, Ars Machina Tecnologia da  
Informação Ltda.
http://www.arsmachina.com.br

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
For additional commands, e-mail: users-help@tapestry.apache.org


Re: How to insert in a Grid

Posted by Christian Riedel <ch...@gmx.net>.
Rewriting the generated DOM sounds so easy but... How can I accomplish that?
I'm facing some problems with that approach...

I tried to make that dummy-code working:

.tml
<t:grid ... t:mixins="GridFooter" >
    <p:foot>
       <tr><td>my footer</td></tr>
    </p:foot>
</t:grid>

First problem was to get a <tfoot> tag rendered around the block "foot" 
that I specified as a parameter for the mixin. I tried several things, 
but I think I need a little help here.

This is not working, but shows how I'd like to do it:

    @Parameter(name = "foot", defaultPrefix = BindingConstants.LITERAL)
    private Block foot;

    private Element tfoot, table;

    @BeginRender
       void renderBegin(MarkupWriter writer) {
        tfoot = writer.element("tfoot");
    }
   
    @BeginRender
    Object renderFoot(MarkupWriter writer) {
        return foot;
    }
   
    @BeginRender
    void renderTagEnd(MarkupWriter writer) {
        writer.end(); // end tfoot
    }

Instead of the desired markup this will render <tfoot></tfoot> followed 
by the contents of the "foot"-block although the render methods are 
called in the right order. I cannot end <tfoot> in the afterRender 
method because it would wrap around the whole t5-grid, and moving it in 
the dom would be even more difficult. So how can I wrap some markup 
around a block?

Second, I didn't make it to get the table element of the grid. I hope 
afterRender is the right place for that.
   
    void afterRender(MarkupWriter writer) {
        Element gridElement = ...  // that should point to grid's 
sorrounding div
        table = gridElement.find("./div/table");   
        tfoot.moveToBottom(table);
    }

I don't know if that xpath command would work if I'd find the 
gridElement in the first place, but apart from that it should work like 
that, right? How can I get the gridElement?
The autocompleter mixin and some others I've seen are injecting their 
containers (@InjectContainer ... someField) and calling 
someField.getClientId, which would maybe a solution if grid contained a 
getClientId method...

I'm open for any ideas :)

Thanks so far,
Christian


Thiago H. de Paula Figueiredo schrieb:
> Em Wed, 02 Dec 2009 15:41:12 -0200, Christian Riedel 
> <ch...@gmx.net> escreveu:
>
>> Hi,
>
> Hi!
>
>> I'd like to put a <tfoot> tag inside my table, but it seems the 
>> Tapestry Grid component is not able to do that... or can somebody 
>> point out a solution?
>
> You can write a mixin that rewrites the generated DOM and adds the tag 
> you want. Take a look at the Autocomplete mixin source to have an idea.
>

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
For additional commands, e-mail: users-help@tapestry.apache.org


Re: How to insert in a Grid

Posted by "Thiago H. de Paula Figueiredo" <th...@gmail.com>.
Em Wed, 02 Dec 2009 15:41:12 -0200, Christian Riedel  
<ch...@gmx.net> escreveu:

> Hi,

Hi!

> I'd like to put a <tfoot> tag inside my table, but it seems the Tapestry  
> Grid component is not able to do that... or can somebody point out a  
> solution?

You can write a mixin that rewrites the generated DOM and adds the tag you  
want. Take a look at the Autocomplete mixin source to have an idea.

-- 
Thiago H. de Paula Figueiredo
Independent Java, Apache Tapestry 5 and Hibernate consultant, developer,  
and instructor
Owner, software architect and developer, Ars Machina Tecnologia da  
Informação Ltda.
http://www.arsmachina.com.br

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
For additional commands, e-mail: users-help@tapestry.apache.org