The Renderer objects#

Creating a renderer#

As described in The Nagare component model, the views of a component receives a renderer. A renderer is a DOM objects factory that the developer uses to build the DOM tree of the view:

from nagare import presentation

class App(object):
    pass

@presentation.render_for(App):
def render(self, h, *args):
    # the `h` parameter is the renderer, used to generate a DOM tree
    return h.div(h.h1('Hello world!'))

By default, each view receives a new HTML renderer (instance of nagare.renderers.html.Renderer) suitable to build a synchronous HTML DOM tree.

A single view can also use a different renderer, for example to generate another XML dialect than HTML:

from nagare import presentation
from nagare.renderers import xml

class App(object):
    pass

@presentation.render_for(App):
def render(self, h, *args):
    h.response.content_type = 'text/xml'

    x = xml.Renderer(h)    # Creating a XML renderer
    return x.contact(x.name('John Doe'), x.age(20))

or to generate an asynchronous view of a component:

from nagare import presentation, component
from nagare.renderers import xhtml
from MyBlog import Blog

class App:
    def __init__(self):
        self.content = component.Component(Blog())

@presentation.render_for(App):
def render(self, h, *args):
    # Creating an asynchronous XHTML renderer
    a = h.AsyncRenderer()
    return h.div(self.content.render(a))

Note

The new renderer must be created with the current renderer as first parameter, to maintain a parent/child relationship between them.

Now, if all your views need to generate XML (or use any other renderer), it would be cumbersome to create a renderer in each view. Instead, you can tell Nagare to use another default renderer by setting the renderer_factory attribute of the WSGIApp, as shown in the example below:

from nagare import wsgi
from nagare.renderers import xml

class WSGIApp(wsgi.WSGIApp):
    # use the XML renderer by default
    renderer_factory = xml.Renderer

Then, all the views will receive an instance of xml.Renderer as renderer. Note that you can still create another renderer in the view as explained above.

Default renderers#

HTML renderer#

A HTML renderer is an instance of nagare.renderers.html.Renderer.

A HTML renderer is a factory for all the possible HTML tags. It also has:

  • a request and a response attributes which are WebOb request and response objects.

  • a head attribute which is a Head renderer instance, see below.

  • the following methods:

    • parse_html(self, source, fragment=False, no_leading_text=False, xhtml=False) – to read HTML or XHTML (chosen with the xhtml parameter) from the source file to a DOM tree. If fragment is True, the (X)HTML read can have multiple roots and the result will always be a list of DOM trees. Also, if fragment is True, if no_leading_text is True and the HTML begins by a text instead of a node, this text is removed.

    • parse_htmlstring(self, text, fragment=False, no_leading_text=False, xhtml=False) – as parse_html() but the HTML is read from the text string.

Head renderer#

The head attribute of a HTML renderer is a Head renderer, an instance of nagare.renderers.html.HeadRenderer.

A head renderer is a factory for all the HTML tags that can be embedded into the <head> section: base, head, link, meta, title, style and script:

from nagare import presentation

class App:
    pass

@presentation.render_for(App):
def render(self, h, *args):
    # h.head is a Head renderer
    h.head << h.head.title('My Application')

    return h.div(h.h1('Hello world !'))

A head renderer has also the following method:

  • css(self, name, style) – add a css style definition. The style must be named so that, if added by several views, it will be included only once in the generated page.

    @presentation.render_for(App):
    def render(self, h, *args):
        h.head.css('main_content', '.main { border: 1px red solid }')
    
        return h.div(h.h1('Hello world!'), class_='main')
    
  • css_url(self, url) – add a css url. If the url is relative, it’s relative to the static directory of the application.

    @presentation.render_for(App):
    def render(self, h, *args):
        h.head.css_url('http://www.nagare.org/site.css') # Absolute URL
        h.head.css_url('css/my_application.css')         # Relative URL
    
        return h.div(h.h1('Hello world!'), class_='main')
    
  • javascript(self, name, script) – add a javascript definition. The javascript code must be named so that, if added by several views, it will be included only once in the generated page.

    @presentation.render_for(App):
    def render(self, h, *args):
        h.head.javascript('debug', 'function debug(msg) { alert(msg) }')
    
        return h.div(h.h1('Hello world!'), class_='main')
    
  • javascript_url(self, url) – add a javascript url. If the url is relative, it’s relative to the static directory of the application

    @presentation.render_for(App):
    def render(self, h, *args):
        h.head.javascript_url('http://www.nagare.org/anim.js') # Absolute URL
        h.head.javascript_url('js/debug.js')                   # Relative URL
    
        return h.div(h.h1('Hello world!'), class_='main')
    

