在 Mustache 模板中,有一种优雅的方式来表达逗号分隔的列表,而不使用尾随逗号

  • 本文关键字:分隔 列表 Mustache 一种 方式 mustache
  • 更新时间 :
  • 英文 :


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

红色、绿色、蓝色

考虑到结构,使用尾随逗号创建列表很简单

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

和模板

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

这将解析为

红色、绿色、蓝色、

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

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

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

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

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

以呈现该逗号。

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

因此,您的数据将如下所示:

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

和您的模板

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

我知道它并不优雅,但正如其他人提到的,胡须非常轻巧,不提供这样的功能。

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

我猜有人会说这是在演示文稿中放置标记的糟糕情况,但我认为不是。逗号分隔值是一种表示决策,用于更轻松地解释基础数据。这类似于在条目上交替字体颜色。

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

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

派对有点晚,但也许对现在看的人有帮助:5.1.1 可以这样做:

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

输出:

var1, var2, var3

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

这是车把,请注意。 如果test是数组,@index将起作用。

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

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

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

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

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

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 +和其他现代浏览器。

对于 JSON 数据,我建议:

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

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

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

因为问题是:

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

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

任何具有数组索引的胡须模板语言都可以正确地做到这一点。 无需向数据添加任何内容。这包括车把、主动式.js和其他流行的胡须实现。

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

在更复杂的方案中,出于多种原因,视图模型是可取的。它以更适合显示或在本例中适合模板处理的方式表示模型的数据。

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

型:

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

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

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

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

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

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

我倾向于认为这是一个非常适合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);

如果您使用的是 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());
我知道

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

主要答案

Mustache支持lambdas(参见文档(,所以可以这样写:

模板:

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

我在使用动态 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/

@LostMekkaSoft解决方案是有意义的,但 lambda 对于简单任务(辅助函数(来说是一个问题:

  • 是一个依赖于语言的解决方案;
  • 污染输入:参见维基百科上的模板系统概念。

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

胡子需要一个标准的助手库

假设有一个包含 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}]

最新更新