Der beste Weg, um seitenspezifisches JavaScript in eine Rails 3-App einzufügen?

159

Rails 3 hat etwas unauffälliges JavaScript, was ziemlich cool ist.

Aber ich habe mich gefragt, wie ich am besten zusätzliches JavaScript für eine bestimmte Seite einfügen kann.

Zum Beispiel, wo ich vorher getan haben könnte:

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

Wir können es jetzt mit so etwas unauffällig machen

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

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

Ich denke also, meine Frage ist, wo / wie man dieses JavaScript einfügt. Ich möchte die application.jsDatei nicht füllen, da dieses JavaScript nur für diese eine Ansicht gilt. Sollte ich irgendwie eine benutzerdefinierte JavaScript-Datei für jede Seite einfügen oder sie in eine Instanzvariable einfügen, nach der der Header sucht?

Brian Armstrong
quelle
Wenn Sie wissen möchten, wie dies funktioniert und was am besten ist, lesen Sie bitte railsapps.github.io/rails-javascript-include-external.html . Dies ist bei weitem die beste Dokumentation, die ich zu diesem Thema gesehen habe. Es ist eine großartige Lektüre nicht nur für Rails, sondern für alle, die sich mit Webentwicklern beschäftigen. Aus diesem Grund ist es am besten, die Dinge auf Schienen zu erledigen. Ich werde sicherlich Unholy Rails für meine zukünftigen Fragen verwenden. BEEINDRUCKEND.
DutGRIFF
2
@ KateGregory, dass man neuer ist.
Ciro Santilli 14 冠状 病 六四 事件 法轮功

Antworten:

153

Was ich gerne mache, ist das Per-View-Javascript in einen content_for :headBlock aufzunehmen und dannyield in diesen Block in Ihrem Anwendungslayout aufzunehmen. Beispielsweise

Wenn es ziemlich kurz ist, dann:

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

oder, wenn länger, dann:

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

Dann in Ihrer Layoutdatei

<head>
  ...
  <%= yield :head %>
</head>
bjg
quelle
40
Warum nicht das benutzen <%= javascript_include_tag "my_javascipt_file" %> ?
AJP
5
@AJP Ich denke, dann besiegt es den Zweck der Asset-Pipeline
Lulalala
2
@lulalala, aber macht der Code in der Antwort das nicht trotzdem, aber auf eine chaotischere Art und Weise?
AJP
8
@AJP es ist chaotischer, aber es muss eine http-Anfrage weniger gestellt werden als Ihre Antwort.
Lulalala
103

Wenn Sie Javascript nur auf einer Seite einfügen möchten, können Sie es natürlich inline in die Seite einfügen. Wenn Sie jedoch Ihr Javascript gruppieren und die Asset-Pipeline, minimierte JS usw. nutzen möchten, ist dies möglich und es ist zusätzlich möglich js-Assets, die kombiniert und nur auf bestimmten Seiten geladen werden, indem Sie Ihre js in Gruppen aufteilen, die nur in bestimmten Controllern / Ansichten / Abschnitten der Site gelten.

Verschieben Sie Ihre js in Assets in Ordner mit jeweils einer separaten Manifestdatei. Wenn Sie also eine js-Bibliothek für Administratoren haben, die nur im Backend verwendet wird, können Sie Folgendes tun:

  • Vermögenswerte
    • Javascripts
      • Administrator
        • ... js
      • admin.js (Manifest für Administratorgruppe)
      • application.js (Manifest für globale App-Gruppe)
      • global
        • ... js

in der vorhandenen application.js

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

in einer neuen Manifestdatei admin.js.

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

Stellen Sie sicher, dass dieses neue js-Manifest geladen ist, indem Sie config / Production.rb bearbeiten

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

Passen Sie dann Ihr Seitenlayout so an, dass Sie einige zusätzliche js für den Seitenkopf einfügen können:

<%= content_for :header %>   

Dann in Ansichten, in denen Sie diese bestimmte js-Gruppe (sowie die normale Anwendungsgruppe) und / oder alle seitenspezifischen js, css usw.:

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

Sie können natürlich dasselbe mit CSS tun und es auf ähnliche Weise gruppieren, um es nur auf bestimmte Bereiche der Site anzuwenden.

