在 phx-change 之后更新表单字段的值

Update the value of a form field after phx-change

我有一个 LiveView 应用程序可以搜索机场代码。当用户输入 ham 时,它应该用 HAM (String.upcase/1) 替换该表单字段的内容,但事实并非如此。但根据我对我的代码的理解,它应该。我必须更改什么才能用大写版本替换该字段中的所有输入?

顺便说一句:如果我向它添加一个按钮并使用 phx-submit 而不是 phx-change,它就会起作用。但我想让它为 phx-change 工作。

设置

$ mix phx.new travelagent --live --no-ecto
$ cd travelagent

lib/travelagent_web/router.ex

[...]
scope "/", TravelagentWeb do
  pipe_through :browser

  live "/", PageLive, :index
  live "/search", SearchLive
end
[...]

lib/travelagent/airports.ex

defmodule Travelagent.Airports do
  def search_by_code(""), do: []

  def search_by_code(code) do
    list_airports()
    |> Enum.filter(&String.starts_with?(&1.code, code))
  end

  def list_airports do
    [
      %{name: "Berlin Brandenburg", code: "BER"},
      %{name: "Berlin Schönefeld", code: "SXF"},
      %{name: "Berlin Tegel", code: "TXL"},
      %{name: "Bremen", code: "BRE"},
      %{name: "Köln/Bonn", code: "CGN"},
      %{name: "Dortmund", code: "DTM"},
      %{name: "Dresden", code: "DRS"},
      %{name: "Düsseldorf", code: "DUS"},
      %{name: "Frankfurt", code: "FRA"},
      %{name: "Frankfurt-Hahn", code: "HHN"},
      %{name: "Hamburg", code: "HAM"},
      %{name: "Hannover", code: "HAJ"},
      %{name: "Leipzig Halle", code: "LEJ"},
      %{name: "München", code: "MUC"},
      %{name: "Münster Osnabrück", code: "FMO"},
      %{name: "Nürnberg", code: "NUE"},
      %{name: "Paderborn Lippstadt", code: "PAD"},
      %{name: "Stuttgart", code: "STR"}
    ]
  end
end

lib/travelagent_web/live/search_live.ex

defmodule TravelagentWeb.SearchLive do
  use TravelagentWeb, :live_view
  alias Travelagent.Airports

  def mount(_params, _session, socket) do
    socket =
      socket
      |> assign(:airport_code, "")
      |> assign(:airports, [])

    {:ok, socket}
  end

  def handle_event(
        "airport_code_search",
        %{"airport_code" => airport_code},
        socket
      ) do
    airport_code = String.upcase(airport_code)

    socket =
      socket
      |> assign(:airport_code, airport_code)
      |> assign(:airports, Airports.search_by_code(airport_code))

    {:noreply, socket}
  end
end

lib/travelagent_web/live/search_live.html.leex

<form phx-change="airport_code_search">
  <fieldset>
    <label for="nameField">Airport Code</label>
    <input type="text" name="airport_code" value="<%= @airport_code %>"
    placeholder="e.g. FRA" 
    autofocus autocomplete="off" />
  </fieldset>
</form>

<%= unless @airports == [] do %>
  <h2>Search Results</h2>
  <table>
    <thead>
      <tr>
        <th>Airport Code</th>
        <th>Name</th>
      </tr>
    </thead>
    <tbody>
      <%= for airport <- @airports do %>
      <tr>
        <td><%= airport.code %></td>
        <td><%= airport.name %></td>
      </tr>
      <% end %>
    </tbody>
  </table>
<% end %>

JavaScript 控制台输出

phx提交版本

如果我使用这种形式:

<form phx-submit="airport_code_search">
  <fieldset>
    <label for="nameField">Airport Code</label>
    <input type="text" name="airport_code" value="<%= @airport_code %>"
    placeholder="e.g. FRA" 
    autofocus autocomplete="off" />
    <input class="button-primary" type="submit" value="Search Airport">
  </fieldset>
</form>

我得到了这个工作结果(输入 fra 并单击按钮后):

PS:JavaScript 或 CSS 这个问题有上百万种可能的解决方案。但我想知道如何使用 LiveView 正确解决这个问题。

根据documentation,

The JavaScript client is always the source of truth for current input values. For any given input with focus, LiveView will never overwrite the input's current value, even if it deviates from the server's rendered updates.

这种情况下的解决方案是使用 JS hooks.