1

I have the following leex template with a collection of cards and inside each card, there is a nested button. I would like to update the text on the button only without rendering again the whole component. Is it possible? I am stuck here. The idea is to send an update to this button but it's not clear how if possible at all. Or is another way would be assigning again the @repos to the socket?

<form phx-change="gsearch">
    <fieldset>
        <label for="name">Name</label>
        <input id="name" type="text" name="name" value="<%= @name %>"
               placeholder="Search repos..."
               autofocus autocomplete="off" phx-debounce="500" />

        <input class="button-primary" type="submit" value="Clear"/>
    </fieldset>
</form>

<%= unless @repos == [] do %>
<h2>Search Results</h2>

<div class="cont">
    <%= for repo <- @repos do %>
        <%= card do %>
            <%= card_header do %>
                <h2>Name: <%= repo.name %> Stars: <%= repo.stars %></h2>
                <%= if repo.description != nil do %>
                    <%= repo.description %>
                <% end %>
            <% end %>
            <%= card_body do %>
                <%= link repo.link, to: repo.link, target: "_blank" %>
                <button id="<%= repo.id %>"
                        class="button-primary fav"
                        phx-click="save"
                        phx-value-id="<%= repo.id %>"
                        phx-value-action="<%= if repo.liked do %>unlike<% else %>like<% end %>"
                        style="display: block">
                <%= if repo.liked do %>Remove<% else %>Add<% end %>
                </button>
            <% end %>
        <% end %>
    <% end %>
</div>
<% end %>

When I click the button, I do the job on the server side and then I want to update only this button in particular.

As it is clear from the code, buttons are located inside the card component which is just a div.

Is it possible to update just the button which I clicked?

Shaik Subhan
  • 284
  • 1
  • 10
Dmitry Dyachkov
  • 1,715
  • 2
  • 19
  • 46
  • `button id="<%= repo.id %>"` is not an assign so the changes will not be tracked. I assumed that this part `for repo <- @repos` will be used for tracking the changes hence the update on the whole card's div. – Raymond Lagonda Jan 19 '23 at 05:07
  • @RaymondLagonda I though so too. To make it at this time, I've used same repos and just propagate it again to the page but I believe theres even more performant way to implement it – Dmitry Dyachkov Jan 19 '23 at 08:31
  • The other way I can think of is through JS hook. Instead of rendering the whole card through assign tracking, you can hook the button id to small script and push the text changes using event. There is also temporary assigns, I guess this one is more preferrable. Check this out: [temporary assigns](https://hexdocs.pm/phoenix_live_view/dom-patching.html#temporary-assigns) – Raymond Lagonda Jan 19 '23 at 13:44
  • @RaymondLagonda I just wanted to make it Elixir way – Dmitry Dyachkov Jan 20 '23 at 08:49

1 Answers1

0

Since you're using LiveView, updating the socket such that the new repos assign contains an updated target repo with a toggled liked attribute should do the trick. Here's one way of updating the assigns:

def handle_event("save", %{"id" => id}, socket) do
  # update repo in db
  # ...

  # then update repo in socket
  updated_socket =
    update(socket, :repos, fn repos -> 
      update_in(repos, [Access.filter(&(&1.id == id}}], fn repo ->                 
        %{repo | liked: !repo.liked}
      end)
    end)

  {:noreply, updated_socket}
end

I'd also suggest reading through the Managing State section of the LiveView docs. It's meant for LiveComponent and Heex, but should still be applicable and lays out the different options you have for state management.

codeanpeace
  • 194
  • 1
  • 5