在Moustache模板中,有没有一种优雅的方式来表示逗号分隔的列表而没有尾部的逗号?


83

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

红色,绿色,蓝色

给定结构,使用结尾逗号创建列表很简单

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

和模板

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

这将解决

红色,绿色,蓝色

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


我建议在代码中建立逗号分隔的列表,并将其作为单个字符串传递给小胡子。在经典的编程语言中,比可选的选项和简单的列表更复杂的逻辑几乎总是更具可读性。
yeoman '18

比胡子更复杂的模板引擎可以很容易地做到这一点。不过,在所有人中这都不是很容易理解的,并且考虑到这一点,让胡须变得如此简单的决定是经过深思熟虑的:D
yeoman

Answers:


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

@AnthonyChua虽然很优雅,但它可能(a)没有记录在案的行为,因此可能会在将来进行更改,尽管这不太可能,并且(b)不会在逗号后加空格,因此您会得到类似的内容first,second,third
caw

8
只需将一个属性添加到最后一项{"name": "blue", "last": 1},然后使用倒置的部分{{#items}} {{name}}{{^last}}, {{/last}} {{/items}}
工作量较小

1
@ slick86您上面的注释的确会导致逗号分隔的列表,但不是其中每个项目都用双引号引起来的列表。
GlenRSmith '16

92

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

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

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

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

呈现该逗号。


2
我喜欢它。每当我认为Mustache没有足够的功能时,那是因为我认为模板可以完成所有工作的“旧”方式-好吧,我来自JSP。
Nicolas Zozol

1
@NicolasZozol您是否意识到,正是出于这种简单性而创建了胡须?:D
yeoman '18

@NicolasZozol对于非常复杂的情况,最好直接用编程语言构建字符串,从而创建模板语言可以处理的简单视图模型。在这种情况下,逗号分隔的列表将通过代码作为一个字符串提供。
yeoman '18

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指的是JavaScript Mustache库,但这可能有助于其他jmustache用户(如我)找到此页面。
dbort 2015年

非常感谢您的回答。我也用SpringBoot渲染模板。无需更改模型。我真的在寻找这个功能。我也想知道是否有平等控制权(例如{{#something=TEXT}}
JeanValjean

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.

请注意,这是把手。@index如果test是Array则可以使用。


看起来很该死的优雅!假设#if和@index在所有或大多数胡子实现中都可用...请记住,即使是最常见的用例,我们中的许多胡子用户也不会生成HTML。
Spike0xff

这是一个很棒的解决方案,就像一个魅力。如果您想将结果包装在HTML标记中,请赶快遇到这个答案的人{{.}}
NetOperator Wibby '04

这是正确的指针。看来您现在也可以使用[first]和[last]条件来进行更好的控制。stackoverflow.com/questions/11479094/...
马克西姆

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

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

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

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

或者,您可以为", "对象添加定界符字符串,或者如果您使用的是具有继承性的语言,则可以理想地添加基类,然后将“ delimiter”设置" "为最后一个元素的空字符串,如下所示:

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

1
为了清楚起见,输入数据中的某些内容实际上需要命名为“ last”才能起作用。正确?
FrustratedWithFormsDesigner 2015年

1
正确,您必须通过添加一个名为的布尔属性来更改模型last。然后将集合中的最后一项设置为last=true
cosbor11 2015年

1
在这种情况下,“模型”实际上是用户可以编辑的配置文件...我不希望我信任他们将“最后”适当地放在列表中的正确项目上。
FrustratedWithFormsDesigner 2015年

您使用什么语言来调用模板引擎?
cosbor11

2
在这种情况下,在运行时将 config文件表示形式转换为python对象。我猜该配置在json或中xml,对吗?然后,在将其传递给模板引擎之前,获取集合的最后一项并应用last属性。
cosbor11

3

对于JSON数据,我建议:

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

regexp将删除,最后一个属性之后的所有左悬挂。

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


因为我有模板化的JSON模式,所以这绝对对我有用。谢谢!
yahyazini

2

问题是:

有没有一种优雅的方式来表示以逗号分隔的列表而不用逗号结尾?

然后更改数据(当最后一项已被数组中的最后一项隐含时)并不理想。

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

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

1
好。但胡子没有if
mauron85 '19

@ mauron85确实。我(和许多其他人)将胡须用作复数形式,以启发原始胡须启发的各种模板语言。
mikemaccana

1

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

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

    让renderingData =小胡子渲染器(dataToRender,data); renderingData =(renderedData.trim())。substring(0,renderingData.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支持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包装,但是功能几乎相同。这就是创建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())
            })

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

第二个列表表示形式很糟糕,但是用代码创建却非常简单。虽然映射你的模型视图模型,只需更换每次你需要列表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,因为其中之一足以避免尾随逗号。

使用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/

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.