Dans la création de modèles Moustache, existe-t-il une manière élégante d'exprimer une liste séparée par des virgules sans la virgule de fin?

83

J'utilise la bibliothèque de modèles Moustache et j'essaie de générer une liste séparée par des virgules sans virgule de fin, par exemple

rouge, vert, bleu

Créer une liste avec la virgule de fin est simple, compte tenu de la structure

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

et le modèle

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

cela se résoudra à

rouge, vert, bleu,

Cependant, je ne vois pas de manière élégante d'exprimer la casse sans la virgule de fin. Je peux toujours générer la liste dans le code avant de la passer dans le modèle, mais je me demandais si la bibliothèque offre une approche alternative telle que vous permettre de détecter s'il s'agit du dernier élément d'une liste dans le modèle.

John Kane
la source
Je suggère de créer la liste séparée par des virgules dans votre code et de la passer à moustache sous forme de chaîne unique. Une logique plus complexe que les options et les listes simples est presque toujours plus lisible dans les langages de programmation classiques.
yeoman
Des moteurs de modèles plus complexes que moustache peuvent le faire assez facilement. Ce n'est pas très lisible dans aucun d'entre eux, cependant, et dans cet esprit, la décision de rendre la moustache aussi simple qu'elle est était tout à fait délibérée: D
yeoman

Réponses:

43

Hrm, douteux, la démo de moustache vous montre à peu près, avec la firstpropriété, que vous devez avoir la logique à l'intérieur des données JSON pour savoir quand mettre la virgule.

Ainsi, vos données ressembleraient à quelque chose comme:

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

et votre modèle

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

Je sais que ce n'est pas élégant, mais comme mentionné par d'autres, Moustache est très léger et ne fournit pas de telles fonctionnalités.

