Rails: Delete nested attribute while updating entry - javascript

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');
})
})

Related

refresh page once checkbox is checked (in the background)

How can I refresh the page (in the background, seamlessly) when a user checks a checkbox so that once the checkbox is checked off, that div or task is then moved to another location because it's completed?
I have separated them like so
def home
if current_user
#todos = current_user.todos.where(completed: false)
end
end
def complete
if current_user
#todos = current_user.todos.where(completed: !false)
end
end
So that once the checkbox is checked it is moved, and this works - but the page has to be refreshed to see that the task has been moved.
In my todos controller where I mark the task complete it looks like this
def completed
if #todo.update_attribute(:completed, !#todo.completed)
flash[:success] = "Congratulations, it was successful."
redirect_to dashboard_path
else
flash.now[:error] = "ERROR: Please try again."
render :new
end
end
And my view is as so
<% #todos.each do |todo| %>
<div class="card hoverable">
<div class ="card-content mh-100">
<span class="card-title"><%= todo.title %></span>
<p><%= todo.item %></p>
</div>
<div class="card-action grey lighten-5">
<p style="margin: 0;">
<%= check_box_tag 'todo[completed]', todo.id, todo.completed, data: { remote: true, url: url_for(controller: :todos, action: :completed, id: todo), method: "POST" }, id: todo.id, :onclick => "Materialize.toast('Todo Completed, Grats!', 4000)" %>
<%= label_tag todo.id, "COMPLETE", :class => 'strikethrough' %>
</p>
</div>
</div>
<% end %>
But how can I refresh the page, or the div when the checkbox is checked so the task disappears seamlessly?
Give your div a unique ID based on the todo id value so that you can select it for removal:
<div class="card hoverable" id="todo_container_<%= todo.id %>">
Add a class to your check box so that you can specify a click handler:
check_box_tag 'todo[completed]', todo.id, todo.completed, class: 'todo_completed', data: { remote: true, url: url_for(controller: :todos, action: :completed, id: todo), method: "POST" }, id: todo.id, :onclick => "Materialize.toast('Todo Completed, Grats!', 4000)"
Then (assuming you are using jQuery) specify the click handler:
$(".todo_completed").click(function(){
// it looks like you've got all the info you need for your ajax call in the data attributes attached to the checkbox, so I think the call just looks like this:
$.ajax($(this).data());
});
Finally your controller needs to render a .js template instead of redirecting - just call it completed.js.erb and it should be rendered automatically. In that template put the javascript to remove the container div from the DOM:
// completed.js.erb
$("todo_container_<%= #todo.id %>").remove();
Use removeChild() with the div id to remove it, just put this in an onClick handler for the checkbox.
If desired:
Save the div when you delete it (from the return value of removeChild) in a variable, so if the checkbox is unchecked, you can use the div in createChild().

Trying to create a form that automatically submits and reloads page upon change

