Хелперы (Handlebars)

Вы можете делать шаблоны и проверять их работу в Песочнице Handlebars.

Основная информация о хелперах

При помощи переменных можно делать много чего еще. Например, выбирать их в зависимости от определенных условий, перебирать, складывать, умножать делить и вычитать.
Чтобы сделать это возможным, в Ботмаме есть разные функции-помощники, которые мы дальше будем называть хелперами.
Ботмама использует «движок» шаблонов Handlebars. Это значит, что почти все, что можно сделать с его помощью, можно сделать и в Ботмаме. Небольшие отличия есть, все же Handlebars был создан в первую очередь для веб-страниц, но они почти незаметны.
Шаблоны в этом смысле — это любой текст, в котором есть переменные или хелперы.
Xелперы — это маленькие помощники, которые могут что-то сделать с нашими переменными. Они бывают двух видов: блочные и строчные.
Блочные хелперы выглядят так:
{{#helper переменная1 переменная2 переменнаяN}}
Какой-то текст или переменные
{{/helper}}
То есть начинаются они со строки #имя-хелпера — начало блока, а заканчиваются строкой /имя-хелпера — конец блока.
Такая конструкция формирует смысловой блок. У хелпера есть начало, конец, какое-то тело с текстом и переменными, поэтому он и называется блочным.
Обычно блочные хелперы используют чтобы показать какой-то текст в зависимости от каких-то условий. Не переживайте, только в теории не понятно, на практике вы сразу с ними разберетесь.
Строчные хелперы сильно проще
{{helper переменная1 переменная2 переменнаяN}}
Они похожи на переменные. Если вы знакомы с любым языком программирования, и знаете что такое функции, то заметите, что строчные хелперы очень на них похожи.
У них есть имя хелпера и могут быть какими-то переменными, с которыми они что-то могут сделать.

Условия

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

Сравнение переменной со значением или другой переменной

Давайте представим, что у нас есть переменная gender, в которую мы через развилку запишем мужчина или женщина в зависимости от того, что клиент написал боту.
И теперь хотим в зависимости от пола отправить клиентам разный текст.
Мы можем использовать блочный хелпер сравнения переменных eq:
Привет!
{{#eq gender "женщина"}}
Хорошо, что ты написала!
{{/eq}}
{{#eq gender "мужчина"}}
Хорошо, что ты написал!
{{/eq}}
Что тут происходит?
Мы открыли блочный хелпер #eq и использовали с ним переменную gender и строку женщина.
В примере видно, что вместо переменных мы можем сразу использовать так называемые литералы. Т. е. мы можем сравнивать не только переменные, но и сразу строки и числа.
Строки нужно записывать в двойных программных кавычках ".
И после тела блока мы закрыли его /eq. Блочные хелперы всегда нужно закрывать, иначе в шаблоне случится ошибка, и бот ничего не пришлет.
У многих блочных хелперов, которые используются для сравнений, есть так называемый альтернативный блок.
Давайте еще раз посмотрим на наш пример, у нас может быть только мужчина и женщина — всего два варианта.
Мы можем не делать для каждого из вариантов свой блок, а использовать специальный разделитель {{else}}:
Привет!
{{#eq gender "женщина"}}
Хорошо, что ты написала!
{{else}}
Хорошо, что ты написал!
{{/eq}}
Т. е. если в переменной gender записана строка женщина, то бот напишет: Привет! Хорошо, что ты написала!, а во всех остальных случаях: Привет! Хорошо, что ты написал!

Сравнение «меньше» или «больше»

Чтобы сравнить два числа на меньше или больше есть два блочных хелпера #lt и #gt соответственно.
ltless than — меньше чем
gtgreater than — больше чем
Представьте, что в боте мы спросили клиента, сколько ему лет, и добавили еще одну переменную, в которую записали возраст — age.
Допустим, мы хотим предупредить, что пользоваться ботом можно только если клиент старше 16 лет. Если клиент младше, то попросим его не пользоваться ботом.
{{#lt age 16}}
К сожалению, ботом нельзя пользоваться до 16 лет
{{/lt}}
Аналогично можно сделать и с хелпером #gt и разделителем {{else}}
{{#gt age 15}}
{{else}}
К сожалению, ботом нельзя пользоваться до 16 лет
{{/lt}}
Вышло немного нелогично. Потому что нам нужны те, кому 16 лет и старше, а получились те, кому строго больше 15.
Чтобы в подобных случаях сравнивать включительно числу, есть дополнительные хелперы #lte (меньше или равно) и #gte (больше или равно).
Перепишем последний пример на #gte и добавим приветствие для подходящих клиентов:
{{#gte age 16}}
Ого! Добро пожаловать, вам так много лет!
{{else}}
К сожалению, вам еще нет 16 лет
{{/gte}}
Кстати, блочные хелперы можно друг в друга вкладывать. Давайте объединим два примера:
{{#gte age 16}}
Ого! Добро пожаловать!
{{#eq gender "женщина"}}Вы такая взрослая!{{else}}Вы такой взрослый!{{/eq}}
{{else}}
К сожалению, вам еще нет 16 лет
{{/gte}}

Все хелперы сравнения

#eq — равны ли переменные?
{{#eq переменная1 переменная2}}
Если переменные равны
{{else}}
Необязательный текст, если переменные не равны
{{/eq}}
#lt — первая переменная меньше второй?
{{#lt переменная1 переменная2}}
Если первая переменная меньше
{{else}}
Необязательный текст, если вторая переменная меньше
{{/eq}}
#gt — первая переменная больше второй?
{{#lt переменная1 переменная2}}
Если первая переменная больше
{{else}}
Необязательный текст, если вторая переменная больше
{{/eq}}
#if — переменная существует и в ней «правдивое значение»?
{{#if переменная}}
Если переменная существует и в ней НЕ пустая строка "", ноль 0, логическая ложь false, ссылка на «ничего» null
{{else}}
Необязательные текст, для обратного условия
{{/if}}
#lte — первая переменная меньше или равна второй?
{{#lt age 16}}
К сожалению, ботом нельзя пользоваться до 16 лет
{{/lt}}
#gte — первая переменная больше или равна второй?
{{#gte age 16}}
Ого! Добро пожаловать, вам так много лет!
{{/gte}}
Ботмама поддерживает и другие хелперы сравнения (comparison), но не гарантирует их работу в будущих обновлениях.

Математика

В Ботмаме есть хелперы для всех основных математических операций. Все хеплеры математики — линейные.

Сложение

Чтобы сложить два числа, нам понадобится хелпер add:
{{add переменная1 переменная2}}
Как и в блочных хелперах, в линейных можно указывать не только переменные, но и литеральные значения:
{{add 5 100}}
В сообщении отправится 105.

Вычитание

Чтобы вычесть два числа, мы можем использовать хелпер subtract:
{{subtract переменная1 переменная2}}

Умножение

Чтобы умножить два числа, мы можем использовать хелпер multiply:
{{multiply переменная1 переменная2}}

Деление

Чтобы разделить два числа, мы можем использовать хелпер divide:
{{divide переменная1 переменная2}}

Как использовать один хелпер внутри другого

Линейные хелперы можно вкладывать друг в друга и в блочные хелперы. Чтобы использовать один хелпер внутри другого, нужно записать его в круглых скобках (helper) вместо фигурных {{helper}}.
Например, это может пригодится в математических вычислениях, когда нам нужно одни вычисления передать в другие. Например выражение 10 + 5 − 15 можно записать так:
{{add 10 (subtract 5 15)}}
Получится ноль.
Вкладывать можно насколько угодно глубоко:
{{add 10 (subtract 5 (multiply (divide 10 2) 3))}}
И тут тоже получится ноль. Математически можно записать так: 10 + (5 − ((10 ᛬ 2) × 3))
В блочных хелперах мы, например, можем вложить вычисление баланса клиента в хелпер #gte
{{#gte (subtract balance cost) 0}}
Отлично, у вас достаточно денег для покупки этого курса
{{else}}
Кажется, на вашем счете не достаточно средств
{{/gte}}

Комментарии

Есть специальный хелпер для комментариев. Все, что внутри, не будет отправлено клиенту и не попадет в итоговый текст.
Нужно просто обернуть текст в {{!-- и --}}:
{{!-- это сообщение никуда не попадет --}}
А этот текст будет показан
Комментарии полезны, если у вас большие тексты с хелперами, и вы не хотите запутаться:
{{!-- если баланс минус стоимость курса больше нуля --}}
{{#gte (subtract balance cost) 0}}
{{!-- то расскажем об этом --}}
Отлично, у вас достаточно денег для покупки этого курса
{{else}}
{{!-- а если нет, то попросим пополнить баланс --}}
Кажется, на вашем счете не хватает денег для покупки курса
Пополните баланс по ссылке https://example.com/pay
{{/gte}}

Как работать с массивами и списками

Чтобы перебрать и вывести каждый элемент массива, можно использовать хелпер #each.
Представьте, что вы сделали запрос в свою систему учета клиентов, получили из нее список имен сотрудников и записали его в переменную names. Мы хотим сделать так, чтобы одного из сотрудников клиент мог выбрать.
Внутри переменной names этот список может выглядеть как-то так:
["Александрова Александра", "Иванов Иван", "Константинопольский Константин", "Ярослава Ярославна"]
Каждый элемент массива внутри #each доступен как переменная this:
Пришлите мне имя сотрудника, к которому вы хотите записаться:

{{#each names}}
{{this}}
{{/each}}
Клиенту придет сообщение:
Пришлите мне имя сотрудника, к которому вы хотите записаться:

Александрова Александра
Иванов Иван
Константинопольский Константин
Ярослава Ярославна
Внутри #each также доступна специальная переменная @index — это порядковый номер элемента. Есть одна особенность — #each нумерует массивы от нуля:
Пришлите мне имя сотрудника, к которому вы хотите записаться:

{{#each names}}
{{@index}}. {{this}}
{{/each}}
Получится:
Пришлите мне имя сотрудника, к которому вы хотите записаться:

0. Александрова Александра
1. Иванов Иван
2. Константинопольский Константин
3. Ярослава Ярославна
Мы можем использовать внутри тела #each любые линейные хелперы и легко исправить нумерацию при помощи сложения с единицей:
Пришлите мне имя сотрудника, к которому вы хотите записаться:

{{#each names}}
{{add @index 1}}. {{this}}
{{/each}}
Теперь получится текст таким, каким мы его и задумывали:
Пришлите мне имя сотрудника, к которому вы хотите записаться:

1. Александрова Александра
2. Иванов Иван
3. Константинопольский Константин
4. Ярослава Ярославна

Объекты в массивах и списках

Внутри массивов могут быть не только простые данные вроде строк, как в прошлом примере. Там могут быть и вложенные объекты.
Допустим, мы попросили у вашей системы учета клиентов список сотрудников в виде объектов и записали его в переменную staff. Наш список в переменных может выглядеть как-то так:
[
  {
    "id": 12345,
    "name": "Александрова Александра",
    "rating": 5
  },
  {
    "id": 12346,
    "name": "Иванов Иван",
    "rating": 5
  },
  {
    "id": 12347,
    "name": "Константинопольский Константин",
    "rating": 3
  },
  {
    "id": 12348,
    "name": "Ярослава Ярославна",
    "rating": 4
  }
]
С такими списками мы можем работать точно также, как с простыми:
Пришлите мне имя сотрудника, к которому вы хотите записаться:

{{#each staff}}
{{!-- this теперь — это не строка с именем, а имя текущего объекта --}}
{{!-- мы можем обратиться к внутренней переменной `name` через this.name --}}
{{add @index 1}}. {{this.name}}
{{/each}}
Как видите, все также просто, как и со строками.
Когда мы работаем с объектами внутри списков, мы можем опустить this и писать сразу имя внутренней переменной. Давайте дополнительно отправим клиенту рейтинг:
Пришлите мне имя сотрудника, к которому вы хотите записаться:

{{#each staff}}
{{!-- можно опустить this --}}
{{add @index 1}}. {{name}} — рейтинг: {{rating}}
{{/each}}
Получится такое сообщение:
Пришлите мне имя сотрудника, к которому вы хотите записаться:

1. Александрова Александра — рейтинг: 5
2. Иванов Иван — рейтинг: 5
3. Константинопольский Константин — рейтинг: 3
4. Ярослава Ярославна — рейтинг: 4
Если вдруг потребуется получить что-то из переменной вне массива, внутри #each мы можем вернуться наверх и прочитать нужную переменную с помощью ../.
Представьте, что мы спросили у клиента, какой рейтинг сотрудника его больше всего интересует, и хотим отправить все имена, где рейтинг совпадает или даже лучше. Снова представьте, что рейтинг который он прислал мы записали в переменную min:
Пришлите мне имя сотрудника, к которому вы хотите записаться:

{{#each staff}}
{{!-- Комментарий: `../` — мы вышли из массива --}}
{{!-- Комментарий: `../min` — вышли из массива и получили `min` --}}
{{#gte rating ../min}}
{{!-- Комментарий: если `../min` равен или больше `rating` --}}
{{add @index 1}}. Вам может подойти {{name}}. Рейтинг: {{rating}}
{{else}}
{{!-- Комментарий: если `../min` меньше `rating` --}}
{{add @index 1}}. Кажется {{name}} вам не подходит. Рейтинг всего: {{rating}}
{{/gte}}
{{/each}}
Если min = 4, то получится такое сообщение:
Пришлите мне имя сотрудника, к которому вы хотите записаться:

1. Вам может подойти Александрова Александра. Рейтинг: 5
2. Вам может подойти Иванов Иван. Рейтинг: 5
3. Кажется Константинопольский Константин вам не подходит. Рейтинг всего: 3
4. Вам может подойти Ярослава Ярославна. Рейтинг: 4