ckirkendall / enfocus

DOM manipulation and templating library for ClojureScript inspired by Enlive.
http://ckirkendall.github.com/enfocus-site/
370 stars 41 forks source link

Clone-for and lists #107

Closed karneaud closed 8 years ago

karneaud commented 8 years ago

I'm new to clojurescript and I'm trying to follow your tutorials

i have the following markup in html

[:div#questions-panel.panel
  [:h3.question [:span.text]]
  [:ol#answers.answers
    [:li.answer {:value 1} [:p.text]]
  ]
]

I have some json data converted into arraymap


 [ {"id":1, "answer": 2, "text":"What is your name",
      "answers": [
        {
          "id": 1,
          "text": "This an answer"
        },
        {
          "id": 2,
          "text": "This an another answer"
        },
        {
          "id": 3,
          "text": "This a <b>3rd answer</b>"
        },
      ]
    }]

on bind/view I do the following to some data

(defn set-question-answers
  [n data]
(ef/at n ["#answers > .answer:first-child"]
      (em/clone-for [q data]
        ".text" (ef/content (:text q))
        ;; (ef/set-attr :value (:id q))
      ))
  )

(set-question-answers n (:answers data))

I'm trying to clone the [:li.answer ... to output more but it doesn't seem to work.... by my understanding the clone-for clones the node ref for each item in the list and adds it to the dom right? so then shouldn't this work?

ckirkendall commented 8 years ago

I think the problem is the selector. Have you tried just placing one node at that selector? The selector you have looks for an id of #answers then looks for .answers in its descendants. Since .answers is on the same node as #answers I don't think that will select anything. If you put together a little github project that show the issue I can help debug it.

karneaud commented 8 years ago

@ckirkendall I believe you might be incorrect.... I'm not looking for ol.answers I'm looking for li.answer which is a descendant of ol#answers.answers

Based on your example I need to clone li.answer for every item in the collection

Or is it that i need to elect the parent to clone the child?

ckirkendall commented 8 years ago

Is there a way you can put together a small project with this in it. I can help debug it. I have found it to be a lot easier than trying to do it over text. I believe you are right I read the selector wrong. I am not sure why it is not working for you.

karneaud commented 8 years ago

Here is a repo

ckirkendall commented 8 years ago

I was able to figure out what was going on. The core of the issue is the page is rendering before questions comes back and current-question is nil. So it deletes the first child then when the data does arrive there is no node to clone. I am going to restructure you code a bit and submit a pull request to fix a couple of other issues I noticed also. You need to be using a template to store the list structure to avoid this.

ckirkendall commented 8 years ago

Also note that you may find it a lot easier just send html files vs storing them all as hiccup. Something like enlive mirrors enfocus on the server allowing you to work with raw html templates and selectors.

karneaud commented 8 years ago

@ckirkendall thanks I'm new to clojure/ clojurescript would you mind summarising what you did there especially with the "atoms" part as I'm still trying to wrap my head around these "state" like scenarios.

I agree with the html part and loading times. I'm going to retry refactoring the code to output html and use static html.

I need to separate html form code as I will be handing the final project over to a designer.

Thanks in advance.

karneaud commented 8 years ago

I was unaware that defsnippet works with selectors as oppose to the html file based on the example in your documentations. Thanks for this.

karneaud commented 8 years ago

@ckirkendall I had one more question that I had forgot to ask

in the logic

[data]
  ".answer" (em/clone-for [q data]
              "." (ef/set-attr :value (:id q))

If I wanted to set the node in context usually in javascript it would be the this keyword How would i reference the context node "answer" in order to apply the ef/set-attr to it?

tried "//.", "." and [this] but doesn't seem to work

ckirkendall commented 8 years ago

It is not document well but you can use ef/this-node as the selector. https://github.com/ckirkendall/enfocus/blob/master/src/cljs/enfocus/core.cljs#L763

karneaud commented 8 years ago

Aaaaahhhhh....I see thanks

karneaud commented 8 years ago

Ok I tried your suggestion

(em/clone-for [q data]
                ".text" (ef/do->
                  (ev/listen-live :click ".text" check-is-answer)
                  (ef/html-content (:text q))
                  )
                (ef/this-node (ef/set-attr :value (:id q)))

But I get the following

1 This an answer for second question 1 This is the SECOND answer 1 This a 3rd answer

which is wrong as the list should be numbered in sequence

I try

 (ef/this-node (ef/set-attr :value (:id q)))
.text" (ef/do->
                  (ev/listen-live :click ".text" check-is-answer)
                  (ef/html-content (:text q))
                  )

I get the error

Uncaught Error: No protocol method ISelector.select defined for type enfocus.core/t_enfocus$core27385: [object Object]

Seems the ef/this-node shifts the context of current node. is this a bug? If not how do I get back into the current node?

I thought (ef/at... would have worked but no dice.

ckirkendall commented 8 years ago

It is not a function call.

(em/clone-for [q data]
    ".text" (ef/do->
                   (ev/listen-live :click ".text" check-is-answer)
                   (ef/html-content (:text q)))
     ef/this-node (ef/set-attr :value (:id q))
karneaud commented 8 years ago

Wow...that explains a lot.....but if it is not a function call why is it being defined as defn

ckirkendall commented 8 years ago

@karneaud - There is a ISelector protocol that is extended to functions so you can define custom selectors that will take a root node. Transforms also just return functions that take a node or a set of nodes.