Usando Rails 3.1, ¿dónde coloca su código JavaScript "específico de la página"?


388

A mi entender, todo tu JavaScript se fusiona en 1 archivo. Rails hace esto por defecto cuando se agrega //= require_tree .a la parte inferior de suapplication.js archivo de manifiesto.

Esto suena como un verdadero salvavidas, pero estoy un poco preocupado por el código JavaScript específico de la página. ¿Este código se ejecuta en cada página? Lo último que quiero es que todos mis objetos sean instanciados para cada página cuando solo se necesitan en 1 página.

Además, ¿no hay potencial para el código que también choca?

¿O pones un pequeño script etiqueta en la parte inferior de la página que solo llama a un método que ejecuta el código de JavaScript para la página?

¿Ya no necesitas require.js entonces?

Gracias

EDITAR : aprecio todas las respuestas ... y no creo que realmente estén captando el problema. Algunos de ellos son sobre el estilo y no parecen relacionarse ... y otros solo mencionanjavascript_include_tag ... lo que sé que existe (obviamente ...) pero parece que el camino de Rails 3.1 en adelante es concluir todo su JavaScript en 1 archivo en lugar de cargar JavaScript individual en la parte inferior de cada página.

La mejor solución que se me ocurre es envolver ciertas características en divetiquetas con ids o classes. En el código JavaScript, solo verifica si el ido classestá en la página, y si lo está, ejecuta el código JavaScript que está asociado con él. De esta manera, si el elemento dinámico no está en la página, el código JavaScript no se ejecuta, aunque se haya incluido en el application.jsarchivo masivo empaquetado por Sprockets.

Mi solución anterior tiene la ventaja de que si se incluye un cuadro de búsqueda en 8 de las 100 páginas, se ejecutará solo en esas 8 páginas. Tampoco tendrá que incluir el mismo código en 8 de las páginas del sitio. De hecho, nunca más tendrá que incluir etiquetas de script manual en su sitio en ningún otro lugar.

Creo que esta es la respuesta real a mi pregunta.


11
"El camino hacia adelante de Rails 3.1 es envolver todo su Javascript en 1 archivo en lugar de cargar Javascript individual en la parte inferior de cada página". para administrar JavaScript. Los archivos pequeños son generalmente mejores (vea mis comentarios en otra parte). Cuando se trata de JavaScript, la forma Rails rara vez es la forma correcta (excepto por la canalización de activos, que patea el culo y el estímulo de CoffeeScript).
Marnen Laibow-Koser

Entonces, ¿incluirás los archivos js específicos de tu página en cada página? Creo que es un desperdicio, estoy más de acuerdo con la respuesta de ClosureCowboy.
gerky

1
¿Le echó un vistazo a la respuesta aceptada para esta pregunta? stackoverflow.com/questions/6571753/…
rassom

1
@DutGRIFF En otras palabras: no, no es mejor hacer las cosas a la manera de Rails en este caso (o al menos, no poner todo application.js), y de hecho la referencia que proporcionó señala por qué esto es así: la descarga es la parte más lenta del proceso de ejecución de JS. Muchos archivos pequeños son más almacenables en caché que uno grande. La gente de Unholy Rails no parece darse cuenta, entonces, de que sus recomendaciones son inconsistentes con los principios a los que intentan adherirse y, por lo tanto, sus recomendaciones no deben tomarse en serio.
Marnen Laibow-Koser

1
@DutGRIFF No, un archivo JS grande normalmente no sería algo bueno, incluso una vez en caché. Vea mis comentarios en otra parte de esta página: los archivos pequeños pueden apuntar mejor a páginas específicas y pueden almacenarse en caché con una granularidad más fina. No veo ningún caso de uso bueno para un solo archivo grande a menos que no hay código de la página específica en absoluto .
Marnen Laibow-Koser

Respuestas:


157

Los documentos de Asset Pipeline sugieren cómo hacer JS específicos del controlador:

Por ejemplo, si ProjectsControllerse genera un, habrá un nuevo archivo en app/assets/javascripts/projects.js.coffeey otro en app/assets/stylesheets/projects.css.scss. Debe colocar cualquier JavaScript o CSS exclusivo de un controlador dentro de sus respectivos archivos de activos, ya que estos archivos se pueden cargar solo para estos controladores con líneas como <%= javascript_include_tag params[:controller] %>o <%= stylesheet_link_tag params[:controller] %>.

