W Rails 3.1, gdzie umieszczasz swój kod JavaScript „specyficzny dla strony”?

388

O ile mi wiadomo, wszystkie skrypty JavaScript zostają scalone w jeden plik. Rails robi to domyślnie, gdy dodaje //= require_tree .się do dolnej części application.jspliku manifestu.

To brzmi jak prawdziwa oszczędność życia, ale trochę martwię się o specyficzny dla strony kod JavaScript. Czy ten kod jest wykonywany na każdej stronie? Ostatnią rzeczą, jakiej chcę, jest tworzenie instancji wszystkich moich obiektów dla każdej strony, gdy są one potrzebne tylko na 1 stronie.

Ponadto, czy nie ma też potencjału do konfliktu kodu?

A może umieścisz mały scripttag na dole strony, który po prostu wywołuje metodę, która wykonuje kod javascript dla strony?

Czy nie potrzebujesz już wtedy pliku.j.j?

Dzięki

EDYCJA : Doceniam wszystkie odpowiedzi ... i nie sądzę, że tak naprawdę mają problem. Niektóre z nich dotyczą stylizacji i nie wydają się odnosić do innych ... a inne po prostu wspominają javascript_include_tag... o których wiem, że istnieją (oczywiście ...), ale wydaje się, że drogą do Rails 3.1 jest zakończenie wszystkich JavaScript w jednym pliku zamiast ładowania pojedynczego JavaScript na dole każdej strony.

Najlepszym rozwiązaniem, jakie mogę wymyślić, jest zawinięcie niektórych funkcji w divtagi za pomocą ids lub classes. W kodzie JavaScript wystarczy sprawdzić, idczy classna stronie jest ikona lub, a jeśli tak, uruchom kod JavaScript, który jest z nią powiązany. W ten sposób, jeśli elementu dynamicznego nie ma na stronie, kod JavaScript nie działa - nawet jeśli został zawarty w ogromnym application.jspliku spakowanym przez Sprockets.

Moje powyższe rozwiązanie ma tę zaletę, że jeśli pole wyszukiwania znajduje się na 8 ze 100 stron, będzie działać tylko na tych 8 stronach. Nie musisz także umieszczać tego samego kodu na 8 stronach w witrynie. W rzeczywistości nigdy więcej nie będziesz musiał umieszczać ręcznych tagów skryptowych w swojej witrynie.

Myślę, że to jest właściwa odpowiedź na moje pytanie.

Godło ognia
źródło
11
„W przyszłości Rails 3.1 polega na zawinięciu całego kodu JavaScript w jeden plik zamiast ładowaniu pojedynczego kodu Javascript na dole każdej strony.” - Tylko dlatego, że zespół podstawowy Rails jest i zawsze był bardzo zły, wiedząc, jak zarządzać JavaScript. Małe pliki są ogólnie lepsze (zobacz moje komentarze w innym miejscu). Jeśli chodzi o JavaScript, sposób w Railsach rzadko jest właściwy (z wyjątkiem potoku zasobów, który kopie tyłek i zachęty do korzystania z CoffeeScript).
Marnen Laibow-Koser
Więc na każdej stronie umieścisz pliki js specyficzne dla strony? Myślę, że to strata, zgadzam się bardziej z odpowiedzią ClosureCowboy.
gerky
1
Czy spojrzałeś na zaakceptowaną odpowiedź na to pytanie? stackoverflow.com/questions/6571753/…
rassom
1
@DutGRIFF Innymi słowy: nie, w tym przypadku nie najlepiej jest robić rzeczy po Railsach (a przynajmniej nie wkładać wszystkiego application.js), a podane przez ciebie odniesienie wskazuje, dlaczego tak jest: pobieranie jest najwolniejsza część procesu wykonywania JS. Wiele małych plików jest bardziej buforowalnych niż jeden duży. Ludzie z Unholy Rails nie zdają sobie zatem sprawy z tego, że ich rekomendacje są niezgodne z zasadami, których starają się przestrzegać, i dlatego ich rekomendacje nie powinny być traktowane poważnie.
Marnen Laibow-Koser
1
@DutGRIFF Nie, duży plik JS zwykle nie byłby dobry, nawet po buforowaniu. Zobacz moje komentarze w innym miejscu na tej stronie: małe pliki mogą lepiej kierować na określone strony i mogą być buforowane z większą szczegółowością. Nie widzę żadnej sprawy dobrego wykorzystania dla jednego dużego pliku, chyba że nie ma kodu strony specyficzne w ogóle .
Marnen Laibow-Koser

Odpowiedzi:

157

Dokumenty potoku zasobów sugerują, jak wykonać JS specyficzne dla kontrolera:

Na przykład, jeśli a ProjectsControllerjest generowany, będzie nowy plik o, app/assets/javascripts/projects.js.coffeea drugi o app/assets/stylesheets/projects.css.scss. Należy umieścić dowolny kod JavaScript lub CSS unikalny dla kontrolera w odpowiednich plikach zasobów, ponieważ pliki te można następnie załadować tylko dla tych kontrolerów za pomocą linii takich jak <%= javascript_include_tag params[:controller] %>lub <%= stylesheet_link_tag params[:controller] %>.

