mhdtrifork

mhdtrifork

Hi there, im pretty new to Elixir, Phoenix and Liveview. I come from an app developer background.

Im having a little hard time, wrapping my head around, how to properly compartmentalize (is that a word?) an advanced view, and which responsibilities falls to the liveview, and which falls to the live_component.

How the view was structured: (several live views, each implementing the header, above its own content)
Before

And how im thinking of structuring it now:
after

Because every tab was its own liveview previously, the presentation of specific modals, were each handled by their own “tab” liveview (using patch, and toggling visibility using live_action).

When refactoring this I want to rewrite the old live views into live_components. However, im unsure of wether or not it makes sense keep the toggling of the modals, in the components, or if this functionality should be moved to the “mother” liveview.

My gut feeling and experience from other platforms and languages, tells me that a “view” should handle navigation, and that its best to delegate this responsibility to the controller. However there are a lot of modals, which would mean that my liveview, will get cluttered by many functions. (there will be 9 tabs)

My current plan was to have the component be “dumb” and just fire a send self() from it, like what is shown in the docs here: docs
But if im piping the events through the component to the liveview, does it even make sense for it to be a live_component, or could I just as well use a function component? (im a little lost on when to use what, and for what purpose)

I need good suggestions for how to architecture a screen like this, and comments about best practice in the phoenix/liveview world.

Bonus info: This is an admin tool, where each tab is a specific view, for working and tweaking specific parts of a project.

Showing Posts 1 to 10

soup

soup

I think if your state is complex enough to divide it up, you should put that in a live_component as they can manage their own state, otherwise its normally best in a function component. I think this is where your head was already at anyway.

Here is a layout in a recent project that is similar in structure to yours, just as an example I guess.

In this, each “green” live component manages its own structs & form – specific to that live component and passes data back up to the parent. This could all be done with function components but the main live view would get very bloated. Technically the tab bar is a function component.

So I think this is a spin on your “after”, where instead of nesting the tab content inside a tab component, I cut out the middle man. The “which tab are we on” state isn’t sufficiently complex to need a separate live component in my case. I have used this kind of layout many times on complicated multi-stage forms.

All the tabs are rendered into the page at once and just display: hidden in this case. This does have an impact on the initial page load time if the data structure (well, if the html derived from the datastructure) is complex, so if you can, I would try and only render one at a time. In this case it’s simpler to just load them all instead of passing data up and down and re/de-hydrating all the time and it’s an internal tool so the extra 100-300ms initial render time isn’t too problematic.

So I guess my advice is to start with functional components until the state being tracked is complicated enough that you want to modularise it; at which point you can put it into its own live_component and focus your state changes through messages. This send pattern is really great when you want to only send “valid” data back up to the parent as you can localise all the validation cruft to section-specific changesets & “views” (live_components).

Hope some of that is helpful?

But if im piping the events through the component to the liveview, does it even make sense for it to be a live_component, or could I just as well use a function component? (im a little lost on when to use what, and for what purpose)

If I understand what you mean here, I would not pipe the “handle_params” event down to the component, just use that to determine which to render?

cmo

cmo

If you’re tracking the selected tab as an assign, you can keep that in the liveview and use live/function components for the content. The content could be a slot in your tab component or sit below it.

I aim for function components but if I want to handle state and events, then consider a live component.

mhdtrifork

mhdtrifork OP

The state has to be synchronized across the different tabs, as some of them work on the same data, which is also why, I think its easier to have the liveview be in charge.

My real big question is, the modals I show (which are the ones actually modifying the data, the tabs actually only display data) where should their displaying originate from? Currently It feels kind funky to make a patch call from the tab, only to then have the liveview pass down to the tab, that it should now display the modal component. It also couple the tab to the liveview, preventing reusability (I could probably fix this by injecting the destinations, into the component)

Any thoughts on this?

soup

soup

It looks to me like the liveview should be in charge of showing and hiding the tabs and modals, as they’re basically the same concept no? And whether a modal is visible is “view state”, not “tab state”?

