Работа с пользовательскими событиями в htmlix.js

от автора

В данной статье будут рассмотрены базовые принципы работы с пользовательскими событиями в Htmlix и создано небольшое приложение из четырех компонентов: формы ввода имени пользователя, приветствия, кнопки выхода из приложения, и массива с пользователями приложения.

Уже готовый пример можно покликать здесь

Почитать api htmlix можно здесь

После ввода имени пользователя в форму, приложение отобразит его имя в приветствии, кнопке выхода и создаст новый контейнер в массиве с пользователями, а также сохранит данные в localStorage. После перезагрузки страницы, возьмет имя из localStorage. При клике по кнопке «выйти из профиля» удалит данные со всех компонентов и переменную из localStorage.

Давайте создадим четыре компонента: форму входа — form, приветствие — greeting, кнопку выхода — logout и массив со всеми посетителями — users_array, и посмотрим как бы мы создавали между ними коммуникацию если бы не пользовались пользовательскими событиями.

Создадим html разметку всех компонентов:

<!-- компонент - контейнер форма с двумя свойствами input c типом свойства "inputvalue" и  click с типом "click" -->  <form data-form="container" class="card col-12">    <div class="form-group">        <label for="">Введите имя</label>       <textarea data-form-input="inputvalue" class="form-control" rows="1"></textarea>     </div>     <button data-form-click="click" type="submit">Submit</button> </form>   <!-- компонент - контейнер приветствие и свойство user_name с типом - "text" --> 		 <div data-greeting="container" class="col-6 card">			     <p>Привет: <span  data-greeting-user_name="text">guest</span></p> </div> 		  <!-- компонент - контейнер кнопка выхода и свойство user_name с типом - "text" --> 		 <div data-logout="container" class="col-6 card">		       <a href="#"> Выйти из профиля: ( <span  data-logout-user_name="text"></span>  )       </a>				 </div> 			 <!-- компонент - массив пользователей, изначально с двумя контейнерами, в каждом контейнере свойство user_name - "text"  -->  		 <div class="container-fluid" style="border: 1px solid red; margin-top: 20px;">      <p> все пользователи:</p> 		        <div data-users_array="array" class="row"> 		          <div data-user="container" class="col-4 card">		 	     <p>пользователь -                      <span data-user-user_name="text">user_name_1</span>              </p>						 	</div>           <div data-user="container" class="col-4 card">		 	     <p>пользователь -                      <span data-user-user_name="text">user_name_1</span>              </p>						 	</div> 									      </div>	 </div>		  

Теперь перенесем их в javascript:

