какие операторы rx js использовать в каком порядке в сценарии автосохранения - PullRequest
2 голосов
/ 01 апреля 2020

Я хочу автоматически сохранить форму с помощью Rx JS в Angular. Форма будет выдавать событие при его изменении

Это сценарий:

  1. Когда выходные данные формы изменились, но не изменились снова в течение следующих 10 секунд , он должен выдавать событие сохранения.

  2. Только если форма продолжает меняться чаще, чем раз в 10 секунд, в течение не менее 30 секунд она также должна генерировать событие сохранения.

Для первого я мог бы использовать debounce, но как мне объединить его со вторым правилом?

Ответы [ 2 ]

1 голос
/ 01 апреля 2020

Это было немного сложно, но правильная комбинация debounceTime и throttleTime может решить проблему:

const s = new Subject();
s.subscribe();

const saveEvery3sSubject = new Subject();
saveEvery3sSubject.subscribe();

const saveEvery3s$ = saveEvery3sSubject.pipe(
  throttleTime(3000),
  mapTo('saving after 3000 ms'),
  startWith(null)
);


const debounce$ = s.pipe(
  tap(r => {
    saveEvery3sSubject.next(r); // this step starts the 3000ms timer
  }),
  debounceTime(1000),
  startWith(null),
);

// combining them both so the saving will occur on debounce or after 3000ms
const result$ = combineLatest([debounce$, saveEvery3s$]).pipe(
    filter(([d, s]) => s !== null)
)


// first 5 values will be debounced forever because 500 < 1000(debounceTime)
interval(500).pipe(
    take(5)
)
  .subscribe(i => {
    s.next('form changed #' + i);
})

// these values will be emitted correctly one by one
timer(9000, 1100).pipe(
    take(20)
)
  .subscribe(i => {
    s.next('after pause #' + i);
})

result$.subscribe();

Использование дополнительного субъекта необходимо, потому что вы оперируете двумя независимыми таймфреймами:

  • debounceTime - при изменении формы и отправке изменений
  • throttleTime - чтобы удостовериться, что каждые 30 секунд форма сохраняется независимо от дебазированного ввода (если вы быстрее, чем debounceTime, значения меня вообще не испустят!)

После того, как первый formChange войдет в поток с debounce, он запускает таймер (в моем примере - 3000 мс), означающий, что если пользовательский ввод отсутствует, будет не будет необходимых выбросов.

Также обратите внимание на startWith(null) - это необходимо для combineLatest, чтобы испустить первое значение.

Это решение в действии: https://rxviz.com/v/38MZnMZo

1 голос
/ 01 апреля 2020

Вам нужно разделить ваш поток на два потока.

const source = fromEvent(document.getElementById("text"), "input");

source
  .pipe(debounceTime(10 * 1000))
  .subscribe(() => console.log("saved after typing!"));

source
  .pipe(
    auditTime(10 * 1000)
  )
  .subscribe(() => console.log("saved while typing!"));

В первом случае, как вы писали в вопросе, значение debounceTime будет выдано через 10 секунд после того, как пользователь закончит редактирование формы / ввода. Во втором случае, audTime каждые 10 секунд будет проверять наличие нового значения. Если есть, то будет выброшено значение. В обратном вызове вы можете повторно использовать ту же функцию, которая отвечает за сохранение данных из формы. Вы можете попробовать это в этой демонстрации на stackblitz:

https://stackblitz.com/edit/rxjs-jj3kot?file=index.ts

Я только что изменил время в демонстрации на 1 секунду, чтобы было легче проверить результат в консоли.

Примечание:

Это простая JS демонстрация. Если вы разрабатываете приложение Angular, вам следует избегать получения элементов с помощью getElementById. Вместо этого используйте прямую ссылку на шаблон: https://angular.io/api/core/ViewChild

Редактировать:

Согласно комментарию OP, я немного изменил реализацию , Я объединил две наблюдаемые и использовал оператор DifferentUntilChanged, чтобы избежать двойного вывода одного и того же значения. Демонстрация в stackblitz также изменена

const afterTyping = source.pipe(debounceTime(1000));

const whileTyping = source.pipe(auditTime(1000));

merge(whileTyping, afterTyping)
  .pipe(distinctUntilChanged())
  .subscribe(console.log);
...