Rails, Ajax, and boolean; can't uncheck box - javascript

I know there are a bunch of related questions out there, but I can't get mine to work. Here is what I have...
#app/views/tasks/index.html.erb
<%- #tasks.each do |task| %>
<div class="task-wrapper">
<%= check_box_tag 'checked_in', task.id , task.checked_in, :class => "task-check" %>
<%= content_tag :span, task.name %>
</div>
<% end %>
<%= link_to 'New Task', new_task_path %>
<script>
$(".task-check").bind('change', function(){
if (this.checked){
$.ajax({
url: '/tasks/'+this.value+'/toggle',
type: 'POST',
data: {"checked_in": this.checked}
});
}
else {
alert("no");
}
});
</script>
#app/controllers/tasks_controller.rb
...
def toggle
#task = Task.find(params[:id])
if #task.update_attributes(:checked_in => params[:checked_in])
# do I need something here?
else
# do I need something here?
end
end
...
My task model has a 'checked_in' attribute that is boolean.
I got this code from this question...
Rails change boolean value with checkbox and jquery ajax
...and don't quite understand everything that is going on. When I create a new task I can successfully check the box to set my boolean value to true. However, when I uncheck the box I get the js pop-up that says "No", but nothing get set in the DB, and nothing is sent back to server.
Any ideas?

The problem comes from your js code
$(".task-check").bind('change', function(){
if (this.checked){
$.ajax({
url: '/tasks/'+this.value+'/toggle',
type: 'POST',
data: {"checked_in": this.checked}
});
}
else {
alert("no");
}
});
When you check/uncheck the box, the changeevent is triggered, then the function is testing this.checked. It returns false false when the box is unchecked, so you don't go inside the condition but directly in the else, which calls alert.
So you probably want to remove the condition.

Thats how browsers work. They dont send the value of unchecked checkboxes.
Check/Uncheck need to be determined based on the presence of the parameter.

Thanks Antoine. That worked...doing the best I can to learn JS. For posterity here is what worked...
app/views/tasks/index.html.erb
<%- #tasks.each do |task| %>
<div class="task-wrapper">
<%= check_box_tag 'checked_in', task.id , task.checked_in, :class => "task-check" %>
<%= content_tag :span, task.name %>
</div>
<% end %>
<%= link_to 'New Task', new_task_path %>
<script>
$(".task-check").bind('change', function(){
$.ajax({
url: '/tasks/'+this.value+'/toggle',
type: 'POST',
data: {"checked_in": this.checked}
});
});
</script>
...additionally I was getting a template error being thrown in the console, so here is updated controller code.
def toggle
#task = Task.find(params[:id])
#task.update_attributes(:checked_in => params[:checked_in])
render :nothing => true
end

Related

Rails - pass a parameter to controller from form select dropdown

