Reputation: 5495
I am interested making a feature similar to that of SO's, where an user who has asked a question could select an answer from a list of responses, but am stuck on how to handle the click events from the controller perspective.
Currently, I have the models Question
, which has_many
answers
. Additionally, I have made an Selection
model, which is meant to symbolize the answer selection, where each Question
only could has_one
selected answer.
I render each questions's answers along with the question in the question's show
action. In my _answer
partial, I want to place the following logic: "if the user who has asked the question is logged on, he could see links to 'unselect' the answer if the related selection
has that answer's answer_id
already stored (in other words, marking that this answer was previously selected), pr otherwise he could 'select' the answer which stores the answer's answer_id
in selection
. (I should mention that I adapted most of the code below from Rails tutorial here, where I (unfortunately) don't quite have a solid understanding how to use POST
to match or assign attributes to an instance variable )
<% if current_user?(answer.question.user) %>
<% if current_user.selections.where(:answer_id => answer.id) == answer.id %>
<%= link_to "unselect", selections_path(:selection => {:answer_id => nil}), :method => :post %>
<% else %>
<%= link_to "select", selections_path(:selection => {:answer_id => answer.id}), :method => :post %>
<% end %>
<% end %>
My roadblock is in the controller, where I'm stuck on how to use the create
method to assign the @selection
variable to a new answer_id
if the user selects a new answer via the "select" link. Any help, or any guides on how to write on-click events would be much appreciated. Thanks!
Upvotes: 1
Views: 923
Reputation: 12564
There's many different ways to do this, but i tend to think that you have a design problem here, so here's how i would do it.
I assume from what you said that your associations look like this :
__________ ________
| Question |1 *| Answer |
| |<-------------------------------| |
| |1 * ___________ * 1| |
| |<------| Selection |----------->|________|
| | | |
| | | |* 1 ________
| | |___________|----------->| User |
| |* 1| |
|__________|------------------------------->|________|
This visual representation clearly shows the problem : your Selection
model is redundant, as it represents the fact that a question belongs_to
a User
(which we already know) ; it would not be redundant if there could be multiple selections for each couple of Question / User
, but in practice we want that selection to be unique for each couple...
A belongs_to
relation between Question
and Answer
would achieve the same thing that your Selection
model does, and in fact it would do it better because you won't need all the logic to find the right selection, and to ensure it is unique / that the user is the rightfull owner of the question, etc.
So here's what i'd do :
In the Question
model
has_many :answers, inverse_of: :question
belongs_to :accepted_answer, class_name: :answer, foreign_key: :accepted_answer_id
In the Answer
model
belongs_to :question, inverse_of: :answers
def accepted?
return false if new_record?
question.try( :accepted_answer_id ) == id
# an alternative is to use question.try( :accepted_answer ) == self
end
In your routes
resources :questions do
member do
# we use put because these are update actions
put :accept_answer
put :clear_accepted_answer
end
end
In your QuestionsController
respond_to :js, only: [:accept_answer, :clear_accepted_answer]
def accept_answer
@question = Question.find( params[:id] )
# ...cue some logic to ensure current_user
# has required rights to update the question
if @question.update_attributes( accepted_answer_id: params[:answer_id] )
# ...render the js that updates your view (for example,
# find and replace calling link with an "unselect" one )
else
# .. an error has occurred, render an "unprocessable entity" status
end
end
def clear_accepted_answer
@question = Question.find( params[:id] )
# ...cue some logic to ensure current_user
# has required rights to update the question
if @question.update_attributes( accepted_answer_id: nil )
# ...render the js that updates your view (for example,
# find and replace calling link with a "select" one )
else
# .. an error has occurred, render an "unprocessable entity" status
end
end
in your view
<% if current_user?(answer.question.user) %>
<% if answer.accepted? %>
<%= link_to "unselect",
clear_accepted_answer_question_path( answer.question ),
method: :put,
remote: true %>
<% else %>
<%= link_to "select",
accept_answer_question_path(
answer.question,
answer_id: answer.id
),
method: :put,
remote: true %>
<% end %>
<% end %>
Upvotes: 1