Usando Rails 3.1, dove metti il ​​tuo codice JavaScript "specifico della pagina"?


388

Per quanto ne so, tutto il tuo JavaScript viene unito in 1 file. Rails lo fa per impostazione predefinita quando si aggiunge //= require_tree .alla fine del application.jsfile manifest.

Sembra un vero salvavita, ma sono un po 'preoccupato per il codice JavaScript specifico della pagina. Questo codice viene eseguito su ogni pagina? L'ultima cosa che voglio è che tutti i miei oggetti vengano istanziati per ogni pagina quando sono necessari solo su 1 pagina.

Inoltre, non esiste un potenziale per il codice che si scontra anche?

Oppure metti un piccolo scripttag nella parte inferiore della pagina che chiama semplicemente un metodo che esegue il codice javascript per la pagina?

Allora non hai più bisogno di request.js?

Grazie

MODIFICARE : apprezzo tutte le risposte ... e non penso che stiano davvero affrontando il problema. Alcuni riguardano lo stile e non sembrano relazionarsi ... e altri menzionano solo javascript_include_tag... che io conosco esiste (ovviamente ...) ma sembrerebbe che il modo di andare avanti di Rails 3.1 sia concludere tutto il tuo JavaScript in 1 file invece di caricare singoli JavaScript nella parte inferiore di ogni pagina.

La migliore soluzione che posso trovare è quella di racchiudere alcune funzionalità nei divtag con idS o classes. Nel codice JavaScript, devi solo controllare se ido classè nella pagina e, se lo è, esegui il codice JavaScript ad esso associato. In questo modo se l'elemento dinamico non si trova sulla pagina, il codice JavaScript non viene eseguito, anche se è stato incluso nell'enorme application.jsfile impacchettato da Sprockets.

La mia soluzione sopra ha il vantaggio che se una casella di ricerca è inclusa in 8 delle 100 pagine, verrà eseguita solo su quelle 8 pagine. Inoltre, non dovrai includere lo stesso codice in 8 pagine del sito. In effetti, non dovrai mai più includere tag manuali di script sul tuo sito.

Penso che questa sia la vera risposta alla mia domanda.