Link do: rurociąg_zasobów

meleyal
źródło
50
To najbardziej elegancki sposób na zrobienie tego. Ale musisz także usunąć linię // = wymagany_drzew. z application.js.coffee
zsljulius
2
Całkowicie zgadzam się z tą metodą. Inne metody wydają się bardzo niezgrabne i wciąż kończą się ładowaniem ogromnego pliku js. Projekt, nad którym pracuję, ma prawie 2 MB wartości plików JS / wtyczek itp. PO łączeniu / minimalizacji.
Bill Garrison
2
Jestem dość nowy w Railsach, ale wydaje mi się, że powinno to być zachowanie domyślne.
Ross Hambrick
12
Dla kontroli specyficznej dla akcji mam to w swoim układzie, ponieważ nie każda akcja dla każdego kontrolera ma określoną JS. page_specific_js = "#{params[:controller]}_#{params[:action]}"i wtedy; javascript_include_tag page_specific_js if Rails.application.assets.find_asset page_specific_js
Sujimichi
2
Czy działania określone przez kontrolera są nadal minimalizowane? Czy są dodawane do pojedynczego pliku js utworzonego przez koła łańcuchowe, czy też prowadzą do wielu żądań plików zasobów?
Jason
77

W przypadku stron specyficznych dla strony można użyć rozwiązania Garber-Irish .

Tak więc folder javascripts w Railsach może wyglądać tak dla dwóch kontrolerów - samochodów i użytkowników:

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

A javascripts będą wyglądać następująco:

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

a markup_based_js_execution będzie zawierał kod dla obiektu UTIL, a na wykonanie UTIL.init gotowe do DOM.

I nie zapomnij umieścić tego w pliku układu:

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

Myślę również, że lepiej jest używać klas zamiast data-*atrybutów, aby uzyskać lepszy css specyficzny dla strony. Jak wspomniał Jason Garber: selektory CSS specyficzne dla strony mogą być naprawdę niewygodne (gdy używasz data-*atrybutów)

Mam nadzieję, że to Ci pomoże.

welldan97
źródło
4
Co jeśli potrzebujesz zmiennej dostępnej dla wszystkich akcji w kontrolerze użytkowników, ale niedostępnej w innych kontrolerach? Czy ta metoda nie ma problemów z zasięgiem?
tybro0103,
@ tybro0103, myślę, że aby zaimplementować to zachowanie, chciałbyś napisać coś window.varForOneController='val'w tej funkcji inicjującej kontroler. Pomocny może być również gem gem ( github.com/gazay/gon ). Mogą istnieć inne obejścia.
welldan97
1
@ welldan97 Oddawanie głosu nie dla twojego wyjaśnienia - co jest doskonałe - ale dlatego, że struktura Garber-Irish jest zła. Ładuje wszystkie twoje JS na każdej stronie i zależy od klas i identyfikatorów w elemencie <body>, aby to uporządkować. To pewny znak walki z DOM: w normalnych okolicznościach element <body> nie powinien potrzebować klasy ani identyfikatora, ponieważ w dokumencie jest tylko jeden. Właściwy sposób to po prostu usunąć //= require_tree .JavaScript i używać specyficznego dla strony JavaScript. Jeśli aktywnie próbujesz tego nie robić, to dążysz do złych praktyk.
Marnen Laibow-Koser
2
@ MarnenLaibow-Koser Osobiście uważam, że ładowanie wszystkich plików js na każdej stronie jest dobre dla większości projektów, gdy połączysz wszystkie pliki js w jeden plik i zminimalizujesz go. Uważam, że ogólnie działa szybciej dla użytkownika. Przynajmniej jest to bardziej plik konfliktu jeden js vs. wiele (np. Spójrz na stackoverflow.com/questions/555696/… ). Ponadto nie ma nic złego w korzystaniu z klas i identyfikatorów na ciele, jeśli upraszcza to kod i działa dla Ciebie. Modernizr ( modernizr.com ) robi to i kilka innych bibliotek.
welldan97
2
@ MarnenLaibow-Koser, rurociąg wydaje mi się dobrym kandydatem do porównania z kompilacją. Programista pisze swój javascript w ładnych, oddzielonych modułach, a następnie jest łączony w całość, minimalizowany i obsługiwany. Podobnie jak w przypadku języków kompilowanych, zawsze znajdą się programiści, którzy myślą, że są o krok przed kompilatorem ... ale myślę, że rzadko jest to prawdą.
Ziggy,
65

Widzę, że odpowiedziałeś na własne pytanie, ale oto inna opcja:

Zasadniczo zakładasz, że

//= require_tree .

jest wymagane. To nie jest. Możesz go usunąć. W mojej bieżącej aplikacji, po raz pierwszy, szczerze mówiąc, stworzyłem trzy różne pliki JS najwyższego poziomu. Mój application.jsplik ma tylko

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

W ten sposób mogę tworzyć podkatalogi z własnymi plikami JS najwyższego poziomu, które zawierają tylko to, czego potrzebuję.

