Лучший способ добавить специфичный для страницы JavaScript в приложение Rails 3?

159

В Rails 3 есть немного ненавязчивый JavaScript, который довольно крутой.

Но мне было интересно, как лучше всего включить дополнительный JavaScript для конкретной страницы.

Например, где я, возможно, ранее сделал:

<%= f.radio_button :rating, 'positive', :onclick => "$('some_div').show();" %>

Теперь мы можем сделать это ненавязчивым с чем-то вроде

<%= f.radio_button :rating, 'positive' %>

# then in some other file
$('user_rating_positive').click(function() {
  $('some_div').show();
}

Итак, я думаю, мой вопрос, где / как включить этот JavaScript? Я не хочу заполнять application.jsфайл, потому что этот JavaScript применим только к этому одному представлению. Должен ли я как-то включить собственный файл JavaScript для каждой страницы или вставить его в переменную экземпляра, которую ищет заголовок?

Брайан Армстронг
источник
Для тех, кто хочет полностью понять, как это работает и что лучше всего, пожалуйста, прочитайте railsapps.github.io/rails-javascript-include-external.html . На данный момент это лучшая документация, которую я видел по этой теме. Это отличное чтение не только для Rails, но и для всех, кто занимается веб-разработкой. Вот почему лучше всего делать так, как надо. Я обязательно буду использовать Unholy Rails для своих будущих вопросов. ВОТ ЭТО ДА.
DUTGRIFF
2
@KateGregory, что новее.
Сиро Сантилли 郝海东 冠状 病 六四 事件 法轮功

Ответы:

153

Что мне нравится делать, так это включать Javascript для каждого вида в content_for :headблок, а затем yieldв этот блок в макете приложения. Например

Если он довольно короткий, то:

<% content_for :head do %>
  <script type="text/javascript">
    $(function() {
      $('user_rating_positve').click(function() {
        $('some_div').show();
      }
    });
  </script>
<% end %>

или, если дольше, то:

<% content_for :head do %>
  <script type="text/javascript">
    <%= render :partial => "my_view_javascript"
  </script>
<% end %>

Затем в вашем файле макета

<head>
  ...
  <%= yield :head %>
</head>
BJG
источник
40
Почему бы не использовать <%= javascript_include_tag "my_javascipt_file" %> ?
AJP
5
@ AJP Я думаю, тогда это побеждает цель конвейера активов
Лулалала
2
@lulalala, но разве код в ответе не делает это в любом случае, но более грязно?
AJP
8
@AJP это сложнее, но у него меньше HTTP-запроса, чем у вас.
Лулалала
4
Мне понравилась эта статья railsapps.github.com/rails-javascript-include-external.html
Ziggy
103

Если вы хотите включить javascript только на одной странице, вы, конечно же, можете включить его на страницу, но если вы хотите сгруппировать свой javascript и воспользоваться преимуществами конвейера ресурсов, минимизированного js и т. Д., Это можно сделать и получить дополнительные Активы js, которые объединяются и загружаются только на определенных страницах, разбивая ваши js на группы, которые применяются только в определенных контроллерах / представлениях / разделах сайта.

Переместите ваши js-ресурсы в папки, с отдельным файлом манифеста для каждого, поэтому, если у вас есть библиотека js-администратора, которая используется только в серверной части, вы можете сделать это:

  • активы
    • JavaScripts
      • админ
        • ... JS
      • admin.js (манифест для группы администраторов)
      • application.js (манифест для глобальной группы приложений)
      • Глобальный
        • ... JS

в существующем application.js

//= require jquery
//= require jquery_ujs
//= require_tree ./global // requires all js files in global folder

в новом файле манифеста admin.js

//= require_tree ./admin // requires all js files in admin folder

Убедитесь, что этот новый js-манифест загружен, отредактировав config / production.rb

config.assets.precompile += %w( admin.js )

Затем настройте макет страницы, чтобы в заголовок страницы можно было добавить несколько js:

<%= content_for :header %>   

Затем в представлениях, где вы хотите включить эту конкретную группу js (а также обычную группу приложений) и / или любые специфичные для страницы js, css и т. Д .:

<% content_for :header do %>
  <%= javascript_include_tag 'admin' %>  
<% end %>

Конечно, вы можете сделать то же самое с css и сгруппировать его аналогичным образом, чтобы применить его только к определенным областям сайта.

Кенни Грант
источник
Если вы храните все свои манифесты в одном месте, вы можете указать rails прекомпилировать все в этой папке. Таким образом, вам нужно всего лишь один раз отредактировать production.rb и не отслеживать, что вы к нему добавили.
Undistraction
Не знал, что вы могли бы сделать это - чтобы вы могли иметь папку «манифесты», в которой содержались ваши манифесты, а также отдельные группы js? Я мог бы попробовать это. Мне бы очень хотелось, чтобы у них была немного более нормальная настройка по умолчанию, так как кажется, что они предполагают, что вы просто поместите все js в один большой файл, импортированный везде.
Кенни Грант
странно, я получаю ошибку Sprockets :: FileNotFound, если я использую //= require_tree ./global, но не если я //= require_directory ./globalиспользую Rails 3.2.12.
Qix
2
Похоже, у вас будет много скриптовых тегов. Я думал, что цель с конвейером ресурсов состояла в том, чтобы иметь только один тег сценария? Я бы просто написал javascripts, чтобы они были специфичными для страницы.
Зигги
1
Я думаю, что понял. Однако избегание дополнительных тегов скриптов очень важно для загрузки страницы.
Зигги
12

Эти ответы мне очень помогли! Если кто-то хочет немного больше ...

  1. Вы должны поместить javascripts в манифесты, если вы хотите, чтобы они были предварительно скомпилированы. Однако, если вам требуется каждый файл javascript из application.js.coffeeэтого файла, все javacsripts будут загружаться каждый раз, когда вы переходите на другую страницу, и цель создания специфичных для страницы javascript-скриптов будет отменена.

Следовательно, вам нужно создать свой собственный файл манифеста (например speciifc.js), который потребует все специфичные для страницы файлы javascript. Кроме того, изменить require_treeизapplication.js

приложение / активы / JavaScripts / application.js

//= require jquery
//= require jquery_ujs
//= require_tree ./global

приложение / активы / JavaScripts / specific.js

//= require_tree ./specific

Затем environments/production.rbдобавьте этот манифест в предварительно скомпилированный список с параметром config,

config.assets.precompile += %w( specific.js )

Готово! Все общие javascripts, которые всегда должны быть загружены, будут помещены в app/assets/javascripts/globalпапку, а специфичные для страницы javascripts - в app/assets/javascripts/specific. Вы можете просто вызвать специфичные для страницы javascripts из вида как

<%= javascript_include_tag "specific/whatever.js" %> //.js не является обязательным.

Этого достаточно, но я тоже хотел этим воспользоваться javascript_include_tag params[:controller]. Когда вы создаете контроллеры, ассоциированный файл coffeescript генерируется так же, app/assets/javascriptsкак и другие упомянутые люди. Существуют действительно специфичные для контроллера javascripts, которые загружаются только тогда, когда пользователь достигает определенного вида контроллера.

Итак, я создал еще один манифест controller-specific.js

приложение / активы / JavaScripts / контроллер-specific.js

//= require_directory .

Это будет включать в себя все автоматически сгенерированные сценарии, связанные с контроллерами. Также вам необходимо добавить его в предварительно скомпилированный список.

config.assets.precompile += %w( specific.js controller-specific.js )

Максимус С
источник
Большое спасибо за подробный ответ. Где я должен поставить эту строку javascript_include_tag params[:controller]. В настоящее время в моей application.html.erb есть эта строка javascript_include_tag 'application'. Должен ли я заменить это другой строкой?
Сима
1
Кроме того, я должен поставить эту строку <%= javascript_include_tag "specific/whatever.js" %>в content_for :headerблоке?
Сима
1
Мне странно, что с тобой нужно связываться config.assets.precompile. Разве все не app/assets/javascripts/*.jsпрекомпилируется автоматически?
AlexChaffee
@AlexChaffee Только файлы, доступные автоматически, являются application.cssи application.js. Другие должны быть либо включены в другие файлы, либо перечислены с помощью config.assets.precompile. Читайте больше здесь
Сон Чо
Декларация манифеста была перемещена. Я бегу рельсы 4.2.0. и нашел этот комментарий в файле production.rb: # config.assets.precompileи config.assets.versionпереместился в конфигурацию / Инициализаторы / assets.rb
резки
11

Я предпочитаю следующее ...

В вашем файле application_helper.rb

def include_javascript (file)
    s = " <script type=\"text/javascript\">" + render(:file => file) + "</script>"
    content_for(:head, raw(s))
end

а затем в вашем конкретном представлении (app / views / books / index.html.erb в этом примере)

<% include_javascript 'books/index.js' %>

... кажется, работает на меня.

cailinanne
источник
9

Если вы не хотите использовать конвейер ресурсов или сложные обходные пути, чтобы получить необходимый javascript для конкретной страницы (я сочувствую), самый простой и надежный способ, который достигается так же, как и ответы выше, но с меньшим количеством кода, это просто использовать:

<%= javascript_include_tag "my_javascipt_file" %>

Примечание: для этого требуется еще один запрос http на каждый тег включения, чем ответы, которые используют content_for :head

AJP
источник
javascript_include_tagбыло именно то, что я искал, ура AJP
Том Маккензи
где вы положили "my_javascipt_file"? В активах / javascripts? Но если это так, разве это не будет автоматически занесено //= require .в application.js?
Qix
@Linus, да, //= require .этот сценарий будет перенесен на любую страницу вашего сайта, поэтому вам нужно будет сделать что-то вроде предложенного Кенни Гранта (использовать //= require_tree ./global) или bjg.
AJP
1
Это было самым простым способом для меня, я изменил , //= require_tree .чтобы //= require_directory .в application.js , а затем создал подкаталог в активах \ JavaScripts. Обратите внимание, что вы должны включить новый каталог в config \ initializer \ assets.rb, чтобы предварительно скомпилировать ваш js, добавив строку:Rails.application.config.assets.precompile += %w( new_dir/js_file.js )
Jonesy
7

Посмотрите на гем pluggable_js . Вы можете найти это решение проще в использовании.

peresleguine
источник
3

Насколько я понимаю, конвейер ресурсов предназначен для уменьшения времени загрузки страницы путем объединения всех ваших js в один (минимизированный) файл. Хотя на первый взгляд это может показаться отвратительным, на самом деле эта функция уже существует в популярных языках, таких как C и Ruby. Такие вещи, как теги include, предназначены для предотвращения многократного включения файла и для помощи программистам в организации их кода. Когда вы пишете и компилируете программу на C, весь этот код присутствует в каждой части вашей работающей программы, но методы загружаются в память только тогда, когда этот код используется. В некотором смысле, скомпилированная программа не содержит ничего, чтобы гарантировать, что код является красиво модульным. Мы делаем код модульным, записывая наши программы таким образом, и операционная система загружает в память только те объекты и методы, которые нам нужны для данной местности. Есть ли вообще такая вещь, как «метод-специфическое включение»? Если ваше приложение rails спокойное, это, по сути, то, о чем вы просите.

Если вы пишете свой javascript так, чтобы он улучшал поведение элементов HTML на странице, то эти функции являются «специфичными для страницы» по своему дизайну. Если есть какой-то сложный код, который вы написали таким образом, что он будет выполняться независимо от его контекста, возможно, стоит рассмотреть возможность привязки этого кода к элементу html (вы можете использовать тег body, как описано в методе Garber-Irish ). Если функция выполняется условно, производительность, вероятно, будет меньше, чем у всех этих дополнительных тегов сценария.

Я думаю об использовании драгоценного камня paloma , как описано в проекте rails apps . Затем вы можете сделать свой javascript специфичным для страницы, включив специфичные для страницы функции в обратный вызов paloma:

Paloma.callbacks['users']['new'] = function(params){
    // This will only run after executing users/new action
    alert('Hello New Sexy User');
}; 

Вы используете рельсы, поэтому я знаю, что вы любите драгоценные камни :)

Ziggy
источник
3

Вам не следует загружать файлы JS или CSS за пределы конвейера ресурсов, потому что вы теряете важные функции, которые делают Rails таким замечательным. И тебе не нужен еще один драгоценный камень. Я верю в использование как можно меньшего количества драгоценных камней, и использование драгоценного камня здесь не обязательно.

То, что вы хотите, известно как «Javascript для конкретного контроллера» («Javascript для конкретного действия включен внизу). Это позволяет вам загружать определенный файл JavaScript для определенного КОНТРОЛЛЕРА. Попытка подключить ваш Javascript к представлению является своего рода ... назад и не следует шаблону проектирования MVC. Вы хотите связать его с вашими контроллерами или действиями внутри ваших контроллеров.

К сожалению, по какой-то причине разработчики Rails решили, что по умолчанию каждая страница будет загружать каждый файл JS, расположенный в вашем каталоге ресурсов. Почему они решили сделать это вместо включения «Javascript для контроллера» по умолчанию, я никогда не узнаю. Это делается с помощью файла application.js, который по умолчанию содержит следующую строку кода:

//= require_tree .

Это известно как директива . Это то, что sprockets использует для загрузки каждого файла JS в каталоге assets / javascripts. По умолчанию sprockets автоматически загружает application.js и application.css, а директива require_tree загружает каждый файл JS и Coffee в соответствующие каталоги.

ПРИМЕЧАНИЕ. Когда вы создаете леса (если вы не используете леса, сейчас самое время начать), Rails автоматически генерирует файл кофе для вас, для контроллера этого леса. Если вы хотите, чтобы он генерировал стандартный файл JS вместо файла кофе , то удалите самоцвет кофе , включенный по умолчанию в вашем Gemfile , и ваш скаффолд будет создавать файлы JS.

Итак, первый шаг к включению «Javascript для конкретного контроллера» - это удалить код require_tree из вашего файла application.js, ИЛИ изменить его на папку в вашем каталоге assets / javascripts, если вам все еще нужны глобальные файлы JS. IE:

//= require_tree ./global

Шаг 2: Перейдите в файл config / initializers / assets.rb и добавьте следующее:

%w( controllerone controllertwo controllerthree ).each do |controller|
  Rails.application.config.assets.precompile += ["#{controller}.js", "#{controller}.css"]
end

Вставьте нужные имена контроллеров.

Шаг 3: Замените javascript_include_tag в вашем файле application.html.erb следующим образом (обратите внимание на часть params [: controller]:

<%= javascript_include_tag 'application', params[:controller], 'data-turbolinks-track': 'reload' %>

Перезагрузите свой сервер и альт! Файл JS, сгенерированный вашим скаффолдом, теперь будет загружаться только при вызове этого контроллера.

Нужно загрузить конкретный файл JS на конкретный ДЕЙСТВИЕ в вашем контроллере , IE / article / new ? Сделайте это вместо этого:

application.html.erb :

<%= javascript_include_tag "#{controller_name}/#{action_name}" if AppName::Application.assets.find_asset("#{controller_name}/#{action_name}") %>

config / initializers / assets.rb :

config.assets.precompile += %w(*/*)

Затем добавьте новую папку с тем же именем, что и у вашего контроллера, в папку assets / javascripts и поместите в нее файл js с тем же именем, что и у вашего действия. Затем он загрузит его на это конкретное действие.

Эрик Мейнард
источник
1

Хорошо, возможно, это похоже на худшую работу, но я создал метод контроллера, который только что вывел файл .js

контроллер

def get_script
   render :file => 'app/assessts/javascripts/' + params[:name] + '.js'
end
def get_page
   @script = '/' + params[:script_name] + '.js?body=1'
   render page
end

Посмотреть

%script{:src => @script, :type => "text/javascript"}

если по какой-то причине мы не хотим этого делать, дайте мне знать.

Билл
источник
1

Предпочтительным способом добавления JS является нижний колонтитул, поэтому вы можете сделать это следующим образом:

show.html.erb:

<% content_for :footer_js do %>
   This content will show up in the footer section
<% end %>

макеты / application.html.erb

<%= yield :footer_js %>
Сайед
источник