11
"Il modo in cui Rails 3.1 sta andando avanti è quello di racchiudere tutto il tuo Javascript in 1 file invece di caricare singoli Javascript nella parte inferiore di ogni pagina." - Solo perché il core team di Rails è, ed è sempre stato, davvero pessimo nel sapere come per gestire JavaScript. I file piccoli sono generalmente migliori (vedi i miei commenti altrove). Quando si tratta di JavaScript, la strada di Rails è raramente la strada giusta (tranne per la pipeline di risorse, che prende a calci in culo e l'incoraggiamento di CoffeeScript).
Marnen Laibow-Koser,

Quindi includerai i tuoi file js specifici della pagina in ogni pagina? Penso che sia uno spreco, sono più d'accordo con la risposta di ClosureCowboy.
Gerky,

1
Hai dato un'occhiata alla risposta accettata per questa domanda? stackoverflow.com/questions/6571753/...
rassom

1
@DutGRIFF In altre parole: no, in questo caso non è meglio fare le cose come Rails (o almeno non inserire tutto application.js), e infatti il ​​riferimento che hai fornito indica perché è così: il download è il parte più lenta del processo di esecuzione di JS. Molti piccoli file sono più memorizzabili nella cache di uno grande. La gente di Unholy Rails non sembra rendersi conto, quindi, che le loro raccomandazioni sono incompatibili con i principi a cui stanno cercando di aderire, e quindi le loro raccomandazioni non dovrebbero essere prese sul serio.
Marnen Laibow-Koser,

1
@DutGRIFF No, un file JS di grandi dimensioni non sarebbe normalmente una buona cosa anche una volta memorizzato nella cache. Vedi i miei commenti altrove in questa pagina: i piccoli file possono indirizzare meglio pagine specifiche e possono essere memorizzati in cache con una granularità più fine. Non vedo alcun caso d'uso buono per un singolo file di grandi dimensioni a meno che non v'è alcun codice della pagina-specifica a tutti .
Marnen Laibow-Koser,

Risposte:


157

I documenti della pipeline delle risorse suggeriscono come eseguire JS specifici del controller:

Ad esempio, se ProjectsControllerviene generato un, ci sarà un nuovo file in app/assets/javascripts/projects.js.coffeee un altro in app/assets/stylesheets/projects.css.scss. È necessario inserire JavaScript o CSS univoci in un controller all'interno dei rispettivi file di risorse, poiché questi file possono essere caricati solo per questi controller con linee come <%= javascript_include_tag params[:controller] %>o <%= stylesheet_link_tag params[:controller] %>.

Link a: asset_pipeline


50
Questo è il modo più elegante per farlo. Inoltre, dovrai rimuovere la riga // = require_tree. dall'applicazione.js.coffee
zsljulius,

2
Sono totalmente d'accordo con questo metodo. Gli altri metodi sembrano molto goffi e finiscono comunque per caricare un file js gigante. Il progetto su cui sto lavorando ha quasi 2 MB di file / plugin JS ecc. DOPO essere combinati / minimizzati.
Bill Garrison,

2
Sono abbastanza nuovo su Rails, ma mi sembra che questo dovrebbe essere il comportamento predefinito.
Ross Hambrick,

12
Per il controllo specifico dell'azione ho questo nel mio layout, poiché non tutte le azioni per ogni controller hanno JS specifico. page_specific_js = "#{params[:controller]}_#{params[:action]}"e poi; javascript_include_tag page_specific_js if Rails.application.assets.find_asset page_specific_js
Sujimichi,

2
Le azioni specifiche del controller vengono ancora minimizzate? Vengono aggiunti al singolo file js creato da ruote dentate o alci questo porta a più richieste di file di risorse?
Jason il

77

Per i j specifici della pagina puoi usare la soluzione Garber-Irish .

Quindi la tua cartella javascripts di Rails potrebbe apparire così per due controller: auto e utenti:

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

E i javascript saranno così:

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

e markup_based_js_execution conterrà il codice per l'oggetto UTIL e sull'esecuzione UTIL.init pronta per DOM.

E non dimenticare di metterlo nel tuo file di layout:

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

Penso anche che sia meglio usare le classi invece degli data-*attributi, per i migliori CSS specifici della pagina. Come ha detto Jason Garber: i selettori CSS specifici della pagina possono diventare davvero imbarazzanti (quando si utilizzano gli data-*attributi)

Spero che questo ti possa aiutare.


4
Cosa succede se è necessaria una variabile disponibile per tutte le azioni nel controller degli utenti, ma non disponibile in altri controller? Questo metodo non presenta alcuni problemi di scoping?
tybro0103

@ tybro0103, penso che per implementare questo comportamento ti piacerebbe scrivere qualcosa come window.varForOneController='val'in questa funzione init controller. Anche gon gem può essere d'aiuto qui ( github.com/gazay/gon ). Possono esserci altre soluzioni alternative.
welldan97,

1
@ welldan97 Downvoting non per la tua spiegazione - che è eccellente - ma perché la struttura Garber-Irish è malvagia. Carica tutto il tuo JS su ogni pagina e dipende da classi e ID sull'elemento <body> per sistemare le cose. Questo è un segno sicuro di combattere il DOM: in circostanze normali l'elemento <body> non dovrebbe aver bisogno di una classe o di un ID, dato che ce n'è solo uno in un documento. Il modo corretto per farlo è semplicemente rimuovere //= require_tree .e utilizzare JavaScript specifico della pagina. Se stai attivamente cercando di non farlo, allora stai lottando per cattive pratiche.
Marnen Laibow-Koser,

2
@ MarnenLaibow-Koser Personalmente credo che il caricamento di tutti i j su ogni pagina sia utile per la maggior parte dei progetti quando si combinano tutti i j in un file e si minimizza. Credo che nel complesso funzioni più velocemente per l'utente. Almeno è più simile a un conflitto tra un file js e molti (vale a dire guardare stackoverflow.com/questions/555696/… ). Inoltre, non c'è niente di male nell'usare classi e ID su body se rende il codice più semplice e funziona per te. Modernizr ( modernizr.com ) fa questo, e anche alcune altre librerie.
welldan97,

2
@ MarnenLaibow-Koser, la pipeline delle risorse di binari, per me, sembra un buon candidato per il confronto con la compilazione. Un programmatore scrive il proprio javascript in simpatici moduli disaccoppiati, quindi viene raggruppato, minimizzato e servito. Proprio come nel caso dei linguaggi compilati, ci saranno sempre programmatori che pensano di essere un passo avanti rispetto al compilatore ... ma penso che ciò sia raramente vero.
Ziggy,

65

Vedo che hai risposto alla tua domanda, ma ecco un'altra opzione:

Fondamentalmente, lo stai assumendo

//= require_tree .

è obbligatorio. Non è. Sentiti libero di rimuoverlo. Nella mia attuale applicazione, la prima che sto facendo con 3.1.x onestamente, ho creato tre diversi file JS di livello superiore. Il mio application.jsfile ha solo

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

In questo modo, posso creare sottodirectory, con i loro file JS di livello superiore, che includono solo ciò di cui ho bisogno.

Le chiavi sono:

  1. È possibile rimuovere require_tree: Rails consente di modificare i presupposti che formula
  2. Non c'è niente di speciale nel nome application.js: qualsiasi file nella assets/javascriptsottodirectory può includere direttive pre-processore con//=

Spero che ciò aiuti e aggiunga alcuni dettagli alla risposta di ClosureCowboy.

Sujal


8
+1 Questo è fantastico da sapere per un principiante come me. Darei +2 se potessi.
jrhorn424,

5
@sujal Exactly. Il core team di Rails è noto per la gestione abissale di JavaScript. Sentiti libero di ignorare i loro suggerimenti e usa solo le parti buone della pipeline degli asset. :)
Marnen Laibow-Koser,