Enlace a: asset_pipeline


50
Esta es la forma más elegante de hacerlo. Pero también, deberá eliminar la línea // = require_tree. de la aplicación.js.coffee
zsljulius

2
Estoy totalmente de acuerdo con este método. Los otros métodos parecen muy torpes y todavía terminan cargando un archivo js gigante. El proyecto en el que estoy trabajando tiene casi 2 MB de archivos JS / complementos, etc. DESPUÉS de ser combinado / minimizado.
Bill Garrison

2
Soy bastante nuevo en Rails, pero me parece que este debería ser el comportamiento predeterminado.
Ross Hambrick

12
Para el control de acciones específicas, tengo esto en mi diseño, ya que no todas las acciones de cada controlador tienen JS específicos. page_specific_js = "#{params[:controller]}_#{params[:action]}"y entonces; javascript_include_tag page_specific_js if Rails.application.assets.find_asset page_specific_js
Sujimichi

2
¿Las acciones específicas del controlador todavía se minimizan? ¿Se añaden al único archivo js creado por las ruedas dentadas, o esto genera múltiples solicitudes de archivos de activos?
Jason

77

Para los js específicos de la página, puede usar la solución Garber-Irish .

Entonces su carpeta Rails javascripts podría verse así para dos controladores: autos y usuarios:

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

Y los javascripts se verán así:

// 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
}

y markup_based_js_execution contendrá código para el objeto UTIL y en la ejecución UTIL.init lista para DOM.

Y no olvide poner esto en su archivo de diseño:

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

También creo que es mejor usar clases en lugar de data-*atributos, para el mejor CSS específico de la página. Como Jason Garber ha mencionado: los selectores CSS específicos de la página pueden volverse realmente incómodos (cuando usa data-*atributos)

Espero que esto ayude.


44
¿Qué sucede si necesita una variable disponible para todas las acciones en el controlador de usuarios, pero no disponible en otros controladores? ¿Este método no tiene algunos problemas de alcance?
tybro0103

@ tybro0103, creo que para implementar este comportamiento, le gustaría escribir algo como window.varForOneController='val'en esta función de inicio del controlador. También gon gem puede ayudar aquí ( github.com/gazay/gon ). Puede haber otras soluciones alternativas.
welldan97

1
@ welldan97 Voto negativo no para su explicación, que es excelente, sino porque la estructura Garber-irlandesa es mala. Carga todos sus JS en cada página, y depende de las clases y los ID en el elemento <body> para ordenar las cosas. Esa es una señal segura de luchar contra el DOM: en circunstancias normales, el elemento <body> no debería necesitar una clase o ID, ya que solo hay uno en un documento. La forma correcta de hacer esto es simplemente eliminar //= require_tree .y usar JavaScript específico de la página. Si está tratando activamente de no hacer eso, entonces está luchando por las malas prácticas.
Marnen Laibow-Koser

2
@ MarnenLaibow-Koser Personalmente, creo que cargar todos los js en cada página es bueno para la mayoría de los proyectos cuando combina todos los js en un archivo y lo minimiza. Creo que en general funciona más rápido para el usuario. Al menos es más como conflicto un archivo js frente a muchos (es decir, mire stackoverflow.com/questions/555696/… ). Además, no hay nada malo en usar clases e identificadores en el cuerpo si simplifica el código y funciona para usted. Modernizr ( modernizr.com ) hace esto, y algunas otras librerías también.
welldan97

2
@ MarnenLaibow-Koser, la cartera de activos de rieles, para mí, parece un buen candidato para la comparación con la compilación. Un programador escribe su javascript en buenos módulos desacoplados, y luego se agrupa, minimiza y sirve. Al igual que en el caso de los lenguajes compilados, siempre habrá programadores que piensen que están un paso por delante del compilador ... pero creo que esto rara vez es cierto.
Ziggy

65

Veo que has respondido tu propia pregunta, pero aquí hay otra opción:

Básicamente, estás asumiendo que

//= require_tree .

es requerido. No es. Siéntase libre de eliminarlo. En mi aplicación actual, la primera que estoy haciendo con 3.1.x honestamente, he creado tres archivos JS de nivel superior diferentes. Mi application.jsarchivo solo tiene

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

De esta manera, puedo crear subdirectorios, con sus propios archivos JS de nivel superior, que solo incluyen lo que necesito.

