Structuring a LiveView with many similar (dynamic?) components

I’ve just been doing something similar, building pluggable charts for borehole data.

What I ended up doing was:

  • Pluggable data extractor modules (basically a “read_data” function in each module that receives a keyword list for context - e.g. dates - in my case a borehole identifier)
  • A small piece of configuration (currently hard-coded, but in future will be user-configurable) that maps named datasets to the data extractor modules
  • On mount or handle_params, I iterate the data extraction configuration to build a map of extracted datasets using the different extractor modules and put them on the socket as socket.assigns.dataset (in my case about 20Mb per liveview process - thankfully there’s only ever a handful of users!). There are also some derived datasets generated from the base ones (e.g. moving averages) - the data extraction process runs the base ones first, then generates the derived ones.

In the case where you are listening for live updates from other parts of the system I would just update the data held in socket.assigns.datasets in the appropriate handle_info

That sorts out the data extraction.

I also have pluggable renderers for each different type of visualisation I want to show (mostly based on Contex FWIW - they emit SVG so no JS hooks are required).

I then have a definition for each display element that defines the named dataset to use, the pluggable rendering module and any settings to control the rendering. This is added to the socket as display_blocks

Finally I have a component that is embedded in the main liveview along the lines of what you have:

<%= live_component(@socket, MyLayoutComponent, datasets: @datasets, display_blocks: @display_blocks, other_stuff: @other_stuff, id: "some-id") %>

MyLayoutComponent handles organising height & width of all the sub-components based on the settings in the passed display_blocks, passing in the correct dataset and settings and invoking the rendering. The whole thing re-renders when anything changes at the moment (data, settings or other things like the currently selected item - aka other_stuff), which is a bit inefficient but actually performs ok. I feel the code and approach is reasonably well organised and easy enough to extend with additional visualisations.

I hope this makes sense!