1
Grazie mille per questo consiglio. Non ho diversi file JS "di livello superiore", a seconda del modulo della mia app. Funziona bene.
elsurudo,

1
+1 Il punto importante qui per me è che puoi sostituirlo //= require_tree .con //= require_directory .così puoi mantenere tutti i file esistenti dove sono e creare nuove directory per i file specifici della pagina.
zelanix,

41

Un'altra opzione: per creare file specifici per pagina o modello, è possibile creare directory all'interno della assets/javascripts/cartella.

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

Il tuo application.jsfile manifest principale potrebbe essere configurato per caricare i suoi file da global/. Pagine o gruppi specifici di pagine potrebbero avere i propri manifest che caricano file dalle proprie directory specifiche. I pignoni combinano automaticamente i file caricati application.jscon i file specifici della pagina, il che consente a questa soluzione di funzionare.

Questa tecnica può essere utilizzata anche per style_sheets/.


13
Mi hai fatto desiderare cupcakes ora .. Dangit!
Chuck Bergeron,

Mi piace molto questa soluzione. L'unico problema che ho è che quei manifesti extra non sono compressi / ugificati. Tuttavia sono compilati correttamente. C'è una soluzione o mi sto perdendo qualcosa?
clst

1
significa che il browser carica un file js, ovvero una combinazione di file globali + specifici per pagina?
Lulalala,

Potresti dare un'occhiata alla mia domanda se sei disponibile? stackoverflow.com/questions/17055213/…
Maximus S

1
@clst Credo che questa sia la risposta che stai cercando: guide.rubyonrails.org/asset_pipeline.html#precompiling-assets
FrontierPsycho

23

Apprezzo tutte le risposte ... e non penso che stiano davvero affrontando il problema. Alcuni riguardano lo stile e non sembrano relazionarsi ... e altri menzionano solo javascript_include_tag... che io so esiste (ovviamente ...) ma sembrerebbe che il modo in cui Rails 3.1 andrà avanti sia quello di concludere il tuo Javascript in 1 file anziché caricare il singolo Javascript nella parte inferiore di ogni pagina.

La migliore soluzione che posso trovare è quella di racchiudere alcune funzionalità nei divtag con idS o classes. Nel codice JavaScript. Quindi controlla solo se ido classè nella pagina e, se lo è, esegui il codice javascript ad esso associato. In questo modo se l'elemento dinamico non è nella pagina, il codice javascript non viene eseguito, anche se è stato incluso nell'enorme application.jsfile compresso da Sprockets.

La mia soluzione sopra ha il vantaggio che se una casella di ricerca è inclusa in 8 delle 100 pagine, verrà eseguita solo su quelle 8 pagine. Inoltre, non dovrai includere lo stesso codice in 8 pagine del sito. In effetti, non dovrai mai più includere tag manuali di script sul tuo sito, tranne forse per precaricare i dati.

Penso che questa sia la vera risposta alla mia domanda.


Ma in realtà vuoi quei <script>tag manuali . Sì, classi e ID fanno parte della risposta, ma non ha senso per l'utente caricare JavaScript che quella particolare pagina non richiede.
Marnen Laibow-Koser,

