Elixir phx更改后更新表单字段的值

Elixir phx更改后更新表单字段的值,elixir,phoenix-framework,phoenix-live-view,Elixir,Phoenix Framework,Phoenix Live View,我有一个LiveView应用程序,可以搜索机场代码。当用户输入ham时,它应该将表单字段的内容替换为ham(String.upcase/1),但它不会。但根据我对代码的理解,应该是这样的。要用升级版本替换该字段中的所有输入,我必须更改什么 顺便说一句:如果我添加了一个按钮,并使用phx submit而不是phx change,它就会工作。但是我想让它为phx change工作 安装程序 lib/travelagent\u web/router.ex [...] scope "/", Trave

我有一个LiveView应用程序,可以搜索机场代码。当用户输入
ham
时,它应该将表单字段的内容替换为
ham
(String.upcase/1),但它不会。但根据我对代码的理解,应该是这样的。要用升级版本替换该字段中的所有输入,我必须更改什么

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

安装程序 lib/travelagent\u web/router.ex

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

  live "/", PageLive, :index
  live "/search", SearchLive
end
[...]
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
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
<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 %>
lib/travelagent/airports.ex

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

  live "/", PageLive, :index
  live "/search", SearchLive
end
[...]
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
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
<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 %>
lib/travelagent\u web/live/search\u live.ex

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

  live "/", PageLive, :index
  live "/search", SearchLive
end
[...]
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
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
<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 %>
lib/travelagent\u web/live/search\u live.html.leex

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

  live "/", PageLive, :index
  live "/search", SearchLive
end
[...]
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
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
<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 %>

机场代码
搜索结果
机场代码
名称
我得到这个工作结果(在输入
fra
并点击按钮后):

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

根据

JavaScript客户端始终是当前输入值的真实来源。对于任何具有焦点的给定输入,LiveView将永远不会覆盖输入的当前值,即使它与服务器的渲染更新不同


这种情况下的解决方案是使用。

您是否尝试过在控制台中使用
liveSocket.enableDebug()
,以确保
@airport\u code
上的更新被下推到客户端?我添加了JavaScript控制台的屏幕截图。这很奇怪。可能是一种设计选择。如果对您有效,可选择此解决方案—
style=“text transform:uppercase”
在输入字段上。将使它们始终显示为大写。但是,如果用户界面中有人键入小写字母,但看到大写字母(无论是通过CSS还是LiveView),可能会造成一些混乱。您使用的是什么版本的LiveView?