web-dev-qa-db-ja.com

Rails 3.1を使用して、「ページ固有の」JavaScriptコードをどこに配置しますか?

私の理解では、すべてのJavaScriptは1つのファイルにマージされます。 Railsは、//= require_tree .マニフェストファイルの最後にapplication.jsを追加すると、デフォルトでこれを行います。

これは実際の命の恩人のように聞こえますが、ページ固有のJavaScriptコードについて少し心配しています。このコードはすべてのページで実行されますか?私が最後に欲しいのは、すべてのオブジェクトが1ページでのみ必要なときに、すべてのページでインスタンス化されることです。

また、コードが衝突する可能性もありませんか?

または、ページのjavascriptコードを実行するメソッドを呼び出すだけの小さなscriptタグをページの下部に配置しますか?

あなたはもはやrequire.jsを必要としませんか?

ありがとう

EDIT:私はすべての答えに感謝します...そして、彼らが本当に問題に取り組んでいるとは思いません。それらのいくつかはスタイリングに関するものであり、関連していないようです...そして、他の人はjavascript_include_tag...に言及しているだけですが、これは存在することは明らかです(もちろん...)各ページの下部に個別のJavaScriptをロードするのではなく、JavaScriptを1つのファイルにまとめます。

私が思いつく最善の解決策は、特定の機能をdivタグまたはidsまたはclassesでラップすることです。 JavaScriptコードでは、idまたはclassがページにあるかどうかを確認するだけで、存在する場合は、それに関連付けられているJavaScriptコードを実行します。このように、動的要素がページにない場合、JavaScriptコードは実行されません-Sprocketsによってパッケージ化された巨大なapplication.jsファイルに含まれていても。

上記のソリューションには、100ページのうち8ページに検索ボックスが含まれている場合、その8ページのみで実行されるという利点があります。また、サイトの8つのページに同じコードを含める必要はありません。実際、サイトに手動でスクリプトタグを追加する必要はもうありません。

これが私の質問に対する実際の答えだと思います。

386
Fire Emblem

私はすべての答えに感謝します...そして彼らが本当に問題に取り組んでいるとは思いません。それらのいくつかはスタイリングに関するものであり、関連していないようです...そして、他の人はjavascript_include_tag...に言及しているだけですが、これは存在することは明らかです(もちろん...)各ページの下部にある個々のJavascriptをロードするのではなく、Javascriptを1つのファイルにまとめます。

私が思いつく最善の解決策は、特定の機能をdivタグまたはidsまたはclassesでラップすることです。 JavaScriptコード内。次に、idまたはclassがページにあるかどうかを確認し、ある場合は、それに関連付けられているjavascriptコードを実行します。このように、動的要素がページ上にない場合、Sprocketsによってパッケージ化された巨大なapplication.jsファイルに含まれていても、javascriptコードは実行されません。

上記のソリューションには、100ページのうち8ページに検索ボックスが含まれている場合、その8ページのみで実行されるという利点があります。また、サイトの8つのページに同じコードを含める必要はありません。実際、サイトに手動のスクリプトタグを含める必要はもうありません-データをプリロードする場合を除きます。

これが私の質問に対する実際の答えだと思います。

23
Fire Emblem

Asset Pipelineのドキュメントでは、コントローラー固有のJSの実行方法が提案されています。

たとえば、ProjectsControllerが生成されると、app/assets/javascripts/projects.js.coffeeに新しいファイルが、app/assets/stylesheets/projects.css.scssに別のファイルが作成されます。これらのファイルは、<%= javascript_include_tag params[:controller] %><%= stylesheet_link_tag params[:controller] %>などの行を使用してこれらのコントローラーにのみロードできるため、コントローラーに固有のJavaScriptまたはCSSをそれぞれのアセットファイル内に配置する必要があります。

リンク:asset_pipeline

157
meleyal

ページ固有のjsには、 Garber-Irish solution を使用できます。

