Any suggestions on a HTML/CSS/JS TreeView that fits into the LiveView environment

These things happen, no problem.

It’s clearer what you meant then, but I really don’t follow how arrived at that conclusion and I’m not sure if a 10,000 word article would help either. Maybe describe using only a sentence or two exactly where a front-end using Streams turns from React-mode to jQuery-mode. As allergic as I am to writing JS I’m reasonably sure I’d have noticed if I needed to resort to writing my frontend code in JS rather than Elixir.

So once you have that succinctly described point where streams unavoidably breaks down, I’ll be extremely keen to watch a debate between yourself and the authors of the system you’re saying is doing these bad things.

Zach involved himself by asking how I connected Ash to this topic and as I explained already the declarative motif versus Streams triggered a thought in my overactive mind which resulted in a throwaway remark that perhaps Ash stands for “Anti-Stream Huddle”.

It actually sounds more like your views on development runs somewhat orthogonal to the concepts underpinning Phoenix, LiveView and Streams in the sense that you seem prone to tackling problems in the frontend which others using the Phoenix framework tend to solve on the server well before presenting the result through the frontend which is effectively written in the same language as the server. Sure it involves some JS that runs on the UA, but the clever guys write and test that code so the rest of us don’t have do anything at that level.

That’s actually what my original question was about. I asked for insights into how others handle Tree Views in Phoenix LiveView. I’ve got that mostly sorted now without any additional JS libraries or even code I had to inject at all.

As for managing nested/recursive data in streams I found MapSets to be a powerful enabler. I have some thoughts brewing on how it could be done more efficiently directly in Ecto and using something even more custom-built for flattening recursive structs for sreaming purposes, but that’s a discussion for another day.

I simply had to reiterate my reaction to this. If your goto approach is to regard the server as nothing more than a minimalistic data store feeding records to the frontend where the real features are implemented you are bound to run into exactly that type of problem, and very quickly too, as you obviously experienced. Perhaps it’s not how your mind is put together, but believe me when I say that most if not all features become a whole lot easier to implement efficiently and robustly if you leverage the server’s unique opportunities.

Yep, I helped derail the conversation, sorry about that. I agree that, while I use LiveView for the majority of my projects, it leaves some things to be desired in terms of declarative UI especially those used to FRP.

With that said, having worked with react for a few years and witnessed the complexity boom, it can be a very costly abstraction. Months of my time lost (in aggregate) playing games with “does this cause rerenders” and how state management, side effects with hooks etc. are all intertwined in the rendering process.

I’m a big proponent of declarative design, and personally I think that Liveview stands to be a basis on which we can achieve the true dream of a client/server FRP model. Eventually :slight_smile:

Personally I’ve found huge productivity wins with LV despite there being new challenges. Its a very new programming model compared to the existing models.

1 Like

It’s become clear to me reading this reply that you’re not really responding to the points I’ve actually made, and I think perhaps it’s because they got lost in the advice I was trying to give (about recursive UI elements being a pain point for streams). But this is your thread and you’ve continued to show interest in my perspective on streams in general, so I will try one more time to make my point from scratch, and I will avoid as many metaphors and as much undefined jargon as I can so that we don’t get lost.

My preferred definition of declarative is that which preserves “referential transparency”: put simply, pure functions. Pure functions are functions which have no side-effects. Elixir is a functional language, and most functions, unless they do I/O, return immutable results. Most functions are pure.

Imperative is the opposite of that: side effects everywhere.

In Phoenix LiveView, which is a stateful framework for rendering UI on the server, the frontend is on the server. So, by frontend, I mean “the LiveView templates”.

React is a framework designed to render UI templates declaratively. Meaning, the UI is a pure function of the application state. The rendered templates are then diffed into the DOM, but this is an implementation detail, and you do not need to think about it to build your UI.

Before React, most web applications were built with imperative tools: when you wanted to change the content of an element, you would modify that element specifically. React’s declarative paradigm did away with that. This is almost universally understood to be a better approach.

Phoenix LiveView follows the React model by default. The rendered template is a pure function of the application state (assigns). Therefore, Phoenix LiveView renders templates declaratively by default. Almost all LiveView applications and components are built this way. This is good.

LiveView streams are an API which abandons declarative rendering in exchange for performance. LiveView streams are therefore imperative. Streams reduce template sizes on the wire, and they reduce the amount of memory used by the server. This is why they were added to Phoenix. This is not necessarily a bad decision: there are some applications which may need these optimizations, and something is better than nothing.

The problem is that imperative frontend UI forces the developer to reason about exactly which parts of the UI must be updated when the state is changed. For reasons I have laid out in previous comments, this is intractable for any non-trivial use case.

But I should not have to substantiate this further as, as I mentioned previously, the React model (declarative) has been universally adopted, including by Phoenix LiveView - unless you use streams.

There is one, and only one, point I am making here: Abandoning declarative rendering for performance is rarely a worthwhile tradeoff, and should be done with extreme caution.

I hope the comment I just left will clarify this, but I just want to reiterate once more:

I am not criticizing LiveView. I love LiveView. I am criticizing streams for abandoning the principles laid out by LiveView.

LiveView, by default, works exactly the way I am advocating for! It’s really good!

Also, it is still possible to use streams, it’s just hard. One thing I would like to think about more is how to design an API with the performance benefits of streams that does not have the drawbacks.

I haven’t quite figured it out yet, but I think the solution will be a combination of two things: nested diffing of arbitrary collections on the server, and some way to maintain an in-memory view on the server for infinite scrolling. From a technical perspective, I think both of these can be done.

I remember seeing some work towards nested diffs in the commits for LV1.0, which is exciting to me, but I don’t think it’s finished yet.

Streams aren’t the only place we deviate from the declarative model. Any time we write hooks we procedurally tap into LVs lifecycle for an element, and pushing events etc is imperative. There are many places that working with LV is not a “pure” FRP model.

2 Likes

Also, I don’t necessarily disagree with the premise here. Writing declarative wrappers over complexity that would be otherwise untenable in the individual case is literally my thing :laughing: If I have anything of value to offer this discussion it would be that the imperative version of all declarative things has always existed before the declarative version of it. I’m not sure what it would realistically look like for LiveView streams, I’ve only really used them in their simple/intended use cases.

2 Likes

A.K.A “Gall’s Law”

OK, as topical and interesting as this discussion has been, it really is way off the topic of my original question. I have a genuine and real-world interest in handing my use-case both effectively and efficiently without resorting to any of the mesures you’ve described as the undoing of streams.

I’m moving this to a Discussion on Recursive Streams, as participants on this topic, please join me there @zachdaniel, @garrison, @derek-zhou, @cmo and @LostKobrakai

In practice these two topics got mixed up after all. The direction I ended up taking rendered it pointless to discuss recursive streams further so most o what is being dicussed on the new thread isn’t about that but about building a rich primary user interface for large indefinitely recursive datasets at scale.