Klucze to:

  1. Możesz usunąć require_tree- Railsy pozwalają zmienić przyjęte założenia
  2. W nazwie nie ma nic specjalnego application.js- każdy plik w assets/javascriptpodkatalogu może zawierać dyrektywy poprzedzające procesor//=

Mam nadzieję, że to pomaga i dodaje pewne szczegóły do ​​odpowiedzi ClosureCowboy.

Sujal

sujal
źródło
8
+1 To wspaniale wiedzieć dla nowicjusza takiego jak ja. Dałbym to +2, gdybym mógł.
jrhorn424,
5
@sujal Dokładnie. Główny zespół Railsów słynie z beznadziejnego zarządzania JavaScript. Możesz zignorować ich sugestie i po prostu skorzystać z dobrych części potoku zasobów. :)
Marnen Laibow-Koser
1
Bardzo dziękuję za tę radę. Nie mam kilku plików JS „najwyższego poziomu”, w zależności od modułu mojej aplikacji. Działa dobrze.
elsurudo,
1
+1 Ważnym punktem tutaj dla mnie jest to, że można zastąpić //= require_tree .z //= require_directory .tak można zachować wszystkie istniejące pliki, gdzie one są i tworzyć nowe katalogi dla strona konkretnych plików.
zelanix
41

Inna opcja: aby utworzyć pliki specyficzne dla strony lub modelu, możesz utworzyć katalogi w swoim assets/javascripts/folderze.

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

Główny application.jsplik manifestu można skonfigurować tak, aby ładował jego pliki global/. Określone strony lub grupy stron mogą mieć własne manifesty, które ładują pliki z ich własnych katalogów. Koła zębate automatycznie połączą ładowane pliki application.jsz plikami specyficznymi dla strony, co pozwala na działanie tego rozwiązania.

Tę technikę można również zastosować style_sheets/.

Zamknięcie Cowboy
źródło
13
Sprawiłeś, że mam teraz ochotę na babeczki .. Dangit!
Chuck Bergeron,
Naprawdę podoba mi się to rozwiązanie. Jedyny problem, jaki mam z tym, to to, że te dodatkowe manifesty nie są kompresowane / uglikowane. Są jednak poprawnie skompilowane. Czy istnieje rozwiązanie, czy coś brakuje?
clst
1
czy to oznacza, że ​​przeglądarka ładuje jeden plik js, który jest kombinacją pliku globalnego + specyficznego dla strony?
lulalala
Czy możesz spojrzeć na moje pytanie, jeśli jest dostępne? stackoverflow.com/questions/17055213/…
Maximus S
1
@clst Uważam, że jest to odpowiedź, której szukasz: guide.rubyonrails.org/asset_pipeline.html#precompiling-assets
FrontierPsycho
23

Doceniam wszystkie odpowiedzi ... i nie sądzę, aby naprawdę rozwiązywały ten problem. Niektóre z nich dotyczą stylizacji i nie wydają się odnosić do innych ... a inne po prostu wspominają javascript_include_tag... o których wiem, że istnieją (oczywiście ...), ale wydaje się, że drogą do Rails 3.1 jest zakończenie wszystkich JavaScript w jednym pliku zamiast ładowania pojedynczego Javascript na dole każdej strony.

Najlepszym rozwiązaniem, jakie mogę wymyślić, jest zawinięcie niektórych funkcji w divtagi za pomocą ids lub classes. W kodzie javascript. Następnie po prostu sprawdzasz, idczy classna stronie jest ikona lub, a jeśli tak, to uruchamiasz powiązany z nią kod javascript. W ten sposób, jeśli elementu dynamicznego nie ma na stronie, kod javascript nie działa - nawet jeśli został zawarty w ogromnym application.jspliku spakowanym przez Sprockets.

Moje powyższe rozwiązanie ma tę zaletę, że jeśli pole wyszukiwania znajduje się na 8 ze 100 stron, będzie działać tylko na tych 8 stronach. Nie musisz także umieszczać tego samego kodu na 8 stronach w witrynie. W rzeczywistości nigdy więcej nie będziesz musiał umieszczać ręcznych tagów skryptowych w swojej witrynie - z wyjątkiem być może danych wstępnego ładowania.

Myślę, że to jest właściwa odpowiedź na moje pytanie.