したがって、Rails javascriptsフォルダーは、車とユーザーの2つのコントローラーで次のようになります。

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

JavaScriptは次のようになります。

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

およびmarkup_based_js_executionには、UTILオブジェクトのコードと、DOM対応のUTIL.init実行のコードが含まれます。

そして、これをレイアウトファイルに入れることを忘れないでください:

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

また、ページ固有のCSSを改善するには、data-*属性の代わりにクラスを使用することをお勧めします。 Jason Garberが述べたように:ページ固有のCSSセレクターは本当に扱いにくい(data-*attributesを使用する場合)

これがあなたのお役に立てば幸いです。

77
welldan97

あなたはあなた自身の質問に答えたことがわかりますが、別の選択肢があります:

基本的に、あなたはそれを仮定しています

//= require_tree .

必要とされている。そうではありません。削除してください。現在のアプリケーションでは、最初に3.1.xで最初にやったことですが、3つの異なるトップレベルのJSファイルを作成しました。 application.jsファイルには

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

このようにして、必要なものだけが含まれる独自のトップレベルJSファイルでサブディレクトリを作成できます。

キーは次のとおりです。

  1. require_treeを削除できます-Railsを使用すると、仮定を変更できます
  2. application.jsという名前について特別なことはありません。assets/javascriptサブディレクトリ内のファイルには、//=のプリプロセッサディレクティブを含めることができます。

ClosureCowboyの答えにいくつかの詳細が役立ち、追加されることを願っています。

スジャール

65
sujal

別のオプション:ページまたはモデル固有のファイルを作成するには、assets/javascripts/フォルダー内にディレクトリを作成できます。

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

メインのapplication.jsマニフェストファイルは、global/からファイルをロードするように構成できます。特定のページまたはページのグループは、独自の特定のディレクトリからファイルをロードする独自のマニフェストを持つことができます。スプロケットは、application.jsによってロードされたファイルをページ固有のファイルと自動的に結合するため、このソリューションが機能します。

この手法は、style_sheets/にも使用できます。

40
ClosureCowboy

私はこのパーティーに少し遅れて来ていることに気づきましたが、最近使用しているソリューションを投入したかったのです。しかし、最初に言及させてください...

The Rails 3.1/3.2 Way(いいえ、私は好きではありません。)

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

この回答には完全を期すために以下を含めていますが、それは実行可能な解決策ではないためです...私はあまり気にしませんが。

「Rails Way」は、この質問の最初の著者が要求したようにビュー指向ではなく、コントローラー指向のソリューションです。それぞれのコントローラーにちなんで命名されたコントローラー固有のJSファイルがあります。これらのファイルはすべて、application.jsのrequireディレクティブにデフォルトで含まれていないフォルダーツリーに配置されます。

コントローラー固有のコードを含めるために、次がビューに追加されます。

<%= javascript_include_tag params[:controller] %>

私はこの解決策を嫌いますが、それはそこにあり、迅速です。おそらく、代わりにこれらのファイルを「people-index.js」や「people-show.js」のような名前で呼び出してから、"#{params[:controller]}-index"のようなものを使用してビュー指向のソリューションを得ることができます。繰り返しますが、簡単に修正できますが、それは私にはぴったりではありません。

My Data Attribute Way

クレイジーと呼びますが、デプロイするときにすべてのJSをコンパイルしてapplication.jsに縮小してほしいです。これらの小さなストラグラーファイルをあちこちに含めることを覚えておく必要はありません。

私はすべてのJSを1つのコンパクトなブラウザキャッシュファイルにロードします。 application.jsの特定の部分をページで起動する必要がある場合、RailsではなくHTMLに通知させます。

JSを特定の要素IDにロックしたり、マーカークラスでHTMLを散らかすのではなく、data-jstagsというカスタムデータ属性を使用します。

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

