it-swarm.com.ru

Используя Rails 3.1, куда вы помещаете свой "специфичный для страницы" код JavaScript?

Насколько я понимаю, все ваши JavaScript объединены в один файл. Rails делает это по умолчанию, когда добавляет //= require_tree . в конец файла манифеста application.js.

Это звучит как реальный спаситель жизни, но я немного обеспокоен специфичным для страницы кодом JavaScript. Этот код выполняется на каждой странице? Последнее, что я хочу, - чтобы все мои объекты создавались для каждой страницы, когда они нужны только на 1 странице.

Кроме того, не существует ли потенциал для кода, который конфликтует тоже?

Или вы помещаете небольшой тег script внизу страницы, который просто вызывает метод, который выполняет код javascript для страницы?

Вам больше не нужен require.js?

Спасибо

EDIT: Я ценю все ответы ... и я не думаю, что они действительно решают проблему. Некоторые из них касаются стиля и, похоже, не связаны между собой ... а другие просто упоминают javascript_include_tag..., который, как я знаю, существует (очевидно ...), но может показаться, что путь Rails 3.1 идет вперед - это обернуть весь ваш JavaScript в 1 файл, а не загружать отдельный JavaScript в нижней части каждой страницы.

Лучшее решение, которое я могу придумать, - это обернуть некоторые функции в теги div в ids или classes. В коде JavaScript вы просто проверяете, есть ли на странице id или class, и если это так, вы запускаете связанный с ним код JavaScript. Таким образом, если динамический элемент отсутствует на странице, код JavaScript не запускается, даже если он включен в массивный файл application.js, упакованный Sprockets.

Мое вышеупомянутое решение имеет то преимущество, что если окно поиска включено на 8 из 100 страниц, оно будет работать только на этих 8 страницах. Вам также не нужно будет включать этот код на 8 страницах сайта. На самом деле, вам больше никогда не придется добавлять теги сценариев вручную на ваш сайт.

Я думаю, что это фактический ответ на мой вопрос.

386
Fire Emblem

Я ценю все ответы ... и я не думаю, что они действительно решают проблему. Некоторые из них касаются стиля и, похоже, не связаны между собой ... а другие просто упоминают javascript_include_tag..., который, как я знаю, существует (очевидно ...), но может показаться, что Rails 3.1 идет вперед - это обернуть весь ваш Javascript в 1 файл, а не загружать отдельный Javascript внизу каждой страницы.

Лучшее решение, которое я могу придумать, - это обернуть некоторые функции в теги div в ids или classes. В коде JavaScript. Затем вы просто проверяете, есть ли id или class на странице, и если это так, вы запускаете связанный с ним код javascript. Таким образом, если динамический элемент отсутствует на странице, код javascript не запускается, даже если он включен в массивный файл application.js, упакованный Sprockets.

Мое вышеупомянутое решение имеет то преимущество, что если окно поиска включено на 8 из 100 страниц, оно будет работать только на этих 8 страницах. Вам также не нужно будет включать этот код на 8 страницах сайта. На самом деле, вам больше никогда не придется включать теги сценариев вручную на ваш сайт, кроме как для предварительной загрузки данных.

Я думаю, что это фактический ответ на мой вопрос.

23
Fire Emblem

Документы Asset Pipeline предлагают, как сделать JS для конкретного контроллера:

Например, если сгенерировано ProjectsController, будет новый файл в app/assets/javascripts/projects.js.coffee и другой файл в app/assets/stylesheets/projects.css.scss. Вы должны поместить любой JavaScript или CSS, уникальный для контроллера, в их соответствующие файлы ресурсов, поскольку эти файлы могут быть загружены только для этих контроллеров с помощью таких строк, как <%= javascript_include_tag params[:controller] %> или <%= stylesheet_link_tag params[:controller] %>.

ссылка на: asset_pipeline

157
meleyal

Для js для конкретной страницы вы можете использовать Garber-Irish Solution .

Таким образом, ваша папка Rails javascripts может выглядеть так для двух контроллеров - машин и пользователей:

