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:
- functional
- imperative
- 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.ecomp.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 theattributes
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 notNone
, repeat the element on which repeat was called, otherwise find the child element with ameld:id
matchingchildname
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. Changingnewelement
(typically based on values fromdata
) 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 theencoding
parameter. Ifpipeline
is True, the eventualmeld: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 theencoding
parameter. Ifpipeline
is True, the eventualmeld:id
attributes are removed.