raquo / Airstream

State propagation and event streams with mandatory ownership and no glitches
MIT License
246 stars 28 forks source link

add flatmap to ReactiveEventProp #99

Closed doofin closed 2 years ago

doofin commented 2 years ago

for example,I can't say onClick.flatMap(x=>other eventStream) . I use composeEvents[Ev, Ev, Out](eventPropToProcessor[Ev](eventProp))(composer) as workaround

Seems that ReactiveEventProp and eventStream share lots of common interface,should we unify operators for them ?

raquo commented 2 years ago

Hi,

I already have flatMap implemented on ReactiveEventProp in Laminar 15.0.0 which will be released later this year:

  /** Similar to the Airstream `flatMap` operator.
    *
    * Use this when you want to create a new stream or signal on every event, e.g.:
    *
    * button(onClick.preventDefault.flatMap(_ => makeAjaxRequest()) --> observer)
    *
    * #TODO[IDE] IntelliJ (2021.3.2) shows false errors when using this flatMap implementation,
    *  making it annoying. Use flatMapStream or flatMapSignal to get around that.
    *
    * Note: This method is not chainable. Put all the operations you need inside the `operator` callback,
    *       or use the `compose` method instead for more flexibility
    */
  def flatMap[Out, Obs[_] <: Observable[_]](
    operator: V => Obs[Out]
  )(
    implicit flattenStrategy: FlattenStrategy[EventStream, Obs, Observable]
  ): LockedEventKey[Ev, V, Out] = {
    new LockedEventKey[Ev, V, Out](this, eventStream => eventStream.flatMap(operator)(flattenStrategy))
  }

  /** Similar to `flatMap`, but restricted to streams only. */
  def flatMapStream[Out](
    operator: V => EventStream[Out]
  )(
    implicit flattenStrategy: FlattenStrategy[EventStream, EventStream, Observable]
  ): LockedEventKey[Ev, V, Out] = {
    flatMap(operator)(flattenStrategy)
  }

  /** Similar to `flatMap`, but restricted to signals only. */
  def flatMapSignal[Out](
    operator: V => Signal[Out]
  )(
    implicit flattenStrategy: FlattenStrategy[EventStream, Signal, Observable]
  ): LockedEventKey[Ev, V, Out] = {
    flatMap(operator)(flattenStrategy)
  }

For now, I don't really want to copy most of the EventStream API to ReactiveEventProp, or to build complex abstractions to make a common API available on both, but in 15.0.0 I've added a simple compose method to ReactiveEventProp which lets you use any stream operators without the annoyance of inContext or composeEvents:

  /** Similar to the Airstream `compose` operator.
    *
    * Use this when you need to apply stream operators on this element's events, e.g.:
    *
    *     div(onScroll.compose(_.throttle(100)) --> observer)
    *
    *     a(onClick.preventDefault.compose(_.delay(100)) --> observer)
    *
    * Note: This method is not chainable. Put all the operations you need inside the `operator` callback.
    */
  def compose[Out](
    operator: EventStream[V] => Observable[Out]
  ): LockedEventKey[Ev, V, Out] = {
    new LockedEventKey(this, operator)
  }
doofin commented 2 years ago

Thanks! For IDE,I sometimes use vscode with metals which is closer to compilers and more accurate