Godło ognia
źródło
Ale tak naprawdę chcesz te ręczne <script>tagi. Tak, klasy i identyfikatory są częścią odpowiedzi, ale nie ma sensu, aby użytkownik ładował JavaScript, którego ta strona nie wymaga.
Marnen Laibow-Koser
4
@ MarnenLaibow-Koser powodem braku dodawania ręcznych tagów skryptu do każdej unikalnej strony jest to, że musisz pobrać tę treść skryptu przy każdym wyświetleniu strony. Jeśli jesteś w stanie spakować cały javascript do application.js przy użyciu potoku zasobów, wówczas użytkownik pobiera te skrypty tylko raz i pobiera application.js z pamięci podręcznej przy wszystkich kolejnych ładowaniach stron
jakeonrails
@ jakeonrails „powodem nie dodawania ręcznych znaczników skryptu do każdej unikalnej strony jest to, że musisz pobrać tę treść skryptu przy każdym wyświetleniu strony” - całkiem źle. Skrypt zostanie pobrany raz, a następnie zostanie pobrany z pamięci podręcznej przeglądarki na kolejne żądania. „Jeśli jesteś w stanie spakować wszystkie pliki javascript do application.js przy użyciu potoku zasobów, wówczas użytkownik pobiera te skrypty tylko raz” - prawda, ale kosztem niepotrzebnego kodu. Jeśli możesz zbudować JS w wiele małych plików zamiast jednego dużego, zyskujesz buforowanie bez niepotrzebnego kodu.
Marnen Laibow-Koser
1
@ MarnenLaibow-Koser Myślę, że lepiej byłoby powiedzieć, że jeśli spakujesz wszystko w jednym skrypcie, użytkownik musi pobrać tylko 1 skrypt dla dowolnej strony witryny. Jeśli masz wiele skryptów dla różnych części aplikacji, to oczywiście użytkownik musi pobrać więcej niż jeden skrypt. Obie metody zostaną oczywiście zapisane w pamięci podręcznej, ale w większości przypadków (małe i średnie aplikacje) jednorazowe podanie jednej aplikacji.js będzie bardziej wydajne w pobieraniu. Analiza JS może być inną historią, w zależności od tego, co serwujesz.
jakeonrails
1
@Ziggy Ponadto, jeśli mały plik jest używany tylko na 8 ze 100 stron, dlaczego kod powinien cały czas znajdować się w pamięci podręcznej użytkownika? Lepiej rzucić rzeczy, które nie są potrzebne.
Marnen Laibow-Koser
16

Zdaję sobie sprawę, że przychodzę na tę imprezę trochę później, ale chciałem wprowadzić rozwiązanie, którego ostatnio używałem. Najpierw jednak wspomnę ...

The Rails 3.1 / 3.2 Way (Nie, proszę pana. Nie podoba mi się.)

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

Podaję poniższe w celu zapewnienia kompletności w tej odpowiedzi, a ponieważ nie jest to nieopłacalne rozwiązanie ... chociaż nie dbam o to zbytnio.

„Rails Way” jest rozwiązaniem zorientowanym na kontroler, a nie zorientowanym na widok, jak zażądał pierwotny autor tego pytania. Istnieją pliki JS specyficzne dla kontrolera, nazwane na cześć odpowiednich kontrolerów. Wszystkie te pliki są umieszczone w drzewie folderów, które NIE są domyślnie zawarte w żadnej wymaganej przez aplikację.js.

Aby dołączyć kod specyficzny dla kontrolera, do widoku dodano następujące elementy.

<%= javascript_include_tag params[:controller] %>

Nienawidzę tego rozwiązania, ale ono istnieje i jest szybkie. Prawdopodobnie można zamiast tego nazwać te pliki czymś w rodzaju „people-index.js” i „people-show.js”, a następnie użyć czegoś takiego, "#{params[:controller]}-index"aby uzyskać rozwiązanie zorientowane na widok. Znowu szybka naprawa, ale to nie pasuje do mnie.

My Data Attribute Way

Nazywaj mnie szalonym, ale chcę, aby WSZYSTKIE moje JS zostały skompilowane i zminimalizowane w pliku application.js podczas wdrażania. Nie chcę pamiętać, aby w każdym miejscu umieścić te małe pliki maruderów.

Ładuję wszystkie moje JS w jednym kompaktowym, niedługo buforowanym pliku przeglądarki. Jeśli jakiś fragment pliku application.js wymaga uruchomienia na stronie, pozwalam HTMLowi powiedzieć, a nie Railsom.

Zamiast blokować mój JS do określonych identyfikatorów elementów lub zaśmiecać mój HTML klasami znaczników, używam niestandardowego atrybutu danych o nazwie data-jstags.

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

Na każdej stronie używam - wstaw tutaj preferowaną metodę biblioteki JS - aby uruchomić kod po zakończeniu ładowania DOM. Ten kod ładowania początkowego wykonuje następujące działania:

  1. Iteruj po wszystkich elementach w DOM oznaczonych data-jstag
  2. Dla każdego elementu podziel wartość atrybutu na spację, tworząc tablicę ciągów znaczników.
  3. Dla każdego ciągu znacznika wykonaj wyszukiwanie w haszu dla tego znacznika.
  4. Jeśli znaleziono pasujący klucz, uruchom powiązaną z nim funkcję, przekazując element jako parametr.

Powiedzmy, że mam gdzieś w mojej pliku 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
};

Zdarzenie ładowania początkowego zastosuje funkcje my_autosuggest_initi w my_hint_initstosunku do danych wejściowych wyszukiwania, zamieniając je w dane wejściowe, które wyświetlają listę sugestii podczas pisania przez użytkownika, a także zapewniają pewien rodzaj wskazówek wejściowych, gdy dane wejściowe są puste i nie są skoncentrowane.

