Clienteinblicke

Erweiterte JsRender Templating-Features

John Papa

John PapaVorlagen sind sehr nützlich, aber manchmal brauchen Sie mehr als nur die Standardfeatures, die ein Vorlagenmodul direkt zur Verfügung stellt. Vielleicht wollen Sie Daten konvertieren, eine eigene Hilfsfunktion definieren oder einen eigenen Tag erstellen. Die gute Nachricht ist, dass Sie mit den Kernfunktionen von JsRender dies und noch viel mehr tun können.

In meiner April-Kolumne (msdn.microsoft.com/magazine/hh882454) habe ich die grundlegenden Features der JsRender-Templating-Bibliothek vorgestellt. Hier fahre ich mit der Erläuterung von JsRender fort; dazu verwende ich weitere Szenarien, wie etwa das Rendering externer Vorlagen, die Änderung des Kontexts mit dem {{for}}-Tag und die Verwendung komplexer Ausdrücke. Darüber hinaus zeige ich, wie einige der leistungsstärkeren Funktionen von JsRender verwendet werden, darunter die Erstellung benutzerdefinierter Tags, Konverter, Kontexthelfer sowie das Zulassen von benutzerdefiniertem Code. Alle Codebeispiele stehen auf archive.msdn.microsoft.com/mag201205ClientInsight und JsRender selbst auf bit.ly/ywSoNu zum Download zur Verfügung.

{{for}}-Variationen

Der {{for}}-Tag kann in verschiedener Weise eine ideale Lösung sein. In meiner letzten Kolumne habe ich gezeigt, wie der {{for}}-Tag mithilfe eines Blocks bei der Iteration durch ein Array helfen kann, und wie die gleichzeitige Iteration durch mehrere Objekte möglich ist: 

    

<!-- looping {{for}} -->
{{for students}}
{{/for}}           
<!--  combo iterators {{for}} -->
{{for teachers students staff}}
{{/for}}

Der {{for}}-Tag (oder jeder Block-Tag) kann von einem Block-Tag (mit Inhalt) zu einem selbstschließenden Tag konvertiert werden. Dazu wird der Block-Inhalt durch eine externe Vorlage ersetzt, auf die sie deklarativ als eine tmpl-Eigenschaft verweisen. Der Tag rendert dann die externe Vorlage anstelle des Inline-Inhalts.

Dies erleichtert die Verwendung eines modularen Konzepts für Vorlagen, bei dem Sie Vorlagenmarkup an verschiedenen Stellen verwenden und Vorlagen organisieren und zusammenstellen können:

<!--  self closing {{for}} -->
{{for lineItems tmpl="#lineItemsDetailTmpl" /}}

Die Daten sind relativ flach, daher ist die Arbeit mit Objekthierarchien ein wichtiges Merkmal für Vorlagen. In meiner letzten Kolumne habe ich die grundlegenden Techniken für die Arbeit mit einer Objekthierarchie mit Punktnotation und eckigen Klammern gezeigt, Sie können aber auch den {{for}}-Tag verwenden, um den Code zu reduzieren. Dies wird deutlicher, wenn Sie eine Objektstruktur haben, bei der Sie in eine Objekthierarchie gehen und einen Satz von Eigenschaften von einem untergeordneten Objekt rendern müssen. Zum Beispiel: Beim Rendern der Adresse eines Personenobjekts können Sie die Vorlage folgendermaßen schreiben, wobei der "Address"-Term in dem Pfad mehrmals wiederholt wird:

<div>{{:address.street1}}</div>
<div>{{:address.street2}}</div>
<div>{{:address.city}}, {{:address.state}} {{:address.postalCode}}</div>

Der{{for}}-Tag kann den Code für das Rendern einer Adresse deutlich einfacher machen, da das Adressenobjekt nicht wiederholt werden muss, wie hier gezeigt:

<!--  "with" {{for}} -->
{{for address}}
  <div>{{:street1}}</div>
  <div>{{:street2}}</div>
  <div>{{:city}}, {{:state}} {{:postalCode}}</div>
{{/for}}