Las claves son:

  1. Puede eliminarlo require_tree: Rails le permite cambiar las suposiciones que hace
  2. El nombre no tiene nada de especial application.js: cualquier archivo del assets/javascriptsubdirectorio puede incluir directivas de preprocesador con//=

Espero que ayude y agregue algunos detalles a la respuesta de ClosureCowboy.

Sujal


8
+1 Es bueno saberlo para un novato como yo. Le daría +2 si pudiera.
jrhorn424

55
@sujal Exactamente. El equipo central de Rails es conocido por la administración abismal de JavaScript. Siéntase libre de ignorar sus sugerencias y simplemente use las partes buenas de la cartera de activos. :)
Marnen Laibow-Koser

1
Muchas gracias por este consejo. No tengo varios archivos JS de "nivel superior", según el módulo de mi aplicación. Funciona bien.
elsurudo

1
1 El punto importante aquí es que para mí puede reemplazar //= require_tree .con //= require_directory .lo que puede mantener todos los archivos existentes en las que se encuentran y crean nuevos directorios para archivos específicos de la página.
zelanix

41

Otra opción: para crear archivos específicos de página o modelo, puede crear directorios dentro de su assets/javascripts/carpeta.

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

Su application.jsarchivo de manifiesto principal podría configurarse para cargar sus archivos global/. Las páginas específicas o grupos de páginas podrían tener sus propios manifiestos que cargan archivos de sus propios directorios específicos. Sprockets combinará automáticamente los archivos cargados porapplication.js con los archivos específicos de su página, lo que permite que esta solución funcione.

Esta técnica también se puede utilizar style_sheets/.


13
Me has hecho desear magdalenas ahora ... ¡Dangit!
Chuck Bergeron

Realmente me gusta esta solución. El único problema que tengo es que esos manifiestos adicionales no están comprimidos / uglificados. Sin embargo, están correctamente compilados. ¿Hay alguna solución o me falta algo?
clst

1
¿Significa esto que el navegador carga un archivo js, ​​que es una combinación de archivo global + específico de página?
lulalala

¿Podría echar un vistazo a mi pregunta si está disponible? stackoverflow.com/questions/17055213/…
Maximus S

1
@clst Creo que esta es la respuesta que estás buscando: guides.rubyonrails.org/asset_pipeline.html#precompiling-assets
FrontierPsycho

23

Aprecio todas las respuestas ... y no creo que realmente estén llegando al problema. Algunos de ellos son sobre el estilo y no parecen relacionarse ... y otros solo mencionanjavascript_include_tag ... lo que sé que existe (obviamente ...) pero parece que el camino de Rails 3.1 en adelante es concluir todo su Javascript en 1 archivo en lugar de cargar Javascript individual en la parte inferior de cada página.

La mejor solución que se me ocurre es envolver ciertas características en divetiquetas con ids o classes. En el código javascript. Luego simplemente verifica si el ido classestá en la página, y si lo está, ejecuta el código de JavaScript que está asociado con él. De esta manera, si el elemento dinámico no está en la página, el código JavaScript no se ejecuta, a pesar de que se ha incluido en el application.jsarchivo masivo empaquetado por Sprockets.

Mi solución anterior tiene la ventaja de que si se incluye un cuadro de búsqueda en 8 de las 100 páginas, se ejecutará solo en esas 8 páginas. Tampoco tendrá que incluir el mismo código en 8 de las páginas del sitio. De hecho, nunca más tendrá que incluir etiquetas de script manual en su sitio en ningún lugar, excepto tal vez para precargar datos.

Creo que esta es la respuesta real a mi pregunta.


Pero en realidad quieres esas <script>etiquetas manuales . Sí, las clases y los identificadores son parte de la respuesta, pero no tiene sentido que el usuario cargue JavaScript que esa página en particular no requiere.
Marnen Laibow-Koser

44
@ MarnenLaibow-Koser, la razón para no agregar etiquetas de script manuales a cada página única es que debe descargar el contenido del script en cada vista de página. Si usted es capaz de empaquetar todo javascript en application.js utilizando el ducto activo, entonces el usuario descarga los guiones sólo una vez, y tirones application.js de la memoria caché en todas las cargas de página posteriores
jakeonrails

