I am trying to implement a search function on my Rails app to get a search box working.
However, when running the code, the following error is raised:
NoMethodError in PostsController#index undefined method `paginate' for #<Searchkick::Results:0x007f3ff123f0e0>
(I also have a tag cloud, which is working fine if I keep the code below unchanged, but if I change #posts = #posts to #posts = Post.search it breaks the tag functionality too.)
I am using:
Rails 4.2.0
ruby 2.2.1p85 (2015-02-26 revision 49769) [x86_64-linux]
Code:
Here is how my PostsController looks like:
class PostsController < ApplicationController
before_action :find_post, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
def new
#post = current_user.posts.build
end
def create
#post = current_user.posts.build(post_params)
if #post.save
redirect_to #post
else
render 'new'
end
end
def edit
#post = Post.friendly.find(params[:id])
end
def update
#post = Post.friendly.find(params[:id])
if #post.update(post_params)
redirect_to #post
else
render 'edit'
end
end
def destroy
#post.destroy
redirect_to root_path
end
def index
if params[:tag]
#posts = Post.tagged_with(params[:tag]).paginate(page: params[:page], per_page: 5)
else
#posts = Post.order('created_at DESC').paginate(page: params[:page], per_page: 2)
end
if params[:nil].present?
#posts = #posts.search(params[:nil]).paginate(page: params[:page])
else
#posts = #posts.paginate(page: params[:page])
end
end
def show
#post = Post.friendly.find(params[:id])
end
def autocomplete
render json: Post.search(params[:query], autocomplete: true, limit: 5).map(&:title)
end
private
def find_post
#post = Post.friendly.find(params[:id])
end
def post_params
params.require(:post).permit(:title, :description, :content, :tag_list, :preview)
end
end
end
This is how my navbar search form looks like
<li class="navs">
<%= form_tag posts_path, method: :get do%>
<%= text_field_tag :search, params[:query], placeholder: "Search Blog", name: "nil" , required: "", class: "input-field", id: "post_search", autocomplete: "off" do %>
<%= submit_tag "", class: "material-icons search-box" %>
<% end %>
<% if params[:search].present? %>
<%= link_to "X", posts_path %>
<% end %>
<% end %>
</li>
I have searched a lot and couldn't find any answers specific that could give me a right direction as to what I am doing wrong.
I would really appreciate any help.
The problem is that the search call will return a Searchkick::Results collection, rather than an ActiveRecord::Relation. The latter has been patched with the paginate method, while the former has not, thus raising a NoMethodError.
According to the documentation, you should be able to make this work by passing the pagination parameters to the search method:
#posts = #posts.search(params[:nil], page: params[:page])
Alternatively to doing pagination within the SeachKick query you can just do the following in a separate step.
#posts = Kaminari.paginate_array(#posts).page(params[:page])
Related
I have created a simple_form_For common for both new and update,
currently, it's working fine for new but for edit/update, it calling the wrong URL.
class NewsfeedsController < ApplicationController
before_action :find_post, only: [:show, :destroy, :edit, :update]
def index
#posts = Post.all.order("created_at DESC")
end
def show
# before_action is taking care of all 4 i.e(sho,edit,update and destroy)..Keeping it DRY
end
def new
#post = Post.new
end
def create
#post = Post.new(post_params)
if #post.save
redirect_to root_path
else
render 'new'
end
end
def edit
end
def update
if #post.update(post_params)
redirect_to newsfeed_path(#post)
else
render 'edit'
end
end
def destroy
end
private
def post_params
params.require(:post).permit(:content)
end
def find_post
#post = Post.find(params[:id])
end
end
In Form.html
<%= simple_form_for #post, url: newsfeeds_path(#post) do |f| %>
<%= f.input :content,label: false, placeholder: "write your post here..." %>
<%= f.button :submit %>
<% end %>
on inspect element on browser,
i am getting action wrong,
it needs to be action="/newsfeeds/7"
Please guide
As you are using common _form for new and update, you need to add condition in url respectively,
<%= simple_form_for #post, url:(#post.new_record? ? newsfeeds_path : newsfeed_path(#post)) do |f| %>
This will help you using both new and update method.
Hope this will help.
Update action should be newsfeed_path(#post) and not newsfeeds_path(#post). Please correct it and try again!
<%= simple_form_for #post, url: newsfeed_path(#post) do |f| %>
Hope that helps!
I keep receiving the below error message when trying to view the results of a search when using nested resource routes as below. For some reason I can't get rails to show the results of my search using the following routes and using the redirect_to #search call in the controller.
routes.rb:
resources :users do
resources :searches
end
Below is the Error:
NoMethodError in SearchesController#create
undefined method `search_url' for #<SearchesController:0x007fc68a881708>
Extracted source (around line #19):
17 #search = current_user.searches.create(search_params)
18
19 redirect_to #search
20
21 end
22
Searches Controller:
class SearchesController < ApplicationController
before_action :require_user
def index
end
def new
# #states = ["red","green","blue"]
#states = State.all
#cities = City.all
#languages = Language.all
#search = Search.new
end
def create
#search = current_user.searches.create(search_params)
redirect_to #search
#Old Search
##search = Search.create(search_params)
end
def show
##search = Search.find(params[:id])
##search = #user.searches.find(params[:id])
#search = current_user.searches.find_by(id: params[:id])
end
#Deleting searches, tied to the "delete link" on the view
def destroy
#search.destroy
flash[:success] = "Micropost deleted"
redirect_to request.referrer || #searches
end
private
def search_params
#:userid = #user.id
params.require(:search).permit(:searchname, :city, :min_gpa, :max_gpa, :firstname, :state, :city, :age, :gender, :universityname, :language, :livingin, :workexperience, :monthsspentabroadLiving, :monthsspentabroadworking, :degree , :degreetype, :countryofdegree, :wantstoworkin, :hasworkexperiencein, :permissiontoworkin, :currentlyemployed, :referencesuponrequest, :worktype, :charitywork)
end
end
New Search Form - View:
<%= bootstrap_form_for #search, url: user_searches_path(current_user), html: {class: "pure-form"} do |s| %>
<%= s.hidden_field :userid, :value => current_user.id %>
<div class="field">
<%= s.text_field :searchname, label: "Search Name" %>
</div>
If you do a bundle exec rake routes, you will see that you have this route:
user_search GET /users/:user_id/searches/:id(.:format) searches#show
This is because your searches resource is nested under the users resource. So, you need to pass the current_user and the #search to the user_search_path helper method.
So, the correct way to redirect to the user's search page is:
redirect_to user_search_path(current_user, #search)
I'm building simple web app where people can share their thought / pictures / so on. It has just two controllers Post, and nested in it Comments. Generally, at the moment all works perfectly, users can add, edit and delete posts the same way as comments. The thing I'm trying to do, and have big troubles with, is possibility to Edit comments without redirecting to the Edit Comment View - so to be able to do it from the "posts#show" level, the same way as comments actually are being created. I think it just would look much more nicer... Here are my:
posts_controller.rb
class PostsController < ApplicationController
before_action :find_post, only: [:show, :edit, :update, :destroy]
def index
#posts = Post.all.order(created_at: :desc)
end
def new
#post = Post.new
end
def create
#post = Post.new(post_params)
if #post.save
redirect_to #post
else
render 'new'
end
end
def show
#comments = #post.comments
end
def edit
end
def update
if #post.update(post_params)
redirect_to #post
else
render 'edit'
end
end
def destroy
#post.destroy
redirect_to root_path
end
private
def find_post
#post = Post.find(params[:id])
end
def post_params
params.require(:post).permit(:title, :description)
end
end
comments_controller.rb
class CommentsController < ApplicationController
before_action :find_post, only: [:create, :edit, :update, :destroy]
def create
#comment = #post.comments.create(comment_params)
if #comment.save
redirect_to #post
else
render 'new'
end
end
def edit
#comment = #post.comments.find(params[:id])
end
def update
#comment = #post.comments.find(params[:id])
#comment.update_attributes(comment_params)
if #comment.save
redirect_to #post
else
render 'edit'
end
end
def destroy
#comment = #post.comments.find(params[:id])
#comment.destroy
redirect_to #post
end
private
def find_post
#post = Post.find(params[:post_id])
end
def comment_params
params.require(:comment).permit(:content)
end
end
View show.html.haml - for post
%h1 Show page
%h3= #post.title
%p= #post.description
=link_to "Edit memory", edit_post_path
=link_to "Delete memory", post_path, method: :delete
%h4 Share your thoughts about the memory
- #comments.each do |comment|
%p= comment.content
=link_to "Edit thought", edit_post_comment_path(#post, comment)
=link_to "Delete thought", post_comment_path(#post, comment), method: :delete
= simple_form_for([#post, #post.comments.build]) do |c|
= c.input :content
= c.submit "Share thought"
View edit.html.haml - for comments (the one I'd like to get rid of / nest somehow to the view shown above)
= simple_form_for([#post, #comment]) do |c|
= c.input :content
= c.submit "Update thought"
I believe there is a simple solution, however despite the fact that I've been reading a lot about possible solution, for a newbie like me it's still difficult to figure out how this should be programed.
The terminology you're looking for is in-place editing, or inline editing
You'll either need a form or JQuery plugin to make any comment written by current_user (assuming you're using Devise) editable.
I've done this a little (you can sign up for free here, click "profile" and then edit the description):
--
The way you'd want to do it is something like this:
#app/assets/javascripts/application.js
# include x-editable scripts
$(".editable).editable([..options..]);
#app/views/posts/index.html.erb
<% #posts.each do |post| %>
<%= post.body %>
<% post.comments.each do |comment| %>
<% if comment.author == current_user %>
<%= content_tag :div, comment.body, class: "editable" %>
<% end %>
<% end %>
<% end %>
I've forgotten how we implemented the provided example; x-editable seems to be the rage now.
Here's how it works:
Include X-Editable (or other plugin) in your app
X-Editable will check for the presence of a .class or #id on an element
Providing such an element will allow X-Editable to make it editable
X-Editable then sends the completed request to the server, acting as Ajax
Looks like there's an x-editable-rails gem you can use:
#Gemfile
gem `x-editable-rails`
#app/assets/javascripts/application.js
#= require editable/bootstrap-editable
#= require editable/rails
#app/views/posts/index.html.erb
<% #posts.each do |post| %>
<% post.comments.each do |comment| %>
<%= editable comment, :body if comment.author == current_user %>
<% end %>
<% end %>
Being new to Rails I've still yet to understand params and the controllers affect on my entire app. I have problems with accessing #variables accross the entire program. For example while working with a parent resource controller when I try to define #child in the controller, I often get the error "Couldn't find Project with 'id'=" or "undefined method `children' for nil:NilClass.
class ProjectsController < ApplicationController
before_action :find_project, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
before_action :find_campaigns, only: [:index]
def index
if params[:search]
#projects = Project.search(params[:search]).order("created_at DESC")
else
#projects = Project.all.order('created_at DESC')
end
end
def new
#project = current_user.projects.build
end
def create
#project = current_user.projects.build(project_params)
# #project = Project.new(project_params)
if #project.save
redirect_to #project, notice: "Successfully created new Project"
else
render 'new'
end
end
def edit
end
def update
if #project.update(project_params)
redirect_to #project, notice: "Project was Successfully updated"
else
render 'edit'
end
end
def show
#random_project = Project.where.not(id: #project).order("RANDOM()").first
end
def destroy
#project.destroy
redirect_to root_path
end
private
def project_params
params.require(:project).permit(:title, :location, :goal, :investor, :description, :image, :category)
end
def find_project
#project = Project.find(params[:id])
end
def find_campaigns
#campaign = #project.campaigns.find(params[:id])
end
end
The above is my child controller. If I add a private method called find_projects (which is my parent object) is there a way to access all children from all projects and display them on one page?
You would loop through each Project and access its children. Start by removing your find_campaigns method and before_action :find_campaigns as they are not needed.
Open up app/views/projects/index.html.erb
<% if #projects.any? %>
<ul class="projects">
<% #projects.each do |p| %>
<li>
<h3><%= p.name %></h3>
<% if p.campaigns.any? %>
<ul class="campaigns">
<% #p.campaigns.each do |c| %>
<li><%= c.name %><li>
<% end %>
</ul>
<% end %>
</li>
<% end %>
</ul>
<% end %>
However when doing this you need to take care since you are cause what is called a N+1 query where every iteration of #projects.each will do a seperate SQL query to fetch the children which is really slow.
We can avoid this by using includes so that the campaigns are loaded in one sweep:
def find_project
#project = Project.includes(:campaigns).find(params[:id])
end
You might want to look at the Rails Guides as it has some pretty nice tricks for using partials to clean up the loop above.
I have implemented a save buttons for jobs and it work fine now i want to list some of the saved jobs in the jobs index page for this i have this code
<h3>Saved Jobs</h3>
<ul>
<% #user.saved_jobs.limit(5).order(:created_at).reverse_order.each do |saved_job| %>
<li><%= link_to saved_job.job.title, saved_job.job.url %>
<span class="delete_button">
<%= link_to "X", saved_job, :method => :delete, :remote => true %></span></li>
<% end %>
</ul>
<%= link_to "see all", saved_jobs_path %>
but when i want access to the jobs index page i get this error undefined method saved_jobs' for nil:NilClass
this my saved_jobs controller
class SavedJobsController < ApplicationController
before_filter :authenticate_user!
def index
#saved_jobs = SavedJob.find_all_by_user_id(current_user.id)
end
def create
#job = Job.find(params[:saved_job][:job_id])
current_user.save_job!(#job)
respond_to do |format|
format.html { redirect_to #job }
format.js
end
end
def destroy
#job = SavedJob.find(params[:id]).job
current_user.saved_jobs.find(params[:id]).destroy
respond_to do |format|
format.html { redirect_to #job }
format.js
end
end
end
and this is my user controller
class ProfilesController < ApplicationController
before_filter :authenticate_user!
def show
#user = User.find_by_slug(params[:id])
if #user
#posts = Post.all
render action: :show
else
render file: 'public/404', status: 404, formats: [:html]
end
end
def index
#users = user_scope.paginate(page: params[:page], per_page: 2)
end
private
def user_scope
current_user ? User.where.not(id: current_user.id) : User.all
end
end
The error suggests that you didn't set #user in your controller action. Can you post the code from your controller?
Update based on controller code:
Your index action in SavedJobsController does not set #user. When you then call #user.saved_jobs.limit(5)..etc... in the view #user is nil, rather than current_user or whatever.
Additionally, you have set #saved_jobs - why not just use that instead of #user.saved_jobs?
for instance:
#in SavedJobsController
def index
#saved_jobs = current_user.saved_jobs.limit(5).order('created_at DESC')
end
and then, in your view:
<% #saved_jobs.each do |saved_job| %>