У шаблоні «Вуса» існує елегантний спосіб вираження списку, розділеного комами, без кінцевої коми?


83

Я використовую бібліотеку шаблонів Mustache і намагаюся створити список, відокремлений комами, без кінцевої коми, наприклад

червоний, зелений, синій

Створення списку з кінцевою комою є простим, враховуючи структуру

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

і шаблон

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

це вирішить

червоний, зелений, синій,

Однак я не бачу елегантного способу вираження справи без кінцевої коми. Я завжди можу сформувати список у коді перед тим, як передати його в шаблон, але мені було цікаво, чи пропонує бібліотека альтернативний підхід, наприклад, дозволяє визначити, чи є останнім елементом у списку в шаблоні.


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

Більш складні двигуни шаблонів, ніж вуса, можуть зробити це досить легко. Це не дуже читається в жодному з них, однак, і маючи це на увазі, рішення зробити вуса такими простими, як це було, було цілком обдуманим: D
yeoman

Відповіді:


43

Хмм, сумнівно, демонстрація вусів майже показує вам, з firstвластивістю, що ви повинні мати логіку всередині даних JSON, щоб зрозуміти, коли ставити кому.

Отже, ваші дані виглядатимуть приблизно так:

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

і ваш шаблон

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

Я знаю, що це не елегантно, але, як згадували інші, Вуса дуже легкий і не надають таких функцій.


24
Ви також можете відформатувати свої дані, так що "items": ["red", "green", "blue"], тоді ви можете просто зробити {{items}}, це вже виведе список, відокремлений комами :)
Ентоні Чуа

10
Коментар насправді повинен бути правильною відповіддю. Набагато чистіше. Це дуже погана форма для модифікації вашого джерела даних для задоволення візуальних потреб споживача
Slick86,

@AnthonyChua Незважаючи на те, що це елегантно, (a) поведінка не може бути задокументована і, отже, може бути змінена в майбутньому, хоча й малоймовірно, і (b) це не ставить пробіли після коми, тож ви отримуєте щось подібне first,second,third.
caw

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

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

92

Я думаю, що кращий спосіб - це динамічно змінювати модель. Наприклад, якщо ви використовуєте JavaScript:

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

і у своєму шаблоні використовуйте перевернутий розділ:

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

щоб відобразити цю кому.


2
Я це люблю. Кожного разу, коли я думаю, що у вусів недостатньо функцій, це тому, що я думаю, що "старий" спосіб, де шаблон робить все - ну, я походжу з JSP
Nicolas Zozol

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

Це Handlebars, зауважте. @indexбуде працювати, якщо testце масив.


здається досить проклятим! припускаючи, що #if та @index доступні у всіх або більшості реалізацій вусів ... Майте на увазі, що багато хто з нас, хто користується вусами, не створюють HTML, навіть якщо це найпоширеніший варіант використання.
Spike0xff

Це чудове рішення, працює як шарм. Готовий для всіх, хто стикається з цією відповіддю. Якщо ви хочете обернути результат у тег HTML, зробіть це навколо {{.}}.
NetOperator Wibby

Це був правильний вказівник. Здається, тепер ви також можете використовувати [перший] та [останній] умови для ще кращого контролю. stackoverflow.com/questions/11479094/…
Максим

6

На питання, чи пропонує Вуса елегантний спосіб зробити це, відповіли, але мені спало на думку, що найелегантнішим способом зробити це може бути використання 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}}

Або ви можете додати рядок-роздільник як ", "до об’єкта, або в ідеалі базовий клас, якщо ви використовуєте мову, яка має успадкування, а потім встановіть "роздільник" на порожній рядок " "для останнього елементу, як це:

{{#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. Дякую!
yahyazini

2

Як питання:

чи існує елегантний спосіб вираження списку, розділеного комами, без кінцевої коми?

Тоді зміна даних - коли останній елемент вже є неявним, оскільки він є кінцевим елементом у масиві - не елегантна.

Будь-яка мова шаблону вусів, яка має індекси масивів, може це зробити належним чином. тобто не додаючи нічого до даних . Сюди входять кермо, ractive.js та інші популярні реалізації вусів.

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

1
В порядку. Але вусів немаєif
mauron85,

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

1

Найпростіший спосіб, який я знайшов, - це відобразити список, а потім видалити останній символ.

  1. Надайте вуса.
  2. Видаліть пробіли до і після рядка.
  3. Потім видаліть останній символ

    let renderedData = Вус Рендери (dataToRender, дані); renderedData = (renderedData.trim ()). підрядок (0, renderedData.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

Я знаю, що це старе запитання, але я все-таки хотів додати відповідь, яка передбачає інший підхід.

Головна відповідь

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

Шаблон:

    {{#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/


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}}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.