¿La mejor manera de agregar JavaScript específico de página en una aplicación Rails 3?

159

Rails 3 tiene un JavaScript discreto que es bastante bueno.

Pero me preguntaba cuál es la mejor manera de incluir JavaScript adicional para una página en particular.

Por ejemplo, donde podría haberlo hecho anteriormente:

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

Ahora podemos hacerlo discreto con algo como

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

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

Entonces, ¿mi pregunta es dónde / cómo incluir ese JavaScript? No quiero llenar el application.jsarchivo porque este JavaScript solo es aplicable a esta vista. ¿Debo incluir un archivo JavaScript personalizado para cada página de alguna manera, o pegarlo en una variable de instancia que busca el encabezado?

Brian Armstrong
fuente
Para aquellos que desean una comprensión completa de cómo funciona esto y qué es lo mejor, lea railsapps.github.io/rails-javascript-include-external.html . Esta es, con mucho, la mejor documentación que he visto sobre este tema. Es una gran lectura no solo para Rails sino para cualquiera que trabaje en desarrollo web. Por eso es mejor hacer las cosas a la manera de los rieles. Seguramente usaré Unholy Rails para mis futuras preguntas. GUAU.
DutGRIFF
2
@KateGregory ese es más nuevo.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Respuestas:

153

Lo que me gusta hacer es incluir el Javascript por vista en un content_for :headbloque y luegoyield a ese bloque en el diseño de su aplicación. Por ejemplo

Si es bastante corto, entonces:

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

o, si es más largo, entonces:

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

Luego, en su archivo de diseño

<head>
  ...
  <%= yield :head %>
</head>
bjg
fuente
40
¿Por qué no usar el <%= javascript_include_tag "my_javascipt_file" %> ?
AJP
55
@AJP Supongo que luego derrota el propósito de la cartera de activos
lulalala
2
@lulalala, pero el código en la respuesta, ¿no lo hace de todos modos, pero de una manera más desordenada?
AJP
8
@AJP es más complicado, pero tiene una solicitud HTTP menos que tu respuesta.
lulalala
103

Si desea incluir javascript en una sola página, puede incluirlo en la página en línea, por supuesto, sin embargo, si desea agrupar su javascript y aprovechar la canalización de activos, mins js, etc., es posible hacerlo y tener más js activos que se combinan y solo se cargan en páginas específicas al dividir sus js en grupos que solo se aplican en ciertos controladores / vistas / secciones del sitio.

Mueva sus js en activos a carpetas, con un archivo de manifiesto separado para cada uno, por lo que si tenía una biblioteca admin js que solo se usa en el back-end, puede hacer esto:

  • bienes
    • javascripts
      • administración
        • ... js
      • admin.js (manifiesto para el grupo de administración)
      • application.js (manifiesto para el grupo global de aplicaciones)
      • global
        • ... js

en la aplicación existente.js

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

en un nuevo archivo de manifiesto admin.js

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

Asegúrese de que este nuevo manifiesto js se cargue editando config / production.rb

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

Luego, ajuste el diseño de su página para que pueda incluir algunos js adicionales para el encabezado de la página:

<%= content_for :header %>   

Luego, en las vistas donde desea incluir este grupo js específico (así como el grupo de aplicaciones normal) y / o cualquier js, css, etc., específicos de la página:

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

Por supuesto, puede hacer lo mismo con css y agruparlo de manera similar para aplicarlo solo a ciertas áreas del sitio.

Kenny Grant
fuente
Si mantiene todos sus manifiestos en el mismo lugar, puede decirle a los rieles que precompilen todo en esa carpeta. De esa manera, solo necesita editar production.rb una vez y no realizar un seguimiento de lo que ha agregado.
Desvío
¿No sabía que podía hacer eso, por lo que podría tener una carpeta de 'manifiestos' que contuviera sus manifiestos, así como los grupos js individuales? Podría probar esto. Desearía que tuvieran una configuración un poco más sensata de forma predeterminada, ya que parece que asumen que solo colocará todos los js en un gran archivo importado a todas partes.
Kenny Grant
extraño, recibo un error Sprockets :: FileNotFound si lo uso //= require_tree ./global, pero no si lo uso //= require_directory ./globalcon Rails 3.2.12.
qix
2
Parece que terminarás con muchas etiquetas de script. ¿Pensé que el objetivo con la cartera de activos era tener solo una etiqueta de script? Simplemente escribiría los javascripts para que sean específicos de la página.
Ziggy
1
Creo entender. Sin embargo, evitar etiquetas de script adicionales es bastante importante para el tiempo de carga de la página.
Ziggy
12

