I am following the Railcast tutorial on endless page. I am receiving a Argument Error The #users variable appears to be empty. Did you forget to pass the collection object for will_paginate?
The javascript has been modified so you have to click a read more link to get more results. So the pagination will work by click instead of scrolling.
The error points to the line <%= will_paginate #questions %>
Questions Controller:
def index
#Before gem added it was `#questions = Question.all`
#questions = Question.order("question").page(params[:users]).per_page(2)
respond_with(#questions)
end
Users Controller:
def profile
#profile = User.profile
#user = User.find(params[:id])
#question = Question.where(recipient_id: params[:id]).first
end
def show
#user = User.find(params[:id])
#question = Question.where(recipient_id: params[:id])
end
View:
<% #question.select(&:answer?).each do |question| %>
Question: <%= question.question %>
<br>
Answer: <%= question.answer %><br><br>
<%= will_paginate #questions %>
<% end %>
Questions.js.coffee:
jQuery ->
if $('.pagination').length
$('#append_and_paginate').prepend('<a id="append_more_results" href="javascript:void(0);">Show more questions</a>');
$('#append_more_results').click ->
url = $('.pagination .next_page').attr('href')
if url
$('.pagination').text('Fetching more questions...')
$.getScript(url)
Index.js.erb:
$('#questions').append('<%= j render(#questions) %>');
<% if #questions.next_page %>
$('.pagination').replaceWith('<%= j will_paginate(#questions) %>');
<% else %>
$('#append_and_paginate').remove();
<% end %>
<% sleep 1 %>
Replace
#questions = Question.order("question").page(params[:users]).per_page(2)
With
#questions = Question.order("question").paginate(:page => params[:page], :per_page => 2)
EDIT
Update the User model as below:
class User < ActiveRecord::Base
has_many :sender_questions, :class_name => 'Question', :foreign_key => 'sender_id'
has_many :recipient_questions, :class_name => 'Question', :foreign_key => 'recipient_id'
end
Update the UsersController#show action as below:
def show
#user = User.find(params[:id])
#question = #user. recipient_questions.paginate(page: params[:page])
end
Related
Right now I have a like button that allows you to like foods. When you try to unlike the food, I get this error:
The action 'destroy' could not be found for UsersController
I'm not sure why it is looking for the destroy action in the users controller. My only guess is because the button is on the user show page, so I assume it defaults to that controller, but how would I access the delete method from my votes controller?
Shared like form
<% unless current_user.votes.empty? || current_user.votes.pluck(:food_id).include?(food.id) %>
<%= form_for #vote do |f| %>
<%= f.hidden_field 'food_id', food.id %>
<%= f.hidden_field 'user_id', food.user.id %>
<%= f.submit "Vote", :class => "like_button" %>
<% end %>
<% else %>
<% vote = food.votes.where(user_id: current_user.id).first %>
<div class="unlike_button">
<%= button_to "Unlike", vote, method: :delete %>
</div>
<% end %>
class VotesController < ApplicationController
def index
#votes = Vote.all
end
def new
#vote = Vote.new
end
def create
#vote = Vote.new(vote_params)
if #vote.save
puts #vote
flash[:notice] = "Thanks for voting!"
redirect_back(fallback_location: root_path)
else
puts "No"
flash[:notice] = "Something went wrong"
redirect_back(fallback_location: root_path)
end
end
def destroy
#vote = Vote.find(params[:id])
if #vote.destroy!
flash[:notice] = "Unvoted!"
redirect_back(fallback_location: root_path)
end
end
private
def vote_params
params.require(:vote).permit(:food_id, :user_id)
end
end
class Vote < ApplicationRecord
belongs_to :user
belongs_to :food
end
So I'm still a Rails noob so I may be completely going at this wrong but I have two controllers. A Question Controller and an Answer Controller. I am trying to build a grading function that allows an admin user to assign points to essay questions. I am using the /answer/:id to be where the :id is the id of the question and then rendering a partial to iterate through all of the answers for that id. Clear as mud I'm sure...
My problem: Within the partial where the user's answer is displayed, I have a form that allows the admin to fill out the number of points for that answer and submit. Ideally, I'd like it to move to the next page (using will_paginate), but at a minimum, I'd like to stay on the same page. I am able to get the form working but it keeps going to /answers/:id but where :id is the id of the individual answer, so not what I'm hoping.
answers_controller.rb
class AnswersController < ApplicationController
def index
#user = current_user
#questions = Question.all
end
def show
#user = current_user
#question = Question.find(params[:id])
#answers = Answer.where("question_id = ?", #question.id).paginate(:page => params[:page], :per_page => 1)
#answer = Answer.where("question_id =? AND user_id = ?", #question.id, #user.id)
end
def edit
#answer = Answer.find(params[:id])
end
def update
#answer = Answer.find(params[:id])
if #answer.update_attributes(grade_params)
flash[:success] = "Answer Graded"
else
flash[:warning] = "Not Graded"
end
end
private
def grade_params
params.require(:answer).permit(:points_earned)
end
end
_essay_grades.html.erb (partial that is being rendered on the show page that contains the form)
<% #answers.each do |answer| %>
<p>User: <%= answer.user_id %></p>
<%= answer.answer %><br>
<%= #question.value %>
<br>
<%= form_for(answer) do |f| %>
<%#= f.radio_button :points_earned, #question.value %><br>
<%#= f.radio_button :points_earned, 0 %> <br>
<%= f.text_field :points_earned %> Points<br>
<br>
<%= f.submit "Award Points" %>
<% end %>
<% end %>
<br>
<br>
<%= will_paginate #answers, renderer: BootstrapPagination::Rails %>
routes.rb
Rails.application.routes.draw do
resources :admins, :answers, :static_pages, :questions
devise_for :users, :controllers => { registrations: 'registrations' },
:path => '', :path_names =>
{ :sign_in => "login", :sign_up => "register" }
root "static_pages#index"
end
I'm sure there's a simple solution here (or maybe it's changing how I have things set up...). Any help is greatly appreciated!
AFTER FEEDBACK:
Added the grades model and set up a through relationship with questions.
answer_controller.rb
class AnswersController < ApplicationController
def show
#user = current_user
#question = Question.find(params[:id])
#answers = Answer.where("question_id = ?", #question.id).paginate(:page => params[:page], :per_page => 1)
#answer = Answer.where("question_id =? AND user_id = ?", #question.id, #user.id)
end
def update
#user = current_user
#question = Question.find(params[:question_id])
#answer = #question.answers.find(params[:id])
#grade = #question.grades.new(grade_params)
if #grade.save
flash[:success] = "Answer Graded"
redirect_to #question
end
end
private
def grade_params
params.require(:grade).permit(:user_id, :answer_id, :points_earned, :graded_by, :comment)
end
end
_answer.html.erb
<%= answer.user_id %>
<%= form_tag [#question, answer], method: :put do %>
<%= hidden_field_tag :graded_by, current_user.id %>
<%= hidden_field_tag :answer_id, answer.id %>
<%= number_field_tag :points_earned %>
<%= submit_tag "Submit Grade" %>
<% end %>
routes.rb
Rails.application.routes.draw do
resources :questions do
resources :answers, only: [:update]
end
resources :admins, :static_pages
questions/show.html.erb
...
<h3>Show answers</h3>
<%= render #answers, locals: {question: #question} %>
<%= will_paginate #answers, renderer: BootstrapPagination::Rails %>
You will have to use the following in your form so the update does not load a new page, but still submits your update. Use Chrome / Firefox developer tools to view requests / responses.
<%= form_for(answer), :remote => true do |f| %>
Then, alter the update action in the answers controller to load the 'next unrated answer':
def update
rated_answer = Answer.find(params[:id])
if rated_answer.update_attributes(grade_params)
flash[:success] = "Answer Graded"
else
flash[:warning] = "Not Graded"
end
#answer = get_next_unrated_answer(rated_answer.question_id)
end
private
def get_next_unrated_answer(question_id)
# I am making a couple of assumptions on your model here, but get an answer that has not been rated yet for this question
next_answer = Answer.where("question_id = ? and points_earned = ?", question.id, nil)
#returned automatically
end
Then you will have to create app/views/answers/update.js.erb to load the new answer to your page with the following line:
$('#main_div').html('<%= escape_javascript(render partial: 'whatever_partial_you_have_created_to_display_the_next_unrated_answer') %>');
Just go and create a new partial that displays your answer and form correctly for the next unrated answer. Or ideally load your initial 'show.html.erb' with the relevant partials and reuse them.
This is the simple way to do it, but if I were you I would probably rename these new functions to not use 'update' or 'show' but rather call it something like 'rate' and even 'rate_show' so you can use update and show in its original form (for updating and answer or showing an answer) if required later in your project.
From what I understood of your question, I think you'd be best looking into nested routes:
#config/routes.rb
resources :questions do
resources :answers, only: [:update]
end
#app/controllers/questions_controller.rb
class QuestionsController < ApplicationController
def show
#question = Question.find params[:id]
end
end
#app/views/questions/show.html.erb
<%= #question.title %>
<%= render #question.answers, locals: {question: #question} %>
#app/views/questions/_answer.html.erb
<%= answer.title %>
<%= form_tag [question, answer], method: :put do %>
<%= text_field_tag :grade %>
<%= submit_tag %>
<% end %>
The above will give you what you have already (just to clarify your "clearly mad" remark is not the case at all!).
-
The following is where the nested resources come in.
At the moment, it seems you're having a problem associating an answer to a question:
where :id is the id of the individual answer, so not what I'm hoping
A remedy for this is as follows:
#app/controllers/answers_controller.rb
class AnswersController < ApplicationController
def update
#question = Question.find params[:question_id]
#answer = #question.answers.find params[:id]
#grade = #answer.grades.new grade_params
redirect_to #question if #grade.save
end
private
def grade_params
params.permit(:points_earned) #-> will probably have to refactor this
end
end
This will create a new grade (which you should have in an associated model), for that specific answer. Because the answer has been associated to a question, it will allow you to use the nested routes to load both.
In terms of your setup, I'd personally add a Grade model, so that you can have multiple grades per answer. This is against your current schema, but works well to ensure you have the functionality necessary to facilitate multiple grades:
#app/models/grade.rb
class Grade < ActiveRecord::Base
belongs_to :answer
belongs_to :user
end
#app/models/answer.rb
class Answer < ActiveRecord::Base
has_many :grades
end
I have these models:
class List < ActiveRecord::Base
has_many :list_items, :dependent => :destroy
accepts_nested_attributes_for :list_items, :reject_if => lambda { |a| a(:task.blank?)}
end
class ListItem < ActiveRecord::Base
belongs_to :list
end
and this controller:
class ListsController < ApplicationController
def index
#lists = List.order("lists.created_at DESC")
end
def show
#list = List.find(params[:id])
end
def new
#list = List.new({:name => Time.now.strftime("%A %d %B")})
3.times { #list.list_items.build}
end
def create
#list = List.new(list_params)
if #list.save
flash[:notice] = "List successfully added."
redirect_to(:action => 'index')
else
render(:action => 'new')
end
end
def edit
#list = List.find(params[:id])
end
def update
#list = List.find(params[:id])
if #list.update_attributes
flash[:notice] = "List updated successfully."
redirect_to(:action => 'index')
else
render(:action => 'edit')
end
end
def delete
#list = List.find(params[:id])
end
def destroy
#list = List.find(params[:id]).destroy
flash[:notice] = "List successfully deleted"
redirect_to(:action => 'index')
end
private
def list_params
params.require(:list).permit(:name, :task)
end
end
and this form:
<%= form_for #list do |f| %>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
<%= f.fields_for :list_items do |builder| %>
<p>
<%= builder.label :task %><br />
<%= builder.text_field :task %>
</p>
<% end %>
<p><%= f.submit "Create" %></p>
<% end %>
You can probably see what I am trying to do -> create a form where it creates a new list and then adds task to that list but for some reason whenever I hit submit on the form the name stays but tasks don't save?
I solved the problem turns out i needed to add the list_item attributes to the list controller. I was trying to do this the rails 3 way in teh model with attr_accessible which kept giving me an error. I needed to update the controller to have this at the bottom:
def list_params
params.require(:list).permit(:name, :list_items_attributes => :task)
end
New to rails so I am implementing a basic app.
Text field and a submit button, on clicking the submit button the text to be displayed on the div placed below the submit button of the index page.
Code and things I trying is as follows -
Siteurl - http://localhost:3000/lists/index
list controller code -
class ListsController < ApplicationController
def index
List.all
end
def add
#message = params[:todo_text]
List.create(:title => #message)
redirect_to '/lists/index', :notice => "Notice"
end
end
Model list.rb -
class List < ActiveRecord::Base
attr_accessible :title
end
Views list index.html.erb code -
<div id="search">
<%= form_tag("/lists/add", :method=>"post") do %>
<%= text_field_tag(:todo_text, '',:class=>"formtext" ) %>
<%= submit_tag("Add Message", :class=>"submit") %>
<% end %>
</div>
Now when I submit some text it redirects to the same..but data is not reverting back to the page. Let me know what I am doing wrong and how do i fix it ?
in controller
def index
#lists = List.all
end
in index.html.erb
<% #lists.each do |list| %>
...
<% end %>
try
<%= form_for #list, :url => { :action => "add" } do |f| %>
controller
def index
#lists = List.all
end
def add
#list = List.new(params[:title])
redirect_to #lists, :notice => "Notice"
end
I have a table of venues and offers. Each venue can have have many offers.
I would like to be able to add the offers to the venues from the venues edit page. So far I have this (code below) but its giving a "NoMethodError in Venues#edit, undefined method `model_name' for NilClass:Class" error.
venues edit page
(the div id="tabs-3" is a container in an accordion)
<div id="tabs-3">
<%= form_for [#venue, #offer] do |f| %>
<h2 class="venue_show_orange">Offers</h2>
<%= f.fields_for :offers do |offer| %>
<div class="add_offer">
<%= offer.text_field :title %><br>
</div>
<div class="button"><%= submit_tag %></div>
<% end %>
<% end %>
</div>
offers controller
class OffersController < ApplicationController
def new
#offer = Offer.new
end
def create
#offer = #venue.offers.create!(params[:offer])
#offer.venue = #venue
if #offer.save
flash[:notice] = 'Offer added'
redirect_to offers_path
else
render :action => :new
end
end
def edit
#offer = Offer.find(params[:id])
end
def update
#offer = Offer.find(params[:id])
#offer.attributes = params[:offer]
if #offer.save!
flash[:notice] = 'Offer updated successfully'
redirect_to offers_path(#offer)
end
end
end
venues controller
(nothing offer related in here - is this where I'm going wrong?)
class VenuesController < ApplicationController
protect_from_forgery :only => [:update, :delete, :create]
load_and_authorize_resource
def new
#venue = Venue.new
5.times { #venue.venuephotos.build }
end
def create
#venue = Venue.new params[:venue]
if #venue.save
flash[:notice] = 'Venue added'
redirect_to venues_path
else
render :action => :new
end
end
def edit
#venue = Venue.find(params[:id])
5.times { #venue.venuephotos.build }
end
def update
#venue = Venue.find(params[:id])
#venue.attributes = params[:venue]
if #venue.save!
flash[:notice] = 'Venue updated successfully'
redirect_to :back
end
end
end
Any help is much appreciated thanks very much!
edit
venues edit page
<div id="tabs-3">
<%= form_for #venue do |f| %>
<div class="edit_venue_details">
<h2 class="venue_show_orange">Offers</h2>
<%= render :partial => 'offers/offer', :collection => #venue.offers %>
<div class="clearall"></div>
<h2 class="edit_venue_sub_header">Add a new offer</h2>
<%= f.fields_for :offers do |offer| %>
<% if offer.object.new_record? %>
<p class="edit_venue">title: <br>
<%= offer.text_field :title, :class => "edit_venue_input" %></p>
<% end %>
<% end %>
</div>
<button class="submit_button" type="submit"> Save changes</button>
<% end %>
</div>
whats being displayed
however if I add a new offer, that will display correctly:
Some remarks:
1) Replace:
<%= form_for [#venue, #offer] do |f| %>
with:
<%= form_for #venue do |f| %>
Because offers data will be updated through the related venue, only one controller action will handle the form.
2) If you want to add some unexisting offers in this form, you shoud instantiate them the way you did with venuephotos
3) Show your Venue model. You should have at least:
accepts_nested_attributes_for :offers
Fixed with:
<%= f.fields_for :offers, #venue.offers.build do |offer| %>