Is there a good way to insert random HTML/JS snippets in elm? - elm

I'd like to embed the following snippet in an elm app I'm writing:
<script src="https://gist.github.com/jpaugh/2988462.js"></script>
I've tried using the [markdown|..|] quasi-quoter,
header = plainText "blah, blah."
gist = [markdown|
<script src='https://gist.github.com/jpaugh/2988462.js'></script>
|]
main = flow down [header, gist]
And that throws an error that clearly represents a bug in Elm, and puts all of my content inside a <noscript>.
<noscript>
<p>blah, blah</p>
<p><script src='https://gist.github.com/jpaugh/2988462.js'></script>
</p>
</noscript>
But is there another way to do this? Using Markdown syntax to insert html snippets seems precarious to me. Is this covered by one of the library functions? And how should I insulate it from Elm's own javascript? (Using an <iframe> doesn't seem to help.)
Edit: Here's the error message. This is what shows up onscreen, not the code.

It may be best to take it the other way around: don't use JS/CSS to mess up elm's "domain" and rather embed elm in normal HTML: you can do what you want outside of the elm box and still run your JS outside:
http://elm-lang.org/blog/announce/version-0.8.elm#embedding-elm-in-html-and-js
But I think you can achieve the contents from that snippet already in Elm without using any javascript, I'm not sure what is the thing you are trying to achieve in the end.