javascripts/
├── application.js
├── init.js
├── markup_based_js_execution
├── cars
│   ├── init .js
│   ├── index.js
│   └── ...
└── users
    └── ...

И javascripts будет выглядеть так:

// application.js

//= 
//= require init.js
//= require_tree cars
//= require_tree users

// init.js

SITENAME = new Object();
SITENAME.cars = new Object;
SITENAME.users = new Object;

SITENAME.common.init = function (){
  // Your js code for all pages here
}

// cars/init.js

SITENAME.cars.init = function (){
  // Your js code for the cars controller here
}

// cars/index.js

SITENAME.cars.index = function (){
  // Your js code for the index method of the cars controller
}

и markup_based_js_execution будет содержать код для объекта UTIL, а также для выполнения UTIL.init, готового к DOM.

И не забудьте поместить это в свой файл макета:

<body data-controller="<%= controller_name %>" data-action="<%= action_name %>">

Я также думаю, что лучше использовать классы вместо атрибутов data-*, для лучшего css для конкретной страницы. Как упоминал Джейсон Гарбер: CSS-селекторы для конкретной страницы могут быть очень неудобными (когда вы используете data-*attributes)

Я надеюсь, что это поможет вам.

77
welldan97

Я вижу, что вы ответили на свой вопрос, но вот еще один вариант:

По сути, вы делаете предположение, что

//= require_tree .

необходимо. Это не. Не стесняйтесь удалить это. В моем текущем приложении, честно говоря, первое, что я делаю с 3.1.x, я сделал три разных JS-файла верхнего уровня. Мой application.js файл имеет только

//= require jquery
//= require jquery_ujs
//= require_directory .
//= require_directory ./api
//= require_directory ./admin

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

Ключи:

  1. Вы можете удалить require_tree - Rails, чтобы изменить предположения, которые он делает
  2. В имени application.js нет ничего особенного - любой файл в подкаталоге assets/javascript может содержать директивы препроцессора с //=

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

Sujal

65
sujal

Другой вариант: для создания файлов, специфичных для страницы или модели, вы можете создать каталоги в папке assets/javascripts/.

assets/javascripts/global/
assets/javascripts/cupcakes
assets/javascripts/something_else_specific

Ваш основной файл манифеста application.js может быть настроен для загрузки его файлов из global/. Определенные страницы или группы страниц могут иметь свои собственные манифесты, которые загружают файлы из их собственных определенных каталогов. Звездочки автоматически объединят файлы, загруженные application.js, с файлами, специфичными для вашей страницы, что позволяет этому решению работать.

Эту технику можно использовать и для style_sheets/.

40
ClosureCowboy

Я понимаю, что прихожу на эту вечеринку немного поздно, но я хотел добавить решение, которое я использовал в последнее время. Тем не менее, позвольте мне сначала упомянуть ...

Путь Rails 3.1/3.2 (Нет, сэр. Мне это не нравится.)

Смотрите: http://guides.rubyonrails.org/asset_pipeline.html#how-to-use-the-asset-pipeline

Для полноты изложения я включаю следующее и потому, что это не является нежизнеспособным решением ... хотя меня это не волнует.

"Rails Way" - это решение, ориентированное на контроллеры, а не ориентированное на просмотр, как просил первоначальный автор этого вопроса. Существуют специфичные для контроллера файлы JS, названные в честь соответствующих контроллеров. Все эти файлы помещаются в дерево папок, которое по умолчанию НЕ включено ни в одно из приложений. Js требуют директив.

Чтобы включить специфичный для контроллера код, в представление добавляется следующее.

<%= javascript_include_tag params[:controller] %>

Я ненавижу это решение, но оно есть и оно быстрое. Предположительно, вы могли бы вместо этого назвать эти файлы как-то "people-index.js" и "people-show.js", а затем использовать что-то вроде "#{params[:controller]}-index", чтобы получить ориентированное на представление решение. Опять же, быстрое исправление, но это не устраивает меня.

