Why aren't NodeList / HtmlCollection seqable? - clojurescript

As a newcomer to Clojurescript it appears to me that every Clojurescript project will have some snippet of code like this:
(extend-type js/NodeList
ISeqable
(-seq [array] (array-seq array 0)))
Why isn't this part of the core library?

You have to think that clojurescript is a compiler to javascript as a language, not only browser JavaScript. You can also use it in other platforms like nodejs or with the QT library where NodeList does not exist (because it is part of the Dom api and not the standard language).

If you are looking for a way to create a sequence from a NodeList there is array-seq function.
(array-seq (js/document.querySelectorAll "div"))

With a patch for https://clojure.atlassian.net/browse/CLJS-3199 applied in ClojureScript 1.10.741 (seq (js/document.querySelectorAll "div")) now actually works out of the box.

Related

How to use Polymer components with ClojureScript + React?

I'm using Polymer components with ClojureScript + Reframe (actually, at the moment, I have just one Polymer component working). My component is mostly stateless (a drawing tool for Dicom images), so it plays nicely with Reframe. I use HTML + callbacks to communicate with it. It works nicely, but I'm not using ClojureScript advanced compilation. I haven't used advanced compilation before and have some questions about its use:
As I understand, Polymer isn't compatible with Google Closure. Is that still the case?
If I'm using just HTML/callbacks to talk to the component, do I still have problems with name mangling? I suppose that I am safe with the HTML part, but am I going to have problems with the callbacks? Such as [component-x {... :on-mouse-down (dispatch-canvas-event editor-id)}]
What happens to the javascript portion of the WebComponent during compilation? Does it get changed, even if I don't call any function directly (just HTML/callbacks)?
If I do have to call a function in a component, any examples of how to write an Extern file for a Polymer component?

Save generated HTML using Canopy

Can a website's generated HTML be saved using Canopy? Looking at the documentation under 'Getting Started', I could not find anything related.
You can run arbitrary JavaScript using js, document.documentElement.outerHTML will return the current DOM, so
let html = js "return document.documentElement.outerHTML" |> string
does the trick.
Canopy is a wrapper around Selenium that provides some useful helper functions. But it also provides access to the Selenium IWebElement instances in case you need them, via the element function (halfway down the page; there don't seem to be internal anchors in that page so I couldn't link directly to the function). Then once you have the IWebElement object, your problem becomes similar to this one, where the answer seems to be elem.getAttribute("innerHtml") where elem is the elememt whose content you want (which might even be the html element). Note that the innerHtml attribute is not a standard DOM attribute, so this won't work with all Selenium drivers; it will be dependent on which browser you're running in. But it apparently works on all major Web browsers.
See Get HTML Source of WebElement in Selenium WebDriver using Python for a related question using Python, which has more discussion about whether the innetHtml attribute will work in all browsers. If it doesn't, Canopy also has the js function, which you could leverage to run some Javascript to get the HTML you're looking for -- but if you're having trouble with that, you probably need to ask a Javascript question rather than an F# question.

AngularJS dollar sign $() usage

I've been learning Angular and now I'm trying to understand a large piece of code that was given me, and it has a lot of $() in the code containing a variable inside and a method call $(variable_name).method() or even a CSS class inside, between double quotes $(".class_name").method().
I understand $scope well, but I get confused with the syntax I explained above. Can someone explain what is it? Thanks in advance. /Teo
P.S.: The code is a directive, so I assume JS don't have this syntax except for the Angular framework.
Angular uses a subset of jQuery called jqLite. Here you can read a documentation: https://docs.angularjs.org/api/ng/function/angular.element Using $() function is basically wrapping an element so you can call jqLite function on it an chaining them. In your particular example $(variable_name).method() will wrap a DOM node stored in variable variable_name with jqLite and then run method on it. $(".class_name").method() is another usage of $(). It works the same as querySelectorAll() but instead of collection of DOM nodes will return collection of jqLite wraped nodes and then do the same - run method on each of them.

How to turn text into DOM elements using Clojurescript? (And Om?)

In a ClojureScript/Om app, I have a DOM and a string of HTML.
How can I translate that string of HTML into elements I can insert into the DOM?
I've started down the path of parsing the HTML with hickory, planning to then process the hickory data to create DOM elements, but I think there must be a simpler way I'm overlooking.
(I don't need to validate the HTML, I can assume it's safe and valid enough.)
You don't need to parse the HTML string. It's unnecessary overhead. React/Om supports something like DOM's innerHTML property. Just set the prop this way:
(om.dom/div #js {:dangerouslySetInnerHTML #js {:__html "<b>Bold!</b>"}} nil)
If you work with plain DOM without Om, set the innerHTML property like:
(let [div (. js/document getElementById "elId")]
(set! (. div -innerHTML) "<b>Bold!</b>"))
Aleš Roubíček answer is much better. I'll leave this un case it helps somebody.
Hickory offers a as-hiccup function. Hiccup uses Clojure data structures to represent HTML. You could feed those data structures to Clojurescript libraries that follow the same conventions:
Hiccups to generate regular DOM elements.
Sablono to generate React DOM elements and use with Om.
You could also use Kioo/Enfocus and instead of passing a file path, pass the string directly. This would be more direct and instead of using two libraries (Hickory + Sablono) you would use only one. The caveat is that Kioo and Enfocus follow the Enlive style of templating (which is great but has a learning curve) and the docs are focused on file paths and not strings (even though it is possible to pass strings).

How to set an attribute of a DOM element in Clojurescript?

I wish to set the "value" property of an "input" element using Clojurescript, but I am having trouble with the syntax of setProperties in goog.com. Has anyone got a working example?
Update
------
This seems to work:
(goog.dom.setProperties
(goog.dom/getElement "element-name")
(clj->js {:value "text"}))
If you need to create throwaway JS objects for use with JS APIs, you can do so directly using js-obj:
(js-obj "value" "text")
;; produces {"value": "text"} in the compiled output
Of course if you already have a ClojureScript map with the appropriate entries, clj->js will be more convenient.
More importantly, you might want to consider switching to a ClojureScript library for DOM manipulation. Several are available:
Luke VanderHart's Domina, which might have been the first one, is used by Enfocus (listed below) and Pedestal;
Prismatic's dommy, notable due to its own merits as well as the very entertaining blog posts about it on Prismatic's blog (which can serve as a great introduction to the benefits of macros: first one, second one, third one);
Creighton Kirkendall's Enfocus, which is in a nutshell an Enlive-like library for ClojureScript, which is awesome;
Kevin Lynagh's Singult, which is a Hiccup-style library for ClojureScript with cool functionality for merging in changes to the DOM, rather than rerendering from scratch.