Najlepszy sposób na dodanie specyficznego dla strony języka JavaScript w aplikacji Rails 3?

159

Rails 3 ma trochę dyskretnego JavaScript, który jest całkiem fajny.

Ale zastanawiałem się, jak najlepiej jest dołączyć dodatkowy JavaScript do konkretnej strony.

Na przykład tam, gdzie mogłem wcześniej:

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

Teraz możemy sprawić, że będzie dyskretny za pomocą czegoś takiego jak

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

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

Więc myślę, że moje pytanie brzmi: gdzie / jak dołączyć ten JavaScript? Nie chcę wypełniać application.jspliku, ponieważ ten JavaScript ma zastosowanie tylko do tego jednego widoku. Czy powinienem jakoś dołączyć niestandardowy plik JavaScript dla każdej strony, czy też umieścić go w zmiennej instancji, której szuka nagłówek?

Brian Armstrong
źródło
Dla tych, którzy chcą w pełni zrozumieć, jak to działa i co jest najlepsze, przeczytaj railsapps.github.io/rails-javascript-include-external.html . To zdecydowanie najlepsza dokumentacja, jaką widziałem na ten temat. To świetna lektura nie tylko dla Railsów, ale dla każdego, kto zajmuje się tworzeniem stron internetowych. Dlatego najlepiej jest robić rzeczy po szynach. Z pewnością będę używać Unholy Rails do moich przyszłych pytań. ŁAŁ.
DutGRIFF
2
@KateGregory, że jest nowszy.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Odpowiedzi:

153

To, co lubię, to dołączanie kodu JavaScript per-view do content_for :headbloku, a następnie yielddo tego bloku w układzie aplikacji. Na przykład

Jeśli jest dość krótki, to:

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

lub, jeśli dłużej, to:

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

Następnie w pliku układu

<head>
  ...
  <%= yield :head %>
</head>
bjg
źródło
40
Dlaczego nie użyć <%= javascript_include_tag "my_javascipt_file" %> ?
AJP
5
@AJP Wydaje mi się, że to pokonuje cel potoku aktywów
lulalala
2
@lulalala, ale czy kod w odpowiedzi nie robi tego tak, ale w bardziej niechlujny sposób?
AJP
8
@AJP jest bardziej nieuporządkowany, ale ma o jedno żądanie http mniej niż twoja odpowiedź.
lulalala
4
Podobał mi się ten artykuł railsapps.github.com/rails-javascript-include-external.html
Ziggy
103

Jeśli chcesz umieścić javascript tylko na jednej stronie, możesz oczywiście dołączyć go na stronie w tekście, jednak jeśli chcesz zgrupować swój javascript i skorzystać z potoku zasobów, zminimalizowanego js itp., Możesz to zrobić i mieć dodatkowe js, które są łączone i ładowane tylko na określonych stronach, dzieląc js na grupy, które mają zastosowanie tylko w niektórych kontrolerach / widokach / sekcjach witryny.

Przenieś pliki js w zasobach do folderów, z osobnym plikiem manifestu dla każdego, więc jeśli masz bibliotekę admin js, która jest używana tylko na zapleczu, możesz zrobić to:

  • majątek
    • javascripts
      • Administrator
        • ... js
      • admin.js (manifest dla grupy admin)
      • application.js (manifest dla globalnej grupy aplikacji)
      • światowy
        • ... js

w istniejącym pliku application.js

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

w nowym pliku manifestu admin.js.

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

Upewnij się, że nowy manifest js został załadowany, edytując plik config / production.rb

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

Następnie dostosuj układ strony, aby można było dołączyć dodatkowe pliki js dla nagłówka strony:

<%= content_for :header %>   

Następnie w widokach, w których chcesz uwzględnić tę konkretną grupę js (a także normalną grupę aplikacji) i / lub dowolne pliki js, css itp. Dotyczące strony:

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

Możesz oczywiście zrobić to samo z css i zgrupować go w podobny sposób, aby zastosować tylko do niektórych obszarów witryny.

