Context-aware Tags?

12 views
Skip to first unread message

Ian Tegebo

unread,
May 30, 2015, 2:47:13 PM5/30/15
to gra...@googlegroups.com
In reviewing Mrdigs' Bootstrap.jsp, I noticed that he makes use of SimpleTagSupport's "findAncestorWithClass" method:


In considering how I might use ideas from Bootstrap.jsp in my own Grails taglib, I realized that I don't know how to write similar context-aware tags.  Of course, I know I'm supposed to able to simply use his JSP taglib from Grails; this question is about whether and how one can write context-aware tags in GSP.

Aleh Bykhavets

unread,
May 31, 2015, 8:04:36 AM5/31/15
to gra...@googlegroups.com
TagLib made in Grails is plain simple closure.  Yet you can rely on implicit `pageScope` variable to do the trick.
Or fall back to JSP taglibs.

https://grails.github.io/grails-doc/latest/ref/Tag%20Libraries/pageScope.html

Besides example code at that page I can think of some variations, all using the same method.

1) push/pop from stack

def stack = pageScope._tagStack ?: []
// ...
// ... find parent, sanity check, etc

stack.push(object_that_defines_this_tag)
try {
  body() // <-- process child elements
} finally {
  stack.pop()
}

Thus, all logic is around that stack.  A child access it to fetch list/data of ancestors.
To protect stack from damage you can clone it before invoking body() and restore after that.
Even better, use utility class/methods that prevent your from copy-paste that code in all related tags.


2) expose parent object that tracks hierarchy
So you can hide assertion checks and other logic in it.
And control whether a child should know more than its direct parent.

def parent = pageScope._parent
// ...
// ... parent may know grant parent and so on...

def me = parent.createChild()

// substitute active parent reference for my children
pageScope._parent = me

try {
  body()  // <-- go deeper
} finally {
  // restore real parent for next sibling, if any
  pageScope._parent = parent
}


3) gather children in list
In some cases you may need to postpone execution of body() of children and delegate that to their parent/ancestor.
At the moment of execution each child may know what siblings were before one but knows nothing about what come after.

For example, to divide grid row equally among 1/2/3/4 columns.
While some of them may be omitted with <g:if test="..."></g:if>.
Until you gather all columns you have no idea which bootstrap class (col-md-#) is required.

Here parent can add some list in pageScope:
pageScope._columns = []
body()
// ... process pageScope._columns later

Where each child have to place reference on it's body variable instead of invoking it:
pageScope._columns << body

Note that all other output inside parent-tag but outside of selected children will be omitted.
Or replace `body()` with `out << body()` at parent and have a nice mix.

Ian Tegebo

unread,
Jun 1, 2015, 11:40:18 AM6/1/15
to gra...@googlegroups.com
Thanks Aleh,

While I was hoping someone would point out something along the lines of how GSP may be compiled to JSP and so could use its methods somehow, your suggestions are much appreciated.

I believe your use of an underscore as a prefix is meant to convey privacy, or implementation details, so I'd assume that if I were to use this trick in a plugin, I'd want to use some suitable unique prefix within the pageScope to avoid collisions with user code, e.g. "pageScope._bootstrapPlugin".

I'll report back if and when I use some combination of these approaches to deal with Bootstrap's various context dependencies.
Reply all
Reply to author
Forward
0 new messages