I've got a little demo setup in which clicking a checkbox toggles an attribute via AJAX. It's working fine, but Rails REALLY wants to render something, so I've basically resorted to creating a blank toggle.js.erb file in my views.
Controller action in question:
def toggle
#task = Task.find(params[:id])
respond_to do |format|
format.js do
if (#task.status != true)
#task.status = true
else
#task.status = false
end
#task.save
render :layout => false
end
end
end
View in question:
<h1>Tasks</h1>
<ul style="list-style-type: none;">
<% #tasks.each do |task| %>
<li id="<%= dom_id(task) %>">
<%= check_box_tag(dom_id(task), value = nil, checked = task.status) %>
<%= task.action %> <%= link_to 'Edit', edit_task_path(task) %>
<%= link_to 'Delete', task, :confirm => 'Are you sure?', :method => :delete, :remote => true %>
</li>
<% end %>
</ul>
<%= link_to 'New Task', new_task_path %>
<script>
$$('input').each(function(el) {
el.observe('click', function(event) {
// Get the task ID
var elId = el.id.split("_")[1];
// Build the toggle action path
var togglePath = '/tasks/' + elId + '/toggle/';
// Create request, disable checkbox, send request,
// enable checkbox on completion
new Ajax.Request(togglePath, {
onCreate: function() {
el.disable();
},
onSuccess: function(response) {
},
onComplete: function() {
el.enable();
}
});
});
});
</script>
Without the blank toggle.js.erb file I've got in the views, Rails still gives me an error saying that it's trying to render something.
Ultimately, I'd like to both not have to have a blank toggle.js.erb file, and I'd like to get that Prototype stuff into my static JavaScript stuff and out of the view.
I'm pretty new to Rails, so there's probably an easier way to be doing this, but I'm kind of stuck here.
render :layout => false means that you want to render 'toggle' view without layout.
If you don't want render anything at all, you should use :nothing => true option
def toggle
#task = Task.find(params[:id])
#task.toggle! :status
# if it used only by AJAX call, you don't rly need for 'respond_to'
render :nothing => true
end
EDIT: In Rails4/5 you can use head :ok instead of render nothing: true, it's more preferable way to do this, but result is the same.
Related
I am looking for solution for infinite scroll with Kaminari in my project, but it still does not work as expected.
In my home page have view for render post:
<div class="home_post_tab_content">
<%if #posts.present?%>
<div id="home_post_infinite_scroll">
<%= render #posts %>
</div>
<%if #posts.total_count > 10%>
<div class="home_post_pagination_button" id="home_post_pagination">
<%= link_to_next_page(#posts, 'Xem thêm', :remote => true) %>
</div>
<%end%>
<%end%>
</div>
Controller:
respond_to do |format|
format.html {}
format.js
end
Script file:
// Append new data
$("<%=j render #posts %>").appendTo($("#home_post_infinite_scroll"));
// Update pagination link
<% if #posts.last_page? %>
$('#home_post_pagination').remove();
<% else %>
$('#home_post_pagination').html("<%=j link_to_next_page(#posts, 'See more', :remote => true) %>");
<% end %>
Q: How can I trigger the next button when user scrolls in end of page (jquery or JS...)?
Or, If any one has another solution for infinite scroll please let me know. Thanks so much!
I found the solution for resolve it:
<script>
$(document).on('turbolinks:load', function() {
$(window).scroll(function() {
var next_url = $("#home_post_pagination a[rel='next']").attr('href');
if (next_url && ($(window).scrollTop() > ($(document).height() - $(window).height() - 5000))) {
$('#home_post_pagination').show();
$('#home_post_pagination').html('<a>Loading...</a>');
$.getScript(next_url);
return;
}
});
return $(window).scroll();
});
</script>
The $.getScript(next_url) will be trigger next button on scroll
The object Task contains a boolean field done. How to Ajax refresh the page after the change of status? And instead true - false display value complete - incomplete?
routes.rb:
resources :tasks do
member do
post 'done'
end
end
Controller:
def done
#task = Task.find(params[:id])
#task.update_attributes(:done => params[:done])
respond_to do |format|
format.js {}
end
end
View:
....
<td><%= task.done %></td>
<td><%= check_box_tag 'done', task.id , task.done, :class => "task-check", remote: true %></td>
....
<script>
$(".task-check").bind('change', function(){
$.ajax({
url: '/tasks/'+this.value+'/done',
type: 'POST',
data: {"done": this.checked},
});
});
</script>
Update
edited script
<script>
$(".task-check").bind('change', function(){
$.ajax({
url: '/tasks/'+this.value+'/done',
type: 'POST',
data: {"done": this.checked},
}).done(function(ajax_response){
location.reload();
});
});
</script>
and controller
def done
#task = Task.find(params[:id]) # make sure you have this
#task.update_attributes(:done => params["done"])
render :json => { data: "Success", is_done: params[:done] }
end
How to update page content without having to reboot ?
Controller:
def done
#task = Task.find(params[:id]) # make sure you have this
#task.update_attributes(:done => params["done"])
render :json => { data: "Success", is_done: params[:done] }
end
View:
....
<td><%= task.done %></td>
<td><%= check_box_tag 'done', task.id , task.done, :class => "task-check" %></td>
....
<script>
$(".task-check").on('change', function(e){
// removed remote-true. you can preventDefault if you need to but doubt page tries to refresh from a checkbox getting checked. e.preventDefault() if you need it
$.ajax({
type: "POST",
url: '/tasks/done',
type: 'POST',
data: {done: $('.task-check').val()},
}).done(function(ajax_response){
// update whatever you want after it completes
});
});
</script>
Didn't test this but I write these all the time. Let me know if it doesn't work post the payload and I'll figure out what's missing.
If you just want to update the value from 'incomplete' to 'complete', you can simply target the element that's called incomplete. if it has a class of, say, 'task-status' you can target that element and update in the .done part of the function. For instance:
$('.task-status').text('Complete')
should replace what previously said incomplete. If it's a text input you might use .val() instead. If it's just text on the page .text() should work.
I'm currently learning rails and working on what I'm sure is everyone's first rails app, a simple todo list. I need to implement a checkbox next to the items to indicate whether they are complete or not. Each item has a boolean attribute called "completed" in their model. I have found a couple checkbox questions while searching but none explain the syntax very easily in the context of the index view.
Also, I really want the checkbox to work without a submit button. I know I could accomplish something like this using AngularJS's ng-model but I don't think it would be practical to implement angular for such a small thing and I don't know how angular works with rails.
If anyone could give me a pointer in the right direction, it would be greatly appreciated. Here's my index.html.erb for reference.
<h1>
To Do List
</h1>
<table>
<tr>
<% #todo_items.each do |item| %>
<!-- Checkbox here -->
<tc style="<%= 'text-decoration: line-through' if item.completed %>">
<%= link_to item.title, item %>
</tc>
<tc>
<%= item.description %>
</tc>
<tc>
<%= link_to "Edit", edit_todo_item_path(item) %>
</tc>
<tc>
<%= link_to "Delete",item, data:{:confirm => "Are you sure you want to delete this item?"}, :method => :delete %>
</tc>
<hr/>
<% end %>
</tr>
</table>
<p>
<%= link_to "Add Item", new_todo_item_path %>
</p>
This is my way, I don't know this way is right direction or not but this works for me (also different case but same of concept).
views for checkbox
You could put an id item or something into attribute of checkbox for find an object in controller if you send data to controller for get record of object, and you could define if attribute completed of record is true or false:
<%= check_box_tag :completed_item, 1, item.completed? ? true : false, { class: 'name-of-class', data: { id: item.id} } %>
controller
You need two action call set_completed and remove_completed, and also you don't need templates for them, just use format as json:
before_action :set_item, only [:set_completed, :remove_completed, :other_action]
def set_completed
#item.set_completed!
respond_to do |format|
format.json { render :json => { :success => true } }
end
end
def remove_completed
#item.remove_completed!
respond_to do |format|
format.json { render :json => { :success => true } }
end
end
private
def set_item
#item = Item.find params[:id]
end
Model
For set_completed! and remove_completed! you could define in your model
def set_default!
self.update_attributes(:completed => true)
end
def remove_default!
self.update_attributes(:completed => false)
end
routes
resources :address do
collection do
post 'set_completed'
post 'remove_completed'
end
end
Also, you need help JavaScript for handle send request from view to controller event click of checkbox:
jQuery
$(".completed_item").click(function(){
var check = $(this).is(":checked");
if (check == true){
set_completed($(this).attr('data-id'));
} else{
remove_completed($(this).attr('data-id'));
}
});
function set_completed(data_id) {
$.ajax({
type: 'POST',
url: "/items/set_completed",
data: { id: data_id},
dataType: 'json',
success: function(response){
if(response){
}else{
alert('error');
}
}
})
}
function remove_compelted(data_id) {
$.ajax({
type: 'POST',
url: "/items/set_completed",
data: { id: data_id},
dataType: 'json',
success: function(response){
if(response){
}else{
alert('error');
}
}
})
}
I have this link in Rails:
<%= link_to "Add to Journal", add_post_journal_path(post), :method => :put %>
However I want transform this link to show a fancybox with the content listing my content to choose. First, I use this code:
<%= link_to "fancy", "#add_post", :class=>"fancybox" %>
but I have errors, because I want pass the actual post to fancybox, so I'm using this code: in add_post.html.erb:
<h1>Escolha o Jornal que deseja adicionar:</h1>
<ul>
<% current_user.journals.each do |journal| %>
<li><%= link_to journal.name,add_post_complete_journal_path(journal),:remote=>true %> </li>
<% end %>
</ul>
and my controller is:
def add_post
#journal_post = JournalsPosts.new
session[:post_add] = params[:id]
end
def add_post_complete
#journal_post = JournalsPosts.create(:post_id => session[:post_add],:journal_id => params[:id])
respond_with #journal_post
end
How can I transform this code to use my content in my fancybox?
Add on your action add_post the next respond with js:
def add_post
#journal_post = JournalsPosts.new
session[:post_add] = params[:id]
respond_to do |format|
format.js
end
end
Add on a file on your views add_post.js.erb with the next content:
$.fancybox('<%= escape_javascript(render(:partial => 'path_to/add_post'))%>',
{
openEffect: "fade",
closeEffect: "fade",
autoSize: true,
minWidth: 480,
scrolling: 'auto',
});
For example, you have add a partial _add_post.html.erb on your views. Now inside this partial you can write your code view:
#code for your view inside partial `add_post.html.erb`
<%= #journal_post %>
<h1>Escolha o Jornal que deseja adicionar:</h1>
<ul>
.
.
Regards!
I have a page on my site that lets users respond to invites by either accepting or declining them.
Their response is stored in the database as the boolean 'accepted', and is used to apply the classes 'selected' or 'not_selected' (selected makes the text orange) to the 'attending' div or the 'not attending' div.
<% #going, #not_going = invite.accepted ? ['selected','not_selected'] : ['not_selected','selected'] %>
<%= link_to(outing_invite_accept_path( { :outing_id => invite.outing_id, :invite_id => invite.user_id } )) do %>
<div class="attending_div <%= #going %>">
attending
</div>
<%end %>
<%= link_to(outing_invite_decline_path( { :outing_id => invite.outing_id, :invite_id => invite.user_id } )) do %>
<div class="attending_div <%= #not_going %>">
not attending</div>
</div>
<% end %>
When either div is clicked, it's diverted to the appropriate controller actions:
def invite_accept
#outing = Outing.find(params[:outing_id])
#invite = OutingGuest.find_by_outing_id_and_user_id(params[:outing_id], params[:invite_id])
#invite.update_attribute(:accepted, true)
redirect_to({:action => "index"})
end
def invite_decline
#outing = Outing.find(params[:outing_id])
#invite = OutingGuest.find_by_outing_id_and_user_id(params[:outing_id], params[:invite_id])
#invite.update_attribute(:accepted, false)
redirect_to({:action => "index"})
end
And as right now, this code works just fine. But it requires the index page be refreshed for it to take effect.
I know it's possible to update the page without a refresh using a jQuery ajax call attached to a listener on the appropriate div, but I have no idea what such a call would look like, or where to start, really...
You want to use rail's link_to :remote => true.
See http://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-link_to
For dealing with callbacks you can bind to certain events that will trigger. For example:
<%= link_to "Click Me!", some_path, :class => 'ajax', :remote => true %>
<script>
jQuery(function($) {
$("a.ajax")
.bind("ajax:loading", console.log('loading'))
.bind("ajax:complete", console.log('complete'))
.bind("ajax:success", function(event, data, status, xhr) {
console.log(data);
})
.bind("ajax:failure", function(xhr, status, error) {
console.log(error);
});
});
</script>
This page is also a pretty good write up: http://www.simonecarletti.com/blog/2010/06/unobtrusive-javascript-in-rails-3/