In Rails 3, how does one render HTML within a JSON response? - ruby-on-rails

I'm porting an application from Merb 1.1 / 1.8.7 to Rails 3 (beta) / 1.9.1 that uses JSON responses containing HTML fragments, e.g., a JSON container specifying an update, on a user record, and the updated user row looks like . In Merb, since whatever a controller method returns is given to the client, one can put together a Hash, assign a rendered partial to one of the keys and return hash.to_json (though that certainly may not be the best way.) In Rails, it seems that to get data back to the client one must use render and render can only be called once, so rendering the hash to json won't work because of the partial render.
From reading around, it seems one could put that data into a JSON .erb view file, with <%= render partial %> in and render that. Is there a Rails-way of solving this problem (return JSON containing one or more HTML fragments) other than that?
In Merb:
controller:
only_provides :json
...
self.status = 204 # or appropriate if not async
return {
'action' => 'update',
'type' => 'user',
'id' => #user.id,
'html' => partial('user_row', format: :html, user: #user)
}.to_json
In Rails:
controller:
respond_to do |format|
format.json do
render template: '/json/message-1',
locals: {
action: 'update',
type: 'user',
id: #user.id,
partial: 'user_row.html.erb',
locals: { user: #user }
}
end
end
view: json/message-1.json.erb
{
"action": <%= raw action.to_json %>,
"type": <%= raw type.to_json %>,
"id": <%= raw id.to_json %>,
"html": <%= raw render(partial: partial, locals: locals).to_json %>
}

The closest to the original from Merb approach I could find in Rails is to use #render_to_string
render json: {
'action' => 'update',
'type' => 'user',
'id' => #user.id,
'html' => render_to_string(partial: 'user_row.html.erb', locals: { user: #user })
}
This gets around a fair bit of complexity that comes in from adding a layer of json.erb templates into the mix, whether it's Rails Purist approach I couldn't say; possibly something with RJS would typically be used.

There's another question that has more solutions for json.erb files. See json erb template cannot find other html partial

class UsersController < ApplicationController
respond_to :json
def show
#user = User.find(params[:id])
respond_with(#user) do |format|
if #user.save
format.json { render :json => #user }
else
format.json { render :json => #user.errors, :status => :unprocessable_entity }
end
end
end
end

Related

nocontent returned from controller action that calls helper rendering a partial

I am basically trying to use a helper method that renders a partial in my controller and getting a nocontent errors or 500 errors "no template" depending on the variations I'm trying. If I just run the code in the helper directly from the controller action, all is well. It seems like the rails guides don't touch on this (using a helper method that does the rendering, as opposed to directly from the controller action). Unfortunately, I don't have the luxury right now to refactor it out because the helper is used in other places, and it seems to me if there is a way to use this helper in the controller action also, that's the better option.
Anyone know how to do this? Nothing I am trying is working for me :S
Helper method
def render_call_orders_filters url_or_path_method, query_params = #query_params
render :partial => 'call_orders/call_orders_filters', :locals => {url_or_path_method: url_or_path_method, query_params: query_params}
end
Controller action
# GET /call_order/filters
def filters
respond_to do |format|
format.html {
# This (of course) works...
# render :partial => 'call_orders/call_orders_filters', \
# :locals => { url_or_path_method: method(:call_orders_path), query_params: #query_params }
# This does not
helpers.render_call_orders_filters(method(:call_orders_path))
# This gives me a "Template is missing" 500 error
renter text: helpers.render_call_orders_filters(method(:call_orders_path))
}
end
end
based on your last example code, there is render :partial => that works
I think it could be refactored like this
# The helper...
def orders_filters_param_to_template(url_or_path_method, query_params = {})
{
partial: 'call_orders/call_orders_filters', locals: { url_or_path_method: url_or_path_method,
query_params: query_params }
}
end
def render_call_orders_filters(url_or_path_method, query_params = #query_params)
template_data = orders_filters_param_to_template(url_or_path_method, query_params)
render partial: template_data[:partial],
locals: template_data[:locals]
end
# The controller action...
def filters
respond_to do |format|
format.html do
template_data = helpers.orders_filters_param_to_template(method(:call_orders_path), #query_params)
render partial: template_data[:partial],
locals: template_data[:locals]
end
end
end

Hacking RJS behaviour in Rails 5

I have a old rails 2.x project that I have converted mostly over to rails 5.
One issue is some of my actions used RJS, so it looked like:
if request.xhr?
render :action => 'new_user' and return
end
The new_user.js.rjs looks something like:
page.call "User.create", render(:partial => 'new_user'), {:userId => #user.id}
Looking at the response in chrome I can see it is just returning:
User.create('<tr><td>....</td></tr>', {"userId" : 123});
I only have to support the page.call type RJS call, what would be a easy "hack" to get this to work in rails 5?
I don't want to modify all of my javascript code, I just need to basically have a javascript block that I pass the JS code to in my view pages right?
Try to render a response as a string:
if request.xhr?
render render_to_string partial: 'new_user',
locals: { userId: #user.id },
layout: false
end
Or try to use format handler instead:
respond_to do |format|
format.rjs do
render render_to_string partial: 'new_user',
locals: { userId: #user.id },
layout: false
end
end
I ended up returning a JSON response to my view pages like this:
some_partial_html = render_to_string(:partial => 'something')
response = {
"html": some_partial_html,
"a" : 1
}.to_json
render json: response
And then in my view I used the json response values as arguements to the javascript object that performs the functionality I needed.

dynamic rendering based on data results

I am curious if this is even possible. I am looking to render data based on the results of an action in ruby on rails. Here is the code:
def convert
render :text => Item.convert(params[:file], params[:output])
end
As you can see currently it just renders text. The item model takes in two parameters: a file and output. The output could be xml, json, plain text, etc. Some type of data. What I was hoping I could do is that I could render the output based on the output parameter. But make it dynamic. I was hoping I could do:
def convert
#items = Item.convert(params[:file], params[:output])
respond_to do |format|
format.json { render :json => #items }
format.xml { render :xml => #items }
end
end
Something that would call the method and based on the data returned it would render the appropriate data. Testing the above code returns an empty page. The body tag is completely empty.
Any ideas?
thanks
Mike Riley
If you are relying on the name of params[:output] to determine what to render, I would write is as below:
def convert
#items = Item.convert(params[:file], params[:output])
if File.extname(params[:output].match("text"))
render :text => Item.convert(params[:file], params[:output])
elsif File.extname(params[:output].match("json"))
render :json => Item.convert(params[:file], params[:output])
elsif File.extname(params[:output].match("xml"))
render :xml => Item.convert(params[:file], params[:output])
else
render :html => "default_html"
end
end
and you should have a default_html.html.erb that you would render if the extension name of the file does not match what is expected

Adding strings to Rails JSON output

I need to output custom JSON in order to be backwards compatible with existing software, which is written in Javascript, so I need to wrap my JSON with "parseDate(" at the beginning of it, and ");" at the end of it.
I tried doing it in controller like this
def index
#data = Data.all
#products = Product.all
respond_to do |format|
format.html
format.json {render :json => { :products => {:product => #data.name}}}
end
end
And then specify it in the view:
app/views/products.json.erb
<%= p "parseData(" %>
<%= render :json %>
<%= p "};" %>
But it outputs pure JSON completely skipping both "parseData(" and ");", why? How do I make JSON to be printed in the middle of the view and then append strings to the top and bottom?
JSON renderer supports a callback option.
format.json { render :json => { :products => {:product => #data.name }}, :callback => 'parseDate' }
You can read the implementation in the renderer.rb source code.

Ruby data structure to render a certain JSON format

[
{
"id":"123",
"name":"House"
},
{
"id":"1456",
"name":"Desperate Housewives"
},
{
"id":"789",
"name":"Dollhouse"
},
{
"id":"10",
"name":"Full House"
}
]
How can I render to produce this JSON format from within Ruby? I have all the data from the DB (#result) and don't know what data structure to use in Ruby that will render to this when I do this:
respond_to do |format|
format.json { render :json => #result}
end
What data structure should #result be and how can I iterate to produce it? Thank you!
If #result is an array of ActiveRecord model instances then render :json => #result will produce something like what you are after, but will include all the attributes of the model (render calls to_json on the object you pass it unless it is a string).
To only include the id and name attributes, you can use the :only parameter of to_json:
respond_to do |format|
format.json { render :json => #result.to_json(:only => [:id, :name] }
end
Alternatively, you can create a array of Hash objects that only contain the required attributes:
respond_to do |format|
format.json { render :json =>
#result.collect {|o| {:id => o.id, :name => o.name} } }
end
Edit: See #dt's comment below. There is an attribute in the model named text that needs to be output as name. This can be done by creating an alias for text in the model:
class Model < ActiveRecord::Base
alias_method :name, :text
and including the name using :methods:
respond_to do |format|
format.json { render :json => #result.to_json(:only => :id, :methods => :name }
end
Alternatively, the array of hashes approach can be used to rename the attribute:
respond_to do |format|
format.json { render :json =>
#result.collect {|o| {:id => o.id, :name => o.text} } }
end
Try using the json gem. It will allow to you do things like
#result.to_json
to convert your structures (say, a Hash) to json format. If you're using Ruby on Rails, this functionality should already be accessible to you, so just create a Hash and call to_json.
To create a JSON object of that particular format, you need a ruby array containing hashes for its elements.
#result = [{:id => "10", :name => "Full House"}, {:id => "789", :name => "blahblah"},...]
Rails will convert the ruby array to json correctly in your render response block:
respond_to do |format|
format.json { render :json => #result}
end
I was using a jQuery plugin (FCBKComplete) that needed the json results with specific key names, specifically 'caption' and 'value', which did not exist in the array I was calling to_json on.
So I did this (hacky, but it works):
render :json => taggings.map { |t| {:caption => t.tag.name, :value => t.tag.name} }.to_json
Where taggings is an array returned by an ActiveRecord find, and that returns json like so:
[{"value":"tag.a","caption":"tag.a"},{"value":"tag.b","caption":"tag.b"}]

Resources