Right now I am displaying notifications for a user on a view. I have the ajax working where it removes the item from the database by clicking the close link. However, what I can't figure out is how to have the view update without actually refreshing the page.
Am I going to have to use a partial of some sort? I haven't really done anything like this and googling is really not helping me since I seem to be going down the rabbit hole.
Right now for my view I have:
<% current_user.notifications.each do |n| %>
<div class="alert alert-info">
<%= link_to 'x', n, :method => :delete, :remote => true, :class => 'delete_notification close' %>
<%= n.title %>
<%= n.description %>
</div>
<% end %>
and then I have my js:
$('.delete_notification').bind('ajax:success', function() {
$(this).closest('tr').fadeOut();
});
I'm not really sure how to ask the question since I am lost on what the 'next step' is. I will provide more information if/when I know what else I need to provide.
Thanks in advance!
UPDATE:
I accepted an answer seeing as it was extremely helpful and exactly what I was looking for!
What I ended up comming up with is:
$(function(){
$(".delete_notification").click(function(){
var $this = $(this);
var url = $this.data('url');
$.ajax(url, {
method: 'DELETE',
type: 'POST',
dataType: 'json',
success: function() {
var parent = $this.parent();
parent.fadeOut("slow");
}
});
});
});
The link_to's callback option (:update) is removed since Rails 3, so I'd rather implement the AJAX link myself.
<% current_user.notifications.each do |n| %>
<div class="alert alert-info">
<%= link_to 'x', n, :class => 'delete_notification close' %>
<%= n.title %>
<%= n.description %>
</div>
<% end %>
Setup AJAX so that it passes Rails' CSRF protection:
$.ajaxSetup({
beforeSend: function(xhr) {xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))}
});
And finally the effect you want:
$('.delete_notification').click(function(ev) {
ev.preventDefault(); // prevent refreshing page
var $this = $(this);
var url = $this.attr('href');
$.ajax(url, {method: 'DELETE'}).done(function() {
$this.closest('tr').fadeOut();
});
});
Maybe you should also modify your controller (I haven't tested this):
class NotificationsController < ApplicationController
# Other actions ...
def destroy
# Your logic (remove rendering or redirection)
head :no_content
end
end
Create a js template in your view called destroy.js.erb, and in that file include the javascript you want to run - something like this:
$('#delete_notification_<%= params[:id] %>').closest('tr').fadeOut();
In your view make sure you specify the ID of the link:
<%= link_to 'x', n, method: :delete, remote: true, class: 'delete_notification close' id: "delete_notification_#{n.object.id}" %>
Related
I had typeahead and search working in my rails 4 app with typeahead v0.10.5 but since updating to v0.11.1 it broke. I orginally got the code from various answers on this site without really understanding what is going on. I have it mostly working now but the dropdown is populating with text of all attributes from my model and not just names like I want it to. I want to make sure I am doing things correctly and hopefully gain a better understanding of what's going on.
item.rb
def self.search(search)
if search
where(['lower(name) LIKE ?', "%#{search}%"])
else
Item.all
end
end
items_controller.rb
def index
#items= Item.search(params[:query])
end
def typeahead
render json: Item.where('name ilike ?', "%#{params[:query]}%")
end
_header.html.erb
<div role="search">
<%= form_tag items_path, class: 'navbar-form', method: :get do %>
<div class="form-group">
<%= text_field_tag :query, params[:query], id: 'typeahead', class: 'search-input form-control',
placeholder: 'Search items' %>
</div>
<span class="search-icon">
<%= button_tag "<i class='fa-navbar fa fa-search'></i>".html_safe, name: nil, class: 'search-button' %>
</span>
<% end %>
</div>
.
.
.
<script>
$(document).ready(function(){
var bloodhound = new Bloodhound({
datumTokenizer: function (d) {
return Bloodhound.tokenizers.whitespace(d.value);
},
queryTokenizer: Bloodhound.tokenizers.whitespace,
limit: 10,
remote: {url: '/typeahead/%QUERY',
wildcard: '%QUERY'}
});
bloodhound.initialize();
$('#typeahead').typeahead(null, {
name: 'name',
source: bloodhound.ttAdapter()
});
$('#typeahead').bind('typeahead:selected', function(event, datum, name) {
window.location.href = '/items/' + datum.id;
});
});
</script>
config/routes.rb
get 'typeahead/:query', to: 'items#typeahead'
The source of my data comes from my model based on the user input. Is there any place for prefetch in my example?
In the remote I have the url set to '/typeahead/%QUERY'. In other examples I have seen something like '/typeahead?q=%QUERY'. What is the difference?
What is the prepare and wildcard options in bloodhound? I have read the api explanation here but still don't understand.
The problem with the suggestions dropdown displaying properly was because I was missing displayKey: 'name' as a typeahead option. Adding this fixed my problem.
$('#typeahead').typeahead(null, {
name: 'name',
displayKey: 'name',
source: bloodhound.ttAdapter()
});
I'm trying to rewrite the following button code so that instead of redirecting me to the show page, it just creates the object and the current page stays displayed.
<div class="colors">
<li class="colors" id="greys">
<%= button_to "some text", votes_path(color: 'grey', kid_id: current_kid, scoop_id: scoop.id, :method => :create), class: 'grey color-button' %>
</li>
</div>
You should use a remote flag to send the request via javascript. And possibly give feedback to the user.
To send a request via js in rails you have to add remote: true to the options hash:
<div class="colors">
<li class="colors" id="greys">
<%= button_to "some text", votes_path(color: 'grey', kid_id: current_kid, scoop_id: scoop.id, :method => :create), class: 'grey color-button', remote: true %>
</li>
</div>
In your controller you can do special responses by respondig to js
#votes controller
def create
# ...
respond_to do |format|
format.js render
end
end
In your create.js.erb you can write javascript code with embeded ruby to give responses. You can even render partials here.
alert('create.js.erb')
First, have some id for your button:
<%= button_to "some text", votes_path(color: 'grey', kid_id: current_kid, scoop_id: scoop.id, :method => :create), class: 'grey color-button' id: 'buttonID' %>
And then, in your Javascript code, whenever the button is clicked, send a request to server, create the new record, and then update the current page, so that it shows the record has been created.
I would show you the sculpture of how to do it:
$("buttonID").click(function() {
$.ajax({
url: "some_url" // Your URL, for example, /votes
type: "POST", // The type of request
data: { color: "grey" } // Send data to server in this format
success: function() {
// Here you can update the page so that the user could
// know the data has been posted, and no need to press
// the button again
}
});
});
I am new to rails and trying to change the value of a boolean via a checkbox and using jquery ajax:
<%- #tasks.each do |task| %>
<div class="task-wrapper">
<%= check_box_tag 'completed', task.id , task.completed, :class => "task-check" %>
<%= content_tag :span, task.task %>
<%= content_tag :span, task.deadline %>
</div>
<% end %>
and the javascript:
$(".task-check").bind('change', function(){
if (this.checked){
var bool = this.checked ? 1 : 0;
$.ajax({
url: '/todos/toggle',
type: 'POST',
data: '{"task_id":"'+ this.value +'", "bool":"'+ bool +'"}'
});
}
else {
alert("no");
}
});
then the controller:
def toggle(task_id, bool)
#task = Todo.find_by_id(task_id)
if #task != nil?
#task.update_attributes(:completed => bool)
else
set_flash "Error, please try again"
end
end
finally the routes:
resources :todos do
member do
post 'toggle'
end
end
also tried collection but gives the same error.
when ever i try it i get a 404 error on the action.
what is the problem?
thanks
As of Rails 4, there's a way to do this without needing any additional JS or CSS:
<%= check_box_tag 'completed', task.id, task.completed,
data: {
remote: true,
url: url_for(action: :toggle, id: task.id),
method: "POST"
} %>
It turns out that adding remote: true to an input causes jquery-ujs to make it ajax-y in all the nice ways. Thoughtbot's "A Tour of Rails jQuery UJS" briefly touches this (and many other good things available); the "Unobtrusive scripting support for jQuery" page in the jQuery UJS wiki does a thorough job on this as well.
Try the following (leaving everything else as is):
the javascript:
$(".task-check").bind('change', function(){
if (this.checked){
$.ajax({
url: '/todos/'+this.value+'/toggle',
type: 'POST',
data: {"completed": this.checked}
});
}
else {
alert("no");
}
});
the controller:
def toggle
#task = Todo.find(params[:id])
if #task.update_attributes(:completed => params[:completed])
# ... update successful
else
# ... update failed
end
end
Take a look at bundle exec rake routes to show you the paths that rails generates. In the case of your post 'toggle' which is a member you get a path like /todos/:id/toggle, hence the updated url in the ajax.
In the controller the :id from the path ends up in params[:id]. The data from the ajax request also ends up in the params hash, hence params[:completed].
I have this AJAX request:
$("#searchagain_form").submit(function() {
$.ajax({
type: 'POST',
url: "/searchforward",
data: {engine: $("#engine_options").find(".on_engine").text()}
});
});
This is my form:
<%= form_for :searchagain, :url => {:controller => 'results', :action => 'searchforward'}, :html => { :id => "searchagain_form" } do |f| %>
<%= f.text_field :search, :value => #search, :id => 'searchagain_bar' %>
<%= f.submit :value => "search!", :id => 'searchagain_submit' %>
<% end %>
However, checking my logs, the data does not seem to be POSTed to the server when the form is submitted. Any idea why?
You did not cancel the submission, so the page will unload instead of sending the ajax-request.
Append a return false; to the function.
But however, it's not clear what kind of element #engine_options .on_engine is, if it is an text-input use val() instead of text() (text() should return nothing for text-inputs).
When it is a textarea it will return the textNode inside the element, not any later modified text. So use val() also for textarea.
You don't need a semicolon here:
{engine: $("#engine_options").find(".on_engine").text();}
Other than that, you can use a traffic monitor like Charles to monitor the post and make sure it's sending what you want it to.
Edit:
$('#searchagain_form').serialize()
will also give you an object with all your form data
Why don't you add an error handler in your ajax params?
$("#searchagain_form").submit(function() {
$.ajax({
type: 'POST',
url: "/searchforward",
data: {engine: $("#engine_options").find(".on_engine").text()},
error: function(jqXHR, textStatus, errorThrown){
console.log(errorThrown);
}
});
});
How can I use jQuery UJS to submit a form after the change event of a select box fires in Rails?
My view looks like this:
<% for perm in #permissions %>
<%= form_for [#brand,perm], { :remote => true } do |f| %>
<tr id="permission_<%= perm.id %>">
<td><%= perm.configurable.name %></td>
<td><%= f.select :action, ['none', 'read', 'update', 'manage'] %></td>
</tr>
<% end %>
<% end %>
Pretty straightforward. My controller is like so:
class PermissionsController < ApplicationController
before_filter :authenticate_user!
respond_to :html, :js
load_and_authorize_resource :brand
load_and_authorize_resource :permission, :through => :brand
def index
end
def update
#permission = Permission.find(params[:id])
#permission.update_attributes(params[:permission])
end
end
And in my application.js:
// Place your application-specific JavaScript functions and classes here
// This file is automatically included by javascript_include_tag :defaults
$(function() {
$("#permission_action").change(function() {
this.form.submit(); // This needs to be an AJAX call, but how?
});
})
Please note that the form submission fires just fine. But it is doing a regular post rather than an AJAX submission. When I place a submit button on the form and use that, the AJAX submission works fine. I need to change:
this.form.submit();
to an AJAX call, but I don't know how. Can anyone help?
Looking through the jquery-ujs sources, i figured that this might be the safest way:
// in application.js
jQuery.fn.ajaxSubmit = function() {
this.trigger('submit.rails');
};
it just fires the event as would .submit() but in a ujs flavour, so you would call
$('.my_form').ajaxSubmit();
I see you are already using jQuery that's good. Following code was taken from here: http://railscasts.com/episodes/136-jquery
I strongly advise you get familiar with these screen casts they help a ton (understatement).
// public/javascripts/application.js
jQuery.ajaxSetup({
'beforeSend': function(xhr) {xhr.setRequestHeader("Accept", "text/javascript")}
})
jQuery.fn.submitWithAjax = function() {
this.submit(function() {
$.post(this.action, $(this).serialize(), null, "script");
return false;
})
return this;
};
$(document).ready(function() {
$("#new_review").submitWithAjax();
})