marker icon indicating copy to clipboard operation
marker copied to clipboard

Integration w/ Phoenix helpers does not seem to work...

Open ijcd opened this issue 8 years ago • 3 comments

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

ijcd avatar Aug 26 '17 17:08 ijcd

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

ejpcmac avatar Jun 19 '18 11:06 ejpcmac

@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.

ejpcmac avatar Jun 19 '18 11:06 ejpcmac

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().

ejpcmac avatar Jun 21 '18 16:06 ejpcmac