yonicd / jsTree

R htmlwidget for inspecting heirachal structures with the ‘jQuery’ ‘jsTree’ Plugin.
https://yonicd.github.io/jsTree/
Other
31 stars 7 forks source link

prevent child nodes to be selected when clicking on parents #11

Closed dylancis closed 6 years ago

dylancis commented 6 years ago

Hi @yonicd , thanks very much for your support on this. There is not final thing I wanted to discuss with you: the fact that when clicking on the parent note, all the childrens get also selected while I did use core = list(multiple = FALSE). How can I make sure that you can only select the deeper node and making impossible selection of parent ?

image

yonicd commented 6 years ago

not sure - the answer is probably on stack overflow

dylancis commented 6 years ago

You are right, this setting is exposed via core = list(data = list(state = list(opened = TRUE, disabled = TRUE))) indeed but reading at your documentation, I understood the core.data is not really exposed via the R jsTreemethod. as seen in stackoverflow Is it correct ? Thank you, Dylan

yonicd commented 6 years ago

You cant really intervene in core.data, but there are plugins that would do the same thing. Eg conditionalselect i think

dylancis commented 6 years ago

indeed, so I will wait for the pluggin feature then, thank you.

dylancis commented 6 years ago

Hi @yonicd , I have puleld the R6_api and while all the plugins work well, I have a problem with one of them related to this issue here. conditionalselect seems to return false all the time whatever condition I tried - see example below where I was hopping 'South' will not be selectable but instead none of them are:

library(jsTree)
data(states)
data(state_bird)

x <- jsTree::jsTree$new()
x$data <- states
x$active_plugins.append(c('conditionalselect'))
x$plugins[['conditionalselect']] <- "function (node) { return node.text == 'South' ? false : true; },"
# show the widget
x$show()

Any idea how to make this plugin works? I have also try surrounding my javascript function by JS()function but it failed in R instead with the following message printed: _Error: No method asJSON S3 class: JSEVAL

The general idea was to replicate this fiddle but with a condition of the number of children the node are to prevent selecting parent node.

Thank you for your help, Dylan

yonicd commented 6 years ago

i also tested this out and cant get it to react. There is probably a js problem.

Regarding the error message. I updated the repo to accept unnamed list objects. This passes through ok now.

tree$plugins[['conditionalselect']] <- 
  list(htmlwidgets::JS("function (node) { return node.text == 'South' ? false : true; }"))
dylancis commented 6 years ago

Hi @yonicd - I have made further testing and I beleive the JS feature works week since I can make this fiddle works just fine: try here I have now used JS() function with your updated repo installed and when I open up developer tools I can locate the jstree log error: image And from the console I can see: send @ jquery.min.js:4 6VM147:4 Uncaught TypeError: this.settings.conditionalselect.call is not a function at a.jstree.plugins.conditionalselect.activate_node (<anonymous>:4:23683) at a.jstree.plugins.conditionalselect.<anonymous> (<anonymous>:2:5707) at HTMLAnchorElement.e (jquery.min.js:2) at HTMLDivElement.dispatch (jquery.min.js:3) at HTMLDivElement.q.handle (jquery.min.js:3)

yonicd commented 6 years ago

it seems like the js on the R side isnt transpiling the function the JS(). If I hard code it in I get the right tree functionality.

var tree = $('.jstree' + el.id).jstree({
        'core' : x.core,
        'plugins'           : x.active_plugins,
        'checkbox'          : jQuery.extend(x.plugins.checkbox          , x.jsplugins.checkbox),
        'contextmenu'       : jQuery.extend(x.plugins.contextmenu       , x.jsplugins.contextmenu),
        'dnd'               : jQuery.extend(x.plugins.dnd               , x.jsplugins.dnd),
        'massload'          : jQuery.extend(x.plugins.massload          , x.jsplugins.massload),
        'search'            : jQuery.extend(x.plugins.search            , x.jsplugins.search),
        'sort'              : jQuery.extend(x.plugins.sort              , x.jsplugins.sort),
        'state'             : jQuery.extend(x.plugins.state             , x.jsplugins.state),
        'types'             : jQuery.extend(x.plugins.types             , x.jsplugins.types),
        'unique'            : jQuery.extend(x.plugins.unique            , x.jsplugins.unique),
        'wholerow'          : jQuery.extend(x.plugins.wholerow          , x.jsplugins.wholerow),
        'changed'           : jQuery.extend(x.plugins.changed           , x.jsplugins.changed),
        // 'conditionalselect' : jQuery.extend(x.plugins.conditionalselect , x.jsplugins.conditionalselect)
        'conditionalselect' : function (node) { return node.text == 'South' ? false : true; }
      })

If I keep just x.jsplugins.conditionalselect i get a function back, that isnt useful which causes the errors you see.

screen shot 2018-07-23 at 8 46 32 am

I am probably misinterpreting how to use JS(), although I did get it to react properly on the contextMenu plugin.

dylancis commented 6 years ago

Hi @yonicd - With a developer colleague of mine, we dig into the issue and we understood why this is no working as expected. While contextemenu has some child attributes such as items, which makes the list()wrap up appropriate, conditionalselect does not have any.

Which means that when inspected the object the function is wrapped up in a dictionary where key: 0. image

Insead if we execute the function by slicing the key '0' in the JS console, it works: this.settings.conditionalselect[0].call(this, {text: 'South'}) image

The solution would be to handle the plugins which don't have any attributes (such as items or any other) differently to allow the function to not be wrapped up in a dictionary.

yonicd commented 6 years ago

good catch. i'll see how i can split the two cases

dylancis commented 6 years ago

Hi @yonicd , can I help on anything here ? I could hard code that rule for conditionalselect but was hoping we can come out with a smarter way of doing it if other plugin have the same design.

yonicd commented 6 years ago

I got a bit farther.

I got the js.plugin to work correctly, but got stuck on another js thing. the $.extend is too aggressive and strips out the attributes of the js.plugin.

The purpose of using that was to contcatentate into a single object that goes into the element conditionalSelect.

yonicd commented 6 years ago

https://github.com/metrumresearchgroup/jsTree/commit/0cd67bb14bbcff7e2e08af9b4e5ac671c7b1b5a2