I have 2 models: Team and Quest. When creating a new team, I have a drop-down of all the quests. When a quest is selected, I want to display information about the quest.
My understanding is that everything in the form is on the client side and AJAX is required to pass the selected quest to the server side. My code is based on this Stack Overflow answer.
Here is how I constructed my form:
app/views/teams_form.html.erb
<%= form_for(#team) do |f| %>
<fieldset>
<ol>
<li>
<%= f.label :name %>
<%= f.text_field :name %>
</li>
<li>
<%= f.label :quest_id %>
<%= f.select :quest_id,
options_from_collection_for_select(#quests, :id, :name),
{}, {remote: true, url: '/teams/new', method: 'get'} %>
</li>
<% if #x != nil && #x.id != nil %>
<li><%= #x.id %></li>
<% end %>
</ol>
<p>
<%= f.submit %>
</p>
</fieldset>
<% end %>
app/controllers/team_controller.rb
def new
#team = Team.new
#quests = Quest.all
respond_to do |format|
if params[:quest_id] != nil
#x = Quest.find(params[:quest_id])
end
format.html #new.html.erb
format.json
format.js
end
end
My goal was to pass the :quest_id parameter from the form to the #x variable and use that in the form.
This has produced nothing. I'm not getting the parameter in the controller and I'm not sure what I'm missing.
As per the description shared it seems like the you are unable to get the value of the selected item from the dropdown.
Below mentioned code is used for selecting value from dropdown also you can inspect the same using developer tools of the browser.
quest = $("#quest_id").val();
Note: Assuming the selector id is quest_id, change it according to your form.
Now, you can call the ajax using the below mentioned code.
$.ajax({
type: "GET",
url: "/teams/new",
data:{ quest_id: quest },
dataType: "json",
success:function(data){
# logic for using ajax result
}
Hope it helps!!
Finally got this working, wanted to post if anyone sees this and is having the same problem:
I went with a separate AJAX request since that was being suggested
app/views/teams_form.html.erb
<script>
$(document).ready(function() {
$('#team_quest_id').change(function() {
$.ajax({
url: "/teams/new",
data: {quest_id: $("#team_quest_id option:selected").val()},
dataType: "script",
method: "get",
success: function(r){}
});
});
});
</script>
I moved the location of the parameter assignment
app/controllers/team_controller.rb
def new
#team = Team.new
#quests = Quest.all
if params[:quest_id] != nil
#x = Quest.find(params[:quest_id])
end
respond_to do |format|
format.html #new.html.erb
format.json
format.js
end
end
And most importantly - I created a js file to render my form
app/views/new.js.erb
$('#new_team').html("<%= j (render 'form') %>");
This video was extremely helpful
The code in your question is almost correct, you forgot to nest the attributes in data.
<% # app/views/teams_form.html.erb %>
<%= f.select :quest_id,
options_from_collection_for_select(#quests, :id, :name),
{}, {remote: true, url: '/teams/new', method: 'get'} %>
<% # should be: %>
<%= f.select :quest_id,
options_from_collection_for_select(#quests, :id, :name),
{}, {data: {remote: true, url: '/teams/new', method: 'get'}} %>
<% # or even better, use the path helper instead of the hard coded path %>
<%= f.select :quest_id,
options_from_collection_for_select(#quests, :id, :name),
{}, {data: {remote: true, url: new_team_path, method: :get}} %>
Having set the attributes correctly, we still need to fix the form further. On page request the browser will request the form, but #x will never be set. Since ERB will not be send to the client we'll need to add a handle to find our quest container element back.
<% # app/views/teams_form.html.erb %>
<% if #x != nil && #x.id != nil %>
<li><%= #x.id %></li>
<% end %>
<% # should be something like %>
<li id="quest-info-container"></li>
Now in the controller split of the HTML request from the JS request.
# app/controllers/teams_controller.rb
def new
respond_to do |format|
format.html do
#team = Team.new
#quests = Quest.all
end
format.js do
#quest = Quest.find(params.dig(:team, :quest_id))
end
end
end
You could simplify the above by sending the select data-path to another url that handles the quest preview.
Now we need to render the preview in our container we need 2 files for this, first of how the resulting structure should look. Keep in mind that this will be rendered inside the container.
<% # app/views/teams/_quest_preview.html.erb %>
<% # Here comes what you want to display about the quest. You can give this %>
<% # file another name if you like. You have #quest to your disposal here. %>
<%= #quest.id %> <strong><%= #quest.name %></strong>
Now we only need a JavaScript file that loads the above structure into our created handle.
<% # app/views/teams/new.js.erb %>
handle = document.getElementById('quest-info-container');
handle.innerHTML = '<%= j render('quest_preview') %>';
The j is an alias for escape_javascript. If the partial is not in the same directory use <%= j render('other_dir/some_partial') %> instead.

Following Or Unfollowing a User using Ajax causes href for other links to change? Images / Examples attached

I'll try explain this strange situation as best as possible. Images / Examples are attached
I have a page dedicated to show all the users a particular user is following.
users_controller.rb
def following
#pals = current_user.following
end
following.html.erb
<% #pals.each do |pal| %>
<div class="following-user-btn">
<% if current_user_is_following(current_user.id, pal.wall.id) %>
<%= link_to 'Following' , unfollow_wall_path(pal.wall.id), remote: true, method: :post, class: 'unfollow-button' %>
<% else %>
<%= link_to 'Follow' , follow_wall_path(pal.wall.id), remote: true, method: :post, class: 'btn follow-button' %>
<% end %>
</div>
<% end %>
When I load the page, everything shows up fine, the Following button is next to each user accordingly and unfollowing a user works fine. However after clicking the unfollow button once it will change the href for the other users to the first user you unfollowed. You will not be able to Follow again if the following button is currently being used by another user.
Here is my relationships controller and my javascript
def follow_wall
if current_user.follow #wall.id
respond_to do |format|
format.html { redirect_to root_url }
format.js { render 'walls/follow_wall' }
end
end
end
def unfollow_wall
if current_user.unfollow #wall.id
respond_to do |format|
format.html { redirect_to root_url }
format.js { render 'walls/unfollow_wall' }
end
end
end
Unfollow_wall.js.erb
$('.unfollow-button').bind('ajax:success', function(){
$(this).closest('.unfollow-button').hide();
$(this).closest('.following-user-btn').html('<%= link_to 'Follow' , follow_wall_path(#wall.id), remote: true, method: :post, class: 'btn follow-button' %>');
});
Follow_wall.js.erb
$('.follow-button').bind('ajax:success', function(){
$(this).closest('.follow-button').hide();
$(this).closest('.following-user-btn').html('<%= link_to 'Following' , unfollow_wall_path(#wall.id), remote: true, method: :post, class: 'unfollow-button' %>');
});
I even tried changing it to this:
$('#follow-button').attr('class', 'btn unfollow-button')
.text('Following')
.attr('href', "/<%= #wall.id %>/unfollow_wall")
.attr('id', 'unfollow-button');
$('#unfollow-button').text('Follow')
.attr('class', 'btn follow-button')
.attr('href', "/<%= #wall.id %>/follow_wall")
.attr('id', 'follow-button');
No luck for either
Notice the href is correct for all users upon a fresh reload.
When I unfollow the user in the middle, everything is still fine.
Now when I Unfollow the top user the top user href changes to the middle user? This is where I'm really getting confused?
This has been doing my head in for the past few days... ANY help is REALLY appreciated. Thank you!
I don't think you should be binding an ajax:success event at that point in the code.
Binding an event with a function to an element means that, from that time on, the element will watch for the event and react to it by running the function whenever the event happens. This means that the binding should be done before the expected time of the first event.
However Rails will run the JS in unfollow_wall.js.erb as a response to the button being clicked - that's not a time for binding the function with an event, that's a time for running the function.
The way I would do this is to not do binding, but to use the wall id in the element identifiers on the page like this:
Here see the id of the outer div of each button
<%# following.html.erb %>
<% #pals.each do |pal| %>
<div id="following-user-btn-<%= pal.wall.id %>">
<% if current_user_is_following(current_user.id, pal.wall.id) %>
<%= link_to 'Following' , unfollow_wall_path(pal.wall.id), remote: true, method: :post, class: 'unfollow-button' %>
<% else %>
<%= link_to 'Follow' , follow_wall_path(pal.wall.id), remote: true, method: :post, class: 'btn follow-button' %>
<% end %>
</div>
<% end %>
and in the js just find the button with the right id in the outer div
# unfollow_wall.js.erb
$('#following-user-btn-<%= #wall.id %>').find('.unfollow-button').hide();
$('#following-user-btn-<%= #wall.id %>').html('<%= link_to 'Follow' , follow_wall_path(#wall.id), remote: true, method: :post, class: 'btn follow-button' %>');
the js code in the unfollow_wall.js.erb file is just as it is here in entirety, not enclosed in a bind function.
The same would apply to the other js file of course.

Rails: Delete nested attribute while updating entry

I have a form with nested attributes, where you can dynamically add and remove attributes (the form lets you click "add hobby" and "remove hobby" links...a user has hobbies, and when entering his profile he can add one, two, five, or fifty hobbies).
With the help of the nested form gem this works completely fine. However, I also provide the ability to UPDATE a user's profile.
When doing this, a user can click the dynamic "remove" link to take off a hobby. However, this doesn't delete the "hobby" from the database, like I want it to. So I'm trying to write Jquery or Javascript or something that deletes the object from the database, if it exists.
This is what the code looks like:
...
...
<%= f.fields_for :hobbys, :wrapper => false do |hobby| %>
<tr class="fields">
<td> <%= user.text_field :hobby %> </td>
<td> <%= user.link_to_remove "Remove" %> </td>
</tr>
<% end %>
</table>
<p><%= f.link_to_add "Add hobby", :hobbys, :data => { :target => "#hobbys" } %></p>
<script>
$(document).on('nested:fieldRemoved', function(event){
var field = event.field;
/* ??? Delete field somehow ??? */
})
</script>
Is there any way to do this? I really don't know how to go about it....it doesn't seem like Jquery can delete off a database...
UPDATE
here's my best attempt so far...
...
...
<%= f.fields_for :hobbys, :wrapper => false do |hobby| %>
<tr class="fields">
<td> <%= user.text_field :hobby %> </td>
<td> <%= user.link_to_remove "Remove" %> </td>
</tr>
<% end %>
</table>
<p><%= f.link_to_add "Add hobby", :hobbys, :data => { :target => "#hobbys" } %></p>
<script>
$(document).on('nested:fieldRemoved', function(event){
$.get("delete", { id: 2 })
alert( "Deleted: ");
})
</script>
I'm just trying to get this damn thing to work, by hardcoding an id as the argument (since I can't yet easily extract it). But it keeps using the string "delete" as the id of the user.....huh??
Here's the log output:
Started GET "/users/delete?id=2" for 127.0.0.1 at 2016-10-05 08:35:15 -0400
Processing by UsersController#show as */*
Parameters: {"id"=>"delete"}
Current user: anonymous
Completed 404 Not Found in 19.5ms
ActiveRecord::RecordNotFound (Couldn't find User with id=delete):
You could use Jquery to call a function in Ruby to do it for you:
Javascript:
$.get( "delete", { id: id_to_delete } )
.done(function( deleted ) {
alert( "Deleted: " + deleted.id );
});
Route (route.rb):
get 'delete', to: 'application_controller#delete'
Application Controller (application_controller.rb):
def delete
id = params[:id].to_i
to_delete = Hobby.find(id).destroy
render json: {id: id}
end
Add a listener on click of the remove button for each activity and make an ajax request to your backend api which handles it. It will look something like below:
$('.activities').each(function(activity) {
$(activity).click(function(event) {
$.ajax
url: 'activity/:id',
method: delete,
success: function(data) { REMOVE ELEMENT IN HERE }
error: alert('this didnt work');
})
})

Javascript not working when page is refreshed (Rails)

On Rails 4, I am trying to submit a form whenever I focus out the input field, without having to click on submit. When I place the script on the html page, it works fine:
<td>
<%= form_for commitment, html: {class:"index_edit_comments"}, remote: :true do |f| %>
<%= f.text_field :comments, class:"index_edit_comments_input" %>
<% end %>
<script>
$(document).ready(function() {
$(".index_edit_comments").each(function() {
$(this).find(".index_edit_comments_input").blur(function() {
$(this).submit();
});
});
});
</script>
</td>
But when I move the same code to a separated .js file, it is not functioning properly. It works right after loading the page, but when I use another function that refreshes the page, it stops working.
On the .js.coffee file it looks like this:
$(document).ready ->
$(".index_edit_comments").each ->
$(this).find(".index_edit_comments_input").blur ->
$(this).submit()
return
return
return
The other function that refreshes the page is this:
At index.html.erb:
<% if commitment.check == true %>
<td><%= link_to 'Yes', toggle_check_commitment_path(commitment), type: "button" %></td>
<% else %>
<td><%= link_to 'No', toggle_check_commitment_path(commitment), type: "button" %></td>
<% end %>
At controller:
def toggle_check
#c = Commitment.find(params[:id])
#c.toggle!(:check)
redirect_to commitments_path
end
Thanks for your help. I've did a lot of research and was not able to figure this out by myself.
After page refresh DOM has change, so your cod will not work.
Try:
$('html').find('.index_edit_comments').each(function() {
$(this).find(".index_edit_comments_input").blur(function() {
$(this).submit();
});
});
If refresh change all DOM html, my code example will not help.

How do insert a custom "not found message" on my search function using Javascript in Rails?

I've been able to implement a search function using AJAX in Rails but when a user types in a search query and doesn't find anything the result currently displays nothing. Instead of this, I want to display a message such as: 'Sorry nothing was found!', but I can't seem to get this to work.
This is my code from the index.js.erb file:
$('#products').append('<%= escape_javascript render(#products) %>');
$('.pagination').replaceWith('<%= escape_javascript paginate(#products) %>');
This is the code in my index.html.erb file:
<%= form_tag products_path, method: :get, authentication: false, id: 'search-form' do %>
<%= text_field_tag :search, params[:search] %>
<%= submit_tag "Search" %>
<% end %>
<div class="small-block-grid-2 medium-block-grid-3 large-block-grid-3" id="products">
<% #products.each do |product| %>
<%= render product %>
<% end %>
</div>
And finally this is the bit of relevant code in my products_controller.rb file:
respond_to :html, :js, :json
def index
#products = if params[:search].present?
Product.where("name LIKE ?", "%#{params[:search]}%")
else
Product.all
end
#products = Product.order(created_at: :desc).page(params[:page]).per(13)
respond_with #products
end
def search
#products = Product.where("name LIKE ?", "%#{params[:search]}%")
render #products
end
Sorry I should've included this in the question as well. Now I am able to get this message of "Sorry nothing was found!" message to display when a search query comes up with nothing, BUT the problem is that I have to press enter twice in order for it to display the message. What can I do to change my code here so when nothing is found the first time it displays: "Sorry nothing was found!"?
This is the code in my assets/javascripts/products.js file:
$(document).ready(function(){
$('#search-form').submit(function(event){
event.preventDefault();
var searchValue = $('#search').val();
$.get('/products/search?search='+searchValue)
.done(function(data){
console.log(data);
if ($("#products").children().length != 0){
$('#products').html(data);
}else{
$('#products').html("<h3>Sorry nothing was found!</h3>");
}
});
});
});
I believe that's all the necessary information needed to understand this question. If you need more clarification in order to understand what I'm doing, I'll be glad to provide it.
Thank you all for your input!

Categories