3.3 - Utilisation de gabarits récursifs

Attention : Ce tutoriel est ancien, incomplet, et n'est pas mis à jour. Il ne contient pas toutes les nouveautés du système de template de Gecko 1.9 / Firefox 3.0 qui est largement simplifié et permet d'utiliser sqlite ou xml comme source de données. Aussi est-il préférable d'aller consulter la version anglaise sur developer.mozilla.org.

Vous devez garder à l'esprit que les gabarits génèrent leur contenu récursivement. Après qu'une série de données aient été générées, chaque résultat est utilisé comme nouveau point de référence pour une autre étape récursive du gabarit. L'utilisation habituelle est la génération du contenu d'un arbre ou d'un menu. L'itération interne utilise les mêmes règles que l'itération externe. Cependant, Il est possible que vous préfériez faire apparaître les noeuds enfants ou orphelins de manière différente de leurs parents. Les règles multiples sont utiles dans cette situation. Dans ce cas, une règle sera utilisée pour trouver les données externes et une autre règle sera utilisée pour trouver les données internes. Le constructeur appliquera toutes les règles dans tous les cas, mais si les règles ont été correctement créées, les données devraient y correspondre comme souhaité.

Par exemple, nous pouvons imaginer une source de données représentée par les maisons d'un quartier. Le noeud principal contient plusieurs fils, un pour chaque rue. Chaque rue contient également des fils, un pour chaque maison. Naturellement, vous désirez que les rues d'affichent différemment des maisons. La nature récursive des gabarits peut être utilisée pour cet exemple. La passe externe débutera au noeud principal et générera le contenu pour chaque rue. La passe suivante utilisera une rue comme point de départ et générera le contenu pour chaque maison. Vous pourriez continuer et générer les données pour chaque pièce de chaque maison en ajoutant d'autres règles.

Voici un exemple affichant un échantillon de données d'un quartier d'habitation.

<hbox datasources="template-guide-streets.rdf"
      ref="http://www.xulplanet.com/rdf/myneighbourhood"
      xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
  <template>
    <rule rdf:type="http://www.xulplanet.com/rdf/House">
      <vbox uri="rdf:*" class="box-padded">
        <label value="Adresse : rdf:http://www.xulplanet.com/rdf/address"/>
        <label value="Appartement : rdf:http://www.xulplanet.com/rdf/floors"/>
      </vbox>
    </rule>
    <rule>
      <groupbox uri="rdf:*" class="box-padded">
        <caption label="rdf:http://purl.org/dc/elements/1.1/title"/>
      </groupbox>
    </rule>
  </template>
</hbox>

La première règle recherche seulement les items ayant un type RDF http://www.xulplanet.com/rdf/House. La seconde règle n'a aucun filtre conditionnel et va ainsi trouver tous les résultats. Le point de départ indiqué par l'attribut ref est http://www.xulplanet.com/rdf/myneighbourhood. Dans les données RDF, il s'agit d'un Bag RDF avec deux fils. Puisque les deux règles utilisent la syntaxe simplifiée, le constructeur va parcourir les fils des résultats générés. Lors de cette passe, les deux fils de http://www.xulplanet.com/rdf/myneighbourhood sont des rues et non des maisons, donc aucun d'eux ne correspondra à la première règle. Toutefois, ils vont correspondre à la seconde règle. Ainsi, deux correspondances seront créées en utilisant la seconde règle. Celle-ci crée un groupbox avec un caption. Si vous regardez l'image de l'exemple, vous noterez que deux boîtes de groupe ont été créées.

Le constructeur va boucler récursivement en utilisant le résultat obtenu comme nouveau point de départ. Pour la première rue, le nouveau point de départ sera http://www.xulplanet.com/rdf/marion. Le constructeur réapplique les règles à partir de ce nouvel emplacement dans le graphe RDF. Le nouveau noeud est un Seq RDF avec des enfants, donc les règles simplifiées peuvent générer quelques résultats. Cependant, ces résultats sont des maisons qui correspondront à la première règle. Ils correspondront également à la seconde règle puisqu'elle n'a pas de conditions, mais comme la première règle est prioritaire sur la seconde, ces règles ne seront jamais appliquées. Il en résulte que le contenu de la première règle sera utilisé pour chaque maison. Ce contenu est inséré à l'intérieur du contenu externe déjà généré pour les rues. En clair, le vbox et les deux libellés seront placés à l'intérieur de l'élément groupbox généré lors de la passe précédente.

