Модуль JavaScript для отображения и обработки откроется форма для добавления пользователей


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

Во-первых, код:

HTML-код

<form id="add-user-form">
  <input type="text" placeholder="username" name="username">
  <button>add User</button>
</form>
<hr>
<ul id="user-list"></ul>


<script id="user-template" type="text/html">
  <li data-key="${key}">${name}</li>
</script>

В JavaScript

// lets you get templates from HTML ( as you can see in the HTML above ). 
// This is to avoid writing out HTML inside JavaScript
const Template = {
  cached: {},
  get(templateId, placeholders) {
   let templateHtml = ''
   if (!this.cached[templateId]) {
      templateHtml = document.getElementById(templateId).innerHTML.trim()
      this.cached[templateId] = templateHtml
   }
   else {
      templateHtml = this.cached[templateId]
   }
   
   for (key in placeholders) {
     templateHtml = templateHtml.replace(new RegExp("\\${\\s*" + key + "\\s*}", "g"), placeholders[key]);
   }
   return templateHtml
  }
}

const Users = {
  users: [],
  init() {
    this.cacheDom()
    this.bindEvents()
  },
  cacheDom() {
    this.addUserFormEl = document.getElementById('add-user-form')
    this.userListEl = document.getElementById('user-list')
  },
  bindEvents() {
    this.addUserFormEl.addEventListener('submit', this.handleAddUser.bind(this))
  },
  render() {
    // gets the HTML from ALL users of our user list and replaces the current user list
    const userHtml = this.users.map( user => Template.get('user-template', user) ).join('')
    this.userListEl.innerHTML = userHtml
  },
  handleAddUser(e) {
    e.preventDefault()

    const username = e.currentTarget.elements['username'].value
    if (!username) return
    e.currentTarget.elements['username'].value = ''

    const key = this.users.length // I know I know this is bad practice, please ignore :)

    this.users.push({
      key: key,
      name: username      
    })

    this.render()
  }
}

Users.init()

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

Я хочу сохранить дом изменения к минимуму, поэтому я поставил их внутри метода Users.render. Однако это имеет один побочный эффект. Всякий раз, когда я Добавить пользователя, он загружает весь список. Он может работать с таким объемом данных, но предположим, у меня есть тысячи записей, может быть, даже с поля ввода. Это все опять опустели.

Я мог, конечно, просто добавить новый вход в дом внутри Users.handleAddUser способ но что, если позже я хочу удалять пользователей? Мне придется удалить запись в Users.handleRemoveUser и тогда есть два метода, которые обрабатывают дом модификациями одного и того же.

Я знаю, что это все может быть легко и красиво достигнуто с такими платформами, как VueJs, что использовать вдом, но я ищу vanillaJs способ справиться с этим. (Обратите внимание, что код-это просто пример. Да модульного тестирования и машинопись не хватает и я прекрасно осознаю тот факт, что код не будет работать в браузерах, которые не поддерживают Эс-6).



Комментарии
1 ответ

Ваши Проблемы/Вопросы


Всякий раз, когда я Добавить пользователя, он загружает весь список.

Один альтернативный подход мог бы заключаться в том, чтобы handleAddUser() магазин вновь добавленных пользователей в собственность (например, recentlyAdded) и затем render() способ искать, что собственность - если она установлена (не null) затем добавить оказываемых шаблона нового пользователя и понять, что собственность.

Когда страница загружается, существует ли список пользователей, которые будут добавлены в список? Если так, может быть, они могут быть сохранены в другое имущество и существующий код в render() может искать что собственность на оказание существующие записи.


Я мог, конечно, просто добавить новую запись к внутренним пользователям дом.способ handleAddUser но что, если позже, я тоже хочу удалять пользователей. Мне придется удалить запись в Users.handleRemoveUser и так уже есть два метода, которые обрабатывают дом модификациями одного и того же.

Идя вместе с альтернативного подхода выше, можно иметь handleRemoveUser способ также хранить ключ от недавно удаленных пользователей, а затем render() способ может выглядеть для этого свойства, и удалить такой элемент, ассоциированный с этим ключом - возможно, путем добавления id или других данных атрибута в шаблон.