@jakeonrails "la razón para no agregar etiquetas de script manuales a cada página única es que tiene que descargar el contenido del script en cada vista de página", es un error. La secuencia de comandos se descargará una vez, luego se obtendrá de la memoria caché del navegador en futuras solicitudes. "Si puede empaquetar todos los javascript en application.js utilizando la canalización de activos, el usuario descarga esos scripts solo una vez", es cierto, pero a costa de muchos códigos innecesarios. Si puede estructurar su JS en muchos archivos pequeños en lugar de uno grande, obtendrá beneficios de almacenamiento en caché sin código innecesario.
Marnen Laibow-Koser

1
@ MarnenLaibow-Koser Creo que hubiera sido mejor decir que si empaqueta todo en un script, su usuario solo tiene que descargar 1 script para cualquier página de su sitio. Si tiene múltiples scripts para diferentes partes de su aplicación, entonces obviamente el usuario debe descargar más de un script. Ambos métodos se almacenarán en caché, por supuesto, pero en la mayoría de los casos (aplicaciones pequeñas y medianas), servir una aplicación.js una vez será más eficiente para la descarga. El análisis del JS puede ser otra historia, dependiendo de lo que sirva.
jakeonrails

1
@Ziggy Además, si el archivo pequeño solo se usa en 8 de cada 100 páginas, ¿por qué el código debe permanecer en la memoria caché del usuario todo el tiempo? Es mejor dejar caer las cosas que no se necesitan.
Marnen Laibow-Koser

16

Me doy cuenta de que voy a llegar a esta fiesta un poco tarde, pero quería agregar una solución que he estado usando últimamente. Sin embargo, permítanme mencionar primero ...

The Rails 3.1 / 3.2 Way (No, señor. No me gusta).

Ver: http://guides.rubyonrails.org/asset_pipeline.html#how-to-use-the-asset-pipeline

Incluyo lo siguiente en aras de la exhaustividad en esta respuesta, y porque no es una solución inviable ... aunque no me importa mucho.

"Rails Way" es una solución orientada al controlador, en lugar de estar orientada a la vista, como solicitó el autor original de esta pregunta. Hay archivos JS específicos del controlador que llevan el nombre de sus respectivos controladores. Todos estos archivos se colocan en un árbol de carpetas que NO se incluye de manera predeterminada en ninguna de las aplicaciones.js requieren directivas.

Para incluir código específico del controlador, se agrega lo siguiente a una vista.

<%= javascript_include_tag params[:controller] %>

Odio esta solución, pero está ahí y es rápida. Presumiblemente, podría llamar a estos archivos algo así como "people-index.js" y "people-show.js" y luego usar algo como "#{params[:controller]}-index"para obtener una solución orientada a la vista. Nuevamente, solución rápida, pero no me sienta bien.

Mi manera de atributo de datos

Llámame loco, pero quiero que TODO mi JS esté compilado y minimizado en application.js cuando lo implemente. No quiero tener que recordar incluir estos pequeños archivos rezagados por todas partes.

Cargo todo mi JS en un archivo compacto, que pronto será almacenado en caché del navegador. Si una determinada parte de mi application.js necesita ser activada en una página, dejo que el HTML me lo diga, no Rails.

En lugar de bloquear mi JS a ID de elementos específicos o ensuciar mi HTML con clases de marcador, utilizo un atributo de datos personalizado llamado data-jstags .

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

En cada página, yo uso - inserte el método de biblioteca JS preferido aquí - para ejecutar el código cuando el DOM haya terminado de cargarse. Este código de arranque realiza las siguientes acciones:

  1. Iterar sobre todos los elementos en el DOM marcado con data-jstag
  2. Para cada elemento, divida el valor del atributo en el espacio, creando una matriz de cadenas de etiquetas.
  3. Para cada cadena de etiqueta, realice una búsqueda en un hash para esa etiqueta.
  4. Si se encuentra una clave coincidente, ejecute la función que está asociada con ella, pasando el elemento como parámetro.

Digamos que tengo lo siguiente definido en algún lugar de mi 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
};

El evento bootstrapping aplicará las funciones my_autosuggest_inity my_hint_initcontra la entrada de búsqueda, convirtiéndola en una entrada que muestra una lista de sugerencias mientras el usuario escribe, y proporcionará algún tipo de sugerencia de entrada cuando la entrada se deja en blanco y desenfocada.

A menos que se etiquete algún elemento data-jstag="auto-suggest", el código de sugerencia automática nunca se activa. Sin embargo, siempre está ahí, minimizado y eventualmente almacenado en caché en mi application.js para los momentos en que lo necesito en una página.