4
@ MarnenLaibow-Koser il motivo per cui non è stato aggiunto tag di script manuali a ciascuna pagina univoca è che è necessario scaricare il contenuto dello script in ogni visualizzazione di pagina. Se sei in grado di impacchettare tutto javascript in application.js utilizzando la pipeline delle risorse, l'utente scarica quegli script una sola volta e estrae application.js dalla cache su tutti i successivi caricamenti di pagina
jakeonrails

@jakeonrails "la ragione per non aggiungere tag di script manuali ad ogni pagina unica è che devi scaricare quel contenuto di script su ogni visualizzazione di pagina" - abbastanza sbagliato. Lo script verrà scaricato una volta, quindi verrà recuperato dalla cache del browser per ulteriori richieste. "Se sei in grado di impacchettare tutto javascript in application.js usando la pipeline delle risorse, l'utente scarica quegli script una sola volta", vero, ma a costo di un sacco di codice non necessario. Se riesci a strutturare il tuo JS in molti piccoli file anziché in uno solo grande, otterrai vantaggi nella cache senza codice inutile.
Marnen Laibow-Koser,

1
@ MarnenLaibow-Koser Penso che sarebbe stato meglio dire che se impacchetti tutto in uno script, il tuo utente deve solo scaricare 1 script per qualsiasi pagina del tuo sito. Se hai più script per parti diverse della tua app, ovviamente l'utente deve scaricare più di uno script. Entrambi i metodi verranno memorizzati nella cache, ovviamente, ma nella maggior parte dei casi (app medio-piccole), servire una application.js una volta sarà più efficiente per il download. L'analisi di JS potrebbe essere un'altra storia, a seconda di cosa servi.
jakeonrails,

1
@Ziggy Inoltre, se il file di piccole dimensioni viene utilizzato solo su 8 pagine su 100, perché il codice dovrebbe rimanere sempre nella cache dell'utente? Meglio abbandonare effettivamente le cose che non sono necessarie.
Marnen Laibow-Koser,

16

Mi rendo conto di venire a questa festa un po 'in ritardo, ma volevo dare una soluzione che ho usato di recente. Tuttavia, vorrei prima menzionare ...

The Rails 3.1 / 3.2 Way (No, signore. Non mi piace.)

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

Includo quanto segue per completezza in questa risposta, e perché non è una soluzione non praticabile ... anche se non mi interessa molto.

"Rails Way" è una soluzione orientata al controller, piuttosto che essere orientata alla vista come richiesto dall'autore originale di questa domanda. Esistono file JS specifici del controller che prendono il nome dai rispettivi controller. Tutti questi file sono collocati in un albero di cartelle che NON è incluso per impostazione predefinita in nessuna delle application.js richiedono direttive.

Per includere il codice specifico del controller, a una vista viene aggiunto quanto segue.

<%= javascript_include_tag params[:controller] %>

Detesto questa soluzione, ma è lì ed è veloce. Presumibilmente, potresti invece chiamare questi file come "people-index.js" e "people-show.js" e quindi usare qualcosa di simile "#{params[:controller]}-index"per ottenere una soluzione orientata alla vista. Ancora una volta, soluzione rapida, ma non mi sta bene.

Modo dei miei dati personali

Chiamami pazzo, ma voglio TUTTO il mio JS compilato e minimizzato in application.js quando eseguo la distribuzione. Non voglio ricordarmi di includere questi piccoli file sfalsati ovunque.

Carico tutto il mio JS in un file compatto, che sarà presto memorizzato nella cache del browser. Se un determinato pezzo del mio application.js deve essere attivato su una pagina, lascio che l'HTML me lo dica, non Rails.

Invece di bloccare il mio JS su specifici ID elemento o sporcare il mio HTML con classi marker, utilizzo un attributo di dati personalizzato chiamato data-jstags.

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

Su ogni pagina, utilizzo - inserire qui il metodo di libreria JS preferito - per eseguire il codice al termine del caricamento del DOM. Questo codice di bootstrap esegue le seguenti azioni:

  1. Scorrere su tutti gli elementi nel DOM contrassegnati con data-jstag
  2. Per ogni elemento, dividi il valore dell'attributo nello spazio, creando una matrice di stringhe di tag.
  3. Per ogni stringa di tag, esegui una ricerca in un hash per quel tag.
  4. Se viene trovata una chiave corrispondente, esegui la funzione ad essa associata, passando l'elemento come parametro.

