Rails, How to Render a View/Partial in a Model

Rails, How to render a view/partial in a model

proper solution

Well, "they" are right. You really have to do the rendering in a controller -
but it's fair game to call that controller from a model! Fortunately, AbstractController
in Rails 3 makes it easier than I thought. I wound up making a simple
ActionPusher class, working just like ActionMailer. Perhaps I'll get ambitious and
make this a proper gem someday, but this should serve as a good start for anyone else in my shoes.

I got the most help from this link: http://www.amberbit.com/blog/2011/12/27/render-views-and-partials-outside-controllers-in-rails-3/

in 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

in app/pushers/users_pusher.rb. I guess the require could go somewhere more global?

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

Now in my model, I can just do this:

after_commit :push_add_notice

private

def push_add_notice
UsersPusher.new(user).add_notice(self).push
end

and then you'll want a partial, e.g. app/views/users_pusher/add_notice.js.haml, which could be as simple as:

alert('#{@notice.body}')

I guess you don't really need to do it with Pushable inner class and the .push
call at the end, but I wanted to make it look like ActiveMailer. I also have a
pusher_key method on my user model, to make a channel for each user - but this
is my first day with anything like Pusher, so I can't say for sure if that's the right
strategy. There's more to be fleshed out, but this is enough for me to get started.

Good luck!

(this was my first draft answer, leaving it in because it might help someone)

I've got the general outline of a solution working. Like this, in your model:

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

that is definitely working - the template is rendering, and gets eval()'ed on the client side successfully. I'm planning to clean it up, almost certainly move render_anywhere somewhere more general, and probably try something like this

I can see that pushes will need their own templates, calling the generally available ones, and I may try to collect them all in one place. One nice little problem is that I sometimes use controller_name in my partials, like to light up a menu item, but I'll obviously have to take a different tactic there. I'm guessing I might have to do something to get more helpers available, but I haven't gotten there yet.

Success! Hooray! This should answer your question, and mine - I'll add more detail if it seems appropriate later. Good luck!!!!

original non-answer from an hour ago left for clarity

I don't have an answer, but this timely question deserves more clarification, and I'm hoping to get closer to my answer by helping ask :)

I'm facing the same problem. To explain a little more clearly, Pusher asynchronously sends content to a connected user browser. A typical use case would be a showing the user they have a new message from another user. With Pusher, you can push a message to the receiver's browser, so they get an immediate notification if they are logged in. For a really great demo of what Pusher can do, check out http://wordsquared.com/

You can send any data you like, such as a JSON hash to interpret how you like it, but it would be very convenient to send RJS, just like with any other ajax call and eval() it on the client side. That way, you could (for example) render the template for your menu bar, updating it in its entirety, or just the new message count displayed to the user, using all the same partials to keep it bone-DRY. In principle, you could render the partial from the sender's controller, but that doesn't make much sense either, and there might not even be a request, it could be triggered by a cron job, for example, or some other event, like a stock price change. The sender controller just should not have to know about it - I like to keep my controllers on a starvation diet ;)

It might sound like a violation of MVC, but it's really not - and it really should be solved with something like ActionMailer, but sharing helpers and partials with the rest of the app. I know in my app, I'd like to send a Pusher event at the same time as (or instead of) an ActionMailer call. I want to render an arbitrary partial for user B based on an event from user A.

These links may point the way towards a solution:

  • http://blog.choonkeat.com/weblog/2006/08/rails-calling-r.html
  • How to render a Partial from a Model in Rails 2.3.5
  • http://mattwindsurfs.wordpress.com/2008/06/19/rails-render-in-a-model/
  • http://davetroy.blogspot.com/2008/02/actsasrenderer-brings-output-to-models.html
  • https://github.com/asapnet/acts_as_renderer
  • http://ethilien.net/archives/render-rails-templates-anywhere-even-in-a-model/

The last one looks the most promising, offering up this tantalizing snippet:

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

