在Mustache模板中是否有一种表达逗号分隔列表而没有尾随逗号的优雅方式?

时间:2011-05-24 17:30:56

标签: mustache

我正在使用Mustache templating library并尝试生成逗号分隔列表而不使用尾随逗号,例如。

  

红色,绿色,蓝色

给定结构

,使用尾随逗号创建列表很简单
{
  "items": [
    {"name": "red"},
    {"name": "green"},
    {"name": "blue"}
  ]
}

和模板

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

这将解析为

  

红色,绿色,蓝色,

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

19 个答案:

答案 0 :(得分:87)

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

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

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

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

渲染该逗号。

答案 1 :(得分:38)

嗯,可疑,mustache demo几乎向你展示了first属性,你必须在JSON数据中包含逻辑以确定何时放入逗号。

所以你的数据看起来像是:

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

和您的模板

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

我知道它不优雅,但是as mentioned by others Mustache非常轻巧,并没有提供这样的功能。

答案 2 :(得分:37)

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

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

答案 3 :(得分:22)

如果您正在使用jmustache,则可以使用特殊的-first-last变量:

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

答案 4 :(得分: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 :(得分:5)

我想不出很多情况,你想在<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.

这是Handlebars,请注意。如果@index是数组,则test将有效。

答案 6 :(得分:5)

在Mustache中没有内置方法可以做到这一点。您必须更改模型才能支持它。

在模板中实现此功能的一种方法是使用反向选择帽{{^last}} {{/last}}标记。它只会省略列表中 last 项目的文字。

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

或者你可以将一个分隔符字符串", "添加到该对象,或者理想情况下是基类,如果你正在使用一种具有继承的语言,那么设置&#34; delimiter&#34;到最后一个元素的空字符串" ",如下所示:

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

答案 7 :(得分:2)

问题是:

  

是否有一种优雅的方式来表达逗号分隔的列表而不使用尾随逗号?

然后更改数据 - 当最后一项已被隐含,因为它是数组中的最后一项 - 不是很优雅。

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

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" />
<div class="navbar-wrapper">
  <div class="container">
    <nav class="navbar navbar-inverse navbar-static-top">

      <div class="navbar-header">
        <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false">
          <span class="sr-only">Toggle navigation</span>
          <span class="icon-bar"></span>
          <span class="icon-bar"></span>
          <span class="icon-bar"></span>
        </button>
      </div>
      <div id="navbar" class="collapse navbar-collapse">
        <ul class="nav navbar-nav">
          <li class="active"><a href="#">HOME</a>
          </li>
          <li class="dropdown">
            <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">ABOUT<span class="caret"></span></a>
            <ul class="dropdown-menu">
              <li><a href="#">LOCATION</a>
              </li>
              <li><a href="#">PRESS</a>
              </li>
            </ul>
          </li>
          <li><a href="#contact">STRINGS 1</a>
          </li>
          <li><a href="#contact">STRINGS 2</a>
          </li>
          <li><a href="#contact">HELL RAMEN</a>
          </li>
          <li><a href="#contact">JOBS</a>
          </li>
          <li><a href="#contact">DARK CHEFS</a>
          </li>
        </ul>
      </div>

    </nav>
  </div>
</div>

答案 8 :(得分:1)

对于JSON数据,我建议:

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

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

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

答案 9 :(得分:1)

由于Mustache站在Handlebars上,为什么不使用@data变量?我发现这是最干净的选择:

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

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

答案 10 :(得分: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 }
    ]
}

第二个列表表示形式很糟糕,但是用代码创建却非常简单。将模型映射到视图模型时,只需用此表示替换您需要firstlast的每个列表即可。

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

答案 11 :(得分:1)

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

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

输出:

<块引用>

var1、var2、var3

答案 12 :(得分:0)

有趣。我知道它有点懒惰,但我通常通过模板赋值而不是试图用逗号分隔值来解决这个问题。

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

答案 13 :(得分: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);

答案 14 :(得分: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());

答案 15 :(得分:0)

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

  1. 留胡子。
  2. 删除字符串前后的空白。
  3. 然后删除最后一个字符

    let renderingData =小胡子渲染(dataToRender,data); renderingData =(renderedData.trim())。substring(0,renderingData.length-1)

答案 16 :(得分:0)

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

主要答案

小胡子支持lambda(there was no option on the UI to change back to Python 2),因此可以这样写:

模板:

    {{#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包装,但是功能几乎相同。这就是创建lambda的样子:(在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())
            })

答案 17 :(得分: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/

答案 18 :(得分:0)

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

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

Mustache 需要一个标准的辅助库

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

对于 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}]