Reputation: 8607
I'm battling with how much logic should be in a controller and how much in a view?
For example, I have a 'toggle' link that turns a filter on and off. The result is a link with some text depending on the state of the toggle, and whether a query param added or not.
i.e. in one state
<%= link_to 'With filter', polymorphic_path(Thing, { filtered: 1 }) %>
and in the other state
<%= link_to 'Without filter', polymorphic_path(Thing, {}) %>
I'm torn between either assigning the link label and query params in the controller, so then I'll have no logic in the view, and both variables are set in the controller...
<%= link_to @filter_link_text, polymorphic_path(Thing, @filter_link_params) %>
but it seems like I'm putting too much view logic into the controller
or the controller sets up a simple flag and leaves the rest to the view...
<% if @offer_filter %>
<%= link_to 'With filter', polymorphic_path(Thing, { filtered: 1 }) %>
<% else %>
<%= link_to 'Without filter', polymorphic_path(Thing, {}) %>
<% end %>
Could translations help?
Thanks.
Upvotes: 1
Views: 65
Reputation: 164819
You have a number of options, it depends on the circumstances.
You do not want to put view text into the controller. No @filter_link_text
. The controller connects models to views. The view handles how things are displayed.
Simplest is what you already suggested, the controller sets a flag which the view uses.
<% if @offer_filter %>
<%= link_to 'With filter', polymorphic_path(@thing, { filtered: 1 }) %>
<% else %>
<%= link_to 'Without filter', polymorphic_path(@thing, {}) %>
<% end %>
Then you can move this into a partial. Move the code into app/views/shared/_filtered_thing.html.erb
and render it in your view. This simplifies your view and allows sharing of view code.
<%= render 'shared/filtered_thing' %>
A middle ground is to use a decorator. A decorator is a thin wrapper around a model which lets you add and change its behavior specific to a certain use. It avoids fattening up the model with a bunch of view code.
draper
implements decorators and ties in well with Rails. It uses a very similar problem as its example.
class ThingDecorator < Draper::Decorator
delegate_all
def filter_toggle_link(filtered)
if filtered
# h allows access to the Rails helper methods
h.link_to 'With filter', h.polymorphic_path(self, { filtered: 1 })
else
h.link_to 'Without filter', h.polymorphic_path(self, {})
end
end
end
Now your view calls that method on the decorated object.
<%= @thing.filter_toggle_link(@offer_filter) %>
You can also add flags to your decorator. For example, if you needed to set filtered per object.
class ThingDecorator < Draper::Decorator
delegate_all
attr_accessor :filtered
def filter_toggle_link
if filtered?
# h allows access to the Rails helper methods
h.link_to 'With filter', h.polymorphic_path(self, { filtered: 1 })
else
h.link_to 'Without filter', h.polymorphic_path(self, {})
end
end
def filtered?
filtered
end
end
# In the controller
@thing.filtered = true
# In the view
<%= @thing.filter_toggle_link %>
A big advantage of a decorator over a view partial is you can unit test a decorator.
Partials and decorators open up more ways to keep your view logic well factored and keeping it from bloating your controllers and models.
Upvotes: 2