Kenny Grant
quelle
Wenn Sie alle Ihre Manifeste an derselben Stelle aufbewahren, können Sie Rails anweisen, alles in diesem Ordner vorkompiliert zu haben. Auf diese Weise müssen Sie Production.rb nur einmal bearbeiten und nicht nachverfolgen, was Sie hinzugefügt haben.
Undistraction
Wussten Sie nicht, dass Sie das tun können - also könnten Sie einen Ordner "Manifeste" haben, der Ihre Manifeste sowie die einzelnen js-Gruppen enthält? Ich könnte das ausprobieren. Ich wünschte, sie hätten standardmäßig ein etwas vernünftigeres Setup, da sie anscheinend davon ausgehen, dass Sie einfach alle js in eine große Datei zusammenfassen, die überall importiert wird.
Kenny Grant
ungerade, erhalte ich eine Kette :: FileNotFound Fehler , wenn ich verwende //= require_tree ./global, aber nicht , wenn ich verwende //= require_directory ./globalRails 3.2.12 verwenden.
Qix
2
Es sieht so aus, als würden Sie am Ende viele Skript-Tags haben. Ich dachte, das Ziel mit der Asset-Pipeline wäre, nur ein Skript-Tag zu haben. Ich würde nur die Javaskripte schreiben, damit sie seitenspezifisch sind.
Ziggy
1
Ich denke ich verstehe. Das Vermeiden zusätzlicher Skript-Tags ist jedoch für die Ladezeit der Seite sehr wichtig.
Ziggy
12

Diese Antworten haben mir sehr geholfen! Wenn jemand ein bisschen mehr will ...

  1. Sie müssen Javaskripte in Manifeste einfügen, wenn Sie möchten, dass sie vorkompiliert werden. Wenn Sie jedoch jede Javascript-Datei benötigen, werden application.js.coffeealle Javacskripte jedes Mal geladen , wenn Sie zu einer anderen Seite navigieren, und der Zweck, seitenspezifische Javascripts zu erstellen, wird zunichte gemacht.

Daher müssen Sie Ihre eigene Manifestdatei (z. B. speciifc.js) erstellen , für die alle seitenspezifischen Javascript-Dateien erforderlich sind. Ändern Sie auch require_treevonapplication.js

App / Assets / Javascripts / application.js

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

App / Assets / Javascripts / Specific.js

//= require_tree ./specific

Fügen Sie dann environments/production.rbdieses Manifest mit der Konfigurationsoption zur vorkompilierten Liste hinzu.

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

Getan! Alle freigegebenen Javascripts, die immer geladen werden sollten, werden in einem app/assets/javascripts/globalOrdner abgelegt und die seitenspezifischen Javascripts inapp/assets/javascripts/specific . Sie können die seitenspezifischen Javascripts einfach aus der Ansicht wie aufrufen

<%= javascript_include_tag "specific/whatever.js" %> //.js ist optional.

Das ist ausreichend, aber ich wollte es auch nutzen javascript_include_tag params[:controller]. Wenn Sie Controller erstellen, wird eine zugehörige Coffeescript-Datei app/assets/javascriptswie bei anderen genannten Personen generiert . Es gibt wirklich Controller-spezifische Javascripts, die nur geladen werden, wenn der Benutzer die spezifische Controller-Ansicht erreicht.

Also habe ich ein anderes Manifest erstellt controller-specific.js

App / Assets / Javascripts / Controller-spezifisch.js

//= require_directory .

Dies schließt alle automatisch generierten Kaffeeskripte ein, die den Controllern zugeordnet sind. Außerdem müssen Sie es der vorkompilierten Liste hinzufügen.

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