Quindi dire che ho definito quanto segue da qualche parte nel mio 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
};

L'evento bootstrap applicherà my_autosuggest_initemy_hint_init funzioni sull'input di ricerca, trasformandolo in un input che visualizza un elenco di suggerimenti mentre l'utente digita, oltre a fornire un tipo di suggerimento per l'input quando l'input viene lasciato vuoto e non focalizzato.

A meno che alcuni elementi non siano etichettati con data-jstag="auto-suggest" , il codice di suggerimento automatico non si attiva mai. Tuttavia, è sempre lì, minimizzato e infine memorizzato nella cache del mio application.js per quelle volte che ne ho bisogno su una pagina.

Se devi passare parametri aggiuntivi alle tue funzioni JS taggate, dovrai applicare un po 'di creatività. O aggiungi gli attributi dei parametri dei dati, crea un qualche tipo di sintassi dei parametri o usa persino un approccio ibrido.

Anche se ho un flusso di lavoro complicato che sembra specifico del controller, creerò semplicemente un file per esso nella mia cartella lib, lo comprimerò in application.js e lo taggerò con qualcosa come 'new-thing-wizard'. Quando il mio bootstrap colpisce quel tag, il mio simpatico, elegante mago verrà istanziato ed eseguito. Funziona per le viste di quel controller quando è necessario, ma non è altrimenti accoppiato al controller. In effetti, se codifico correttamente la mia procedura guidata, potrei essere in grado di fornire tutti i dati di configurazione nelle viste e quindi poter riutilizzare la procedura guidata in un secondo momento per qualsiasi altro controller che ne abbia bisogno.

Ad ogni modo, questo è il modo in cui ho implementato JS specifico per la pagina da un po 'di tempo, e mi ha servito bene sia per la progettazione di siti semplici sia per applicazioni più complesse / ricche. Spero che una delle due soluzioni che ho presentato qui, a modo mio o Rails, sia utile a chiunque si imbatta in questa domanda in futuro.


6
Un piccolo dettaglio: c'è questa idea nella tua risposta che una volta che js è memorizzato nella cache del browser, non ha alcun impatto. Questo non è del tutto vero. Il browser evita davvero il download, se il file js è correttamente memorizzato nella cache, ma compila comunque il codice su ogni rendering di pagina. Quindi, devi bilanciare i compromessi. Se hai un sacco di JS in aggregato, ma ne viene usato solo un po 'per pagina, potresti essere in grado di migliorare i tempi delle pagine suddividendo il JS.
sujal

Per ulteriori informazioni sugli effetti pratici di quella fase della compilazione di cui sto parlando, vedere la spiegazione di 37 Signals su come pjax ha influenzato Basecamp Next: 37signals.com/svn/posts/…
sujal

Questo è un punto giusto. Dopo aver letto l'articolo e ripensato ai progetti in cui ho usato la soluzione sopra, mi rendo conto di aver scritto essenzialmente la stessa soluzione "invia il codice HTML modificato" che menzionano nell'articolo. La frequente ricompilazione di JS non era stata un problema nei miei progetti a causa di ciò. Il passo della compilazione è qualcosa che terrò a mente quando lavoro su siti meno orientati alle "applicazioni desktop".
Ryan,

2
Downvoting per "Chiamami pazzo, ma voglio TUTTO il mio JS compilato e minimizzato in application.js quando eseguo la distribuzione." Non lo vuoi davvero, in quanto fa sì che l'utente carichi JavaScript di cui non ha bisogno e fa in modo che i tuoi gestori cerchino attributi che non ci saranno nemmeno. Avere tutto in app.js è allettante e Rails lo rende certamente facile, ma la cosa giusta da fare è modulare meglio JavaScript.
Marnen Laibow-Koser,

Hai diritto a un'opinione diversa ... e tecnicamente hai il diritto di votare per una differenza di opinione. Tuttavia, sarebbe bello vedere una giustificazione del perché un file di grandi dimensioni e memorizzato nella cache è inferiore alla forzatura di più richieste HTTP per afferrare JS modulare. Inoltre, ti sbagli riguardo alla procedura di ricerca del gestore. I valori dei tag NON vengono cercati. Viene mai eseguita una sola ricerca che estrae tutti gli elementi che hanno un attributo data-jstag. Non cerca per nome tag, trova solo tutti gli elementi che hanno tag e quindi crea un'istanza solo degli oggetti necessari.
Ryan,

7