¡Estas respuestas me ayudaron muchísimo! Si alguien quiere un poco más ...

  1. Debe poner javascripts en manifiestos si desea que estén precompilados. Sin embargo, si necesita todos los archivos javascript, application.js.coffeeentonces todos los javacsripts se cargarán cada vez que navegue a una página diferente, y se anulará el propósito de hacer javascripts específicos de la página.

Por lo tanto, debe crear su propio archivo de manifiesto (por ejemplo speciifc.js) que requerirá todos los archivos de JavaScript específicos de la página. Además, modificar require_treedesdeapplication.js

app / assets / javascripts / application.js

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

app / assets / javascripts / specific.js

//= require_tree ./specific

Luego, al environments/production.rbagregar este manifiesto a la lista precompilada con la opción de configuración,

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

¡Hecho! Todos los javascripts compartidos que siempre deben cargarse se colocarán en la app/assets/javascripts/globalcarpeta y los javascripts específicos de la página app/assets/javascripts/specific. Simplemente puede llamar a los javascripts específicos de la página desde la vista como

<%= javascript_include_tag "specific/whatever.js" %> //.js es opcional.

Esto es suficiente, pero también quería utilizarlo javascript_include_tag params[:controller]. Cuando crea controladores, se genera un archivo coffeescript asociado al app/assets/javascriptsigual que otras personas mencionadas. Realmente hay controladores específicos javascripts , que se cargan solo cuando el usuario alcanza la vista específica del controlador.

Entonces creé otro manifiesto controller-specific.js

app / assets / javascripts / controller-specific.js

//= require_directory .

Esto incluirá todos los coffeescripts generados automáticamente asociados con los controladores. Además, debe agregarlo a la lista precompilada.

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