Update: No longer possible
I did this, because I am evil.
script' : List Attribute -> String -> Html
script' attrs s = node "script" attrs [ text s ]
based on Html and Html.Attributes respectively. Example usage:
div [] [ script' [] "alert(tremble in fear elm);" ]
as such
script : List Attribute -> List Html -> Html
script attrs children = node "script" attrs children
scriptSrc : String -> Html
scriptSrc s = script [ type' "text/javascript", src s ] []
scriptRun : String -> Html
scriptRun s = script [ type' "text/javascript" ] [ text s ]

Related

Apply vue-katex to content loaded from static folder

I'm trying to make a blog using Vue as laid out in the excellent demo here. I'd like to include some mathematical formulas and equations in my blog, so I thought I'd try to use vue-katex. vue-katex formats my mathematical notation perfectly when I put all my KaTeX HTML directly into my Vue templates, but to create a useable blog I need to keep my content separate from my templates (as shown in the demo).
I can't get vue-katex to format HTML content in the static folder. That's what I'd like help with.
Setup
I cloned the github repo for the demo.
I added vue-katex to package.json:
"vue-katex": "^0.1.2",
I added the KaTeX CSS to index.html:
<!-- KaTeX styles -->
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.9.0-alpha2/katex.min.css"
integrity="sha384-exe4Ak6B0EoJI0ogGxjJ8rn+RN3ftPnEQrGwX59KTCl5ybGzvHGKjhPKk/KC3abb"
crossorigin="anonymous"
>
I added the import statement to src/App.vue:
import Vue from 'vue'
import VueKatex from 'vue-katex'
Vue.use(VueKatex)
and I added a simple line of HTML with KaTeX to the BlogPost template:
<p>Here's an equation in the actual Vue template: <div class="equation" v-katex="'X \\sim N(\\mu, \\sigma^2)'"></div></p>
As I said, this works - I see formatted mathematical notation in my blog post (URL http://localhost:8080/read/neque-libero-convallis-eget):
However, I need different equations for every blog post, of course.
So I tried adding KaTeX HTML to the "content" field in the JSON for the first blog post: static/api/post/neque-libero-convallis-eget.json. I changed the "content" line to:
"content": "Here's an equation in the static folder: <div class=\"equation\" v-katex=\"'X \\sim N(\\mu, \\sigma^2)'\"></div>",
This content appears on the page, but the equation doesn't render. I see this: (the text appears but no equation is shown)
When I use Developer Tools to inspect the HTML on the page, I see this:
You can see that vue-katex has been applied to the equation I put in the template directly: it has parsed the HTML I typed into lots of spans with all the mathematical symbols, which are showing perfectly.
However the KaTeX HTML I've added to the "content" in the static folder has simply been placed on the page exactly as I typed it, and is therefore not showing up as an equation on the page. I really need to keep my blog post content in this static folder - I don't want to have to create a different .vue file for each blog post, that defeats the point!
My question is: is there a way to manually "apply" vue-katex to the HTML I place in the static folder, when it loads? Perhaps there is something I can add to the plugins/resource/index.js file, since this contains the function that loads the data from the static folder?
Many thanks in advance for any help.
*Disclaimer: I'm definitely no expert / authority on what I'm about to explain!
One thing to remember is that Vue reads the templates you write, and then replaces them as reactive components. This means that although you often write Vue attributes like v-for, v-html or in this case v-katex these attributes are only useful up until the app or component is mounted.
With this in mind, if you have a Vue app that ajax loads some html, its not going to be able to rerender itself with those Vue bindings in place.
I have somewhat ignored your current set up and set about solving the issue in another way.
Step 1: Reformat your data from the server side
I've put the posts into an array, and each post contains the template (just a string of html) and the equations separately as an array. I've used [e1] in the post as a placeholder for where the katex will go.
var postsFromServer = [{
content : `<div>
<h2>Crazy equation</h2>
<p>Look here!</p>
[e1]
</div>`,
equations : [
{
key : 'e1',
value : "c = \\pm\\sqrt{a^2 + b^2}"
}
]
}];
Step 2: When the post is rendered, do some work on it
Rather than just use v-html="post.content", I've wrapped the html output in a method
<div id="app">
<div v-for="post in posts" v-html="parsePostContent(post)">
</div>
</div>
Step 3: Create a method that renders all the katex, and then replaces the placeholders in the post
methods : {
parsePostContent(post){
// Loop through every equation that we have in our post from the server
for(var e = 0; e < post.equations.length; e++){
// Get the raw katex text
var equation = post.equations[e].value;
// Get the placeholder i.e. e1
var position = post.equations[e].key;
// Replace [e1] in the post content with the rendered katex
post.content = post.content.replace("[" + position + "]", katex.renderToString(equation));
}
// Return
return post.content;
}
}
Here is the whole set up, which renders Katex:
https://codepen.io/EightArmsHQ/pen/qxzEQP?editors=1010

Is there a way to generate an empty Html node in Elm-Html?

I'm writing a function that displays error messages, so in my view, I have something like
div [] [ displayErrors model ]
in the event that there are no errors, how can I make displayErrors return something that is interpreted as an empty Html node?
You can use an Html.text node with an empty string:
emptyNode = Html.text ""
#robertjlooby's answer looks perfectly fine to me.
However, if you really want not to have any node, you could make displayErrors return a Maybe (Html msg).
Then, you can change your code to the following:
import Maybe.Extra exposing (maybeToList)
div [] ( displayErrors model |> maybeToList )
When displayErrors returns Nothing, the div will be really empty.
Note: you can install the Maybe.Extra module with the following command:
elm package install elm-community/maybe-extra

Elm: how to use an iframe

I want to render an iframe given a String input. For example, I have some string of HTML like this:
str = "<!DOCTYPE html><html><head></head><body>HELLO ELM</body></html>"
In JS I would do this by setting innerHtml on the iframe.
For now, I'm trying to just get the iframe to render anything at all, but haven't had success. I'm trying this sort of thing:
iframe
[]
[ body [] [ div [][text "hi"], div [][text "hi"] ] ]
Once I get it working with a list of HTML nodes, I'll move onto making it work with a string. What I'm looking for would be some kind of stringToHtml: String -> List Html method.
Any help or suggestions for getting started with iframes in elm?
You can use the srcdoc attribute of an iframe to set the html from an html string, like so:
import Html exposing (..)
import Html.Attributes exposing (srcdoc)
main =
div [ ]
[ div [] [ text "Outside the iframe" ]
, iframe
[ srcdoc "<div>Inside the iframe</div>" ]
[]
]
(IE and Edge do not currently support srcdoc but there is a polyfill)
In the end, though, that's a rather brittle solution because it bypasses the Virtual DOM. At this time, there appears no other way to manipulate iframe content within the Virtual DOM. Could your content instead be stored in another page and referenced through the iframe src attribute?

How to get elm to display HTML from an ajax call

I have the following Elm code, it is doing an Ajax call which will return some HTML which I want to embed directly into the dom. The problem is that the code here escapes the html so the user sees the markup, not the intended result. So I need to replace plainText with something else, but I am at a loss as to what that would be
load_comp_from_comp_set : String -> Signal Element
load_comp_from_comp_set compset_id =
Signal.constant ("http://localhost:8000/finch/compset/" ++ compset_id)
|> Http.sendGet
|> Signal.map (result >> plainText)
You can use Markdown.toElement from the elm-markdown library. I tried this the following code on elm-lang.org/try and it injected the HTML as expected.
import Markdown
main = Markdown.toElement """
<div>
<h1 style="display: inline">Hello!</h1>
<span></span> <sub>world</sub>
</div>
"""
You can use elm-package to install the library so elm-make automatically picks it up. It should be as simple as elm-package install evancz/elm-markdown.

Elm: How do I display a list of strings in an HTML list?

I'm writing an elm program that should format its output in an HTML list. The function I want takes,
inputs = ["first", "second", "third"]
and outputs some kind of Elm Element that is essentially,
<ul>
<li>first</li>
<li>second</li>
<li>third</li>
</ul>
Sadly, I can't find any built-in functions to do this. Perhaps the markdown syntax could be extended to take Mustache-like templates,
[markdown|
{{#inputs}}
* {{text}}
{{/inputs}}
]
(sorry I'm not sure what the correct Mustache syntax is for an array-of-strings, instead of array-of-objects).
Raw HTML element emitting would also be nice. Thanks in advance!
Do you want to render in an element (i.e. canvas) or truly as html. If the latter, then you can use elm/html and
renderList : List String -> Html msg
renderList lst =
ul []
(List.map (\l -> li [] [ text l ]) lst)
or in piping style
renderList : List String -> Html msg
renderList lst =
lst
|> List.map (\l -> li [] [ text l ])
|> ul []
or in point-free style
renderList : List String -> Html msg
renderList lst =
lst
|> List.map (li [] << List.singleton << text)
|> ul []
When you start writing a function, it's helpful to define your function's signature. The signature is written on the line before the function definition. The signature you want is:
toHtmlList : List String -> Html msg -- here's the signature
toHtmlList strings = -- here's the start of the function body
???
The output of this function has type Html msg, which is provided by the elm-html package. You add this to your project by running the command $ elm-package install elm-lang/html from your project root.
Once that is installed, add a statement to import Html into your elm file, and implement the function. Here's a working implementation:
StringList.elm
import Html exposing (..)
inputs = ["first", "second", "third"]
toHtmlList : List String -> Html msg
toHtmlList strings =
ul [] (List.map toLi strings)
toLi : String -> Html msg
toLi s =
li [] [ text s ]
Interestingly, I don't think Elm has a built-in Element for lists. I'm not sure whether that's intentional because you can roll your own*, or if it's just that nobody ever needed a non-static list of things before. (HTML lists are used on the elm-lang.org website, but those are static lists that I think are defined in MarkDown)
Markdown interpolation with moustache syntax was implemented but I'm not sure on it's status. And at any rate it was not as powerful as the thing you're describing.
Emitting raw HTML is not part of the philosophy of Elm's Graphics API. The idea is that the current HTML/CSS/JavaScript way of writing websites and web-applications is a mess, even with all the libraries etc. built on top of it. So Elms way is to put a layer of abstraction over it so you can just talk about rectangular Elements that naturally stack horizontally and vertically (with flow*), and free-form Forms that can be put in a collage (which is again a rectangular Element).
*(see Daniƫls answer for one with bullets, this one is just bare-bones):
inputs = ["first", "second", "third"]
main = flow down <| map plainText inputs
You can write a bullet list in Elm using a collage:
bullet : Element
bullet = collage 12 20 [circle 3 |> filled black]
render : [String] -> Element
render = flow down . map (beside bullet . plainText)
main : Element
main = render ["first", "second", "third"]
Live example: http://share-elm.com/sprout/538e460fe4b07afa6f981ab6