How do I paginate activities for a given project
In my routes file I have
resources :projects do
resources :activities
end
In my activities controller (should it go in my Projects controller?)
def index
#project = current_user.projects.find(params[:project_id])
#activities = #project.activities.all.paginate(:per_page => 5, :page => params[:page])
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #activities }
end
end
In my view
<%= will_paginate #project.activities %>
However no error returned but pagination is missing under table. Any idea?
Firstly, I would urge you to check out Kaminari for your pagination plugin as will_paginate seemed to have stalled out and its still only in pre-release 2 for Rails 3.
https://github.com/amatsuda/kaminari
Secondly, in your view try doing:
<%= will_paginate #activities %>
You're not linking to the variable #activities you created in your controller.
Related
I'm trying to create a select field for a form that selects based on records selected for a model (called "Cancellation_Reasons").
In my model called Cancellation:
<%= form_for(#cancellation do |f| %>
<%= options_from_collection_for_select(#cancellation_reasons, :id, :name) %>
<% end %>
In the Cancellation_Controller:
def new
#cancellation = Cancellation.new
#cancellation_reasons = CancellationReason.find(1)
respond_to do |format|
format.html # new.html.erb
format.json { render json: #trade }
end
end
When I run CancellationReason.find(1) in the the Rails Console it finds the record, so #cancellation_reasons isn't nil. I think that it's probably in how I'm using the select helpers (I've tried experimenting with them, but I'm not quite sure which one to use even after reading the Rails Guide and Rails API docs).
options_from_collection_for_select expect a collection (even it it is a collection of 1).
So change the code to be:
def new
#cancellation = Cancellation.new
#cancellation_reasons = CancellationReason.all
respond_to do |format|
format.html # new.html.erb
format.json { render json: #trade }
end
end
In my app there is an association problem, which I'm unable to fix.
My app is quite simple: There's an Article model; each article has_many comments, and each of those comments has_many votes, in my case 'upvotes'.
To explain the way I designed it, I did a comments scaffold, edited the comment models and routes to a nested resource, everything works fine. Now, I basically did the same process again for 'upvotes' and again edited model and routes to make this a nested resource within the comment nested resource. But this fails at the following point:
NoMethodError in Articles#show
Showing .../app/views/upvotes/_form.html.erb where line #1 raised:
undefined method `upvotes' for nil:NilClass
My _form.html.erb file looks like this:
<%= form_for([#comment, #comment.upvotes.build]) do |f| %>
<%= f.hidden_field "comment_id", :value => :comment_id %>
<%= image_submit_tag "buttons/upvote.png" %>
<% end %>
Why is 'upvotes' undefined in this case, whereas here:
<%= form_for([#article, #article.comments.build]) do |form| %>
rest of code
everything works totally fine? I copied the same mechanism but with #comment.upvotes it doesn't work.
My upvotes_controller:
class UpvotesController < ApplicationController
def new
#upvote = Upvote.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #upvote }
end
end
def create
#article = Article.find(params[:id])
#comment = #article.comments.find(params[:id])
#upvote = #comment.upvotes.build(params[:upvote])
respond_to do |format|
if #upvote.save
format.html { redirect_to(#article, :notice => 'Voted successfully.') }
format.xml { render :xml => #article, :status => :created, :location => #article }
else
format.html { redirect_to(#article, :notice =>
'Vote failed.')}
format.xml { render :xml => #upvote.errors, :status => :unprocessable_entity }
end
end
end
end
I'm sorry for this much code.., my articles_controller: (extract)
def show
#upvote = Upvote.new(params[:vote])
#article = Article.find(params[:id])
#comments = #article.comments.paginate(page: params[:page])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #article }
end
end
And my 3 models:
class Article < ActiveRecord::Base
attr_accessible :body, :title
has_many :comments
end
class Comment < ActiveRecord::Base
attr_accessible :content
belongs_to :user
belongs_to :article
has_many :upvotes
end
class Upvote < ActiveRecord::Base
attr_accessible :article_id, :comment_id, :user_id
belongs_to :comment, counter_cache: true
end
Upvote migration file:
class CreateUpvotes < ActiveRecord::Migration
def change
create_table :upvotes do |t|
t.integer :comment_id
t.integer :user_id
t.timestamps
end
end
end
My routes:
resources :articles do
resources :comments, only: [:create, :destroy] do
resources :upvotes, only: [:new, :create]
end
end
Sorry for that much code. If anyone might answer this, they would be so incredibly awesome!
Thank you in advance!
Why is 'upvotes' undefined in this case, whereas here:
This is because you're calling upvotes on a nil object, the comment doesn't exist yet.
Best thing to do would be looking into nested attributes:
http://guides.rubyonrails.org/2_3_release_notes.html#nested-attributes
http://guides.rubyonrails.org/2_3_release_notes.html#nested-object-forms
Your error message, says that you try call upvotes on nil. Specifically it is a part of code #comment.upvotes.build in your /app/views/upvotes/_form.html.erb view.
You have to fix show action in you ArticlesController, by adding #comment (with contents) variable.
def show
#upvote = Upvote.new(params[:vote])
#article = Article.find(params[:id])
#comments = #article.comments.paginate(page: params[:page])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #article }
end
end
Also strange things are happening in UpvotesController, in create action.
#article = Article.find(params[:id])
#comment = #article.comments.find(params[:id])
#upvote = #comment.upvotes.build(params[:upvote])
Firstly you had fetched one #article using params[:id], then you had fetched all comments of that #article (throught association), where comments id is the same as #article id. Please review your code, it is inconsistent and will not work correctly.
Everything fixed and works fine now. Took a different approach and simply used Upvote.new instead of nesting it into the comments and building associations, edited my routes as well. Implemented Matthew Ford's idea
I would suspect you have many comments on the article page, the comment variable should be local e.g #article.comments.each do |comment| and then use the comment variable to build your upvote forms.
Thanks everybody for your help!
I'm using jquery to render a like button on a story. I have two models story and like
Here's the likes controller code:
def create
#like = Like.new(params[:like])
#story = Story.find(params[:story])
#like.story = #story
if #like.save
respond_to do |format|
format.html
format.js
end
end
end
def destroy
#like = Like.find(params[:id])
##story = #like.story
#like.destroy
respond_to do |format|
format.html { redirect_to stories_url }
format.js
format.json { head :ok }
end
end
This is the button partial (stories/like_button):
<% unless user_likes_story?(#story, current_user) %>
<%= button_to 'like', "/likes?story=#{#story.id}", :id => 'like_button', :remote => true %>
<% else %>
<%= button_to 'liked', #liked, :class => 'like_button unlike', :id => 'unlike_button', :remote => true, method: :delete %>
<% end %>
The problem is that my create.js.erb, when rendering the 'unlike' button, doesn't properly load the #liked instance variable, because it's being set in the #show action of the stories controller before the user has liked the story, so I can't figure out how or where to set it so the JS will render the unlike properly. I'm probably making this harder than it has to be, but...
create.js.erb
$('.button_to').replaceWith('<%=j render 'stories/like_button' %>');
$('#story_likes_count').replaceWith('<%=j render 'stories/likes_count' %>');
Here's how I tried setting #liked, in stories#show
def show
#like = Like.new
#story = Story.find(params[:id])
if current_user
#liked = Like.find_by_user_id_and_story_id(current_user,#story)
end
respond_to do |format|
format.html # show.html.erb
format.json { render json: #story }
end
end
Any idea how to do this properly? Should I just move the partial out of the stories folder?
The solution was pretty simple. In the controller, doing this:
format.js {#liked = #like}
Passes the variable and makes it available to the partial when it's rendered.
I have a blog style site and under each article there is the usual 'view' 'edit' and 'delete' functions for users once they are signed in. The issue is when I click on 'delete' the link it sends you to the 'view' article page and doesn't delete the article. Can't figure this one out as the delete and view paths are different in my code but do the same thing...??
class ArticlesController < ApplicationController
before_filter :authenticate_user!, :except => [:index, :show]
# GET /articles
# GET /articles.xml
# display the amount of article on the home page
def index
#articles = Article.published.page(params[:page]).per(6).ordered
respond_to do |format|
format.html # index.html.erb
# format.atom #index.atom.builder
format.xml { render :xml => #articles }
end
end
# GET /articles/1
# GET /articles/1.xml
def show
#article = Article.find(params[:id])
#comment = Comment.new(:article=>#article)
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #article }
end
end
...
/controllers/articles_controller
# DELETE /articles/1.xml
def destroy
#article = Article.find(params[:id])
authorize! :destroy, #article
#article.destroy
respond_to do |format|
format.html { redirect_to(articles_url) }
format.xml { head :ok }
end
end
end
views/articles/_article.html.erb
<div class= "art-links">
<%= link_to 'Read', article %>
<% if can? :update, article %>
| <%= link_to 'Edit', edit_article_path(article) %> |
<% end %>
<% if can? :destroy, article %>
<%= link_to 'Delete', article, :confirm => 'Are you sure?', :method => :delete %>
<% end %>
</div>
<br />
This may be because you don't have the right javascript files set up. Rails 3 uses unobtrusive javascript to create the DELETE request when you click the link. If the javascript isn't working, it will fall back to doing a GET which will render the show action.
Make sure you have rails.js and prototype.js in your layout. If you are using a javascript framework other than Prototype, you will need to have that framework (jQuery or whatever) along with the appropriate port of rails.js.
This line
format.html { redirect_to(articles_url) }
should send it back to your index action, where do you want to end up?
This case happen also in my project.
Finnaly I know the cause. It was because my application.js that located in app/assets/javascript was being replaced by another application.js that I copy when I try to explore about bootstap.
I simply add this 3 rows back:
//= require jquery
//= require jquery_ujs
//= require jquery-ui
to the application.js on the top row its okay.
and put this
on my app/views/layout/application.html.erb
n it work..
in case if the jquery have been install n still error like me :D
sory for bad english :)
Fairly new to Rails 3 and have been Googling every which way to no avail to solve the following problem, with most tutorials stopping short of handling errors.
I have created a Rails 3 project with multiple content types/models, such as Articles, Blogs, etc. Each content type has comments, all stored in a single Comments table as a nested resource and with polymorphic associations. There is only one action for comments, the 'create' action, because there is no need for the show, etc as it belongs to the parent content type and should simply redisplay that page on submit.
Now I have most of this working and comments submit and post just fine, but the last remaining issue is displaying errors when the user doesn't fill out a required field. If the fields aren't filled out, it should return to the parent page and display validation errors like Rails typically does with an MVC.
The create action of my Comments controller looks like this, and this is what I first tried...
def create
#commentable = find_commentable
#comment = #commentable.comments.build(params[:comment])
respond_to do |format|
if #comment.save
format.html { redirect_to(#commentable, :notice => 'Comment was successfully created.') }
else
format.html { redirect_to #commentable }
format.xml { render :xml => #commentable.errors, :status => :unprocessable_entity }
end
end
end
When you fill nothing out and submit the comments form, the page does redirect back to it's appropriate parent, but no flash or nothing is displayed. Now I figured out why, from what I understand, the flash won't persist on a redirect_to, only on a render. Now here's where the trouble lies.
There is only the 'create' action in the comment controller, so I needed to point the render towards 'blogs/show' (NOTE: I know this isn't polymorphic, but once I get this working I'll worry about that then). I tried this in the "else" block of the above code...
else
format.html { render 'blogs/show' }
format.xml { render :xml => #commentable.errors, :status => :unprocessable_entity }
end
Anyway, when I try to submit an invalid comment on a blog, I get an error message saying "Showing [...]/app/views/blogs/show.html.erb where line #1 raised: undefined method `title' for nil:NilClass."
Looking at the URL, I think I know why...instead of directing to /blogs/the-title-of-my-article (I'm using friendly_id), it's going to /blogs/the-title-of-my-article/comments. I figure that extra "comments" is throwing the query off and returning it nil.
So how can I get the page to render without throwing that extra 'comments' on there? Or is there a better way to go about this issue?
Not sure if it matters or helps, but the route.rb for comments / blogs looks like this...
resources :blogs, :only => [:show] do
resources :comments, :only => [:create]
end
I've been plugging away at this over the last few weeks and I think I've finally pulled it off, errors/proper direction on render, filled out fields remain filled in and all. I did consider AJAX, however I would prefer to do it with graceful degradation if at all possible.
In addition, I admit I had to go about this a very hacky-sack way, including pulling in a way to pluralize the parent model to render the appropriate content type's show action, and at this stage I need the code to simply work, not necessarily look pretty doing it.
I KNOW it can be refactored way better, and I hope to do so as I get better with Rails. Or, anyone else who thinks they can improve this is welcomed to have at it. Anyway, here is all my code, just wanted to share back and hope this helps someone in the same scenario.
comments_controller.rb
class CommentsController < ApplicationController
# this include will bring all the Text Helper methods into your Controller
include ActionView::Helpers::TextHelper
def create
#commentable = find_commentable
#comment = #commentable.comments.build(params[:comment])
respond_to do |format|
if #comment.save
format.html { redirect_to(#commentable, :notice => 'Comment was successfully created.') }
else
# Transform class of commentable into pluralized content type
content_type = find_commentable.class.to_s.downcase.pluralize
# Choose appropriate instance variable based on #commentable, rendered page won't work without it
if content_type == 'blogs'
#blog = #commentable
elsif content_type == 'articles'
#article = #commentable
end
format.html { render "#{content_type}/show" }
format.xml { render :xml => #commentable.errors, :status => :unprocessable_entity }
end
end
end
private
# Gets the ID/type of parent model, see Comment#create in controller
def find_commentable
params.each do |name, value|
if name =~ /(.+)_id$/
return $1.classify.constantize.find(value)
end
end
end
end
articles_controller.rb
class ArticlesController < ApplicationController
def show
#article = Article.where(:status => 1).find_by_cached_slug(params[:id])
#comment = Comment.new
# On another content type like blogs_controller.rb, replace with appropriate instance variable
#content = #article
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #article }
end
end
end
show.html.erb for articles (change appropriate variables for blog or whatever)
<h1><%= #article.title %></h1>
<%= #article.body.html_safe %>
<%= render :partial => 'shared/comments', :locals => { :commentable => #article } %>
shared/_comments.html.erb (I'm leaving out the displaying of posted comments here for simplification, just showing the form to submit them)
<%= form_for([commentable, #comment]) do |f| %>
<h3>Post a new comment</h3>
<%= render :partial => 'shared/errors', :locals => { :content => #comment } %>
<div class="field">
<%= f.label :name, :value => params[:name] %>
<%= f.text_field :name, :class => 'textfield' %>
</div>
<div class="field">
<%= f.label :mail, :value => params[:mail] %>
<%= f.text_field :mail, :class => 'textfield' %>
</div>
<div class="field">
<%= f.text_area :body, :rows => 10, :class => 'textarea full', :value => params[:body] %>
</div>
<%= f.submit :class => 'button blue' %>
<% end %>
shared/_errors.html.erb (I refactored this as a partial to reuse for articles, blogs, comments, etc, but this is just a standard error code)
<% if content.errors.any? %>
<div class="flash error">
<p><strong><%= pluralize(content.errors.count, "error") %> prohibited this page from being saved:</strong></p>
<ul>
<% content.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
I slightly refactored #Shannon answer to make it more dynamic. In my 'find_parent' method I'm grabbing the url path and fetching the controller name. In the 'create' method I'm creating an 'instance_variable_set' which creates a dynamic variable for either Articles (#article) or Blogs (#blog) or what ever it may be.
Hopefully you'll like what I've done? Please let me know if you have any doubts or if something can be improved?
def create
#comment = #commentable.comments.new(params[:comment])
if #comment.save
redirect_to #commentable, notice: "Comment created."
else
content_type = find_parent
instance_variable_set "##{content_type.singularize}".to_sym, #commentable
#comments = #commentable.comments
render "#{content_type}/show"
end
end
def find_parent
resource = request.path.split('/')[1]
return resource.downcase
end
You're getting an error because the blogs/show view likely refers to the #blog object, which isn't present when you render it in the comments controller.
You should go back to using the redirect_to rather than render. It wasn't displaying a flash when you made an invalid comment because you weren't telling it to set a flash if the comment wasn't saved. A flash will persist till the next request.