95

我正在使用Mustache 模板库并尝试生成一个没有尾随逗号的逗号分隔列表,例如

红、绿、蓝

给定结构,创建带有尾随逗号的列表很简单

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

和模板

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

这将解决

红、绿、蓝、

但是,如果没有尾随逗号,我看不到一种优雅的表达方式。在将列表传递给模板之前,我总是可以在代码中生成列表,但我想知道库是否提供了另一种方法,例如允许您检测它是否是模板中列表中的最后一项。

4

19 回答 19

102

我认为更好的方法是动态更改模型。例如,如果您使用 JavaScript:

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

并在您的模板中,使用倒置部分:

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

渲染那个逗号。

于 2011-09-29T02:37:43.817 回答
46

Hrm,值得怀疑,小胡子演示几乎向您展示了该first属性,您必须在 JSON 数据中具有逻辑才能确定何时放置逗号。

所以你的数据看起来像:

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

和你的模板

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

我知道它并不优雅,但正如其他人所提到的, Mustache 非常轻量级并且不提供此类功能。

于 2011-05-25T16:54:08.460 回答
46

作弊并使用 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: ", "    
}

我猜有人会说这是在演示文稿中添加标记的一个不好的例子,但我不这么认为。逗号分隔值是一种表示决策,可以更轻松地解释基础数据。这类似于在条目上交替字体颜色。

于 2012-09-14T21:38:17.220 回答
35

如果您碰巧使用jmustache,则可以使用特殊-first-last变量:

{{#items}}{{name}}{{^-last}}, {{/-last}}{{/items}}
于 2015-11-19T02:42:28.073 回答
7

我想不出在很多情况下您想在<ul>or之外列出未知数量的项目<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.

这是把手,请注意。@index如果test是一个数组,它将起作用。

于 2013-12-13T20:08:23.357 回答
7

在 Mustache 中没有内置的方法可以做到这一点。你必须改变你的模型来支持它。

在模板中实现这一点的一种方法是使用反向选择帽子{{^last}} {{/last}}标签。它只会省略列表中最后一项的文本。

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

或者,您可以为对象添加分隔符字符串", ",或者如果您使用具有继承的语言,最好添加基类,然后将“分隔符”设置" "为最后一个元素的空字符串,如下所示:

{{#items}}
    {{name}}{{delimiter}}
{{/items}}
于 2015-07-14T00:47:11.970 回答
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+ 和其他现代浏览器。

于 2012-08-08T15:28:39.463 回答
5

聚会有点晚了,但可能对现在正在寻找的人有所帮助:5.1.1 可以这样做:

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

输出:

变量 1、变量 2、变量 3

于 2021-04-12T09:06:57.033 回答
3

对于 JSON 数据,我建议:

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

正则表达式将删除,最后一个属性之后的任何左挂。

这也,将从字符串值中删除连续“,}”或“,]”,因此请确保您知道将哪些数据放入您的 JSON

于 2016-01-24T11:29:04.280 回答
3

如果使用 Handlebars 是一个选项,它扩展了 Mustache 的功能,您可以使用 @data 变量:

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

更多信息:http ://handlebarsjs.com/reference.html#data

于 2016-09-29T13:41:30.653 回答
2

因为问题是:

有没有一种优雅的方式来表达没有尾随逗号的逗号分隔列表?

然后更改数据 - 当最后一项已经隐含为数组中的最后一项时 - 并不优雅。

任何具有数组索引的 mustache 模板语言都可以正确执行此操作。IE。 无需向数据添加任何内容。这包括把手、ractive.js 和其他流行的 mustache 实现。

{{# names:index}}
    {{ . }}{{ #if index < names.length - 1 }}, {{ /if }}
{{ / }}
于 2016-04-12T16:55:57.947 回答
1

我发现最简单的方法是渲染列表,然后删除最后一个字符。

  1. 渲染小胡子。
  2. 删除字符串前后的所有空格。
  3. 然后删除最后一个字符

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

于 2018-08-27T10:26:40.410 回答
1

在更复杂的场景中,出于多种原因需要视图模型。它以更适合显示或在本例中为模板处理的方式表示模型的数据。

如果您使用的是视图模型,您可以轻松地以有助于实现目标的方式表示列表。

模型:

{
    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 }
    ]
}

第二个列表表示很难键入,但从代码创建却非常简单。在将您的模型映射到视图模型时,只需将您需要的每个列表firstlastfor 替换为此表示。

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,因为其中之一足以避免尾随逗号。

使用first

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

使用last

{{#numbers}}{{value}}{{^last}}, {{/last}}{{/numbers}}
于 2020-11-08T09:04:52.907 回答
0

有趣的。我知道这有点懒,但我通常通过在值分配中进行模板化而不是尝试用逗号分隔值来解决这个问题。

var global.items = {};
{{#items}}
    global.items.{{item_name}} = {{item_value}};
{{/items}}
于 2012-05-21T17:16:55.863 回答
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);
于 2016-09-17T22:12:08.937 回答
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());
于 2017-10-07T01:01:05.153 回答
0

我知道这是一个老问题,但我仍然想添加一个提供另一种方法的答案。

主要答案

Mustache 支持 lambda,(请参阅文档)因此可以这样编写:

模板:

    {{#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);
        }
      }
    }

输出:

红、绿、蓝

评论

就个人而言,我喜欢这种方法而不是其他方法,因为恕我直言,模型应该只指定渲染的内容而不是渲染的方式。从技术上讲,lambda 是模型的一部分,但意图要清楚得多。

我使用这种方法来编写我自己的 OpenApi 生成器。在那里,Mustache 由 Java 包装,但功能几乎相同。这就是我创建 lambdas 的样子:(在 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())
            })
于 2020-04-21T18:23:41.967 回答
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/

于 2020-11-06T03:14:53.860 回答
0

@LostMekkaSoft 解决方案很有意义,但 lambda 是简单任务(辅助函数)的问题:

也许一个解决方案是一个复杂的辅助函数,它通过额外的输入配置在输入中“注入”这种标准 lambdas ......另一个解决方案是选择一组“标准函数”并将其构建为标准 Mustache Helper - 每种语言的函数库

Mustache 需要一个标准的辅助库

假设有一个ms_*()Mustache 辅助函数库。定义示例ms_items_setLast()。要实施,这里(2021 年)投票最多的解决方案是@Clyde 的解决方案;因此,将其推广到任何语言:

对于 Javascript:

function ms_items_setLast(x,label='') {
   if (label=='' || label === undefined)
     x[ x.length - 1 ].last = true
   else
     x[label][ x[label].length - 1 ].last = true
   return x
}
// model = ms_items_setLast(model,'items');

对于 Python:

def ms_items_setLast(x,label=''):
   if label=='':
      x[ len(x) - 1 ]['last'] = True
   else:
      x[label][ len(x[label]) - 1 ]['last'] = True

在终端使用它:

model = {
"items": [
    {"name": "red"},
    {"name": "green"},
    {"name": "blue"}
  ]
}
items= [
    {"name": "red"},
    {"name": "green"},
    {"name": "blue"}
]
ms_items_setLast(model,'items')
ms_items_setLast(items)
model
items

结果是

>>> model
{'items': [{'name': 'red'}, {'name': 'green'}, {'name': 'blue', 'last': True}]}
>>> items
[{'name': 'red'}, {'name': 'green'}, {'name': 'blue', 'last': True}]
于 2021-05-24T15:33:12.033 回答