Maximus S
fuente
Muchas gracias por la respuesta detallada. ¿Dónde debo poner esta línea javascript_include_tag params[:controller]? Actualmente mi application.html.erb tiene esta línea javascript_include_tag 'application'. ¿Debo reemplazar esto con la otra línea?
simha
1
Además, ¿debo poner esta línea <%= javascript_include_tag "specific/whatever.js" %>en un content_for :headerbloque?
simha
1
Es extraño para mí con el que necesites meterte config.assets.precompile. ¿No está todo app/assets/javascripts/*.jsprecompilado automáticamente?
AlexChaffee
@AlexChaffee Solo los archivos disponibles automáticamente son application.cssy application.js. Otros deben incluirse en otros archivos o enumerarse usando config.assets.precompile. Lea más aquí
Sung Cho
La declaración de manifiesto ha sido movida. Estoy corriendo rieles 4.2.0. y encontraron este comentario en el archivo production.rb: # config.assets.precompiley config.assets.versionse han trasladado a config / inicializadores / assets.rb
Curt
11

Prefiero lo siguiente ...

En su archivo application_helper.rb

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

y luego en su vista particular (app / views / books / index.html.erb en este ejemplo)

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

... Parece funcionar para mi.

cailinanne
fuente
9

Si no desea utilizar la canalización de activos o las soluciones complejas para obtener ese JavaScript específico de la página necesaria (simpatizo), la forma más simple y robusta, que logra lo mismo que las respuestas anteriores pero con menos código es solo para utilizar:

<%= javascript_include_tag "my_javascipt_file" %>

Nota: esto requiere una solicitud http más por etiqueta de inclusión que las respuestas que usan content_for :head

AJP
fuente
javascript_include_tagfue exactamente lo que estaba buscando, aplausos AJP
Tom McKenzie
¿dónde pones "my_javascipt_file"? En activos / javascripts? Pero si es así, ¿no sería recogido automáticamente con un //= require .en la aplicación.js?
qix
@Linus sí, //= require .traería ese script a cualquier página de su sitio, por lo que tendría que hacer algo como Kenny Grant (uso //= require_tree ./global) o bjg sugerido.
AJP
1
Este fue, con mucho, el método más fácil para mí, cambié //= require_tree .a //= require_directory .application.js y luego creé un subdirectorio en assets \ javascripts. Tenga en cuenta que debe incluir el nuevo directorio en config \ initializers \ assets.rb para precompilar su js agregando la línea:Rails.application.config.assets.precompile += %w( new_dir/js_file.js )
Jonesy
7

Echa un vistazo a la gema pluggable_js . Puede encontrar esta solución más fácil de usar.

peresleguina
fuente
3

Tengo entendido que la canalización de activos está destinada a disminuir el tiempo de carga de la página al combinar todos sus js en un solo archivo (minificado). Si bien esto puede parecer repugnante en la superficie, en realidad es una característica que ya existe en lenguajes populares como C y Ruby. Cosas como las etiquetas "incluir" están destinadas a evitar la inclusión múltiple de un archivo y a ayudar a los programadores a organizar su código. Cuando escribe y compila un programa en C, todo ese código está presente en cada parte de su programa en ejecución, pero los métodos solo se cargan en la memoria cuando se usa ese código. En cierto sentido, un programa compilado no incluye nada para garantizar que el código sea muy modular. Hacemos que el código sea modular al escribir nuestros programas de esa manera, y el sistema operativo solo carga en la memoria los objetos y métodos que necesitamos para una localidad determinada. ¿Existe incluso algo así como la "inclusión específica del método"? Si su aplicación de rieles es tranquila, esto es esencialmente lo que está pidiendo.

Si escribe su javascript para que aumente el comportamiento de los elementos HTML en la página, entonces esas funciones son 'específicas de la página' por diseño. Si hay algún código complicado que escribió de tal manera que se ejecutará independientemente de su contexto, tal vez considere vincular ese código a un elemento html de todos modos (puede usar la etiqueta del cuerpo, como se describe en el método Garber-Irish ). Si una función se ejecuta condicionalmente, el rendimiento probablemente será menor que todas esas etiquetas de script adicionales.

Estoy pensando en usar la gema de paloma , como se describe en el proyecto de aplicaciones de rieles . Luego puede hacer que su página de JavaScript sea específica al incluir funciones específicas de la página en una devolución de llamada de paloma:

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

Usas rieles, así que sé que te encantan las gemas :)

Ziggy
fuente
3

No debería cargar sus archivos JS o CSS fuera de la canalización de activos porque pierde características importantes que hacen que Rails sea tan bueno. Y no necesitas otra gema. Creo en usar la menor cantidad de gemas posible, y usar una gema no es necesario aquí.

Lo que desea se conoce como "Javascript específico del controlador" ("Javascript específico de la acción se incluye en la parte inferior). Esto le permite cargar un archivo JavaScript específico para un CONTROLADOR específico. Intentar conectar su Javascript a una vista es una especie de ... hacia atrás y no sigue el patrón de diseño MVC. Desea asociarlo con sus Controladores o acciones dentro de sus Controladores.

Desafortunadamente, por cualquier razón, los desarrolladores de Rails decidieron que, por defecto, cada página cargará todos los archivos JS ubicados en su directorio de activos. Nunca sabré por qué decidieron hacer esto en lugar de habilitar el "Javascript específico del controlador" de forma predeterminada. Esto se realiza a través del archivo application.js, que incluye la siguiente línea de código de manera predeterminada:

//= require_tree .

Esto se conoce como una directiva . Es lo que utiliza Sprockets para cargar cada archivo JS en el directorio assets / javascripts. Por defecto, sprockets carga automáticamente application.js y application.css, y la directiva require_tree carga cada archivo JS y Coffee en sus respectivos directorios.

NOTA: Cuando andamia (si no está andamiando, ahora es un buen momento para comenzar), Rails genera automáticamente un archivo de café para usted, para el controlador de ese andamio. Si desea que genere un archivo JS estándar en lugar de un archivo de café , elimine la gema de café que está habilitada de forma predeterminada en su Gemfile , y su andamio creará archivos JS en su lugar.

Bien, entonces el primer paso para habilitar el "Javascript específico del controlador" es eliminar el código require_tree de su archivo application.js, O cambiarlo a una carpeta dentro de su directorio assets / javascripts si aún necesita archivos JS globales. ES DECIR:

//= require_tree ./global

Paso 2: vaya a su archivo config / initializers / assets.rb y agregue lo siguiente:

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

Inserte los nombres de controlador que desee.

Paso 3: Reemplace javascript_include_tag en su archivo application.html.erb con esto (tenga en cuenta la parte params [: controller]:

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

Reinicie su servidor y viola! El archivo JS que se generó con su andamio ahora solo se cargará cuando se llame a ese controlador.

¿Necesita cargar un archivo JS específico en una ACCIÓN específica en su controlador , IE / articles / new ? Haz esto en su lugar:

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(*/*)

Luego agregue una nueva carpeta con el mismo nombre que su controlador en su carpeta de activos / javascripts y coloque su archivo js con el mismo nombre que su acción dentro. Luego lo cargará en esa acción específica.

Erick Maynard
fuente
1

De acuerdo, tal vez este sea el peor trabajo posible, pero creo un método de controlador que acaba de generar el archivo .js

Controlador

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

Ver

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

si por alguna razón no queremos hacer esto, hágamelo saber.

Cuenta
fuente
1

La forma preferida de agregar JS es en el pie de página, por lo que puede hacerlo de esta manera:

show.html.erb:

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

layouts / application.html.erb

<%= yield :footer_js %>
Syed
fuente