e: Not sure I’m being real helpful down here, might be a bit too tangential to your question as your coupling is really just about an event and effect.

Can you make the argument that the state is related? What about the tab changes when the modal changes and does that warrant the coupling?

(You may update the data between the tab-view and modal-edit on change but IMO this is actually an anti pattern as the modal isn’t “saved” yet, otherwise just edit in the tab – so I would not view this as a reasonable reason to couple and if done would prefer to only send valid data to the tab-view anyway meaning the state is not actually “related” but “derived”?)

cmo

cmo

mhdtrifork

mhdtrifork OP

The tabs more or loss add/edit/delete data, from a list.
It makes sense for me that the live component in the tab, could be in charge of the modals for editing, but it feels wrong to do it through a patch, as we do now, as that is hard coupling the tab to the liveview.

However if I show the modal without a patch, then the url won’t update.

Any ideas here?

mhdtrifork

mhdtrifork OP

So ive been thinking, i think there is 2 ways to go about this:

  1. The component is selfcontained, it handles when modals are showed. I also include a Call to the parent liveview, for when it needs to update the url, and update the State for the other tabs that use the same dataset.

  2. I make it a function component, and let the liveview handle everything. (Gonna make the liveview Big, but Its probably were the events and State belong in this case)

Any comments?

Btw is there any Way to split a liveview into multiple .ex files?

soup

soup

You can split any module into submodules and import/etc those, or you could split a live view into a collection of live components (:thinking:).

Not sure I can judge the validity of any approach much more from the outside beyond what I wrote before – showing tabs and showing modals both seem like “page content” and so should be probably controlled by the “page” (which is our liveview here), separating them also means you can reuse the modal (not the modal wrapper but maybe the actual modal form) in other places without requiring a tab to control it. I just can’t see the reasoning of tying them together but I don’t have the whole context.

Honestly it sounds like you’re over thinking it a bit? Build it one way and discover why you hate it and build it the other way (and discover that way also sucks for other reasons :wink:).

mhdtrifork

mhdtrifork OP

Just to clarify, the modal is already a component.

Think of it like this:
In the tab, we have 3 tables, with each row corresponding to an entry in the db of that data type (belonging to the model im currently working on.
The tab has buttons for adding,editing,deleting rows, where add and edit prompts a modal.

Before it was made weirdly, and each tab was Its own liveview, and because of that all modals are presented by a patch call.
The above feels wrong, as i dont Think a component should handle navigation, or be linked to the liveview it lives in.
On the other hand, the modals only work on the data, in the component, so the argument could be made that Its couple to that component.

(Im general i Think it should live in the liveview, but when i have 9 tabs, all with different events and modal to show, the liveview Will very quickly become a very Long file. And that might be the only reason im considering having the component display the modals…)

DirkDiggler

DirkDiggler

I’ve just hit this very same problem.

I’ve got a sidebar component for top level navigation in a layout. It’s inner content is made up of LiveViews that provide the views for each item in the sidebar. Except one I wish to be a tabbed view. I have 3 tabs, each provide CRUD like operations over entities within the app and it’s a mess. I can totally see how having 9 tabs in this scenario doing the same CRUD like things would get insane.

I feel like the obvious solution is leverage the router as much as possible and then use live_patch to navigate between tabs. But then you’re left with a trade off when it comes to the the live action:

  • Use it to dictate which tab is active and you can’t specify CRUD or other actions
  • Use it to specify the CRUD or other action and you can’t specify the tab

The only workarounds I’ve found are to parse the URI in handle_params to infer either the tab or action.

Where Next? Top

Trending in Questions Top

RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
nseaSeb
Hello, I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
RemyXRenard
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
samoloth
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
asweet-confluent
I recently noticed that Elixir’s Logger defaults its primary log level to :debug when no :logger, :level application configuration is pre...
New

Other Trending Topics Top

JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews