Rails AJAX Search form reloading page - ruby-on-rails

New to AJAX and search. I feel like I'm an inch away on this one, but I'm not sure what to fix. Also, my controller looks really hacky to me right now.
At any rate, I'm trying to create a search that allows users to search through blog posts on my page using AJAX. Here are the (relevant parts of the) parts:
posts_controller.rb
def show
#posts = Post.all.reverse
#post = Post.find(params[:id])
#link_num = 10
respond_to do |format|
format.html # show.html.erb
format.json { redirect_to #post }
end
end
def search
#link_num = 10
#posts = Post.all.reverse
#post = Post.find(params[:id])
#The including function returns the search results
#search = Post.first.including(params[:term])
render 'show'
end
What strikes me as "hacky" here is that I repeat all the variable assignments (there are others I didn't show cause they're not relevant). Shouldn't an AJAX call ideally not have to redefine/reload all these variables? Also, I have to pass :id to my search action through a hidden field. This feels weird/wrong to me.
show.html.erb
<h1 class="squeeze">Recent Posts</h1>
<%= form_tag("/search", method: "get", class: "search") do %>
<%= text_field_tag(:term, '', placeholder: "Search posts:") %>
<%= hidden_field_tag(:id, #post.id) %>
<%= submit_tag("Search", class: "btn search_button", remote: true) %>
<% end %>
<% if !#search%>
<ul>
<% #posts.first(#link_num).each do |p| %>
<li>(<%= p.created_at.strftime("%b %d, %Y") %>)</span></li>
<% end %>
<% if #posts.length > #link_num %>
<div class="link_disclaimer">
<h4>---</h4>
<h5><%= "Only showing #{#link_num} most recent posts." %></h5>
<h5>Search to narrow results.</h5>
</div>
<% end %>
</ul>
<% elsif #search.empty? %>
<h3>Term not found!</h3>
<% else %>
<ul>
<% #search.first(#link_num).each do |p| %>
<li>(<%= p.created_at.strftime("%b %d, %Y") %>)</span></li>
<% end %>
<% if #search.length > #link_num %>
<div class="link_disclaimer">
<h4>---</h4>
<h5><%= "Only showing first #{#link_num} relevant hits." %></h5>
<h5>Narrow search for more specific results.</h5>
</div>
<% end %>
</ul>
<% end %>
routes.rb
match '/search', to: 'posts#search'
Currently, the search itself works fine, with three major problems:
The aforementioned messiness of my Controller.
The fact that the whole page reloads. Isn't that the opposite of what AJAX is supposed to do? How can I get it to reload just the list of links?
The URL. It's super messy (i.e "/search?utf8=✓&term=limits&id=11&commit=Search"). I'd ideally have it remain untouched by the search, but if that's not possible, maybe just something like "/search?=". How's that done?

Based on the comment here is basic logic to make the function work(Sorry for no real code as that is too time consuming)
In controller you make a method say "search". The method need an argument which is the phrase to search. It gets the result from db and respond to JSON only with the result.
In template you have a search form.
In JS, you listen the event of user clicking the submit button, catch the characters they write, and handle it to ajax function.
Write Ajax code, preferred using jQuery's ajax(). Basically use ajax() to send the typed characters to controller method in #1 as argument. Then ajax() will get the response(the search result) from server.
Update the result in DOM. You can either add a new div to show search result, or replace current main content div to show result.

Related

List all posts on Show view page - Rails

I'd like to make a list of posts on the app/views/posts/show.html.erb page and sort each by id.
Similar to how all of the posts are listed on my app/views/posts/index.html.erb page using the code block below:
<% #posts.each do |post| %>
<div class="col-md-4">
<%= image_tag post.img %>
<h1><%= post.title %></h1>
<p><%= post.content %></p>
<br>
<%= link_to 'Read More', post_path(post) %>
</div>
<% end %>
When I try to use the same each do method on the show page I get an error. But this is what I currently have (it only displays an img/link to the current post):
<h1>Recent Posts</h1>
<ul>
<li>
<%= image_tag #post.img %>
<h2>
<%= link_to #post.title %>
</h2>
</li>
</ul>
Index is for displaying all the items of x.
def index
#posts = Post.all
end
So what you are doing is taking all your posts, and putting them in an array called #posts. You can iterate or enumerate over those with .each do |x|. That means go through each object in the array and show the post image, title and content.
You didn't display your show, but typically a show looks like:
def show
#post = Post.find(params[:id])
end
So you are finding the post with :id and storing that data in #post. This is only 1 object, it's not an array. That's why your .each do |x| isn't working.
There is nothing stopping you from making
def show
#posts = Post.all
end
But then you can't take advantage of rails shortcuts and are repeating yourself, which isn't good in programming. If you want two very distinct windows that use the same information, it's better to figure that out in html/css with a bit of javascript.
The show action of your PostsController is probably only setting up #post, and not #posts. You can't use .each with #post because it's an instance of Post, and not an array, or something that responds to .each. Look at how #posts is set up in the index action, and copy that to your show action.

Rails Ransack - how display empty result?

I've integrated Ransack into my Rails app, I use it via ajax post, soso in t he index.html.erb it uses the _partner.html.erb
But when I get back empty search result, I would like to achieve display a "No result" text.
I've tried it this way(in index.js.erb):
$('#partners_list').html('<% if #partners %> <%= escape_javascript render(#partners) %> <% else %><li class="collection-item">No result</li><% end %>');
$('nav.pagination').html('<%= escape_javascript(paginate(#partners, :remote => true).to_s) %>');
But this is not work, cause if there aren't any search result it will generate this javascript code:
$('nav.pagination').html(' ');
$('nav.pagination').html('');
This will not insert any <li> elements into the <ul>
(index.html.erb)
<ul id="partners_list" class="collection margin-0-minus-15 z-depth-2">
<%= render #partners %>
</ul>
(I've tried handle here in the index.html.erb too, but it seems if there is no search result then the #partners does not exist... because the <%= debug(#partners) %> will not show nothing on the view if the search result is empty)
This is my action:
def index
#search = Partner.where(is_active: true).search(params[:q])
#partners = #search.result.includes(:user, :county).order(created_at: :desc).page(params[:page])
end
How can I achieve when I the back-end searching response with empty result, then the application display an "Empty result" text?
Have u tried with if else statement outside the html('')?
<% if #partners.any? %>
$('#partners_list').html("<%= j render(#partners) %>")
<% else %>
$('#partners_list').html("<li class="collection-item">No result</li>")
<% end %>

Rails - how do you "show" a page if you don't know the id?

I am trying to make a very simple vocabulary webapp that shows a bunch of words with their definitions on the front (index) page. Each word has a field called "knowledge," the value of which can either be "learning" or "learned."
I would like to put a link at the bottom of the index page called "Practice." Clicking on the link will take you to a page showing the word and definition of the first word in the knowledge: "learning" column. Then there will be a "next" link that takes you to the next word in the knowledge: "learning" column. When you run out of words in the knowledge: "learning" column, you're taken back to the index page.
I am getting an id => nil error. The problem appears to be that clicking on the practice link sends me to a show page which requires an id, however I do not actually query the database to determine the id of the first word until I reach the show action in the controller. How do I make a link to "show" the first word in a column?
The "practice" link on the index page looks like this:
<%= link_to 'Practice', page_path(#page) %>
I am using resources :pages in the routes file.
My the show action in my controller Pages looks like this:
def show
if #page.nil?
#page = Page.where(knowledge: 'Learning').first
else
#next = Page.where("id > ?", params[:id]).order(:id).first
end
end
Finally, my show page looks something like this:
<% if #page%>
<p> <%= #page.word %></p>
<p> <%= #page.content %></p>
<p><%= link_to "Next", page_path(#next) %></p>
<% elsif #next %>
<p> <%= #next.word %></p>
<p> <%= #next.content %></p>
<p><%= link_to "Next", page_path(#next) %></p>
<% else %>
<p> Good Job!</p>
<p><%= link_to "Home", pages_path %> </p>
<%end%>
Error message is:
No route matches {:action=>"show", :controller=>"pages", :id=>nil} missing required keys: [:id].
The error highlights this line from my index template:
<%= link_to 'Practice', page_path(#page) %>
*Note that I am very new to programming and rails in general. This is probably really simple.
In general it's a bad idea to link to a "show" action without having an object to show. This is what the "index" action is for.
Try linking to it like this:
<%= link_to 'Practice', pages_path %>
Then in your controller do this:
def index
page = Page.where(knowledge: 'Learning').first
redirect page_path(page)
end
That way you'll always have an ID when the show action is called. I think you'll also want to adjust your show method to this so that #page and #next are available to your view.
def show
#page = Page.find_by(id: params[:id])
#next = Page.where("id > ?", params[:id]).order(:id).first
end
Welcome to Rails!
The page path requires an id. If you go into your project folder, and type rake routes, it will show you a list of your routes, and which require ids. Like this:
user GET /users/:id(.:format) users#show
You are going to need to get the first record and load it in your index page:
def index
#page = Page.where(knowledge: 'Learning').first
end
Then for your link on your index page:
<%= link_to 'Practice', page_path(#page, first: true) %>
first: true is a second parameter that you can check for in your show action so you can tell if the person is coming from the index page.
Then for your show action - (params always returns a string, that's why, the 'true' is in quotes for your show action)
def show
if params[:first] == 'true'
#page = Page.where(knowledge: 'Learning').first
#next = Page.where("id > ?", #page.id).order(:id).first
else
#page = Page.where("id > ?", params[:id]).order(:id).first
#next = Page.where("id > ?", #page.id).order(:id).first
end
end
For your view:
<% if #page%>
<p> <%= #page.word %></p>
<p> <%= #page.content %></p>
<p><%= link_to "Next", page_path(#next) %></p>
<% else %>
<p> Good Job!</p>
<p><%= link_to "Home", pages_path %> </p>
<%end%>
You also may want to look into the will_paginate gem for pagination: https://github.com/mislav/will_paginate

Flash Message (Rails v.4.1.1) Curly Brackets

I'm creating a blog on rails. Everytime a post is created on the new page I want to be back at the index page and see a flash message saying "You post was saved". On the layout I have the following:
<body>
<%= flash.each do |key, value| %>
<p><%= value %></p>
<% end %>
<%= yield %>
</body>
The problem is that I have now on every page (index, new, etc.) curly brackets on the top and I don't know why. Also, instead of just having a message "Your post was saved.". It also appears the following: "{"notice"=>"Your post was saved."}". This is the code for the message hold on the controller (posts_controller.rb):
def create
#post = Post.new(post_params)
if #post.save
redirect_to posts_path
flash[:notice] = "Your post was saved."
else
render "new"
end
end
I'm beginning with Rails, thanks for the help.
See update below for explanation
remove the = in <%= flash.each.... %> Should just be <% flash.each.....%>
<% flash.each do |key, value| %>
<p><%= value %></p>
<% end %>
To keep it simple, when you want your ruby code to render something onto the page, use <%= %>, ex: to show the current time you would use <%= Time.now %>. For other things like settings variables in your view without text rendering on the page, use <% %>.
For example, say I want to assign a variable equal to the current time so I can use it below in the view, I would use <% time = Time.now %> then I can show that time on the page with <%= time %>.
<% time = Time.now %>
<p>The current time is <%= time %>.</p>

form_for save input values to session variables

I am trying to create a compare functionality for an index of schools. Currently I am using the following code which takes any checked school and adds it to the school_ids[] in the params.
In my gemfile:
gem 'will_paginate'
In my school's index.html.erb:
<%= form_tag compare_path, :method => 'get' do %>
<%= submit_tag "Compare" %>
<ul>
<% #schools.each do |school| %>
<li>
<%= check_box_tag'school_ids[]', school.id %>
<%= link_to school.name, school %><br>
<%= school.city %>, <%= school.state %>
</li>
<% end %>
</ul>
<% end %>
In my school controller I have:
def compare
#schools = School.find(params[:school_ids])
end
This works great as long as all of the check schools are on the same page. But since I'm using will_paginate to paginate the list of schools, if I change pages, the check boxes do not persist. I'm assuming I need to save to sessions somehow.
Do you mean you want to be able to add a check mark to a school A on page 1 of the index, go to page 2 of the index and add another check mark for school B, then submit the compare form and see schools A and B? If that's the case, then you're correct, you need to get the check boxes into the session. Attach a js click event, like
$('.checkbox_class').click(function(){
$.post('update_session_method', { school_id: $(this).val(), checked: $(this).is(:checked)]);
});
then add a controller method
def update_session_method
session[:school_ids] ||= []
if params[:checked]
session[:school_ids] << params[:school_id]
else
session[:school_ids].delete(params[:school_id])
end
end
then your compare method
def compare
#schools = School.find(params[:school_ids].merge(session[:school_ids] || []))
end

Resources