问题
I have a simple template system which calls a show_me
method defined inside different classes of my model (kinda Widgets) when rendering a template. These widgets originally returned the html as a string. So in my erb I have somethink like this.
<% @template.widgets.each do |widget| %>
<%= widget.show_me %>
<% end %>
As the views of the widgets became more complex I start using partials to render them, calling the ActionView::Base
render method from inside my widgets (please don't throw up yet :)
def show_me
# ... specific calculations and other magic.
ActionView::Base.new(MyApp::Application.config.view_path).render(:partial => "widgets/weather_widget", :locals => {:data => data, :settings => settings})
end
So, this works like a charm (really...) but when I want to use helpers inside the widget specific partial (eg. widgets/_weater_widget.html.erb
) they don't work. for example. javascript_tag
raises
can't convert nil into String
actionpack (3.0.0) lib/action_view/helpers/asset_tag_helper.rb:790:in `join'
actionpack (3.0.0) lib/action_view/helpers/asset_tag_helper.rb:790:in `rails_asset_id'
actionpack (3.0.0) lib/action_view/helpers/asset_tag_helper.rb:813:in `rewrite_asset_path'
actionpack (3.0.0) lib/action_view/helpers/asset_tag_helper.rb:742:in `compute_public_path'
actionpack (3.0.0) lib/action_view/helpers/asset_tag_helper.rb:256:in `javascript_path'
actionpack (3.0.0) lib/action_view/helpers/asset_tag_helper.rb:822:in `javascript_src_tag'
actionpack (3.0.0) lib/action_view/helpers/asset_tag_helper.rb:362:in `block in javascript_include_tag'
actionpack (3.0.0) lib/action_view/helpers/asset_tag_helper.rb:362:in `collect'
actionpack (3.0.0) lib/action_view/helpers/asset_tag_helper.rb:362:in `javascript_include_tag'
I think I'm missing something when I create the ActionView::Base.
Any thoughts how to solve this? Also any suggestion on the overall design is welcome too :)
回答1:
Please for the love of all things MVC don't render view code from a model. :( There is always a better way to do it.
Example:
Model
class Widget < ActiveRecord::Base
def name
"weather_widget"
end
def settings
## something here
end
def data
## calculations here
end
end
Helper
module WidgetHelper
def render_widget(widget)
render(:partial => "widgets/_#{widget.name}", :locals => {:data => widget.data, :settings => widget.settings})
end
end
View
<% @template.widgets.each do |widget| %>
<%= render_widget(widget) %>
<% end %>
Obviously that may not be the exact solution for your situation, but is just a guide to show you something that can be done to keep your code clean. :)
回答2:
You may be able to get a call to a view helper to work from a model, but Rails will fight you every step of the way. Rails doesn't want you to do things like this.
Consider introducing a widget presenter class. It could live in app/helpers,
like
module WidgetHelper
class WidgetPresenter < Struct.new(:widget)
def show_me
render(:partial => "widgets/#{widget.class}/show", :locals => {:widget => widget })
end
end
def show_widgets(template)
@template.widgets.map do |widget|
WidgetPresenter.new(widget).show_me
end.join
end
end
That way you can use OO techniques to share presentation logic between widget presenters, and you can keep Rails happy (and future developers) by isolating your information from it's presentation.
来源:https://stackoverflow.com/questions/4127539/access-actionview-helpers-from-inside-the-model