nickcoxdotme
nickcoxdotme

Reputation: 6697

Rails controller renders JSON in browser

I have a simple controller that I have responding to both html and json. I'm using the json response for a Backbone app. Everything works as expected, except that when I click a link that uses the show method, and then click the back button, the index method just prints a big string of JSON into the browser. If I refresh, it displays HTML as expected. Here's the controller.

class RecipesController < ApplicationController
  def index
    @user = User.find(params[:user_id])
    @recipes = Recipe.all

    respond_to do |format|
      format.html
      format.json { render json: Recipe.where(user_id: params[:user_id]).featured }
    end
  end
  ...
end

I tried adding a check for response.xhr?, and only rendering JSON if it was an AJAX request, but that didn't work.

Edit

This is a Rails 3 app not utilizing turbolinks.

Edit 2

Here is the relevant Backbone code.

# app/assets/javascripts/collections/recipe_list_.js.cofee
@App.Collections.RecipeList = Backbone.Collection.extend
  url: ->
    "/users/#{@userId}/recipes"
  model: window.App.Models.Recipe
  initialize: (opts) ->
    @userId = opts.userId


# app/assets/javascripts/app.js.coffee
$ ->
  urlAry = window.location.href.split('/')
  userId = urlAry[urlAry.length - 2]
  App = window.App
  App.recipeList = new App.Collections.RecipeList(userId: userId)
  App.recipeListView = new App.Views.RecipeListView

Upvotes: 8

Views: 1676

Answers (4)

Yossi Shasho
Yossi Shasho

Reputation: 3640

If you're referring to a chrome and turbolinks issue, then an easy fix is to disable caching on ajax requests:

$.ajaxSetup({cache: false})

Upvotes: 4

Benjamin Bouchet
Benjamin Bouchet

Reputation: 13181

I bet it's due to turbolink, or ajax based page rendering (backbone, remote=true, ...)

I always disable turbolink and keep control over which links are remote=true, and for all ajax response I insert this javascript line at the end

history.pushState(null, '', '/the/requested/url' );

If you don't want to manually implement this line for each of your link responses, you can wrap it in an ajax:complete event (more info), and I assume turbolink has an event you can use as well.

Second part of the trick is to bind popstate so when your users click on the "back" button the page will be refreshed from the server (through the url that was pushState-ed earlier) and the ajax/js/json/whatever response won't be displayed anymore.

setTimeout( function () {
  $(window).bind('popstate', function () {
    window.location = location.href;
  });
}, 500);

As you see I wrap the popstate event binding in a setTimeout, because if you don't do that you may have trouble with some browser that would infinitely refresh the page.

Upvotes: 3

hraynaud
hraynaud

Reputation: 736

Are you using Chrome? if so this is a known issue. When you hit the back button chromes serves the page from cache since what was returned was json that is what it dumps on the screen. This post has some suggested workarounds

https://code.google.com/p/chromium/issues/detail?id=108766

Upvotes: 2

somedude
somedude

Reputation: 154

you could try using /recipes.html and /recipes.json and /recipes/1.html and /recipes/1.json

instead of relying on backbone and history to always send the correct headers

Upvotes: 3

Related Questions