var StateMap = { 	 	form: {//форма входа 		container: "form", 		props: ["input", "click"], 		methods: { 			 			click: function(){ 				event.preventDefault(); //отменяем перезагрузку страницы                                   //получаем данные свойства input 				var text = this.parent.props.input.getProp();  				console.log(text);							 			}			 		},		 	},	 	greeting: {//приветствие 		 		container: "greeting", 		props: [ "user_name", ],  		methods: { 	        } 	}, 	logout: { //кнопка выхода 		 		container: "logout", 		props: [ "user_name", ],  		methods: { 		},	 	}, 	users_array: { //массив с пользователями  			 		container: "user", 		props: [ "user_name", ], 		methods: {		   		},        	 	}, } window.onload = function(){//создаем экземпляр приложения htmlix 			 	var HM = new HTMLixState(StateMap); 	console.log(HM);	 }  

В примере выше мы записали данные свойства «input» из компонента формы в переменную «text». Теперь нам нужно отобразить их в свойствах компонентов greeting и logout, а также создать новый контейнер в массиве users_array. Для понимания того зачем нужны пользовательские события давайте сначала попробуем сделать это различными способами без их использования.

1. способ — в самом методе click формы напрямую перейти к каждому компоненту и установить свойство text:

         event.preventDefault();         var text = this.parent.props.input.getProp();                 //установили значения свойствам                 this.rootLink.state["greeting"].props.user_name.setProp(text);                 this.rootLink.state["logout"].props.user_name.setProp(text);                                      ///создали новый контейнер                  this.rootLink.state["users_array"].add({user_name: text});  

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

2. способ это создать метод в объекте stateMethods и вызывать его при изменении данных:

 ststeMethods: {           entryUser: function(text){                  this.state["greeting"].props.user_name.setProp(text);                 this.state["logout"].props.user_name.setProp(text);                  this.state["users_array"].add({user_name: text});        } }  // после загрузки страници window.onload = function(){              var name = window.localStorage.getItem('user_name');               if(name)HM.stateMethods.entryUser.call.(HM, name); ///вызываем метод передав ему контекст= HM }  //в форме click: function(){                   event.preventDefault(); 		  var text = this.parent.props.input.getProp();                   this.rootLink.stateMethods.entryUser.call.(this.rootLink, text);  } 

В данном случае нам удалось избежать дублирования кода, однако проблема в том что методы для работы со свойствами компонента находятся не внутри него а во внешних функциях. Что если перед тем как установить свойство его нужно как-то отформатировать, по разному для каждого компонента, или нам нужно будет установить не одно свойство, а несколько, поэтому лучше когда компоненты сами работают со своими свойствами.

3. способ — перенести всю логику работы со свойствами в компоненты, и создать в них методы, с помощью которых вызывать определенные действия.

	 	greeting: { 		 		container: "greeting",                  //добавили вспомогательный метод для приветствия пользователя 		props: [ "user_name", ["greet_user", "aux"] ],  		methods: {                                              greet_user: function(name){                                 this.props.user_name.setProp(name+" !!!");                      } 	        } 	}, 	logout: { 		 		container: "logout",  		props: [ "user_name", ["set_name", "aux"]], //добавили вспомогательный метод для работы со свойством компонента 		methods: {                       set_name: function(name){                                 this.props.user_name.setProp(name);                      }                            		},	 	}, 	users_array: {  		arrayProps: [["entry_user", "aux"]],  ///добавляем контейнер с новым пользователем из массива                 arrayMethods: {                        entry_user: function(name){                               this.add({user_name: name})                        }                }	 		container: "user", 		props: [ "user_name", ], 		methods: {		   		},        	 	},     stateMethods: {   //изменили общий метод                    entryUser: function(text){                  this.state["greeting"].methods.greet_user(text);                 this.state["logout"].methods.set_name(text);                 this.state["users_array"].entry_user(text);         }    }    //далее также вызываем данный метод entryUser из формы и при загрузке страницы 

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

Чтобы облегчить данную задачу можно воспользоваться пользовательскими событиями. Компонент подписывается на событие, и при его наступлении делает что-то со своими свойствами.

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

var StateMap = {     eventEmiters: {	              //создали эмитер события - входа пользователя	              ["emiter-entry-user"]: {prop: ""}, 		    },    form: { 	container: "form", 	props: ["input", "click"], 	methods: { 			 		click: function(){ 		    event.preventDefault(); 		    var text = this.parent.props.input.getProp();  		     //вызвали событие в форме и передали в него данные 		    this.rootLink.eventProps["emiter-entry-user"].setEventProp(text); 	 		    window.localStorage.setItem('user_name', text);							 		}			 	},		 },	 greeting: { 		 	container: "greeting", 	props: [ "user_name", ['listen_entry_user', "emiter-entry-user", "" ] ], //добавили слушатель события "emiter-entry-user" 	methods: { 			 		listen_entry_user: function(){ 		   //получили данные из события и обновили свойство 		   this.parent.props.user_name.setProp( this.emiter.getEventProp() ); 		},	 	},		 }, logout: {  		 	container: "logout", 	props: [ "user_name", ["listen_entry_user", "emiter-entry-user", "" ]], //свойство слушатель события "emiter-entry-user" 	methods: { 			 		listen_entry_user: function(){ 		     //получили данные из события и вызвали свойство  		     this.parent.props.user_name.setProp( this.emiter.getEventProp() ); 		},			 	},	 },     //здесь слушатель события добавляется в свойство массива, т.к. если добавить его в контейнер оно будет вызвано для каждого контейнера  users_array: {  		 	arrayProps: [ ['listen_entry_user', "emiter-entry-user", ""] ], //свойство слушатель события "emiter-entry-user" 	   arrayMethods: { 			 		listen_entry_user: function(){ 			//получили данные из события и создали новый контейнер 			this.parent.add( {user_name: this.emiter.getEventProp()} ); 		},			 	},		 	container: "user", 	props: [ "user_name",  ['listen_exit_user', "emiter-exit-user", ""]  ], 	methods: {						 		} 	},        	 },  } window.onload = function(){ 	 		 	var HM = new HTMLixState(StateMap); 	var name = window.localStorage.getItem('user_name'); 	 	if(name != null)HM.eventProps["emiter-entry-user"].setEventProp(name); ///вызвали событие "emiter-entry-user" при загрузке сайта и передали в него данные	 }  

Таким образом мы избавились от промежуточного метода «entryUser» и просто вызываем событие передав в него новые данные в форме и после загрузки страницы. Теперь если нам нужно изменить что-либо в компоненте, или вообще удалить его нам не потребуется поправлять код в каждой функции, которых может быть сколько угодно. А чтобы отписаться от какого либо события можно просто удалить слушателя в компоненте, или временно отключить его с помощью метода disableEvent() в свойстве — подписчике.

Полный код данного примера вместе со вторым событием «emiter-exit-user» можно посмотреть здесь

ссылка на оригинал статьи https://habr.com/ru/post/507540/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *