Surface.Component now is built on top of function components instead of stateless live components. This decision implies some breaking changes described below with solutions that allow you updade your code smoothly.
Basically, any data preparation that was done inside those callbacks must be moved to render/1. The Phoenix Live View API has been updated so you can use assign, assign_new, etc. in any function component.
Before:
defmodule StatelessComponent do
use Surface.Component
prop count, :string
data count_mount, :string
data count_updated, :string
@impl true
def mount(socket) do
socket =
socket
|> assign(:count_mount, socket.assigns.count + 1)
{:ok, socket}
end
@impl true
def update(assigns, socket) do
socket =
socket
|> assign(assigns)
|> assign(:count_updated, assigns.count + 2)
{:ok, socket}
end
@impl true
def render(assigns) do
~F"""
<div>{@count} - {@count_updated}</div>
"""
end
endAfter:
defmodule StatelessComponent do
use Surface.Component
prop count, :string
data count_mount, :string
data count_updated, :string
@impl true
def render(assigns) do
assigns =
|> assign_new(:count_mount, fn -> assigns.count + 1 end) assigns
|> assign(:count_updated, assigns.count + 2)
~F"""
<div>{@count} - {@count_mount} - {@count_updated}</div>
"""
end
endIf you were using the @socket assign to render routes, you should now use the application Endpoint instead.
Before
Routes.page_path(@socket, :show, "Hello")
# or
MyAppWeb.Router.Helpers.page_path(@socket, :show, "Hello")After:
Routes.page_path(MyAppWeb.Endpoint, :show, "Hello")
# or
MyAppWeb.Router.Helpers.page_path(MyAppWeb.Endpoint, :show, "Hello")This guide provides detailed instructions on how to run the built-in converter to
translate Surface v0.4 code into the new v0.5 syntax.
-
By design, the converter doesn't touch Surface code inside documentation or macro components. If you have any code written inside
<!-- -->or<#Raw>...</#Raw>, you need to convert it manually. -
The replacement of
~Hwith~Fhappens globally in a.ex(or.exs) file, i.e., the converter will replace any occurrence of~Hfollowed by""",",[,(or{, including occurrences found in comments. -
The replacement of
slot name, props: [...]withslot name, args: [...]happens globally in a.ex(or.exs) file, i.e., the converter will replace any occurrence of it, even if found in comments. -
Running the converter on a project that has already been converted may generate invalid code. If anything goes wrong with the conversion, make sure you revert the changes before running it again.
-
Make sure you have committed your work or have a proper backup before running the converter. It may touch a lot of files so it's recommended to have a safe way to rollback the changes in case anything goes wrong.
-
If you're using an earlier version of Surface, make sure you update it to
v0.4.1and fix any deprecation warning that might be emitted. If you have too many warnings regardingautomatic conversion of string literals into atoms is deprecated and will be removed in v0.5.0and you don't want to fix them manually, you can try @paulstatezny's surface_atom_shorthand_converter to fix them all for you. -
Check your dependencies. For a safer migration, all dependencies providing Surface components should be converted before running the converter on the main project. Otherwise, you might not be able to compile your project in case any of those dependencies is using the invalid old syntax. If the dependency you need has not been updated yet, please consider running the converter against it and submitting a PR with the updated code. The steps to convert a dependency are the same described in this guide.
Update your .formatter informing about .sface files and any additional folder where you might have any component
to be converted:
[
surface_inputs: ["{lib,test}/**/*.{ex,exs,sface}", "priv/catalogue/**/*.{ex,exs,sface}"],
...
]
Update mix.exs to use the new version:
defp deps do
[
{:surface, "~> 0.5.0"},
...
]
end
Compile the dependencies:
mix clean && mix deps.get && mix deps.compile
Run the converter:
mix surface.convert
Compile the converted project:
mix compile
| Subject | Examples (Old syntax -> New syntax) |
|---|---|
| Sigil | ~H""" -> ~F""" |
| Interpolation | {{@value}} -> {@value} |
| Templates | <template> -> <#template> |
| If | <If condition={{ expr }}> -> {#if expr} |
| For | <For each={{ expr }}> -> {#for expr} |
| Interpolation in attr values | id="id_{{@id}}" -> id={"id_#{@id}"} |
ErrorTag's phx_feedback_for |
<ErrorTag phx_feedback_for="..." /> -> <ErrorTag feedback_for="..." /> |
| Non-string attr values | • selected=true -> selected={true} • tabindex=1 -> tabindex={1} |
| Slots | • <slot :props={{ item: item }}> -> <#slot :args={item: item}> • slot name, props: [...] -> slot name, args: [...] |
In case you run into any trouble while running the converter, please open an issue at https://github.com/surface-ui/surface/issues/ providing detailed information about the problem, including the error message (if any) and a snippet of the related code.