Drab: Using a Shared Commander and Living Assigns

phoenix
drab

#1

I’m working on a golf scorecard app. I’d like to be able to quickly update the number of strokes using Drab. I’m displaying the scores for each round in a table then rendering each score in a partial for that row.

show.html.eex

<h2>Scorecard</h2>

<table>
  <thead>
    <tr>
      <th>Hole Number</th>
      <th>Par</th>
      <th>Add</th>
      <th>Strokes</th>
    </tr>
  </thead>
  <tbody>
    <%= for score <- @round.scores do %>
      <%= render "score_rows.html", Map.put(assigns, :score, score) %>
    <% end %>
  </tbody>
</table>

score_rows.html.drab

<tr drab-commander="GolfWeb.RoundCommander">
  <td>
    <%= @score.hole.hole_number %>
  </td>
  <td>
    <%= @score.hole.par %>
  </td>
  <td>
    <button drab="click:add({id: <%= @score.id %>, num_strokes: <%= @score.num_strokes %>})">+</button>
  </td>
  <td>
    <%= @score.num_strokes %>
  </td>
</tr>

Eventually I will update the database, but I’m just trying to get it wired up for now. Here is my commander.
round_commander.ex

defmodule GolfWeb.RoundCommander do
  use Drab.Commander

  defhandler add(socket, sender, %{"id" => id, "num_strokes" => num_strokes}) do
    score = %{id: id, num_strokes: add_stroke(num_strokes), hole: %{par: 4, hole_number: 2}}
    poke socket, "score_rows.html", score: score
  end

  defp add_stroke(nil), do: 1
  defp add_stroke(num_strokes), do: (num_strokes + 1)
end

When I click the add button, all of the scores in the table are updated rather than just the score within the row.

In the Drab Examples section titled “Shared Commanders and Living Assigns”, it looks like only the partials within each drab-commander should be updated. I checked the rendered html and each partial has a different drab-id.

I also found the Shared Commanders section in the Drab docs and couldn’t find anything I needed to change.

Is what I’m trying to do possible or any ideas about what I’m doing wrong? Thanks!


#2

I was able to get the basic functionality working using set_prop instead of poke:

score_rows.html.drab

<tr drab-commander="GolfWeb.RoundCommander">
  <td>
    <%= @score.hole.hole_number %>
  </td>
  <td>
    <%= @score.hole.par %>
  </td>
  <td>
    <button drab="click:add({id: <%= @score.id %>})">+</button>
  </td>
  <td class="num_strokes">
    <%= @score.num_strokes %>
  </td>
</tr>

RoundCommander.ex

  defhandler add(socket, sender, _params) do
    # hard code "4" for testing
    set_prop socket, this_commander(sender) <> " .num_strokes", innerHTML: "4"
  end

This updates only the score next to the button.


#3

Well, it should be like this, otherwise it is counter-intuitive. I did not test this behaviour under the partial, but I will. Thanks for this!