Presentation tier

This document describes how to generate HTML and XML dialects with the service offered by the framework.

The Nagare component model explains how to associate HTML or XML views to the components.

Principles

With Nagare, to generate a HTML text, you always build a tree of objects in memory, a Document Objects Model (not the DOM version of the W3C), that you can serialize in a (X)HTML text.

One benefic effect of this serialization is that the generated HTML or XHTML is always valid.

The framework offers to you three ways to generate the DOM:

  1. functional
  2. imperative
  3. templating

It’s important to note that these three methods all generate a DOM and so can be freely mixed to build an aggregating DOM.

DOM element creation

A Renderer is a DOM element factory. For example, the XHTML renderer nagare.namespaces.xhtml.Renderer has an attribute, for each XHTML tag, that create a DOM element:

>>> from nagare.namespaces import xhtml
>>> h = xhtml.Renderer()
>>> h.div
<Element div at 8410b6c>

DOM elements API

ElementTree API

A DOM element is an instance of the class nagare.namespaces.xml._Tag. As this class inherits from lxml.etree.Element, the DOM element offers an ElementTree API.:

>>> from nagare.namespaces import xhtml
>>> h = xhtml.Renderer()

>>> root = h.div
>>> root.set('id', 'content')

>>> title = h.h1
>>> title.text = 'Hello world'

>>> root.append(title)
>>> root.write_htmlstring()
'<div id="content"><h1>Hello world</h1></div>'

Lxml API

You can also validate a DOM tree against a DTD, XML Schema, RelaxNG or Schematron document.

And you can use XPath and Xsl transformations.

Functional DOM building API

The _Tag class implements a __call__ method to add children, text or attributes to a DOM element:

def __call__(self, *children, **attributes)

Each element in children can be a:

  • DOM element that is added as a child of the DOM element
  • string, unicode string, integer or float that is converted to unicode string and added as data to the DOM element
  • list, tuple or generator which each elements are added to the DOM element
  • dictionary which each items are added as attributes of the DOM element
  • component comp wich will be rendered with its default view (i.e comp.render(h)) and the resulting DOM tree added to the DOM element

Each items in attributes are added as attributes of the DOM element.

Note

As a keyword parameter cannot have the name of a Python reserved keyword, add a _ character to the name (i.e: h.div(class_='content'))

Using the DOM element creation API of the renderers, with the nesting of calls to the created DOM element, a DOM tree can be built in a functional way:

>>> from nagare.namespaces import xhtml
>>> h = xhtml.Renderer()

>>> root = h.div(h.h1('Hello world'), id='content')
>>> root.write_htmlstring()
<div id="content"><h1>Hello world</h1></div>'

Imperative DOM building API

A DOM element is also a context manager

Once a context is opened using the with statement, children can be added to the context manager using the << operator on the renderer.

So a DOM tree can also be imperatively built from the nesting of contexts. With its root finally retrieved from the attribute root of the renderer:

>>> from nagare.namespaces import xhtml
>>> h = xhtml.Renderer()

>>> with h.div:
...     h << { 'id' : 'content' }
...     with h.h1:
...         h << 'Hello world'

>>> root = h.root
>>> root.write_htmlstring()
'<div id="content"><h1>Hello world</h1></div>'

Note

In a component view, don’t forget to return the tree with h.root

Remember that the different ways to build a DOM tree can be freely mixed, so the example above can be rewrote as:

>>> from nagare.namespaces import xhtml
>>> h = xhtml.Renderer()

>>> with h.div(id='content'):
...     h << h.h1('Hello world')

>>> root = h.root
>>> root.write_htmlstring()
'<div id="content"><h1>Hello world</h1></div>'

Or, to create a HTML list from the Python list l:

# Functional
>>> root = h.ul([u.li(element) for element in l])

# Imperative
>>> with h.ul:
...     for element in l:
...         h << h.ul(element)
>>> root = h.root

Template

The templating system in Nagare follows the idea of the meld family: the designer who creates the XHTML template only gives identifiers to nodes. Then the developer can parse the template into a DOM tree, search for the DOM elements given their identifiers and manipulates them in Python.

1. Creating the template

The template must be a valid XML document, for example a XHTML template and the namespace http://www.plope.com/software/meld3 must be declared.

To mark the nodes, use the attribute id of this namespace:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:meld="http://www.plope.com/software/meld3">
    <body>
        <h1 meld:id="title">Hello</h1>
    </body>
</html>

The Python helper method meld_id() is available to add a meld:id attribute to a DOM element:

>>> from nagare.namespaces import xhtml
>>> h = xhtml.Renderer()