Kenny Grant
źródło
Jeśli trzymasz wszystkie swoje manifesty w tym samym miejscu, możesz nakazać railsom prekompilację wszystkiego w tym folderze. W ten sposób wystarczy tylko raz edytować plik production.rb i nie śledzić tego, co do niego dodałeś.
Undistraction,
Nie wiedziałeś, że możesz to zrobić - mógłbyś więc mieć folder „manifesty”, który zawierał Twoje manifesty, a także poszczególne grupy js? Mógłbym tego spróbować. Żałuję, że domyślnie mają nieco bardziej rozsądną konfigurację, ponieważ wydaje się, że zakładają, że po prostu globujesz wszystkie pliki js w jeden duży plik importowany wszędzie.
Kenny Grant
dziwne, otrzymuję błąd Sprockets :: FileNotFound, jeśli używam //= require_tree ./global, ale nie, jeśli używam //= require_directory ./globalRails 3.2.12.
qix
2
Wygląda na to, że skończysz z wieloma tagami skryptów. Myślałem, że celem w przypadku potoku aktywów jest posiadanie tylko jednego tagu skryptu? Po prostu napisałbym skrypty javascriptu, aby były specyficzne dla strony.
Ziggy
1
Myślę, że rozumiem. Unikanie dodatkowych tagów skryptów jest jednak bardzo ważne dla czasu ładowania strony.
Ziggy,
12

Te odpowiedzi bardzo mi pomogły! Jeśli ktoś chce trochę więcej ...

  1. Musisz umieścić javascripts w manifestach, jeśli chcesz, aby były prekompilowane. Jeśli jednak potrzebujesz każdego pliku javascript od application.js.coffeetego momentu, wszystkie javacsripts będą ładowane za każdym razem, gdy przejdziesz do innej strony, a cel wykonywania skryptów javascript specyficznych dla strony zostanie pokonany.

Dlatego musisz utworzyć własny plik manifestu (np. speciifc.js), Który będzie wymagał wszystkich plików javascript specyficznych dla strony. Modyfikuj także require_treezapplication.js

aplikacja / asset / javascripts / application.js

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

aplikacja / asset / javascripts / specific.js

//= require_tree ./specific

Następnie environments/production.rbdodaj ten manifest do prekompilowanej listy z opcją config,

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

Gotowe! Wszystkie udostępnioneapp/assets/javascripts/global skrypty JavaScript, które powinny być zawsze ładowane, zostaną umieszczone w folderze, a skrypty związane ze stroną wapp/assets/javascripts/specific . Możesz po prostu wywołać skrypty JavaScript specyficzne dla strony z widoku, takiego jak

<%= javascript_include_tag "specific/whatever.js" %> //.js jest opcjonalny.

To wystarczy, ale chciałem też z tego skorzystać javascript_include_tag params[:controller]. Podczas tworzenia kontrolerów powiązany plik coffeescript jest generowany w app/assets/javascriptstaki sam sposób, jak wspomniane inne osoby. Istnieją naprawdę specyficzne dla kontrolera JavaScript, które są ładowane tylko wtedy, gdy użytkownik osiągnie określony widok kontrolera.

Więc stworzyłem kolejny manifest controller-specific.js

app / asset / javascripts / controller-specific.js

//= require_directory .

Obejmuje to wszystkie automatycznie generowane skrypty kawowe powiązane z kontrolerami. Musisz także dodać go do wstępnie skompilowanej listy.

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