Questa è stata risposta e accettata molto tempo fa, ma ho trovato la mia soluzione basata su alcune di queste risposte e sulla mia esperienza con Rails 3+.

La pipeline degli asset è dolce. Usalo

Innanzitutto, nel application.jsfile, rimuovi//= require_tree.

Quindi nel tuo application_controller.rbcreare un metodo di supporto:

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

Quindi nel tuo application.html.erbfile di layout, aggiungi il tuo nuovo helper tra gli include javascript esistenti, con il prefisso rawhelper:

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

Ora, ora puoi facilmente creare javascript specifici per la vista usando la stessa struttura di file che usi ovunque nelle rotaie. Inserisci semplicemente i tuoi file app/assets/:namespace/:controller/action.js.erb!

Spero che aiuti qualcun altro!


1
Questo non causerà problemi dopo che gli asset sono stati precompilati e in fase di esecuzione <%= raw ... %>restituirà un 404?
Nishant,

Penso che la pipeline delle risorse non sia dolce, poiché crea un mucchio di file che spesso non dovrebbero essere usati. Quindi per me fare affidamento sulla pipeline delle risorse sta creando una dipendenza da un sistema inefficiente.
Deborah,

1
@DeborahSpeece Quando la pipeline delle risorse crea file che non devono essere utilizzati? Stai confondendo la pipeline delle risorse (buona) con require_tree /(cattiva)?
Marnen Laibow-Koser,

6

Puoi aggiungere questa riga nel tuo file di layout (ad es. Application.html.erb) per caricare automaticamente il file javascript specifico del controller (quello che è stato creato quando hai generato il controller):

<%= javascript_include_tag params[:controller] %>

È inoltre possibile aggiungere una riga per caricare automaticamente un file di script in base all'azione.

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

Inserisci gli script della tua pagina in una sottodirectory che prende il nome dal controller. In questi file è possibile includere altri script utilizzando = request. Sarebbe bello creare un aiuto per includere il file solo se esiste, per evitare un errore 404 nel browser.


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

2
Sembra che potrebbe essere in grado di rispondere alla domanda. Potresti per favore aggiungere altro alla risposta per approfondire?


5

La gemma LoadJS è un'altra opzione:

LoadJS fornisce un modo per caricare il codice Javascript specifico della pagina in un'app Rails senza perdere la magia fornita da Sprockets. Tutto il tuo codice Javascript continuerà a essere minimizzato in un file Javascript ma alcune parti di esso verranno eseguite solo per determinate pagine.

https://github.com/guidomb/loadjs


3

La risposta di Filippo è abbastanza buona. Ecco il codice per farlo funzionare:

In application.html.erb:

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

Supponendo che il tuo controller si chiami Progetti, che genererà:

<body class="projects">

Quindi in projects.js.coffee:

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

Downvoting: qualsiasi soluzione che inserisce una classe su <body>è ipso facto errata. Vedi i miei commenti altrove in questa pagina.
Marnen Laibow-Koser,

Non farlo Il problema qui è che ogni volta che aggiungi uno di questi, stai aggiungendo un altro pezzo di js che deve essere eseguito al caricamento della pagina. Potrebbe sicuramente causare un certo peggioramento delle prestazioni man mano che il progetto cresce.
ifightcrime,

2

Gli script Java vengono uniti solo quando dici a Rails (Sprockets, piuttosto) di unirli.


Ovviamente. Immagino di averlo chiesto perché le impostazioni predefinite di Rails includono tutto nella cartella ... il che significa che David intende che tu lo faccia. Ma come ho detto nell'altro commento a @rubyprince, non sono sicuro dell'esecuzione quando viene eseguita in questo modo. Sto pensando di dover disabilitare //= require_tree .?
Fire Emblem,

@FireEmblem Sì. require_tree .di solito è una cattiva idea.
Marnen Laibow-Koser,

2

Ecco come ho risolto il problema dello styling: (scusa Haml)

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

In questo modo inizio tutti i file .css.sass specifici della pagina con:

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

In questo modo puoi facilmente evitare qualsiasi scontro. Quando si tratta di file .js.coffee è possibile inizializzare elementi come;

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

Spero che questo abbia aiutato alcuni.


1
Leggi di nuovo l'ultimo bit, per favore, per JavaScript puoi sfruttare la stessa struttura utilizzata per i fogli di stile per inizializzare le funzioni specifiche della vista.
zeeraw,