As does this link provided by another poster above.

I'll report back if I get something working

tl;dr: me too!

How to render partial view?

This question has already been answered, but when the partial is in the same directory as your current view, you can just render the partial with no underscore. But if you want to extract a partial from another controller you will need to take it one step back and use a more complete path, like "users/partial".

Rails form partial rendering of one model in the view of another model

May be you have to have the partial inside user views or send parameter as local hash if you are trying with goals/form

Render partial from another model

Very strange — if you write <%= render :partial => 'room_form' %> than rails will assume that it is app/views/calculator/_room_form.html.erb, but in case of <%= render :partial => 'rooms/room_form' %> it will assume that it is app/views/rooms/_room_form.html.erb

Watch your log — there you will see which partials were rendered

Rails: Render a View (not a partial) From Within a View

Rendering a non-partial view inside another view isn't exactly the Rails Way™. Your current solution is probably better, and not an uncommon approach. Rename it _body, or something else appropriate, if you feel weird about the the partial name being the same as the action.

However if your view can be shared, as it seems like it could in this case, you could just make it a layout.

This is facilitated by the fact that, somewhat against the principle of least surprise, Rails will render an html template for a js action if no js template exists. This means that you could remove both the js template, and the partial, and just create a layout entitled, for example, fadein.js.erb:

# yourviews/show.html.erb
<div>Content!</div>

# layouts/fadein.js.erb
$("#main").fadeIn("<%= escape_javascript(yield) %>");

# YourController.rb
def show
# ...
respond_to do |wants|
wants.html
wants.js { render :layout => "fadein" }
end
end

Rails: Render partial with selected instance of model

So I fixed this using Muhammad's suggestion in the comments by updating def details in the albums_controller.rb to include @album = Album.find(params[:id]) :

  def details
@album = Album.find(params[:id])
respond_to do |format|
format.js { render :layout => false }
end
end

How to render a Edit form partial in the Show page of another model in Rails

This is what I ended up doing and it works.

In my show.html.erb for my "Trip".

show.html.erb

<div id="activity_form">
<h2>Activities</h2>
</div>

link to "Edit' an Activity. Notice the :remote => true which tells the Rails controller that this will be an AJAX request so to render edit.js.erb

<%= link_to activity.location[0, 20], edit_day_activity_path(day, activity), :class=>"btn btn-info fixedwidthbtn", method: :get, :remote => true 

_form.html.erb This form partial is under the Activities View directory (../views/activities/_form.html.erb).

<%= form_for([@day, @activity], :remote => true) do |f| %>
<fieldset>
<%= f.label :title, "Activity" %>
<%= f.text_field :title, :rows => 1 %>
</fieldset>
<div class="actions">
<%= f.submit %>
</div>
</form>
<%= link_to 'Delete', [@day, @activity], method: :delete, data: { confirm: 'Are you sure?' } %>
<% end %>

edit.js.erb This is a file under the Activities view directory (../views/activities/edit.js.erb). Says to grab the DOM element with ID of "activity_form" and render the partial "form"

$("#activity_form").html("<%= escape_javascript(render(:partial => "form"))%>");

update.js.erb I included this javascript after hitting update on the Edit form to render an updated partial of the Activities List. So that I don't have to reload the page to see an update.

$("#activities_list").html("<%= escape_javascript( render(:partial => "/trips/activities") ) %>");

routes.rb This is how I nest my routes. Only doing it 1 level following best practices.

resources :trips do 
resources :days
end

resources :days do
resources :activities
end

Nicest way to render different partials on a rails object?

After a little digging, it looks like this is the best way

<%= render video, list: true %>

_video.html.erb

<% if local_assigns[:list] %>
#list view
<% elsif local_assigns[:select] %>
#select view
<% else %>
#default view
<% end %>

This actually feels pretty nice. Do I'll mark it as correct in a few days if no one can suggest something that might be better?

Thanks



Related Topics



Leave a reply



Submit