Maximus S.
quelle
Vielen Dank für die ausführliche Antwort. Wo soll ich diese Zeile setzen javascript_include_tag params[:controller]. Derzeit hat meine application.html.erb diese Zeile javascript_include_tag 'application'. Soll ich dies durch die andere Leitung ersetzen?
Simha
1
Soll ich diese Zeile auch <%= javascript_include_tag "specific/whatever.js" %>in einen content_for :headerBlock setzen?
Simha
1
Es ist seltsam für mich, dass Sie sich damit anlegen müssen config.assets.precompile. Ist nicht alles app/assets/javascripts/*.jsautomatisch vorkompiliert?
AlexChaffee
@AlexChaffee Nur automatisch verfügbare Dateien sind application.cssund application.js. Andere müssen entweder in anderen Dateien enthalten sein oder mit aufgelistet werden config.assets.precompile. Lesen Sie hier mehr
Sung Cho
Die Manifestdeklaration wurde verschoben. Ich laufe Schienen 4.2.0. und fand diesen Kommentar in der Datei product.rb : # config.assets.precompileund config.assets.versionbin nach config / initializers / assets.rb umgezogen
kurz
11

Ich bevorzuge folgendes ...

In Ihrer Datei application_helper.rb

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

und dann in Ihrer speziellen Ansicht (app / views / books / index.html.erb in diesem Beispiel)

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

... scheint für mich zu arbeiten.

Cailinanne
quelle
9

Wenn Sie nicht die Asset-Pipeline oder die komplexen Workarounds verwenden möchten, um das erforderliche seitenspezifische Javascript zu erhalten (ich verstehe), ist der einfachste und robusteste Weg, der das Gleiche wie die obigen Antworten erreicht, aber mit weniger Code nur zu verwenden:

<%= javascript_include_tag "my_javascipt_file" %>

Hinweis: Dies erfordert eine http-Anforderung mehr pro Include-Tag als die verwendeten Antworten content_for :head

AJP
quelle
javascript_include_tagwar genau das, wonach ich gesucht habe, Prost AJP
Tom McKenzie
Wo legst du "my_javascipt_file" ab? In Assets / Javascripts? Aber wenn ja, würde das nicht automatisch mit einem //= require .in der application.js aufgenommen werden?
Qix
@Linus yep, //= require .würde dieses Skript auf jede Seite Ihrer Website bringen, sodass Sie etwas tun müssten, wie es Kenny Grant (verwenden //= require_tree ./global) oder bjg vorgeschlagen hat.
AJP
1
Dies war bei weitem die einfachste Methode für mich. Ich //= require_tree .habe //= require_directory .in application.js gewechselt und dann ein Unterverzeichnis in Assets \ Javascripts erstellt. Beachten Sie, dass Sie das neue Verzeichnis in config \ initializers \ assets.rb aufnehmen müssen, um Ihre js vorkompilieren zu können, indem Sie die folgende Zeile hinzufügen:Rails.application.config.assets.precompile += %w( new_dir/js_file.js )
Jonesy
7

Schauen Sie sich pluggable_js gem an. Möglicherweise ist diese Lösung einfacher zu verwenden.

peresleguine
quelle
3

Meines Wissens nach soll die Asset-Pipeline die Ladezeit von Seiten verkürzen, indem alle Ihre JS in einer (minimierten) Datei zusammengefasst werden. Während dies auf den ersten Blick abstoßend erscheint, ist es tatsächlich eine Funktion, die bereits in populären Sprachen wie C und Ruby vorhanden ist. Dinge wie "Include" -Tags sollen das mehrfache Einschließen einer Datei verhindern und Programmierern helfen, ihren Code zu organisieren. Wenn Sie ein Programm in C schreiben und kompilieren, ist der gesamte Code in jedem Teil Ihres laufenden Programms vorhanden. Methoden werden jedoch nur dann in den Speicher geladen, wenn dieser Code verwendet wird. In gewisser Weise enthält ein kompiliertes Programm nichts, um sicherzustellen, dass der Code gut modular aufgebaut ist. Wir machen den Code modular, indem wir unsere Programme auf diese Weise schreiben, und das Betriebssystem lädt nur die Objekte und Methoden in den Speicher, die wir für einen bestimmten Ort benötigen. Gibt es überhaupt eine "methodenspezifische Inklusion"? Wenn Ihre Rails-App erholsam ist, ist dies im Wesentlichen das, wonach Sie fragen.

Wenn Sie Ihr Javascript so schreiben, dass es das Verhalten von HTML-Elementen auf der Seite verbessert, sind diese Funktionen von Natur aus "seitenspezifisch". Wenn es einen komplizierten Code gibt, den Sie so geschrieben haben, dass er unabhängig von seinem Kontext ausgeführt wird, sollten Sie diesen Code möglicherweise trotzdem an ein HTML-Element binden (Sie können das Body-Tag verwenden, wie in der Garber-Irish-Methode beschrieben ). Wenn eine Funktion bedingt ausgeführt wird, ist die Leistung wahrscheinlich geringer als bei all diesen zusätzlichen Skript-Tags.

Ich denke darüber nach, das Paloma- Juwel zu verwenden, wie im Rails-Apps-Projekt beschrieben . Anschließend können Sie Ihr Javascript seitenspezifisch gestalten, indem Sie seitenspezifische Funktionen in einen Paloma-Rückruf aufnehmen:

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

Du benutzt Schienen, also weiß ich, dass du Edelsteine ​​liebst :)

Ziggy
quelle
3

Sie sollten Ihre JS- oder CSS-Dateien nicht außerhalb der Asset-Pipeline laden, da Sie wichtige Funktionen verlieren, die Rails so großartig machen. Und du brauchst kein weiteres Juwel. Ich glaube daran, so wenig Edelsteine ​​wie möglich zu verwenden, und die Verwendung eines Edelsteins ist hier nicht erforderlich.

Was Sie möchten, wird als "Controller-spezifisches Javascript" bezeichnet ("Aktionsspezifisches Javascript ist unten enthalten). Auf diese Weise können Sie eine bestimmte JavaScript-Datei für einen bestimmten CONTROLLER laden. Der Versuch, Ihr Javascript mit einer Ansicht zu verbinden, ist eine Art von .. Sie möchten es Ihren Controllern oder Aktionen in Ihren Controllern zuordnen.

Leider haben Rails-Entwickler aus irgendeinem Grund entschieden, dass standardmäßig jede Seite jede JS-Datei in Ihrem Assets-Verzeichnis lädt. Warum sie sich dazu entschlossen haben, anstatt standardmäßig "Controller Specific Javascript" zu aktivieren, werde ich nie erfahren. Dies erfolgt über die Datei application.js, die standardmäßig die folgende Codezeile enthält:

//= require_tree .

Dies ist als Richtlinie bekannt . Es ist das, was sprockets verwendet, um jede JS-Datei in das Verzeichnis assets / javascripts zu laden. Standardmäßig lädt sprockets application.js und application.css automatisch, und die Direktive require_tree lädt jede JS- und Coffee-Datei in ihre jeweiligen Verzeichnisse.

HINWEIS: Wenn Sie ein Gerüst bauen (wenn Sie kein Gerüst haben, ist jetzt ein guter Zeitpunkt, um zu beginnen), generiert Rails automatisch eine Kaffeedatei für Sie, für den Controller dieses Gerüsts. Wenn Sie es wollen , einen generieren Standard JS statt einer Datei Kaffee - Datei, entfernen Sie den Kaffee Juwel , die standardmäßig in Ihrem aktiviert ist Gemfile , und Ihr Gerüst wird JS Dateien erstellen statt.

Ok, der erste Schritt zum Aktivieren von "Controller Specific Javascript" besteht darin, den Code "require_tree" aus Ihrer Datei application.js zu entfernen oder ihn in einen Ordner in Ihrem Verzeichnis "resources / javascripts" zu ändern, wenn Sie weiterhin globale JS-Dateien benötigen. IE:

//= require_tree ./global

Schritt 2: Gehen Sie in Ihre Datei config / initializers / assets.rb und fügen Sie Folgendes hinzu:

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

Geben Sie die gewünschten Controllernamen ein.

Schritt 3: Ersetzen Sie den javascript_include_tag in Ihrer application.html.erb-Datei durch diesen (beachten Sie den Teil params [: controller]:

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

Starten Sie Ihren Server und Ihre Bratsche neu! Die JS-Datei, die mit Ihrem Gerüst erstellt wurde, wird jetzt nur geladen, wenn dieser Controller aufgerufen wird.

Müssen Sie eine bestimmte JS-Datei auf eine bestimmte AKTION in Ihrem Controller , IE / articles / new , laden ? Tun Sie dies stattdessen:

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

Fügen Sie dann einen neuen Ordner mit demselben Namen wie Ihr Controller in Ihren Ordner "Assets / Javascripts" ein und legen Sie Ihre JS-Datei mit demselben Namen wie Ihre Aktion darin ab. Es wird dann auf diese bestimmte Aktion geladen.

Erick Maynard
quelle
1

Ok, vielleicht ist dies die schlechteste Arbeit, die es je gab, aber ich erstelle eine Controller-Methode, die gerade die .js-Datei gerendert hat

Regler

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

Aussicht

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

Wenn wir dies aus irgendeinem Grund nicht tun möchten, lassen Sie es mich wissen.

Rechnung
quelle
1

Die bevorzugte Methode zum Hinzufügen von JS ist die Fußzeile. Sie haben also folgende Möglichkeiten:

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
quelle