4

My issue has nothing to do with the Angular router bug.

I have a component that can be dragged and dropped. The code for the drag and drop functionality looks like this:

@HostListener('mousedown')
onMousedown(): void {
  const mousemove$ = Observable.fromEvent(document, 'mousemove');
  const mouseup$ = Observable.fromEvent(document, 'mouseup');

  const modifiedMouseMove$ = mousemove$
    .map(some transformations)
    .takeUntil(mouseup$);

  modifiedMouseMove$.subscribe(do something on each move);
  modifiedMouseMove$.last().subscribe(drop the item)
}

If I just "click" on the item, I get:

Error: EmptyError { message: 'no elements in sequence', ... }

I'm assuming the issues is that when I click, it fires a mousedown -> mouseup, with no mousemove events in between. Causing the modifiedMouseMove$ to be "empty" and the call to .last() returns nothing. This error does not impact the functionality of my application. I can catch it and do nothing:

modifiedMouseMove$.last().subscribe(() => drop the item, (err) => {
  if (err.name === 'EmptyError') { 
    return;
  } else {
    throw err;
  }
});

Now it doesn't show up in the console anymore, but purposefully swallowing an error seems...bad. How do I actually handle the error?

Should I not activate the second subscription (with the last() operator) if there are no mouse moves? If so, what's the best way to do that?

Is there a magical operator that will only subscribe if there is an event and leave the observable cold otherwise?

vince
  • 7,808
  • 3
  • 34
  • 41

2 Answers2

4

If you want just the last item from an Observable and you're fine when there's none it's better to use the takeLast(1) operator:

modifiedMouseMove$.takeLast(1).subscribe(...)

The last() and first() operators require one and only one matching item. Any other situation results into an error notification.

martin
  • 93,354
  • 25
  • 191
  • 226
1

Agreed with Martin's answer. Would also add a defaultIfEmpty to handle after takeLast(1) to avoid no elements being emitted.

E.g.,

takeLast(1).defaultIfEmpty(element).subscribe()