>>> tree = h.html(
...            h.body(
...                h.h1('Hello').meld_id('title')
...            )
...        )
>>> print tree.write_xmlstring(pretty_print=True)
<html>
  <body>
    <h1 xmlns:ns0="http://www.plope.com/software/meld3" ns0:id="title">Hello</h1>
  </body>
</html>

2. Parsing the template

You can use the parse_htmlstring() method of a xhtml renderer to parse a template:

>>> from nagare.namespaces import xhtml
>>> h = xhtml.Renderer()

>>> root = h.parse_html('/tmp/template.xml', xhtml=True)

>>> print root.write_xmlstring()
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:meld="http://www.plope.com/software/meld3">
    <body>
        <h1 meld:id="title">Hello</h1>
    </body>
</html>

3. Finding the DOM elements

Use the findmeld() method of the DOM elements to retreive the marked nodes:

>>> from nagare.namespaces import xhtml
>>> h = xhtml.Renderer()

>>> root = h.parse_html('/tmp/template.xml', xhtml=True)

>>> root.findmeld('title')
<Element {http://www.w3.org/1999/xhtml}h1 at 8410c5c>

4. Manipulating the DOM elements

Once a DOM element is found, you can manipulate it with the normal ElementTree, Lxml, functional or imperative API:

>>> from nagare.namespaces import xhtml
>>> h = xhtml.Renderer()

>>> root = h.parse_html('/tmp/template.xml', xhtml=True)

>>> t = root.findmeld('title')
>>> t.text = 'World'

>>> print root.write_xmlstring()
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:meld="http://www.plope.com/software/meld3">
    <body>
        <h1 meld:id="title">World</h1>
    </body>
</html>

These three methods of the DOM elements are helpers to work with templates:

  • fill(self, *children, **attributes) – replaces the children of the DOM element by the given children and appends the attributes to the DOM element current attributes.

    >>> from nagare.namespaces import xhtml
    >>> h = xhtml.Renderer()
    
    >>> root = h.div(h.h1('Hello world'), id='content')
    >>> root.write_htmlstring()
    <div id="content"><h1>Hello world</h1></div>'
    
    >>> root.fill('Go to ', h.a('Nagare home', href='http://www.nagare.org'), class_='description')
    
    >>> root.write_htmlstring()
     '<div id="content" class="description">Go to <a href="http://www.nagare.org">Nagare home</a></div>'
    
  • replace(self, *children) – replace the DOM element by the given children:

    >>> from nagare.namespaces import xhtml
    >>> h = xhtml.Renderer()
    
    >>> root = h.div(h.h1('Hello world'), id='content')
    >>> root.write_htmlstring()
    <div id="content"><h1>Hello world</h1></div>'
    >>> root[0].write_htmlstring()
    <h1>Hello world</h1>
    
    >>> root[0].replace(h.h2('Nagare'))
    
    >>> root.write_htmlstring()
    '<div id="content"><h2>Nagare</h2></div>'
    
  • repeat(self, iterable, childname=None)

    Repeats an element with values from an iterable.

    If childname is not None, repeat the element on which repeat was called, otherwise find the child element with a meld:id matching childname and repeat that. The element is repeated within its parent element.

    This method returns an iterable; the value of each iteration is a two-sequence in the form (newelement, data). newelement is a clone of the template element (including clones of its children) which has already been seated in its parent element in the template. data is a value from the passed in iterable. Changing newelement (typically based on values from data) mutates the element “in place”.

    >>> from nagare.namespaces import xhtml
    >>> h = xhtml.Renderer()
    
    >>> root = h.parse_xmlstring('''<ul xmlns:meld="http://www.plope.com/software/meld3">
    ...   <li meld:id="alist" align="center">A line example</li>
    ... </ul>''')
    
    >>> items = ['Item %d' % i for i in range(3)]
    
    >>> for (element, item) in root.findmeld('alist').repeat(items):
    ...     element.text = item
    ...
    
    >>> print root.write_xmlstring()
    <ul xmlns:meld="http://www.plope.com/software/meld3">
    <li align="center">Item 0</li>
    <li align="center">Item 1</li>
    <li align="center">Item 2</li>
    </ul>
    

DOM tree serialization

Once a tree of DOM elements is build, it can be serialized in (X)HTML:

  • write_xmlstring(self, encoding='utf-8', pipeline=True) – serializes the DOM tree into a XML string, encoded according to the encoding parameter. If pipeline is True, the eventual meld:id attributes are removed, else they are kept so that the xml string can be re-parse as a template.
  • write_htmlstring(self, encoding='utf-8', pipeline=True) – serializes the DOM tree into a HTML string, encoded according to the encoding parameter. If pipeline is True, the eventual meld:id attributes are removed.