views:

1436

answers:

2

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 %>
}
A: 
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
jpartogi
I'm probably missing the point: the only way I can see in this example to get HTML fragments into the JSON would be to implement #to_json on User and have it render partials, which would presumably run afoul of the double render problem and, even if not, wouldn't allow for different fragments to be used in different contexts, e.g., user_row v. user_detail. Is there something in here I'm not getting?
ylg
A: 

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.

ylg