Skip to content
This repository

How would you render a tree ? #70

Closed
simonc opened this Issue July 19, 2011 · 13 comments

6 participants

Simon Courtois Nathan Esquenazi Jason Vasquez rob mathews Robert Fletcher vibgy
Simon Courtois
simonc commented July 19, 2011

Hi,

I have a folders tree in my application and I would like to know how I could render it in json with rabl.
I guess I'll have to use a partial but I'm not sure how.

Any idea ?

Thanks !

Nathan Esquenazi
Owner

First decide how you'd like the final JSON to look in the API. Perhaps something like:

{ 'folder' => { 'subfolder' =>  { ... } }

Once you decide the JSON response you want, how you craft it in RABL will be im sorry to say probably very custom (and could use partials or not). It could be something like:

object false

node :tree do |m|
   @tree.folders.map { |folder| # ... build a hash here ... }
   # Really this can be anything, array, hash, etc 
end

Presumably that method might be elaborate but essentially my guess is a tree is going to be pretty custom so it amounts to just constructing the hash yourself either in the model or in the rabl output. I am not sure of a better way to do it unless you describe the breakdown of your models and/or the expected json output.

Simon Courtois
simonc commented July 20, 2011

Ok, some more infos:

I use Mongoid::Tree to get the tree structure on the model side.
The simple JSON output I want for now should look like this:

[{ folder:
   {
     name: "some-name",
     children: [{ folder:
       {
          name: "some-sub-folder",
          children: []
       }
    }]
  }
},
{
  folder:
  {
    ...
  }
}]

For HTML it's just a simple partial calling itself. I was thinking about the same thing with rabl but it's maybe not the right way to do it with rabl.

What do you think ?

Jason Vasquez

I have a similar situation, with arbitrarily deep nesting. The model is like:

class Node < ActiveRecord::Base
  has_many :children, :class_name => "Node", :foreign_key => "parent_id"
end

I'd like to render something like:

{"node":{
  "id":1,
  "name":"RootNode",
  "children":[
    {
      "id":2,
      "name":"Child 1 Level 1",
      "children":[
        {
          "id":3
          "name":"Child 1 Level 2",
          "children":[
            {
              "id":6
              "name":"Child 1 Level 3"
             }
          ]
         }
      ]
    },
    {
      "id":4,
      "name":"Child 2 Level 1",
      "children":[
        {
          "id":5
          "name":"Child 2 Level 2"
         }
      ]
    }
  ]
}

Is something like that possible? The solution doesn't seem immediately obvious to me.

Nathan Esquenazi
Owner

Couldn't this work with a recursive extends?

# node/show.json.rabl
attributes :id, :name
child :children => :children do
  extends "nodes/show"
end

Just an experiment to try, let me know if this works at all

Jason Vasquez

This is very close! The only issue is that the nested children are all named "child", e.g. (pseudo):

node: {
    children: [
        child: {
            children: [ ]
        }
    ]
}

The desired output would be:

node: {
    children: [
        node: {
            children: [ ]
        }
    ]
}

The template looks like:

object @root_node => :node
attributes :id, :name, :parent_id
child :children => :children do
  extends "molecule/index"
end

Thoughts? (Thanks for the quick follow-up!)

Nathan Esquenazi
Owner

Alright how about:

object @root_node => :node
attributes :id, :name, :parent_id
child :children => :nodes do
  extends "molecule/index"
end

I think we are getting close :)

If not maybe a bit more manual with "node":

object @root_node => :node
attributes :id, :name, :parent_id
node :children do |n|
   n.children.map { |c| { :node => partial("molecule/index", :object => c) }  }
end
Jason Vasquez

Ah hah! The 2nd form is perfect. I had tried several permutations of something similar, but had not hit upon the correct thing. :)

(The 1st form results in what was desired to be 'children' being named 'nodes')

Thanks again!

rob mathews

Maybe update the docs? It's completely non-obvious how to do this, and I spent more than 2 hrs hacking around before I read this, and even this explanation is less than complete (ie, where did the template "molecule/index" come from? Is it the same as this template, or is it a different template only used for rendering the child and lower levels)

Nathan Esquenazi
Owner

Added the docs to link to this ticket in case others need to render a tree. If anyone can help with a wiki page for rendering a tree, I will link there instead.

Nathan Esquenazi nesquena closed this March 28, 2012
Robert Fletcher

Alright, so I'm trying to do something similar, but I don't have a root node, just a group of nodes, each of which is the root of a tree. Here's what I have so far:

# the root page, index.rabl
object false    

@elements.map do |element|    
  { :element => partial("elements/tree", :object => element) }    
end
# the partial, _tree.rabl
attributes :id, :title

node :children do |element|
  element.children.map do |child_element|
    { :element => partial("elements/tree", :object => child_element) }
  end 
end

The interesting thing is, if I print out the result in the log, it looks exactly like what I want, but then by the time it gets rendered to the client, I get an empty json object, {}. Any ideas what's going wrong?

Nathan Esquenazi
Owner

Can you try:

# index.rabl
collection @elements, :root => false, :object_root => "element"
extends "elements/tree"

and see how close that gets you?

Robert Fletcher
vibgy
vibgy commented June 30, 2013

Can someone please suggest changes in my code so that I can get the desired layout:

#desired output:
{"length":2,
 "agents":[
            {"object":"agent","firstName":"Sallie","lastName":"Bla","email":"agent@dantler.us","agentId":1},
            {"object":"agent","firstName":null,"lastName":null,"email":"new@dantler.xa","agentId":2}
             ]}

#what I'm getting is this:
{"length":2,
 "agents":[
            {"agent":{"object":"agent","firstName":"Sallie","lastName":"Bla","email":"agent@dantler.us","agentId":1}},
            {"agent":{"object":"agent","firstName":null,"lastName":null,"email":"new@dantler.xa","agentId":2}}
             ]}

# agents.rabl
object false
node (:length) {|m| @agents.length}

child @agents => "agents" do |x|
    extends 'agent'
end

# agent.rabl
object @agent => ""
node do |x|
{
    :object => "agent",
    :firstName => x.first_name,
    :lastName => x.last_name,
    :email => x.email,
    :agentId => x.id
}
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.