各ページで、-ここに優先JSライブラリメソッドを挿入します-を使用して、DOMの読み込みが完了したときにコードを実行します。このブートストラップコードは、次のアクションを実行します。

  1. data-jstagでマークされたDOM内のすべての要素を反復処理します
  2. 各要素について、属性値をスペースで分割し、タグ文字列の配列を作成します。
  3. タグ文字列ごとに、そのタグのハッシュでルックアップを実行します。
  4. 一致するキーが見つかった場合、そのキーに関連付けられている関数を実行し、要素をパラメーターとして渡します。

したがって、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
};

ブートストラップイベントは、検索入力に対してmy_autosuggest_initおよびmy_hint_init関数を適用し、ユーザーが入力している間、候補のリストを表示する入力に変換します。また、入力が空白でフォーカスされていない場合に何らかの入力ヒントを提供します。

いくつかの要素がdata-jstag="auto-suggest"でタグ付けされていない限り、自動提案コードは実行されません。ただし、ページ上で必要なときのために、常にそこにあり、縮小され、最終的にapplication.jsにキャッシュされます。

タグ付きJS関数に追加のパラメーターを渡す必要がある場合は、創造性を適用する必要があります。データパラメーター属性を追加するか、何らかのパラメーター構文を作成するか、ハイブリッドアプローチを使用します。

コントローラー固有の複雑なワークフローがある場合でも、libフォルダーにそのファイルを作成し、application.jsにパックして、「new-thing-wizard」などのタグを付けます。ブートストラップがそのタグにヒットすると、素敵でファンシーなウィザードがインスタンス化されて実行されます。必要に応じてそのコントローラーのビューに対して実行されますが、それ以外の場合はコントローラーに結合されません。実際、ウィザードを適切にコーディングすれば、すべての構成データをビューで提供できるため、後でウィザードを必要とする他のコントローラーでウィザードを再利用できる場合があります。

とにかく、これが私がしばらくページ固有のJSを実装してきた方法であり、単純なサイト設計とより複雑でリッチなアプリケーションの両方に役立ちました。ここで紹介した2つのソリューション、私の方法とRailsの方法のいずれかが、将来この質問に出くわした人にとって役立つことを願っています。

16
Ryan

これはかなり前に回答され、受け入れられましたが、これらの回答のいくつかとRails 3+での経験に基づいて独自のソリューションを思いつきました。

アセットパイプラインは便利です。これを使って。

まず、application.jsファイルで、//= require_tree.を削除します

次に、application_controller.rbでヘルパーメソッドを作成します。

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

次に、application.html.erbレイアウトファイルで、rawヘルパーを先頭に付けて、既存のjavascriptインクルードの中に新しいヘルパーを追加します。

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

これで、Railsの他の場所で使用しているのと同じファイル構造を使用して、ビュー固有のJavaScriptを簡単に作成できます。ファイルをapp/assets/:namespace/:controller/action.js.erbに貼り付けるだけです!

それが他の誰かを助けることを願っています!

7
Mike A
<%= javascript_include_tag params[:controller] %>
6
Mr Bohr

レイアウトファイル(application.html.erbなど)に次の行を追加して、コントローラー固有のjavascriptファイル(コントローラーを生成したときに作成されたファイル)を自動的にロードできます。

<%= javascript_include_tag params[:controller] %>

行を追加して、アクションごとにスクリプトファイルを自動的にロードすることもできます。

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

コントローラー名にちなんで名付けられたサブディレクトリにページスクリプトを配置するだけです。これらのファイルには、= requireを使用して他のスクリプトを含めることができます。ブラウザーで404エラーが発生するのを避けるために、ファイルが存在する場合にのみファイルを含めるヘルパーを作成するとよいでしょう。

6
mcubik

おそらく、適切なソリューションとして pluggable_js gemが見つかるでしょう。

6
peresleguine

LoadJS gemは別のオプションです。

