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

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

Вот рабочие Ember 3 demo и Ember 2 demo всех трех стратегий, а также обычное использование помощника ввода Ember.

Блоки кода и примеры шаблонов в этой статье подходят для Ember 1.13–3. См. Мою статью Как использовать код Ember 2 в приложении Ember 3 для получения дополнительной помощи с использованием старых примеров кода.

В чем проблема?

Есть что-то странное в ползунках диапазона, которые не используют встроенный в Ember помощник input. Когда вы их сдвигаете, изменение не влияет на связанные переменные шаблона, которые вы использовали для установки значения. Привязка только односторонняя (в отличие от того, что происходит, когда вы используете встроенные помощники ввода Ember для текстовых полей и флажков).

Вот пример. Допустим, мы передали score равным 0. Ползунок будет в правильном месте, на 0, когда компонент отрисовывается. Но когда вы перемещаете ползунок, score по-прежнему 0.

<input type="range" min=”0" max=”10" value={{score}}>
This doesn’t change when you move the slide: {{score}}

Нам нужно обработать событие самостоятельно, чтобы изменить значение score. Но как?

Вам нужно писать это от руки?

Может быть. Есть несколько ползунков диапазона, доступных как надстройки Ember, а также встроенный помощник по вводу (руководства и документы). Я использовал кучу ui-ember-slider, и их интерактивные документы просто потрясающие. Вы должны проверить их, если просто хотите знать, во что стрелять, когда вы пишете аддон;)

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

Во многих случаях встроенный помощник ввода Ember работает нормально. Вот пример. Это вызовет действие, когда кто-то перестанет перетаскивать ползунок и отпустит щелчок мыши. (Демо, Вариант 0)

<!-- my-component.hbs template -->
{{input type='range' min=0 max=10 value=sliderValue mouseUp=(action 'sliderUpdate' sliderValue)}}

// in my-component.js
actions: {
  sliderUpdate(sliderValue) {
     // do something with the size variable
   } 
}

Однако в моем случае использования все, что я пробовал, приводило к тому, что кнопка ползунка пропускалась и мерцала при прокрутке маршрутов. У нас не было 60 кадров в секунду. Он не мог угнаться за остальной частью моего приложения. Аддоны могут сработать для вас. Сначала попробуйте их. Но если вам нужен более точный контроль…

Вариант 1. Используйте помощник mut (и поймите его)

Раньше я пропускал сразу те фрагменты кода, в которых использовался помощник mut. Я не понял. Так что, если вы чувствуете то же самое, потерпите меня, и я посмотрю, смогу ли я научить вас быстрее. Это то, что я использовал в своем приложении.

Мне нужно работать только с одним файлом, range-slider-component.hbs:

<input type="range" 
    oninput={{action (mut score) value="target.value"}} 
    min={{min}} 
    max={{max}} 
    value={{score}} 
    steps="1">