After the rendering phase, Nagare generates a <head> section which is the concatenation of all the DOM objects the different views have put into the Head renderer.

HTML5 renderer#

A HTML5 renderer is an instance of nagare.renderers.html5.Renderer.

A HTML5 renderer is a factory for all the possible HTML5 from the HTML5 specification.

Since it’s extremely likely that you want to use this renderer in all the views of your application instead of the default HTML renderer, you can install it like this:

from nagare import wsgi
from nagare.renderers import xhtml5

class WSGIApp(wsgi.WSGIApp):
    # use the HTML5 renderer by default
    renderer_factory = xhtml5.Renderer

Then, all the views will receive a HTML5 renderer by default.

This renderer is like an HTML renderer except that the set of available tags and attributes is a little different. For example, HTML5 offers <audio>, <video> and <canvas> tags and more structural markup such as <section>, <article>, <aside>, <nav>, <figure>, <header> and <footer> tags.

XML renderer#

A XML renderer is an instance of nagare.renderers.xml.Renderer.

It’s a simple renderer which accepts any tag creation:

>>> from nagare.renderers import xml

>>> x = xml.Renderer()
>>> tree = x.contact(x.name('John Doe'), x.age(20))

>>> tree.tostring()
'<contact><name>John Doe</name><age>20</age></contact>'

XSL renderer#

A XSL renderer is an instance of nagare.renderers.xsl.Renderer and has a factory for all the possible XSL tags.

ESI renderer#

A ESI renderer is an instance of nagare.renderers.esi.Renderer and has a factory for all the possible ESI tags : attempt, choose, comment, include, inline, otherwise, remove, try, vars, when.

Namespaces and renderers mixin#

A renderer can create DOM object in a specific XML namespace. First the namespace(s) must be declared to the renderer by setting its namespaces attribute to a namespace prefix -> namespace uri dictionary. Then, at any moment, the namespace to use is choosen by setting its default_namespace atttribute to the prefix of the namespace:

>>> from nagare.renderers import xsl

>>> x = xsl.Renderer()
>>> x.namespaces = {'xsl': 'http://www.w3.org/1999/XSL/Transform' }
>>> x.default_namespace = 'xsl'

>>> xsl = x.stylesheet(
...           x.output(encoding='utf-8'),
...           x.template(
...               x.copy_of(select='.'),
...               match='/'
...           )
...       )

>>> print(xsl.tostring(pretty_print=True))
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output encoding="utf-8"/>
  <xsl:template match="/">
    <xsl:copy-of select="."/>
  </xsl:template>
</xsl:stylesheet>

Different renderers, with different namespaces, can be combined to build a DOM tree:

>>> from nagare.renderers import xhtml, esi

>>> h = xhtml.Renderer()

>>> s = esi.Renderer()
>>> s.namespaces = {'esi': esi.NS }
>>> s.default_namespace = 'esi'

>>> tree = h.html(
...            h.body(
...                h.h1('Hello'),
...                s.include(src='http://www.nagare.org'),
...                h.p('world')
...            )
...        )
>>> print(tree.tostring(pretty_print=True))
<html>
  <body>
    <h1>Hello</h1>
    <esi:include xmlns:esi="http://www.edge-delivery.org/esi/1.0" src="http://www.nagare.org"/>
    <p>world</p>
  </body>
</html>