LoadJSは、Sprocketsが提供する魔法を失うことなく、Railsアプリにページ固有のJavascriptコードをロードする方法を提供します。すべてのJavascriptコードは1つのJavascriptファイルで縮小されて続行されますが、その一部は特定のページに対してのみ実行されます。

https://github.com/guidomb/loadjs

5
meleyal

フィリップの答えはとても良いです。これを機能させるコードは次のとおりです。

Application.html.erb内:

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

コントローラがプロジェクトと呼ばれると仮定すると、それは以下を生成します

<body class="projects">

次に、projects.js.coffeeで:

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

Jsをフォルダーにグループ化し、ページに応じてアセットパイプラインを引き続き使用して javascriptを選択的にロード することもできます。

2
Kenny Grant

JavaScriptは、Rails(Sprockets)にマージするように指示した場合にのみマージされます。

2
yfeldblum

私はあなたの答えに同意します、そのセレクタがそこにあるかどうかを確認するには、次を使用します:

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

(誰も実際のソリューションを追加するのを見ませんでした)

2
Kyle C

これは、スタイリングの問題を解決する方法です:(excuse the Haml)

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

このようにして、すべてのページ固有の。css.sassファイルを開始します。

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

これにより、衝突を簡単に回避できます。 。js.coffeeファイルに関しては、次のような要素を初期化できます。

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

これが助けになることを願っています。

2
zeeraw

私は本当にそれをすべてまとめて、あなたのためにそれをレイアウトする答えを見ません。したがって、meleyalsujal(a la ClosureCowboy)、Ryanの回答の最初の部分、そして偶数Gal'sBackbone.jsについての太字のステートメント...短く簡潔な方法でまとめます。そして、ご存知のとおり、私はMarnen Laibow-Koserの要件も満たすかもしれません。

編集例

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


assets/javascripts/-foo.js

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


assets/javascripts/foostuff/foothis.js.coffee

alert "Hello world!"


簡単な説明

  • application.jsから//= require_tree .を削除し、各ページが共有するJSのみをリストします。

  • 上記のapplication.html.erbの2行は、application.jsとページ固有のJSを含める場所をページに指示しています。

  • 上記のindex.html.erbの3行は、ビューにページ固有のJSを探し、「:javascript」という名前のyield領域(または名前を付けたいもの)に含めるように指示します。 )。この例では、コントローラーは "foo"であるため、Railsはアプリケーションレイアウトの:javascript yield領域に "foo.js"を含めようとします。

  • foo.js(またはコントローラーの名前)にページ固有のJSをリストします。一般的なライブラリ、ツリー、ディレクトリなどをリストします。

  • カスタムページ固有のJSは、他のカスタムJSとは別に簡単に参照できる場所に保管してください。この例では、foo.jsにはfoostuffツリーが必要であるため、foothis.js.coffeeなどのカスタムJSをそこに配置します。

  • ここには厳しいルールはありません。自由に物事を移動し、必要に応じてさまざまなレイアウトでさまざまな名前の複数のyield領域を作成することもできます。これは、考えられる最初の一歩を示しています。 (Backbone.jsを使用していることを考えると、このようにはしません。また、foo.jsをfoostuffではなくfooという名前のフォルダーにドロップすることもできますが、まだ決定していません。)

ノート

CSSと<%= stylesheet_link_tag params[:controller] %>でも同様のことができますが、これは質問の範囲を超えています。

ここで際立ったベストプラクティスを逃した場合は、メモを送ってください。適応を検討します。 Railsは私にとってかなり新しいものであり、正直なところ、これまでデフォルトでエンタープライズ開発と平均的なRailsプログラムが生成するすべてのトラフィックにもたらす混乱にひどく感銘を受けていません。

2
juanitogan

Paloma プロジェクトは、ページ固有のJavaScriptコードを管理する興味深いアプローチを提供します。

ドキュメントの使用例:

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

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

ryguyの答えは、負のポイントに降格されたとしても、良い答えです。

