Chapter 5: Les vues

Les vues

views
template language
HTML

web2py utilise Python pour ses modèles, contrôleurs, et vues, bien qu'il utilise une syntaxe légèrement modifiée de Python dans les vues pour permettre un code plus lisible sans imposer de restrictions sur l'usage habituel de Python.

Le but d'une vue est d'embarquer du code (Python) dans un document HTML. En général, ceci pose certains problèmes :

  • Comment le code embarqué devrait être échappé ?
  • L'indentation devrait-elle être basée sur Python ou sur les règles HTML ?

web2py utilise{{ ... }} pour échapper du code Python embarqué dans l'HTML. L'avantage d'utiliser les accolades plutôt que des parenthèses est la transparence vis-à-vis de tous les éditeurs HTML communs. Ceci permet au développeur d'utiliser ces éditeurs pour créer des vues web2py. Ces délimiteurs peuvent être changés par exemple avec

response.delimiters = ('<?','?>')

Si cette ligne est dans un modèle, elle sera appliquée partout, si elle est dans un contrôleur seules les vues attachées aux actions du contrôleur, si dans une action elle n'impactera que la vue attachée à cette action.

Tant que le développeur embarque du code Python dans l'HTML, le document devrait être indenté en respectant les règles HTML, et non celles Python. Il est donc permis d'utiliser du code Python non indenté dans les tags {{ ... }}. Puisque Python utilise logiquement l'indentation pour délimiter les blocs de code, nous avons besoin d'un moyen différent de les délimiter ; c'est pourquoi le template de langue web2py ajoute l'usage du mot-clé Python pass.

Un bloc de code démarre avec une ligne finissant avec deux-points et se termine avec une ligne commençant par pass. Le mot-clé pass n'est pas nécessaire lorsque la fin du bloc est évidente dans le contexte.

Voici un exemple :

{{
if i == 0:
response.write('i is 0')
else:
response.write('i is not 0')
pass
}}

Notez que pass est un mot-clé Python, et non un mot-clé web2py. Certains éditeurs Python, tels que Emacs, utilisent le mot-clé pass pour spécifier la division de blocs et l'utilise pour ré-indenter le code automatiquement.

Le template de langue web2py fait exactement de même. Lorsqu'il trouve quelque chose comme :

<html><body>
{{for x in range(10):}}{{=x}}hello<br />{{pass}}
</body></html>

il le traduit dans un programme :

response.write("""<html><body>""", escape=False)
for x in range(10):
    response.write(x)
    response.write("""hello<br />""", escape=False)
response.write("""</body></html>""", escape=False)

response.write écrit dans le response.body.

Lorsqu'il y a une erreur dans une vue web2py, le rapport d'erreur montre le code la vue générée, et non la vue actuelle telle qu'écrite par le développeur. Ceci aide le développeur à débuguer le code en soulignant le code actuel qui est exécuté (qui est quelque chose qui peut être débugué avec un éditeur HTML ou un inspecteur DOM de navigateur).

Notez également que :

{{=x}}

génère

response.write
escape

response.write(x)

Les variables injectées dans l'HTML par ce moyen sont échappées par défaut. L'échappement est ignoré si x est un objet XML, même si l'échappement est défini à True.

Voici un exemple qui présente l'helper H1 :

{{=H1(i)}}

qui est traduit en :

response.write(H1(i))

durant l'évaluation, l'objet H1 et ses composant sont récursivement sérialisés, échappés et écrits vers le corps de réponse. Les tags générés par H1 et le contenu HTML ne sont pas échappés. Ce mécanisme garantit que tout texte --- et seulement le texte --- affiché sur la page web est toujours échappé, évitant ainsi les vulnérabilités XSS. De même, le code est simple et peut être facilement débugué.

La méthode response.write(obj, escape=True) prend 2 arguments, l'objet à écrire et s'il doit être échappé (défini à True par défaut). Si obj a une méthode .xml(), il est appelé et le résultat est écrit dans le corps de la réponse (l'argument escape est ignoré). Autrement, il utilise la méthode __str__ de l'objet pour le sérialiser, et, si l'argument d'échappement est True, l'échappe. Tous les objets helper pré-construits (H1 dans l'exemple) sont des objets qui savent comment se sérialiser via la méthode .xml().

Tout ceci est fait de manière transparente. Vous n'avez jamais besoin (et ne devriez jamais) appeler la méthode response.write explicitement.

Syntaxe basique

Le template web2py supporte toutes les structures de contrôle Python. Nous fournissons ici quelques exemples pour chacun d'entre eux. Ils peuvent être imbriqués selon les pratiques habituelles de programmation.

for...in

for

Dans les templates, vous pouver parcourir n'importe quel objet itérable :

{{items = ['a', 'b', 'c']}}
<ul>
{{for item in items:}}<li>{{=item}}</li>{{pass}}
</ul>

ce qui produit :

<ul>
<li>a</li>
<li>b</li>
<li>c</li>
</ul>

Ici, item est un objet itérable telle une liste Python, un tuple Python, ou un objet Rows, ou tout autre objet qui est implémenté comme itérateur. Les éléments affichés sont d'abord sérialisés puis échappés.

while

while

Vous pouvez créer une boucle en utilisant le mot-clé while :

{{k = 3}}
<ul>
{{while k > 0:}}<li>{{=k}}{{k = k - 1}}</li>{{pass}}
</ul>

qui produit :

<ul>
<li>3</li>
<li>2</li>
<li>1</li>
</ul>

if...elif...else

if
elif
else

Vous pouvez utiliser des clauses conditionnelles :

{{
import random
k = random.randint(0, 100)
}}
<h2>
{{=k}}
{{if k % 2:}}is odd{{else:}}is even{{pass}}
</h2>

qui produit :

<h2>
45 is odd
</h2>

Puisqu'il est évident que else ferme le premier bloc if, il n'y a pas besoin de déclaration pass, et en utiliser une serait incorrect. Cependant, vous devez fermer explicitement le bloc else avec un pass.

Pour rappel, en Python "else if" est écrit elif comme dans l'exemple suivant :

{{
import random
k = random.randint(0, 100)
}}
<h2>
{{=k}}
{{if k % 4 == 0:}}is divisible by 4
{{elif k % 2 == 0:}}is even
{{else:}}is odd
{{pass}}
</h2>

Ceci produit :

<h2>
64 is divisible by 4
</h2>

try...except...else...finally

try
except
else
finally

Il est également possible d'utiliser les déclarations try...except dans les vues, avec un désavantage. Considérez l'exemple suivant :

{{try:}}
Hello {{= 1 / 0}}
{{except:}}
division by zero
{{else:}}
no division by zero
{{finally}}
<br />
{{pass}}

Il produira la sortie suivante :

Hello
division by zero
<br />

Cet exemple illustre que toute sortie générée avant qu'une exception survienne est rendue (incluant la sortie qui précède l'exception) dans le bloc try. "Hello" est écrite puisqu'il précède l'exception.

def...return

def
return

Le template web2py permet au développeurs de définir et implémenter des fonctions qui peuvent retourner n'importe quel objet Python dans une chaîne text/html. Nous considérons ici deux exemples :

{{def itemize1(link): return LI(A(link, _href="http://" + link))}}
<ul>
{{=itemize1('www.google.com')}}
</ul>

produit la sortie suivante :

<ul>
<li><a href="http:/www.google.com">www.google.com</a></li>
</ul>

La fonction itemize1 retourne un objet helper qui est inséré à l'endroit où la fonction est appelée.

Considérons maintenant le code suivant :

{{def itemize2(link):}}
<li><a href="http://{{=link}}">{{=link}}</a></li>
{{return}}
<ul>
{{itemize2('www.google.com')}}
</ul>

Il produit exactement la même sortie que ci-dessus. Dans ce cas, la fonction itemize2 représente un morceau d'HTML qui va être remplacé par le tag web2py où la fonction est appelée. Notez qu'il n'y a pas de '=' devant l'appel à itemize2, tant que la fonction ne retourne pas le texte, mais l'écrit directement dans la réponse.

Il y a une faiblesse : les fonctions définies dans une vue doivent se terminer avec une déclaration return, ou l'indentation automatique échouera.

HTML helpers

helpers

Considérez le code suivant dans une vue :

{{=DIV('this', 'is', 'a', 'test', _id='123', _class='myclass')}}

rendu en :

<div id="123" class="myclass">thisisatest</div>

DIV est une classe helper, i.e., quelque chose qui peut être utilisé pour construire du HTML via un programme. Il correspond au tag HTML <div>.

