Rails ajax ActionController::Parameters permitted: false - ruby-on-rails

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 %>

Related

Checkbox doesn't take using Ajax

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

Rails 4.2.7 & Devise 4.1.1 - Change password with current password required in user preferences page with AJAX

My application has three different user types (i.e. Devise models): SuperAdminUser, AdminUser and StandardUser. Every user, regardless of type, has a preferences page where they will be able to update a number of attributes (e.g. :email, :password, :username, etc.), with two of the attributes requiring the user to enter their current password to update (:email, :password), while the remaining attributes will not require the current password to update.
Now, in my ideal world, the preference updates will utilize AJAX so that only the attribute changed will refresh in the page rather than an entire page reload. Currently I have this working for all of the attributes that don't require the user to enter their current password, but I've been unable to get it to work with :email and :password. Oddly enough, I have it working in a previous prototype, but that had only one Devise user model, not three, and that single model didn't have it's own registration controller overriding the Devise registration controller, which the three user models in the current app do, although I don't see anything in these overriding registration controllers that would be impacting the issue here.
The only error message I'm getting is my own, on the preferences page:
Sorry, but we were unable to update your password.
The problem appears to be that the :current_password param value is not being captured from the form. The console log statement shows it as a blank string, while the :new_password and :new_password_confirmation values are captured without problem. I haven't been able to determine why this is happening. As an alternate approach, I also tried to mimic as closely as I could the change password example in the Devise wiki (the last example) (here: https://github.com/plataformatec/devise/wiki/How-To:-Allow-users-to-edit-their-password), but to no avail. Any suggestions would be greatly appreciated.
Additionally, I guess an important question to ask is if should I be using AJAX with the password attribute. That is, is it inherently insecure passing these password values via AJAX? If so, is there a way to do it securely (e.g. encrypted)?
Here is my relevant code:
app/controllers/super_admin_users/preferences_controller.rb
class SuperAdminUsers::PreferencesController < ApplicationController
include ApplicationHelper
before_action :authenticate_super_admin_user!
.
.
.
def change_password
#super_admin_user = SuperAdminUser.find_by(id: current_super_admin_user.id)
if resource.update_with_password(params.permit(:password,
:password_confirmation,
:current_password))
sign_in resource_name, self.resource, bypass: true
status_message = 'Your password has been updated successfully!'
else
status_message = 'Sorry, but we were unable to update your password.'
end
respond_to do |format|
format.json { render json: { status_message: status_message } }
end
end
def password_section_partial
#change_password_status_message = params[:change_password_status_message] || ""
render 'super_admin_users/preferences/password/password_section_partial'
end
.
.
.
end
end
app/views/super_admin_users/preferences/password/_change_password_form.html.haml
= form_tag super_admin_user_prefs_change_password_path, id: 'prefs-change-password-form' do
.prefs-label-and-input-wrapper
#prefs-new-password-label-wrapper.prefs-input-label-wrapper
= label_tag 'password', 'New Password', id: 'prefs-new-password-label',
class: 'prefs-input-label'
#prefs-new-password-input-wrapper.prefs-input-wrapper
= password_field_tag 'password', nil, autofocus: true,
autocomplete: 'off',
id: 'prefs-new-password-input',
class: 'prefs-input'
.prefs-label-and-input-wrapper
#prefs-new-password-confirmation-label-wrapper.prefs-input-label-wrapper
= label_tag 'password_confirmation', 'New Password Confirmation', id: 'prefs-new-confirmation-password-label',
class: 'prefs-input-label'
#prefs-new-password-confirmation-input-wrapper.prefs-input-wrapper
= password_field_tag 'password_confirmation', nil, autocomplete: 'off',
id: 'prefs-new-password-confirmation-input',
class: 'prefs-input'
.prefs-label-and-input-wrapper
#prefs-current-password-label-wrapper.prefs-input-label-wrapper
= label_tag 'current_password', 'Current Password', id: 'prefs-current-password-label',
class: 'prefs-input-label'
#prefs-current-password-input-wrapper.prefs-input-wrapper
= password_field_tag 'current_password', nil, autocomplete: 'off',
id: 'prefs-current-password-input',
class: 'prefs-input'
#prefs-change-password-form-buttons-wrapper
#prefs-change-password-form-submit-btn-wrapper
= link_to 'Update', 'javascript:;', id: 'prefs-change-password-form-submit-btn', class: 'btn btn-sm btn-success btn-submit'
-#%input{ id: 'prefs-change-password-form-submit-btn', class: 'btn btn-sm btn-success', type: 'submit', value: 'Update' }
#prefs-change-password-form-cancel-btn-wrapper
= link_to 'Cancel', 'javascript:;', id: 'prefs-change-password-form-cancel-btn', class: 'btn btn-sm btn-secondary btn-cancel'
app/views/super_admin_users/preferences/password/_change_password_status_message.html.haml
#prefs-change-password-status-message.prefs-status-message= change_password_status_message
app/views/super_admin_users/preferences/password/_password_section.html.haml
#prefs-password-section-header.prefs-section-header
#prefs-password-headline-wrapper
.prefs-section-headline Password
#prefs-password-edit-btn-wrapper
= link_to 'Edit', 'javascript:;', id: 'prefs-password-edit-btn', class: 'btn btn-sm btn-primary btn-edit'
#prefs-change-password-form-wrapper.prefs-form-wrapper.no-display
= render partial: 'super_admin_users/preferences/password/change_password_form'
#prefs-change-password-status-message-wrapper.prefs-status-message-wrapper.no-display
app/views/super_admin_users/preferences/password/password_section_partial.js.haml
$('#prefs-change-password-status-message-wrapper').empty();
$('#prefs-change-password-form-wrapper').addClass('no-display');
$('#prefs-change-password-form').trigger('reset');
- if #change_password_status_message.present?
$('#prefs-change-password-status-message-wrapper').append("#{ escape_javascript(render(partial: 'super_admin_users/preferences/password/change_password_status_message', locals: { change_password_status_message: #change_password_status_message })) }");
$('#prefs-change-password-status-message-wrapper').removeClass('no-display');
$('#prefs-password-edit-btn').removeClass('no-display');
app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
include ApplicationHelper
include DeviseParamSanitizerOverrides
def get_current_user_type
respond_to do |format|
format.json { render json: { current_user_type: resource_name.to_s } }
end
end
def get_current_environment
respond_to do |format|
format.json { render json: { current_environment: Rails.env } }
end
end
protected
def devise_parameter_sanitizer
if resource_class.to_s == 'SuperAdminUser'
SuperAdminUser::ParameterSanitizer.new(SuperAdminUser, :super_admin_user, params)
elsif resource_class.to_s == 'AdminUser'
AdminUser::ParameterSanitizer.new(AdminUser, :admin_user, params)
elsif resource_class.to_s == 'StandardUser'
StandardUser::ParameterSanitizer.new(StandardUser, :standard_user, params)
end
end
end
app/controllers/concerns/devise_param_sanitizer_overrides.rb
module DeviseParamSanitizerOverrides
extend ActiveSupport::Concern
class SuperAdminUser::ParameterSanitizer < Devise::ParameterSanitizer
def initialize(*)
super
permit(:sign_up, keys: [:email, :first_name, :last_name, :username])
permit(:sign_in, keys: [:email, :username])
permit(:account_update, keys: [:current_password, :email, :first_name, :last_name, :time_zone, :username])
end
end
.
.
.
end
app/helpers/application_helper.rb
module ApplicationHelper
def resource
if super_admin_user_signed_in?
#super_admin_user ||= SuperAdminUser.new
elsif admin_user_signed_in?
#admin_user ||= AdminUser.new
elsif standard_user_signed_in?
#standard_user ||= StandardUser.new
end
end
def resource_name
if super_admin_user_signed_in?
:super_admin_user
elsif admin_user_signed_in?
:admin_user
elsif standard_user_signed_in?
:standard_user
end
end
def resource_class
if super_admin_user_signed_in?
SuperAdminUser
elsif admin_user_signed_in?
AdminUser
elsif standard_user_signed_in?
StandardUser
end
end
def devise_mapping
if super_admin_user_signed_in?
#devise_mapping ||= Devise.mappings[:super_admin_user]
elsif admin_user_signed_in?
#devise_mapping ||= Devise.mappings[:admin_user]
elsif standard_user_signed_in?
#devise_mapping ||= Devise.mappings[:standard_user]
end
end
def resource_authenticated_root
if super_admin_user_signed_in?
authenticated_super_admin_user_root
elsif admin_user_signed_in?
authenticated_admin_user_root
elsif standard_user_signed_in?
authenticated_standard_user_root
end
end
end
app/assets/javascripts/preferences.js
var currentUserType;
var currentEnvironment;
getCurrentUserType('/get_current_user_type'
).done(function(getCurrentUserTypeResponse) {
currentUserType = getCurrentUserTypeResponse.current_user_type;
});
getCurrentEnvironment('/get_current_environment'
).done(function(getCurrentEnvironmentResponse) {
currentEnvironment = getCurrentEnvironmentResponse.current_environment;
});
$(document).ready(function() {
$('#prefs-password-edit-btn').click(function (e) {
$('#prefs-password-edit-btn').addClass('no-display');
$('#prefs-change-password-form').trigger('reset');
$('#prefs-change-password-form-wrapper').removeClass('no-display');
});
$(function() {
return $('body').on('click', '#prefs-change-password-form-submit-btn', function() {
$('#prefs-change-password-form-wrapper').addClass('no-display');
changePassword('/' + currentUserType + 's/preferences/change_password?password=' + $('#prefs-new-password-input').val() +
'&password_confirmation=' + $('#prefs-new-password-confirmation-input').val() +
'&current_password=' + $('#prefs-current-password-input').val()
).done(function(changePasswordResponse) {
$.ajax({url: '/' + currentUserType + 's/preferences/password_section_partial?change_password_status_message=' + changePasswordResponse.status_message});
});
});
});
$(function() {
return $('body').on('click', '#prefs-change-password-form-cancel-btn', function() {
$('#prefs-change-password-form-wrapper').addClass('no-display');
$('#prefs-change-password-form').trigger('reset');
$('#prefs-password-edit-btn').removeClass('no-display');
});
});
function getCurrentUserType(url) {
return $.ajax({
url: url,
type: 'get',
dataType: 'json'
})
.fail(function() {
if (currentEnvironment === 'development') {
alert('AJAX Get Current User Type Error');
}
})
}
function getCurrentEnvironment(url) {
return $.ajax({
url: url,
type: 'get',
dataType: 'json'
})
.fail(function() {
alert('AJAX Get Current Environment Error');
})
}
function changePassword(url) {
return $.ajax({
url: url,
type: 'get',
dataType: 'json'
})
.fail(function() {
if (currentEnvironment === 'development') {
alert('AJAX Change Password Error');
}
})
}
config/routes.rb
Rails.application.routes.draw do
get '/get_current_user_type', to: 'application#get_current_user_type'
get '/get_current_environment', to: 'application#get_current_environment'
.
.
.
devise_for :super_admin_users, controllers: { registrations: 'super_admin_users/registrations' }
authenticated :super_admin_user do
root to: 'super_admin_users/dashboard#index', as: 'authenticated_super_admin_user_root'
end
as :super_admin_user do
get 'super_admin_users/preferences/password_section_partial', to: 'super_admin_users/preferences#password_section_partial',
as: :super_admin_user_password_section_partial
get 'super_admin_users/preferences/change_password', to: 'super_admin_users/preferences#change_password',
as: :super_admin_user_prefs_change_password
end
end

Angular + Rails + MongoDB(mongoid) delete function won't work

I have an Angular app with a rails back end and using the mongoid gem as a database. I'm trying to make a delete function, but I can't get it to work.
I get
No route matches [DELETE] "/api/tags"
I can't seem to get the ID. I thought I was doing that in the factory. Otherwise, I don't know how to get it.
My code:
angular:
var myApp = angular.module('tagsapp', ['ngRoute', 'ngResource']);
myApp.factory("Tag", function($resource) {
return $resource("/api/tags/:id", { _id: "#id"},
{
'create': { method: 'POST' },
'index': { method: 'GET', isArray: true },
'show': { method: 'GET', isArray: false },
'update': { method: 'PUT' },
'destroy': { method: 'DELETE' }
}
);
})
// Controllers
myApp.controller("TagListCtrl", ['$scope', '$resource', 'Tag', '$location',
function($scope, $resource, Tag, $location) {
$scope.tags = Tag.query();
$scope.saveNewTag = function() {
Tag.create({ tag: $scope.newTag }, function(response){
$scope.tags.push(response);
$scope.newTag = null;
});
}
$scope.deleteTag = function(tagId) {
Tag.destroy({ _id: tagId }, function(response){
var index = $scope.tags.indexOf(tagId);
$scope.tags.splice(index, 1)
$location.path('/')
})
};
}]);
and my rails controller:
class TagsController < ApplicationController
before_action :set_tag, only: [:show, :edit, :update, :destroy]
respond_to :json
def index
#tags = Tag.all
end
def show
#tag = Tag.find(params[:id])
end
def new
#tag = Tag.new
end
def edit
#tag = Tag.find(params[:id])
end
def create
# #tag = Tag.new(tag_params)
tag = Tag.create!(tag_params)
render json: tag, status: 201
end
def update
tag.update_attributes(tag_params)
render json: tag, status: 201
end
def destroy
#tag.destroy
respond_with #tag
end
private
def set_tag
#tag = Tag.find(params[:id])
end
def tag_params
params.require(:tag).permit(:name, :color, :order)
end
end
and my view:
<div ng-controller="TagListCtrl" ng-style="{color: myColor}">
<table>
Name: <input ng-model="newTag.name"> <br>
Color: <input ng-model="newTag.color"> <br>
Order: <input ng-model="newTag.order"> <br>
<button ng-click="saveNewTag()">Save</button>
</div>
<tr ng-show="tags.length" ng-repeat="tag in tags">
<td>{{tag.name}}</td>
<td>{{tag.color}}</td>
<td>{{tag.order}}</td>
<td>
Remove
</td>
</tr>
</table>
</div>
rails routes:
Rails.application.routes.draw do
scope "api", defaults: {format: :json} do
resources :tags
end
root to: "tags#index", anchor: false
end
I'm suspecting that my routes are off. So far, adding a new tag works in the rails index.html.erb. I didn't put in any templates for angular to route to.

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.

How to pass variable into controller action from view?

views/vehicles/_form.html.haml
= link_to "Deactivate", "/vehicles/deactivate"
I want to pass in #vehicle in my link_to above.
How do I do this?
controllers/vehicles_controller.rb
def deactivate
#vehicle = Vehicle.find(params[:id])
#vehicle.active = 0
#vehicle.save
respond_to do |format|
format.html { redirect_to vehicles_url }
format.json { head :no_content }
end
end
To make it easy and in Rails way, you can use Rails resources:
# routes.rb
resources :vehicles do
put 'deactivate', on: :member
end
# view:
= link_to 'Deactivate', deactivate_vehicle_path(#vehicle), method: :put
Best answer already given by Marek Lipka.
There is also a way using ajax
<%= link_to 'Deactivate', javascript::void(0), :class => "deactivate" %>
Put some script:
<script>
$(".deactivate").click(function() {
$.ajax({
type: "post",
url: "/vehicles/deactivate",
data: {id: <%= #vehicle.id %>},
dataType:'script',
beforeSend: function(){
// do whatever you want
},
success: function(response){
// do whatever you want
}
});
});
</script>
This worked for me, I ended up using the Update action in my controller.
= link_to "Deactivate", vehicle_path(#vehicle, :vehicle => {:active => 0}), method: :put, :class=>'btn btn-mini'

Resources