问题
I am using devise, and scaffolded Textbook. I like to implement my strategy. When buyer clicks an @textbook.title -> Buyer can send an email to the @textbook's seller. I have every model has column for 'user_email' So, Whenever a seller create a @textbook, automatically current_user.email is saved into @textbook.user_email.
I just don't know how to grab the seller's user_email and send email.
I have following
Textbook model:
class Textbook < ActiveRecord::Base
belongs_to :user
validates :title, :presence => true
validates :subject, :presence => true
validates :price, :presence => true
validates :offer, :presence => false
validates :created_at, :presence => false
validates :user_email, :presence => true
validates :description, :presence => true
end
I am not sure this model syntax is right for subject and current_user.email Contact model:
class Contact < MailForm::Base
attribute :name, :validate => true
attribute :current_user.email, :validate => /\A([\w\.%\+\-]+)@([\w\-]+\.)+([\w]{2,})\z/i
attribute :message, :validate => true
def headers
{
:subject => "I like to buy #{@textbook.id.title}",
:to => "@textbook.id.user_email",
:from => %(<#{email}>)
}
end
end
My detail question is this: If a user clicks 'contact' when buyer was inside of a specific textbook ant it links the user to textbook#show. Below is the form when the user clicked 'contact'.
How can I make sure this below view access the correct textbook.id or textbook.title?
<h1> Contact to the Seller </h1>
<div>
<%=form_for @contact do |f|%>
<h3>Send email for: <%=@textbook.id.title%> </h3>
<%= f.label :message %><br>
<%= f.text_area :message, as: :text %><br>
<%=f.submit 'Send message', class: 'button' %>
<%end%>
</div>
Specially, I don't know how to handle grab attributes that is from different model inside different views.
Thank you in advance!
-!#-!#-!#-!#-!#-!#-!#-!#-!#-!#-!#-!#-!#-!#-!#-!#-!
Update 1:
I have contact controller like this:
class ContactsController < ApplicationController
def new
@contact = Contact.new
end
def create
@contact = Contact.new(params[:contact])
#@contact.request = request
if @contact.deliver
flash[:success] = "Email sent."
else
flash[:alert] = "Cannot send an email."
render :new
end
end
end
I just edited my 'class Contact < MailForm::Base'
class Contact < MailForm::Base
attribute :name, :validate => true
attribute :email, :validate => /\A([\w\.%\+\-]+)@([\w\-]+\.)+([\w]{2,})\z/i
attribute :message, :validate => true
def headers
{
:subject => "I like to buy #{textbook.title}",
:to => "@textbook.user_email",
:from => %(<#{current_user.email}>)
}
end
end
But I got error:
NameError in ContactsController#create
undefined local variable or method `textbook' for #<Contact:0x007fbac641be40>
Extracted source (around line #8):
def headers
{
:subject => "I like to buy #{textbook.title}",
:to => "@textbook.user_email",
:from => %(<#{current_user.email}>)
}
@zeiv I fixed textbook.title -> @textbook.title I get error an another error.
NoMethodError in ContactsController#create
undefined method `title' for nil:NilClass
def headers
{
:subject => "I like to buy #{@textbook.title}",
:to => "@textbook.user_email",
:from => %(<#{current_user.email}>)
}
I have views/textbooks.html.erb:
<div class="container">
<p>
<h3><strong>Title:</strong>
<%= @textbook.title %></h3>
</p>
<p>
<strong>Subject:</strong>
<%= @textbook.subject %>
</p>
<p>
<strong>Price:</strong>
$<%= @textbook.price %>
</p>
<p>
<strong>Accept Offer:</strong>
<%if @textbook.offer == true%>
<%='Yes'%>
<%else%>
<%='No'%>
<%end%>
</p>
<p>
<strong>Description:</strong>
<pre><%= @textbook.description %></pre>
</p>
<p>
<strong>Image:</strong>
<pre><%= image_tag @textbook.thumbnail.url(:medium) %></pre>
</p>
<p>
<strong>Created on:</strong>
<%= @textbook.created_at.strftime("%d %b. %Y") %>
</p>
<p>
<%= link_to 'Contact', new_contact_path %>
</p>
<%if @textbook.user_email == current_user.email %>
<%= link_to 'Edit', edit_textbook_path(@textbook) %> |
<%= link_to 'Back to list', textbooks_path %>
<%else %>
<%= link_to 'Back to list', textbooks_path %>
<%end%>
And I have textbooks_controller:
class TextbooksController < ApplicationController
before_action :set_textbook, only: [:show, :edit, :update, :destroy]
#before_action :set_textbook, only: [:show]
#before_action :authorize_resource!, except: [:new, :index, :show]
# GET /textbooks
# GET /textbooks.json
def index
#@textbooks = Textbook.all
@textbooks = Textbook.all.order(created_at: :desc).paginate(page: params[:page], per_page: 10)
#@textbooks = Textbook.paginate(:page => params[:page], :per_page => 10)
end
# GET /textbooks/1
# GET /textbooks/1.json
def show
end
I have config/routes:
resources :textbooks
resources :contacts, only: [:new, :create]
devise_for :users
When I rake routes at this moment 4/17 5:05pm
new_textbook GET /textbooks/new(.:format) textbooks#new
edit_textbook GET /textbooks/:id/edit(.:format) textbooks#edit
textbook GET /textbooks/:id(.:format) textbooks#show
PATCH /textbooks/:id(.:format) textbooks#update
PUT /textbooks/:id(.:format) textbooks#update
DELETE /textbooks/:id(.:format) textbooks#destroy
contacts POST /contacts(.:format) contacts#create
new_contact GET /contacts/new(.:format) contacts#new
UPDATE 2 -!#-!#-!#-!#-!#-!#-!#-!#-!#-!#-!#-!#-!#-!#-!#-!#-!
below is after 04/17/2016 11:00pm @zeiv I did what you told me. But still I get error when I click 'contact' button in views/textbooks/show.html.erb
#views/textbooks/show.html.erb
<p>
<%= link_to 'Contact', new_contact_textbook_path %>
</p>
my routes.rb has now:
Rails.application.routes.draw do
resources :textbooks do
member do
get 'contact', to: 'textbooks#new_contact', as: 'new_contact'
post 'contact', to: 'textbooks#send_contact', as: 'send_contact'
end
end
rake routes has now:
Prefix Verb URI Pattern Controller#Action
new_contact_textbook GET /textbooks/:id/contact(.:format) textbooks#new_contact
send_contact_textbook POST /textbooks/:id/contact(.:format) textbooks#send_contact
textbooks GET /textbooks(.:format) textbooks#index
POST /textbooks(.:format) textbooks#create
new_textbook GET /textbooks/new(.:format) textbooks#new
edit_textbook GET /textbooks/:id/edit(.:format) textbooks#edit
textbook GET /textbooks/:id(.:format) textbooks#show
PATCH /textbooks/:id(.:format) textbooks#update
PUT /textbooks/:id(.:format) textbooks#update
DELETE /textbooks/:id(.:format) textbooks#destroy
The error I get is this:
NoMethodError in Textbooks#new_contact
undefined method `id' for nil:NilClass
Extracted source (around line #4):
<div>
Texbook id is: <%= @textbook.id %>
</div>
I am running heroku local the error shows:
10:56:13 PM web.1 | Rendered textbooks/new_contact.html.erb within layouts/application (2.0ms)
10:56:13 PM web.1 | Completed 500 Internal Server Error in 7ms (ActiveRecord: 0.1ms)
10:56:13 PM web.1 | ActionView::Template::Error (undefined method `id' for nil:NilClass):
10:56:13 PM web.1 | 1: <h1> contact seller! - working? </h1>
10:56:13 PM web.1 | 2:
10:56:13 PM web.1 | 3: <div>
10:56:13 PM web.1 | 4: Texbook id is: <%= @textbook.id %>
10:56:13 PM web.1 | 5: </div>
回答1:
Basically what you need to do is to write your mailers and controllers in such a way that all the information you want is passed to the mailer. So if you want an instance of your Textbook model to be passed to the mailer, you will need to do so from the controller in which you send your email. You might event want to nest your contact controller routes within your textbook routes to help you. Alternatively, rather than having an entire controller for Contact, just have a contact action within your textbook controller.
# route.rb
...
resources :textbooks do
member do
get "contact", to: "textbooks#new_contact", as: "new_contact"
post "contact", to: "textbooks#send_contact", as: "send_contact"
end
end
That will give you routes like /textbook/24/contact
. member do
means that the routes are for individual instances of your model rather than the whole collection, so you will need to specify which textbook you are referring to when calling their helpers: new_contact_textbook_path(@textbook.id)
.
So in your Textbook controller, you would do this:
# textbooks_controller.rb
before_action :set_textbook, only: [:show, :edit, :update, :destroy, :new_contact, :send_contact]
...
def new_contact
# We are NOT doing Contact.new here
# Only put logic here that you need to display the form
end
def send_contact
message = params[:message]
if Contact.send_contact(@textbook, current_user, message).deliver
flash[:success] = "Email sent."
redirect_to @textbook
else
flash[:alert] = "There was a problem sending the email."
render :new_contact
end
end
Then put your new_contact.html.erb
file in with your other Textbook views.
<h1> Contact to the Seller </h1>
<div>
<%= form_tag send_contact_textbook_path(@textbook.id) do %>
<h3>Send email for: <%=@textbook.title%> </h3>
<%= label_tag :message, "Type your message:" %><br>
<%= text_area_tag :message %><br>
<%= submit_tag 'Send message', class: 'button' %>
<%end%>
</div>
Notice that I'm using form_tag
instead of form_for
because we don't have a Contact object to pass it. (That is, Contact isn't a model. It's a mailer.)
Your mailer would then look something like this:
class Contact < ApplicationMailer
def send_contact(textbook, current_user, message)
@textbook = textbook
@current_user = current_user
@message = message
mail(
from: "#{@current_user.name} <#{@current_user.email}>",
to: @textbook.user.email,
subject: "I would like to buy #{@textbook.title}",
reply_to: @current_user.email,
)
end
end
And finally, put the template/view for you mailer in /app/views/contact/send_contact.html.erb
:
<!DOCTYPE html>
<html>
<head>
<meta content='text/html; charset=UTF-8' http-equiv='Content-Type' />
</head>
<body>
<h1><%= @current_user.name %> is wondering about <%= @textbook.title %>:</h1>
<p><%= @message %></p>
</body>
</html>
And that should do it! Although you may have to adjust some things to suit your needs. Also see these links for more examples:
Contact Form Mailer in Rails 4
https://sendgrid.com/docs/Integrate/Frameworks/rubyonrails.html
来源:https://stackoverflow.com/questions/36672859/how-can-i-grab-or-access-different-model-attribute-when-i-send-email-using-sendg