Gibt es in Moustache-Vorlagen eine elegante Möglichkeit, eine durch Kommas getrennte Liste ohne das nachfolgende Komma auszudrücken?

83

Ich verwende die Moustache-Vorlagenbibliothek und versuche, eine durch Kommas getrennte Liste ohne ein nachfolgendes Komma zu erstellen, z

rot grün blau

Das Erstellen einer Liste mit dem nachfolgenden Komma ist angesichts der Struktur unkompliziert

{
  "items": [
    {"name": "red"},
    {"name": "green"},
    {"name": "blue"}
  ]
}

und die Vorlage

{{#items}}{{name}}, {{/items}}

Dies wird zu lösen

rot grün blau,

Ich kann jedoch keine elegante Art sehen, den Fall ohne das nachfolgende Komma auszudrücken. Ich kann die Liste immer im Code generieren, bevor ich sie an die Vorlage übergebe. Ich habe mich jedoch gefragt, ob die Bibliothek einen alternativen Ansatz bietet, z. B. das Erkennen, ob es sich um das letzte Element in einer Liste in der Vorlage handelt.

John Kane
quelle
Ich schlage vor, die durch Kommas getrennte Liste in Ihrem Code zu erstellen und sie als einzelne Zeichenfolge an den Schnurrbart zu übergeben. Komplexere Logik als Optionen und einfache Listen sind in klassischen Programmiersprachen fast immer besser lesbar.
Yeoman
Komplexere Template-Engines als Schnurrbart können dies ganz einfach. Es ist jedoch in keinem von ihnen sehr gut lesbar, und in diesem Sinne war die Entscheidung, den Schnurrbart so einfach wie möglich zu machen, ganz bewusst: D
yeoman

Antworten:

42

Hrm, zweifelhaft, die Schnurrbart-Demo zeigt Ihnen ziemlich genau, mit demfirst Eigenschaft , dass Sie die Logik in den JSON-Daten haben müssen, um herauszufinden, wann das Komma gesetzt werden muss.

Ihre Daten würden also ungefähr so ​​aussehen:

{
  "items": [
    {"name": "red", "comma": true},
    {"name": "green", "comma": true},
    {"name": "blue"}
  ]
}

und Ihre Vorlage

{{#items}}
    {{name}}{{#comma}},{{/comma}}
{{/items}}

Ich weiß, dass es nicht elegant ist, aber wie von anderen erwähnt, ist Moustache sehr leicht und bietet keine solchen Funktionen.

Luca Matteis
quelle
23
Sie können Ihre Daten auch so formatieren, dass "Elemente": ["Rot", "Grün", "Blau"]. Dann können Sie einfach ein {{Elemente}} ausführen. Dadurch wird bereits eine durch Kommas getrennte Liste ausgegeben :)
Anthony Chua
10
Der Kommentar sollte eigentlich die richtige Antwort sein. Viel sauberer. Es ist wirklich eine schlechte Form, Ihre Datenquelle zu ändern, um den visuellen Bedürfnissen des Verbrauchers
gerecht zu werden
@AnthonyChua Obwohl elegant, kann dies (a) nicht dokumentiertes Verhalten sein und daher zukünftigen Änderungen unterliegen, obwohl dies unwahrscheinlich ist, und (b) dies setzt keine Leerzeichen nach den Kommas, so dass Sie so etwas erhalten first,second,third.
Caw
8
Es ist weniger Arbeit, nur eine Eigenschaft zum letzten Element hinzuzufügen {"name": "blue", "last": 1}und dann einen invertierten Abschnitt zu verwenden{{#items}} {{name}}{{^last}}, {{/last}} {{/items}}
TmTron
1
@ slick86 Der Kommentar über Ihrem führt zu einer durch Kommas getrennten Liste, jedoch nicht zu einer Liste, in der jedes Element in doppelte Anführungszeichen eingeschlossen ist.
GlenRSmith
92

Ich denke, ein besserer Weg ist es, das Modell dynamisch zu ändern. Wenn Sie beispielsweise JavaScript verwenden:

model['items'][ model['items'].length - 1 ].last = true;

Verwenden Sie in Ihrer Vorlage den umgekehrten Abschnitt:

{{#items}}
    {{name}}{{^last}}, {{/last}}
{{/items}}

um dieses Komma zu rendern.

Clyde
quelle
2
Ich liebe es. Jedes Mal, wenn ich denke, dass Moustache nicht genug Funktionen hat, liegt es daran, dass ich denke, dass die 'alte' Art, wie die Vorlage alles macht - nun, ich komme von JSPs.
Nicolas Zozol
1
@NicolasZozol Sie wissen, dass Schnurrbart mit genau dieser Einfachheit erstellt wurde? : D
yeoman
@NicolasZozol In wirklich komplizierten Fällen ist es am besten, Zeichenfolgen direkt in einer Programmiersprache zu erstellen und so ein einfaches Ansichtsmodell zu erstellen, das von der Vorlagensprache verarbeitet werden kann. In diesem Fall würde die durch Kommas getrennte Liste über Code als eine einzelne Zeichenfolge bereitgestellt.
Yeoman
41

Cheat und benutze CSS.

Wenn Ihr Modell ist:

{
  "items": [
    {"name": "red"},
    {"name": "green"},
    {"name": "blue"}
  ]
}

Dann machen Sie Ihre Vorlage

<div id="someContainer">
{{#items}}
    <span>{{name}}<span>
{{/items}}
</div>

und fügen Sie ein wenig CSS hinzu

#someContainer span:not(:last-of-type)::after {
  content: ", "    
}

Ich vermute, jemand wird sagen, dass dies ein schlechter Fall ist, wenn Markups in die Präsentation eingefügt werden, aber ich denke nicht, dass dies der Fall ist. Durch Kommas getrennte Werte sind eine Präsentationsentscheidung, um die Interpretation der zugrunde liegenden Daten zu vereinfachen. Es ähnelt dem Abwechseln der Schriftfarbe bei Einträgen.

Jared
quelle
Sollte beachtet werden, dass dies nur in IE9 + kompatibel ist. Wenn Sie also alte Browser unterstützen müssen, müssen Sie möglicherweise einen anderen Ansatz verwenden
PoeHaH
1
Dies lässt die Annahme zu, dass Schnurrbart für Webseiten verwendet wird - es gibt auch viele
andere
30

Wenn Sie zufällig jmustache verwenden , können Sie das Special -firstoder die -lastVariablen verwenden:

{{#items}}{{name}}{{^-last}}, {{/-last}}{{/items}}
dbort
quelle
4
Mir ist klar, dass sich das OP auf die JavaScript Moustache-Bibliothek bezieht, aber dies kann anderen jmustache-Benutzern (wie mir) helfen, die diese Seite finden.
dbort
Vielen Dank für die Antwort. Ich habe dies auch zum Rendern einer Vorlage mit SpringBoot verwendet. Das Modell muss nicht geändert werden. Ich habe wirklich nach dieser Funktion gesucht. Ich frage mich auch, ob es Gleichstellungskontrollen gibt (zB {{#something=TEXT}})
JeanValjean
8

Ich kann mir nicht viele Situationen vorstellen, in denen Sie eine unbekannte Anzahl von Elementen außerhalb eines <ul>oder auflisten möchten <ol>, aber so würden Sie es tun:

<p>
    Comma separated list, in sentence form;
    {{#each test}}{{#if @index}}, {{/if}}{{.}}{{/each}};
    sentence continued.
</p>

…wird herstellen:

Command separated list, in sentence form; asdf1, asdf2, asdf3; sentence continued.

Das ist wohlgemerkt Lenker. @indexfunktioniert, wenn testes sich um ein Array handelt.

Steven Vachon
quelle
scheint verdammt elegant! Angenommen, #if und @index sind in allen oder den meisten Implementierungen von Schnurrbart verfügbar ... Beachten Sie, dass viele von uns Schnurrbartbenutzern kein HTML generieren, selbst wenn dies der häufigste Anwendungsfall ist.
Spike0xff
Dies ist eine großartige Lösung, die wie ein Zauber wirkt. Wenn Sie das Ergebnis in ein HTML-Tag einbinden möchten, sollten Sie dies tun {{.}}.
NetOperator Wibby
Dies war der richtige Zeiger. Es scheint, dass Sie jetzt auch [erste] und [letzte] Bedingungen für eine noch bessere Kontrolle verwenden können. stackoverflow.com/questions/11479094/…
Maksym
6

Die Frage, ob Moustache eine elegante Möglichkeit bietet, dies zu tun, wurde beantwortet, aber mir ist der Gedanke gekommen, dass die eleganteste Möglichkeit dies sein könnte, CSS zu verwenden, anstatt das Modell zu ändern.

Vorlage:

<ul class="csl">{{#items}}<li>{{name}}</li>{{/items}}</ul>

CSS:

.csl li
{
    display: inline;
}
.csl li:before
{
    content: ", "
}
.csl li:first-child:before
{
    content: ""
}

Dies funktioniert in IE8 + und anderen modernen Browsern.

David Hammond
quelle
5

In Moustache gibt es keine integrierte Möglichkeit, dies zu tun. Sie müssen Ihr Modell ändern, um es zu unterstützen.

Eine Möglichkeit, dies in der Vorlage zu implementieren, besteht darin, das invertierte Auswahlhut- {{^last}} {{/last}}Tag zu verwenden. Es wird nur Text für das letzte Element in der Liste weggelassen.

{{#items}}
    {{name}}{{^last}}, {{/last}}
{{/items}}

Sie können ", "dem Objekt auch eine Trennzeichenfolge oder im Idealfall die Basisklasse hinzufügen , wenn Sie eine vererbte Sprache verwenden, und dann "Trennzeichen" " "für das letzte Element wie folgt auf eine leere Zeichenfolge setzen :

{{#items}}
    {{name}}{{delimiter}}
{{/items}}
cosbor11
quelle
1
Um klar zu sein, müsste etwas in den Eingabedaten tatsächlich "last" genannt werden, damit dies funktioniert. Richtig?
FrustratedWithFormsDesigner
1
Richtig, Sie müssten Ihr Modell ändern, indem Sie eine boolesche Eigenschaft namens aufrufen last. Setzen Sie dann das letzte Element in der Sammlung auf last=true.
cosbor11
1
Das "Modell" ist in diesem Fall tatsächlich eine Konfigurationsdatei, die Benutzer bearbeiten können ... Ich glaube nicht, dass ich ihnen vertrauen möchte, dass sie "last" ordnungsgemäß auf das richtige Element in einer Liste setzen.
FrustratedWithFormsDesigner
In welcher Sprache rufen Sie die Template-Engine auf?
cosbor11
2
In diesem Fall konvertieren Sie zur Laufzeit die configDateidarstellung in ein Python-Objekt. Ich vermute, die Konfiguration ist in jsonoder xml, oder ? Bevor Sie es an die Vorlagen-Engine übergeben, rufen Sie das letzte Element der Sammlung ab und wenden Sie die lastEigenschaft an.
cosbor11
2

Für JSON-Daten schlage ich vor:

Mustache.render(template, settings).replace(/,(?=\s*[}\]])/mig,'');

Der reguläre Ausdruck entfernt alle ,nach den letzten Eigenschaften hängen gebliebenen Elemente .

Dadurch werden auch ,Zeichenfolgenwerte entfernt, die ",}" oder ",]" enthalten. Stellen Sie daher sicher, dass Sie wissen, welche Daten in Ihren JSON-Code eingefügt werden

mathiasrw
quelle
Dies hat definitiv den Trick für mich getan, da ich ein JSON-Schema mit Vorlagen habe. Vielen Dank!
Yahyazini
2

Da ist die Frage:

Gibt es eine elegante Möglichkeit, eine durch Kommas getrennte Liste ohne das nachfolgende Komma auszudrücken?

Dann ist das Ändern der Daten - wenn das letzte Element bereits impliziert ist, weil es das letzte Element im Array ist - nicht elegant.

Jede Schnurrbart-Vorlagensprache mit Array-Indizes kann dies ordnungsgemäß ausführen. dh. ohne etwas zu den Daten hinzuzufügen . Dies umfasst Lenker, ractive.js und andere beliebte Schnurrbartimplementierungen.

{{# names:index}}
    {{ . }}{{ #if index < names.length - 1 }}, {{ /if }}
{{ / }}
Mikemaccana
quelle
1
OK. Aber Schnurrbart hat nichtif
Mauron85
@ Mauron85 In der Tat. Ich (und viele andere) benutze Schnurrbart als Plural für verschiedene Vorlagensprachen, die vom ursprünglichen Schnurrbart inspiriert sind.
Mikemaccana
1

Verwenden Sie eine @ data-Variable, da Moustache am Lenker steht. Ich fand das die sauberste Option:

{{#if @last}}, {{/if}}

Weitere Informationen: http://handlebarsjs.com/reference.html#data

Roberto14
quelle
2
Der Lenker baut auf dem Schnurrbart auf, nicht umgekehrt
Andy Jones,
1

Der einfachste Weg, den ich gefunden habe, war, die Liste zu rendern und dann das letzte Zeichen zu entfernen.

  1. Schnurrbart rendern.
  2. Entfernen Sie alle Leerzeichen vor und nach der Zeichenfolge.
  3. Entfernen Sie dann das letzte Zeichen

    let renderedData = Moustache Render (dataToRender, data); renderedData = (renderingData.trim ()). Teilzeichenfolge (0, renderedData.length-1)

Thabelo Phusu Mmbengeni
quelle
0

Interessant. Ich weiß, dass es etwas faul ist, aber ich komme normalerweise darum herum, indem ich die Wertzuweisung vorlege, anstatt zu versuchen, die Werte durch Kommas abzugrenzen.

var global.items = {};
{{#items}}
    global.items.{{item_name}} = {{item_value}};
{{/items}}
Iwnnay
quelle
0

Ich neige dazu zu denken, dass dies eine Aufgabe ist, die gut für CSS geeignet ist (wie von anderen beantwortet). Angenommen, Sie versuchen beispielsweise, eine CSV-Datei zu erstellen, stehen Ihnen HTML und CSS nicht zur Verfügung. Wenn Sie in Betracht ziehen, Daten zu ändern, um dies trotzdem zu tun, ist dies möglicherweise eine übersichtlichere Methode:

var data = {
  "items": [
    {"name": "red"},
    {"name": "green"},
    {"name": "blue"}
  ]
};

// clone the original data. 
// Not strictly necessary, but sometimes its
// useful to preserve the original object
var model = JSON.parse(JSON.stringify(data));

// extract the values into an array and join 
// the array with commas as the delimiter
model.items = Object.values(model.items).join(',');

var html = Mustache.render("{{items}}", model);
Jefferey Höhle
quelle
0

Wenn Sie Java verwenden, können Sie Folgendes verwenden:

https://github.com/spullara/mustache.java/blob/master/compiler/src/test/java/com/github/mustachejava/util/DecoratedCollectionTest.java

MustacheFactory mf = new DefaultMustacheFactory();
Mustache test = mf.compile(new StringReader("{{#test}}{{#first}}[{{/first}}{{^first}}, {{/first}}\"{{value}}\"{{#last}}]{{/last}}{{/test}}"), "test");
StringWriter sw = new StringWriter();
test.execute(sw, new Object() {
    Collection test = new DecoratedCollection(Arrays.asList("one", "two", "three"));
}).flush();
System.out.println(sw.toString());
王子 1986
quelle
0

Ich weiß, dass dies eine alte Frage ist, aber ich wollte trotzdem eine Antwort hinzufügen, die einen anderen Ansatz bietet.

Hauptantwort

Moustache unterstützt Lambdas ( siehe Dokumentation ), so dass man es so schreiben kann:

Vorlage:

    {{#removeTrailingComma}}{{#items}}{{name}}, {{/items}}{{/removeTrailingComma}}

Hash:

    {
      "items": [
        {"name": "red"},
        {"name": "green"},
        {"name": "blue"}
      ]
      "removeTrailingComma": function() {
        return function(text, render) {
          var original = render(text);
          return original.substring(0, original.length - 2);
        }
      }
    }

Ausgabe:

rot grün blau

Kommentar

Persönlich mag ich diesen Ansatz gegenüber den anderen, da das Modell meiner Meinung nach nur angeben sollte, was gerendert wird und nicht, wie es gerendert wird. Technisch gesehen ist das Lambda Teil des Modells, aber die Absicht ist viel klarer.

Ich benutze diesen Ansatz, um meine eigenen OpenApi-Generatoren zu schreiben. Dort wird Moustache von Java umhüllt, aber die Funktionalität ist ziemlich gleich. So sieht es für mich aus, Lambdas zu kreieren: (in Kotlin)

    override fun addMustacheLambdas(): ImmutableMap.Builder<String, Mustache.Lambda> =
        super.addMustacheLambdas()
            .put("lowerCase", Mustache.Lambda { fragment, writer ->
                writer.write(fragment.execute().toLowerCase())
            })
            .put("removeLastComma", Mustache.Lambda { fragment, writer ->
                writer.write(fragment.execute().removeSuffix(","))
            })
            .put("printContext", Mustache.Lambda { fragment, writer ->
                val context = fragment.context()
                println(context) // very useful for debugging if you are not the author of the model
                writer.write(fragment.execute())
            })
LostMekkaSoft
quelle