To upgrade an application using Phoenix LiveView 1.0 to version 1.1 (RC 3 at the moment), you need to take a couple of different steps.
Upgrading the dependencies
-
In your
mix.exs
, updatephoenix_live_view
to latest and addlazy_html
as a dependency (you also need Phoenix 1.8, if you want to try colocated hooks):{:phoenix, "~> 1.8.0-rc.4", override: true}, {:phoenix_live_view, "~> 1.1.0-rc.3", override: true}, {:lazy_html, ">= 0.0.0", only: :test},
Note you may remove
floki
as a dependency if you don't use it anywhere. -
Still in your
mix.exs
, prepend:phoenix_live_view
to your list of compilers insidedef project
, such as:compilers: [:phoenix_live_view] ++ Mix.compilers(),
-
(optional) In your
config/dev.exs
, finddebug_heex_annotations
, and also adddebug_tags_location
for improved annotations:config :phoenix_live_view, debug_heex_annotations: true, debug_tags_location: true, enable_expensive_runtime_checks: true
-
Run
mix deps.get
to install the dependencies -
Run the following command to update the code for the new changes:
mix phoenix_live_view.upgrade 1.0.0 1.1.0
Once the final version of Phoenix LiveView 1.1 will be released, it will be as simple as executing the following command to do all the hard work (thanks to igniter from Zach Daniel):
mix igniter.upgrade phoenix_live_view
Moving from Floki to LazyHTML
LiveView v1.1 moves to LazyHTML as the HTML engine used by LiveViewTest
. LazyHTML is based on lexbor and allows the use of modern CSS selector features, like :is()
, :has()
, etc. to target elements. Lexbor's stated goal is to create output that "should match that of modern browsers, meeting industry specifications".
This is a mostly backwards compatible change. The only way in which this affects LiveView projects is when using Floki specific selectors (fl-contains
, fl-icontains
), which will not work any more in selectors passed to LiveViewTest's element/3
function. In most cases, the text_filter
option of element/3
should be a sufficient replacement, which has been available since LiveView v0.12.
Note that in Phoenix versions prior to v1.8, the phx.gen.auth
generator used the Floki specific fl-contains
selector in its generated tests in two instances, so if you used the phx.gen.auth
generator to scaffold your authentication solution, those tests will need to be adjusted when updating to LiveView v1.1. In both cases, changing to use the text_filter
option is enough to get you going again:
{:ok, _login_live, login_html} =
lv
- |> element(~s|main a:fl-contains("Sign up")|)
+ |> element("main a", "Sign up")
|> render_click()
|> follow_redirect(conn, ~p"<%= schema.route_prefix %>/register")
If you're using Floki itself in your tests through its API (Floki.parse_document
, Floki.find
, etc.), you are not required to rewrite them when you update to LiveView v1.1.
Colocated hooks
LiveView v1.1 introduces colocated hooks to allow writing the hook's JavaScript code in the same file as your regular component code.
A colocated hook is defined by placing the special :type
attribute on a <script>
tag:
alias Phoenix.LiveView.ColocatedHook
def input(%{type: "phone-number"} = assigns) do
~H"""
<input type="text" name={@name} id={@id} value={@value} phx-hook=".PhoneNumber" />
<script :type={ColocatedHook} name=".PhoneNumber">
export default {
mounted() {
this.el.addEventListener("input", e => {
let match = this.el.value.replace(/\D/g, "").match(/^(\d{3})(\d{3})(\d{4})$/)
if(match) {
this.el.value = `${match[1]}-${match[2]}-${match[3]}`
}
})
}
}
</script>
"""
end
Important: LiveView now supports the phx-hook
attribute to start with a dot (.PhoneNumber
above) for namespacing. Any hook name starting with a dot is prefixed at compile time with the module name of the component. If you named your hooks with a leading dot in the past, you'll need to adjust this for your hooks to work properly on LiveView v1.1.
Colocated hooks are extracted to a phoenix-colocated
folder inside your _build/$MIX_ENV
directory (Mix.Project.build_path()
). See the quick update section at the top of the changelog on how to adjust your esbuild
configuration to handle this. With everything configured, you can import your colocated hooks inside of your app.js
like this:
...
import {LiveSocket} from "phoenix_live_view"
+ import {hooks as colocatedHooks} from "phoenix-colocated/my_app"
import topbar from "../vendor/topbar"
...
const liveSocket = new LiveSocket("/live", Socket, {
longPollFallbackMs: 2500,
params: {_csrf_token: csrfToken},
+ hooks: {...colocatedHooks}
})
The phoenix-colocated
folder has subfolders for each application that uses colocated hooks, therefore you'll need to adjust the my_app
part of the import depending on the name of your project (defined in your mix.exs
). You can read more about colocated hooks in the module documentation of Phoenix.LiveView.ColocatedHook
. There's also a more generalized version for colocated JavaScript, see the documentation for Phoenix.LiveView.ColocatedJS
.
Other notable changes
If this post was enjoyable or useful for you, please share it! If you have comments, questions, or feedback, you can email my personal email. To get new posts, subscribe use the RSS feed.