Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

How would you render a tree ? #70

Closed
simonc opened this Issue · 13 comments

6 participants

@simonc

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 !

@nesquena
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.

@simonc

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 ?

@jasonvasquez

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.

@nesquena
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

@jasonvasquez

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!)

@nesquena
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
@jasonvasquez

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!

@robmathews

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)

@nesquena
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.

@nesquena nesquena closed this
@mockdeep

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?

@nesquena
Owner

Can you try:

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

and see how close that gets you?

@mockdeep
@vibgy

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.