Мой путь к данным

Считай меня сумасшедшим, но я хочу, чтобы ВСЕ мои JS компилировались и минимизировались в application.js при развертывании. Я не хочу помнить, чтобы включить эти маленькие файлы бродяги повсюду.

Я загружаю все свои JS в один компактный файл, который скоро будет кэширован браузером. Если какой-то кусок моего application.js нужно запустить на странице, я позволяю HTML сказать мне, а не Rails.

Вместо того, чтобы привязывать мой JS к определенным идентификаторам элементов или засорять мой HTML классами маркеров, я использую пользовательский атрибут данных с именем data-jstags.

<input name="search" data-jstag="auto-suggest hint" />

На каждой странице я использую - вставьте сюда предпочтительный метод библиотеки JS - для запуска кода после завершения загрузки DOM. Этот код начальной загрузки выполняет следующие действия:

  1. Перебирать все элементы в DOM, отмеченные data-jstag
  2. Для каждого элемента разделите значение атрибута по пробелам, создав массив строк тегов.
  3. Для каждой строки тега выполните поиск в хэше для этого тега.
  4. Если соответствующий ключ найден, запустите связанную с ним функцию, передав элемент в качестве параметра.

Скажем, у меня в файле application.js определено следующее:

function my_autosuggest_init(element) {
  /* Add events to watch input and make suggestions... */
}

function my_hint_init(element) {
  /* Add events to show a hint on change/blur when blank... */
  /* Yes, I know HTML 5 can do this natively with attributes. */
}

var JSTags = {
  'auto-suggest': my_autosuggest_init,
  'hint': my_hint_init
};

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

Если какой-либо элемент не помечен data-jstag="auto-suggest", код автоматического предложения никогда не срабатывает. Тем не менее, он всегда присутствует, минимизируется и в конечном итоге кэшируется в моем application.js для тех случаев, когда он мне нужен на странице.

Если вам нужно передать дополнительные параметры вашим тегированным функциям JS, вам придется применить некоторые творческие возможности. Либо добавьте атрибуты data-paramter, придумайте некоторый синтаксис параметров или даже используйте гибридный подход.

Даже если у меня есть какой-то сложный рабочий процесс, который кажется специфичным для контроллера, я просто создам файл для него в своей папке lib, запакую его в application.js и добавлю к нему что-то вроде 'new-thing-wizard'. Когда мой bootstrap попадет в этот тег, будет создан и запущен мой замечательный мастер Nice. Он работает для представлений этого контроллера, когда это необходимо, но никак не связан с контроллером. На самом деле, если я правильно закодирую свой мастер, я смогу предоставить все данные конфигурации в представлениях и, следовательно, позже смогу повторно использовать мой мастер для любого другого контроллера, который нуждается в нем.

Как бы то ни было, именно так я уже давно внедряю JS для конкретных страниц, и это хорошо мне помогло как для простого дизайна сайта, так и для более сложных/насыщенных приложений. Надеемся, что одно из двух решений, которые я представил здесь, мой путь или путь Rails, пригодится всем, кто сталкивается с этим вопросом в будущем.

16
Ryan

На этот вопрос уже давно ответили, но я придумала собственное решение, основанное на некоторых из этих ответов и моем опыте с Rails 3+.

Трубопровод активов сладок. Используй это.

Сначала в файле application.js удалите //= require_tree.

Затем в вашем application_controller.rb создайте вспомогательный метод:

helper_method :javascript_include_view_js //Or something similar

def javascript_include_view_js
    if FileTest.exists? "app/assets/javascripts/"+params[:controller]+"/"+params[:action]+".js.erb"
        return '<script src="/assets/'+params[:controller]+'/'+params[:action]+'.js.erb" type="text/javascript"></script>'
    end
end

Затем в файле макета application.html.erb добавьте своего нового помощника среди существующих включений javascript с префиксом помощника raw:

<head>
    <title>Your Application</title>
    <%= stylesheet_link_tag "application", :media => "all" %>
    <%= javascript_include_tag "application" %>
    <%= raw javascript_include_view_js %>
