cornerman on master
Update README.md (compare)
mergify[bot] on master
Update scalajs-dom to 2.2.0 (#6… (compare)
fdietze on scala3
Improve workaround comment (compare)
fdietze on scala3
Move UndefinedModifier into low… Almost fix the last compile err… (compare)
cornerman on scala3
potential fix (compare)
fdietze on scala3
Scala 3 (compare)
fdietze on explicit-dependencies
Add sbt-explicit-dependencies p… (compare)
Hi, I'm need an event handler to access the elemet's
scrollTop
position, but can't figure out how. I've tried:import outwatch._ import outwatch.dsl._ import org.scalajs.dom.raw.HTMLElement import zio.Task import zio.interop.catz._ object Header { def apply(): HtmlVNode = { val logo = Task { val isDetached = events.document.onScroll.map { e => val scrollTop = e.target.asInstanceOf[HTMLElement].scrollTop println(s"top dist = $scrollTop") if (scrollTop > 50) "detached" else "attached" }.debounceMillis(250) div( // ... className <- isDetached ) } div(logo) } }
I can't get how you made it to work? I mean div(logo)
when logo is the zio.Task
. For me there is a error like
Found: (logo : zio.Task[outwatch.HtmlVNode])
Required: outwatch.VDomModifier
Effect
instance for zio tasks. You get this implicit via the import: import zio.interop.catz._
. Though you need an implicit runtime in the scope.implicit def runtime = Runtime.default
@cornerman I have made some progress implementing what I was talking about recently.
Please take a look at https://github.com/busti/hummingbird
I have started implementing the concept into outwatch on this branch: https://github.com/busti/outwatch/tree/integrate_hummingbird
Unfortunately my current implementation is flawed. Since using the library requires passing around instances of the base trait and not implementations of it, there will be instances of them at every call. Value-Classes don't work in this case.
I will probably try to refactor what I have so far to remove that limitation.
But what do you think of the general idea and of how the interface is designed?
Laminar's library Airstream has the following to say:
Some reactive UI libraries such as Outwatch give you a way to bind the lifecycle of subscriptions to the lifecycle of corresponding UI components, and that automatically kills the subscription (removes the observer) when the UI component it relates to is destroyed. However, the underlying streaming libraries that such UI libraries use have no concept of such binding, and so in those libraries you can manually call stream.addObserver and create a subscription that will not be automatically killed when the UI component that it conceptually relates to is unmounted.
Is that still true? Is that easy to fix?
@bblfish:matrix.org I was personally drawn to outwach because it combined the syntax of scalatags with monix, a library I had already used before. We have since replaced monix with an abstraction that makes it possible to use other reactive programming libraries such as scala.Rx and perhaps fs2, we have also adopted the F[_]
pattern for effects, which basically allows me to share large portions of code between my backend server and the frontend and something that makes the library stand out more than it did when I first started using it.
There is still a lot of work going on regarding the streaming and effect abstraction parts though to allow for more library support such as zio and zio-streams.
I must admit that I am not as familiar with laminar as I want to be, but the whole ownership concept is super interesting and definitely something I want to take a deeper dive into in the future.
There is probably some interesting stuff that laminar does that we cannot do as easily since it's very tightly integrated with it's own streaming lib.
Since we want to support all kinds of streaming libraries handling lifecycles outside of those that arise from the DOM is outside our scope and must be handled by those libraries. I.e. I would love to know how fs2 solves the problems the airstream docs are talking about.
I personally like the F[_]
functional programming style for libraries which is why I avoided using laminar in the past and I would like to migrate my front-end to zio in the future which is why I am looking into supporting it at the moment.
That being said, interacting with the dom directly is really interesting and probably somewhat faster. We currently use the javascript v-dom library (snabbdom)[https://github.com/snabbdom/snabbdom] for dom interaction.
I have some ideas on how we could model dom diffing directly in outwatch I never did anything specific.
"com.github.outwatch.outwatch" %%% "outwatch" % "e02749756fdb3bad7fb92250ed8a2bfab0f69ea7"
[success] Total time: 1 s, completed 7 Nov 2021, 21:43:34
[error] [webpack-cli] Unable to load '@webpack-cli/serve' command
[error] [webpack-cli] TypeError: options.forEach is not a function
[error] at WebpackCLI.makeCommand (/Volumes/Dev/Programming/Scala3/solidapp/target/scala-3.1.0/scalajs-bundler/main/node_modules/webpack-cli/lib/webpack-cli.js:108:21)
[error] at ServeCommand.apply (/Volumes/Dev/Programming/Scala3/solidapp/target/scala-3.1.0/scalajs-bundler/main/node_modules/@webpack-cli/serve/lib/index.js:41:19)
webpack-cli -> 4.9.1
and @webpack-cli/serve -> 1.6.0
, I get another runtime error:[error] [webpack-cli] Invalid options object. Dev Server has been initialized using an options object that does not match the API schema.
[error] - options has an unknown property 'inline'. These properties are valid:
[error] object { allowedHosts?, bonjour?, client?, compress?, devMiddleware?, headers?, historyApiFallback?, host?, hot?, http2?, https?, ipc?, liveReload?, magicHtml?, onAfterSetupMiddleware?, onBeforeSetupMiddleware?, onListening?, open?, port?, proxy?, server?, setupExitSignals?, static?, watchFiles?, webSocketServer? }
@bblfish:matrix.org Regarding the question about subscriptions and comparison to laminar.
Laminar uses their own streaming library called airstream
, which has a really nice concept about reactive state management. There each operation that might lead to a subscription needs an implicit Owner
. This owner then handles the subscription - i.e. is responsible for killing it. Using this in laminar, the owner is the enclosing html element. Airstream is in some ways similar to scala-rx, both fixing frp-glitches and using an Owner concept.
In outwatch we try to abstract over different streaming libraries, and not all have this owner concept. The subscription methods there, do not require an Owner, but instead return a Cancelable
- a token to cancel the subscription. As long as it is handled, everything is fine.
Now as long as you do not call subscribe
on Observables yourself (in colibri or monix), but just use them in outwatch components, everything is fine and subscriptions will be automatically cleaned up.
The only way to leak here, is to call subscribe yourself and actively ignore the returned subscription, like val _ = observable.subscribe(observer)
. You can still let outwatch handle the subscription manually with managed(SyncIO(observable.subscribe(observer))
inside a component - which imho is rarely needed. Normally, you just use an observable in your component and get everything handled automatically.
scala-rx
directly - even with outwatch - the mentioned danger does not apply. Additionally, we should be able to support airstream with outwatch as well.
Things start working when I move from
addSbtPlugin("ch.epfl.scala" % "sbt-scalajs-bundler" % "0.21.0-RC1")
down to
addSbtPlugin("ch.epfl.scala" % "sbt-scalajs-bundler" % "0.20.0")
in the plugins
dependencyTree
tells me it is colibri that colibri that imports it.... (nice tool)