Luca Matteis
la source
24
vous pouvez également formater vos données, de sorte que "items": ["red", "green", "blue"] alors vous pouvez simplement faire un {{items}} cela affichera déjà une liste séparée par des virgules :)
Anthony Chua
10
Le commentaire devrait en fait être la bonne réponse. Beaucoup plus propre. C'est vraiment une mauvaise forme pour modifier votre source de données pour répondre aux besoins visuels du consommateur
Slick86
@AnthonyChua Bien qu'élégant, cela peut (a) ne pas être un comportement documenté et donc sujet à des changements futurs, bien que peu probables, et (b) cela ne met pas d'espaces après les virgules, vous obtenez donc quelque chose comme first,second,third.
caw
8
il est moins de travail d'ajouter seulement une propriété au dernier élément {"name": "blue", "last": 1}, puis d'utiliser une section inversée{{#items}} {{name}}{{^last}}, {{/last}} {{/items}}
TmTron
1
@ slick86 Le commentaire au-dessus du vôtre produit une liste séparée par des virgules, mais pas une dans laquelle chaque élément est placé entre guillemets.
GlenRSmith
92

Je pense qu'une meilleure façon est de changer le modèle de manière dynamique. Par exemple, si vous utilisez JavaScript:

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

et dans votre modèle, utilisez la section inversée:

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

pour rendre cette virgule.

Clyde
la source
2
J'aime cela. Chaque fois que je pense que Moustache n'a pas assez de fonctionnalités, c'est parce que je pense que la «vieille» façon où le template fait tout - eh bien, je viens de JSP.
Nicolas Zozol
1
@NicolasZozol Vous vous rendez compte que la moustache a été créée avec exactement ce genre de simplicité à l'esprit? : D
yeoman
@NicolasZozol Pour les cas vraiment compliqués, il est préférable de créer des chaînes directement dans un langage de programmation, et ainsi de créer un modèle de vue simple que le langage de modèle peut gérer. Dans ce cas, la liste séparée par des virgules serait fournie via le code sous la forme d'une seule chaîne.
yeoman
41

Trichez et utilisez CSS.

Si votre modèle est:

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

puis faites votre modèle

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

et ajoutez un peu de CSS

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

Je suppose que quelqu'un dira que c'est un mauvais cas de mise en place d'un balisage dans la présentation, mais je ne pense pas que ce soit le cas. La séparation des valeurs par des virgules est une décision de présentation pour faciliter l'interprétation des données sous-jacentes. C'est similaire à l'alternance de la couleur de la police sur les entrées.

Jared
la source
Il convient de noter que cela n'est compatible que dans IE9 +, donc si vous devez prendre en charge d'anciens navigateurs, vous devrez peut-être utiliser une autre approche
PoeHaH
1
Cela suppose que la moustache est utilisée pour les pages Web - elle a également de nombreuses utilisations en dehors de cela
tddmonkey
30

Si vous utilisez jmustache , vous pouvez utiliser les variables spéciales -firstou -last:

{{#items}}{{name}}{{^-last}}, {{/-last}}{{/items}}
dbort
la source
4
Je me rends compte que l'OP faisait référence à la bibliothèque JavaScript Moustache, mais cela peut aider d'autres utilisateurs de jmustache (comme moi) qui trouvent cette page.
dbort
Merci beaucoup pour la réponse. Je l'ai également utilisé pour rendre un modèle avec SpringBoot. Pas besoin de changer de modèle. J'étais vraiment à la recherche de cette fonctionnalité. Je me demande aussi s'il y a des contrôles d'égalité (par exemple {{#something=TEXT}})
JeanValjean
8

Je ne peux pas penser à de nombreuses situations dans lesquelles vous voudriez répertorier un nombre inconnu d'éléments en dehors d'un <ul>ou <ol>, mais voici comment procéder:

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

…produira:

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

Ceci est Guidon, remarquez. @indexfonctionnera si testest un tableau.

Steven Vachon
la source
semble sacrément élégant! en supposant que #if et @index sont disponibles dans toutes ou la plupart des implémentations de moustache ... Gardez à l'esprit que beaucoup d'entre nous, utilisateurs de moustache, ne génèrent pas de HTML, même si c'est le cas d'utilisation le plus courant.
Spike0xff
C'est une solution géniale, fonctionne comme un charme. Attention à toute personne rencontrant cette réponse, si vous souhaitez envelopper le résultat dans une balise HTML, faites-le autour de vous {{.}}.
NetOperator Wibby le
C'était le bon indicateur. Il semble que vous pouvez maintenant utiliser les conditionnelles [premier] et [dernier] pour un contrôle encore meilleur. stackoverflow.com/questions/11479094/…
Maksym
6

La question de savoir si Moustache offre une manière élégante de faire cela a été résolue, mais il m'est venu à l'esprit que la manière la plus élégante de le faire pourrait être d'utiliser CSS plutôt que de changer le modèle.

Modèle:

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

CSS:

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

Cela fonctionne dans IE8 + et d'autres navigateurs modernes.

David Hammond
la source
5

Il n'y a pas de moyen intégré de faire cela dans Moustache. Vous devez modifier votre modèle pour le supporter.

Une façon d'implémenter cela dans le modèle consiste à utiliser la {{^last}} {{/last}}balise chapeau de sélection inversée . Il omettra uniquement le texte du dernier élément de la liste.

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

Ou vous pouvez ajouter une chaîne de délimitation quant ", "à l'objet, ou idéalement la classe de base si vous utilisez un langage qui a un héritage, puis définissez "delimiter" sur une chaîne vide " "pour le dernier élément comme ceci:

{{#items}}
    {{name}}{{delimiter}}
{{/items}}
cosbor11
la source
1
Pour être clair, quelque chose dans les données d'entrée devrait en fait être nommé «dernier» pour que cela fonctionne. Correct?
FrustratedWithFormsDesigner
1
Correct, vous devrez modifier votre modèle en ajoutant une propriété booléenne appelée last. Définissez ensuite le dernier élément de la collection sur last=true.
cosbor11
1
Le "modèle" dans ce cas est en fait un fichier de configuration que les utilisateurs peuvent éditer ... Je ne pense pas que je veux leur faire confiance pour mettre correctement "dernier" sur le bon élément dans une liste.
FrustratedWithFormsDesigner
quelle langue utilisez-vous pour appeler le moteur de modèle?
cosbor11
2
Dans ce cas, lors de l'exécution, convertissez la configreprésentation du fichier en un objet Python. Je suppose que la configuration est dans jsonou xml, non? Ensuite, avant de le transmettre au moteur de modèle, récupérez le dernier élément de la collection et appliquez la lastpropriété.
cosbor11
3

Pour les données JSON, je suggère:

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

L'expression régulière supprimera tout ce qui ,reste en suspens après les dernières propriétés.

Cela supprimera également ,de la chaîne les valeurs continuant ",}" ou ",]", alors assurez-vous de savoir quelles données seront placées dans votre JSON

mathiasrw
la source
Cela a vraiment fait l'affaire pour moi puisque j'ai un schéma JSON basé sur un modèle. Merci!
yahyazini
2

Comme la question est:

Existe-t-il une manière élégante d'exprimer une liste séparée par des virgules sans la virgule de fin?

Ensuite, changer les données - lorsque le fait d'être le dernier élément est déjà implicite en étant l'élément final du tableau - n'est pas élégant.

Tout langage de modèle de moustache qui a des index de tableau peut le faire correctement. c'est à dire. sans rien ajouter aux données . Cela inclut les guidons, ractive.js et d'autres implémentations de moustache populaires.

{{# names:index}}
    {{ . }}{{ #if index < names.length - 1 }}, {{ /if }}
{{ / }}
Mikemaccana
la source
1
D'accord. Mais la moustache n'a pasif
mauron85
@ mauron85 En effet. J'utilise (et beaucoup d'autres) moustache comme pluriel pour diverses langues de création de modèles inspirées de la moustache originale.
mikemaccana
1

Le moyen le plus simple que j'ai trouvé était de rendre la liste, puis de supprimer le dernier caractère.

  1. Rendre la moustache.
  2. Supprimez tout espace blanc avant et après la chaîne.
  3. Puis supprimez le dernier caractère

    let renduData = Rendu de moustache (dataToRender, données); renduData = (renduData.trim ()). sous-chaîne (0, renduData.longueur-1)

Thabelo Phusu Mmbengeni
la source
1

Dans le cas où l'utilisation de Handlebars est une option, qui étend les capacités de Moustache, vous pouvez utiliser une variable @data:

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

Plus d'infos: http://handlebarsjs.com/reference.html#data

Roberto14
la source
3
Le guidon s'appuie sur Moustache, pas l'inverse
Andy Jones
0

Intéressant. Je sais que c'est un peu paresseux, mais je contourne généralement cela en créant des modèles dans l'attribution de valeur plutôt que d'essayer de délimiter les valeurs par des virgules.

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

J'ai tendance à penser que c'est une tâche bien adaptée au CSS (comme l'ont répondu d'autres). Cependant, en supposant que vous essayez de faire quelque chose comme produire un fichier CSV, vous n'auriez pas de HTML et de CSS à votre disposition. De plus, si vous envisagez de modifier les données pour le faire de toute façon, cela peut être une façon plus ordonnée de le faire:

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);
Grotte de Jefferey
la source
0

Si vous utilisez java, vous pouvez utiliser les éléments suivants:

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
la source
0

Je sais que c'est une vieille question, mais je voulais quand même ajouter une réponse qui propose une autre approche.

Réponse principale

Moustache supporte les lambdas, ( voir documentation ) donc on peut l'écrire de cette façon:

Modèle:

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

Hacher:

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

Production:

rouge, vert, bleu

Commentaire

Personnellement, j'aime cette approche par rapport aux autres, car à mon humble avis, le modèle ne doit spécifier que ce qui est rendu et non comment il est rendu. Techniquement, le lambda fait partie du modèle, mais l'intention est beaucoup plus claire.

J'utilise cette approche pour écrire mes propres générateurs OpenApi. Là, Moustache est enveloppé par Java, mais la fonctionnalité est à peu près la même. Voici à quoi ressemble la création de lambdas pour moi: (en 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
la source
0

J'utilise des fonctions personnalisées pour cela, dans mon cas lorsque je travaille avec des requêtes SQL dynamiques.

    $(document).ready(function () {
    var output = $("#output");    
    var template = $("#test1").html();
    var idx = 0;
    var rows_count = 0;
    var data = {};
    
    data.columns = ["name", "lastname", "email"];
    data.rows  = [
        ["John", "Wick", "[email protected]"],
      ["Donald", "Duck", "[email protected]"],
      ["Anonymous", "Anonymous","[email protected]"]
    ];

    data.rows_lines = function() {
        let rows = this.rows;
      let rows_new = [];
      for (let i = 0; i < rows.length; i++) {
        let row = rows[i].map(function(v) {
            return `'${v}'`
        })
                rows_new.push([row.join(",")]);
      }
      rows_count = rows_new.length;
      return rows_new
    }
    data.last = function() {
        return idx++ === rows_count-1; // omit comma for last record
    }
    
    var html = Mustache.render(template, data);
    output.append(html);

});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mustache.js/4.0.1/mustache.min.js"></script>
<h2>Mustache example: Generate SQL query (last item support - omit comma for last insert)</h2>

<div id="output"></div>

<script type="text/html" id="test1">
    INSERT INTO Customers({{{columns}}})<br/>
    VALUES<br/>
        {{#rows_lines}}
                ({{{.}}}){{^last}},{{/last}}<br/>
      {{/rows_lines}}
</script>

https://jsfiddle.net/tmdoit/4p5duw70/8/

tmdoit
la source
0

Dans des scénarios plus complexes, un modèle de vue est souhaitable pour de nombreuses raisons. Il représente les données du modèle d'une manière qui convient mieux à l'affichage ou, dans ce cas, au traitement du modèle.

Dans le cas où vous utilisez un modèle de vue, vous pouvez facilement représenter des listes d'une manière qui facilite votre objectif.

Modèle:

{
    name: "Richard",
    numbers: [1, 2, 3]
}

Voir le modèle:

{
    name: "Richard",
    numbers: [
        { first: true, last: false, value: 1 },
        { first: false, last: false, value: 2 },
        { first: false, last: true, value: 3 }
    ]
}

La représentation de la deuxième liste est horrible à taper mais extrêmement simple à créer à partir du code. Lors du mappage de votre modèle sur le modèle de vue, remplacez simplement chaque liste dont vous avez besoin firstet lastpour par cette représentation.

function annotatedList(values) {
    let result = []
    for (let index = 0; index < values.length; ++index) {
        result.push({
            first: index == 0,
            last: index == values.length - 1,
            value: values[index]
        })
    }
    return result
}

Dans le cas de listes illimitées, vous pouvez également définir firstet omettre uniquement last, car l'une d'elles suffit pour éviter la virgule de fin.

Utilisation first:

{{#numbers}}{{^first}}, {{/first}}{{value}}{{/numbers}}

Utilisation last:

{{#numbers}}{{value}}{{^last}}, {{/last}}{{/numbers}}
yeoman
la source