Rails,如何在模型中渲染视图/部分

AnA*_*ice 58 ruby ruby-on-rails ruby-on-rails-3

在我的模型中,我有:

after_create :push_create
Run Code Online (Sandbox Code Playgroud)

我push_create我需要渲染一个视图.我试着这样做:

  def push_event(event_type)
    X["XXXXX-#{Rails.env}"].trigger(event_type, 
      {
        :content => render( :partial =>"feeds/feed_item", :locals => { :feed_item => self })
      }
    )
  end
Run Code Online (Sandbox Code Playgroud)

这会激怒rails,因为它不像我在模型中渲染视图但我需要它.

错误:

NoMethodError (undefined method `render' for #<WallFeed:0x1039be070>):
Run Code Online (Sandbox Code Playgroud)

建议?我应该以某种方式在其他地方呈现它吗?或者我如何在模型中渲染以设置内容?谢谢

ste*_*com 65

妥善解决

好吧,"他们"是对的.你真的必须在控制器中进行渲染 - 但从模型中调用该控制器是公平的游戏!幸运的是,Rails 3中的AbstractController比我想象的要容易.我结束了制作一个简单的ActionPusher类,就像ActionMailer一样.也许有一天我会雄心勃勃,让它成为一个合适的宝石,但这应该成为我鞋子里的其他人的良好开端.

我从这个链接获得了最多的帮助:http://www.amberbit.com/blog/2011/12/27/render-views-and-partials-outside-controllers-in-rails-3/

在lib/action_pusher.rb中

class ActionPusher < AbstractController::Base
  include AbstractController::Rendering
  include AbstractController::Helpers
  include AbstractController::Translation
  include AbstractController::AssetPaths
  include Rails.application.routes.url_helpers
  helper ApplicationHelper
  self.view_paths = "app/views"

  class Pushable
    def initialize(channel, pushtext)
      @channel = channel
      @pushtext = pushtext
    end

    def push
      Pusher[@channel].trigger('rjs_push', @pushtext )
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

在app/pushers/users_pusher.rb中.我猜这个要求可能会更加全球化吗?

require 'action_pusher'

class UsersPusher < ActionPusher
  def initialize(user)
    @user = user
  end

  def channel
    @user.pusher_key
  end

  def add_notice(notice = nil)
    @notice = notice
    Pushable.new channel, render(template: 'users_pusher/add_notice')
  end
end
Run Code Online (Sandbox Code Playgroud)

现在在我的模型中,我可以这样做:

after_commit :push_add_notice

private

def push_add_notice
  UsersPusher.new(user).add_notice(self).push
end
Run Code Online (Sandbox Code Playgroud)

然后你会想要一个部分的,例如app/views/users_pusher/add_notice.js.haml,它可以很简单:

alert('#{@notice.body}')
Run Code Online (Sandbox Code Playgroud)

我猜你真的不需要在最后使用Pushable内部类和.push调用,但我想让它看起来像ActiveMailer.我的用户模型上也有一个pusher_key方法,为每个用户创建一个频道 - 但这是我第一天有像Pusher这样的东西,所以我不能肯定这是否是正确的策略.还有更多可以充实,但这足以让我开始.

祝好运!

(这是我的第一个答案草稿,留下来因为它可能对某人有帮助)

我已经得到了解决方案的大致轮廓.像这样,在你的模型中:

after_create :push_new_message

private

def render_anywhere(partial, assigns = {})
  view = ActionView::Base.new(ActionController::Base.view_paths, assigns)
  view.extend ApplicationHelper
  view.render(:partial => partial)
end  

def push_new_message
  pushstring = render_anywhere('notices/push_new_message', :message_text => self.body)
  Pusher[user.pusher_key].trigger!('new_message', pushstring)
end
Run Code Online (Sandbox Code Playgroud)

这肯定是有效的 - 模板正在渲染,并在客户端成功获得eval().我打算清理它,几乎可以肯定地将render_anywhere移动到某个更普遍的地方,并且可能尝试类似这样的东西

我可以看到推送将需要他们自己的模板,调用普遍可用的模板,我可能会尝试在一个地方收集它们.一个不错的小问题是我有时会在部分中使用controller_name,比如点亮一个菜单项,但我显然必须采取不同的策略.我猜我可能要做些什么来获得更多帮助,但我还没有到达那里.

成功!万岁!这应该回答你的问题,我的 - 如果以后看起来合适,我会添加更多细节.祝好运!!!!

一小时前原来的非回答是为了清楚

我没有答案,但这个及时的问题值得进一步澄清,我希望通过帮助问:) 来接近我的答案:)

我面临着同样的问题.为了更清楚地解释一下,Pusher异步将内容发送到连接的用户浏览器.典型的用例是向用户显示他们有来自其他用户的新消息.使用Pusher,您可以将消息发送到接收者的浏览器,以便他们在登录时立即得到通知.有关Pusher可以做的非常棒的演示,请查看http://wordsquared.com/

您可以发送您喜欢的任何数据,例如JSON哈希来解释您的喜好,但发送RJS非常方便,就像在客户端使用任何其他ajax调用和eval()一样.这样,您可以(例如)渲染菜单栏的模板,完整地更新它,或者仅显示给用户的新消息计数,使用所有相同的部分来保持其骨干.原则上,你可以从发送者的控制器渲染部分,但这也没有多大意义,甚至可能没有请求,它可能由cron作业触发,例如,或某些其他事件,如股价变动.发送者控制器不应该知道它 - 我喜欢让我的控制器处于饥饿状态;)

这可能听起来像是违反了MVC,但实际上并非如此 - 它确实应该通过ActionMailer之类的东西来解决,但是与应用程序的其余部分共享帮助程序和部分内容.我知道在我的应用程序中,我想在(或代替)ActionMailer调用的同时发送Pusher事件.我想基于来自用户A的事件为用户B呈现任意部分.

这些链接可能指向解决方案:

最后一个看起来最有希望,提供这个诱人的片段:

def render_anywhere(partial, assigns)
  view = ActionView::Base.new(Rails::Configuration.new.view_path, assigns)
  ActionView::Base.helper_modules.each { |helper| view.extend helper }
  view.extend ApplicationHelper
  view.render(:partial => partial)
end
Run Code Online (Sandbox Code Playgroud)

正如上面另一张海报提供的链接一样.

如果我得到了一些工作,我会报告回来

tl;博士:我也是!


Kev*_*vin 58

我这样做:

ApplicationController.new.render_to_string(partial: 'messages/any', locals: { variable: 'value' })
Run Code Online (Sandbox Code Playgroud)

  • 出于某种原因,我不能在这个解决方案的部分中使用`link_to`.对于nil:NilClass`,错误是`undefined method`host' (4认同)
  • `cache`也为nil抛出`undefined method host':NilClass` (2认同)
  • 这对我有用:`ActionController :: Base.new.render_to_string` - 没有ApplicationController错误. (2认同)

Ole*_*yev 35

Rails 5路

在Rails 5中,由于实现了控制器类方法,在控制器外部呈现变得非常简单: render

# render template
ApplicationController.render 'templates/name'
# render action
FooController.render :index
# render file
ApplicationController.render file: 'path'
# render inline
ApplicationController.render inline: 'erb content'
Run Code Online (Sandbox Code Playgroud)

render控制器外部调用时,可以通过assigns选项分配实例变量,并使用控制器中可用的任何其他选项:

ApplicationController.render(
  assigns: { article: Article.take },
  template: 'articles/show',
  layout: false
)
Run Code Online (Sandbox Code Playgroud)

可以通过默认选项来定制请求环境

ApplicationController.render inline: '<%= users_url %>'
# => 'http://default_host.com/users'

ApplicationController.renderer.defaults[:http_host] = 'custom_host.org'
# => "custom_host.org"

ApplicationController.render inline: '<%= users_url %>'
# => 'http://custom_host.org/users'
Run Code Online (Sandbox Code Playgroud)

或者通过初始化一个新的渲染器显式地

renderer = ApplicationController.renderer.new(
  http_host: 'custom_host.org',
  https: true
)
renderer.render inline: '<%= users_url %>'
# => 'https://custom_host.org/users'
Run Code Online (Sandbox Code Playgroud)

希望有所帮助.


hsg*_*ert 24

您可以直接使用ActionView并将部分渲染为字符串而无需控制器.我发现该模式对于创建封装一些javascript生成的模型很有用.

html = ActionView::Base.new(Rails.configuration.paths['app/views']).render(
  partial: 'test', 
  formats: [:html],
  handlers: [:erb],
  locals: { variable: 'value' }
)
Run Code Online (Sandbox Code Playgroud)

然后,只需将您_test.html.erb的视图文件夹放入其中即可试用!

  • 呵呵,我自己直接搜索了这个问题.谢谢你自己过去! (19认同)
  • 如果将actionview基本实例存储为"printer",则可以执行printer.extend(Rails.application.helpers) (3认同)
  • 简单易行,最佳答案 (2认同)

tro*_*skn 5

Rails 6.0.0 兼容答案,因为我在搜索解决方案时最终出现在此页面上:

lookup_context = ActionView::LookupContext.new(Rails.configuration.paths["app/views"])
renderer = ActionView::Base.new(lookup_context)
renderer.extend(Rails.application.helpers)
renderer.render \
  template: "foo/bar",
  formats: [:html],
  handlers: [:erb],
  locals: { user: User.new }
Run Code Online (Sandbox Code Playgroud)