Вот пошаговая инструкция. Сначала посмотрим, как я прохожу value={{score}? Мне все еще нужно инициализировать ползунок значением, которое мы передали.

Далее, видите это oninput? Есть только два события, которые могут быть запущены из ползунка диапазона, input и change. Ввод срабатывает каждый раз, когда ползунок перемещается. Смена срабатывает только после того, как пользователь отпустит щелчок. В моем случае я хотел использовать ввод. Чтобы подключиться к вводу, я установил действие, равное on, плюс имя события. Если вы работаете с другими типами элементов помимо ползунка диапазона, вы можете использовать ту же стратегию для любых событий, которые находятся в этом списке в Ember Guides. Только не забудьте добавить on в начало.

Взгляните на это target.value. Событие - это обычный HTML-материал, похожий на аргументы, которые вы получаете в обработчике кликов JQuery. События с целями и значениями не связаны с Ember. Наше событие здесь input, target - это элемент HTML, а value - это номер, на котором находится ползунок.

Что случилось с mut? Есть много способов использовать mut, что отчасти и сбивает меня с толку. В этом случае я думаю о mut как о сеттере. Первый аргумент score - это то, что будет изменено. Второй аргумент target.value - это значение ползунка. Таким образом, мы устанавливаем в качестве оценки значение ползунка каждый раз, когда значение ползунка изменяется и срабатывает событие input. Mut также можно использовать для пометки переменных как подлежащих редактированию при передаче от родительского компонента к дочернему, но это не то, что мы здесь делаем.

Вот более простой пример mut:

<p>Kitten Count: {{kittenCount}}</p>
<button onclick={{action (mut kittenCount) 1}}>1</button>
<button onclick={{action (mut kittenCount) 2}}>2</button>
<button onclick={{action (mut kittenCount) 3}}>3</button>

Нажатие на любую из этих кнопок установит для KittenCounter значение, которое следует после (mut kittenCount). Мой js-файл компонента пуст. В нем нет действий, и я также не передавал никаких действий от родителя.

Вариант 2. Используйте встроенные прослушиватели событий Ember

Хорошо, так что помните этот список событий, который можно использовать в шаблонах, например oninput, onchange, onclick и т. Д.? Все эти события также можно прослушивать из компонента. Это жутковато.

Range-slider-component.hbs:

<input type="range" 
    min={{min}} 
    max={{max}} 
    value={{score}} 
    steps="1">

Range-slider-component.js:

import Ember from ‘ember’;
export default Ember.Component.extend({
  input(event) {
    console.log(event.target.value)
  }
});

Это немного странно, потому что в шаблоне нет действий, как и в нашем файле JS. Есть только эта input функция. Он ожидает срабатывания входного события из шаблона. Само событие автоматически доступно в качестве аргумента. event.target.value - это значение, в которое был установлен ползунок диапазона. Попробуйте console.logging всего объекта события, а затем event.target, чтобы лучше понять, что здесь происходит.

Теперь это решение фактически не изменит score, если вы отображали его в другом месте шаблона. Если хотите, вы можете установить score в качестве значения, например this.set(‘score’, e.target.value). Это работает, но я чувствую себя немного странно по этому поводу. В моем случае мне не нужно было фактически изменять значения переменных, которые я передал, а просто инициировать некоторые другие изменения пользовательского интерфейса, поэтому я просто вызвал их внутри функции ввода.

Вариант 3. Используйте закрывающие действия

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

В моем шаблоне hbs-компонента диапазона-слайдера:

<input type="range" 
  oninput={{action 'sliderMoved'}} 
  min={{min}} 
  max={{max}} 
  value={{someValue}}
  steps="1">

В range-slider-component.js:

import Ember from ‘ember’;
export default Ember.Component.extend({
 actions: {
   sliderMoved(event) {
    console.log(event.target.value) // the slider's value
   }
 },
});

Мы используем менее известное поведение закрытия. Термин закрывающие действия может означать множество вещей, но здесь он означает установку действия, равного имени события в шаблоне, например oninput={{action 'sliderMoved'}}. Каждый раз, когда вы используете действие таким образом, по умолчанию оно получает событие в качестве аргумента. Как и в других примерах, event.target.value - это номер, на котором остановился ползунок.

Если вы не являетесь поклонником неявной передачи аргументов события, вы можете вместо этого сделать что-то вроде этого:

<input type="range" 
  oninput={{action 'sliderMoved' value="target.value"}} 
  min={{min}} 
  max={{max}} 
  value={{score}}
  steps="1">

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

Дальнейшее чтение

Мне очень нравится Deep Dive on Ember Events Мари Чатфилд. Думаю, это помогло мне понять, как я думаю о событиях DOM.

Я прочитал Углубленные действия по закрытию Мигеля Камбы около 8 раз, и, хотя я до сих пор не понимаю всего этого, использование закрывающих действий было важной частью моего рефакторинга всего приложения.

Наконец, давно я написал статью Простой выбор элемента в EmberJS, основанную на статье Балинта Эрди Как сделать выбор в Ember 2.0. В этих статьях описаны некоторые очень похожие стратегии, но для раскрывающегося списка выбора вместо ползунка диапазона. Прошел почти год с тех пор, как я впервые прочитал статью Балинта, и вы можете увидеть все те же шаблоны, которые мы используем здесь. Просто потребовалось время, чтобы осознать.

В заключение

Какой вариант правильный? Ну вы знаете, что они говорят ...