</head>

Вуаля, теперь вы можете легко создавать специфичные для представления javascript, используя ту же структуру файлов, что и везде в Rails Просто вставьте свои файлы в app/assets/:namespace/:controller/action.js.erb!

Надеюсь, что это помогает кому-то еще!

7
Mike A
<%= javascript_include_tag params[:controller] %>
6
Mr Bohr

Вы можете добавить эту строку в свой файл макета (например, application.html.erb), чтобы автоматически загрузить специфичный для контроллера файл javascript (тот, который был создан при создании контроллера):

<%= javascript_include_tag params[:controller] %>

Вы также можете добавить строку для автоматической загрузки файла сценария для каждого действия.

<%= javascript_include_tag params[:controller] + "/" + params[:action] %>

Просто поместите скрипты вашей страницы в подкаталог, названный в честь имени контроллера. В эти файлы вы можете включить другие скрипты, используя = require. Было бы неплохо создать помощника для включения файла, только если он существует, чтобы избежать сбоя 404 в браузере.

6
mcubik

Возможно, вы найдете pluggable_js gem как подходящее решение.

6
peresleguine

Гем LoadJS - это еще один вариант:

LoadJS предоставляет способ загрузки специфичного для страницы кода Javascript в приложении Rails без потери волшебства, предоставляемого Sprockets. Весь ваш код Javascript будет продолжен в одном файле Javascript, но некоторые его части будут выполняться только для определенных страниц.

https://github.com/guidomb/loadjs

5
meleyal

Ответ Филиппа довольно хороший. Вот код, чтобы заставить это работать:

В application.html.erb:

<body class="<%=params[:controller].parameterize%>">

Предполагая, что ваш контроллер называется Projects, он сгенерирует:

<body class="projects">

Тогда в projects.js.coffee:

jQuery ->
  if $('body.projects').length > 0  
     $('h1').click ->
       alert 'you clicked on an h1 in Projects'
3
Rahil Sondhi

Вы также можете сгруппировать js по папкам и продолжать использовать конвейер ресурсов для выборочно загружать свой javascript в зависимости от страницы.

2
Kenny Grant

JavaScripts объединяются только тогда, когда вы говорите Rails (скорее, Sprockets) объединить их.

2
yfeldblum

Я согласен с вашим ответом, чтобы проверить, есть ли этот селектор, используйте:

if ($(selector).length) {
    // Put the function that does not need to be executed every page
}

(никто не видел, чтобы добавить фактическое решение)

2
Kyle C

Вот как я решил проблему со стилем: (простите за Haml)

%div{:id => "#{params[:controller].parameterize} #{params[:view]}"}
    = yield

Таким образом, я начинаю все специфичные для страницы . Css.sass файлы с:

#post
  /* Controller specific code here */
  &#index
    /* View specific code here */
  &#new
  &#edit
  &#show

Таким образом, вы можете легко избежать любых столкновений. Когда дело доходит до файлов . Js.coffee, вы можете просто инициализировать такие элементы, как;

$('#post > #edit') ->
  $('form > h1').css('float', 'right')

Надеюсь, это помогло некоторым.

2
zeeraw

Я не вижу ответа, который действительно объединяет все это и выкладывает это для вас. Таким образом, я постараюсь поставить meleyal , sujal (а-ля ClosureCowboy ), первая часть ответа Райана и даже Смелое утверждение Гал о Backbone.js ... все вместе кратко и ясно. И, кто знает, я мог бы даже соответствовать требованиям Марнена Лайбоу-Козера .

Пример редактирования

активы/JavaScripts/application.js

//= require jquery
//= require jquery_ujs
//= require lodash.underscore.min
...


Просмотров/макеты/application.html.erb

  ...
  </footer>

  <!-- Javascripts ================================================== -->
  <!-- Placed at the end of the document so the pages load faster -->
  <%= javascript_include_tag "application" %>
  <%= yield :javascript %>

</body>
</html>


