Integration w/ Phoenix helpers does not seem to work...
I would like to use Marker in a Phoenix project, but it seems that it does not know how to deal with encoding the result of phoenix-generate html (i.e. helpers):
template :index do
form_for @conn, user_path(@conn, :create), [as: :search], fn f ->
label "Name: #{text_input f, :name}"
label "Age: #{select f, :age, 18..100}"
submit "Submit"
end
end
protocol String.Chars not implemented for {:safe, [60, "input", [[32, "id", 61, 34, "search_name", 34], [32, "name", 61, 34, "search[name]", 34], [32, "type", 61, 34, "text", 34]], 62]}. This protocol is implemented for: Atom, BitString, Date, DateTime, Decimal, Ecto.Date, Ecto.DateTime, Ecto.Time, Float, Hound.Element, Integer, List, NaiveDateTime, Postgrex.Copy, Postgrex.Query, Postgrex.Stream, Time, URI, Version, Version.Requirement
It seems to be a lack of support for iolists in Marker. Plus, I noted that if you use Marker tags inside a Phoenix tag, only the last one is rendered. A workaround for both problem is to embed the content in a div and convert the output to a safe string:
template :form_example do
div do
h1 "Form example"
# Use Phoenix.HTML.Tag.form_tag/2 here.
form_tag page_path(@conn, :configure) do
# Embed the content in a div so the form_tag has only one child.
div do
p do
label "Name: ", for: "name"
input name: "name", type: "text"
end
p do
label "Age: ", for: "age"
input name: "age", type: "text"
end
input type: "submit", value: "Save"
end
end
# Pipe the form_tag value in a custom function.
|> safe_iodata_to_string()
end
end
# Converts safe iodata to safe string.
@spec safe_iodata_to_string(Phoenix.HTML.safe()) :: Phoenix.HTML.safe()
defp safe_iodata_to_string({:safe, iodata}) do
{:safe, IO.iodata_to_binary(iodata)}
end
@zambal Maybe you have an idea where IO.iodata_to_binary/1 should be called in Marker? I’ve taken a look quickly but I can’t figure how to patch this. It shouldn’t be really difficult though.
As for the div hack to have only one child, shouldn’t we have a void or block or some other tag that does not generate anything but return its content? The thing is the tags from Marker concatenate the values of all tags inside it where the tags from Phoenix behave like an ordinary function: they return their last value.
I tried with some Phoenix.HTML.Form, and it turns we must convert all safe iolists:
template :index do
div do
h1 "Example form"
form_for(
@changeset,
page_path(@conn, :example),
fn f ->
# Wrap all the form content in a div
div do
div class: "form-group" do
# Convert the return value of each Phoenix.HTML function
f |> label(:name, "Name:") |> sts()
f |> error_tag(:name) |> sts()
f |> text_input(:name, class: "form-control") |> sts()
end
input type: "submit", value: "Save", class: "btn btn-primary"
end
end
)
|> sts()
end
end
# Same function as in my previous comment but with a short name and list acceptance.
@spec sts(Phoenix.HTML.safe() | [Phoenix.HTML.safe()]) :: Phoenix.HTML.safe()
defp sts(list) when is_list(list), do: Enum.map(list, &sts/1)
defp sts({:safe, iodata}), do: {:safe, IO.iodata_to_binary(iodata)}
Accepting iolists in Marker would avoid all these |> sts().