Hello people,
Big fan of elixir & phoenix LiveView
.
While designing LiveView
applications I use LiveComponent
extensively.
Issue-Inspiration
I like LiveComponent
to manage their state, but even with stateful components it is hard to abstract them away from the LiveView
.
Usually there is a dependency to the LiveView
(along with boilerplate code) when you want to talk to a different LiveComponent
in the page, initialize the state of the component or make an async call.
That makes it harder to reuse the LiveComponent
, test it, and the complexity of the LiveView
increases by adding more LiveComponents.
Proposed solution
LiveSub
https://gist.github.com/weakwire/d28dc8e5f9aa1edef78017ee308e7022 introduces a way for LiveComponents to talk to each other via a pub/sub like mechanism for the LiveView
Here is a LiveView application example, using LiveSub
. https://github.com/weakwire/live_sub_example.
How to Use
- Use LiveSub in our LiveView and add your components with the
init=true
attribute
defmodule LiveSubExampleWeb.MyLiveview do
use LiveSubExampleWeb, :live_view
use LiveSub.LiveView
...
~H"""
...
<.live_component module={PeopleComponent} id={PeopleComponent} init={true} />
...
"""
...
- Define the “topics” that your LiveComponents are subscribed_to and/or the topics your LiveComponent emits
defmodule LiveSubExampleWeb.PeopleComponent do
@moduledoc false
use LiveSubExampleWeb, :live_component
use LiveSub.LiveComponent,
subscribe_to: [
"person_added",
"people_loaded"
],
emits: [
"people_loaded"
]
- When you define a topic in “emits”, a helper function is created and you can publish your message like so:
SubHelper.pub_person_added(%{name: "John", surname: "Smith"})
- To listen to a message, a local function is called inside the LiveComponent, generated using the topic name from the
subscribe_to
parameter
defmodule LiveSubExampleWeb.PeopleComponent do
....
#This will be called when `SubHelper.pub_person_added` is called from a component
def sub_person_added(person, socket) do
socket |> assign(:person, person)
end
Result
- Adding N LiveComponents to a LiveView doesn’t increase the complexity of the LiveView
- Each LiveComponent is isolated and testable on it’s own.
- LiveComponents can be reused with minimal effort. For eg. an “Edit Person” component can by used in multiple LiveViews that show a person with 0 changes.
- The LiveComponent can initiate it’s state, even if it requires an async call
- Less boilerplate & complexity, inspires people to use more LiveComponents in their LiveViews
Drawbacks
- Currently you need to initialize the LiveComponent using init=true param. (For LiveSub to work, it requires the component id that unfortunately is not in the
mount
callback) -
LiveSub
hijacks thedef update(%{id: id, init: true})
of the LiveComponent. Unfortunately I don’t have any other ways to make it work. You can choose not to override it and you can manually initiate LiveSub.