Filippo, $('#post > #edit') ->sembra non essere valido. In che modo jQuery funziona in un ambito?
Ramon Tayag,

2
Di recente ho iniziato a caricare tutti gli script java e i fogli di stile specifici del controller chiamando questo in application.html.haml; = javascript_include_tag "application"e in = javascript_include_tag params[:controller]questo modo posso mantenere limitato il codice javascript senza dover specificare un ambito all'interno del file.
zeeraw,


2

Accetto la tua risposta, per verificare se quel selettore è presente, utilizza:

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

(non ho visto nessuno aggiungere la soluzione reale)


2

Non vedo una risposta che metta davvero tutto insieme e che lo dia per te. Quindi, proverò a mettere insieme meleyal , sujal (a la ClosureCowboy ), la prima parte della risposta di Ryan , e persino l' affermazione audace di Gal su Backbone.js ... tutti in un modo breve e chiaro. E, chissà, potrei anche soddisfare i requisiti di Marnen Laibow-Koser .

Esempio di modifiche

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 descrizione

  • Rimuovere //= require_tree .da application.js ed elencare solo i JS condivisi da ciascuna pagina.

  • Le due righe mostrate sopra in application.html.erb indicano alla pagina dove includere application.js e il tuo JS specifico per la pagina.

  • Le tre righe mostrate sopra in index.html.erb indicano alla tua vista di cercare alcuni JS specifici della pagina e includerli in una regione di resa denominata chiamata ": javascript" (o come si desidera denominarlo). In questo esempio, il controller è "pippo", quindi Rails tenterà di includere "pippo.js" nell'area di resa: javascript nel layout dell'applicazione.

  • Elenca il tuo JS specifico per la pagina in foo.js (o qualunque sia il nome del controller). Elenca le librerie comuni, un albero, directory, qualunque cosa.

  • Mantieni il tuo JS personalizzato specifico per la pagina in un punto in cui puoi facilmente fare riferimento a esso rispetto all'altro JS personalizzato. In questo esempio, foo.js richiede l'albero di Foostuff, quindi metti lì il tuo JS personalizzato, come foothis.js.coffee .

  • Non ci sono regole rigide qui. Sentiti libero di spostare le cose e forse anche di creare più aree di rendimento con nomi diversi in vari layout, se necessario. Questo mostra solo un possibile primo passo in avanti. (Non lo faccio esattamente in questo modo dato il nostro uso di Backbone.js. Potrei anche scegliere di rilasciare foo.js in una cartella chiamata foo invece di foostuff ma non l'ho ancora deciso.)

Appunti

Puoi fare cose simili con i CSS e <%= stylesheet_link_tag params[:controller] %>questo va oltre lo scopo della domanda.

Se ho perso una best practice eclatante qui, mandami un appunto e mi adeguerò. Rails è abbastanza nuovo per me e, onestamente, non sono così impressionato finora dal caos che porta per impostazione predefinita allo sviluppo aziendale e tutto il traffico generato dal programma Rails medio.


questa sembra la strada da percorrere, vedrò se riesco a implementarla nella mia app, grazie per la risposta dettagliata.
martincarlin87,

1

Ho un'altra soluzione, che sebbene la primitiva funzioni bene per me e non abbia bisogno di strategie di caricamento selettivo. Inserisci la tua funzione pronta per il documento originale, ma verifica la posizione corrente di Windows per vedere se è la pagina a cui è destinato javascript:

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

Ciò consente comunque a tutti i js di essere caricati dalle rotaie 3.x in un piccolo pacchetto, ma non genera un sovraccarico o alcun conflitto con le pagine per le quali js non è previsto.


1

La risposta di Ryguy è una buona risposta, anche se è stata ridimensionata in punti negativi.

Soprattutto se stai usando qualcosa come Backbone JS - ogni pagina ha la sua vista Backbone. Quindi il file erb ha solo una singola riga di javascript inline che attiva la classe di visualizzazione backbone destra. Lo considero una singola riga di "codice colla" e quindi il fatto che sia in linea sia OK. Il vantaggio è che puoi mantenere il tuo "request_tree" che consente al browser di memorizzare nella cache tutto il javascript.

in show.html.erb avrai qualcosa del tipo:

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

e nel tuo file di layout, avrai bisogno di:

<%= yield :javascript %>

Downvoting. JavaScript incorporato non è mai una buona idea. Anche se è un codice colla, dovrebbe essere in un file esterno.
Marnen Laibow-Koser,

1

Sposta tutti i tuoi file JS comuni in una sottocartella come 'app / assets / javascript / global' quindi in application.js, modifica la //= require_tree .riga in //= require_tree ./global.

Ora sei libero di mettere il tuo JS specifico del controller nella radice 'app / assets / javascript /' e non saranno inclusi nel JS compilato, usato solo quando li chiami = javascript_include_tagsul tuo controller / vista.


In nessun modo, questo è un crapload di JavaScript da caricare per una pagina. Non importa nemmeno se è memorizzato nella cache.
jackyalcine,

1

Sebbene tu abbia diverse risposte qui, penso che la tua modifica sia probabilmente la migliore scommessa. Un modello di progettazione che utilizziamo nel nostro team che abbiamo ottenuto da Gitlab è il modello Dispatcher. Fa qualcosa di simile a quello di cui stai parlando, tuttavia il nome della pagina è impostato nel tag body da rotaie. Ad esempio, nel tuo file di layout, includi semplicemente qualcosa di simile (in HAML):

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

Quindi hai solo una chiusura e un'istruzione switch nel tuo dispatcher.js.coffeefile nella cartella javascripts in questo modo:

$ ->
  new Dispatcher()

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

Tutto ciò che devi fare nei singoli file (diciamo products.js.coffeeo login.js.coffeeper esempio) è racchiuderli in una classe e quindi globalizzare quel simbolo di classe in modo da poter accedervi nel dispatcher:

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

Gitlab ha diversi esempi di questo che potresti voler curiosare nel caso tu sia curioso :)


