Reputation: 370
I am a rails beginner and this simple question is really perplexing me. On every page, I want a list of all available pages in my app. So in layouts/application.html.erb I have the following code:
<ul>
<!-- This loop prints out links to all the pages -->
<% @pages = Page.all %>
<% @pages.each do |page| %>
<li><%= link_to page.name, page_path(page) %></li>
<% end %>
This code DOES work, however I am not satisfied with it because it goes against MVC principles. Really instead of querying the database with <% @pages = Page.all %>, I instead want to call the getpages method in the PagesController, which looks like this:
def getpages
@pages = Page.all
end
My first instinct was to replace <% @pages = Page.all %> with <% PagesController.getpages %>, which was unsuccessful. I am confident this problem has something to do with routing, could anyone enlighten me.
Upvotes: 1
Views: 2058
Reputation: 13926
There are several ways you accomplish this. I'll try to sum them up, but the easiest one is by using a before_filter
on your controller.
Considering your method is named get_pages
in the controller, just write:
class SomeController < ApplicationController
before_filter :get_pages
def get_pages
@pages = Page.all
end
end
Then, on your view, you'll be able to access it as @pages
on all your controller actions.
Upvotes: 1
Reputation: 1352
If so, simply add the following code into action in the controller
def youraction
@pages = Page.all
end
This will make the global variable @pages available in your view for use. Call it directly as @pages.
Upvotes: 0
Reputation: 50057
Firstly, let me say that your approach is not that wrong imho. Introducing an extra method which is just an alias for Page.all
is not really needed, or not really an improvement.
But there are definitely better approaches.
I would fix this as follows: in ApplicationController
I would add the method
def get_all_pages
@pages ||= get_all_pages
end
This will make sure that any controller knows that method.
Then, if you are sure, you need it on every page, you can just a before_filter
in your ApplicationController
, but generally, I prefer to write it in the controller itself.
E.g.
class IdeasController < ApplicationController
before_action :get_all_pages
... the rest of your controller ...
end
Also, I would extract your list of pages into a partial, store in under app/views/shared/_pages_list.html.erb
or app/views/pages/_pages_list.html.erb
and then in your application.html.erb
just call to render the partial instead.
The method we called sets the instance variable @pages
, it will be available in the view, so your partial would like:
<ul>
<% @pages.each do |page| %>
<li><%= link_to page.name, page_path(page) %></li>
<% end %>
</ul>
Lastly, for completeness, let me answer how you can make a controller-method available in the view. In a controller write
def get_pages
# ..do something
end
helper_method :get_pages
This will make the method get_pages
available in the view, but only for a controller that was responsible for loading/rendering that view. So if you want it for all views, define this in ApplicationController
. E.g. this is what we generally do when defining current_user
method.
if you start to get some more code, extract the code related to the page-list-fetching-and-rendering into a separate module, and include that into your ApplicationController
. This makes your ApplicationController
more readable.
concerns
were introduced for, so you can use that, if you are on rails 4a Page
is its own resource, so you could always create a separate PagesController
and make sure it is responsible for rendering the list, and use ajax to fetch on the pages where it is needed. This is a really nice solution, but also a bit more work (overkill?). But very nicely structured.
Upvotes: 1
Reputation: 76774
You could use a helper method like this:
#app/helpers/application_helper.rb
Class ApplicationHelper
def pages
Page.all
end
end
Helpers are available in your views, so you'll be able to do this:
#app/views/application/index.html.erb
<% pages.each do |page| %>
<%= page.title %>
<% end %>
Upvotes: 1
Reputation: 2617
Another approach would be using an asynchronous call to load the result of PagesController#index
and show it inside your application layout.
$.ajax("<%= pages_path %>").done(function(html) {
$("#pages-list").html(html);
});
To achieve this you could make PagesController#index
render with layout: false
if the request.xhr?
returns true
.
Upvotes: 0