render_if: Conditional Parts in Nevow Templates

This Nevow renderer has saved me a lot of time:

def render_if(self, ctx, data):
   r=ctx.tag.allPatterns(str(bool(data)))
   return ctx.tag.clear()[r]

Use it like this:

<nevow:invisible nevow:render="if" nevow:data="items">
  <ul nevow:pattern="True"
  nevow:render="sequence">
	<li nevow:pattern="header">The items are a-coming!</li>
	<li nevow:pattern="item">(the items will be here)</li>
  </ul>
</nevow:invisible>

And now, if the list returned by data_items is empty, there will be no <ul> tag at all in the output.

I just realized non-boolean tests may be wanted -- for example, test if a string matches a regexp. You could do that by mangling the data before render_if, but that's not nice, because then you don't have access to the original data inside nevow:pattern="True". So, instead let's parametrize the test:

def render_ifparam(self, name):
   tester = getattr(self, 'tester_%s' % name, None)

   if tester is None:
	   callable = lambda context, data: context.tag[
			"The tester named '%s' was not found in %r." % (name, self)]
	   return callable

   def f(ctx, data):
	   r=ctx.tag.allPatterns(str(bool(tester(data))))
   return ctx.tag.clear()[r]

   return f

Note how we still cast the return value of the tester to boolean. You could avoid that and call the renderer render_switch. Adding support for Deferred tests would be quite easy, too. The only ugly part is I don't know of any way to make the same renderer work nicely for nevow:render="if" and nevow:render="ifparam foo".

[Updated to add return f, also renamed second render_if to render_ifparam to clarify things a bit. Thanks k3mper.]

2006-03-22T22:52:00+02:00, originally published 2005-12-17T16:59:00+02:00