MiServer: navigating with jsTree

MiServer is Dyalog's APL-based web development framework

MiServer: navigating with jsTree

Postby StephenTaylor on Thu Feb 20, 2014 8:14 pm

I have navigation working from a jsTree using the following JS, which I'm trying to replicate using the Dyalog plugin classes:

Code: Select all
$(document).ready(function () {
   $("#navtree").jstree(
      {"plugins" : [ "themes", "html_data", "ui", "cookies" ]}
      ).on("select_node.jstree", function (event, data) {
         $.post("",
         {
            what: data.rslt.obj.attr("id")
         },
         function(data,status) {
            $("#browser").html(status==='success'?data:'<p class="error">Server error</p>');
         });
      })
   ;
});


Seems to me in MiServerthat's:

Code: Select all
      plugins←'"plugins" : [ "themes", "html_data", "ui", "cookies" ]'
      html,←req #.JQO.jsTree 'navtree' levels Items plugins
      html,←req #.JQ.On ('#navtree' 'li') 'select_node.jstree' ('attr' 'id') '#browser'


But I don't seem to be getting reactions to the select_node.jstree events.

If I omit the delegate 'li' I get an event and APLJax fires, but the id returned is #navtree, not the individual list item.

Anyone been here before me?
User avatar
StephenTaylor
 
Posts: 31
Joined: Thu May 28, 2009 8:20 am

Re: MiServer: navigating with jsTree

Postby Brian|Dyalog on Mon Feb 24, 2014 6:17 pm

It's nice to see people making use of some of MiServer's jQuery-based widget interfaces.
As it turns out, jsTree binds the listener for the select_node.jstree event at the tree level, not the node level.
That's why using the 'li' delegate in the JQ.On selector parameter wasn't firing.

One common jQuery event handling convention is to pass data to the handler using the event object and that's what JQ.On was coded to expect.
After a bit of investigation, I found that jsTree uses a separate data object that JQ.On wasn't picking up.
http://old.jstree.com/documentation/core describes the data object...
    {
    "inst" : /* the actual tree instance */,
    "args" : /* arguments passed to the function */,
    "rslt" : /* any data the function passed to the event */,
    "rlbk" : /* an optional rollback object - it is not always present */
    }
It was fairly easy to modify JQ.On to accommodate this.
I've updated the MiServer zip file at http://tools.dyalog.com/library/ with this change.

Here's a page that demonstrates how to access the node level id attribute when a node is clicked.

Code: Select all
:Class tree : MiPage
    :field public event
    :field public what
    :field public clicked←''

    :include #.HTMLInput

    ∇ r←Render req;levels;items
      :Access public
      levels←1 2 3 3 3 2 3 3 2 3
      items←levels{⍕'Item'⍵'Level'⍺}¨⍳⍴levels
      r←req #.JQO.jsTree'mytree'levels items'"plugins" : [ "themes", "html_data", "ui", "cookies" ]'
      r,←req #.JQ.On'#mytree' 'select_node.jstree'('clicked' 'eval' 'data.rslt.obj.attr("id")')
    ∇

    ∇ r←APLJax req
      :Access public
      r←''
      ⎕←clicked
    ∇
:EndClass

JQ.On's third parameter specifies any additional data you want to be sent to the server when the event is triggered.
      ('clicked' 'eval' 'data.rslt.obj.attr("id")')
