Checkbox doesn't take using Ajax - ruby-on-rails

I have a Boolean field called :active and is changeable via Ajax. I have it working for unchecking the box, thereby making the boolean FALSE, or moreover, in the database it removes the attribute making it empty, but works nonetheless.
My problem is it does not work in reverse. I can check the box to make the boolean TRUE and it appears that it is doing something, but it does not actually make a change in the database.
The output in Webbrick shows it updated:
Processing by CompaniesController#toggle as JS
Parameters: {"Active"=>"228", "id"=>"228"}
SQL (0.5ms) UPDATE "companies" SET "active" = $1, "updated_at" = $2
WHERE "companies"."id" = $3 [["active", nil],
["updated_at", 2017-02-15 17:26:19 UTC], ["id", 228]]
(0.8ms) COMMIT
But the database didn’t update. I see where it says [[“active, nil] above, and that is the part that is not right. So technically the update is working, but I’m pretty sure my controller is why it is sending a nil on recheck.
So, how do I send a boolean TRUE in my controller, if that is indeed where I should do it.
companies_controller.rb
def toggle
#company = Company.find(params[:id])
if #company.update_attributes(active: params[:active])
# todo: maybe give a notice
else
# todo: maybe give a notice
end
end
index.html.rb
<%= check_box_tag 'Active', company.id, company.active,
data: {
remote: true,
url: url_for(action: :toggle, id: company.id),
method: "POST"
} %>
routes.rb
resources :companies do
resources :comments
member do
post 'toggle'
end
end
Edit
I got it to work by changing my controller to use an if statement. Not sure if this is the best approach, but it does work in both directions now.
companies_controller.rb
def toggle
#company = Company.find(params[:id])
if #company.active
#company.update_attributes(active: FALSE)
else
#company.update_attributes(active: TRUE)
end
end

You don't really need a separate route. Rather you can just send a PATCH request to your existing route:
<% form_for(#company) do |f| %>
<%= f.check_box :active, class: "company-toggle-active" %>
<% end %>
Make sure the controller handles JSON:
class CompaniesController < ApplicationController
# ...
def update
#company = Company.find(params[:id])
if #company.update(company_attributes)
respond_to do |f|
f.json { head :ok }
f.html { redirect_to #company }
end
else
respond_to do |f|
f.json { json: { errors: #company.errors }, status: 422 }
f.html { render :new }
end
end
end
private
def company_attributes
params.require(:company).permit(:foo, :bar, :active)
end
end
We can then setup an handler for the change event that updates with ajax:
$(document).on('change', '.company-toggle-active', function(){
$.ajax({
url: this.form.action,
type: 'PATCH',
dataType: 'json',
data: {
company: {
active: $(this).is(':checked') ? 'true' : 'false'
}
}
}).done(function(data, textStatus, jqXHR){
// do something if request is successful
}).fail(function(data, textStatus, jqXHR){
// do something if request fails
});
});

Related

Rails ajax ActionController::Parameters permitted: false

I'm trying to do a Ajax call that return all of my reviews when click on a link. When I click on the link I'm calling to a method of User model passing a parameter and I receiving this error <ActionController::Parameters {"controller"=>"users", "action"=>"show_all_reviews"} permitted: false>
My user_controller:
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
#my_reviews = #user.my_reviews.where.not(comment: [nil, ""])
#my_reviews = #my_reviews.paginate(:page => params[:page], :per_page => 1)
#friends = #user.get_friends_list
end
def show_all_reviews
#user = User.find(params[:user_id])
#my_reviews = #user.my_reviews.where.not(comment: [nil, ""])
end
private
def user_params
params.require(:user).permit(:description, :phone)
end
end
That's my button that do the Ajax call
<%= link_to 'Mostrar todos los comentarios', '#', remote: true, id: 'show_more_link', data: {user: #user.id} %>
And my jquery function:
$('#show_more_link').on('click', function(event) {
event.preventDefault();
var user_id = $(this).data("user");
console.log(user_id);
$.ajax({
url: "/show_all_reviews",
type: "POST",
data: {
"user_id": user_id,
},
dataType: "json",
success: function(data) {
alert('done');
}
});
});
I add this to routes.rb
get '/show_all_reviews', to: 'users#show_all_reviews', as: :show_all_reviews
You don't need to use $.ajax(). This can be done in simple way: -
Route: -
get '/show_all_reviews/:user_id', to: 'users#show_all_reviews', as: :show_all_reviews
Add path to link_to including remote: true
<%= link_to 'Mostrar todos los comentarios', show_all_reviews_path(user_id: #user.id), remote: true, id: 'show_more_link' %>
You made a mistake. Change type to GET inside your ajax code. As I see in your routes the custom action with GET type.
Or you can also modify approach here. Use namespace.
in your routes.rb add:
namespace :users, path: nil, as: nil do
resources :users, only: [] do
resources :reviews, only: :index
end
end
create new folder under controllers /users. Add new controller there:
controllers/users/reviews_controller.rb
class Users::ReviewsController < ApplicationController
def index
#user = User.find(params[:user_id])
#reviews = #user.my_reviews.where.not(comment: [nil, ""])
render json: #reviews
end
end
inside view file:
<%= link_to 'reviews', user_reviews_path(user), remote: true %>

Show Associated Model Data with React Rails

I'm using the react-rails gem and have two models: Message and User. User has_many :messages.
In my message.js.jsx, I'd like to show the User of that message. In regular erb, it'd just be <%= message.user.name %>. How would I do this in the message.js.jsx component?
You could rename your component to message.js.jsx.erb and use ERB in it, but it will only be compiled once when Rails starts up.
A more React-ish way to handle is to AJAX load the user data in componentDidMount (or a Store, if using Flux).
message.js.jsx
getInitialState: function() {
return { user: { name: '' } };
},
componentDidMount: function() {
$.getJSON('/users/'+ this.props.id +'.json', function(userData) {
if (this.isMounted()) {
this.setState({ user: userData })
}
});
},
You can create a Rails endpoint to return userData as JSON something like this:
users_controller.rb
def show
#user = User.find(params[:id])
respond_to do |format|
format.html # default html response
format.json { render json: #user.to_json(only: [:id, :name]) }
end
end
See Facebook's page on this for more details
I agree with Unixmonkey that is the react way. You can also do it a few more ways.
#user = JSON.parse user.to_json(include: [:messages], only: [:id, :name])
As well as using componentDidMount to hit a JSON endpoint using jbuilder which you can put a timeout on if you want to update dynamically.
componentDidMount: function() {
$.getJSON('/users/'+ this.props.id +'.json', function(user) {
if (this.isMounted()) {
this.setState({ user: user })
}
});
},
Your show.json.jbuilder under user views would look something like this:
json.id #user.id
json.name #user.name
json.messages #user.messages do |message|
json.id message.id
json.content message.content
json.created_at message.created_at
end

Using AJAX with two select boxes in Rails 4

I have a view containing two select boxes: company and employee. Both have a blank option and when a company is selected, it populates the employees based on the selected company. This works just fine. My issue is that when I submit a form that fails validation (as expected) and I select a company once more once the 'new' view renders again in extensions#create, my 'get' AJAX call has changed from /servers/1/extensions/get_company_employees (correct) to /servers/1/get_company_employees (incorrect) and is returning 404 Not found. Why is this happening and what should I do to remedy this? All relevant code is listed below
routes.config
resources :servers do
scope module: 'servers' do
resources :extensions, shallow: true
end
end
# Ajax call
get 'servers/:id/extensions/get_company_employees', to: 'servers/extensions#get_company_employees', as: 'get_company_employees'
app/controllers/servers/extensions_controller.rb
class Servers::ExtensionsController < ApplicationController
def get_company_employees
#server = Server.find(params[:id])
#extension = #server.extensions.build
#path = [#server, #extension]
#companies = Company.all
#employees = Employee.where("company_id = ?", params[:company_id])
respond_to do |format|
format.js
end
end
def new
#server = Server.find(params[:server_id])
#extension = #server.extensions.build
#path = [#server, #extension]
#companies = Company.all
#employees = Employee.none
end
def create
#server = Server.find(params[:server_id])
#extension = #server.extensions.build(extension_params)
#extension.password = "pass"
if #extension.save
flash[:success] = "Successfully created extension"
redirect_to #extension
else
#path = [#server, #extension]
#companies = Company.all
#employees = Employee.none
flash.now[:error] = "Failed to create extension"
render "new"
end
end
private
def extension_params
params.require(:extension).permit(:value, :password, :employee_id, :server_id, :phone_id)
end
end
app/views/servers/extensions/_form.html.erb
<%= form_for(#path) do |f| %>
<p>
<%= label_tag(:company) %>
<%= select_tag "company", options_from_collection_for_select(#companies, "id", "name"), include_blank: "Select a company" %>
</p>
<p>
<%= f.label(:employee) %>
<%= f.collection_select :employee_id, #employees, :id, :full_name, include_blank: "Select an employee" %>
</p>
<p>
<%= f.submit "Submit" %>
</p>
<% end %>
app/views/servers/extensions/get_company_employees.js.coffee
$("#extension_employee_id").empty()
.append("<option>Select an employee</option>")
.append("<%= j options_from_collection_for_select(#employees, :id, :full_name) %>")
app/assets/javascripts/servers/extensions.coffee
$ ->
$(document).on 'page:load', '#company', (evt) ->
$.ajax 'get_company_employees',
type: 'GET'
dataType: 'script'
data: {
company_id: $("#company option:selected").val()
}
$(document).on 'change', '#company', (evt) ->
$.ajax 'get_company_employees',
type: 'GET'
dataType: 'script'
data: {
company_id: $("#company option:selected").val()
}
Its because you have now specified complete URL in ajax call
It should be something like this in both cases.
$.ajax "/servers/"+ id +"/extensions/get_company_employees',
type: 'GET'
dataType: 'script'
data: {
company_id: $("#company option:selected").val()
}
// store and fetch id attribute from page in any of the dom element
Ideally you should write a function for your ajax call which can be called wherever required and code redundancy can be reduced.

Multiple Select Tag in Rails

Im trying to implement a multiple level drop down list in Rails
I have three Tables in my DB.
vehicle_make.rb
class VehicleMake < ActiveRecord::Base
validates_uniqueness_of :make
has_many :appointments
end
vehicle_model.rb
class VehicleModel < ActiveRecord::Base
validates_uniqueness_of :model
has_many :appointments
end
vehicle_make_model.rb
class VehicleMakeModel < ActiveRecord::Base
validates_uniqueness_of :vehicle_make_id, :scope => :vehicle_model_id
end
and im trying to implement a multiple dropdown list in appointments.html.rb
on selecting the vehicle model only corresponding make should load..
<%= f.select :vehicle_make_id, options_for_select(vehicle_make.map {|s| [s.make, s.id]}, appointment.vehicle_make_id), {}, {class: "form-control"} %>
and in my js i have..
$('#appointment_vehicle_make_id').on('change', function() {
var vehicle_make_id = this.value;
$.ajax({
url : '/appointments/update_models',
type : 'GET',
data : {
make_id : vehicle_make_id
},
success : function(response) {
console.log(response);
}
});
});
and this is my controller method.
def update_models
#vehicle_models = VehicleModel.all
#model_ids = []
#selected_vehicle_models = VehicleMakeModel.where(vehicle_make_id: params[:make_id]).order(:vehicle_model_id) unless params[:make_id].blank?
#selected_vehicle_models.each do |t|
#model_ids << t.vehicle_model_id
end
respond_to do |format|
format.html { render layout: false }
format.js
end
end
I have html page named update_models.html.erb associated to the above action.
<%= select_tag :vehicle_model_id, options_for_select(#model_ids.map {|s| [s.model, s.first.id]}, nil), {}, {class: "form-control"} %>
I get an error in terminal saying
ActionView::Template::Error (wrong number of arguments (4 for 1..3)):
1: <%= select_tag :vehicle_model_id, options_for_select(#model_ids.map {|s| [s.model, s.first.id]}, nil), {}, {class: "form-control"} %>
Im stuck here. I dont know how to proceed from here.. please help
In your controller action update_models, you are trying to render js, so it's trying to find template named as update_models.js.erb.
You can try replacing your respond_to block with:
respond_to do |format|
format.json { render :json => #model_ids }
end
Afterwards, you will need to parse this data in your ajax success callback

select2 AJAX'd to a model, configuration

So, I just discovered select2. Awesome. Now I'm trying to figure out how to use it, server side with ajax / json. All of the examples I see, everywhere, show using select2 with JSONP to retrieve data from an external source. I feel like this should be even easier if calling from a local model, no? I'll get right to the nitty gritty. json returns a value, but the searchbox doesn't autocomplete, it stays blank.
view html:
<%= form_tag request_pal_path, remote: true do %>
<%= hidden_field_tag :email, nil, class: 'ui-corner-all' %>
<%= submit_tag "Send request", class: 'button' %>
<% end %>
and calling some js on it:
$(document).ready(function() {
$("#find_user #email").select2({
width: '400px',
placeholder: "Find user...",
minimumInputLength: 1,
multiple: false,
id: function(obj) {
return obj.id; // use slug field for id
},
ajax: { // instead of writing the function to execute the request we use Select2's convenient helper
url: "/users",
dataType: 'json',
data: function (term, page) {
return {
q: term, // search term
page_limit: 10
};
},
results: function (data, page) { // parse the results into the format expected by Select2.
// since we are using custom formatting functions we do not need to alter remote JSON data
return {results: data};
}
},
formatResult: FormatResult,
formatSelection: FormatSelection,
escapeMarkup: function (m) { return m; }
});
})
function FormatResult(user) {
return '<div>' + user.name + '</div>';
}
function FormatSelection(user) {
return user.name;
}
which goes to the controller, user index action:
def index
#find = User.where('name LIKE ?', "%#{params[:q]}%")
#users = #find.where('id NOT IN (?)', current_user.id).order('random()').page(params[:page]).per(100)
#title = "Potential pals"
respond_to do |format|
format.html
format.js {
#find = #find
#users = #users
}
format.json { #find }
end
end
and I made a .json file for it to respond to (not sure if this is even necessary):
<% #find.each do |user| %>
<%= user.name %>
<% end %>
So, the json is working, to an extent. If I look in the developer console, it shows a response coming from http://localhost:3000/users.json?q=tay, or whereever, and it returns a single value, for Taylor (in that instance). But when I search inside of the select2 search box, it just spins and spins, with no results. No console errors, so that's nice, ha. Thoughts? Thanks!
The select2 plugin expects JSON data in the following format:
[ { "text": "Taylor", "id": 1 }, { "text" : "Tailor", "id": 2 }, ...]
So you need to replace name with text in your user model when converting to JSON:
def as_json(*args)
super.tap { |hash| hash["text"] = hash.delete "name" }
end
and then in the index method:
def index
#find = User.where('name LIKE ?', "%#{params[:q]}%")
#users = #find.where('id NOT IN (?)', current_user.id).order('random()').page(params[:page]).per(100)
#title = "Potential pals"
respond_to do |format|
format.html
format.js {
#find = #find
#users = #users
}
format.json { render json: #find, :only => [:text, :id] } # might be :name here ?
end
end
and you don't need the view for JSON.
I guess the problem is in your .json file, since select2 needs json array or json object. Try to remove it and respond with format.json { render json: #find.to_json }. Other code seems ok to me.

Resources