Si necesita pasar parámetros adicionales a sus funciones JS etiquetadas, deberá aplicar algo de creatividad. Agregue atributos de parámetros de datos, cree algún tipo de sintaxis de parámetros o incluso utilice un enfoque híbrido.

Incluso si tengo un flujo de trabajo complicado que parece específico del controlador, simplemente crearé un archivo para él en mi carpeta lib, lo empaquetaré en application.js y lo etiquetaré con algo como 'new-thing-wizard'. Cuando mi bootstrap llegue a esa etiqueta, mi simpático y elegante asistente será instanciado y ejecutado. Se ejecuta para la (s) vista (s) de ese controlador cuando es necesario, pero no está acoplado al controlador. De hecho, si codifico correctamente mi asistente, podría proporcionar todos los datos de configuración en las vistas y, por lo tanto, volver a utilizar mi asistente más adelante para cualquier otro controlador que lo necesite.

De todos modos, así es como he estado implementando JS específicos de la página durante un tiempo, y me ha servido bien tanto para diseños de sitios simples como para aplicaciones más complejas / ricas. Con suerte, una de las dos soluciones que he presentado aquí, a mi manera o a la manera de Rails, es útil para cualquiera que se encuentre con esta pregunta en el futuro.


66
Un pequeño detalle: existe esta noción en su respuesta de que una vez que el js se almacena en caché del navegador, no tiene ningún impacto. Esto no es del todo cierto. El navegador de hecho evita la descarga, si el archivo js está correctamente almacenado en caché, pero aún compila el código en cada renderizado de la página. Entonces, tienes que equilibrar las compensaciones. Si tiene mucho JS en conjunto, pero solo se usa un poco por página, es posible que pueda mejorar los tiempos de página separando el JS.
sujal

Para obtener más información sobre los efectos prácticos de ese paso de compilación del que estoy hablando, consulte la explicación de 37 Señales de cómo pjax impactó en Basecamp Siguiente: 37signals.com/svn/posts/…
sujal

Ese es un punto justo. Después de leer el artículo y mirar hacia atrás en los proyectos en los que he usado la solución anterior, me doy cuenta de que escribí esencialmente la misma solución "enviar el HTML modificado" que mencionan en el artículo. La frecuente compilación de JS no había sido un problema en mis proyectos debido a eso. El paso de compilación es algo que tendré en cuenta cuando esté trabajando en sitios menos orientados a "aplicaciones de escritorio".
Ryan

2
Voto negativo para "Llámame loco, pero quiero que TODO mi JS esté compilado y minimizado en application.js cuando lo implemente". Realmente no quiere esto, ya que hace que el usuario cargue JavaScript que no necesita y hace que sus controladores busquen atributos que ni siquiera estarán allí. Tener todo en app.js es tentador, y Rails ciertamente lo hace fácil, pero lo correcto es modularizar mejor JavaScript.
Marnen Laibow-Koser

Tiene derecho a una opinión diferente ... y técnicamente tiene derecho a votar en contra de una diferencia de opinión. Sin embargo, sería bueno ver alguna justificación de por qué un archivo grande y en caché es inferior a forzar múltiples solicitudes HTTP para tomar JS modularizado. Además, está equivocado con respecto al procedimiento de búsqueda del controlador. Los valores de la etiqueta NO se buscan. Solo se realiza una búsqueda y extrae todos los elementos que tienen un atributo data-jstag. No busca por nombre de etiqueta, solo encuentra todos los elementos que tienen etiquetas y luego crea instancias solo de los objetos necesarios.
Ryan

7

Esto fue respondido y aceptado hace mucho tiempo, pero se me ocurrió mi propia solución basada en algunas de estas respuestas y mi experiencia con Rails 3+.

La cartera de activos es dulce. Úsalo.

Primero, en su application.jsarchivo, elimine//= require_tree.

Luego, en su application_controller.rbcrear un método auxiliar:

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

Luego, en su application.html.erbarchivo de diseño, agregue su nuevo ayudante entre los javascript existentes incluidos, con el prefijo del rawayudante:

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

Voila, ahora puede crear fácilmente JavaScript específico de la vista usando la misma estructura de archivos que usa en cualquier otro lugar en rieles. ¡Simplemente pegue sus archivos app/assets/:namespace/:controller/action.js.erb!

Espero que ayude a alguien más!