O ile jakiś element nie jest oznaczony data-jstag="auto-suggest", kod autosugestii nigdy nie zostanie uruchomiony. Jednak zawsze tam jest, zminimalizowany i ostatecznie buforowany w mojej application.js na te czasy, kiedy potrzebuję go na stronie.

Jeśli musisz przekazać dodatkowe parametry do oznaczonych funkcji JS, musisz zastosować trochę kreatywności. Dodaj atrybuty parametrów danych, wymyśl jakąś składnię parametrów, a nawet zastosuj podejście hybrydowe.

Nawet jeśli mam skomplikowany obieg pracy, który wydaje się specyficzny dla kontrolera, po prostu utworzę dla niego plik w folderze lib, spakuję go do pliku application.js i oznaczy go słowem „kreator nowych rzeczy”. Kiedy mój bootstrap trafi w ten tag, mój miły, fantazyjny kreator zostanie utworzony i uruchomiony. W razie potrzeby działa dla widoków tego kontrolera, ale nie jest w inny sposób sprzężony z kontrolerem. W rzeczywistości, jeśli poprawnie koduję mojego kreatora, być może będę w stanie dostarczyć wszystkie dane konfiguracyjne w widokach, a zatem będę mógł ponownie użyć mojego kreatora później dla dowolnego innego kontrolera, który tego potrzebuje.

Tak czy inaczej, od pewnego czasu wdrażam JS specyficzne dla strony i dobrze mi to służyło zarówno w przypadku prostych projektów witryn, jak i bardziej złożonych / bogatych aplikacji. Mam nadzieję, że jedno z dwóch rozwiązań, które tu przedstawiłem, na swój sposób lub na Railsach, jest pomocne dla każdego, kto napotka to pytanie w przyszłości.

Ryan
źródło
6
Jeden mały szczegół: w odpowiedzi znajduje się takie pojęcie, że gdy js jest buforowany w przeglądarce, nie ma to wpływu. To nie do końca prawda. Przeglądarka rzeczywiście unika pobierania, jeśli plik js jest poprawnie buforowany, ale nadal kompiluje kod na każdym renderowaniu strony. Musisz więc zrównoważyć kompromisy. Jeśli masz dużo JS łącznie, ale tylko niektóre są używane na stronę, możesz poprawić czasy stron, dzieląc JS na części.
sujal
Aby uzyskać więcej informacji na temat praktycznych efektów tego kroku kompilacji, o którym mówię, zobacz wyjaśnienie 37 Sygnałów, w jaki sposób pjax wpłynął na Basecamp Dalej: 37signals.com/svn/posts/…
sujal
To słuszna kwestia. Po przeczytaniu artykułu i spojrzeniu wstecz na projekty, w których korzystałem z powyższego rozwiązania, zdałem sobie sprawę, że napisałem zasadniczo to samo rozwiązanie „wyślij zmieniony HTML”, o którym wspominali w artykule. Częste ponowne kompilowanie JS nie było z tego powodu problemem w moich projektach. Etap kompilacji jest czymś, o czym będę pamiętać, pracując na stronach mniej zorientowanych na „aplikacje komputerowe”.
Ryan,
2
Głosowanie w dół za „Zadzwoń do mnie szalony, ale chcę, aby WSZYSTKIE moje JS zostały skompilowane i zminimalizowane do pliku application.js podczas wdrażania”. Naprawdę tego nie chcesz, ponieważ powoduje to, że użytkownik ładuje JavaScript, którego nie potrzebuje, i sprawia, że ​​twoje programy obsługi szukają atrybutów, których nawet tam nie będzie. Posiadanie wszystkiego w app.js jest kuszące, a Rails z pewnością ułatwia, ale właściwą rzeczą jest lepsza modularyzacja JavaScript.
Marnen Laibow-Koser
Masz prawo do innej opinii ... i technicznie masz prawo do głosowania za odmienną opinią. Byłoby jednak miło zobaczyć uzasadnienie, dlaczego jeden duży i buforowany plik jest gorszy od zmuszania wielu żądań HTTP do pobrania zmodularyzowanego JS. Co więcej, mylisz się co do procedury wyszukiwania obsługi. Wartości znaczników NIE są przeszukiwane. Tylko jedno wyszukiwanie jest wykonywane i pobiera wszystkie elementy, które mają atrybut data-jstag. Nie wyszukuje według nazwy znacznika, po prostu wyszukuje wszystkie elementy, które mają znaczniki, a następnie tworzy instancję tylko potrzebnych obiektów.
Ryan,
7

Odpowiedź została zaakceptowana i zaakceptowana dawno temu, ale wymyśliłem własne rozwiązanie oparte na niektórych z tych odpowiedzi i moim doświadczeniu z Rails 3+.

Rurociąg zasobów jest słodki. Użyj tego.

Po pierwsze, w twoim application.js usuń pliku//= require_tree.

Następnie w swojej application_controller.rbmetodzie pomocniczej:

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

Następnie w application.html.erbpliku układu dodaj nowego pomocnika spośród istniejących dołączonych skryptów javascript, poprzedzonych rawpomocnikiem:

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

