Есть ли в шаблонах Mustache элегантный способ выражения списка, разделенного запятыми, без конечной запятой?


84

Я использую библиотеку шаблонов Mustache и пытаюсь создать список, разделенный запятыми, без конечной запятой, например

красный, зеленый, синий

Создать список с запятой в конце несложно, учитывая структуру

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

и шаблон

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

это разрешит

красный, зеленый, синий,

Однако я не вижу элегантного способа выразить этот случай без конечной запятой. Я всегда могу сгенерировать список в коде, прежде чем передать его в шаблон, но мне было интересно, предлагает ли библиотека альтернативный подход, например, позволяющий определять, является ли это последним элементом в списке в шаблоне.


Я предлагаю создать в коде список, разделенный запятыми, и передать его в mustache как одну строку. Более сложная логика, чем опции и простые списки, почти всегда более читабельна в классических языках программирования.
yeoman

Более сложные механизмы шаблонов, чем mustache, могут сделать это довольно легко. Однако ни в одном из них это не очень хорошо читается, и с учетом этого решение сделать усы такими простыми, как они есть, было вполне сознательным: D
yeoman

Ответы:


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 Несмотря на то, что это элегантно, это может (а) не быть задокументированным поведением и, таким образом, подлежать будущим изменениям, хотя и маловероятно, и (б) при этом не ставятся пробелы после запятых, поэтому вы получите что-то вроде first,second,third.
caw

8
меньше работы, чтобы добавить только одно свойство к последнему элементу, {"name": "blue", "last": 1}а затем использовать перевернутый раздел{{#items}} {{name}}{{^last}}, {{/last}} {{/items}}
TmTron

1
@ slick86 Комментарий над вашим действительно приводит к списку, разделенному запятыми, но не к списку, в котором каждый элемент заключен в двойные кавычки.
GlenRSmith

93

Я думаю, что лучший способ - это динамическое изменение модели. Например, если вы используете JavaScript:

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

и в вашем шаблоне используйте перевернутый раздел:

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

чтобы отобразить эту запятую.


2
Я люблю это. Каждый раз, когда я думаю, что у Mustache недостаточно функций, это потому, что я считаю «старый» способ, когда шаблон делает все - ну, я пришел из JSP.
Николас Зозол 06

1
@NicolasZozol Вы действительно понимаете, что усы были созданы именно для такой простоты? : D
yeoman

@NicolasZozol Для действительно сложных случаев лучше всего строить строки непосредственно на языке программирования и, таким образом, создавать простую модель представления, которую может обрабатывать язык шаблонов. В этом случае список, разделенный запятыми, будет предоставлен через код в виде одной строки.
yeoman

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

1
Это делает предположение, что усы используются для веб-страниц - они также имеют множество применений и за пределами этого
tddmonkey

30

Если вы используете jmustache , вы можете использовать специальные -firstили -lastпеременные:

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

4
Я понимаю, что OP имел в виду библиотеку JavaScript Mustache, но это может помочь другим пользователям jmustache (например, мне), которые найдут эту страницу.
dbort

Большое спасибо за ответ. Я использовал это также для рендеринга шаблона с помощью SpringBoot. Не нужно менять модель. Я действительно искал эту функцию. Мне также интересно, есть ли контроль за равенством (например {{#something=TEXT}})
Жан Вальжан

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.

Обратите внимание, это Handlebars. @indexбудет работать, если testэто массив.


выглядит чертовски элегантно! предполагая, что #if и @index доступны во всех или большинстве реализаций усов ... Имейте в виду, что многие из нас, пользователей усов, не генерируют HTML, даже если это самый распространенный вариант использования.
Spike0xff 08

Это отличное решение, работает как шарм. Внимание всем, кто сталкивается с этим ответом, если вы хотите обернуть результат в тег HTML, сделайте это {{.}}.
NetOperator Wibby

Это был правильный указатель. Похоже, теперь вы также можете использовать условные выражения [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

В Mustache нет встроенного способа сделать это. Вы должны изменить свою модель, чтобы поддержать ее.

Один из способов реализовать это в шаблоне - использовать {{^last}} {{/last}}тег шляпы перевернутого выделения . Будет пропущен только текст для последнего элемента в списке.

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

Или вы можете добавить строку-разделитель ", "к объекту или, в идеале, базовый класс, если вы используете язык с наследованием, а затем установить «разделитель» на пустую строку " "для последнего элемента, например:

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

1
Чтобы было ясно, что-то во входных данных действительно нужно назвать «последним», чтобы это сработало. Верный?
FrustratedWithFormsDesigner

1
Правильно, вам придется изменить вашу модель, добавив логическое свойство с именем last. Затем установите последний элемент в коллекции на last=true.
cosbor11

1
«Модель» в данном случае на самом деле является файлом конфигурации, который пользователи могут редактировать ... Я не думаю, что хочу доверять им, чтобы они правильно поместили «последний» в нужный элемент списка.
FrustratedWithFormsDesigner

какой язык вы используете для вызова механизма шаблонов?
cosbor11

2
В этом случае во время выполнения преобразуйте configпредставление файла в объект python. Я предполагаю, что конфиг в jsonили xml, не так ли? Затем, прежде чем передать его шаблонизатору, получите последний элемент коллекции и примените lastсвойство.
cosbor11

3

Для данных JSON я предлагаю:

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

Регулярное выражение удалит все, что ,осталось после последних свойств.

Это также удалит ,из строковых значений, содержащих ",}" или ",]", поэтому убедитесь, что вы знаете, какие данные будут помещены в ваш JSON.


Это определенно помогло мне, поскольку у меня есть шаблонная схема JSON. Благодаря!
Яхязини

2

Поскольку вопрос:

есть ли элегантный способ выразить список, разделенный запятыми, без запятой?

Затем изменение данных - когда последний элемент уже подразумевается, поскольку он является последним элементом в массиве, - не изящно.

Любой язык шаблонов усов, который имеет индексы массивов, может делать это правильно. т.е. ничего не добавляя к данным . Сюда входят handlebars, ractive.js и другие популярные реализации усов.

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

1
Хорошо. Но усов нетif
mauron85

@ mauron85 Верно. Я (и многие другие) использую усы во множественном числе для различных языков шаблонов, вдохновленных оригинальными усами.
mikemaccana

1

Самый простой способ, который я нашел, - это отобразить список, а затем удалить последний символ.

  1. Сделайте усы.
  2. Удалите все пробелы до и после строки.
  3. Затем удалите последний символ

    let renderedData = Moustache Render (dataToRender, данные); renderedData = (renderedData.trim ()). substring (0, renderedData.length-1)


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

Второе представление списка ужасно печатать, но чрезвычайно просто создать из кода. При сопоставлении вашей модели с моделью представления просто замените каждый нужный список 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

Интересно. Я знаю, что это немного лениво, но я обычно обхожу это путем создания шаблонов в присвоении значений, а не пытаясь разделять значения запятыми.

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

Я знаю, что это старый вопрос, но я все же хотел добавить ответ, который предлагает другой подход.

Главный ответ

Усы поддерживают лямбды ( см. Документацию ), поэтому их можно написать так:

Шаблон:

    {{#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, но функциональность почти такая же. Вот как для меня выглядит создание лямбд: (в Котлине)

    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

Я использую для этого специальные функции, в моем случае при работе с динамическими 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/

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.