skip to Main Content

I’m in the process of migrating a legacy Rails 5.0 app to (hopefully) 7.x. I got to 5.2.x without too much trouble. I’m currently trying to upgrade to 6.0 and I have a problem with controller actions that are set up to render either js or html depending on the type of request. For example, in a standard ‘new’ action of the general form:

def new
  @post = Post.new
  respond_to do |format|
    format.js
    format.html
  end
end

if a jQuery ajax request is sent with dataType: 'script', the server renders the views/posts/new.html.erb template instead of views/posts/new.js.coffee. The console confirms that the request is received as a js request but that html is being rendered, e.g.

Started GET "/posts/new" for 127.0.0.1
Processing by PostsController#new as JS
  Rendering posts/new.html.erb
  Rendered posts/new.html.erb

However, if I remove new.html.erb from the view directory, then the server renders the js template as required.

Started GET "/posts/new" for 127.0.0.1
Processing by PostsController#new as JS
  Rendering posts/new.js.coffee
  Rendered posts/new.js.coffee

I’m seeing the same behavior with a number of different controllers/actions.

Any suggestions on what I’m missing? FWIW, I am not using webpacker, just regular sprockets. And ruby 2.6.6. The only thing I’ve changed from 5.2.x related to javascript is to add an assets/config/manifest.js file with the following:

//= link_tree ../images
//= link_tree ../javascripts .js
//= link_directory ../stylesheets .css

Edit:
Also FWIW, I added a block with a test statement to the #js and #html methods –

  respond_to do |format|
    format.js {puts "calling js"}
    format.html {puts "calling html"}
  end

and confirmed that the #js method is the one that’s being called, even though it is rendering the html template.

2

Answers


  1. You may need to explicitly set the accepts header in your JQuery ajax request. Since you have templates for both formats. I believe Rails is favoring the HTML format, so long as the request deems it acceptable.

    Login or Signup to reply.
  2. Without going too deep, this bit here is an issue:
    https://github.com/rails/rails/blob/v6.0.0/actionview/lib/action_view/path_set.rb#L48

    def find(*args)
      find_all(*args).first || raise(MissingTemplate.new(self, *args))
      #              ^^^^^^
    end
    # where `find_all` returns
    # =>
    # [
    #   #<ActionView::Template app/views/posts/new.html.erb locals=[]>,
    #   #<ActionView::Template app/views/posts/new.js.coffee locals=[]>
    # ]
    

    html format is added along the way as a fallback for js:
    https://github.com/rails/rails/blob/v6.0.0/actionview/lib/action_view/lookup_context.rb#L291-L294

    if values == [:js]
      values << :html
      @html_fallback_for_js = true
    end
    

    This is definitely a rails bug. I’m not exactly sure when it was fixed but everything works fine in rails v7.


    If you’re planning on upgrading, try some later rails 6 versions, see if they fixed it back then. For now I can think of a couple of solutions:

    respond_to do |format|
      format.js { render formats: :js } # explicitly render js format only
      format.html
    end
    

    Other than that, you could override find_all method and fix the order of templates to correspond to the order of formats – [:js, :html]:

    # config/initializers/render_coffee_fix.rb
    
    module RenderCoffeeFix
      def find_all(path, prefixes = [], *args)
        templates = super
        _, options = args
    
        ordered_templates = options[:formats].map do |format|
          templates.detect { |template| template.format == format }
        end.compact
    
        ordered_templates
      end
    end
    
    ActionView::PathSet.prepend(RenderCoffeeFix)
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search