How to trim rendered html content

Hello guys, I am not sure if this question has been asked before, but I did a search and did not find it.

I am looking to trim the html rendered from a ~F"“” sigil.

 defp produce_trimmed_html(assigns) do 
    html = ~F"""
    <somecomponent />
     """
   String.trim(html)
 end

Here, String.trim is not working because the html is not a string but binary. How can I do this?

Can you show an example of what binary are you getting so we can see why String.trim doesn’t work on it? All other things being normal, it should work on a binary as well.

I am not able to retrieve the binary data.
Even a simple html causes an error.

defp some_method(assigns) do
    item = ~F"""
    <div></div>
    """

    String.trim(item)
end

VSCode reports this:

The function call will not succeed.

String.trim(
  _item :: %Phoenix.LiveView.Rendered{
    :dynamic => (_ -> []),
    :fingerprint => 264_197_832_235_070_763_522_609_757_631_039_213_058,
    :root => true,
    :static => [<<_::96>>, ...]
  }
)

will never return since the success typing is:
(binary()) :: binary()

The Surface error I am getting on the console is this:

no function clause matching in String.trim/1
    (elixir 1.13.4) lib/string.ex:1241: String.trim(%Phoenix.LiveView.Rendered{dynamic: #Function<0.132818971/1 in MoonWeb.Pages.IconsPage.get_import_text_by_attribute/2>, fingerprint: 264197832235070763522609757631039213058, root: true, static: ["<div></div>\n"]})

It tells me that item is not a string but binary()

As the error message tells you sigil_F doesn’t return a string. It returns a struct, which is required to optimize templates in LV usage (split dynamic vs. static parts, …). You simply cannot run String.trim on that.

Hi @alexisdevtailor!

You can achieve that with something like:

~F"""
<somecomponent />
"""
|> Phoenix.HTML.Safe.to_iodata()
|> IO.iodata_to_binary()
|> String.trim()
3 Likes

An alternative approach might work, by trimming first and sigiling later. Maybe something like:

defp produce_trimmed_html(assigns) do 
    """
    <somecomponent />
    """
   |> String.trim
   |> sigil_F
 end
1 Like

@msaraiva Thank you for this suggestion. :blush: It works well, the only problem is that Safe is converting the html tags into safe representations. The data is not user supplied and I am pretty sure it is safe, so I don’t want to convert the html result into safe representation. Is there a way to do this?

@christhekeele Thank you for this suggestion. I am trying to this in a live_view and I get an error stating that sigil_F/1 is undefined. Do I need to import something first? :blush:

Note that returning a trimmed string rather than %Phoenix.LiveView.Rendered{} will remove all diffing, so this is almost definitely not what you want to do unless it’s the top-level render of a dead view. Even the the marginal trimming savings is not going to be worth the extra work.

3 Likes

Upon some digging, I’m now quite sure sigil macros are obliged to do all their work at compile-time only, and are not designed to readily accept run-time strings without a bit of hackery. You could always try to craft a runtime-friendly variant of whatever the sigil does under the covers, but as @chrismccord points out there are other reasons to rethink the approach.