Mustacheテンプレートでは、末尾のコンマなしでコンマ区切りのリストを表現するエレガントな方法はありますか?


83

Mustacheテンプレートライブラリを使用していて、末尾にコンマを付けずにコンマ区切りのリストを生成しようとしています。

赤、緑、青

構造を考えると、末尾のコンマを使用してリストを作成するのは簡単です。

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

とテンプレート

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

これはに解決されます

赤、緑、青、

ただし、末尾のコンマなしでケースを表現するエレガントな方法はわかりません。リストをテンプレートに渡す前に、いつでもコードでリストを生成できますが、ライブラリが、テンプレート内のリストの最後のアイテムであるかどうかを検出できるようにするなど、別のアプローチを提供するかどうか疑問に思いました。


コードでコンマ区切りのリストを作成し、それを単一の文字列として口ひげに渡すことをお勧めします。オプションや単純なリストよりも複雑なロジックは、ほとんどの場合、従来のプログラミング言語で読みやすくなっています。
ヨーマン2018

口ひげよりも複雑なテンプレートエンジンは、これを非常に簡単に行うことができます。しかし、それらのどれでもあまり読みにくいです、そしてこれを念頭に置いて、口ひげをそれがそうであるのと同じくらい単純にする決定は非常に慎重でした:D
ヨーマン

回答:


43

うーん、疑わしいことに、口ひげのデモは、firstプロパティを使用して、コンマをいつ配置するかを判断するためにJSONデータ内にロジックが必要であることをほぼ示しています。

したがって、データは次のようになります。

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

とあなたのテンプレート

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

エレガントではないことは知っていますが、他の人が言っているように、 Mustacheは非常に軽量で、そのような機能を提供していません。


24
データをフォーマットして、 "items":["red"、 "green"、 "blue"]にすることもできます。その後、{{items}}を実行すると、すでにカンマ区切りのリストが出力されます:)
Anthony Chua

10
コメントは実際には正解である必要があります。はるかにきれい。消費者の視覚的ニーズに応えるためにデータソースを変更するのは本当に悪い形です
Slick86 2013年

@AnthonyChuaエレガントですが、これは(a)動作が文書化されていないため、将来変更される可能性がありますが、可能性は低く、(b)カンマの後にスペースを入れないため、のようなものになりますfirst,second,third
caw 2014

