https://habr.com/ru/post/456708/- JavaScript
- Программирование
Htmlix — это микро фреймворк для построения фронтенд на javascript.
Полное приложение todo mvc можно
посмотреть по ссылке в файлах todo.html, /js/todo-mvc.js
Для подключения файлов в проект нужно скопровать папку htmlix по указанной выше ссылке,
пока что данный фремворк находится в тестовой версии, однако уже сейчас позволяет решать типовые задачи фронтенд разработки.
htmlix — микро фреймфорк на основе data- свойств. Из преймуществ это маленький размер, возможность четко структурировать код, наличие пользовательских событий для обновления DOM а также серверный рендеринг на всех языках по умолчанию, т. к. встраивается в существующюю структуру Html файла.
Создадим контейнер todo
В Html:
<div data-todo="container">
</div>
После data- идет название контейнера, в кавычках мы пишем что тип элемента — контейнер.
В javascript:
Для начала создадим объект описания приложения и в нем создадим наш контейнер:
var State = {
todo: { /*название нашего элемента (если бы наш контейнер был в массиве то здесь было бы имя массива)*/
container: "todo", /* название нашего контейнера */
props: [],
methods: {
}
}
}
var HM = new HTMLixState(State); /* теперь создаем экземпляр приложения передав ему в качестве параметра наш объект*/
props — пока что у нас просто контейнер не содержащий свойств, объявлять переменную props обязательно
methods — здесь будут методы для событий которые будут слушать переменные, объявлять обязательно;
теперь у нас есть контейнер который ничего не делает, дале добавим в него свойства.
Добавляем свойства в контейнер тодо:
В Html:
Добавим три свойства paragraf, completed, clickoncheckbox, c права после знака равно пишутся типы свойств, у нас это «text, „checkbox“, „click“, важно написать правильный тип свойства.
Если тип является событием то в js коде необходимо будет создать одноименный метод, в нашем случае clickoncheckbox который будет вызываться при клике на чекбокс.
<div data-todo="container">
<p data-todo-paragraf="text">Старый текст</p>
<input data-todo-completed="checkbox" data-todo-clickoncheckbox="click" />
</div>
В javascript:
var State = {
todo: {
container: "todo",
props: ['paragraf', 'completed', 'clickoncheckbox'], /*причисляем все свойства */
methods: {
clickoncheckbox: function(){ /* создаем одноименный метод */
console.log(this);
this.parentContainer.props.paragraf.setProp("Новый текст");
}
}
}
}
Итак мы перечисляем все свойства нашего контейнера и для свойства 'clickoncheckbox' с типом равным событию „click“ создаем одноименный метод clickoncheckbox в объекте methods.
Теперь при клике на чекбокс вызовется наш метод, в котором this указывает на наш объект-свойство 'clickoncheckbox' в данном обьекте есть доступ к контейнеру todo с помощью свойства .parentContainer а из контейнера ко всем свойствам props обьявленным внутри него.
В данном случае мы получаем доступ ко второму свойству 'paragraf' и меняем cтарый текст на „Новый текст“ с помощью метода .setProp(»Новый текст"), setProp() работает по разному для различных типов свойств, если бы мы вызвали его для 'completed' он бы поменял value на true .setProp(true).
Добавляем todo в массив todos
В коде выше мы создали один контейнер, но что если у нас будет много однотипных контейнеров, которые в процессе работы потребуется обновлять, удалять и добавлять новые. Для всего этого необходимо создать массив array и поместить наш контейнер и другие в него.
В html коде это будет выглядеть так:
<div data-todos="array">
<div data-todo="container">
<p data-todo-paragraf="text">Старый текст</p>
<input data-todo-completed="checkbox" data-todo-clickoncheckbox="click" />
</div>
<div data-todo="container">
<p data-todo-paragraf="text">Старый текст</p>
<input data-todo-completed="checkbox" data-todo-clickoncheckbox="click" />
</div>
</div>
Теперь у нас два одинаковых контейнера внутри массива todos тип для массивов в html коде «array».
Для доступа к массиву в js также потребуется изменить наше описание обьекта.
В javascript:
var State = {
todos: { /*теперь у нас элементом является массив поэтому меняем название элемента с todo на todos
container: "todo", /* название контейнера остается тем- же*/
props: ['paragraf', 'completed', 'clickoncheckbox'],
methods: {
clickoncheckbox: function(){
console.log(this);
this.parentContainer.props.paragraf.setProp("Новый текст");
}
}
}
}
Теперь у нас есть два контейнера и если мы захотим из свойства одного попасть в свойство другого контейнера, это можно сделать следующим образом: this.rootLink.state.todos.data[«id контейнера»].props.paragraf, где rootLink- ссылка на корневой обьект htmlix, state- все обьявленные в js коде элементы, в нашем случае это todos — массив с элементами (todo) — для доступа к конкретному контейнеру todo нужно знать его id или индекс в массиве .data[«id контейнера»], а далее в обьекте props к конкретному свойству, в нашем случае paragraf.
Создаем новый элемент input text
Если мы захотим сделать например input для текста и добавлять новые тодо при нажатии на клавишу enter. Для этого нужно создать новый элемент контейнер с нашим 'инпутом' и добавить обработчик события нажатия клавиши.
В HTml:
<div data-todoinput="container">
<input data-todoinput-input="inputvalue" data-todoinput-clickkeydown="keydown" type="text"/>
</div>
<div data-todos="array">
<div data-todo="container">
<p data-todo-paragraf="text">Старый текст</p>
<input data-todo-completed="checkbox" data-todo-clickoncheckbox="click" />
</div>
<div data-todo="container">
<p data-todo-paragraf="text">Старый текст</p>
<input data-todo-completed="checkbox" data-todo-clickoncheckbox="click" />
</div>
</div>
В коде выше мы добавили новый элемент todoinput c типом «container» и в нем определили два свойства input и clickkeydown, с типами свойств: «inputvalue», «keydown». Второй тип свойства у нас является событием поэтому в js коде потребуется создать одноименный метод clickkeydown.
Javascript:
var State = {
todos: {
container: "todo",
props: ['paragraf', 'completed', 'clickoncheckbox'],
methods: {
clickoncheckbox: function(){
console.log(this);
this.parentContainer.props.paragraf.setProp("Новый текст");
}
}
},
todoinput: { /* название нового элемента*/
container: "todoinput", /*название контейнера в html после data-*/
props: [ 'input', 'clickkeydown'],
methods: {
clickkeydown: function(){ /* однойменный метод для свойства с типом - событие*/
if(event.keyCode !== 13)return; /*определяем что нажатая клавиша = enter*/
this.rootLink.createContainerInArr("todos", /*создаем новый контейнер в массиве todos*/
{
paragraf: this.parentContainer.props['input'].getProp(),
/*устанавливаем свойство нового контейнера paragraf данными которе мы берем из свойства 'input' находящегося в том же контейнере что и clickkeydown*/
});
}
}
},
}
в методе clickkeydown выше мы создали новый контейнер с помощью метода приложения .createContainerInArr(nameElement, objectProps) в котором первый параметр это название массива в котором будет создан контейнер, а второй это свойства которые нужно будет установить при его создании, в данном случае мы заменили paragraf нашим значением которое получили с помощью метода getProp();
Теперь можно добавить кнопку удаления для элементов todo
Добавление кнопки удаления к элементам todo
В Html:
<!--код выше остался без изменения -->
<div data-todos="array">
<div data-todo="container">
<p data-todo-paragraf="text">Старый текст</p>
<input data-todo-completed="checkbox" data-todo-clickoncheckbox="click" />
<button data-todo-removebtn="click" type="button" class="btn btn-danger btn-sm">remove</button>
</div>
<div data-todo="container">
<p data-todo-paragraf="text">Старый текст</p>
<input data-todo-completed="checkbox" data-todo-clickoncheckbox="click" />
<button data-todo-removebtn="click" type="button" class="btn btn-danger btn-sm">remove</button>
</div>
</div>
В коде выше мы добавили две кнопки удаления для элементов todo со свойством removebtn и типом данных «click».
В javascript:
todos: {
container: "todo",
props: ['paragraf', 'completed', 'clickoncheckbox', 'removebtn' ], /*добавили новое свойство*/
methods: {
clickoncheckbox: function(){
console.log(this);
this.parentContainer.props.paragraf.setProp("Новый текст");
},
removebtn: function(){ /* добавили новый метод для свойства 'removebtn' */
this.parentContainer.remove();
}
}
},
Удалить контейнер также можно из приложения вызвав метод this.rootLink.removeByIndexes(nameElement, idexArray), где nameElement — имя элемента массива в нашем случае todos
idexArray — массив индексов элементов (получить индекс контейнера можно this.parentContainer.id)
обратите внимание после удаления и добавления элементов id смежных элементов могут поменяться, т.к. они содержат их индекс в массиве.
Что если мы заходим создать новый элемент который будет отображать текущее количество todo в массиве todos. Для этого нам нужно создать элемент который будет слушать пользовательское событие в нашем случае назовем его emiter-counts-dodos и обновлять DOM при наступлении данного события(создании и удалении todo).
Создание пользовательского события **emiter**
В html коде:
Добавляем новый элемент todolistner и создаем два свойства listenerinfo и info, первое это слушетель пользовательского события с типом «emiter-counts-dodos», второе это info с типом «text» для отображения текстовых данных
/*код выше остался без изменения */
<div data-todolistner="container">
<p data-todolistner-listenerinfo="emiter-counts-dodos" >
<span data-todolistner-info="text">0</span> - элементов
</p>
В javascript файле:
/*создаем новый элемент*/
var State = {
todolistner: { /*добавили новый элемент*/
container: "todolistner", /*название контейнера элемента */
props: ['listenerinfo', 'info',], /*два свойства*/
methods: {
listenerinfo: function(){
var countsTodo = this.emiter.prop; /*берем новые данные из эмитера событий "emiter-counts-dodos" */
this.parentContainer.props.info.setProp(countsTodo); /*обновляем данные в свойстве 'info'*/
},
}
},
eventEmiters: {
["emiter-counts-dodos"] : { /* добавляем наше событие в список объекта eventEmiters с начальным значением переменной = 0 */
prop: 0,
}
}
Далее вызываем событие [«emiter-counts-dodos»] при добавлении и удалении элементов.
removebtn: function(){
this.parentContainer.remove();
this.rootLink.eventProps["emiter-counts-dodos"].setEventProp( this.rootLink.state.todos.lenght);
/*вызвали событие ["emiter-counts-dodos"] в элементе todos методе removebtn, обновив данные prop вызовом функции .setEventProp( новые данные) */
}
/*-------------------------------------------------------------------*/
clickkeydown: function(){
if(event.keyCode !== 13)return;
this.rootLink.createContainerInArr("todos",
{
paragraf: this.parentContainer.props['input'].getProp(),
});
this.rootLink.eventProps["emiter-counts-dodos"].setEventProp( this.rootLink.state.todos.lenght);
}
/*вызвали событие ["emiter-counts-dodos"] в элементе todoinput методе clickkeydown, обновив данные prop вызовом функции .setEventProp( новые данные) */
this.rootLink.eventProps — список всех обьявленных пользовательских событий в обьекте eventEmiters;
[«emiter-counts-dodos»].setEventProp(данные) — обновляет данные prop в обьекте «emiter-counts-dodos» и вызывает методы для всех подписчиков, в нашем случае будет вызван метод listenerinfo в элементе todolistner;
Теперь при добавлении и удалении todo свойство info будет отображать измененные данные в html
Что если при загрузки страницы у нас не будет начальных todo элементов и они будут появляться только после добавления их пользователем.
Создание шаблона template в место контейнера
Для создания шаблона нужно просто заменить слово тип «container» на «template» в первом контейнере, а остальные элементы удалить, также необходимо добавить стиль style=«display: none;» чтобы при загрузке html шаблон не было видно. После инициализации приложения он будет клонирован и добавлен в свойство templateData нашего массива «array» и удален из html файла автоматически.
Html код контейнера todos теперь будет выглядеть так:
<div data-todos="array">
<!--- атрибут style="display: none" обязателен для шаблона, удаляется для остальных елементов("container") автоматически-->
<div data-todo="template style="display: none"">
<p data-todo-paragraf="text">Старый текст</p>
<input data-todo-completed="checkbox" data-todo-clickoncheckbox="click" />
<button data-todo-removebtn="click" type="button" class="btn btn-danger btn-sm">remove</button>
</div>
</div>
В javascript коде изменений не потребуется
Теперь при создании приложения todo будут появляться только после ввода тескта и нажатия клавиши enter.
Пример из пошагового руководства можно скачать:
здесь
Полное приложение todo mvc на Htmlix можно
посмотреть по ссылке в файлах todo.html, /js/todo-mvc.js
Из предыдущих статей:
Создание фильтра и карточки товаров в Htmlix .