I would like a dropdown box and as a user selects a new option from it, it should automatically save (without a submit button) and the page should reload.
As it is, however, selecting a value from the dropdown box does not do anything: it does not save nor reload the page. Anyone got an idea how to get the code below to work?
Form:
<%= form_for #user, method: :patch, url: set_preference_path do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.collection_select :default_relationship_id, #organizations, :id, :name, {onchange: "this.form.submit();"}, {class: 'form-control input-md'} %>
# default_relationship_id is the column in users table to save to, #organizations are the selectable options, id is the value of the options to save, and name is the name to be displayed.
<% end %>
Does onchange: "this.form.submit();" perhaps not work when using collection_select? The examples from which I adapted this implementation (stackoverflow.com/a/24315219 and stackoverflow.com/a/17663373) use it in combination with select instead of collection_select.
Controller:
def overview
#user = current_user
#organizations = #user.organizations.where('fish = ?', true)
end
def set_preference
#user = current_user
#rel = Relationship.where('user_id = ? and organization_id = ? and fish = ?', #user.id, params[:user][:default_relationship_id], true).first
#user.update_attributes(default_relationship_id: #rel.id)
respond_to do |format|
format.js { render inline: "location.reload();" }
format.html { redirect_to overview_path(#user) }
end
end
The html code this produces:
<form class="edit_user" id="edit_user_1" action="/set_preference/test1" accept-charset="UTF-8" method="post"><input name="utf8" type="hidden" value="&#x2*3;" /><input type="hidden" name="_method" value="patch" /><input type="hidden" name="authenticity_token" value="m/9ECijmnYQ==" />
<select class="form-control input-md" name="user[default_relationship_id]" id="user_default_relationship_id">
<option value="1">Example1 business</option>
<option value="56">Example2</option>
</form>
You're specifying :onclick in the options hash instead of the html_options hash (where :class is specified), so move it to the correct hash, but keep the options hash blank:
f.collection_select :default_relationship_id, #organizations, :id, :name, {}, {class: 'form-control input-md', onchange: "this.form.submit();"}
See the documentation for the the full arguments list.
ETA The chosen value of #user.default_relationship_id isn't selected by collection_select because it refers to a relationship, but you've given the collection #organizations. Since collection_select can't find the associated organization from a relationship ID, it selects the first option by default. You should refactor so collection_select receives a #relationships collection. The refactoring should be such that you can update directly from strong params:
set_preference
…
#user.update user_relationship_params
…
end
private
def user_relationship_params
params.require(:user).permit(:default_relationship_id)
end
Read up on form helpers for more information.
With jQuery, try to handle onchange event and try ajax post
Javascript snippet:
/**
* Assume jQuery script on head
*/
<script type="text/javascript">
/**
* Handle onchange on user_default_relationship_id
*/
$("#user_default_relationship_id").change(function () {
var data = $( this ).form.serialize();
$.ajax({
type: "POST",
url: <%= set_preference_path %>,
data: data,
dataType: "application/json"
})
.done(function() {
// handle done event ...
})
.fail(function() {
// handle fail event ...
})
.always(function() {
// handle event ...
});
</script>

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

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

How to trigger javascript from Rails Controller?

In Rails 3, I want to trigger a javascript that displays a pop-up after the user clicks a link to join a group which determines if a user is eligible for joining (controller action determines if they are eligible).
I need to pass the id to the controller, receive the message from the controller, and then trigger the javascript using the message.
I can't get the javascript to trigger. Can someone please help me figure out what I'm doing wrong? I think it's in the link in the view.
controller action:
def join
#group = Group.find(params[:id])
if ineligible
#message = "Sorry, you can't join."
elsif current_user
if !#group.already_joined(current_user)
#message = GroupUser.create(#group,current_user,nil,nil)
else
#message = "You're already registered for this group"
end
else
#message = "If you're already a user, but not a member of this group, please login, and then click the join link again. If you aren't a user yet, please join!"
end
end
javacript in header
<%= javascript_include_tag "jquery" %>
<script>
$(#message).msg('<% #message %>', {
type : 'confirm'
});
</script>
view
<%= link_to 'Join',{:onclick => {:controller => 'groups', :action => 'join', :id => #group.id} }%>
<div id="message"></div>
Try to wrap your #message selector in quotes:
<%= javascript_include_tag "jquery" %>
<script>
$('#message').msg('<% #message %>', {
type : 'confirm'
});
</script>

Hiding a link based on drop down list value selected

I need to write some jquery which will read what the current value in a drop down list is and then hides a particular link on the page.
The dropdown list is a locale selector. So when I select "english" I want the link to be hidden but when select French the link has to remain.
Can you show how i can do this? I want learn this. Thanks
How about this:
function handleLang() {
if ($('#select').val() == 'en') {
$('#link').hide();
} else {
$('#link').show();
}
}
$(function() {
// hide/show on load
handleLang();
// hide/show when the language is changed (useful if the page doesn't reload)
$('#select').change(handleLang);
});
Based on the first answer provided for this question and more hints provided by the author of the first answer in the comments, here is the final (and compact) full version of the code that worked for me (I also removed and styling since was no longer required). This is a main page provided in the index.html.erb for this test application.
<h2 id='main_header'>Showing/hiding link based on dropdown selection</h2>
<table>
<thead>
<th>Action</th>
<th>Skip assessment</th>
<th>Reason for skipping</th>
</thead>
<tbody>
<tr>
<td>
<%= link_to 'Start asseement', url_for(controller: :home, action: :start_assessment), id: 'start_assessment' %>
</td>
<td>
<%= select_tag "skip_option", options_for_select([ "Yes", "No" ], "No"), id: "skip_option", onchange: "reason_picker()" %>
</td>
<td>
<%= text_field_tag "reason_for_skipping", nil, size: 50, placeholder: 'Enter reason here...' %>
</td>
</tr>
</tbody>
</table>
<script type="text/javascript">
$(document).ready (
window.reason_picker = function () {
var selected = document.getElementById("skip_option").selectedIndex;
if (selected == 0) {
$("#reason_for_skipping").show();
$("#start_assessment").hide();
}
else {
$("#reason_for_skipping").hide();
$("#start_assessment").show();
}
}
);
</script>
The start assessment page is very basic, just a title and back link to the main page contained in the start_assessment.html.erb file:
<h1>Assessment</h1>
<%= link_to 'Back', :back %>
I this is of any help, here are the controller home_controller.rb file:
class HomeController < ApplicationController
def index
end
def start_assessment
end
end
And the route route.rb file is:
Rails.application.routes.draw do
root controller: 'home', action: :index
get 'start_assessment' => 'home#start_assessment'
end

Categories