Как использовать RxJS для отображения индикатора "пользователь печатает"?
Я немного знаю BaconJS, но сейчас я пытаюсь изучить RxJS, создав индикатор "Пользователь печатает...". Это довольно просто, это можно объяснить двумя простыми правилами:
- Когда пользователь печатает, индикатор должен быть сразу виден.
- Когда пользователь прекращает печатать, индикатор должен оставаться видимым в течение 1 секунды после последнего действия пользователя.
Я не уверен, что это правильно, но я до сих пор создал два потока:
- Один поток сердцебиения, который испускает
0
каждую секунду. - Один поток, чтобы захватить пользовательские события ввода и испустить
1
для каждого события.
Затем я объединяю их вместе и просто нажимаю на результат. Если это 1
Затем я показываю индикатор. Если это 0
затем я скрываю индикатор.
Вот как это выглядит:
const showTyping = () =>
$('.typing').text('User is typing...');
const showIdle = () =>
$('.typing').text('');
// 1 second heartbeats are mapped to 0
const heartbeat$ = Rx.Observable
.interval(1000)
.mapTo(0);
// user typing events are mapped to 1
const input$ = Rx.Observable
.fromEvent($('#input'), 'input')
.mapTo(1);
// we merge the streams together
const state$ = heartbeat$
.merge(input$)
.do(val => val === 0 ? showIdle() : showTyping())
.subscribe(console.log);
Вот ссылка на JSBin:
http://jsbin.com/vekixuv/edit?js,console,output
У меня есть несколько проблем и вопросов, связанных с этой реализацией:
- Иногда, когда пользователь печатает,
0
пробирается, поэтому индикатор мигает на доли секунды, прежде чем вернуться к следующему нажатию клавиши пользователя. - Не гарантируется, что индикатор исчезнет через 1 секунду после того, как пользователь перестанет печатать. Гарантируется, что индикатор исчезнет в течение 1 секунды (что противоположно тому, что мы хотим).
- Является ли использование потока сердцебиения правильным способом RxJS для этого? У меня есть чувство, что это не может быть.
У меня есть ощущение, что я совершенно не согласен с моей реализацией, я ценю любую помощь, которую вы можете оказать. Благодарю.
2 ответа
Вам даже не нужно использовать два Observables и использовать только один с debounceTime()
, Вся логика, которую вы пытались сделать, уже присутствует в debounceTime()
оператор:
const showTyping = () =>
$('.typing').text('User is typing...');
const showIdle = () =>
$('.typing').text('');
const input$ = Rx.Observable
.fromEvent($('#input'), 'input')
.do(() => showTyping())
.debounceTime(1000)
.subscribe(() => showIdle());
Смотрите живую демонстрацию: http://jsbin.com/cixipa/6/edit?js,console,output
Для этого вам не нужно сердцебиение, просто генерируйте изменения-события всякий раз, когда что-то происходит / изменяется:
const showTyping = () =>
$('.typing').text('User is typing...');
const showIdle = () =>
$('.typing').text('');
// user typing events
const input$ = Rx.Observable
.fromEvent($('#input'), 'input');
// user stopped typing
const stoppedTypingAfter1s$ = input$
.switchMapTo(Rx.Observable.timer(1000));
// we merge the streams together
const state$ = Rx.Observable.merge(
input$.mapTo(1),
stoppedTypingAfter1s$.mapTo(0)
)
.startWith(0)
.do(val => val === 0 ? showIdle() : showTyping())
.subscribe(console.log);
Смотрите в прямом эфире здесь.
switchMap
, будет сбрасывать любой предыдущий 1-таймер всякий раз, когда генерируется новое событие ввода.