8
最後のアイテムに1つのプロパティのみを追加してから、{"name": "blue", "last": 1}反転セクションを使用する方が作業が少なくて済みます{{#items}} {{name}}{{^last}}, {{/last}} {{/items}}
TmTron 2014年

1
@ slick86あなたの上のコメントはコンマで区切られたリストになりますが、各項目が二重引用符で囲まれているリストにはなりません。
GlenRSmith 2016年

92

モデルを動的に変更するのがより良い方法だと思います。たとえば、JavaScriptを使用している場合:

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

テンプレートでは、反転セクションを使用します。

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

そのコンマをレンダリングします。


2
大好きです。Mustacheに十分な機能がないと思うたびに、それはテンプレートがすべてを行う「古い」方法だと思うからです。まあ、私はJSPから来ています。
Nicolas Zozol 2013

1
@NicolasZozol口ひげがまさにこの種の単純さを念頭に置いて作成されたことをご存知ですか?:D
ヨーマン2018

@NicolasZozol非常に複雑なケースでは、プログラミング言語で直接文字列を作成し、テンプレート言語で処理できる単純なビューモデルを作成するのが最善です。この場合、コンマ区切りのリストは、コードを介して1つの単一の文字列として提供されます。
ヨーマン2018

41

CSSをごまかして使用します。

モデルが次の場合:

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

次に、テンプレートを作成します

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

CSSを少し追加します

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

これはプレゼンテーションにマークアップを入れるのは悪いケースだと誰かが言うだろうと思いますが、そうではないと思います。値を区切るコンマは、基になるデータの解釈を容易にするためのプレゼンテーションの決定です。これは、エントリのフォントの色を変えるのと似ています。


これはIE9 +でのみ互換性があることに注意してください。したがって、古いブラウザをサポートする必要がある場合は、別のアプローチを使用する必要があります
PoeHaH 2014年

1
これは、口ひげがウェブページに使用されていることを前提になります-それはあまりにも外にその用途がたくさんある
tddmonkey

30

あなたが使用することが起こる場合jmustacheを、あなたは特別な使用することができます-firstまたは-last変数を:

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

4
OPがJavaScriptMustacheライブラリを参照していたことはわかりましたが、これは、このページを見つけた他のjmustacheユーザー(私のような)に役立つ可能性があります。
dbort 2015年

答えてくれてありがとう。これは、SpringBootでテンプレートをレンダリングするためにも使用しました。モデルを変更する必要はありません。私は本当にこの機能を探していました。また、平等管理があるかどうか疑問に思います(例{{#something=TEXT}}
JeanValjean 2016年

8

<ul>またはの外に不明な数のアイテムをリストしたいという多くの状況を考えることはできません<ol>が、これはあなたがそれを行う方法です:

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

…生成されます:

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

これはハンドルバーです。@indextest配列の場合に機能します。


かなりエレガントなようです!#ifと@indexが口ひげのすべてまたはほとんどの実装で使用可能であると仮定します...私たち口ひげユーザーの多くは、それが最も一般的なユースケースであっても、HTMLを生成していないことに注意してください。
spike0xff 2016

これは素晴らしい解決策であり、魅力のように機能します。結果をHTMLタグでラップしたい場合は、この答えに出くわした人に注意してください{{.}}
NetOperator Wibby 2017

これは正しい指針でした。[first]および[last]条件を使用して、さらに適切に制御できるようになりました。stackoverflow.com/questions/11479094/...
Maksym

6

Mustacheがこれを行うためのエレガントな方法を提供するかどうかという質問には答えましたが、これを行うための最もエレガントな方法は、モデルを変更するのではなく、CSSを使用することかもしれないと思いました。

テンプレート:

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

CSS:

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

これは、IE8 +およびその他の最新のブラウザーで機能します。


5

Mustacheでこれを行うための組み込みの方法はありません。モデルをサポートするには、モデルを変更する必要があります。

これをテンプレートに実装する1つの方法は、反転選択ハットタグを使用すること{{^last}} {{/last}}です。リストの最後の項目のテキストのみが省略されます。

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

または", "、オブジェクトに区切り文字列を追加するか、継承のある言語を使用している場合は基本クラスを追加してから、次の" "ように最後の要素の空の文字列に「区切り文字」を設定します。

{{#items}}
    {{name}}{{delimiter}}
{{/items}}

1
明確にするために、これが機能するためには、入力データ内の何かに実際に「最後」という名前を付ける必要があります。正しい?
frustratedWithFormsDesigner 2015

1
正解です。。というブールプロパティを追加して、モデルを変更する必要がありますlast。次に、コレクションの最後のアイテムをに設定しlast=trueます。
cosbor11 2015

1
この場合の「モデル」は、実際にはユーザーが編集できる構成ファイルです...リストの適切な項目に「最後」を適切に配置することをユーザーに信頼したくないと思います。
FrustratedWithFormsDesigner

テンプレートエンジンを呼び出すためにどの言語を使用していますか?
cosbor11 2015

2
その場合、実行時にconfigファイル表現をPythonオブジェクトに変換し ます。設定はjsonまたはにあるxmlと思いますよね?次に、それをテンプレートエンジンに渡す前に、コレクションの最後のアイテムを取得して、lastプロパティを適用します。
cosbor11 2015

3

JSONデータの場合:私が提案するもの:

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

正規表現はすべてを削除します ,、最後のプロパティの後にぶら下がっているます。

これにより,、「、}」または「、]」が続く文字列値も削除されるため、JSONに入力されるデータを確認してください。


私はテンプレート化されたJSONスキーマを持っているので、これは間違いなく私にとってトリックでした。ありがとう!
yahyazini

2

質問は次のとおりです。

末尾のコンマなしでコンマ区切りのリストを表現するエレガントな方法はありますか?

次に、データの変更(最後の項目であることが配列の最後の項目であるためにすでに暗黙的である場合)は、エレガントではありません。

配列インデックスを持つ口ひげテンプレート言語は、これを適切に行うことができます。すなわち。 データに何も追加せずに。これには、ハンドルバー、ractive.js、およびその他の一般的な口ひげの実装が含まれます。

{{# names:index}}
    {{ . }}{{ #if index < names.length - 1 }}, {{ /if }}
{{ / }}

1
OK。しかし、口ひげにはありませんif
mauron 8519年

@ mauron85確かに。私(および他の多くの人)は、元の口ひげに触発されたさまざまなテンプレート言語の複数形として口ひげを使用しています。
mikemaccana

1

私が見つけた最も簡単な方法は、リストをレンダリングしてから最後の文字を削除することでした。

  1. 口ひげを生やします。
  2. 文字列の前後の空白をすべて削除します。
  3. 次に、最後の文字を削除します

    renderData = Mustache Render(dataToRender、data); renderData =(renderedData.trim())。substring(0、renderedData.length-1)



0

面白い。私はそれが一種の怠惰であることを知っていますが、私は通常、値をコンマで区切るのではなく、値の割り当てをテンプレート化することによってこれを回避します。

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

0

これはCSSに適したタスクだと思う傾向があります(他の人が答えたように)。ただし、CSVファイルの作成などを実行しようとしているとすると、HTMLとCSSを使用できなくなります。また、とにかくこれを行うためにデータを変更することを検討している場合、これはそれを行うためのよりきちんとした方法かもしれません:

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);

0

Javaを使用している場合は、次を使用できます。

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());

0

これは古い質問ですが、別のアプローチを提供する回答を追加したいと思いました。

主な答え

Mustacheはラムダをサポートしているため(ドキュメントを参照)、次のように記述できます。

テンプレート:

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

ハッシュ:

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

出力:

赤、緑、青

コメント

個人的には、このアプローチが他のアプローチよりも好きです。IMHOモデルはレンダリング対象のみを指定しレンダリング方法は指定しないためです。技術的には、ラムダはモデルの一部ですが、意図ははるかに明確です。

私はこのアプローチを使用して、独自のOpenApiジェネレーターを作成しています。そこでは、MustacheはJavaでラップされていますが、機能はほとんど同じです。これは私にとってラムダの作成がどのように見えるかです:(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())
            })

0

より複雑なシナリオでは、多くの理由からビューモデルが望ましいです。これは、表示、またはこの場合はテンプレート処理により適した方法でモデルのデータを表します。

ビューモデルを使用している場合は、目標を容易にする方法でリストを簡単に表すことができます。

モデル:

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

モデルの表示:

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

2番目のリスト表現は入力するのが恐ろしいですが、コードから作成するのは非常に簡単です。ビューモデルにモデルをマッピングしながら、ちょうどあなたが必要とするすべてのリストを交換firstし、lastこの表現を持つため。

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
}

無制限のリストの場合は、設定firstと省略のみが可能ですlast、末尾のコンマを回避するにはそのうちの1つで十分なので、です。

使用first

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

使用last

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

0

私の場合、動的SQLクエリを操作するときは、そのためにカスタム関数を使用しています。

    $(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", "john.wick@hotmail.com"],
      ["Donald", "Duck", "donald.duck@ducks.com"],
      ["Anonymous", "Anonymous","jack.kowalski@gmail.com"]
    ];

    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/

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.