Просмотров/Foo/index.html.erb

...
<% content_for :javascript do %>
  <%= javascript_include_tag params[:controller] %>
<% end %>


активы/JavaScripts/foo.js

//= require moment
//= require_tree ./foostuff


Активы/JavaScripts/foostuff/foothis.js.coffee

alert "Hello world!"


Краткое описание

  • Удалите //= require_tree . из application.js и перечислите только тот JS, который разделяет каждая страница.

  • Две строки, показанные выше в application.html.erb, указывают странице, куда следует включить application.js и ваш JS для конкретной страницы.

  • Три строки, показанные выше в index.html.erb, говорят вашему виду искать какой-то специфичный для страницы JS и включать его в именованную область выхода, называемую ": javascript" (или как вы хотите ее назвать ). В этом примере контроллером является "foo", поэтому Rails попытается включить "foo.js" в область выхода: javascript в макете приложения.

  • Перечислите свой JS для конкретной страницы в foo.js (или в любом другом месте, где указан контроллер). Список общих библиотек, дерево, каталоги, что угодно.

  • Держите свой JS для конкретной страницы где-нибудь, где вы можете легко ссылаться на него отдельно от вашего другого JS. В этом примере для foo.js требуется дерево foostuff, поэтому поместите туда свой пользовательский JS, например foothis.js.coffee.

  • Здесь нет жестких правил. Не стесняйтесь перемещать вещи и, возможно, даже создавать несколько областей доходности с различными именами в различных макетах, если это необходимо. Это только показывает один возможный первый шаг вперед. (Я делаю это не совсем так, учитывая наше использование Backbone.js. Я мог бы также захотеть поместить foo.js в папку с именем foo вместо foostuff, но пока не решил этого.)

Заметки

Вы можете делать подобные вещи с помощью CSS и <%= stylesheet_link_tag params[:controller] %>, но это выходит за рамки вопроса.

Если я пропустил яркую лучшую практику здесь, пришли мне записку, и я придумаю адаптацию. Rails является довольно новым для меня, и, честно говоря, я до сих пор не впечатлен тем хаосом, который он по умолчанию вносит в развитие предприятия, и всем трафиком, который генерирует средняя программа Rails.

2
juanitogan

Paloma проект предлагает интересный подход к управлению javascript-кодом для конкретной страницы.

Пример использования из их документов:

var UsersController = Paloma.controller('Users');

// Executes when Rails User#new is executed.
UsersController.prototype.new = function(){
   alert('Hello Sexy User!' );
};
1
zavg

ответ ryguy является хорошим ответом, даже несмотря на то, что его оценили как негативные.

Особенно, если вы используете что-то вроде Backbone JS - каждая страница имеет свой собственный вид Backbone. Тогда у файла erb есть только одна строка встроенного javascript, который запускает правый базовый класс представления. Я считаю, что это единственная строка "склеивающего кода" и, следовательно, тот факт, что с ней все в порядке. Преимущество заключается в том, что вы можете сохранить ваше "require_tree", которое позволяет браузеру кэшировать весь javascript.

в show.html.erb у вас будет что-то вроде:

<% provide :javascript do %>
  <%= javascript_include_tag do %>
    (new app.views.ProjectsView({el: 'body'})).render();
  <% end %>
<% end do %>

и в вашем файле макета вам понадобится:

<%= yield :javascript %>
1
Gal

Шаг 1. удалить require_tree. в вашем application.js и application.css.

Шаг 2. Отредактируйте ваш application.html.erb (по умолчанию Rails) в папке макета. Добавьте "params [: controller]" в следующие теги.

<%= stylesheet_link_tag    'application', params[:controller], media: 'all', 'data-turbolinks-track' => true %>

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

Шаг 3. Добавьте файл в config/initializers/assets.rb

%w( controller_one controller_two controller_three ).each do |controller|
  Rails.application.config.assets.precompile += ["#{controller}.js", "#{controller}.js.coffee", "#{controller}.css", "#{controller}.scss"]
end