Der {{for}}-Tag wirkt auf die "Address"-Eigenschaft, die ein einzelnes Objekt mit Eigenschaften (und kein Objekt-Array) ist. Wenn die Adresse "truthy" ist (d.h. mindestens einen nicht-"falsey"-Wert enthält), werden die Inhalte des {{for}}-Blocks gerendert. {{for}} ändert auch den aktuellen Datenkontext vom Personenobjekt zum Adressenobjekt, d. h., er fungiert wie ein "with"-Befehl in vielen Bibliotheken und Sprachen. Im vorigen Beispiel ändert der {{for}}-Tag den Datenkontext zur Adresse und rendert dann die Inhalte der Vorlagen einmal (da nur eine Adresse vorhanden ist). Wenn die Person keine Adresse hat (d.h. die Adresseneigenschaft ist Null oder nicht definiert), werden die Inhalte überhaupt nicht gerendert. Deshalb ist der {{for}}-Block ideal für Vorlagen geeignet, die nur unter bestimmten Umständen angezeigt werden sollen. Dieses Beispiel (aus der Datei 08-for-variations.html im Code-Download zu diesem Artikel) zeigt, wie der {{for}}-Tag zur Anzeige von Preisinformationen (falls solche vorhanden sind) verwendet wird:

{{for pricing}}
  <div class="text">${{:salePrice}}</div>
  {{if fullPrice !== salePrice}}
    <div class="text highlightText">PRICED TO SELL!</div>
  {{/if}}
{{/for}}

Externe Vorlagen

Einer der größten Vorteile der Nutzung von Vorlagen ist die Wiederverwendung von Code. Wenn eine Vorlage innerhalb eines <script>-Tags auf derselben Seite definiert wird, auf der sie verwendet wird, ist die Vorlage nicht so wiederverwendbar, wie dies möglich wäre. Vorlagen, die von mehreren Seiten aus zugänglich sein sollen, können in eigenen Dateien erstellt und bei bedarf abgerufen werden. JavaScript und jQuery erleichtern den Abruf einer Vorlage aus einer externen Datei, und JsRender ermöglicht es, sie zu rendern. 

Eine Konvention, die ich für externe Vorlagen verwenden möchte, ist, vor den Dateinamen einen Unterstrich zu setzen; dies ist eine häufig verwendete Benennungskonvention für Teilansichten. Für alle Vorlagendateien verwende ich darüber hinaus das Suffix .tmpl.html. Die Zeichenfolge ".tmpl" bedeutet dabei, dass es sich um eine Vorlage ("Template") handelt, und ".html" macht es Entwicklungstools wie Visual Studio leichter zu erkennen, dass die Datei HTML enthält. Abbildung 1 zeigt das Rendering einer externen Vorlage.

Abbildung 1 Code für das Rendering einer externen Vorlage

my.utils = (function () {
  var
    formatTemplatePath = function (name) {
      return "/templates/_" + name + ".tmpl.html";
    },
    renderTemplate = function (tmplName, targetSelector, data) {
      var file = formatTemplatePath(tmplName);
      $.get(file, null, function (template) {
        var tmpl = $.templates(template);
        var htmlString = tmpl.render(data);
        if (targetSelector) {
          $(targetSelector).html(htmlString);
        }
        return htmlString;
          });
        };
    return {
      formatTemplatePath: formatTemplatePath,
        renderExternalTemplate: renderTemplate
    };
})()

Eine Möglichkeit zum Abruf der Vorlage aus einer externen Datei besteht darin, eine Dienstprogrammfunktion zu schreiben, die vom JavaScript in einer Webanwendung aufgerufen werden kann. Beachten Sie in Abbildung 1, dass die renderExternalTemplate-Funktion auf dem my.utils-Objekt die Vorlage zuerst mit der $.get-Funktion abruft. Wenn der Aufruf abgeschlossen ist, wird die JsRender-Vorlage mit der $.templates-Funktion aus den Inhalten der Antwort erstellt. Abschließend wird die Vorlage mit ihrer Render-Funktion gerendert, und der resultierende HTML-Code wird im Ziel angezeigt. Dieser Code kann mit dem folgenden Code aufgerufen werden, wobei der Vorlagenname, das DOM-Ziel und der Datenkontext an die angepasste renderExternalTemplates-Funktion übergeben werden:

