Пошаговое руководство по созданию todo mvc в Htmlix


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 .




К сожалению, не доступен сервер mySQL