Les arguments de position sont interprétés comme des objets contenus entre les tags d'ouverture et de fermeture. Les arguments nommés qui démarrent avec un underscore sont interprétés comme des attributes de tag HTML (sans l'underscore). Certains helpers ont également des arguments nommés qui ne débutent pas avec un underscore ; ces arguments sont spécifiques aux tags.

Au lieu d'un ensemble d'arguments non nommés, un helper peut également prendre une simple liste ou tuple comme son ensemble de composants e nutilisant la notation * et peut prendre un simple dictionnaire comme ensemble d'attributs en utilisant **, par exemple :

{{
contents = ['this','is','a','test']
attributes = {'_id':'123', '_class':'myclass'}
=DIV(*contents,**attributes)
}}

(produit la même sortie que précédemment).

L'ensemble suivant de helpers :

A, B, BEAUTIFY, BODY, BR, CAT, CENTER, CODE, COL, COLGROUP, DIV, EM, EMBED, FIELDSET, FORM, H1, H2, H3, H4, H5, H6, HEAD, HR, HTML, I, IFRAME, IMG, INPUT, LABEL, LEGEND, LI, LINK, MARKMIN, MENU, META, OBJECT, ON, OL, OPTGROUP, OPTION, P, PRE, SCRIPT, SELECT, SPAN, STYLE, TABLE, TAG, TBODY, TD, TEXTAREA, TFOOT, TH, THEAD, TITLE, TR, TT, UL, URL, XHTML, XML, embed64, xmlescape

peut être utilisé pour construire des expressions complexes qui peuvent être sérialisées en XML[xml-w] [xml-o]. Par exemple :

{{=DIV(B(I("hello ", "<world>"))), _class="myclass")}}

est rendu en :

<div class="myclass"><b><i>hello &lt;world&gt;</i></b></div>

Les helpers peuvent également être sérialisés en chaînes de caractères, de manière équivalente, avec les méthodes __str__ et xml :

>>> print str(DIV("hello world"))
<div>hello world</div>
>>> print DIV("hello world").xml()
<div>hello world</div>
Document Object Model (DOM)

Les mécanismes de helpers en web2py est plus qu'un système pour générer l'HTML sans concaténer les chaînes. Il fournit une représentation côté serveur du Document Object Model (DOM).

Les composants des helpers peuvent être référencés via leur position, et les helpers agissent comme des listes qui respectent leurs composants :

>>> a = DIV(SPAN('a', 'b'), 'c')
>>> print a
<div><span>ab</span>c</div>
>>> del a[1]
>>> a.append(B('x'))
>>> a[0][0] = 'y'
>>> print a
<div><span>yb</span><b>x</b></div>

Les attributs de helpers peuvent être référencés par nom, et les helpers agissent comme des dictionnaires en respectant leurs attributs :

>>> a = DIV(SPAN('a', 'b'), 'c')
>>> a['_class'] = 's'
>>> a[0]['_class'] = 't'
>>> print a
<div class="s"><span class="t">ab</span>c</div>

Note, l'ensemble complet des composants peut être accédé via une liste appelée a.components, et l'ensemble complet des attributs peut être accédé via un dictionnaire appelé a.attributes. Alors, a[i] est équivalent à a.components[i] lorsque i est un entier, et a[s] est équivalent à a.attributes[s] lorsque s est une chaîne.

Notez que les attributs de helper sont passés comme des arguments mots-clés pour le helper. Dans certains cas, cependant, les noms d'attributs incluent des caractères spéciaux qui ne sont pas autorisés dans les identificateurs Python (e.g. traits d'union) et donc ne peuvent pas être utilisés comme nom pour les arguments. Par exemple :

DIV('text', _data-role='collapsible')

ne fonctionnera pas car "_data-role" inclut un trait d'union, ce qui produit une erreur de syntaxe Python.

Dans de tes cas vous avez différentes options. Vous pouvez utiliser l'argument data (cette fois-ci sans underscore) pour passer un dictionnaire des attributs en relation sans leur trait d'union, et la sortie aura les combinaisons désirées e.g.

>>> print DIV('text', data={'role': 'collapsible'})
<div data-role="collapsible">text</div>

ou vous pouvez passer à la place des attributs comme un dictionnaire et faire usage de la notation Python ** pour les arguments de fonction, qui mappe un dictionnaire de paires (key:value) en un ensemble d'arguments mots-clé :

>>> print DIV('text', **{'_data-role': 'collapsible'})
<div data-role="collapsible">text</div>

Notez que des entrées plus élaborées présenteront des entités de caractères HTML, mais fonctionneront néanmoins e.g.

>>> print DIV('text', data={'options':'{"mode":"calbox", "useNewStyle":true}'})
<div data-options="{&quot;mode&quot;:&quot;calbox&quot;, &quot;useNewStyle&quot;:true}">text</div>

Vous pouvez aussi créer dynamiquement des TAGs spéciaux :

>>> print TAG['soap:Body']('whatever',**{'_xmlns:m':'http://www.example.org'})
<soap:Body xmlns:m="http://www.example.org">whatever</soap:Body>

XML

XML

XML est un objet utilisé pour encapsuler du texte qui ne devrait pas être échappé. Le texte peut contenir ou pas du code XML valide. Par exemple, il pourrait contenir du Javascript.

Le texte dans cet exemple est échappé :

>>> print DIV("<b>hello</b>")
&lt;b&gt;hello&lt;/b&gt;

en utilisant XML vous pouvez éviter l'échappement :

>>> print DIV(XML("<b>hello</b>"))
<b>hello</b>

Parfois vous souhaitez rendre de l'HTML stocké dans une variable, mais l'HTML peut contenir des tags non surs tels que des scripts :

>>> print XML('<script>alert("unsafe!")</script>')
<script>alert("unsafe!")</script>