ссылки: http://theflyingdeveloper.com/controller-specific-assets-with-Rails-4/

1
etlds

У меня есть другое решение, которое, хотя примитив отлично работает для меня и не нуждается в каких-либо причудливых выборочных стратегиях загрузки. Поместите в свою обычную функцию готовности документа, но затем проверьте текущее местоположение окон, чтобы увидеть, предназначена ли это страница, для которой предназначен ваш javascript:

$(document).ready(function() {
   if(window.location.pathname.indexOf('/yourpage') != -1) {
          // the javascript you want to execute
   }
}

Это по-прежнему позволяет загружать все js с помощью Rails 3.x в одном небольшом пакете, но не создает больших накладных расходов или конфликтов со страницами, для которых js не предназначен.

1
Peter Abramowitsch

Хотя у вас есть несколько ответов здесь, я думаю, что ваша редакция, вероятно, лучшая ставка. Шаблон проектирования, который мы используем в нашей команде и который мы получили из Gitlab , является шаблоном Dispatcher. Он делает нечто похожее на то, о чем вы говорите, однако имя страницы задается в теге body Rails. Например, в вашем файле макета просто включите что-то вроде (в HAML):

%body{'data-page' => "#{controller}:#{action}" }

Тогда в вашем файле dispatcher.js.coffee в папке javascripts есть только одно замыкание и инструкция switch:

$ ->
  new Dispatcher()

class Dispatcher
  constructor: ->
    page = $('body').attr('data-page')
    switch page
      when 'products:index'
        new Products() 
      when 'users:login'
        new Login()

Все, что вам нужно сделать в отдельных файлах (например, products.js.coffee или login.js.coffee), это заключить их в класс и затем глобализировать этот символ класса, чтобы вы могли получить к нему доступ в диспетчере:

class Products
  constructor: ->
    #do stuff
@Products = Products

У Gitlab есть несколько примеров этого, с которыми вы можете поэкспериментировать, если вам интересно :)

1
onetwopunch

Переместите все ваши JS-файлы commom в подпапку, например "app/assets/javascript/global", затем в application.js, измените строку //= require_tree . на //= require_tree ./global.

Теперь вы можете поместить свой JS для конкретного контроллера в корень "app/assets/javascript /", и они не будут включены в скомпилированный JS, который используется только тогда, когда вы вызываете их через = javascript_include_tag на вашем контроллере/представлении.

1
Gedean Dias

Я делал это ранее, используя этот метод: http://theflyingdeveloper.com/controller-specific-assets-with-Rails-4/ . Супер-легкий, полагается на контроллеры, чтобы выбрать правильный JS для загрузки.

0
Eddie Prislac

Я не пробовал это, но похоже, что верно следующее:

  • если у вас есть content_for, который является javascript (например, с настоящим javascript внутри него), sprockets не будет знать об этом, и, следовательно, это будет работать так же, как и сейчас.

  • если вы хотите исключить файл из большого пакета javascript, вы должны зайти в файл config/sprockets.yml и соответственно изменить исходные файлы. Затем вы просто включите любые файлы, которые вы исключили, где это необходимо.

0
Bill Eisenhauer

Я объединил некоторые ответы в:

Помощник по применению:

module ApplicationHelper
  def js_page_specific_include
    page_specific_js = params[:controller] + '_' + params[:action]
    if Rails.application.assets.find_asset(page_specific_js).nil?
      javascript_include_tag 'application', 'data-turbolinks-track' => true
    else
      javascript_include_tag 'application', page_specific_js, 'data-turbolinks-track' => true
    end
  end
end

макеты/application.html.haml:

 <!DOCTYPE html>
%html{lang: 'uk'}
  %head   
    = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true
   bla-bla-bla
    = js_page_specific_include   
   bla-bla-bla  
0
kapellan

Первое: удалите \\=require_tree из application.js Второе: весь ваш JS-код должен быть размещен в /app/assets/javascritpt, а весь ваш CSS-код должен быть размещен в /app/assets/stylesheets

0
Nicollas Matheus