Вы не включают в себя реализацию handleRemoveUser поэтому я могу только догадываться, что это будет: какой-то способ удаления пользователя из списка this.user. Если this.users по-прежнему массив, потом ищем пользователя для удаления может потребоваться петли. Однако если this.users представляет собой ассоциативный массив (т. е. объект с ключами, соответствующими key каждого пользователя) (или карту), потом ищем пользователей, чтобы удалить может быть достигнуто без петли.

Увидеть это продемонстрировано в следующем фрагменте.


В редакции 2 вас возник этот вопрос, подверглось пересмотру 5:


Как вы можете видеть, я не получаю вход по идентификатору, но и по форме.элементы и затем указать имя.

Это прекрасно, чтобы сделать это, но потому, что данные формы не отправляются на сервер на стороне, вы могли бы также использовать идентификатор атрибута на входе вместо имени атрибута, а затем получить элемент по ID.

Другие Отзывы

В Template.get() функция перебирает ключи в placeholders:


for (key in placeholders) {

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

Я недавно видел этого так ответьте , что предлагает решение для интерполяции строковый литерал как шаблон литерал будет интерполировать. Я сравнил его с регулярным выражением подход в коде и обнаружил, что он значительно медленнее (см. см. Этот тест jsperf тест). Я надеялся, я мог бы предложить этим предложением, но, к сожалению, оно может быть гораздо медленнее.



// lets you get templates from HTML ( as you can see in the HTML above ). 
// This is to avoid writing out HTML inside JavaScript
const Template = {
cached: {},
get(templateId, placeholders) {
let templateHtml = ''
if (!this.cached[templateId]) {
templateHtml = document.getElementById(templateId).innerHTML.trim()
this.cached[templateId] = templateHtml
}
else {
templateHtml = this.cached[templateId]
}

for (let key in placeholders) {
templateHtml = templateHtml.replace(new RegExp("\\${\\s*" + key + "\\s*}", "g"), placeholders[key]);
}
return templateHtml
}
}

const Users = {
users: {},
recentlyAdded: null,
recentlyRemovedKey: null,
nextKey: 1,
init() {
this.cacheDom()
this.bindEvents()
},
cacheDom() {
this.addUserFormEl = document.getElementById('add-user-form')
this.userListEl = document.getElementById('user-list')
},
bindEvents() {
this.addUserFormEl.addEventListener('submit', this.handleAddUser.bind(this))
this.userListEl.addEventListener('click', this.handleListClick.bind(this));
},
render() {
// gets the HTML from ALL users of our user list and replaces the current user list
/*const userHtml = this.users.map( user => Template.get('user-template', user) ).join('')
this.userListEl.innerHTML = userHtml*/
if (this.recentlyAdded) {
this.userListEl.innerHTML += Template.get('user-template', this.recentlyAdded);
this.recentlyAdded = null;
}
if (this.recentlyRemovedKey) {
const element = document.getElementById(this.recentlyRemovedKey);
if (element) {
element.remove();
}
this.recentlyRemoved = null;
}
console.log('userlist after render: ',this.users);
},
handleAddUser(e) {
e.preventDefault()

const username = e.currentTarget.elements['username'].value
if (!username) return
e.currentTarget.elements['username'].value = ''

const key = this.nextKey++;

this.users[key] = {
key: key,
name: username
};
this.recentlyAdded = this.users[key];

this.render();
},
handleListClick(e) {
const target = e.target;
if (target.classList.contains('remove')) {
this.handleRemoveUser(target.parentNode.id);
}
},
handleRemoveUser(key) {
delete this.users[key];
this.recentlyRemovedKey = key;
this.render();
}
}

Users.init()


.remove:before {
content: 'X';
}

<form id="add-user-form">
<input type="text" placeholder="username" name="username">
<button>add User</button>
</form>
<hr>
<ul id="user-list"></ul>

<script id="user-template" type="text/html">
<li data-key="${key}" id="${key}">${name} <button class="remove"></button></li>
</script>




0
ответ дан 9 апреля 2018 в 06:04 Источник Поделиться