Une entrée exécutable non échappée telle que cela (par exemple, entrer dans le corps d'un commentaire dans un blog) n'est pas sûr, puisqu'elle peut être utilisée pour générer des attaques Cross Site Scripting (XSS) contre d'autres visiteurs de la page.

sanitize

L'helper web2py XML peut sécuriser notre texte pour éviter les injections et échapper tous les tags sauf ceux que vous autorisez explicitement. Voici un exemple :

>>> print XML('<script>alert("unsafe!")</script>', sanitize=True)
&lt;script&gt;alert(&quot;unsafe!&quot;)&lt;/script&gt;

Les constructeurs XML considèrent par défaut le contenu de certains tags et certains attributs comme sûrs. Vous pouvez écraser ces défauts en utilisation les arguments optionnels permitted_tags et allowed_attributes. Voici les valeurs par défaut des arguments optionnels du helper XML.

XML(text, sanitize=False,
    permitted_tags=['a', 'b', 'blockquote', 'br/', 'i', 'li',
       'ol', 'ul', 'p', 'cite', 'code', 'pre', 'img/'],
    allowed_attributes={'a':['href', 'title'],
       'img':['src', 'alt'], 'blockquote':['type']})

Helpers pré-compilés

A

Ce helper est utilisé pour construire des liens.

A
>>> print A('<click>', XML('<b>me</b>'),
            _href='http://www.web2py.com')
<a href='http://www.web2py.com'>&lt;click&gt;<b>me/b></a>

Au lieu de _href vous pouvez passer l'URL en utilisant l'argument callback. Par exemple, dans une vue :

{{=A('click me', callback=URL('myaction'))}}

et l'effet de presser le lien entraînera un appel ajax à "myaction" plutôt qu'une redirection. Dans ce cas, vous pouvez spécifier de manière optionnelle deux arguments : target et delete :

{{=A('click me', callback=URL('myaction'), target="t")}}
<div id="t"><div>

et la réponse du callback ajax sera stockée dans le DIV avec un id égal à "t".

<div id="b">{{=A('click me', callback=URL('myaction'), delete='div#b")}}</div>

et en réponse, le tag correspondant le plus à "div#b" sera supprimé. Dans ce cas, le bouton sera supprimé. Une application typique est :

{{=A('click me', callback=URL('myaction'), delete='tr")}}

dans une table. En le pressant, le bouton effectuera la callback et supprimera la ligne de la table.

callback et delete peuvent être combinés.

Le helper A prend un argument spécial appelé cid. Il fonctionne ainsi :

{{=A('linked page', _href='http://example.com', cid='myid')}}
<div id="myid"></div>

et un clic sur le lien entraîne le contenu à être chargé dans le div. C'est similaire mais plus puissant que la syntaxe ci-dessus puisqu'il est fait pour rafraichir les composants de la page. Nous parlons d'applications de cid plus en détail dans le Chapitre 12, dans le contexte de composants.

Ces fonctionnalités ajax requièrent jQuery et "static/js/web2py_ajax.js", qui sont automatiquement incluses en plaçant {{include 'web2py_ajax.html'}} en tête du layout. "views/web2py_ajax.html" définit certaines variables basées sur request et inclut tous les fichiers js et css nécessaires.

B

B

Cet helper met le contenu en gras.

>>> print B('<hello>', XML('<i>world</i>'), _class='test', _id=0)
<b id="0" class="test">&lt;hello&gt;<i>world</i></b>

BODY

BODY

Cet helper créé le corps de la page.

>>> print BODY('<hello>', XML('<b>world</b>'), _bgcolor='red')
<body bgcolor="red">&lt;hello&gt;<b>world</b></body>

BR

BR

Cet helper créé un retour à la ligne.

>>> print BR()
<br />

Notez que les helpers peuvent être répétés en utilisant l'opérateur de multiplication :

>>> print BR()*5
<br /><br /><br /><br /><br />

CAT

CAT

Cet helper concatène d'autres helpers, comme TAG[].

>>> print CAT('Here is a ', A('link',_href=URL()), ', and here is some ', B('bold text'), '.')
Here is a <a href="/app/default/index">link</a>, and here is some <b>bold text</b>.

CENTER

CENTER

Cet helper centre son contenu.

>>> print CENTER('<hello>', XML('<b>world</b>'),
>>>              _class='test', _id=0)
<center id="0" class="test">&lt;hello&gt;<b>world</b></center>

CODE

CODE

Cet helper effectue du surlignement de syntaxe pour du code Python, C, C++, HTML et web2py, et est préférable à PRE pour des listings de code. CODE a également la possibilité de créer des liens vers la documentation de l'API web2py.

Voici un exemple de mise en avant de sections de code Python.

>>> print CODE('print "hello"', language='python').xml()
<table><tr valign="top"><td style="width:40px; text-align: right;"><pre style="
        font-size: 11px;
        font-family: Bitstream Vera Sans Mono,monospace;
        background-color: transparent;
            margin: 0;
            padding: 5px;
            border: none;
        background-color: #E0E0E0;
        color: #A0A0A0;
    ">1.</pre></td><td><pre style="
        font-size: 11px;
        font-family: Bitstream Vera Sans Mono,monospace;
        background-color: transparent;
            margin: 0;
            padding: 5px;
            border: none;
            overflow: auto;
    "><span style="color:#185369; font-weight: bold">print </span>
    <span style="color: #FF9966">"hello"</span></pre></td></tr>
</table>

Voici un exemple similaire pour HTML

>>> print CODE(
>>>   '<html><body>{{=request.env.remote_add}}</body></html>',
>>>   language='html')
<table>...<code>...
<html><body>{{=request.env.remote_add}}</body></html>
...</code>...</table>

Ce sont les arguments par défaut pour le helper CODE :

CODE("print 'hello world'", language='python', link=None, counter=1, styles={})

Les valeurs supportées pour l'argument language sont "python", "html_plain", "c", "cpp", "web2py" et "html". Le langage "html" interprète les tags {{ et }} en code "web2py", alors que "html_plain" ne le fait pas.

Si une valeur link est spécifiée, par exemple "/examples/global/vars/", les références aux API web2py dans le code sont liées à la documentation au lien URL. Par exemple "request" serait lié à "/examples/global/vars/request". Dans l'exemple ci-dessus, le lien URL est géré par l'action "vars" dans le contrômeur "global.py" qui est distribué comme partie de l'application web2py "examples".

L'argument counter est utilisé pour numéroter les lignes. Il peut être défini à 3 valeurs différentes. Il peut être None pour aucun numéro de ligne, une valeur numérique spécifiant le numéro de début, ou une chaîne. Si le compteur est défini avec une chaîne, il est interprété comme un prompt, et il n'y a aucun numéro de lignes.

L'argument styles est un peu délicat. Si vous regardez le code HTML généré ci-dessus, il contient une table avec deux colonnes, et chaque colonne a son propre style déclaré en ligne en utilisant le CSS. Les attributs styles vous permettent d'écraser ces deux styles CSS. Par exemple :

{{=CODE(...,styles={'CODE':'margin: 0;padding: 5px;border: none;'})}}

L'attribut styles doit être un dictionnaire, et il permet deux clés possibles : CODE pour le style du code actuel, et LINENUMBERS pour le style de la colonne de gauche, qui contient les numéros de ligne. Pensez bien que ces styles remplacent complètement les styles par défaut et ne sont pas simplement ajoutés à eux.

COL

COL
>>> print COL('a','b')
<col>ab</col>

COLGROUP

COLGROUP
>>> print COLGROUP('a','b')
<colgroup>ab</colgroup>

DIV

Tous les helpers mis à part XML sont dérivés de DIV et hérite de ses méthodes basiques.

DIV
>>> print DIV('<hello>', XML('<b>world</b>'), _class='test', _id=0)
<div id="0" class="test">&lt;hello&gt;<b>world</b></div>

EM

Souligne son contenu.

EM
>>> print EM('<hello>', XML('<b>world</b>'), _class='test', _id=0)
<em id="0" class="test">&lt;hello&gt;<b>world</b></em>

FIELDSET

FIELDSET

Ceci est utilisé pour créer un champ d'entrée avec son label.

>>> print FIELDSET('Height:', INPUT(_name='height'), _class='test')
<fieldset class="test">Height:<input name="height" /></fieldset>

FORM

FORM

C'est l'un des plus importants helpers. Dans sa simple forme, il créé juste des tags <form>...</form>, mais puisque les helpers sont des objets et ont connaissance de ce qu'ils contiennent, ils peuvent gérer des formulaires envoyés (par exemple, effectuer de la validation de champs). Ce sera présenté en détail dans le Chapitre 7.

>>> print FORM(INPUT(_type='submit'), _action='', _method='post')
<form enctype="multipart/form-data" action="" method="post">
<input type="submit" /></form>

Le "enctype" est "multipart/form-data" par défaut.

hidden

Le constructeur d'un FORM, et de SQLFORM, peut aussi prendre un argument spécial appelé hidden. Lorsqu'un dictionnaire est passé en hidden, ses objets sont traduits en champs INPUT "hidden". Par exemple :

>>> print FORM(hidden=dict(a='b'))
<form enctype="multipart/form-data" action="" method="post">
<input value="b" type="hidden" name="a" /></form>

H1, H2, H3, H4, H5, H6

H1

Ces helpers sont pour les en-têtes et sous en-têtes de paragraphes :

>>> print H1('<hello>', XML('<b>world</b>'), _class='test', _id=0)
<h1 id="0" class="test">&lt;hello&gt;<b>world</b></h1>

HEAD

Pour taguer le HEAD d'une page HTML.

HEAD
>>> print HEAD(TITLE('<hello>', XML('<b>world</b>')))
<head><title>&lt;hello&gt;<b>world</b></title></head>

HTML

HTML
XHTML

Ce helper est un peu différent. En plus de créer les tags <html>, il ajoute le tag avec une chaîne doctype [xhtml-w,xhtml-o,xhtml-school] .

>>> print HTML(BODY('<hello>', XML('<b>world</b>')))
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
                      "http://www.w3.org/TR/html4/loose.dtd">
<html><body>&lt;hello&gt;<b>world</b></body></html>

Le helper HTML prend également certains arguments optionnels en complément qui ont les valeurs suivantes par défaut :

HTML(..., lang='en', doctype='transitional')

où doctype peut être 'strict', 'transitional', 'frameset', 'html5', ou une chaîne complète doctype.

XHTML

XHTML

XHTML est similaire au HTML mais créé un doctype XHTML à la place.

XHTML(..., lang='en', doctype='transitional', xmlns='http://www.w3.org/1999/xhtml')

où doctype peut être 'strict', 'transitional', 'frameset', 'html5', ou une chaîne complète doctype.

HR

HR

Cet helper créé une ligne horizontale dans une page HTML

>>> print HR()
<hr />

I

I

Cet helper rend du contenu italique.

>>> print I('<hello>', XML('<b>world</b>'), _class='test', _id=0)
<i id="0" class="test">&lt;hello&gt;<b>world</b></i>

IFRAME

Cet helper inclut une autre page web dans la page courante. L'url de l'autre page est spécifié via l'attribut "_src".

IFRAME
>>> print IFRAME(_src='http://www.web2py.com')
<iframe src="http://www.web2py.com"></iframe>

IMG

IMG

Peut être utilisé pour embarquer des images dans l'HTML :

>>> IMG(_src='http://example.com/image.png',_alt='test')
<img src="http://example.com/image.ong" alt="rest" />

Voici une combinaison de A, IMG et URL pour inclure une image statique avec un lien :

>>> A(IMG(_src=URL('static','logo.png'), _alt="My Logo"),
      _href=URL('default','index'))
<a href="/myapp/default/index">
  <img src="/myapp/static/logo.png" alt="My Logo" />
</a>

INPUT

INPUT

Créé un tag <input.../>. Un tag input peut ne peut pas contenir d'autres tags, et est fermé par /> au lieu de >. Le tag d'entrée a un attribut optionnel _type qui peut être défini à "text" (le défaut), "submit", "checkbox", ou "radio".

>>> print INPUT(_name='test', _value='a')
<input value="a" name="test" />

Il prend également un argument spécial optionnel appelé "value", distint de "_value". Le dernier définit la valeur par défaut pour le champ input ; le formeur définit sa valeur courante. Pour une entrée de type "text", le formeur écrase le dernier :

>>> print INPUT(_name='test', _value='a', value='b')
<input value="b" name="test" />

Pour les boutons radio, INPUT définit sélectivement l'attribut "checked" :

radio
>>> for v in ['a', 'b', 'c']:
>>>     print INPUT(_type='radio', _name='test', _value=v, value='b'), v
<input value="a" type="radio" name="test" /> a
<input value="b" type="radio" checked="checked" name="test" /> b
<input value="c" type="radio" name="test" /> c

et de manière similaire pour les cases à cocher :

checkbox
>>> print INPUT(_type='checkbox', _name='test', _value='a', value=True)
<input value="a" type="checkbox" checked="checked" name="test" />
>>> print INPUT(_type='checkbox', _name='test', _value='a', value=False)
<input value="a" type="checkbox" name="test" />

LABEL

C'est utilisé pour créer un tag LABEL pour un champ INPUT.

LABEL
>>> print LABEL('<hello>', XML('<b>world</b>'), _class='test', _id=0)
<label id="0" class="test">&lt;hello&gt;<b>world</b></label>

LEGEND

Utilisé pour créer un tag LEGEND pour un champ dans un formulaire.

LEGEND
>>> print LEGEND('Name', _for='myfield')
<legend for="myfield">Name</legend>

LI

Fait un objet de liste et devrait être contenu dans un tag UL ou OL.

LI
>>> print LI('<hello>', XML('<b>world</b>'), _class='test', _id=0)
<li id="0" class="test">&lt;hello&gt;<b>world</b></li>

META

Pour être utilisé pour construire des tags META dans l'en-tête HTML. Par exemple :

META
>>> print META(_name='security', _content='high')
<meta name="security" content="high" />

MARKMIN

Implémente la syntaxe wiki MARKMIN. Il convertir le texte d'entrée dans une sortie html selon les règles markmin décrites dans l'exemple ci-dessous :

MARKMIN
>>> print MARKMIN("this is **bold** or ''italic'' and this [[a link http://web2py.com]]")
<p>this is <b>bold</b> or <i>italic</i> and
this <a href="http://web2py.com">a link</a></p>

La syntaxe markmin est décrite dans ce fichier qui est livré avec web2py :

http://127.0.0.1:8000/examples/static/markmin.html

Vous pouvez utiliser markmin pour générer des documents HTML, LaTeX et PDF :

m = "Hello **world** [[link http://web2py.com]]"
from gluon.contrib.markmin.markmin2html import markmin2html
print markmin2html(m)
from gluon.contrib.markmin.markmin2latex import markmin2latex
print markmin2latex(m)
from gluon.contrib.markmin.markmin2pdf import markmin2pdf
print markmin2pdf(m) # requires pdflatex

(le helper MARKMIN est un raccourci pour markmin2html)

Voici une syntaxe basique :

SOURCEOUTPUT
# titletitle
## sectionsection
### subsectionsubsection
**bold**bold
''italic''italic
``verbatim``verbatim
http://google.comhttp://google.com
http://...<a href="http://...">http:...</a>
http://...png<img src="http://...png" />
http://...mp3<audio src="http://...mp3"></audio>
http://...mp4<video src="http://...mp4"></video>
qr:http://...<a href="http://..."><img src="qr code"/></a>
embed:http://...<iframe src="http://..."></iframe>
[[click me #myanchor]]click me
[[myanchor]]Creating an anchor for a link
$$\int_a^b sin(x)dx$$
Liens MARKMIN

Les liens prennent cette forme : [[link display text <link>]] <link> peut être une ancre e.g. #myanchor ou une URI e.g. http://www.web2py.com ou une référence relative e.g. [[Voir Chapitre 8 ../08]] ou [[Voir Chapitre 8 ../08#myanchor]]

Simplement inclure un lien dans une image, une vidéo ou un fichier audio sans marquer le résultat dans l'image correspondante, la vidéo ou l'audio, l'inclusion est automatique (pour l'audio et la vidéo, il utilise les tags <audio> et <video>).

Ajouter un lien avec le préfixe qr: tel que

qr:http://web2py.com

résulte dans un code QR correspondant et se lie à l'URL donnée.

Ajouter un lien avec le préfixe embed: tel que

embed:http://www.youtube.com/embed/x1w8hKTJ2Co

résulte en une page embarquée, dans ce cas une vidéo youtube est embarquée.

Les images peuvent aussi être embarqués avec la syntaxe suivante :

[[image-description http://.../image.png right 200px]]
Tables et listes MARKMIN

Listes non ordonnées avec :

- one
- two
- three

Listes ordonnées avec :

+ one
+ two
+ three

et tables avec :

----------
 X | 0 | 0
 0 | X | 0
 0 | 0 | 1
----------
Etendre MARKMIN

La syntaxe MARKMIN supporte également les blockquotes, les tags audio et vidéo HTML5, l'alignement d'image, le CSS personnalisé, et il peut être étendu :

MARKMIN("``abab``:custom", extra=dict(custom=lambda text: text.replace('a','c'))

génère

'cbcb'

Les blocs personnalisés sont délimités par ``...``:<key> et ils sont rendus par la fonction passée comme valeur pour la clé correspondante dans l'argument extra du dictionnaire de MARKMIN. Pensez que la fonction peut avoir besoin d'échapper la sortie pour éviter le XSS.

OBJECT

Utilisé pour embarquer des objets (par exemple, un flash player) dans l'HTML.

OBJECT
>>> print OBJECT('<hello>', XML('<b>world</b>'),
>>>              _src='http://www.web2py.com')
<object src="http://www.web2py.com">&lt;hello&gt;<b>world</b></object>

OL

Il reste pour Ordered List. La liste devrait contenir des tags LI. Les arguments OL qui ne sont pas des objets LI ne sont pas automatiquement renfermés par des tags <li>...</li>.

OL
>>> print OL('<hello>', XML('<b>world</b>'), _class='test', _id=0)
<ol id="0" class="test"><li>&lt;hello&gt;</li><li><b>world</b></li></ol>

ON

C'est ici pour une rétro-compatibilité et est simplement un alias pour True. C'est utilisé exclusivement pour les cases à cocher et déprécié depuis que True est plus Pythonique.

ON
>>> print INPUT(_type='checkbox', _name='test', _checked=ON)
<input checked="checked" type="checkbox" name="test" />

OPTGROUP

Vous autorise à grouper de multiples options dans un SELECT et utile pour personnaliser les champs en utilisant du CSS.

OPTGROUP
>>> print SELECT('a', OPTGROUP('b', 'c'))
<select>
  <option value="a">a</option>
  <optgroup>
    <option value="b">b</option>
    <option value="c">c</option>
  </optgroup>
</select>

OPTION

Ceci devrait uniquement être utilisé comme partie d'une combinaison SELECT/OPTION.

OPTION
>>> print OPTION('<hello>', XML('<b>world</b>'), _value='a')
<option value="a">&lt;hello&gt;<b>world</b></option>

Comme dans le cas de INPUT, web2py fait une distinction entre "_value" (la valeur de OPTION), et "value" (la valeur courante du select renfermé). S'ils sont égaux, l'option est "selected".

selected
>>> print SELECT('a', 'b', value='b'):
<select>
<option value="a">a</option>
<option value="b" selected="selected">b</option>
</select>

P

P

C'est pour taguer un paragraphe.

>>> print P('<hello>', XML('<b>world</b>'), _class='test', _id=0)
<p id="0" class="test">&lt;hello&gt;<b>world</b></p>

PRE

PRE

Génère un tag <pre>...</pre> pour afficher du texte pré-formaté. Le helper CODE est généralement préférable pour les listings de code.

>>> print PRE('<hello>', XML('<b>world</b>'), _class='test', _id=0)
<pre id="0" class="test">&lt;hello&gt;<b>world</b></pre>

SCRIPT

SCRIPT

C'est inclure ou lier un script, tel que JavaScript. Le contenu entre les tags est rendu en commentaire HTML, pour le bénéfice de très vieux navigateurs.

>>> print SCRIPT('alert("hello world");', _type='text/javascript')
<script type="text/javascript"><!--
alert("hello world");
//--></script>

SELECT

SELECT

Créé un tag <select>...</select>. C'est utilisé avec un helper OPTION. Ces arguments SELECT qui ne sont pas des objets OPTION sont automatiquement convertis en options.

>>> print SELECT('<hello>', XML('<b>world</b>'), _class='test', _id=0)
<select id="0" class="test">
   <option value="&lt;hello&gt;">&lt;hello&gt;</option>
   <option value="&lt;b&gt;world&lt;/b&gt;"><b>world</b></option>
</select>

SPAN

SPAN

Similaire à DIV mais utilisé pour le tag de contenu en ligne (plutôt qu'en bloc).

>>> print SPAN('<hello>', XML('<b>world</b>'), _class='test', _id=0)
<span id="0" class="test">&lt;hello&gt;<b>world</b></span>

STYLE

STYLE

Similaire à script, mais utilisé pour soit inclure soit lier du code CSS. Voici le CSS inclus :

>>> print STYLE(XML('body {color: white}'))
<style><!--
body { color: white }
//--></style>

et ici lié :

>>> print STYLE(_src='style.css')
<style src="style.css"><!--
//--></style>

TABLE, TR, TD

TABLE
TR
TD

Ces tags (avec les helpers optionnels THEAD, TBODY et TFOOTER) sont utilisé pour construire des tables HTML.

>>> print TABLE(TR(TD('a'), TD('b')), TR(TD('c'), TD('d')))
<table><tr><td>a</td><td>b</td></tr><tr><td>c</td><td>d</td></tr></table>

TR expects TD content; arguments that are not TD objects are converted automatically.

>>> print TABLE(TR('a', 'b'), TR('c', 'd'))
<table><tr><td>a</td><td>b</td></tr><tr><td>c</td><td>d</td></tr></table>

Il est facile de convertir un tableau python en table HTML en utilisant la notation * en argument de fonction Python, qui mappe les éléments de la liste à des arguments de fonction positionnels.

Ici, nous le faisons ligne par ligne :

>>> table = [['a', 'b'], ['c', 'd']]
>>> print TABLE(TR(*table[0]), TR(*table[1]))
<table><tr><td>a</td><td>b</td></tr><tr><td>c</td><td>d</td></tr></table>

Ici nous faisons toutes les lignes à la fois :

>>> table = [['a', 'b'], ['c', 'd']]
>>> print TABLE(*[TR(*rows) for rows in table])
<table><tr><td>a</td><td>b</td></tr><tr><td>c</td><td>d</td></tr></table>

TBODY

TBODY

C'est utilisé pour taguer les lignes contenues dans le corps du tableau, en opposé aux en-têtes et pied. C'est optionnel.

>>> print TBODY(TR('<hello>'), _class='test', _id=0)
<tbody id="0" class="test"><tr><td>&lt;hello&gt;</td></tr></tbody>

TEXTAREA

TEXTAREA

Cet helper créé un tag <textarea>...</textarea>.

>>> print TEXTAREA('<hello>', XML('<b>world</b>'), _class='test')
<textarea class="test" cols="40" rows="10">&lt;hello&gt;<b>world</b></textarea>

La seule faiblesse est que l'optionnelle "value" écrase son contenu (dans l'HTML)

>>> print TEXTAREA(value="<hello world>", _class="test")
<textarea class="test" cols="40" rows="10">&lt;hello world&gt;</textarea>

TFOOT

TFOOT

Utilisé pour taguer les lignes de pied de table.

>>> print TFOOT(TR(TD('<hello>')), _class='test', _id=0)
<tfoot id="0" class="test"><tr><td>&lt;hello&gt;</td></tr></tfoot>

TH

TH

Utilisé plutôt que TD pour les en-têtes de tableau.

>>> print TH('<hello>', XML('<b>world</b>'), _class='test', _id=0)
<th id="0" class="test">&lt;hello&gt;<b>world</b></th>

THEAD

THEAD

Utilisé pour taguer les lignes d'en-tête d'un tableau.

>>> print THEAD(TR(TH('<hello>')), _class='test', _id=0)
<thead id="0" class="test"><tr><th>&lt;hello&gt;</th></tr></thead>

TITLE

TITLE

Utilisé pour taguer le titre d'une page dans un en-tête HTML.

>>> print TITLE('<hello>', XML('<b>world</b>'))
<title>&lt;hello&gt;<b>world</b></title>

TR

TR

Tague une ligne de tableau. Devrait être rendu dans un tableau et contenir des tags <td>...</td>. Les arguments TR qui ne sont pas des objets TD seront automatiquement convertis.

>>> print TR('<hello>', XML('<b>world</b>'), _class='test', _id=0)
<tr id="0" class="test"><td>&lt;hello&gt;</td><td><b>world</b></td></tr>

TT

TT

Tague du texte en texte (mono-spacé) typewriter.

>>> print TT('<hello>', XML('<b>world</b>'), _class='test', _id=0)
<tt id="0" class="test">&lt;hello&gt;<b>world</b></tt>

UL

Signifie un Unordered List et devrait contenir des objets LI. Si son contenu n'est pas tagué comme LI, UL le fait automatiquement.

UL
>>> print UL('<hello>', XML('<b>world</b>'), _class='test', _id=0)
<ul id="0" class="test"><li>&lt;hello&gt;</li><li><b>world</b></li></ul>

URL

Le helper URL est documenté dans Chapitre 4 URL

embed64

embed64(filename=None, file=None, data=None, extension='image/gif') encode les données (binaires) fournies en base64.

filename : si fourni, ouvre et lit ce fichier en mode 'rb'. file : si fourni, lit ce fichier. data : si fourni, utilise les données fournies.

embed64

xmlescape

xmlescape(data, quote=True) retourne une chaîne échappée des données fournies.

xmlescape
>>> print xmlescape('<hello>')
&lt;hello&gt;

Custom helpers

TAG

TAG

Parfois vous avez besoin de générer des tags personnalisés XML. web2py fournit TAG, un générateur de tag universel.

{{=TAG.name('a', 'b', _c='d')}}

génère l'XML suivant

<name c="d">ab</name>

Les arguments "a", "b", et "d" sont automatiquement échappés ; utilisez le helper XML pour supprimer ce comportement. En utilisant TAG vous pouvez générer des tags XHTML/XML pas encore fournis par l'API. Les TAGs peuvent être imbriqués, et sont sérialisés avec str(). Une syntaxe équivalente est :

{{=TAG['name']('a', 'b', c='d')}}

Si l'objet TAG est créé avec un nom vide, il peut être utilisé pour concaténer plusieurs chaînes et helpers HTML ensemble sans les insérer dans un tag d'encadrement, mais cet usage est déprécié. Utilisez le helper CAT à la place.

Les tags auto-fermants peuvent être générés avec le helper TAG. Le nom du tag doit terminer avec un "/".

{{=TAG['link/'](_href='http://web2py.com')}}

génère le code XML suivant :

<link ref="http://web2py.com"/>

Notez que TAG est un objet, et TAG.name ou TAG['name'] est une fonction qui retourne une classe temporaire de helper.

MENU

MENU

Le helper MENU prend une liste de listes ou de tupes du formulaire response.menu (comme décrit dans le chapitre 4) et génère une structure de type arbre en utilisant des listes non ordonnées représentant le menu. Par exemple :

>>> print MENU([['One', False, 'link1'], ['Two', False, 'link2']])
<ul class="web2py-menu web2py-menu-vertical">
  <li><a href="link1">One</a></li>
  <li><a href="link2">Two</a></li>
</ul>

Le troisième objet dans chaque liste/tuple peut être un helper HTML (qui pourrait inclure des helpers imbriqués), et le helper MENU rendront simplement ce helper plutôt que de créer son propre tag <a>.

Chaque objet peut avoir un quatrième argument qui est un sous-menu imbriqué (et ainsi de suite récursivement) :

>>> print MENU([['One', False, 'link1', [['Two', False, 'link2']]]])
<ul class="web2py-menu web2py-menu-vertical">
  <li class="web2py-menu-expand">
     <a href="link1">One</a>
     <ul class="web2py-menu-vertical">
        <li><a href="link2">Two</a></li>
     </ul>
  </li>
</ul>

Un objet menu peut aussi avoir un 5è élément optionnel, qui est un booléen. Lorsqu'il est à False, l'objet de menu est ignoré par le helper MENU.

Le helper MENU prend les arguments optionnels suivants ;

  • _class : par défaut à "web2py-menu web2py-menu-vertical" et définit la classe des éléments UL sortants.
  • ul_class : par défaut à "web2py-menu-vertical" et définit la classe des éléments UL entrants.
  • li_class : par défaut à "web2py-menu-expand" et définit la classe des éléments LI entrants.
  • li_first : permet d'ajouter une classe au premier élément de liste.
  • li_last : permet d'ajouter une classe au dernier élément de liste.
mobile

MENU prend un argument optionnel mobile. Lorsqu'il est défini à True au lieu de construire une structure de menu UL récursive, il retourne un menu déroulant SELECT avec toutes les options de menu et un attribut onchange qui redirige vers la page correspondant à l'option sélectionnée. Ceci est utilisé comme une représentation alternative du menu qui améliore l'utilisation sur des petits périphériques tels que les téléphones.

Normalement, le menu est utilisé dans un layout avec la syntaxe suivante :

{{=MENU(response.menu, mobile=request.user_agent().is_mobile)}}

De ce fait, un périphérique mobile est automatiquement détecté et le menu est rendu en fonction.

BEAUTIFY

BEAUTIFY est utilisé pour construire des représentations HTML d'objets composés, incluant les listes, les tuples et les dictionnaires :

{{=BEAUTIFY({"a": ["hello", XML("world")], "b": (1, 2)})}}

BEAUTIFY retourne un objet semblable à du XML sérialisable en XML, avec une représentation sympathique de son argument de construction. Dans ce cas, la représentation XML de :

{"a": ["hello", XML("world")], "b": (1, 2)}

sera rendue en :

<table>
<tr><td>a</td><td>:</td><td>hello<br />world</td></tr>
<tr><td>b</td><td>:</td><td>1<br />2</td></tr>
</table>

DOM'' côté serveur et parsing

element
elements

elements

Le helper DIV et tous les helpers dérivés fournissent les méthodes de recherche element et elements.

element retourne le premier élément fils correspondant une condition spécifiée (ou None s'il n'y a pas de correspondance).

elements retourne une liste de tous les fils correspondants.

element et elements utilisent la même syntaxe pour spécifier les conditions de matching, ce qui permet pour trois possibilités qui peuvent être mixées et matchées : les expressions jQuery-like, correspondent par la valeur d'attribut exacte, correspondance en utilisant les expressions régulières.

Voici un exemple simple :

>>> a = DIV(DIV(DIV('a', _id='target',_class='abc')))
>>> d = a.elements('div#target')
>>> d[0][0] = 'changed'
>>> print a
<div><div><div id="target" class="abc">changed</div></div></div>

L'argument non-nommé d'elements est une chaîne, qui peuvent contenir : le nom d'un tag, l'id d'un tag précédé par un symbole pound, la classe précédée par un point, la valeur explicite d'un attribut entre crochets.

Voici 4 façons équivalentes de rechercher le tag précédent par id :

>>> d = a.elements('#target')
>>> d = a.elements('div#target')
>>> d = a.elements('div[id=target]')
>>> d = a.elements('div',_id='target')

Voici 4 façons équivalentes de recherche le tag précédent par classe :

>>> d = a.elements('.abc')
>>> d = a.elements('div.abc')
>>> d = a.elements('div[class=abc]')
>>> d = a.elements('div',_class='abc')

Tout attribut peut être utilisé pour localiser un élément (pas juste un id et une class), incluant de multiples attributs (la fonction element peut prendre plusieurs arguments nommés), mais seulement le premier élément correspondant sera retourné.

En utilisant la syntaxe jQuery "div#target" il est possible de spécifier des critères de recherche multiples séparés par une virgule :

>>> a = DIV(SPAN('a', _id='t1'), DIV('b', _class='c2'))
>>> d = a.elements('span#t1, div.c2')

ou l'équivalent

>>> a = DIV(SPAN('a', _id='t1'), DIV('b', _class='c2'))
>>> d = a.elements('span#t1', 'div.c2')

Si la valeur d'un attribut est spécifié en utilisant un argument nommé, il peut être une chaîne ou une expression régulière :

>>> a = DIV(SPAN('a', _id='test123'), DIV('b', _class='c2'))
>>> d = a.elements('span', _id=re.compile('test\d{3}')

Un argument spécial nommé des helpers DIV (et dérivés) est find. Il peut être utilisé pour spécifier une valeur de recherche ou une expression régulière de recherche dans le contenu du texte du tag. Par exemple :

>>> a = DIV(SPAN('abcde'), DIV('fghij'))
>>> d = a.elements(find='bcd')
>>> print d[0]
<span>abcde</span>

ou

>>> a = DIV(SPAN('abcde'), DIV('fghij'))
>>> d = a.elements(find=re.compile('fg\w{3}'))
>>> print d[0]
<div>fghij</div>
components

components

Voici un exemple de listing de tous les éléments dans une chaine html :

html = TAG('<a>xxx</a><b>yyy</b>')
for item in html.components: print item

parent
sibling

parent et siblings

parent retourne le parent de l'élément courant.

>>> a = DIV(SPAN('a'),DIV('b'))
>>> s = a.element('span')
>>> d = s.parent
>>> d['_class']='abc'
>>> print a
<div class="abc"><span>a</span><div>b</div></div>
>>> for e in s.siblings(): print e
<div>b</div>

Replacing elements

Les éléments qui correspondent peuvent aussi être remplacés ou supprimés en spécifiant l'argument replace. Notez qu'une liste d'éléments correspondant est encore retournée comme d'habitude.

>>> a = DIV(SPAN('x'), DIV(SPAN('y'))
>>> b = a.elements('span', replace=P('z')
>>> print a
<div><p>z</p><div><p>z</p></div>

replace peut être appelé. Dans ce cas, l'élément original sera passé et il s'attendra à retourner l'élément de remplacement :

>>> a = DIV(SPAN('x'), DIV(SPAN('y'))
>>> b = a.elements('span', replace=lambda t: P(t[0])
>>> print a
<div><p>x</p><div><p>y</p></div>

Si replace=None, les éléments correspondants seront entièrement supprimés.

>>> a = DIV(SPAN('x'), DIV(SPAN('y'))
>>> b = a.elements('span', replace=None)
>>> print a
<div></div>
flatten

flatten

La méthode flatten sérialise récursivement le contenu du fils d'un élément donné dans du texte régulier (sans tags) :

>>> a = DIV(SPAN('this', DIV('is', B('a'))), SPAN('test'))
>>> print a.flatten()
thisisatest

Flatten peut recevoir un argument optionnel, render, i.e. une fonction qui rend/applatit le contenu en utilisant un protocole différent. Voici un exemple de sérialisation de certains tags en syntaxe wiki Markmin :

>>> a = DIV(H1('title'), P('example of a ', A('link', _href='#test')))
>>> from gluon.html import markmin_serializer
>>> print a.flatten(render=markmin_serializer)
# titles

example of [[a link #test]]

Au moment d'écrire, nous fournissons markmin_serializer et markdown_serialiezr.

Parsing

L'objet TAG est également un parser XML/HTML. Il peut lire du texte et le convertir en structure d'arbre de helpers. Ceci permet la manipulation en utilisant l'API ci-dessus :

>>> html = '<h1>Title</h1><p>this is a <span>test</span></p>'
>>> parsed_html = TAG(html)
>>> parsed_html.element('span')[0]='TEST'
>>> print parsed_html
<h1>Title</h1><p>this is a <span>TEST</span></p>

page layout
layout.html
extent
include

Layout de Page

Les vues peuvent étendre et inclure d'autres vues dans une structure arborescente.

Par exemple, nous pouvons penser à une vue "index.html" qui étent "layout.html" et inclut "body.html". Au même moment, "layout.html" peut inclure "header.html" et "footer.html".

La racine de l'arbre est ce que l'on appelle une vue layout. Juste comme toute autre fichier de template HTML, vous pouvez l'éditer en utilisant l'interface d'administration web2py. Le nom de fichier "layout.html" est juste une convention.

Voici une page minimaliste qui étend la vue "layout.html" et inclut la vue "page.html" :

{{extend 'layout.html'}}
<h1>Hello World</h1>
{{include 'page.html'}}

Le fichier de layout étendu doit contenir une directive {{include}}, comme :

<html>
  <head>
    <title>Page Title</title>
  </head>
  <body>
    {{include}}
  </body>
</html>

Lorsque la vue est appelée, la vue étendue (layout) est chargée, et la vue appelant remplace la directive {{include}} dans le layout. Le process continue récursivement jusqu'à ce que toutes les directives extend et include aient été exécutées. Le template résultant est alors traduit en code Python. Notez, lorsqu'une application est compilée en bytecode, c'est ce code Python qui est compilé, et non les fichiers de vue originaux eux-mêmes. Alors, la version compilée en bytecode d'une vue donnée est un simple fichier .pyc qui inclut le code Python non pas seulement pour la vue originale mais pour son entière architecture de vues étendues et incluses.

extend, include, block et super sont des directives de template, ce ne sont pas des commandes Python.

N'importe quel contenu ou code qui précède la directive {{extend ...}} sera inséré (et donc exécuté) avant le début du contenu/code de la vue étendue. Bien que ce ne soit pas utilisé typiquement pour insérer de l'actuel contenu HTML avant le contenu de la vue étendue, il peut être utile comme moyen de définir des variables ou fonction que vous souhaitez rendre disponible à la vue étendue. Par exemple, considérez une vue "index.html" :

{{sidebar_enabled=True}}
{{extend 'layout.html'}}
<h1>Home Page</h1>

et une extraction de "layout.html" :

{{if sidebar_enabled:}}
    <div id="sidebar">
        Sidebar Content
    </div>
{{pass}}

Puisque l'assignemnet de sidebar_enabled dans "index.html" vient avant le extend, que la ligne est insérée avant le début de "layout.html", rendre sidebar_enabled disponible n'importe où dans le code "layout.html" (une version quelque peu plus sophistiquée de cela est utilisé dans l'application welcome).

Il est également intéressant de montrer que les variables retournées par la fonction contrôleur sont disponibles non seulement dans la vue principale de la fonction, mais dans toutes ses vues étendues et incluses également.

L'argument d'un extend ou include (i.e. le nom d'une vue étendue ou incluse) peut être une variable python (et non une expression python). Cependant, ceci impose une limitation -- les vues qui utilisent des variables dans des déclarations extend ou include ne peuvent pas être compilées en bytecode. Comme noté ci-dessus, les vues compilées en bytecode incluent l'architecture complète des vues étendues et incluses, donc les vues spécifiques étendues et incluses doivent être connues au moment de la compilation, ce qui n'est pas possible si les noms des vues sont des variables (dont les valeurs ne sont pas déterminées jusqu'au démarrage). Puisque compiler les vues en bytecode peut permettre de fournir une accélération significative, utiliser les variables dans extend et include devrait généralement être évité autant que possible.

Dans certains cas, une alternative à l'utilisation de variable dans un include est simplement de placer des directives régulières {{include ...}} dans un bloc if...else.

{{if some_condition:}}
{{include 'this_view.html'}}
{{else:}}
{{include 'that_view.html'}}
{{pass}}

Le code ci-dessus ne présente aucun problème pour la compilation bytecode puisqu'aucune variable n'est mise en jeu. Notez cependant, que la vue compilée en bytecode sera en fait inclue dans le code Python pour "this_view.html" et "that_view.html", alors que seul le code de l'un des deux ne sera exécuté, selon la valeur de some_condition.

Gardez à l'esprit que ceci ne fonctionne que pour include -- vous ne pouvez pas placer des directives {{extend ...}} dans des blocs if...else.

response.menu
menu
response.meta
meta

Les layouts sont utilisés pour encapsuler les points communs aux pages (en-têtes, pieds de page, menus), et bien qu'ils ne soient pas obligatoires, ils faciliteront l'écriture et la maintenance de votre application. En particulier, nous suggérons l'écriture de layouts qui utilisent les variables suivantes qui peuvent être définies dans le contrôleur. En les utilisant, ceci aidera à rendre les layouts interchangeables :

response.title
response.subtitle
response.meta.author
response.meta.keywords
response.meta.description
response.flash
response.menu
response.files

Sauf pour menu et files, ce sont tous des chaînes de caractères et leur signification devrait être évidente.

Le menu response.menu est une liste de 3-tuples ou 4-tuples. Les trois éléments sont : le nom du lien, un booléen représentant si le lien est actif (est le lien courant), et l'URL de la page liée. Par exemple :

response.menu = [('Google', False, 'http://www.google.com',[]),
                 ('Index',  True,  URL('index'), [])]
sub-menu

Le quatrième élément est un sous-menu optionnel.

response.files est une liste de fichiers CSS et JS qui sont nécessaires dans la page.

Nous recommandons également d'utiliser :

{{include 'web2py_ajax.html'}}

dans l'en-tête HTML, puisque cela incluera les librairies jQuery et définira certaines fonctions rétro-compatibles pour des effets spéciaux et Ajax. "web2py_ajax.html" inclut les tags response.meta dans la vue, la base jQuery, le sélectionneur de calendrier, et tout les CSS et JS requis response.files.

Page layout par défaut

Twitter Bootstrap

Le "views/layout.html" livré avec l'application welcome de base web2py (séparée de certaines parties optionnelles) est assez complexe mais a la structure suivante :

<!DOCTYPE html>
<head>
  <meta charset="utf-8" />
  <title>{{=response.title or request.application}}</title>
  ...
  <script src="{{=URL('static','js/modernizr.custom.js')}}"></script>

  {{
  response.files.append(URL('static','css/web2py.css'))
  response.files.append(URL('static','css/bootstrap.min.css'))
  response.files.append(URL('static','css/bootstrap-responsive.min.css'))
  response.files.append(URL('static','css/web2py_bootstrap.css'))
  }}

  {{include 'web2py_ajax.html'}}

  {{
  # using sidebars need to know what sidebar you want to use
  left_sidebar_enabled = globals().get('left_sidebar_enabled',False)
  right_sidebar_enabled = globals().get('right_sidebar_enabled',False)
  middle_columns = {0:'span12',1:'span9',2:'span6'}[
    (left_sidebar_enabled and 1 or 0)+(right_sidebar_enabled and 1 or 0)]
  }}

  {{block head}}{{end}}
</head>

<body>
  <!-- Navbar ================================================== -->
  <div class="navbar navbar-inverse navbar-fixed-top">
    <div class="flash">{{=response.flash or ''}}</div>
    <div class="navbar-inner">
      <div class="container">
        {{=response.logo or ''}}
        <ul id="navbar" class="nav pull-right">
          {{='auth' in globals() and auth.navbar(mode="dropdown") or ''}}
        </ul>
        <div class="nav-collapse">
          {{if response.menu:}}
          {{=MENU(response.menu)}}
          {{pass}}
        </div><!--/.nav-collapse -->
      </div>
    </div>
  </div><!--/top navbar -->

  <div class="container">
    <!-- Masthead ================================================== -->
    <header class="mastheader row" id="header">
        <div class="span12">
            <div class="page-header">
                <h1>
                    {{=response.title or request.application}}
                    <small>{{=response.subtitle or ''}}</small>
                </h1>
            </div>
        </div>
    </header>

    <section id="main" class="main row">
        {{if left_sidebar_enabled:}}
        <div class="span3 left-sidebar">
            {{block left_sidebar}}
            <h3>Left Sidebar</h3>
            <p></p>
            {{end}}
        </div>
        {{pass}}

        <div class="{{=middle_columns}}">
            {{block center}}
            {{include}}
            {{end}}
        </div>

        {{if right_sidebar_enabled:}}
        <div class="span3">
            {{block right_sidebar}}
            <h3>Right Sidebar</h3>
            <p></p>
            {{end}}
        </div>
        {{pass}}
    </section><!--/main-->

    <!-- Footer ================================================== -->
    <div class="row">
        <footer class="footer span12" id="footer">
            <div class="footer-content">
                {{block footer}} <!-- this is default footer -->
                ...
                {{end}}
            </div>
        </footer>
    </div>

  </div> <!-- /container -->

  <!-- The javascript =============================================
       (Placed at the end of the document so the pages load faster) -->
  <script src="{{=URL('static','js/bootstrap.min.js')}}"></script>
  <script src="{{=URL('static','js/web2py_bootstrap.js')}}"></script>
  {{if response.google_analytics_id:}}
    <script src="{{=URL('static','js/analytics.js')}}"></script>
    <script type="text/javascript">
    analytics.initialize({
      'Google Analytics':{trackingId:'{{=response.google_analytics_id}}'}
    });</script>
  {{pass}}
</body>
</html>

Il y a quelques fonctionnalités de ce layout par défaut qui le rendent très facile à utiliser et à personnaliser :

  • Il est écrit en HTML5 et utilise la librairie "modernizr" [modernizr] pour rétro-compatibilité. Le layout actuel inclut certaines déclarations extra requises par IE et sont omises pour raccourcir.
  • Il affiche response.title et response.subtitle qui peuvent être définis dans un modèle. Si non définis, il adopte le nom de l'application comme titre
  • Il inclut le fichier web2py_ajax.html dans l'en-tête qui a généré toutes les déclarations d'import de lien et de scripts.
  • Il utilise une version modifiée du Bootstrap Twitter pour les layouts flexibles qui fonctionne sur périphériques mobiles et ré-arrange les colonnes pour rentrer sur les petits écrans.
  • Il utilise "analytics.js" pour se connecter à Google Analytics.
  • Le {{=auth.navbar(...)}} affiche un bienvenue à l'utilisateur courant et lie les fonctions d'authentification comme login, logout, register, change password, .... selon le contexte. C'est une fabrique de helper et sa sortie peut être manipulée comme tout autre helper. Il est placé dans un {{try:}}...{{except:pass}} dans le cas où auth n'est pas défini.
  • Le {{=MENU(response.menu)}} affiche la structure de menu comme <ul>...</ul>.
  • {{include}} est remplacé par le contenu d'une vue étendue lorsque la page est rendue.
  • Par défaut, il utilise une triple colonne conditionnelle (les sidebar gauche et droite peuvent être désactivées par les vues étendues)
  • Il utilise les classes suivantes : header, main, footer
  • Il contient les blocs suivants : statusbar, left_sidebar, center, right_sidebar, footer.

dans les vues, vous pouvez activer et personnaliser les sidebars comme suit :

{{left_sidebar_enable=True}}
{{extend 'layout.html'}}

This text goes in center

{{block left_sidebar}}
This text goes in sidebar
{{end}}

Customizing the default layout

CSS

Personnaliser le layout par défaut sans éditer est simple car l'application welcome est basée sur le Bootstrap Twitter qui est bien documenté et supporte les htèmes. Dans web2py quatre fichiers qui sont relatifs au style :

  • "css/web2py.css" contient les styles spécifiques web2py
  • "css/bootstrap.min.css" contient le style CSS du Bootstrap Twitter [bootstrap]
    Bootstrap
  • "css/web2py_bootstrap.css" contient certaines surcharges des styles Bootstrap pour être conforme aux besoins web2py.
  • "js/bootstrap.min.js" qui inclut les librairies pour les effets de menu, modals, panels.

Pour changer les couleurs et les images de fond, essayez d'ajouter le code suivant à l'en-tête layout.html :

<style>
body { background: url('images/background.png') repeat-x #3A3A3A; }
a { color: #349C01; }
.header h1 { color: #349C01; }
.header h2 { color: white; font-style: italic; font-size: 14px;}
.statusbar { background: #333333; border-bottom: 5px #349C01 solid; }
.statusbar a { color: white; }
.footer { border-top: 5px #349C01 solid; }
</style>

Bien sûr, vous pouvez aussi complètement remplacer les fichiers "layout.html" et "web2py.css" avec les votres.

Développement mobile

Le layout.html par défaut est fait pour être utilisable sur les périphériques mobiles mais ce n'est pas suffisant. Il peut être nécessaire des vues différentes lorsqu'une page est visitée par un périphérique mobile.

Pour rendre le développement pour bureau et mobile plus facile, web2py inclut le décorateur @mobilize. Ce décorateur est appliqué aux actions qui devraient avoir une vue normale et une vue mobile. Ceci est démontré ici :

from gluon.contrib.user_agent_parser import mobilize
@mobilize
def index():
   return dict()

Notez que le décorateur doit être importé avant de l'utiliser dans un contrôleur. Lorsque la fonction "index" est appelée depuis un navigateur standard (PC de bureau), web2py affichera le dictionnaire retourné en utilisant la vue "[controller]/index.html". Cependant, lorsqu'elle est appelée par un périhpérique mobile, le dictionnaire sera rendu par "[controller]/index.mobile.html". Notez que les vues mobiles ont une extension "mobile.html".

De manière alternative, vous pouvez appliquer la logique suivante pour rendre toutes les vues mobile-friendly :

if request.user_agent().is_mobile:
    response.view.replace('.html','.mobile.html')

La tâche de création des vues "*.mobile.html" est laissée au développeur mais nous suggérons fortement d'utiliser le plugin "jQuery Mobile" qui rend la tâche vraiment facile.

Fonctions dans les vues

Considérez ce "layout.html" :

<html>
  <body>
    {{include}}
    <div class="sidebar">
      {{if 'mysidebar' in globals():}}{{mysidebar()}}{{else:}}
        my default sidebar
      {{pass}}
    </div>
  </body>
</html>

et cette vue étendue

{{def mysidebar():}}
my new sidebar!!!
{{return}}
{{extend 'layout.html'}}
Hello World!!!

Notez que la fonction est définie avant la déclaration {{extend...}} -- ceci entraîne la création de la fonction avant que le code "layout.html" soit exécuté, afin que la fonction puisse être appelée n'importe où dans "layout.html", même avant le {{include}}. Notez également que la fonctoin est incluse dans la vue étendue sans le préfixe =.

Le code génère la sortie suivante :

<html>
  <body>
    Hello World!!!
    <div class="sidebar">
        my new sidebar!!!
    </div>
  </body>
</html>

Notez que la fonction est définie en HTML (bien qu'elle puisse également contenir du code Python) afin que response.write soit utilisé pour écrire son contenu (la fonction ne retourne pas le contenu). C'est pourquoi le layout appelle la fonction de vue en utilisant {{mysidebar()}} plutôt que {{=mysidebar()}}. Les fonctions définies par ce moyen peuvent prendre des arguments.

block

Blocs dans les vues

Le principal moyen de rendre une vue plus modulaire est d'utiliser {{block...}}s et ce mécanisme est une alternative au mécanisme présenté dans la section précédente.

Pour comprendre comment cela fonctionne, considérez les applications basées sur l'application de base Welcome, qui a une vue layout.html. Cette vue est incluse dans une vue default/index.html via {{extend 'layout.html'}}. Les contenus de layout.html prédéfinissent certains blocs avec certains contenus par défaut, et ils sont donc inclus dans default/index.html. Vous pouvez surcharger ces blocs de contenu par défaut en incluant votre nouveau contenu dans le bloc de même nom. La localisation du bloc dans le layout.html n'est pas changée, mais le contenu l'est. Voici une version simplifiée. Imaginez que c'est "layout.html" : <html> <body> {{include}} <div class="sidebar"> {{block mysidebar}} my default sidebar (this content to be replaced) {{end}} </div> </body> </html> :code et ceci est une simple extension de vue default/index.html : {{extend 'layout.html'}} Hello World!!! {{block mysidebar}} my new sidebar!!! {{end}}

:code Ceci génère la sortie suivante, où le contenu est fourni par le bloc surchargé dans la vue étendue, avec le DIV principal et la classe qui viennent du layout.html. Ceci permet la consistence à travers les vues :

<html> <body> Hello World!!! <div class="sidebar"> my new sidebar!!! </div> </body> </html> :code Le vrai layout.html définit un nombre de blocs utile, et vous pouvez facilement en ajouter plus pour faire correspondre le layout à vos besoins. Vous pouvez avoir plusieurs blocs, et si un bloc est présent dans la vue étendue mais pas dans la vue à étendre, le contenu de la vue étendue est utilisé. Notez également que contrairement aux fonctions, il n'est pas nécessaire de définir des blocs avant le {{extend ...}} -- même si défini après le extend, ils peuvent être utilisés pour faire des substitutions n'importe où dans la vue étendue. super:inxx Dans un bloc, vous pouvez utiliser l'expression {{super}} pour inclure le contenu du parent. Par exemple, si nous remplaçons la vue à étendre ci-dessus avec : {{extend 'layout.html'}} Hello World!!! {{block mysidebar}} {{super}} my new sidebar!!! {{end}}

:code nous obtenons :

<html> <body> Hello World!!! <div class="sidebar"> my default sidebar my new sidebar!!! </div> </body> </html> ``:code

 top