Yesod Hamlet breaks HTML by replacing single quotes with double quotes

113 Views Asked by At

I have some HTML code that I'm using in Hamlet:

 <div .modal-card .card data-options='{"valueNames": ["name"]}' data-toggle="lists">

Notice that the single quotes for data-options allows the use of double quotes inside the string.

The problem is that when Hamlet renders the page, Hamlet puts " around the ' and so the HTML is broken:

<div class="modal-card card" data-options="'{" valuenames":"="" ["name"]}'="" data-toggle="lists">

Some external JS library plugin code runs, it tries to parse the JSON inside data-options and fails.

How can I tell Hamlet to include a literal string?

I've tried various combinations of:

let theString = "{\"valueNames\": [\"name\"]}"
let theString2 = "data-options='{\"valueNames\": [\"name\"]}'"
etc

And in the hamlet file:

 <div .modal-card .card data-options='#{ preEscapedText theString }' data-toggle="lists">
or
 <div .modal-card .card #{ preEscapedText theString2 } data-toggle="lists">

But all attempts produce invalid HTML or invalid JSON inside the string.

How can I instruct Hamlet to simply include a literal string in the output HTML?

Update:

Tried more things, no result.

The string2 example doesn't work because Hamlet seems to think that I'm trying to set id="{" as per https://www.yesodweb.com/book/shakespearean-templates#shakespearean-templates_attributes

1

There are 1 best solutions below

2
On BEST ANSWER

Why not render the JSON escaped (" become &quot;) and “handle” the quotes later when parsing?

Interpolate in Hamlet:

<div #the-modal .modal-card .card data-options='#{theString}' data-toggle="lists">

Parse the data attribute as JSON:

let json = document.getElementById("the-modal").getAttribute("data-options");
let opts = JSON.parse(json); // At least in Chrome, it works!

As for theString2 alternative, you can also interpolate attributes in Hamlet using a tuple or list of tuples and the star symbol:

let dataOptions = ("data-options", "{\"valueNames\": [\"name\"]}") :: (Text, Text)
...
<div #the-modal .modal-card .card *{dataOptions} data-toggle="lists">