Nous pourrions être plus précis et spécifier un type pour les rues dans la source de données. Cela n'affectera pas l'affichage de cet exemple mais des gabarits plus complexes deviendront plus performants avec l'écriture de conditions bien spécifiques. S'il y avait d'autres types de construction sur une rue particulière, vous pourrions ajouter une règle supplémentaire pour cela. Par exemple, nous pourrions ajouter une autre règle après la première :

<rule rdf:type="http://www.xulplanet.com/rdf/Store">
  <vbox uri="rdf:*" class="box-padded">
    <label value="Adresse : rdf:http://www.xulplanet.com/rdf/address"/>
    <label value="Enseigne : rdf:http://www.xulplanet.com/rdf/sells"/>
  </vbox>
</rule>

Cette règle est similaire à la première, toutefois seuls les items ayant un type RDF http://www.xulplanet.com/rdf/Store y correspondront.

Générer un menu récursif

Lors de la création de menus récursifs, vous devrez employer de multiples règles puisque les items feuilles devront être créés différemment des items branches. Les items feuilles devront utiliser un élément menuitem tandis que les items branches devront utiliser un élément menu. Deux règles sont nécessaires, bien que vous pourriez en ajouter d'autres si vous aviez d'autres différences à gérer.

<button label="Les maisons de mon quartier" type="menu"
        datasources="template-guide-streets.rdf"
        ref="http://www.xulplanet.com/rdf/myneighbourhood"
        xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
  <template>
    <rule rdf:type="http://www.xulplanet.com/rdf/House">
      <menupopup>
        <menuitem uri="rdf:*" label="rdf:http://www.xulplanet.com/rdf/address"/>
      </menupopup>
    </rule>
    <rule>
      <menupopup>
        <menu uri="rdf:*" label="rdf:http://purl.org/dc/elements/1.1/title"/>
      </menupopup>
    </rule>
  </template>
</button>

La première règle recherche toutes les maisons, alors que la seconde règle est utilisée pour les rues. Le contenu généré par chaque règle diffère seulement sur deux aspects. Tout d'abord, la balise du menu est différente (menuitem contre menu), et le libellé est donné par un autre prédicat. Lors de la première passe, la seconde règle trouve les rues, donc des éléments menupopup et menu sont créés. L'attribut uri est placé sur l'élément menu car nous ne devons pas répéter le popup pour chaque résultat. Après la première passe, le contenu sera équivalent à ce qui suit (en ignorant le contenu associé au gabarit) :

<button label="Les maisons de mon quartier" type="menu">
  <menupopup>
    <menu uri="http://www.xulplanet.com/rdf/marion" label="rue Marion"/>
    <menu uri="http://www.xulplanet.com/rdf/garden" label="Avenue du Jardin"/>
  </menupopup>
</button>

La passe interne à travers les données gère les maisons. Les maisons correspondent à la première règle, donc des éléments menupopup et menuitem sont générés et insérés à l'intérieur du contenu des rues (l'élément menu). Une nouvelle fois, le menupopup est créé une seule fois puisque l'attribut uri est placé sur l'élément menuitem. L'effet obtenu est un menu avec un sous menu. Il n'y a rien de spécial sur le fonctionnement des menus -- le constructeur procède de la même méthode pour n'importe quel type de contenu. Cependant, la nature des menus peut être difficile à appréhender. Voici le résultat de l'exemple ci-dessus après que les deux niveaux aient été traités :

<button label="Les maisons de mon quartier" type="menu">
  <menupopup>
    <menu uri="http://www.xulplanet.com/rdf/marion" label="rue Marion">
      <menupopup>
        <menuitem uri="http://www.xulplanet.com/rdf/garden/16" label="16"/>
        <menuitem uri="http://www.xulplanet.com/rdf/garden/18" label="18"/>
      </menupopup>
    </menu>
    <menu uri="http://www.xulplanet.com/rdf/garden" label="Avenue du Jardin">
      <menupopup>
        <menuitem uri="http://www.xulplanet.com/rdf/garden/25" label="25"/>
        <menuitem uri="http://www.xulplanet.com/rdf/garden/37" label="37"/>
      </menupopup>
    </menu>
  </menupopup>
</button>

Mozilla® est une marque déposée de la fondation Mozilla.
Mozilla.org™, Firefox™, Thunderbird™, Mozilla Suite™ et XUL™ sont des marques de la fondation Mozilla.