sends an object named "clicked" which contains the id attribute of the node that was clicked.
On the server side, we just display this to the APL session (you'd probably want to do something more useful :))

I found there's a newer version of jsTree (v3.0) that passes data a bit differently.
The example above uses version 1.0 which is currently distributed with MiServer.
When MiServer 3.0 is released later this year, we will probably use the latest version jsTree.
User avatar
Brian|Dyalog
 
Posts: 116
Joined: Thu Nov 26, 2009 4:02 pm
Location: West Henrietta, NY

Re: MiServer: navigating with jsTree

Postby StephenTaylor on Thu Feb 27, 2014 12:37 pm

Brian, thanks -- most helpful.

Moving on, I've modified my original design. That was to handle the app from a single MiPage, divided into #navtree and #panel. Navigating with the jsTree would (via APLJax) present the corresponding app module in #panel. But that loads a lot of code into the Index class. And it precludes navigating the app by URL, which is too useful to give up. So I will follow the Intro design and write a class for each module.

That means promoting the jsTree into the skin. And that introduces an issue with binding to JavaScript that is opaque to me. Here's a simple way to see it. Three steps.

(1) I've made my own skin SvPage. It wraps req.Response.HTML as #panel and catenates it to #navtree, in which it has composed the jsTree example from the MiServer Intro site. Thus:

      ___
:Class SvPage : MildPage
...
∇ Wrap req;body;head;html;footer;banner;content;lang;levels;items
⍝ "Wraps" the HTML body
...
levels←1 2 2 1
items←'item'∘,¨⍕¨⍳⍴levels
content,←req #.JQO.jsTree 'navtree' levels items
content,←'div id="panel"'Enclose req.Response.HTML
content←'div id="contentblock"'Enclose content
...

...
:EndClass

In the page class compose another jsTree. (I know. Stay with me here.)
      ___
:Class Index : SvPage

:Include #.HTMLInput

∇ Render req
:Access Public
levels←1 2 2 1
items←'item'∘,¨⍕¨⍳⍴levels
req.Return req #.JQO.jsTree 'tree' levels items

...
:EndClass

Result is two jsTrees, one in #navtree, the other (#tree) is in #panel. All is well.

(2) Replace the jsTree in Index with a P:
      ___
:Class Index : SvPage

:Include #.HTMLInput

∇ Render req
:Access Public
levels←1 2 2 1
items←'item'∘,¨⍕¨⍳⍴levels
⍝ req.Return req #.JQO.jsTree 'tree' levels items
req.Return 'p' Enclose 'Hello folks!'

...
:EndClass

Now the #panel display is the simple para, but the jsTree in #navtree has become a simple UL. No script bound to it. Yuk.

(3) Lastly, remove the lamp in the Index script. The #panel display remains the same, but the JS binding to the jsTree in #navtree has been restored. Clearly the JS binding works in the page script but not in the skin script.

This is puzzling. The req left argument to #.JQO.jsTree is a pointer, so you'd think this would work in either context. I can't see in the source (in #.JQO and #.JQ) an explicit binding to context. And I haven't figured out how to trace into MiServer's execution. (Too long away from Dyalog no doubt. Perhaps someone at BAA London tomorrow will show me.)

For the record, the JS I mean to emulate for #navtree is at

http://www.linuxia.de/blog/jsTree-Navigation-Delegate-Links

Stephen
User avatar
StephenTaylor
 
Posts: 31
Joined: Thu May 28, 2009 8:20 am

Re: MiServer: navigating with jsTree

Postby Brian|Dyalog on Sun Mar 02, 2014 12:27 am

Hi Stephen!

When you pass req as the left argument to #.JQO.jsTree, it adds the necessary JavaScript links to req.Response.HTMLHead.
You didn't show all the code for your Wrap function, but I suspect it has already constructed the <head> element (with code something like 'head' Enclose req.Response.HTMLHead) and the the call to build navtree is later in the code, so the links aren't appearing in the <head> element.

There are a couple of ways to fix this:
  • Add
          req.Use 'jquery.jstree'
    to the top of the Wrap function
  • OR Move the block of code where you construct the content above the call that constructs the <head> element.

I hope this helps!

/Brian
User avatar
Brian|Dyalog
 
Posts: 116
Joined: Thu Nov 26, 2009 4:02 pm
Location: West Henrietta, NY

Re: MiServer: navigating with jsTree

Postby StephenTaylor on Mon Mar 03, 2014 5:22 pm

Thanks Brian, helpful and relevant again.

This thread now entangles the discussion of MiServer tools at BAA London last Friday.

I have customised the #navtree node icons to aid navigation:

svx.png
Screenshot of SVX app prototype UI

There are (at least) four ways to do this in jsTree:

  1. load the tree with JSON with icons assigned
  2. load the tree with HTML with icons assigned in JSON
  3. load the tree with HTML with icons assigned as CSS classes
  4. write JS functions
Failed with (2) but got (3) to work.

So far, so good. What's the source look like?

SvPage.Wrap has slight mods to MiPage.Wrap:

      ∇ Wrap req;body;head;html;footer;banner;content;lang;html
⍝ "Wraps" the HTML body
⍝ This version implements a template to create a standard look and feel for all MildPages served by this site
⍝ This is provided as an example for some of the uses of Wrap

:Access Public
req.Use'JQuery'
req.Use'jquery.jstree'
head←'title'Enclose req.Server.Config.Name ⍝ Sets the name of the page at the top of the browser to the Name specified in server.xml
head,←Tag'meta http-equiv="content-type" content="text/html;charset=UTF-8"' ⍝ make it UTF-8
head,←Tag'link href="/Styles/style.css" rel="stylesheet" type="text/css"' ⍝ add the style sheet reference

⍝↓↓↓ If your code that builds the page updates HTMLHead (content that goes in the <head> element of the HTML document) it gets appended here.
⍝ This means that any style changes introduced by your code will override those in the /Styles/style.css for elements defined by both stylesheets
head,←req.Response.HTMLHead,CRLF ⍝ Adds additional html head information
head←'head'Enclose head

⍝ The design for this template implements a structure for the content within the body of the HMTL file
⍝ The <body> element encloses three <div> elements containing: a banner, the content, a footer
banner←#.Files.GetText req.Server.Root,'Styles\banner.txt'
html←''
html←#.Files.GetText #.Boot.AppRoot,'Data\navtree.html' ⍝ FIXME dynamic
html,←JS #.Files.GetText #.Boot.AppRoot,'Scripts\navtree.js'

content←'div id="contentblock"'Enclose html, 'div id="panel"' Enclose req.Response.HTML
...

Notice I'm not using #.JQO.jsTree, but instead my own navtree.js:

      //
// http://www.linuxia.de/blog/jsTree-Navig ... gate-Links
$(function () {
$("#navtree").jstree({
"plugins" : [ "cookies","html_data","themes","ui" ],
"themes" : { "theme" : "classic" }
});
$("#navtree").delegate("a", "click", function (e) {
if ($("#navtree").jstree("is_leaf", this)) {
document.location.href = this;
} else {
$("#navtree").jstree("toggle_node", this);
}
});
});

Two reasons.

First, in this instance, I don't need the AJAX. Each node will either toggle or GET. Coaxing the JQO tree to stop making AJAX calls looked at least as much work as writing the required JS, which, as usual, only meant adapting someone else's.

Second, the only way I could see to customise the JQO tree's icons was to write JS to change them.

Thanks for bearing with me this far. My point (at last) is:

  1. I couldn't get this done without grappling with JS
  2. When I had to do so, that sweet little JQO tree did nothing to help me on my way
In fact, I wasted a bit of time trying to see if there was something in JQO.jsTree I should be using.

JQO.jsTree is sweet to use and it's clear what it does for you. If that's what you need you can just CPE: copy, paste, execute. But if you need more you have to dive in to both JS and JStree, and then you start from scratch, because nothing in JQO.jsTree relates to the docn and concepts there.

As a general principle, I'd try to design the syntax of APL wrappers to expose and clarify the encapsulated complexity. And, brutally, when Joe APLer is doing CPE, it doesn't matter whether the code looks APL-ish or not.

Best
Stephen
User avatar
StephenTaylor
 
Posts: 31
Joined: Thu May 28, 2009 8:20 am


Return to MiServer

Who is online

Users browsing this forum: No registered users and 1 guest