lessless

lessless

Is my understanding of component composition/communication model correct?

Hi,

My background in components composition is mostly with DDAU in Ember.js. So it naturally serves me as a jump pad as I wrap my head around Live View and wonder how close my mental model is to the idiomatic LiveView approach.

Let me illustrate my thinking process with an example of breaking a modal component further down into smaller components.

I have a Dashboard LiveView within which there is a UploadModal that hosts PatientSelector component.

PatientSelector is a typical typeahead: the clinician types patient names and sees a list of patients whose names match his query.
The selected patient becomes a value of that typeahead and must be propagated to the parent UploadModal component.

                                                                                               
      ───────────────────────────────Chronological flow─────────────────────────────────▶     
                                                                                              
                                                                                              
                                                                                              
     ┌───────────────────────────────────────────────────────────────────────────────────┐    
     │                            Upload Modal Live Component                            │    
     │                                                                                   │    
     └─────────────────────────────────────────────▲────────────────────────────┬────────┘    
            │                                      │                            │             
            │                                      │                            │             
       destination                                 │                            │             
 for`on_selected` event                sends `patient_selected`    updates `selected_patient` 
 and `selected_patient`                 event to `on_selected`                  │             
        property                                target                          │             
            │                                      │                            │             
            │                                      │                            │             
      ┌─────▼──────────────────────────────────────┴────────────────────────────▼────────┐    
      │                          PatientSelector Live Component                          │    
      │                                                                                  │    
      └──────────────────────────────────────────────────────────────────────────────────┘    
                               ▲                                                              
                               │                                                              
                               │                                                              
                     User selects patient                                                     
                               │                                                              
                               │                                                              
                               │                                                              
                               │                                                              
                     ┌───────────────────┐                                                    
                     │       User        │                                                    
                     └───────────────────┘                                                                                   

The cornerstone of this component is an input which triggers a patient lookup

 <input
  phx-debounce="100"
  phx-focus={show_results()}
  phx-blur={hide_results()}
  name="search"
  type="text"
  autocomplete="off"
  placeholder="Search by patient name or email"
   value={@selected_patient.name}
/>

and then there is an autocompletion list in which items are entry points to patient selection:

<ul id="upload-modal-patient-selector-results" class="rounded">
  <%= for patient <- @found_patients do %>
    <li
      phx-click="patient_selected"
      phx-value-selected-patient-id={@patient.id}
      phx-target={@myself}
   >

As I understand it - I can’t assign a struct to phx-value-*, so instead, I’m setting selected patient id phx-value-selected-patient-id={@patient.id} .

Then, since PatientSelector keeps a list of found_patients in its state, I can pull Patient from that list in the event handler and send it to the parent component

def handle_event("patient_selected", %{"selected-patient-id" => selected_patient_id}, socket) do
  selected_patient = Enum.find(socket.assigns.found_patients, &(&1.id == selected_patient_id))

  send_update(socket.assigns.on_selected.module,
    id: socket.assigns.on_selected.id,
    selected_patient: selected_patient,
    event: :patient_selected
  )

  {:noreply, socket}
end

Now, because selected_patient is passed to the PatientSelector, when I update it in the parent component, the child component also gets it.

<!-- somewhere in the parent component template --> 
<.live_component module={PatientSelector} id="patient-selector-component" selected_patient={@selected_patient} on_selected={%{module: __MODULE__, id: @id}} current_user={@current_user} />
defmodule UploadModal do
  use ObfuscatedWeb, :live_component
  alias PatientSelector

  def mount(socket) do
    {:ok, assign(socket, :selected_patient, %{name: nil})}
  end

  def update(%{event: :patient_selected, selected_patient: selected_patient}, socket) do
    {:ok, assign(socket, :selected_patient, selected_patient)}
  end

  def update(assigns, socket) do
    {:ok, assign(socket, assigns)}
  end
end

It all works fine, but I wonder if I’m going against the LV programming model with this or not. The primary motivation behind this wiring is to make the upload modal reusable without exposing its wiring/implementation details to hosting LiveView.

Pinging @chrismccord like he’s not busy enough already :joy:

UPDATE: I’m not sure how it happens but after patient being selected for the first time update in the parent component is not triggered, but I still can see selected patient name in the parent component’s template.

Where Next?

Popular in Discussions Top

arcanemachine
https://nitter.net/josevalim/status/1744395345872683471 https://twitter.com/josevalim/status/1744395345872683471
New
Jayshua
I recently came across the javascript library htmx. It reminded me a lot of liveview so I thought the community here might be interested....
New
sashaafm
I’m trying to evaluate the best combo/stack for a BEAM Web app. Right now I’m exploring Yaws a bit, after having dealt with Phoenix for a...
New
cvkmohan
The upcoming Phoenix 1.6 release looks very interesting. Became a habit to watch the commits - and - what they are bringing in. phx.gen...
New
Nvim
Elixir appears to be a superior language to Python. I don’t see any advantage of Python over Elixir. Are there any?
New
WolfDan
After doing a port from a c++ library to my project in phoenix I’ve seen that I need a faster way to run this algorithm and I found this ...
New
fireproofsocks
I’ve been working on an Elixir project that has required a lot of scripting. I usually reach for Elixir because I like it more (and in th...
New
marciol
Please, let me know if this kind of discussion already took place in another topic . Hi all, how do you consider if is better to build ...
New
hazardfn
I suppose this question is effectively hackney vs. ibrowse but we are at a point in our project where we have to make a choice between th...
New
chulkilee
Here are the list of HTTP client libraries/wrappers, and some thoughts on HTTP client in general. I’d like to hear from others how they w...
New

Other popular topics Top

axelson
This post is a wiki (feel free to hit the edit button near the bottom right of this post to add your own changes!) This post collects co...
239 48475 226
New
9mm
I am constructing a JSON object (map) and I need to conditionally set a field. I’m trying to write proper elixir-way code… and I’m at a l...
New
Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New
lessless
I believe there are people here who are dealing with CSV files import on the daily basis, and since Excel is a really popular tool there ...
New
JeremM34
Hello, how can I check the Phoenix version ? Thanks !
New
msaraiva
Surface is an experimental library built on top of Phoenix LiveView and its new LiveComponent API that aims to provide a more declarative...
564 43806 214
New
gausby
I asked this very same question on twitter and got some interesting feedback, but I thought it would be a good question to ask here as we...
1207 39523 209
New
rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list. ...
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
jaysoifer
Is there a way to rollback a specific migration and only that one (“skipping” all the other ones)? Would mix ecto.rollback -v 200809061...
New

We're in Beta

About us Mission Statement