Meltemi
Meltemi

Reputation: 38349

Rails: Asset Pipeline creates Javascript errors in views scripts weren't intended for

I've got a view for a resource and some javascript that is specific to it. So, I dutifully put the Javascript in app/assets/javascripts/resource.js.coffee.

Problem is that when I go to other views in my application I get all kinds of Javascript errors like:

Type issue: 'null' is not an object...

page_url = page_image.getAttribute('src');

app works fine but these scripts, really, shouldn't be trying to run in views they weren't intended for.

Obviously I'm missing something...well...obvious! What is it?

Upvotes: 0

Views: 200

Answers (3)

Patrick Berkeley
Patrick Berkeley

Reputation: 2286

Two suggestions, the first is hacky, the second is awesome.

1. Manifest + Layout

Create a new JS manifest file for those views that require a different combination of JS, call that manifest file from a new layout, and call the new layout from the controller actions that render those views.

In your example:

app/assets/javascripts/resource.js.coffee

// Requires
//= require_whatever_js_files_your_resource_page_needs_here

// Custom JS below
...

app/views/layouts/resource.html.erb

...
<%# Within your <head> tag %>
<%= javascript_include_tag "resource" %>
...

app/controllers/resources_controller.rb

class ResourceController < ApplicationController
# filters, default layout, other actions, etc.
...

# Your action that needs only the resource JS,
# and therefore the resource layout
def action_that_only_uses_resource_js
  @some_objects = SomeObjects.all
  render layout: 'resource' # <- This is the important part
end

Obviously this method doesn't scale particularly well because it isn't flexible i.e., you can't combine your JS as you'd like and will no doubt need. Which brings us to breaking your JS into...

2. RequireJs Modules

Integrate RequireJs into Rails using this gem. You'll get the modularity you're looking for and prevent conflicts from happening. RequireJs employs the AMD pattern which I believe is what you're looking for.

Upvotes: 0

Frederick Cheung
Frederick Cheung

Reputation: 84114

There's nothing baked into rails for only running some javascript on some pages. The approach I take is based on this post

Each of my page specific coffeescript files looks like

window.App.controller_name = 
  init: ->
    #stuff that happens for all actions
  edit: ->
    #Stuff that happens only for the edit action

The body element is decorated with data-controller and data-action attributes. Some javascript in application.js then runs the appropriate javascript:

execute_hook = (controller, action='init') ->
  ns = window.App
  if controller?
    if ns[controller] && typeof(ns[controller][action]) == "function"
      ns[toplevel][controller][action]()

jQuery ->
  body = document.body
  controller = body.getAttribute("data-controller")
  action = body.getAttribute("data-action")
  execute_hook(controller)
  execute_hook(controller, action)

Upvotes: 1

willglynn
willglynn

Reputation: 11520

By default, application.js includes all the scripts in app/assets/javascripts, and by default, your layout includes all these scripts. This has the advantage that the browser has to make only one request for Javascript files, which can then be cached -- but the disadvantage that all your Javascript runs on every page.

There are two basic solutions:

  1. Write Javascript that does what it's supposed to do when needed, and does nothing when it's not.
  2. Change application.js to not include everything, include individual Javascript files when you need them, and change config.assets.precompile to include all your new top-level files.

Upvotes: 2

Related Questions