特に、Backbone JSのようなものを使用している場合-各ページには独自のBackboneビューがあります。次に、erbファイルには、正しいバックボーンビュークラスを起動するインラインjavascriptが1行だけ含まれています。私はそれを「接着コード」の1行と考えているため、そのインラインは問題ありません。利点は、ブラウザがすべてのjavascriptをキャッシュできるようにする「require_tree」を保持できることです。

show.html.erbには、次のようなものがあります。

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

レイアウトファイルには、次のものが必要です。

<%= yield :javascript %>
1
Gal

ステップ1。 require_treeを削除します。 application.jsおよびapplication.cssで。

ステップ2。レイアウトフォルダーでapplication.html.erb(Railsのデフォルト)を編集します。次のタグに「params [:controller]」を追加します。

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

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

ステップ3。 config/initializers/assets.rbにファイルを追加します

%w( controller_one controller_two controller_three ).each do |controller|
  Rails.application.config.assets.precompile += ["#{controller}.js", "#{controller}.js.coffee", "#{controller}.css", "#{controller}.scss"]
end

参照: http://theflyingdeveloper.com/controller-specific-assets-with-Rails-4/

1
etlds

私は別のソリューションを持っていますが、これはプリミティブではうまく機能しますが、派手な選択的ロード戦略は必要ありません。通常のドキュメント準備機能を追加しますが、現在のウィンドウの場所をテストして、javascriptが対象とするページであるかどうかを確認します。

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

これにより、すべてのjsが1つの小さなパッケージでRails 3.xによってロードされますが、jsが意図していないページと大きなオーバーヘッドや競合は発生しません。

1

ここにはいくつかの答えがありますが、おそらくあなたの編集が最善策だと思います。 Gitlab から得たチームで使用する設計パターンは、Dispatcherパターンです。これはあなたが話していることと似ていますが、ページ名はRailsによってbodyタグに設定されます。たとえば、レイアウトファイルに(HAMLで)次のようなものを含めるだけです。

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

次に、次のように、javascriptsフォルダーのdispatcher.js.coffeeファイルにクロージャーとswitchステートメントが1つだけあります。

$ ->
  new Dispatcher()

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

個々のファイル(たとえばproducts.js.coffeelogin.js.coffeeなど)で行う必要があるのは、それらをクラスで囲み、そのクラスシンボルをグローバル化してディスパッチャでアクセスできるようにすることだけです。

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

Gitlabにはいくつかの例がありますので、興味がある場合に備えて調べてみてください。

1
onetwopunch

すべての共通JSファイルを「app/assets/javascript/global」などのサブフォルダーに移動し、application.jsで//= require_tree .行を//= require_tree ./globalに変更します。

これで、コントローラー固有のJSを 'app/assets/javascript /'ルートに自由に配置できます。これらはコンパイル済みJSには含まれず、コントローラー/ビューで= javascript_include_tag経由で呼び出すときに使用されます。

1
Gedean Dias

以前にこのメソッドを使用して実行しました: http://theflyingdeveloper.com/controller-specific-assets-with-Rails-4/ とても簡単で、コントローラーを使用して適切なjsを選択してロードします。

0
Eddie Prislac

私はこれを試していませんが、次のことが当てはまるようです。

  • javascript(たとえば、実際のjavascriptを含む)であるcontent_forがある場合、スプロケットはそれを認識しないため、現在と同じように機能します。

  • javascriptの大きなバンドルからファイルを除外する場合は、config/sprockets.ymlファイルに移動し、それに応じてsource_filesを変更します。次に、必要な場所で除外したファイルのいずれかを含めるだけです。

0
Bill Eisenhauer

私はいくつかの答えを組み合わせました:

アプリケーションヘルパー:

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  
0
kapellan

最初:application.jsから\\=require_treeを削除します2番目:すべてのJSコードを/app/assets/javascritptに割り当て、すべてのCSSコードを/app/assets/stylesheetsに割り当てる必要があります

0