my.utils.renderExternalTemplate("medMovie", "#movieContainer", my.vm);

Die externe Vorlage für dieses Beispiel befindet sich in der Beispieldatei _medMo­vie.tm­pl.html und enthält nur die HTML- und JsRender-Tags. Sie ist nicht in einem <script>-Tag eingeschlossen. Ich ziehe diese Vorgehensweise für externe Vorlagen vor, da die Entwicklungsumgebung erkennt, dass die Inhalte HTML sind. Dies macht das Schreiben des Codes weniger fehleranfällig, da IntelliSense direkt funktioniert. Die Datei könnte jedoch mehrere Vorlagen enthalten, die jeweils in einen <script>-Tag eingeschlossen und zur Identifikation mit einer eindeutigen ID versehen sind. Dies ist eine der Möglichkeiten für den Umgang mit externen Vorlagen. Abbildung 2 zeigt das abschließende Ergebnis.

The Result of Rendering an External Template
Abbildung 2 Das Ergebnis des Renderings einer externen Vorlage

Ansichtspfade

JsRender bietet verschiedene spezielle Ansichtspfade, die den Zugriff auf das aktuelle Ansichtsobjekt erleichtern. #view ermöglicht den Zugriff auf die aktuelle Ansicht, #data ermöglicht den Zugriff auf den aktuellen Datenkontext für die Ansicht, #parent geht aufwärts durch die Objekthierarchie, und #index gibt eine Indexeigenschaft aus:

