Apologies if this has already been answered but I can't find anything that can help me. I am a newbie with Rails so please be gentle :D
I have been pulling my hair out trying to get nested forms working, I am sure I got nested forms working using Rails 3 and the railscasts demo last year, but Rails 4 is beating me.
Looking at the log, the query is being run to pull the data for the associated table, but nothing is rendered in the form.
I have read many web sites, but none have helped so far and I don't know where to start. The latest article I have followed is this http://www.createdbypete.com/articles/working-with-nested-forms-and-a-many-to-many-association-in-rails-4/
Still nothing being rendered in the view.
Where do I start debugging this, maybe my Rails install is broken?? But I am probably missing something crucial.
Thanks,
Royce
Edit - I have added some of the controllers and the view in question
surveys_controller.rb
class SurveysController < ApplicationController
before_action :set_survey, only: [:show, :edit, :update, :destroy, :answers]
# GET /surveys
# GET /surveys.json
def index
#surveys = Survey.all
end
# GET /surveys/1
# GET /surveys/1.json
def show
end
# GET /surveys/new
def new
#survey = Survey.new
end
# GET /surveys/1/edit
def edit
end
# POST /surveys
# POST /surveys.json
def create
#survey = Survey.new(survey_params)
respond_to do |format|
if #survey.save
format.html { redirect_to #survey, notice: 'Survey was successfully created.' }
format.json { render :show, status: :created, location: #survey }
else
format.html { render :new }
format.json { render json: #survey.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /surveys/1
# PATCH/PUT /surveys/1.json
def update
respond_to do |format|
if #survey.update(survey_params)
format.html { redirect_to #survey, notice: 'Survey was successfully updated.' }
format.json { render :show, status: :ok, location: #survey }
else
format.html { render :edit }
format.json { render json: #survey.errors, status: :unprocessable_entity }
end
end
end
# DELETE /surveys/1
# DELETE /surveys/1.json
def destroy
#survey.destroy
respond_to do |format|
format.html { redirect_to surveys_url, notice: 'Survey was successfully destroyed.' }
format.json { head :no_content }
end
end
def answers
#participants = Participant.all
#questions = #survey.questions
end
private
# Use callbacks to share common setup or constraints between actions.
def set_survey
#survey = Survey.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def survey_params
params.require(:survey).permit(:name,
:questions_attributes => [:id, :content,
:answers_attributes => [:id, :content, :participant_id]
])
end
end
participents_controller.rb
class ParticipantsController < ApplicationController
before_action :set_participant, only: [:show, :edit, :update, :destroy]
# GET /participants
# GET /participants.json
def index
#participants = Participant.all
end
# GET /participants/1
# GET /participants/1.json
def show
end
# GET /participants/new
def new
#participant = Participant.new
end
# GET /participants/1/edit
def edit
end
# POST /participants
# POST /participants.json
def create
#participant = Participant.new(participant_params)
respond_to do |format|
if #participant.save
format.html { redirect_to #participant, notice: 'Participant was successfully created.' }
format.json { render :show, status: :created, location: #participant }
else
format.html { render :new }
format.json { render json: #participant.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /participants/1
# PATCH/PUT /participants/1.json
def update
respond_to do |format|
if #participant.update(participant_params)
format.html { redirect_to #participant, notice: 'Participant was successfully updated.' }
format.json { render :show, status: :ok, location: #participant }
else
format.html { render :edit }
format.json { render json: #participant.errors, status: :unprocessable_entity }
end
end
end
# DELETE /participants/1
# DELETE /participants/1.json
def destroy
#participant.destroy
respond_to do |format|
format.html { redirect_to participants_url, notice: 'Participant was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_participant
#participant = Participant.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def participant_params
params.require(:participant).permit(:name)
end
end
application_controller.rb
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
end
answers.html.erb
<h1><%= #survey.name %> Answers</h1>
<%= form_for(#survey) do |f| %>
<% #participants.each do |participant| -%>
<h3><%= participant.name %></h3>
<table>
<thead>
<tr>
<td>Questions</td>
<td>Answer</td>
</tr>
</thead>
<tbody>
<% #questions.each do |question| -%>
<tr>
<td><%= question.content %></td>
<td>
<%= f.fields_for :questions, question do |q| -%>
<%= q.fields_for :answers, question.answers.find_or_initialize_by(participant: participant) do |a| -%>
<%= a.text_area :content %>
<%= a.hidden_field :participant_id, participant.id %>
<% end -%>
<% end -%>
</td>
</tr>
<% end -%>
</tbody>
</table>
<% end -%>
<div class="actions">
<%= f.submit %>
</div>
<% end -%>
As you're new with Rails, let me explain how nested forms work for you!
--
Nested
Nested forms are not actually nested at all - they are associative forms.
You must remember that Rails (by virtue of being built on Ruby) is an object orientated framework. OOP (object orientated programming) is not just a buzzword - it's a fundamental core construction for your application & how it hands input / execution.
The problem many people have is they don't realize the true nature of Rails, and consequently become confused about how its many features work. If you appreciate that everything you do in Rails should be constructed around objects, life gets much simpler!
--
Form
With this in mind, you can begin to appreciate the role of objects throughout Rails, to the degree that you need to build / invoke objects for every element of your Rails application, including your form:
#app/models/survey.rb
Class Survey < ActiveRecord::Base
has_many :questions
accepts_nested_attributes_for :questions
end
#app/controllers/surveys_controller.rb
Class SurveysController < ApplicationController
def new
#survey = Survey.new
#survey.questions.build #-> very important
end
end
#app/views/surveys/new.html.erb
<%= form_for #survey do |f| %>
...
<%= f.fields_for :questions do |q| %>
<%= q.text_field :title %>
<% end %>
<%= f.submit %>
<% end %>
This should create a form which allows you to pass associative data through to your child model. There are several important elements to consider:
You need to include accepts_nested_attributes_for in your "parent" model
You need to build your associative objects
You need to populate your form with the relative objects
By following this simple pattern, you'll be able to populate the nested form that you wish to show in the view
Try to use the following code:
<%= f.fields_for :questions do |q| -%>
<%= q.fields_for :answers, q.object.answers.find_or_initialize_by(participant: f.object.participant) do |a| -%>
<%= a.text_area :content %>
<%= a.hidden_field :participant_id, participant.id %>
<% end -%>
<% end -%>
and make sure that you render to answers.html.erb, you have accepts_nested_attributes_for :questions in survey.rb file, and accepts_nested_attributes_for :answers in question.rb file
Have you got accepts_nested_attributes_for :question in your survey model? And the same for the answer model?
Related
I’m working on a project that will utilize nested forms that incorporates Rails 6.1.3 and Bootstrap 5.1.2.
I’m having difficulty getting the nested form feature to work.
Project GitHub: cjmccormick88/testapp-nested
There are two models: client and shipping address.
Client accepts nested attributes for the shipping address model. A client can have many shipping addresses.
Authentication is being handled by Devise. Bootstrap is used for styling. Audited is used for audit trail.
Clients Controller
class ClientsController < ApplicationController
before_action :set_client, only: %i[ show edit update destroy ]
# GET /clients or /clients.json
def index
#clients = Client.all
end
# GET /clients/1 or /clients/1.json
def show
end
# GET /clients/new
def new
#client = Client.new
#client.shipping_addresses.build
end
# GET /clients/1/edit
def edit
end
# POST /clients or /clients.json
def create
#client = Client.new(client_params)
#client.shipping_addresses.build(client_params[:shipping_addresses_attributes])
#client.save
respond_to do |format|
if #client.save
format.html { redirect_to #client, notice: "Client was successfully created." }
format.json { render :show, status: :created, location: #client }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: #client.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /clients/1 or /clients/1.json
def update
respond_to do |format|
if #client.update(client_params)
format.html { redirect_to #client, notice: "Client was successfully updated." }
format.json { render :show, status: :ok, location: #client }
else
format.html { render :edit, status: :unprocessable_entity }
format.json { render json: #client.errors, status: :unprocessable_entity }
end
end
end
# DELETE /clients/1 or /clients/1.json
def destroy
#client.destroy
respond_to do |format|
format.html { redirect_to clients_url, notice: "Client was successfully destroyed." }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_client
#client = Client.find(params[:id])
end
# Only allow a list of trusted parameters through.
def client_params
params.require(:client).permit(:client_name, shipping_addresses_attributes: [:id, :address_line1, :address_line2, :city, :state, :country])
end
end
Client Model
class Client < ApplicationRecord
audited
has_many :shipping_addresses, :inverse_of => :client, autosave: true
accepts_nested_attributes_for :shipping_addresses
end
Shipping Address Model
class ShippingAddress < ApplicationRecord
audited
belongs_to :client
validates :client, :presence => true
end
Client View Form Partial
<%= form_with(model: client) do |form| %>
<% if client.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(client.errors.count, "error") %> prohibited this client from being saved:</h2>
<ul>
<% client.errors.each do |error| %>
<li><%= error.full_message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= form.label :client_name %>
<%= form.text_field :client_name %>
</div>
<%= form.fields_for #client.shipping_addresses.build do |s| %>
<div class="field">
<%= s.label :address_line1, 'Address Line 1' %>
<%= s.text_field :address_line1 %>
</div>
<div class="field">
<%= s.label :address_line2, 'Address Line 2' %>
<%= s.text_field :address_line2 %>
</div>
<div class="field">
<%= s.label :city, 'City' %>
<%= s.text_field :city %>
</div>
<div class="field">
<%= s.label :state, 'State' %>
<%= s.text_field :state %>
</div>
<div class="field">
<%= s.label :country, 'Country' %>
<%= s.text_field :country %>
</div>
<% end %>
<div class="actions">
<%= form.submit %>
</div>
<% end %>
In addition, there is a controller for shipping addresses if someone chooses to view those pages on their own.
Shipping Addresses Controller
class ShippingAddressesController < ApplicationController
before_action :set_shipping_address, only: %i[ show edit update destroy ]
# GET /shipping_addresses or /shipping_addresses.json
def index
#shipping_addresses = ShippingAddress.all
end
# GET /shipping_addresses/1 or /shipping_addresses/1.json
def show
end
# GET /shipping_addresses/new
def new
#shipping_address = ShippingAddress.new
end
# GET /shipping_addresses/1/edit
def edit
end
# POST /shipping_addresses or /shipping_addresses.json
def create
#shipping_address = ShippingAddress.new(shipping_address_params)
respond_to do |format|
if #shipping_address.save
format.html { redirect_to #shipping_address, notice: "Shipping address was successfully created." }
format.json { render :show, status: :created, location: #shipping_address }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: #shipping_address.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /shipping_addresses/1 or /shipping_addresses/1.json
def update
respond_to do |format|
if #shipping_address.update(shipping_address_params)
format.html { redirect_to #shipping_address, notice: "Shipping address was successfully updated." }
format.json { render :show, status: :ok, location: #shipping_address }
else
format.html { render :edit, status: :unprocessable_entity }
format.json { render json: #shipping_address.errors, status: :unprocessable_entity }
end
end
end
# DELETE /shipping_addresses/1 or /shipping_addresses/1.json
def destroy
#shipping_address.destroy
respond_to do |format|
format.html { redirect_to shipping_addresses_url, notice: "Shipping address was successfully destroyed." }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_shipping_address
#shipping_address = ShippingAddress.find(params[:id])
end
# Only allow a list of trusted parameters through.
def shipping_address_params
params.require(:shipping_address).permit(:address_line1, :address_line2, :city, :state, :country, :client_id)
end
end
Behavior of the Application
The application accepts the entry of the client form and it manipulates the model for the shipping address; however, the only entry in the table on each row is the client_id value for the client foreign key. It is not committing the other components of the hash into the table.
Screen Display
Things Tried
I've tried the application posted on GitHub at stevepolitodesign/rails-nested-form-app.
I've tried a suggestion made from a similar post on rails forum: difficulties-with-nested-form-implementation-rails-6-1-3/78776/3.
I've gone through as much documentation as I can track down on the standard rails guides for nested forms.
Results from all of these did not yield good results. Item # 1 was a decent app in terms of the pathway it takes; however, when you are using bootstrap it does not seem to work. It could be that the code there has to be modified some to allow that functionality. So far, any posts made for a request regarding bootstrap with that design have not yielded fruit.
Scope
I'm looking to understand the problem that is happening and/or find a better way to accomplish this function that cooperates well with Bootstrap use.
I'm developing an app in which users can have a personal food diary.
It's pretty simple: a user can search for a nutrient on a Nutrients table (let's say milk) - currently through a pretty simple search form - and then should be able to save the amount he consumed of this nutrient (together with the nutrient_id) on a second table, which is called Diaries (that holds the nutrient_id as foreign key and an integer field called "amount").
My search works. I can also create new records in Diaries but I have to type in the nutrient_id manually.
My question is now how I can make this easily work? Ideally a user finds a nutrient clicks on it and will be redirected to a page that shows this nutrient together with a field for "amount" and a save button to save both information (nutrient_id and amount) on the Diaries table.
At the end of the day I think the user will be directed to the new action of my diary controller - the question is how my app sets the nutrient_id for this action for the nutrient the user selected before?
Sorry if this is a too simple question but I just started coding few weeks ago.
Thanks a lot for help!
My code looks like as follows:
nutrient.rb
class Nutrient < ActiveRecord::Base
has_many :diaries
def self.search(search)
where("name LIKE ?", "%#{search}%")
end
end
diary.rb
class Diary < ActiveRecord::Base
belongs_to :nutrient
end
nutrients_controller.rb
class NutrientsController < ApplicationController
before_action :set_nutrient, only: [:show, :edit, :update, :destroy]
# GET /nutrients
# GET /nutrients.json
def index
#nutrients = Nutrient.all
end
def search
if params[:search]
#nutrients = Nutrient.search(params[:search]).order("created_at DESC")
if #nutrients.present?
#nutrients
else
flash[:notice] = "Nutrient not found in database"
end
end
end
# GET /nutrients/1
# GET /nutrients/1.json
def show
end
# GET /nutrients/new
def new
#nutrient = Nutrient.new
end
# GET /nutrients/1/edit
def edit
end
# POST /nutrients
# POST /nutrients.json
def create
#nutrient = Nutrient.new(nutrient_params)
respond_to do |format|
if #nutrient.save
format.html { redirect_to #nutrient, notice: 'Nutrient was successfully created.' }
format.json { render :show, status: :created, location: #nutrient }
else
format.html { render :new }
format.json { render json: #nutrient.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /nutrients/1
# PATCH/PUT /nutrients/1.json
def update
respond_to do |format|
if #nutrient.update(nutrient_params)
format.html { redirect_to #nutrient, notice: 'Nutrient was successfully updated.' }
format.json { render :show, status: :ok, location: #nutrient }
else
format.html { render :edit }
format.json { render json: #nutrient.errors, status: :unprocessable_entity }
end
end
end
# DELETE /nutrients/1
# DELETE /nutrients/1.json
def destroy
#nutrient.destroy
respond_to do |format|
format.html { redirect_to nutrients_url, notice: 'Nutrient was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_nutrient
#nutrient = Nutrient.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def nutrient_params
params.require(:nutrient).permit(:name)
end
end
diaries_controller.rb
class DiariesController < ApplicationController
before_action :set_diary, only: [:show, :edit, :update, :destroy]
# GET /diaries
# GET /diaries.json
def index
#diaries = Diary.all
end
# GET /diaries/1
# GET /diaries/1.json
def show
end
# GET /diaries/new
def new
#diary = Diary.new
end
# GET /diaries/1/edit
def edit
end
# POST /diaries
# POST /diaries.json
def create
#diary = Diary.new(diary_params)
respond_to do |format|
if #diary.save
format.html { redirect_to #diary, notice: 'Diary was successfully created.' }
format.json { render :show, status: :created, location: #diary }
else
format.html { render :new }
format.json { render json: #diary.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /diaries/1
# PATCH/PUT /diaries/1.json
def update
respond_to do |format|
if #diary.update(diary_params)
format.html { redirect_to #diary, notice: 'Diary was successfully updated.' }
format.json { render :show, status: :ok, location: #diary }
else
format.html { render :edit }
format.json { render json: #diary.errors, status: :unprocessable_entity }
end
end
end
# DELETE /diaries/1
# DELETE /diaries/1.json
def destroy
#diary.destroy
respond_to do |format|
format.html { redirect_to diaries_url, notice: 'Diary was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_diary
#diary = Diary.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def diary_params
params.require(:diary).permit(:nutrient_id, :amount)
end
end
routes.rb
Rails.application.routes.draw do
resources :diaries
resources :nutrients do
collection do
get :search
end
end
_form.html.erb (for a new diary record)
<%= form_for(#diary) do |f| %>
<% if #diary.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#diary.errors.count, "error") %> prohibited this diary from being saved:</h2>
<ul>
<% #diary.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :nutrient_id %><br>
<%= f.number_field :nutrient_id %>
</div>
<div class="field">
<%= f.label :amount %><br>
<%= f.number_field :amount %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
There are several ways to resolve this, one the quickest and most likely the best approach to me would be to nest the diaries under nutrients, since diaries belongs_to :nutrients
resources :nutrients do
resources :diaries
collection do
get :search
end
end
This way your all your diaries path method would accept a #nutrient argument and your route would be like: /nutrients/4/diaries/1
So in your diaries_controller, you could have:
class DiariesController < ApplicationController
before_action :set_nutrient
def new
#diary = #nutrient.diaries.new
end
def create
#diary = #nutrient.diaries.new(diary_params) # you can safely remove the nutrient_id from the strong params
... remaining logic here
end
...
private
def set_nutrient
#nutrient ||= Nutrient.find(params[:nutrient_id])
end
# You cans skip/ignore this method, if you don't want to be too strict
def set_diary
#diary ||= #nutrient.diaries.find(params[:id])
end
end
Then in your view, you could then have:
<%= form_for([#nutrient, #diary]) do |f| %>
<% if #diary.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#diary.errors.count, "error") %> prohibited this diary from being saved:</h2>
<ul>
<% #diary.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :amount %><br>
<%= f.number_field :amount %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
As I mentioned, there are still other ways of doing this, as you could also use hidden_fields however, this seem to be the cleanest way to me.
If you don't always want your diaries routes to be nested, you could expect: [the list of actions that should not be nested, eg show], both on your before_action and your routes' resources :diaries. Hope I'm able to help or let me know other confusions you may encounter.
I am building an app that allows a user to create a contest. Each contest has many questions and each contests has many entries. Each entry has many answers and each question has many answers. Here are my models:
class Answer < ActiveRecord::Base
belongs_to :entry
belongs_to :question
end
class Contest < ActiveRecord::Base
has_many :entries
has_many :questions
end
class Entry < ActiveRecord::Base
belongs_to :contest
has_many :answers
accepts_nested_attributes_for :answers, allow_destroy: true
end
class Question < ActiveRecord::Base
has_many :answers
belongs_to :contest
end
Everything works except for when I try to create an entry. I get a "param is missing or the value is empty: entry" error. Here is my controller:
class EntriesController < ApplicationController
before_action :set_entry, only: [:show, :edit, :update, :destroy]
before_action :set_contest
# GET /entries
# GET /entries.json
def index
#entries = Entry.all
end
# GET /entries/1
# GET /entries/1.json
def show
end
# GET /entries/new
def new
#entry = Entry.new
end
# GET /entries/1/edit
def edit
end
# POST /entries
# POST /entries.json
def create
#entry = Entry.new(entry_params)
#entry.contest = #contest
respond_to do |format|
if #entry.save
format.html { redirect_to #entry, notice: 'Entry was successfully created.' }
format.json { render :show, status: :created, location: #entry }
else
format.html { render :new }
format.json { render json: #entry.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /entries/1
# PATCH/PUT /entries/1.json
def update
respond_to do |format|
if #entry.update(entry_params)
format.html { redirect_to #entry, notice: 'Entry was successfully updated.' }
format.json { render :show, status: :ok, location: #entry }
else
format.html { render :edit }
format.json { render json: #entry.errors, status: :unprocessable_entity }
end
end
end
# DELETE /entries/1
# DELETE /entries/1.json
def destroy
#entry.destroy
respond_to do |format|
format.html { redirect_to entries_url, notice: 'Entry was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_entry
#entry = Entry.find(params[:id])
end
def set_contest
#contest = Contest.find(params[:contest_id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def entry_params
params.require(:entry).permit(:contest_id, answers_attributes: [:id, :content, :entry_id, :question_id, :_destroy])
end
end
And here is my entry form:
<%= simple_form_for([#contest, #entry]) do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<h3>Questions</h3>
<%= simple_fields_for :answers do |ff| %>
<% #contest.questions.each do |question| %>
<h4><%= question.content %></h4>
<%= ff.input :content, input_html: {class: 'form-control'} %>
<% end %>
<% end %>
</div>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>
I am still working on the logic but am perplexed as to why the entry form is giving me this error. Any help would be appreciated!
UPDATE
In the Rails Guide example they show the new action as:
def new
#person = Person.new
2.times { #person.addresses.build}
end
Do I need to build the answer objects in my new action? I'm not sure... I tried it but it didn't work. I feel like that can't be the problem though as the error is coming from the entry_params method
You should be adding this line to your new action.
#entry.answers.build
And change this line
<%= simple_fields_for :answers do |ff| %>
to
<%= f.simple_fields_for :answers do |ff| %>
Hi i'm very new to rails and any help will much appreciated. I am trying to implement the thumbs_up GEM. I have read the documentation but still find it challenging to implement it fully.
what i would like is the display of an increasing figure when a user clicks on the thumbs_up image and vice-versa when a user clicks on thumbs_down image
by figure i mean: if a user clicks the image thumbs_up you see a figure of 1, if another user clicks the image thumbs_up the figure increases to 2 [displaying 2 users have liked the event giving it a thumbs_up]
Any advise along side with an explanation will be helpful - below is the stage i have reached
i have a.) run g thumbs_up & b.) rake db:migrate
Models:
#Event.rb
class Event < ActiveRecord::Base
belongs_to :user
acts_as_voteable
end
#User.rb
class User < ActiveRecord::Base
has_many :events, dependent: :destroy
acts_as_voter
end
Controller
class EventsController < ApplicationController
before_action :set_event, only: [:show, :edit, :update, :destroy]
before_filter :authenticate_user!
def index
#events = Event.order(:date)
end
def show
#commentable = #event
#comments = #commentable.comments
#comment = Comment.new
end
def new
#event = Event.new
end
def edit
end
def create
#event = Event.new(event_params)
#event.user = current_user
respond_to do |format|
if #event.save
format.html { redirect_to #event, notice: 'Event was successfully created.' }
format.json { render :show, status: :created, location: #event }
else
format.html { render :new }
format.json { render json: #event.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #event.update(event_params)
format.html { redirect_to #event, notice: 'Event was successfully updated.' }
format.json { render :show, status: :ok, location: #event }
else
format.html { render :edit }
format.json { render json: #event.errors, status: :unprocessable_entity }
end
end
end
def destroy
#event.destroy
respond_to do |format|
format.html { redirect_to events_url, notice: 'Event was successfully destroyed.' }
format.json { head :no_content }
end
end
def vote_for
#event = Event.find(params[:id])
current_user.vote_for(#event)
respond_to do |format|
format.js
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_event
#event = Event.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def event_params
params.require(:event).permit(:name, :description, :date, :time, :city, :price, :user_id)
end
end
Views: app/views/events/show.html.erb
<p>
<strong>Name:</strong>
<%= #event.name %>
</p>
<p>
<strong>Description:</strong>
<%= #event.description %>
</p>
<p>
<%=link_to image_tag('thumbs_up', :border => 0), vote_for_event_path(#event), :remote => true %>
<%=link_to image_tag('thumbs_down', :border => 0), vote_against_event_path(#event), :remote => true %>
</p>
Routes:
Rails.application.routes.draw do
devise_for :users
resources :events do
resources :comments, only: [:create, :destroy]
member do
post :vote_for, :vote_against
end
end
end
To show the votes in your view, use the votes_for method and votes_against method.
For example in your view add these lines:
<p>
<strong>Votes For:</strong>
<%= #event.votes_for %>
</p>
<p>
<strong>Votes Against:</strong>
<%= #event.votes_against %>
</p>
Because you are learning Rails, I suggest you try using basic HTML links instead of AJAX links:
<%=link_to image_tag('thumbs_up', :border => 0), vote_for_event_path(#event) %>
<%=link_to image_tag('thumbs_down', :border => 0), vote_against_event_path(#event) %>
A basic link can help you see what's happening, and your controller will reload the entire page.
When you get the basic links working, then read about AJAX, and how to use the Rails link_to with remote: true to update an HTML div.
There are various ways to accomplish this: you can read about using Rails responders, or using coffescript, or using jQuery to attach a link handler.
There are also various user interface solutions, such as updating the vote immediately because you know the original vote and you can increment the vote immediately on the client side.
Here's a related StackOverflow question that has a bunch of good answers and discussion about remote_to and replacing a div: rails link_to :remote
I have a class called "questions", which is similar to an article, and each of them can have comments. Now the problem is, that I want to show multiple questions on the index page and all displaying the comments of the specific question as well as a small little form to leave a comment, which should be added to its question. Basically I have added the form and done everything, apart from figuring out how to get the question id and pass it to the comment.
I have made a little screenshot as well: http://prntscr.com/2pjk0i
questions_controller.rb
class QuestionsController < ApplicationController
before_action :set_question, only: [:show, :edit, :update, :destroy]
# GET /questions
# GET /questions.json
def index
#current_user ||= User.find_by_id(session[:user_id])
#questions = Question.all
end
# GET /questions/1
# GET /questions/1.json
def show
end
# GET /questions/new
def new
#question = Question.new
end
# GET /questions/1/edit
def edit
end
# POST /questions
# POST /questions.json
def create
#question = Question.new(question_params)
#current_user ||= User.find_by_id(session[:user_id])
#question.update(:user_id => #current_user.id)
respond_to do |format|
if #question.save
format.html { redirect_to #question, notice: 'Question was successfully created.' }
format.json { render action: 'show', status: :created, location: #question }
else
format.html { render action: 'new' }
format.json { render json: #question.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /questions/1
# PATCH/PUT /questions/1.json
def update
respond_to do |format|
if #question.update(question_params)
format.html { redirect_to #question, notice: 'Question was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #question.errors, status: :unprocessable_entity }
end
end
end
# DELETE /questions/1
# DELETE /questions/1.json
def destroy
#question.destroy
respond_to do |format|
format.html { redirect_to questions_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_question
#question = Question.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def question_params
params.require(:question).permit(:title, :body)
end
end
comments_controller.rb
class CommentsController < ApplicationController
before_action :set_comment, only: [:show, :edit, :update, :destroy]
# GET /comments
# GET /comments.json
def index
#current_user ||= User.find_by_id(session[:user_id])
#comments = Comment.all
end
# GET /comments/1
# GET /comments/1.json
def show
end
# GET /comments/new
def new
#comment = Comment.new
end
# GET /comments/1/edit
def edit
end
# POST /comments
# POST /comments.json
def create
#comment = Comment.new(comment_params)
#current_user ||= User.find_by_id(session[:user_id])
#comment.update(:user_id => #current_user.id, :question_id => ?) # What to add here to get the specific question id?
respond_to do |format|
if #comment.save
format.html { redirect_to '/', notice: 'comment was successfully created.' }
format.json { render action: 'show', status: :created, location: #comment }
else
format.html { render action: 'new' }
format.json { render json: #comment.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /comments/1
# PATCH/PUT /comments/1.json
def update
respond_to do |format|
if #comment.update(comment_params)
format.html { redirect_to '', notice: 'comment was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #comment.errors, status: :unprocessable_entity }
end
end
end
# DELETE /comments/1
# DELETE /comments/1.json
def destroy
#comment.destroy
respond_to do |format|
format.html { redirect_to '' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_comment
#comment = Comment.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def comment_params
params.require(:comment).permit(:title, :body)
end
end
index.html.erb
<h1>Listing questions</h1>
<%= link_to 'New Question', new_question_path %>
<hr>
<% #questions.each do |question| %>
<!-- Author -->
<%= question.user.name %> <br>
<!-- Date -->
<%= question.created_at %> <br>
<!-- Title -->
<%= question.title %> <br>
<!-- Body -->
<%= question.body %> <br>
<%= question.id %> <br>
<!-- Comment count -->
<%= question.comments.size %> Comment <br>
<!-- Comments -->
<% question.comments.each do |comment| %>
<!-- Comment Author -->
<%= comment.user.name %> <br>
<!-- Comment Date -->
<%= comment.created_at %> <br>
<!-- Comment Body -->
<%= comment.body %> <br>
<% end %>
<%= form_for(question.comments.new) do |f| %>
<div class="field">
<%= f.label :body %><br>
<%= f.text_area :body %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
<hr>
<% end %>
Thank you in advance for your help! :)
The form_for will need to submit the question_id somehow - either by a route or through the form. I recommend a route.
If you don't interact with comments independently - if there is always a question, then change your routes to something like this:
resources :questions do
resources :comments
end
Then - in your form for, you will do this
<%= form_for [question, question.comments.new] do |f| %>
This will cause the form to submit (POST) to /question/:question_id/comments and you can handle it from there.
In the comments controller - you'll get the question from the params[:question_id] and return the result via an ajax response (respond to json).
This part is still tricky if you haven't done it before. If you need help with that part, you can probably find good examples or ask a separate question...
you can add hidden field inside your form
<%= f.hidden_field :question_id, value: question.id %>
or you can change your form
<%= form_for :comment, :url => comments_path(question_id:question.id) do |f| %>
<div class="field">
<%= f.label :body %><br>
<%= f.text_area :body %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
when u submit this form u will have url like /comments?question_id=id