Voila, teraz możesz łatwo tworzyć javascript specyficzny dla widoku, używając tej samej struktury plików, której używasz wszędzie indziej w szynach. Po prostu włóż swoje plikiapp/assets/:namespace/:controller/action.js.erb !

Mam nadzieję, że pomoże to komuś innemu!

Mike A.
źródło
1
Czy nie spowoduje to problemów po prekompilacji zasobów, a w czasie wykonywania <%= raw ... %>zwróci 404?
Nishant
Myślę, że potok zasobów nie jest słodki, ponieważ tworzy mnóstwo plików, których często nie należy używać. Więc dla mnie poleganie na potoku zasobów tworzy zależność od nieefektywnego systemu.
Deborah
1
@DeborahSpeece Kiedy potok zasobów tworzy pliki, których nie należy używać? Czy mylisz potok zasobów (dobry) z require_tree /(zły)?
Marnen Laibow-Koser
6

Możesz dodać ten wiersz do pliku układu (np. Application.html.erb), aby automatycznie załadować konkretny plik javascript dla kontrolera (ten, który został utworzony podczas generowania kontrolera):

<%= javascript_include_tag params[:controller] %>

Możesz także dodać wiersz, aby automatycznie ładować plik skryptu dla poszczególnych działań.

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

Po prostu umieść skrypty strony w podkatalogu nazwanym na cześć nazwy kontrolera. W tych plikach możesz dołączyć inne skrypty używając = wymagaj. Byłoby miło stworzyć pomocnika, który będzie dołączał plik tylko wtedy, gdy on istnieje, aby uniknąć awarii 404 w przeglądarce.

mcubik
źródło
6
<%= javascript_include_tag params[:controller] %>
Pan Bohr
źródło
2
Wygląda na to, że może odpowiedzieć na pytanie. Czy możesz dodać więcej do odpowiedzi, aby ją rozwinąć?
6

Być może znajdziesz klejnot pluggable_js jako odpowiednie rozwiązanie.

peresleguina
źródło
5

LoadJS gem ma innej opcji:

LoadJS zapewnia sposób ładowania specyficznego dla strony kodu JavaScript w aplikacji Rails bez utraty magii dostarczanej przez Sprockets. Cały kod JavaScript będzie kontynuowany przez zminimalizowanie w jednym pliku JavaScript, ale niektóre jego fragmenty zostaną wykonane tylko dla niektórych stron.

https://github.com/guidomb/loadjs

meleyal
źródło
3

Odpowiedź Filipa jest całkiem dobra. Oto kod, aby to działało:

W application.html.erb:

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

Zakładając, że twój kontroler nazywa się Projekty, które wygenerują:

<body class="projects">

Następnie w projects.js.coffee:

jQuery ->
  if $('body.projects').length > 0  
     $('h1').click ->
       alert 'you clicked on an h1 in Projects'
Rahil Sondhi
źródło
Downvoting: każde rozwiązanie, które stawia klasę, <body>jest ipso facto nieprawidłowe. Zobacz moje komentarze w innym miejscu na tej stronie.
Marnen Laibow-Koser
Nie rób tego Problem polega na tym, że za każdym razem, gdy dodajesz jeden z nich, dodajesz kolejny plik js, który należy wykonać przy ładowaniu strony. Może to z pewnością spowodować spadek wydajności w miarę rozwoju projektu.
ifightcrime
2

Skrypty JavaScript są scalane tylko wtedy, gdy powiesz Railsom (raczej zębatkom), aby je scaliły.

yfeldblum
źródło
Oczywiście. Myślę, że pytam, ponieważ domyślne ustawienia Railsów obejmują wszystko w folderze ... co oznacza, że ​​David zamierza to zrobić. Ale jak powiedziałem w innym komentarzu do @rubyprince, nie jestem pewien, czy wykonanie nastąpi w ten sposób. Myślę, że muszę wyłączyć //= require_tree .?
Godło ognia
@FireEmblem Tak. require_tree .jest zwykle złym pomysłem.
Marnen Laibow-Koser
2

W ten sposób rozwiązałem problem ze stylizacją: (przepraszam Haml)

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

W ten sposób uruchamiam wszystkie pliki .css.sass specyficzne dla strony za pomocą:

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

W ten sposób możesz łatwo uniknąć wszelkich starć. Jeśli chodzi o pliki .js.coffee , możesz po prostu zainicjować elementy takie jak;

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

Mam nadzieję, że to pomogło niektórym.

zeeraw
źródło
1
Przeczytaj jeszcze raz ostatni fragment, w javascript możesz skorzystać z tej samej struktury, z której korzystasz w arkuszach stylów do inicjowania określonych funkcji widoku.
zeeraw
Philip, $('#post > #edit') ->wydaje się być nieważny. Jak zaklasyfikować jQuery do pracy w zakresie?
Ramon Tayag,
2
Ostatnio zacząłem ładować wszystkie skrypty i arkusze stylów specyficzne dla kontrolera, wywołując to w application.html.haml; = javascript_include_tag "application"i w = javascript_include_tag params[:controller]ten sposób mogę zachować kod JavaScript bez konieczności określania zakresu w pliku.
zeeraw 24.09.11
2