1

Il progetto Paloma offre un approccio interessante per gestire il codice javascript specifico per la pagina.

Esempio di utilizzo dai loro documenti:

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

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

1

Passo 1. rimuovere require_tree. in application.js e application.css.

Passo 2. Modifica application.html.erb (per impostazione predefinita su rotaie) nella cartella layout. Aggiungi "params [: controller]" nei seguenti tag.

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

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

Fase 3. Aggiungi un file in config / initializer / 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

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


Sebbene ciò possa teoricamente rispondere alla domanda, sarebbe preferibile includere qui le parti essenziali della risposta e fornire il collegamento come riferimento.
Bhargav Rao

0

Non l'ho provato, ma sembra che quanto segue sia vero:

  • se hai un content_for che è javascript (ad es. con javascript reale al suo interno), i pignoni non lo saprebbero e quindi questo funzionerebbe allo stesso modo di adesso.

  • se vuoi escludere un file dal grosso pacchetto di javascript, vai nel file config / sprockets.yml e modifichi i file source_ di conseguenza. Quindi, includeresti semplicemente uno qualsiasi dei file che hai escluso dove necessario.


Quindi escludere i file o utilizzare javascript personalizzato nella pagina stessa è il "modo giusto"? È così che David voleva che le persone lo usassero?
Fire Emblem,

@FireEmblem Non mi interessa molto cosa intendesse David, perché non credo che David capisca come organizzare JavaScript correttamente.
Marnen Laibow-Koser,


0

Ho combinato alcune risposte in:

Aiuto applicativo:

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

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

Primo: rimuovi \\=require_treeda application.js Secondo: tutto il tuo codice JS deve essere allocato in /app/assets/javascritpte tutto il tuo codice CSS deve essere allocato in/app/assets/stylesheets


-2

Seguendo l'esempio di Ryan, ecco cosa ho fatto-

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 specifico del controller, ad es. controller: utenti, azione: dashboard)

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

Downvoting. Questo è ridicolmente contorto - per non dire insicuro (a causa del eval) se il tuo HTML viene compromesso da un server crackato o da uno script di utenti malintenzionato.
Marnen Laibow-Koser,

-3

Ecco come farlo soprattutto se non devi eseguire tonnellate di librerie per la tua pagina specifica, ma solo per eseguire alcune centinaia di righe di JS più o meno.

Poiché è perfettamente corretto incorporare il codice Javascript in HTML, basta creare nella directory app / views shared.js e posizionare lì il codice specifico per pagina / pagine all'interno di my_cool_partial.html.erb

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

Quindi ora, ovunque tu voglia, fai semplicemente:

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

E questo è tutto, k?


2
Downvoting. Inline JavaScript non è mai consigliabile. HTML deve contenere solo markup. JS e CSS dovrebbero essere in file separati e riutilizzabili.
Marnen Laibow-Koser,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.