render partials in actionmailer templates - ruby-on-rails

I am trying to use an existing partial in an actionmailer template, something like..
My merchant_offer.txt.html.erb
<%= render :partial => "offers/offer", :locals => {:offer => #offer} %>
Notifier.rb (my mailer class):
def merchant_offer(offer)
subject "New Offer from #{offer.merchant.name}"
from "xxx#gmail.com"
recipients xxx#
sent_on Time.now
body :offer => offer
end
The offer partial in in another view folder called offers
But it throws a missing tempalate error.
Is there a way to re-use existing view partial in in mailer tempalates?
Thanks

You should be able to render partial from mailer templates.
I believe the error is in your merchant_offer view. Try renaming 'merchant_offer.txt.html.erb' to 'merchant_offer.html.erb'

Related

send a rendered view as an email rails

I would like to send what is normally a view as the body of an email.
Whats the simplest way to do this?
eg: I have pages/reports.html.erb and I want to send the page that you'd see if you visited that path as the body of an email.
Is there some way to take the html that is rendered and assign it to the body variable of an email?
Additionally, I'd like to include the PDF version as an attachment. (I'm using wicked_pdf)
def reports
#email = params[:email]
#something here?
respond_to do |format|
format.html
format.pdf do
render :pdf => "usage_report"
end
end
end
note: I'm using rails 3.1 with mongoid 2 for the DB and sendgrid on heroku if that helps
EDIT: What I ended up doing:
replace #something here? with:
email_obj = {}
email_obj.to = params[:email]
email_obj.from = 'reports#company.com'
email_obj.subject = 'Report'
email_obj.body = render_to_string(:template => "pages/reports", :layout => false)
ReportsMailer.deliver_report(email_obj).deliver
and in the mailer class mailers/reports_mailer.rb
class ReportsMailer < ActionMailer::Base
default from: "from#example.com"
def deliver_report(email)
#email = email
mail( :to => #email.to,
:subject => #email.subject,
:from => #email.from)
end
end
and in reports_mailer/deliver_report.html.erb
<%= render :inline => #email.body %>
The rails mailer views are stored in a sub folder in views by the name of the mailer just like controller views. It is hard to say if this will work completely for you not knowing what the view/controller for that method looks like but what you could do is take the rendering for that page and turn it into a partial which you can then render in both the controller view and the mailer view as such:
given a partial: _my_partial.html.erb
within the views:
render "my_partial"
Using the partial you can even pass any variable that would be necessary for the view to be rendered to it with:
render "my_partial", :variable => variable
Update:
This may be of interest as well: Rendering a Rails view to string for email

render a partial inside a model rails 3.2

I understand advice available on the net to never, ever, ever render in a model, for this case I need do it
class Order
def canceled_order_message(order)
admin = User.find_or_create_by(email: "myemail")
message = Message.new(subject: I18n.t(".buyer_has_canceled_the_order"),
body: "#{render_to_string :partial => 'users/messages/templates/sent_by_admin/canceled_order', :locals => {:order => order}}")
message.send_message
message.save
end
end
I need render inside message body attribute:
render_to_string :partial => 'users/messages/templates/sent_by_admin/canceled_order
How can I do it?
Thank you!
Please check my answer here:
undefined method `fragment_for' for nil:NilClass on render partial with cache
I think that could be useful.
P.S. Horrible idea, please rethink your architecture.

ActionMailer pass local variables to the erb template

I know I could define instance variables e.g:
def user_register(username, email)
#username = username
#email = email
mail(:to => email, :subject => "Welcome!", :template_name => "reg_#{I18n.locale}")
end
But, is there a way to use local variables instead, just like passing :locals to partials?
As ronalchn pointed out, it's the render that has :locals, not the mail method. So, you need a direct access to the render method in order to pass the locals.
You can give a block to the mail and that way gain access to the render method, something like this:
mail(to: "your_mail#example.com", subject: "Test passing locals to view from mailer") do |format|
format.html {
render locals: { recipient_name: "John D." }
}
end
And now you should be able to use "Hello <%= recipient_name %>"
All options available in the mail method can be found at http://api.rubyonrails.org/classes/ActionMailer/Base.html#method-i-mail.
We know that render has a :locals option. However we can see that there is no :locals option available for mail. Therefore, no, there is no better way than to use instance variables (unless you want to use something hideous like globals or persistent database objects - don't do this).
Instance variables are what you are meant to use.
In Rails 5, you simply have to define instance variables using # in your method. You no longer have access to the locals property for this purpose.
class UserMailer < ApplicationMailer
def welcome_email(user_id:, to_email:, user_full_name:, token:)
# Mail template variables
#user = User.find_by(id: user_id)
#token = token
mail(:to => to_email,
:subject => MAILER_SUBJECTS_WELCOME,
:template_path => "user_mailer",
:template_name => "welcome_email")
end
end
Then you can just access them in your email template using <%= #user %> and <%= #token %>
You can actually use the locals option with mail, it's just a bit confusing and inconsistent as to how.
Once you use :locals you can then access these locals in the mail template using instance variables, e.g.
:locals => { :name => 'Jane' }
and then in the template
Dear <%= #name %>,

Rails3 rendering action from another controller

i want to render an action from another controller, but i get the error:
undefined method `formats' for nil:NilClass
<script>
$("#validate_company").live("keyup", function() {
$("#company_info").html("<%= escape_javascript(render(:controller => 'live_validation', :action => 'validate_client_company')) %>");
});
</script>
This is the Controller:
class LiveValidationsController < ApplicationController
def validate_client_company
if params[:first_name].length > 0
#client = Client.find_by_company(params[:company])
if #client.nil?
#message = "<img src='/images/accepted_48.png' alt='Valid Username'/>"
else
#message = "<img src='/images/cancel_48.png' alt='Invalid Username' /> Name taken"
end
else
#message = ""
end
render :partial => "message"
end
end
The partial _message is just
<%= #message %>
You seem to be mixing up stuff.
You have html inside your controller-method? That should be in your view. For each controller-method, normally a view with the same name is rendered, except if you explicitly call render from within your controller-method.
You do not write html in your controller. You write html in the view, and sometimes you have helpers to make your views more readable.
Secondly, in your first piece of code, which is some view-code, i hope. The view is prepared at server-side and then sent to the client. You can render another view, a partial, from a view. But this does not load data live.
How i would fix this. Inside your views where you want to dynamically render the validation:
<script>
$("#validate_company").live("keyup", function() {
$("#company_info").load("<%= url_for :controller => 'live_validations', :action => 'validate_client_company' %>");
});
</script>
Then inside your controller you write:
class LiveValidationsController < ApplicationController
def validate_client_company
if params[:first_name].length > 0
#client = Client.find_by_company(params[:company])
#error = #client.nil? ? :valid_username : :invalid_username
else
#error = nil
end
render :partial => "message", :layout => false
end
end
Inside your app/helper/live_validations_helper.rb you add a method
def get_validation_message(error)
if error == :invalid_username
image_tag('/images/cancel_48.png', :alt => 'Invalid Username') + "Name taken"
elsif error == :valid_username
image_tag('/images/accepted_48.png', :alt => 'Valid Username')
end
end
and inside your message view you write something like:
<%= get_validation_message(#error) %>
render :action => does not run the associated controller method.
It simply renders the template that Rails would, by default, have associated with the action. That is to say, if the action validate_client_company simply called render without passing any arguments, Rails would look for a template in a folder with the same name as the controller and with a name with the same name as the action. Calling render :action => simply looks for that same template and renders it.
Best guess is that Rails cannot find a template named validate_client_company. I expect that none exists, because the action validate_client_company renders a partial named message instead of rendering a template with the default name for that action.
In the action which renders the script, you need to set up the instance variables and then in the template for that action you need to use the following:
render :partial => 'live_validations/message'
It certainly makes sense to have mini MVC stacks within the larger MVC stack, so that you can run sub-actions within running larger actions. For this scenario, you may wish to look at Cells. However, you cannot do this with Rails by itself.
You probably want something like this:
<script>
$("#validate_company").live("keyup", function() {
$("#company_info").load("<%= url_for :controller => 'live_validation', :action => 'validate_client_company' %>");
});
</script>
But the rest of your code is kinda of messed up... Rendering a partial from inside a controller?
Got it to work!
<script>
$("#validate_company").live("keyup", function() {
$("#company_info").load("/live_validations/validate_client_company",{company:$('#validate_company').val()});
});
</script>

Ruby on Rails form_remote_tag missing template

I'm using form_remote_tag(:url => {:controller => "home", :action => "search"}, :update => "mydiv"). When I click submit on the form "mydiv" is populated with the error "Template is missing. Missing template home/search.erb in view path app/views". I've tried multiple render options in def search, but they all result in the same error.
It looks like the search method is trying to use it's default render even though I'm specifying what I want.
I've tried:
render 'index'
render :text => 'Return this from my method!'
Is my url incorrect? Is it not submitting back to my home controller's search method?
Try
render :action => 'index'
this will use "index.rhtml" or "index.html.erb".
I will try to explain why it said search.erb is not found, lets take create action for a some model, if there is some error in my create action they it will throw missing template create.html.erb file, since you have some error in your create action rails will try to render the create.html.erb in the page. Hope I explained it clearly.
In an ajax action you can't use redirect_to or render options directly.
try using this in your search action
render :update do |page|
page.replace_html "ur_div_id","partial"
end
The form_remote_tag needs prototype to function. Make sure you are including the :defaults for your javascript libraries namely prototype.
<%= javascript_include_tag :defaults %>

Resources