Zgadzam się z twoją odpowiedzią, aby sprawdzić, czy ten selektor istnieje, użyj:

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

(nie widziałem, żeby ktokolwiek dodał rzeczywiste rozwiązanie)

Kyle C.
źródło
2

Nie widzę odpowiedzi, która naprawdę to wszystko łączy i przedstawia dla ciebie. Dlatego postaram się połączyć meleyal , sujal (a la ClosureCowboy ), pierwszą część odpowiedzi Ryana , a nawet śmiałą wypowiedź Gal o Backbone.js ... wszystko razem w sposób krótki i jasny. I kto wie, może nawet spotkam się z Marnen Laibow-Koser .

Przykładowe zmiany

asset / 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 %>


asset / javascripts / foo.js

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


asset / javascripts / foostuff / foothis.js.coffee

alert "Hello world!"


Krótki opis

  • Usuń //= require_tree .z application.js i wymień tylko JS, które udostępnia każda strona.

  • Dwie linie pokazane powyżej w application.html.erb wskazują stronie, gdzie należy dołączyć application.js i JS specyficzny dla strony.

  • Trzy wiersze pokazane powyżej w pliku index.html.erb nakazują widokowi wyszukiwanie JS specyficznych dla strony i dołączenie go do nazwanego regionu wydajności o nazwie „: javascript” (lub jakkolwiek chcesz go nazwać). W tym przykładzie kontrolerem jest „foo”, więc Railsy spróbują dołączyć „foo.js” w regionie wydajności: javascript w układzie aplikacji.

  • Wymień JS specyficzny dla strony w foo.js (lub jakiejkolwiek nazwie kontrolera). Lista wspólnych bibliotek, drzewa, katalogów, cokolwiek.

  • Zachowaj swój niestandardowy JS specyficzny dla strony w miejscu, w którym możesz łatwo do niego odwoływać, oprócz innych niestandardowych JS. W tym przykładzie foo.js wymaga drzewa foostuff, więc umieść tam swój niestandardowy JS, na przykład foothis.js.coffee .

  • Tutaj nie ma twardych zasad. W razie potrzeby możesz swobodnie przenosić różne elementy, a może nawet utworzyć wiele obszarów zbiorów o różnych nazwach w różnych układach. To pokazuje tylko jeden możliwy pierwszy krok naprzód. (Nie robię tego dokładnie tak, biorąc pod uwagę nasze użycie Backbone.js. Mógłbym również upuścić foo.js do folderu o nazwie foo zamiast foostuff, ale jeszcze tego nie zdecydowałem).

Notatki

Możesz robić podobne rzeczy za pomocą CSS, <%= stylesheet_link_tag params[:controller] %>ale to nie wchodzi w zakres pytania.

Jeśli przegapiłem rażącą najlepszą praktykę tutaj, wyślij mi notatkę, a ja dostosuję się do conisder. Rails jest dla mnie dość nowy i, szczerze mówiąc, do tej pory nie jestem pod ogromnym wrażeniem chaosu, który domyślnie wprowadza do rozwoju przedsiębiorstwa i całego ruchu generowanego przez przeciętny program Railsowy.

juanitogan
źródło
wygląda na to, jak należy postępować. Zobaczę, czy uda mi się to zaimplementować we własnej aplikacji, dzięki za szczegółową odpowiedź.
martincarlin87
1

Mam inne rozwiązanie, które choć prymitywne działa dla mnie dobrze i nie wymaga żadnych wymyślnych strategii ładowania selektywnego. Włóż funkcję gotowego dokumentu nornal, ale następnie sprawdź bieżącą lokalizację systemu Windows, aby sprawdzić, czy jest to strona, dla której przeznaczony jest javascript:

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

Nadal pozwala to na załadowanie wszystkich plików JS przez szyny 3.x w jednym małym pakiecie, ale nie generuje dużego obciążenia ani żadnych konfliktów ze stronami, dla których plik JS nie jest przeznaczony.

Peter Abramowitsch
źródło
1

Odpowiedź ryguy jest dobrą odpowiedzią, mimo że została oceniona na negatywne.

Zwłaszcza jeśli używasz czegoś takiego jak Backbone JS - każda strona ma swój własny widok Backbone. Następnie plik erb ma tylko jeden wiersz wbudowanego javascript, który uruchamia klasę widoku prawego szkieletu. Uważam to za pojedynczy wiersz „kodu kleju”, a zatem fakt, że jego wstawka jest OK. Zaletą jest to, że możesz zachować „wymagany_tree”, co pozwala przeglądarce na buforowanie całego javascript.

w show.html.erb będziesz mieć coś takiego:

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

aw pliku układu potrzebujesz:

<%= yield :javascript %>
Gal
źródło
Downvoting. Wbudowany JavaScript nigdy nie jest dobrym pomysłem. Nawet jeśli jest to kod kleju, powinien znajdować się w pliku zewnętrznym.
Marnen Laibow-Koser
1

Przenieś wszystkie swoje wspólne pliki JS do podfolderu, takiego jak „app / asset / javascript / global”, a następnie w pliku application.js zmień //= require_tree .wiersz na //= require_tree ./global.