Maximus S.
źródło
Bardzo dziękuję za szczegółową odpowiedź. Gdzie mam umieścić tę linię javascript_include_tag params[:controller]. Obecnie moja application.html.erb ma tę linię javascript_include_tag 'application'. Czy powinienem zastąpić to inną linią?
simha
1
Czy powinienem też umieścić tę linię <%= javascript_include_tag "specific/whatever.js" %>w content_for :headerbloku?
simha
1
To dla mnie dziwne, że musisz się z tym bawić config.assets.precompile. Czy wszystko w app/assets/javascripts/*.jsprekompilacji nie jest automatycznie?
AlexChaffee
@AlexChaffee Tylko pliki dostępne automatycznie to application.cssi application.js. Inne muszą być zawarte w innych plikach lub wymienione za pomocą config.assets.precompile. Czytaj więcej tutaj
Sung Cho
Deklaracja manifestu została przeniesiona. Prowadzę szyny 4.2.0. i znalazłem ten komentarz w pliku production.rb: # config.assets.precompilei config.assets.versionprzeszedłem do config / initializers / asset.rb
krótko
11

Wolę następujące ...

W pliku application_helper.rb

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

a następnie w Twoim konkretnym widoku (w tym przykładzie app / views / books / index.html.erb)

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

... wydaje się działać dla mnie.

cailinanne
źródło
9

Jeśli nie chcesz używać potoku zasobów lub skomplikowanych obejść, aby uzyskać ten niezbędny skrypt javascript specyficzny dla strony (sympatyzuję), najprostszym i najbardziej niezawodnym sposobem, który daje to samo, co powyższe odpowiedzi, ale przy mniejszej ilości kodu, jest po prostu posługiwać się:

<%= javascript_include_tag "my_javascipt_file" %>

Uwaga: wymaga to jednego żądania http więcej na tag include niż odpowiedzi, które używają content_for :head

AJP
źródło
javascript_include_tagbył dokładnie tym, czego szukałem, pozdrawiam AJP
Tom McKenzie
gdzie wstawiasz „my_javascipt_file”? W zasobach / skryptach JavaScript? Ale jeśli tak, czy nie zostanie to automatycznie odebrane za pomocą pliku //= require .application.js?
qix
@Linus tak, //= require .spowodowałoby przeniesienie tego skryptu na dowolną stronę w witrynie, więc musiałbyś zrobić coś takiego, jak Kenny Grant (użyj //= require_tree ./global) lub zasugerowany przez bjg.
AJP,
1
To była dla mnie zdecydowanie najłatwiejsza metoda, przeszedłem //= require_tree .na //= require_directory .application.js, a następnie utworzyłem podkatalog w asset \ javascripts. Zauważ, że musisz dołączyć nowy katalog do config \ initializers \ asset.rb, aby wstępnie skompilować plik js, dodając wiersz:Rails.application.config.assets.precompile += %w( new_dir/js_file.js )
Jonesy
7

Spójrz na gem pluggable_js . Może się okazać, że to rozwiązanie jest łatwiejsze w użyciu.

peresleguine
źródło
3

Rozumiem, że potok zasobów ma na celu skrócenie czasu ładowania strony poprzez połączenie wszystkich plików js w jeden (zminimalizowany) plik. Choć z pozoru może się to wydawać odrażające, w rzeczywistości jest to funkcja, która już istnieje w popularnych językach, takich jak C i Ruby. Znaczniki typu „include” mają na celu zapobieganie wielokrotnemu dołączaniu pliku i pomaganie programistom w organizowaniu ich kodu. Kiedy piszesz i kompilujesz program w C, cały ten kod jest obecny w każdej części uruchomionego programu, ale metody są ładowane do pamięci tylko wtedy, gdy ten kod jest używany. W pewnym sensie skompilowany program nie zawiera niczego, co mogłoby zagwarantować, że kod jest ładnie modułowy. Tworzymy kod modułowy, pisząc w ten sposób nasze programy, a system operacyjny ładuje do pamięci tylko te obiekty i metody, których potrzebujemy dla danej lokalizacji. Czy istnieje coś takiego jak „włączenie specyficzne dla metody”? Jeśli Twoja aplikacja rails działa uspokajająco, zasadniczo o to prosisz.

Jeśli piszesz swój skrypt javascript tak, aby zwiększał zachowanie elementów HTML na stronie, to te funkcje są „specyficzne dla strony” z założenia. Jeśli istnieje jakiś skomplikowany kod, który napisałeś w taki sposób, że będzie wykonywał się niezależnie od kontekstu, może i tak rozważ powiązanie tego kodu z elementem html (możesz użyć tagu body, jak opisano w metodzie Garber-Irish ). Jeśli funkcja jest wykonywana warunkowo, wydajność prawdopodobnie będzie mniejsza niż wszystkie te dodatkowe znaczniki skryptu.

Myślę o użyciu klejnotu Paloma , zgodnie z opisem w projekcie Rails Apps . Następnie możesz ustawić swoją stronę javascript specyficznie, włączając funkcje specyficzne dla strony w wywołaniu zwrotnym Paloma:

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

Używasz szyn, więc wiem, że kochasz klejnoty :)

Ziggy
źródło
3

Nie powinieneś ładować plików JS lub CSS poza potokiem zasobów, ponieważ tracisz ważne funkcje, które sprawiają, że Railsy są tak świetne. I nie potrzebujesz kolejnego klejnotu. Wierzę w użycie jak najmniejszej liczby klejnotów, a używanie klejnotu nie jest tutaj konieczne.

To, co chcesz, jest znane jako „JavaScript specyficzny dla kontrolera” („JavaScript specyficzny dla akcji znajduje się na dole). Umożliwia to załadowanie określonego pliku JavaScript dla konkretnego KONTROLERA. Próba połączenia skryptu JavaScript z widokiem jest trochę… . do tyłu i nie jest zgodne ze wzorcem projektowym MVC. Chcesz powiązać go z kontrolerami lub akcjami wewnątrz kontrolerów.

Niestety, z jakiegoś powodu, twórcy Rails zdecydowali, że domyślnie każda strona załaduje każdy plik JS znajdujący się w katalogu zasobów. Nigdy się nie dowiem, dlaczego zdecydowali się to zrobić zamiast domyślnie włączać „JavaScript specyficzny dla kontrolera”. Odbywa się to za pośrednictwem pliku application.js, który domyślnie zawiera następujący wiersz kodu:

//= require_tree .

Jest to znane jako dyrektywa . To jest to, czego używa sprockets do załadowania każdego pliku JS w katalogu asset / javascripts. Domyślnie sprockets automatycznie ładuje application.js i application.css, a dyrektywa require_tree ładuje każdy plik JS i Coffee w odpowiednich katalogach.

UWAGA: Kiedy rusztujesz (jeśli nie budujesz rusztowania, teraz jest dobry moment na rozpoczęcie), Railsy automatycznie generują dla ciebie plik kawy dla kontrolera tego rusztowania. Jeśli chcesz, aby generował standardowy plik JS zamiast pliku kawy , usuń klejnot kawy, który jest domyślnie włączony w twoim Gemfile , a twoje rusztowanie utworzy zamiast tego pliki JS.

Ok, więc pierwszym krokiem do włączenia „JavaScript specyficznego dla kontrolera” jest usunięcie kodu require_tree z pliku application.js LUB zmiana go na folder w katalogu asset / javascripts, jeśli nadal potrzebujesz globalnych plików JS. TO ZNACZY:

//= require_tree ./global

Krok 2: przejdź do pliku config / initializers / asset.rb i dodaj następujące informacje:

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

Wstaw żądane nazwy kontrolerów.

Krok 3: Zastąp javascript_include_tag w pliku application.html.erb tym (zwróć uwagę na część params [: controller]:

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

Uruchom ponownie serwer i altówkę! Plik JS, który został wygenerowany za pomocą twojego rusztowania, będzie teraz ładowany tylko wtedy, gdy zostanie wywołany ten kontroler.

Chcesz załadować określony plik JS na konkretną AKCJĘ w kontrolerze , IE / artykuły / nowy ? Zrób to zamiast tego:

application.html.erb :

<%= javascript_include_tag "#{controller_name}/#{action_name}" if AppName::Application.assets.find_asset("#{controller_name}/#{action_name}") %>

config / initializers / asset.rb :

config.assets.precompile += %w(*/*)

Następnie dodaj nowy folder o tej samej nazwie, co kontroler w folderze asset / javascripts i umieść w nim plik js o tej samej nazwie, co twoja akcja. Następnie załaduje go do tej konkretnej akcji.

Erick Maynard
źródło
1

Ok, więc może to najgorsza praca na świecie, ale stworzyłem metodę kontrolera, która właśnie wyrenderowała plik .js

Kontroler

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

Widok

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

jeśli z jakiegoś powodu nie chcemy tego robić, daj mi znać.

Rachunek
źródło
1

Preferowanym sposobem dodawania JS jest stopka, więc możesz to zrobić w ten sposób:

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
źródło