1
¿Esto no causará problemas después de que los activos estén precompilados y en tiempo de ejecución <%= raw ... %>devuelva un 404?
Nishant

Creo que la canalización de activos no es dulce, ya que crea un montón de archivos que a menudo no deberían usarse. Entonces, para mí, confiar en la cartera de activos es crear una dependencia en un sistema ineficiente.
Deborah

1
@DeborahSpeece ¿Cuándo crea la cartera de activos archivos que no deberían usarse? ¿Está confundiendo la cartera de activos (buena) con require_tree /(mala)?
Marnen Laibow-Koser

6

Puede agregar esta línea en su archivo de diseño (por ejemplo, application.html.erb) para cargar automáticamente el archivo javascript específico del controlador (el que se creó cuando generó el controlador):

<%= javascript_include_tag params[:controller] %>

También puede agregar una línea para cargar automáticamente un archivo de script por acción.

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

Simplemente coloque los scripts de su página en un subdirectorio con el nombre del controlador. En estos archivos puede incluir otros scripts usando = require. Sería bueno crear un asistente para incluir el archivo solo si existe, para evitar una falla 404 en el navegador.


6
<%= javascript_include_tag params[:controller] %>

2
Parece que podría responder la pregunta. ¿Podría por favor agregar más a la respuesta para desarrollarla?


5

La gema LoadJS es otra opción:

LoadJS proporciona una forma de cargar el código Javascript específico de la página en una aplicación Rails sin perder la magia proporcionada por Sprockets. Todo su código Javascript continuará minimizado en un archivo Javascript, pero algunas partes solo se ejecutarán para ciertas páginas.

https://github.com/guidomb/loadjs


3

La respuesta de Philip es bastante buena. Aquí está el código para que funcione:

En application.html.erb:

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

Asumiendo que su controlador se llama Proyectos, eso generará:

<body class="projects">

Luego, en projects.js.coffee:

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

Downvoting: cualquier solución que ponga una clase en el <body>es ipso facto incorrecta. Vea mis comentarios en otra parte de esta página.
Marnen Laibow-Koser

No hagas esto. El problema aquí es que cada vez que agrega uno de estos, está agregando otra pieza de js que debe ejecutarse en la carga de la página. Definitivamente podría causar cierta degradación del rendimiento a medida que su proyecto crezca.
ifightcrime

2

JavaScripts solo se fusionan cuando le dice a Rails (Sprockets, más bien) que los combine.


Por supuesto. Supongo que pregunto porque los valores predeterminados de Rails incluyen todo en la carpeta ... lo que significa que David tiene la intención de que hagas eso. Pero como dije en el otro comentario a @rubyprince, no estoy seguro acerca de la ejecución cuando se hace de esta manera. Estoy pensando que tengo que desactivar //= require_tree .?
Fire Emblem

@ FireEmblem Sí. require_tree .Suele ser una mala idea.
Marnen Laibow-Koser

2

Así es como resolví el problema del estilo: (disculpe el Haml)

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

De esta manera, comienzo todos los archivos .css.sass específicos de la página con:

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

De esta manera, puede evitar fácilmente cualquier enfrentamiento. Cuando se trata de archivos .js.coffee , puede simplemente inicializar elementos como;

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

Espero que esto haya ayudado a algunos.


1
Lea el último bit nuevamente, para javascript puede aprovechar la misma estructura utilizada para las hojas de estilo para inicializar las funciones específicas de la vista.
zeeraw

Philip, $('#post > #edit') ->parece ser inválido. ¿Cómo alcanzas jQuery para trabajar dentro de un alcance?
Ramon Tayag

2
Recientemente comencé a cargar todos los scripts y hojas de estilo java específicos del controlador llamando a esto en application.html.haml; = javascript_include_tag "application"y de = javascript_include_tag params[:controller]esta manera puedo mantener el código de JavaScript confinado sin tener que especificar un alcance dentro del archivo.
zeeraw


2

Estoy de acuerdo con su respuesta, para verificar si ese selector está allí, use:

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

(no vi a nadie agregar la solución real)


2

No veo una respuesta que realmente lo reúna todo y lo presente para usted. Por lo tanto, trataré de poner meleyal , sujal (a la ClosureCowboy ), la primera parte de la respuesta de Ryan e incluso la audaz declaración de Gal sobre Backbone.js ... todo junto de una manera corta y clara. Y, quién sabe, incluso podría cumplir con los requisitos de Marnen Laibow-Koser .