Teraz możesz umieścić swoją JS specyficzną dla kontrolera w katalogu głównym „app / asset / javascript /” i nie będą one uwzględnione w skompilowanym JS, będą używane tylko wtedy, gdy wywołasz je za pośrednictwem = javascript_include_tagkontrolera / widoku.

Gedean Dias
źródło
Nie ma mowy, to jest ładunek JavaScript do załadowania dla jednej strony. Nie ma nawet znaczenia, czy jest w pamięci podręcznej.
jackyalcine
1

Chociaż masz tutaj kilka odpowiedzi, myślę, że Twoja edycja jest prawdopodobnie najlepszym wyborem. Wzorzec projektowy, którego używamy w naszym zespole, który otrzymaliśmy od Gitlab, to wzór Dyspozytora. Robi coś podobnego do tego, o czym mówisz, jednak nazwa strony jest ustawiana w tagu body za pomocą szyn. Na przykład w pliku układu po prostu dołącz coś takiego (w HAML):

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

W takim przypadku masz tylko jedno zamknięcie i instrukcję switch w swoim dispatcher.js.coffeepliku w folderze javascripts:

$ ->
  new Dispatcher()

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

Wszystko, co musisz zrobić w poszczególnych plikach (powiedzmy products.js.coffeelub login.js.coffeena przykład), to umieścić je w klasie, a następnie zglobalizować ten symbol klasy, aby uzyskać do niego dostęp w programie rozsyłającym:

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

Gitlab ma kilka przykładów tego, z którymi możesz się wyśmiewać na wypadek, gdybyś był ciekawy :)

onetwopunch
źródło
1

Projekt Paloma oferuje ciekawe podejście do zarządzania specyficznym dla strony kodem javascript.

Przykład użycia z ich dokumentów:

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

// Executes when Rails User#new is executed.
UsersController.prototype.new = function(){
   alert('Hello Sexy User!' );
};
zavg
źródło
1

Krok 1. usuń wymagane drzewo. w plikach application.js i application.css.

Krok 2. Edytuj plik application.html.erb (domyślnie w szynach) w folderze układu. Dodaj „params [: kontroler]” w następujących tagach.

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

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

Krok 3. Dodaj plik w config / initializers / resources.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

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

etlds
źródło
Chociaż teoretycznie może to odpowiedzieć na pytanie, lepiej byłoby zawrzeć tutaj istotne części odpowiedzi i podać odnośnik.
Bhargav Rao
0

Nie wypróbowałem tego, ale wygląda na to, że spełnione są następujące warunki:

  • jeśli masz content_for, czyli javascript (np. z prawdziwym javascript w nim zawartym), koła zębate nie wiedziałyby o tym, a zatem działałoby to tak samo jak teraz.

  • jeśli chcesz wykluczyć plik z dużego pakietu javascript, przejdź do pliku config / sprockets.yml i odpowiednio zmodyfikuj pliki_źródłowe. Następnie wystarczy w razie potrzeby dołączyć dowolny plik, który został wykluczony.

Bill Eisenhauer
źródło
Czy wykluczanie plików lub używanie niestandardowego javascript na samej stronie jest zatem „właściwym sposobem”? Czy tak David chciał, żeby ludzie go używali?
Godło ognia
@FireEmblem Nie obchodzi mnie, co zamierzał David, ponieważ nie sądzę, że David rozumie, jak poprawnie zorganizować JavaScript.
Marnen Laibow-Koser
0

Połączyłem niektóre odpowiedzi w:

Pomocnik aplikacji:

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

layouts / 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  
kapellan
źródło
0

Po pierwsze: usuń \\=require_treez application.js Po drugie: cały twój kod JS musi być przydzielony na, /app/assets/javascritpta cały twój kod CSS musi być przydzielony na/app/assets/stylesheets

Nicollas
źródło
-2

Idąc tropem Ryana, oto co zrobiłem-

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 specyficzny dla kontrolera, np. kontroler: użytkownicy, akcja: pulpit nawigacyjny)

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}}
Kruttik
źródło
Downvoting. Jest to absurdalnie skomplikowane - nie wspominając o niepewności (z powodu eval), jeśli Twój HTML zostanie naruszony przez pęknięty serwer lub złośliwy skrypt użytkownika.
Marnen Laibow-Koser
-3

Oto jak to zrobić, zwłaszcza jeśli nie musisz uruchamiać ton bibliotek dla określonej strony, ale tylko uruchomić kilkaset wierszy JS mniej więcej.

Ponieważ osadzanie kodu JavaScript w HTML jest całkowicie w porządku, po prostu stwórz w katalogu app / views shared.js i umieść tam swój kod strony / stron wewnątrz my_cool_partial.html.erb

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

Teraz z dowolnego miejsca po prostu rób:

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

I to wszystko, k?

łoś
źródło
2
Downvoting. Wbudowany JavaScript nigdy nie jest wskazany. HTML powinien zawierać tylko znaczniki. JS i CSS powinny znajdować się w osobnych plikach wielokrotnego użytku.
Marnen Laibow-Koser