<div>{{:#data.section}}</div>
<div>{{:#parent.parent.data.number}}</div>
<div>{{:#parent.parent.parent.parent.data.name}}</div>
<div>{{:#view.data.section}}</div>

Bei der Verwendung von Ansichtspfaden (außer #view) wirken diese bereits auf die aktuelle Ansicht. Anders ausgedrückt, Folgendes ist äquivalent:

#data
#view.data

Die Ansichtspfade sind nützlich für die Navigation in Objekthierarchien, etwa von Kunden mit Aufträgen und Auftragsdetails oder von Filmen in Lagern an Lagerplätzen (vgl. die Code-Download-Beispieldatei 11-view-paths.html).

Ausdrücke

Allgemeine Ausdrücke sind essenzielle Bestandteile der Logik und können bei der Entscheidung, wie eine Vorlage gerendert werden soll, nützlich sein. JsRender unterstützt allgemeine Ausdrücke, wie u.a. die in Abbildung 3 gezeigten.

Abbildung 3 Allgemeine Ausdrücke in JsRender

Ausdruck Beispiel Anmerkungen
+ {{ :a + b }} Addition
- {{ :a - b }} Subtraktion
* {{ :a * b }} Multiplikation
/ {{ :a / b }} Division
|| {{ :a || b }} Logisches oder
&& {{ :a && b }} Logisches und
! {{ :!a }} Negation
? : {{ :a === 1 ? b * 2: c * 2 }} Tertiärer Ausdruck
( ) {{ :(a||-1) + (b||-1) }} Evaluierungsanordnung mit Klammern
% {{ :a % b }} Modulus-Operation
<= und >= und < und > {{ :a <= b }} Vergleichsoperationen
=== und !== {{ :a === b }} Gleichheit und Ungleichheit

JsRender unterstützt die Ausdrucksevaluierung, jedoch nicht die Zuweisung des Ausdrucks oder die Ausführung von Zufallscode. Dadurch werden Ausdrücke ausgeschlossen, die andernfalls Variablenzuordnungen durchführen oder etwa ein Warnfenster öffnen könnten. Ausdrücke dienen dazu, einen Ausdruck zu evaluieren und dann entweder das Ergebnis zu rendern, auf der Grundlage des Ergebnis eine Aktivität auszulösen oder das Ergebnis in einer anderen Operation zu verwenden.

Zum Beispiel: Die Ausführung von {{:a++}} mit JsRender würde zu einem Fehler führen, da der Ausdruck versucht, eine Variable zu erhöhen. Auch die Ausführung von {{:alert(‘hello’)}} führt zu einem Fehler, da der Ausdruck versucht, eine Funktion, #view.data.alert, aufzurufen, die nicht existiert.

Registrieren benutzerdefinierter Tags

JsRender bietet mehrere leistungsstarke Erweiterungen, wie etwa benutzerdefinierte Tags, Konverter, Hilfsfunktionen und Vorlagenparameter. Die Syntax für den Aufruf dieser Erweiterungen ist:

{{myConverter:name}}
{{myTag name}}
{{:~myHelper(name)}}
{{:~myParameter}}

Jede dieser Erweiterungen dient verschiedenen Zwecken; je nach Situation können aber auch gewisse Überschneidungen vorkommen. Bevor ich zeige, wie Sie dabei die richtige Auswahl treffen, ist es wichtig zu verstehen, welche Funktionen sie jeweils erfüllen, und wie sie definiert werden.

Benutzerdefinierte Tags sind ideal, wenn Sie ein Objekt mit "steuerungsähnlichen" Eigenschaften, das eigenständig sein kann, rendern müssen. So können etwa "Stern-Bewertungen" ganz einfach als eine Zahl mit Daten gerendert werden, etwa so:

{{:rating}}

Es könnte jedoch sinnvoller sein, dafür die JavaScript-Logik mit CSS und einer Reihe leerer und gefüllter "Stern"-Bilder zu verwenden:

{{createStars averageRating max=5/}}

Die Logik für die Erstellung der Sterne kann (und sollte) von der Präsentation getrennt sein. JsRender bietet eine Möglichkeit zur Erstellung eines benutzerdefinierten Tags, der diese Funktion einschließt. Der Code in Abbildung 4 definiert einen benutzerdefinierten Tag mit der Bezeichnung "createStars" und registriert ihn bei JsRender, so dass er auf jeder Seite verwendet werden kann, die dieses Skript lädt. Die Verwendung dieses benutzerdefinierten Tags erfordert, dass seine JavaScript-Datei, jsrender.tag.js im Beispielcode, in der Seite enthalten ist.

Abbildung 4 Erstellen eines benutzerdefinierten Tags

$.views.tags({
  createStars: function (rating) {
    var ratingArray = [], defaultMax = 5;
    var max = this.props.max || defaultMax;
    for (var i = 1; i <= max; i++) {
      ratingArray.push(i <= rating ? 
        "rating fullStar" : "rating emptyStar");
    }
    var htmlString = "";
    if (this.tmpl) {
      // Use the content or the template passed in with the template property.
      htmlString = this. renderContent(ratingArray);
    } else {
        // Use the compiled named template.
        htmlString = $.render.compiledRatingTmpl(ratingArray);
    }
    return htmlString;
  }

Benutzerdefinierte Tags können deklarative Eigenschaften haben, wie etwa die oben gezeigte "max=5"-Eigenschaft von {{createStars}}. Der Zugriff im Code erfolgt über this.props. So registriert beispielsweise der folgende Code einen benutzerdefinierten Tag mit der Bezeichnung "sort", der ein Array aufnimmt (wenn die Eigenschaft "reverse" auf "true" gesetzt wird, {{sort array reverse=true/}}, wird der Array in umgekehrter Reihenfolge zurückgegeben):

$.views.tags({
sort: function(array){
  var ret = "";
  if (this.props.reverse) {
    for (var i = array.length; i; i--) {
      ret += this.tmpl.render(array[i - 1]);
    }
  } else {
      ret += this.tmpl.render(array);
  }
  return ret;
}}

Eine gute Faustregel ist, dass ein benutzerdefinierter Tag dann verwendet werden sollte, wenn Sie ein komplexeres Objekt (wie etwa ein "createStars"- oder "sort"-Tag) rendern müssen. Für einmalige Szenarien sind benutzerdefinierte Tags weniger geeignet.

Konverter

Während benutzerdefinierte Tags ideal für die Erstellung von Inhalten geeignet sind, sind Konverter besser für den einfachen Vorgang der Umwandlung eines Quellwerts in einen anderen Wert geeignet. Konverter können Quellwerte (wie etwa einen Booleschen Wert "true" oder "false") in etwas völlig anderes umwandeln (etwa in die Farben Grün oder Rot). Zum Beispiel: Der folgende Code verwendet den Konverter "priceAlert" zur Ausgabe einer Zeichenfolge mit einer Preiswarnung auf der Grundlage des Wertes "salePrice":

<div class="text highlightText">{{priceAlert:salePrice}}</div>

Konverter sind auch hervorragend für die Umwandlung von URLs geeignet, wie hier gezeigt:

<img src="{{ensureUrl:boxArt.smallUrl}}" class="rightAlign"/>

Im folgenden Beispiel kann der Konverter "ensureUrl" den Wert "boxArt.smallUrl" in eine qualifizierte URL umwandeln (beide Konverter werden in der Datei 12-converters.html verwendet und sind in jsrender.helpers.js mit der Funktion JsRender $.views.converters registriert):

$.views.converters({
  ensureUrl: function (value) {
    return (value ? value : "/images/icon-nocover.png");
  },
  priceAlert: function (value) {0
    return (value < 10 ? "1 Day Special!" : "Sale Price");
  }
});

Konverter dienen zur nicht-parametrisierten Umwandlung von Daten in einen gerenderten Wert. Wenn das Szenario Parameter erfordert, ist eine Hilfsfunktion oder ein benutzerdefinierter Tag besser geeignet, als ein Konverter. Wie wir bereits gesehen haben, erlauben benutzerdefinierte Tags benannte Parameter, der Tag "createStars" könnte also über Parameter für die Definition der Größe der Sterne, für ihre Farben, die anzuwendenden CSS-Klassen usw. verfügen. Wichtig ist hierbei, dass Konverter für einfache Umwandlungen gedacht sind, während benutzerdefinierte Tags für komplexere Renderingvorgänge dienen.

Hilfsfunktionen und Vorlagenparameter

Es gibt verschiedene Möglichkeiten, Hilfsfunktionen oder Parameter zur Verwendung beim Rendern von Vorlagen einzufügen. Eine davon besteht darin, dass Sie $.views.helpers ähnlich wie beim Registrieren von Tags oder Konvertern verwenden:

$.views.helpers({
  todaysPrices: { unitPrice: 23.40 },
  extPrice:function(unitPrice, qty){
    return unitPrice * qty;
  }
});

Dadurch werden sie für alle Vorlagen in der Anwendung verfügbar. Eine andere Möglichkeit ist, sie als Optionen in den Renderingaufruf einzufügen:

$.render.myTemplate( data, {
  todaysPrices: { unitPrice: 23.40 },
  extPrice:function(unitPrice, qty){
    return unitPrice * qty;
  }
});

Dieser Code macht sie nur im Kontext dieses speziellen Vorlagenrendering-Aufrufs verfügbar. Bei beiden Methoden ist der Zugriff auf die Hilfsfunktionen aus der Vorlage heraus dadurch möglich, dass der Name der Funktion oder des Parameters (oder der Pfad) das Präfix “~” erhält.

{{: ~extPrice(~todaysPrices.unitPrice, qty) }}

Hilfsfunktionen können fast alles tun: Daten konvertieren, Berechnungen durchführen, Anwendungslogik ausführen, Arrays oder Objekte zurückgeben oder sogar eine Vorlage zurückgeben.

So könnte etwa eine Hilfsfunktion mit der Bezeichnung "getGuitars" erstellt werden, die ein Array von Produkten durchsucht und alle Gitarrenprodukte findet. Diese Funktion könnte auch einen Parameter für den Gitarrentyp enthalten. Das Ergebnis könnte dann zum Rendern eines einzigen Wertes oder für das Durchlaufen des resultierenden Arrays verwendet werden (da Hilfsfunktionen alles zurückgeben können). Der folgende Code kann ein Array aller Produkte abrufen, bei denen es sich um akustische Gitarren handelt, und sie dann mit einem {{for}-Block durchlaufen:

{{for ~getGuitars('acoustic')}} ... {{/for}}

Hilfsfunktionen können auch andere Hilfsfunktionen aufrufen, etwa zur Berechnung eines Gesamtpreises mit einem Array der Positionsartikel eines Auftrags bei Anwendung von Rabatten und Steuersätzen:

{{:~totalPrice(~extendedPrice(lineItems, discount), taxRate}}

Hilfsfunktionen, die für mehrere Vorlagen zugänglich sind, werden durch die Übergabe eines Objektliterals, das die Hilfsfunktionen enthält, an die Funktion JsRender $.views.helpers definiert. Im folgenden Beispiel wird die Funktion "concat" zur Verkettung mehrerer Argumente definiert:

$.views.helpers({
  concat:function concat() {
    return "".concat.apply( "", arguments );
  }
})

Die Hilfsfunktion "concat" kann mit {{:~concat(first, age, last)}} aufgerufen werden. Wenn die Werte für "first", "middle" und "last" sind "John", "25" und "Doe" sind, wird der Wert "John25Doe" gerendert.

Hilfsfunktionen für einmalige Szenarien

Sie können in eine Situation geraten, in der Sie eine Hilfsfunktion für eine bestimmte Vorlage verwenden, sie jedoch nicht in anderen Vorlagen wiederverwenden möchten. So könnte etwa eine Einkaufswagen-Vorlage eine Berechnung erfordern, die nur für diese Vorlage relevant ist. Eine Hilfsfunktion könnte die Berechnung durchführen, es ist jedoch nicht nötig, diese für alle Vorlagen verfügbar zu machen. JsRender unterstützt dieses Szenario mit der zweiten oben erwähnten Vorgehensweise – durch die Übergabe der Funktion mit den Optionen in einem Renderingaufruf:

$.render.shoppingCartTemplate( data, {
  todaysPrices: { unitPrice: 23.40 },
  extPrice:function(unitPrice, qty){
    return unitPrice * qty;
  }
});

In diesem Fall wird die Einkaufswagen-Vorlage gerendert, und die Hilfsfunktionen und Vorlagenparameter, die sie für die Berechnung benötigt, werden direkt mit dem Renderingaufruf bereitgestellt. Der entscheidende Punkt dabei ist, dass die Hilfsfunktion nur während des Renderings dieser speziellen Vorlage existiert.

Welche Methode soll verwendet werden?

JsRender bietet verschiedene Optionen zur Erstellung leistungsstarker Vorlagen mit Konvertern, benutzerdefinierten Tags und Hilfsfunktionen, es ist jedoch wichtig zu wissen, in welchen Szenarien diese verwendet werden sollten. Eine gute Faustregel bietet der Entscheidungsbaum in Abbildung 5, der bei der Entscheidung für das zu verwendende Feature hilft.

Abbildung 5 Ein Entscheidungsbaum für die Wahl des richtigen Hilfsmittels

if (youPlanToReuse) {
  if (simpleConversion && !parameters){
    // Register a converter.
  }
  else if (itFeelsLikeAControl && canBeSelfContained){
    // Register a custom tag.
  }
  else{
    // Register a helper function.
  }
}
else {
  // Pass in a helper function with options for a template.
}

Wenn die Funktion lediglich einmal verwendet werden soll, ist es nicht nötig, sie in der gesamten Anwendung verfügbar zu machen. Dies ist die ideale Situation für eine "einmaligen" Hilfsfunktion, die bei bedarf eingeführt wird.

Code zulassen

Es gibt Situationen, in denen es einfacher ist, innerhalb der Vorlage benutzerdefinierten Code zu schreiben. JsRender ermöglicht die Einbettung von Code, ich würde jedoch empfehlen, dies nur zu tun, wenn alle anderen Möglichkeiten nicht in Frage kommen. Der Code kann schwer zu verwalten sein, da er Präsentation und Verhaltensweise vermischt. 

Code kann in eine Vorlage eingebettet werden, indem er in einen Block mit dem Präfix {{* }} eingeschlossen und "allowCode" auf "true" gesetzt wird. Zum Beispiel: Die Vorlage mit der Bezeichnung "myTmpl" (in Abbildung 6) enthält eingebetteten Code zur Evaluierung der geeigneten Orte zum Rendern eines Befehls oder des Wortes "und" in verschiedenen Sprachen. Das vollständige Beispiel finden Sie in der Datei 13-allowcode.html. Die Logik ist nicht sehr kompliziert, der Code kann jedoch in der Vorlage schwer zu lesen sein.

JsRender lässt die Ausführung des Codes nur zu, wenn die Eigenschaft "allowCode" auf "true" (die Standardeinstellung ist "false") gesetzt ist. Der folgende Code definiert die kompilierte Vorlage mit der Bezeichnung "movieTmpl", weist ihr den Markup aus dem Skript-Tag aus Abbildung 6 zu, und gibt an, dass in der Vorlage die Verwendung von Code zugelassen ist:

$.templates("movieTmpl", {
  markup: "#myTmpl",
  allowCode: true
});
$("#movieRows").html(
  $.render.movieTmpl(my.vm.movies)
);

Sobald die Vorlage erstellt ist, wird sie gerendert. Die Funktion "allowCode" kann dazu führen, dass der Code schwer zu lesen ist, und in manchen Fällen könnte für den gewünschten Zweck auch eine Hilfsfunktion verwendet werden. Das Beispiel in Abbildung 6 verwendet das Feature "allowCode" von JsRender für die bedarfsweise Hinzufügung von Kommata und des Wortes "and". Dies könnte jedoch auch durch eine Hilfsfunktion bewerkstelligt werden.

$.views.helpers({
  languagesSeparator: function () {
    var view = this;
    var text = "";
    if (view.index === view.parent.data.length - 2) {
      text = " and";
    } else if (view.index < view.parent.data.length - 2) {
      text = ",";
    }
    return text;
  }
})

Abbildung 6 Zulassen von Code in einer Vorlage

<script id="myTmpl" type="text/x-jsrender">
  <tr>
    <td>{{:name}}</td>
    <td>
      {{for languages}}
        {{:#data}}{{*
          if ( view.index === view.parent.data.length - 2 ) {
        }} and {{*
          } else if ( view.index < view.parent.data.length - 2 ) {
        }}, {{* } }}
      {{/for}}
    </td>
  </tr>
</script>

Diese Hilfsfunktion "languagesSeparator" wird durch das Präfix “~” vor ihrem Namen aufgerufen. Dadurch wird der Vorlagencode, der die Hilfsfunktion aufruft, viel einfacher lesbar, wie hier gezeigt:

{{for languages}}
  {{:#data}}{{:~languagesSeparator()}}
{{/for}}

Die Verschiebung der Logik in eine Hilfsfunktion entfernt Verhaltensweisen aus der Vorlage und verschiebt sie in JavaScript, was guten Separierungsmustern entspricht.

Leistung und Flexibilität

JsRender bieten verschiedene Funktionen, die deutlich über das Rendern von Eigenschaftswerten hinausgehen. Dazu gehören etwa die Unterstützung komplexer Ausdrücke, die Iterierung und Änderung von Kontext mit dem {{for}}-Tag und Ansichtspfade für die Navigation durch den Kontext. Darüber hinaus bietet JsRender die Hilfsmittel zur Erweiterung seines Funktionsumfangs durch benutzerdefinierte Tags, Konverter und Hilfsprogramme. Diese Features und das reine stringbasierte Konzept für die Vorlagenerstellung ermöglichen JsRender, eine hohe Leistung zu erbringen und machen es äußerst flexibel.

John Papa ist ein ehemaliger Microsoft-Experte in den Silverlight- and Windows 8-Teams und hat die beliebte "Silverlight-TV"-Sendung moderiert. Er hat weltweit Vorträge auf den BUILD-, MIX-, PDC-, TechEd-, Visual Studio Live!- und DevConnections-Veranstaltungen gehalten. Darüber hinaus ist er Microsoft Regional Director, schreibt Beiträge für das Visual Studio Magazine ("Papa's Perspective") und ist Autor von Schulungsvideos bei Pluralsight. Sie können ihm auf Twitter unter twitter.com/john_papa folgen.

Unser Dank gilt dem folgenden technischen Experten für die Durchsicht dieses Artikels: Boris Moore