Ediciones de ejemplo

assets / javascripts / application.js

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


views / layouts / 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>


views / foo / index.html.erb

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


assets / javascripts / foo.js

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


assets / javascripts / foostuff / foothis.js.coffee

alert "Hello world!"


Breve descripción

  • Elimine //= require_tree .de application.js y enumere solo el JS que comparte cada página.

  • Las dos líneas que se muestran arriba en application.html.erb le dicen a la página dónde incluir application.js y su JS específico de la página.

  • Las tres líneas que se muestran arriba en index.html.erb le indican a su vista que busque algunos JS específicos de la página e incluirlos en una región de rendimiento nombrada llamada ": javascript" (o como quiera nombrarlo). En este ejemplo, el controlador es "foo", por lo que Rails intentará incluir "foo.js" en la región de rendimiento de javascript en el diseño de la aplicación.

  • Enumere su JS específico de la página en foo.js (o como se llame al controlador). Lista de bibliotecas comunes, un árbol, directorios, lo que sea.

  • Mantenga su JS específico de la página personalizada en un lugar donde pueda hacer referencia fácilmente, aparte de su otro JS personalizado. En este ejemplo, foo.js requiere el árbol de forraje, así que coloque su JS personalizado allí, como foothis.js.coffee .

  • No hay reglas estrictas aquí. Siéntase libre de mover cosas y tal vez incluso crear múltiples regiones de rendimiento de varios nombres en varios diseños si es necesario. Esto solo muestra un posible primer paso adelante. (No lo hago exactamente así dado nuestro uso de Backbone.js. También podría elegir colocar foo.js en una carpeta llamada foo en lugar de foostuff, pero aún no lo he decidido).

Notas

Puede hacer cosas similares con CSS y <%= stylesheet_link_tag params[:controller] %>esto está más allá del alcance de la pregunta.

Si me perdí una deslumbrante práctica recomendada aquí, envíeme una nota y me adaptaré. Rails es bastante nuevo para mí y, sinceramente, hasta ahora no estoy terriblemente impresionado con el caos que trae por defecto al desarrollo empresarial y todo el tráfico que genera el programa Rails promedio.


este parece ser el camino a seguir, voy a ver si puedo implementarlo en mi propia aplicación, gracias por la respuesta detallada.
martincarlin87

1

Tengo otra solución, que aunque primitiva funciona bien para mí y no necesita ninguna estrategia de carga selectiva sofisticada. Ponga su función lista para documentos normales, pero luego pruebe la ubicación actual de Windows para ver si es la página para la que está destinado su JavaScript:

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

Esto todavía permite que todos los js se carguen por rails 3.x en un paquete pequeño, pero no genera demasiados gastos generales ni conflictos con las páginas para las que no está destinado el js.


1

La respuesta de Ryguy es una buena respuesta, a pesar de que se ha votado en puntos negativos.

Especialmente si está usando algo como Backbone JS: cada página tiene su propia vista Backbone. Luego, el archivo erb solo tiene una sola línea de JavaScript en línea que activa la clase de vista de red troncal derecha. Lo considero una sola línea de 'código de pegamento' y, por lo tanto, el hecho de que su línea está bien. La ventaja es que puede mantener su "require_tree" que le permite al navegador almacenar en caché todo el javascript.

en show.html.erb, tendrás algo como:

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

y en su archivo de diseño, necesitará:

<%= yield :javascript %>

Votación a favor. JavaScript en línea nunca es una buena idea. Incluso si es un código de pegamento, debe estar en un archivo externo.
Marnen Laibow-Koser

1

Mueva todos sus archivos JS commom a una subcarpeta como 'app / assets / javascript / global' y luego, en application.js, modifique la //= require_tree .línea para//= require_tree ./global .

Ahora es libre de colocar su JS específico del controlador en la raíz 'app / assets / javascript /' y no se incluirán en el JS compilado, que se utilizará solo cuando los llame a través = javascript_include_tagde su controlador / vista.


De ninguna manera, es una carga de JavaScript para cargar en una página. Ni siquiera importa si está en caché.
jackyalcine

1

Aunque tiene varias respuestas aquí, creo que su edición es probablemente la mejor opción. Un patrón de diseño que utilizamos en nuestro equipo que obtuvimos de Gitlab es el patrón Dispatcher. Hace algo similar a lo que está hablando, sin embargo, el nombre de la página se establece en la etiqueta del cuerpo mediante rieles. Por ejemplo, en su archivo de diseño, simplemente incluya algo como (en HAML):

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

