I am new to Ruby on Rails and AJAX and I am curious as to the best way to achieve the following:
I have an index.html.erb file that lists some records.
In this page I have some checkbox buttons. When these buttons are checked or unchecked, a javascript array is updated.
var selected = [];
When my checkboxes are toggled a
filterchanged()
) javascript function is called that does the following ruby code to update the elements on the page (hides and shows elements without refreshing the page or using a controller
<% Post.recent(**selected = []**).each do |post| %>
... retrieve data from database and hide/show elements
<% end %>
I understand that since javascript runs on the client and ruby on the server, I can’t pass a js variable to ruby but how can I achieve the intended result?
Thank you for pointing me to the right direction!! I now have better understanding on the use of the controller.
I was trying to do everything in Javascript and hide show markers on a map using filters in the page and using different ruby scoped finders in the view which is not very MVC.
I will try to implement your suggestions and update the thread.
Since I have many filters in addition to the checkboxes I will have to pass multiple parameters in my ajax call, check for their presence, and then return the html.
What I don’t understand very well is the javascript code. If I understand correctly
returns some javascript and html and it puts it in a #posts div. Is that correct? Can’t I just reload the post/index using the updated @posts from the controller?
Thank you for pointing me to the right direction!! I now have better understanding on the use of the controller.
No problemo.
I was trying to do everything in Javascript and hide show markers on a map using filters in the page and using different ruby scoped finders in the view which is not very MVC.
Definitely put all your queries in the controllers/models, but depending on the number of markers you may want to fetch everything up front and then toggle them client-side.
I’ve recently developed a maps application that queried based on lat/long bounds and appended new markers as you move around - but that may be overkill for you here.
Since I have many filters in addition to the checkboxes I will have to pass multiple parameters in my ajax call, check for their presence, and then return the html.
Yep, pass through all the params you need, make the queries to get the right posts and then return the HTML.
What I don’t understand very well is the javascript code. If I understand correctly
returns some javascript and html and it puts it in a #posts div. Is that correct?
Yep.
Can’t I just reload the post/index using the updated @posts from the controller?
That’s kind of what the code does - It re-renders the ‘posts/index’ view and updates the #posts element with the contents.
“j” is a javascript escape helper to escape the quotes so it doesn’t break.
# /controllers/posts_controller.rb
class PostsController < ApplicationController
respond_to :html, :js
expose(:posts) { posts_in_context }
expose(:categories) { Category.all }
def index
respond_to do |format|
format.html
format.js
end
end
protected
def posts_in_context
posts = Post.order_by("created_at DESC").limit(20)
if params[:categories].present?
posts = posts.where("category_id in (?)", params[:categories])
end
posts
end
end
I’ve got in the habit of using decent_exposure to expose methods to the views rather than using instance variables.
I’ve found it’s easier to share methods with various actions / views that way and it keeps things pretty clean.
Here’s a slightly different method returning straight HTML and putting all js on the client.
Note that remote: true on the form is removed in this example because we’re handling the ajax form submission ourselves.
# /controllers/posts_controller.rb
class PostsController < ApplicationController
respond_to :html, :js
expose(:posts) { posts_in_context }
expose(:categories) { Category.all }
def index
respond_to do |format|
format.html
format.js { render 'post_list' }
end
end
protected
def posts_in_context
posts = Post.order_by("created_at DESC").limit(20)
if params[:categories].present?
posts = posts.where("category_id in (?)", params[:categories])
end
posts
end
end
Thank you so much for your examples. Really useful!
Also the decent_exposure gem seem useful.
I will try to implement this and update. I have a different setup since I do not have a form and a submit button. I only have regular buttons with listeners on them.
The first example seems clearer indeed. Does it use ajax or does it refresh the page every time a button is changed?
If you want to use the code like in the first example you’ll need a form with remote: true
That just adds a data-remote=“true” attribute to the form which is picked up by the jquery_ujs library that is included with Rails by default.
It says you want to submit the form via ajax, these types of submissions get into the format.js {} block in respond_to