Entonces solo tenga un cierre y una declaración de cambio en su dispatcher.js.coffeearchivo en su carpeta javascripts de la siguiente manera:

$ ->
  new Dispatcher()

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

Todo lo que necesita hacer en los archivos individuales (digamos products.js.coffee o login.js.coffeepor ejemplo) es encerrarlos en una clase y luego globalizar ese símbolo de clase para que pueda acceder a él en el despachador:

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

Gitlab tiene varios ejemplos de esto con los que es posible que desee hurgar en caso de que tenga curiosidad :)


1

Paloma proyecto ofrece un enfoque interesante para administrar el código JavaScript específico de la página.

Ejemplo de uso de sus documentos:

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

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

1

Paso 1. eliminar require_tree. en su application.js y application.css.

Paso 2. Edite su application.html.erb (por defecto de rails) en la carpeta de diseño. Agregue "params [: controller]" en las siguientes etiquetas.

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

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

Paso 3. Agregue un archivo en 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

referencias: http://theflyingdeveloper.com/controller-specific-assets-with-rails-4/


Si bien esto puede responder teóricamente a la pregunta, sería preferible incluir aquí las partes esenciales de la respuesta y proporcionar el enlace para referencia.
Bhargav Rao

0

No he probado esto, pero parece que lo siguiente es cierto:

  • si tiene un content_for que es javascript (por ejemplo, con javascript real dentro de él), las ruedas dentadas no lo sabrían y, por lo tanto, esto funcionaría de la misma manera que ahora.

  • si desea excluir un archivo del gran paquete de javascript, debe ingresar al archivo config / sprockets.yml y modificar los archivos de origen en consecuencia. Luego, simplemente incluiría cualquiera de los archivos que excluyó cuando fue necesario.


Entonces, ¿excluir archivos o usar JavaScript personalizado en la página es la "forma correcta"? ¿Es así como David pretendía que la gente lo usara?
Fire Emblem

@FireEmblem No me importa mucho lo que David pretendía, porque no creo que David entienda cómo organizar JavaScript correctamente.
Marnen Laibow-Koser


0

Combiné algunas respuestas en:

Ayudante de aplicación:

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

diseños / 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

Primero: eliminar \\=require_treede application.js Segundo: todo su código JS debe ubicarse en /app/assets/javascritpty todo su código CSS debe ubicarse en/app/assets/stylesheets


-2

Siguiendo el ejemplo de Ryan, esto es lo que he hecho:

application.js.coffee

$ ->
    view_method_name = $("body").data("view") + "_onload"
    eval("#{view_method_name}()") if eval("typeof #{view_method_name} == 'function'")
    view_action_method_name = $("body").data("view") + "_"+$("body").data("action")+"_onload"
    eval("#{view_action_method_name}()") if eval("typeof #{view_action_method_name} == 'function'")

users.js.coffee (coffeescript específico del controlador, por ejemplo, controlador: usuarios, acción: tablero)

window.users_dashboard_onload = () ->
    alert("controller action called")
window.users_onload = () ->
    alert("controller called")

application.html.haml

%body{:data=>{:view=>controller.controller_name, :action=>controller.action_name}}

Votación a favor. Esto es ridículamente complicado, por no mencionar inseguro (debido a eval) si su HTML se ve comprometido por un servidor roto o un script de usuario malicioso.
Marnen Laibow-Koser

-3

Aquí le mostramos cómo hacerlo, especialmente si no tiene que ejecutar toneladas de bibliotecas para su página específica, sino solo para ejecutar unos cientos de líneas de JS más o menos.

Dado que está perfectamente bien incrustar código Javascript en HTML, solo cree en el directorio app / views shared.js y coloque allí su código específico de página / páginas dentro de my_cool_partial.html.erb

<script type="text/javascript"> 
<!--
  var your_code_goes_here = 0;
  function etc() {
     ...
  }
-->
</script>

Entonces, desde donde quieras, simplemente haz lo siguiente:

  = render :partial => 'shared.js/my_cool_partial'

Y eso es todo, k?


2
Votación a favor. JavaScript en línea nunca es aconsejable. HTML solo debe contener marcado. JS y CSS deben estar en archivos separados y reutilizables.
Marnen Laibow-Koser
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.