How to call a controller function from javascript using ajax - javascript

Hello I'm trying to call a function that I wrote on my controller from my javascript as result of an action when I click on a button.
I followed that thread but it's not worked at all. When I click on the button I get the error listed bellow:
Started GET "/projects/:id/repository/:branch" for 127.0.0.1 at 2013-11-29 15:03:43 -0200
Processing by RepositoriesController#show as */*
Parameters: {"id"=>":id", "repository_id"=>":branch"}
←[1m←[35m (0.0ms)←[0m SELECT MAX(`settings`.`updated_on`) AS max_id FROM `settings`
←[1m←[36mUser Load (0.0ms)←[0m ←[1mSELECT `users`.* FROM `users` WHERE `users`.`type` IN ('User', 'AnonymousUser') AND `users`.`status` = 1 AND `users`.`
id` = 10 LIMIT 1←[0m
Current user: guilherme.noronha (id=10)
←[1m←[35mProject Load (1.0ms)←[0m SELECT `projects`.* FROM `projects` WHERE `projects`.`identifier` = ':id' LIMIT 1
Rendered common/error.html.erb (1.0ms)
Filter chain halted as :find_project_repository rendered or redirected
Completed 404 Not Found in 24ms (Views: 21.0ms | ActiveRecord: 1.0ms)
I didn't understand well why I get this error, so I'm here to ask for help.
Bellow my code to try to detect some mistake or absence of something:
_view_button_release.html.erb
<script>
function CallExec(rep) {
$.ajax("/projects/:id/repository/:branch");
}
</script>
<div class="contextual">
<% if User.current.allowed_to?(:exec, #project) %>
<%= button_to_function l(:gerar_build_project), 'CallExec("'+params[:repository_id].to_s+'")' %>
|
<% end %>
</div>
routes.rb
resources :repositories do
match 'projects/:id/repository', :action => 'exec_client', :controller => 'repositories', :via => :post
match 'projects/:id/repository/:branch', :action => 'exec_client', :controller => 'repositories', :via => :post
get :exec_client, on: :collection
end
client.rb (hook)
module InstanceMethods
require_dependency 'repositories_controller'
def exec_client
begin
...
end
end
end
Any suggestion?
UPDATE:
New Log
Started GET "/projects/b1309/repository/b1309i11/" for 127.0.0.1 at 2013-12-02 10:38:00 -0200
Processing by RepositoriesController#show as */*
Parameters: {"id"=>"b1309", "repository_id"=>"b1309i11"}
←[1m←[35m (0.0ms)←[0m SELECT MAX(`settings`.`updated_on`) AS max_id FROM `settings`
←[1m←[36mUser Load (0.0ms)←[0m ←[1mSELECT `users`.* FROM `users` WHERE `users`.`type` IN ('User', 'AnonymousUser') AND `users`.`status` = 1 AND `users`.`
id` = 10 LIMIT 1←[0m
Current user: guilherme.noronha (id=10)

1st. In your js code
$.ajax("/projects/:id/repository/:branch");
you need insert instead of :id project identificator, instead of :branch branch id or branch name (I don't know what controller expects).
2nd. You need to pass params correctly, because I don't see any of them into log. I see params taken from url Parameters: {"id"=>":id", "repository_id"=>":branch"}. How to pass params through ajax you can google. For example you can check jquery doc and find examples.

Related

Rails Acts_as_votable ajax/js upvoting all posts instead of one

Okay, I'm starting to pull my hair out on this one. I'm new to rails, and was following a tutorial on making a pinterest style app. I finished it but wasn't happy with the up-voting system.
It was refreshing the page every time I clicked to up vote. So I found some post about it and did exactly what it said. It kept loading a page that showed me the js code instead of executing it.
This morning I changed a "put" to "get" then back again, and now it's working... NO IDEA what happened to change it.
BUT, now it's up voting every post on the page instead of just the one I click on. When I hover over the links, they all point to the proper ID for that link.
Here's the code from the controller:
def upvote
#pin.upvote_by current_user
respond_to do |format|
format.html { redirect_to :back }
format.js { render :layout => false }
format.json
end
end
And the view (haml):
.btn-group.pull-right
= link_to like_pin_path(pin), method: :put, remote: true, class: "btn btn-like" do
%span.glyphicon.glyphicon-heart
= pin.get_upvotes.size
Upvote.js.erb:
$('.glyphicon-heart').html('<%=#pin.get_upvotes.size%>');
Routes.rb
Rails.application.routes.draw do
devise_for :users
resources :pins do
member do
put "like", to: "pins#upvote", defaults: { format: 'js' }
end
end
root "pins#index"
end
Here's the log from the console when a like button is clicked:
Started PUT "/pins/10/like" for ::1 at 2015-12-08 11:40:02 -0600
Processing by PinsController#upvote as JS
Parameters: {"id"=>"10"}
Pin Load (0.2ms) SELECT "pins".* FROM "pins" WHERE "pins"."id" = ? LIMIT 1 [["id", 10]]
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT 1 [["id", 2]]
(0.3ms) SELECT COUNT(*) FROM "votes" WHERE "votes"."votable_id" = ? AND "votes"."votable_type" = ? AND "votes"."voter_id" = ? AND "votes"."voter_type" = ? AND "votes"."vote_scope" IS NULL [["votable_id", 10], ["votable_type", "Pin"], ["voter_id", 2], ["voter_type", "User"]]
ActsAsVotable::Vote Load (0.6ms) SELECT "votes".* FROM "votes" WHERE "votes"."votable_id" = ? AND "votes"."votable_type" = ? AND "votes"."voter_id" = ? AND "votes"."voter_type" = ? AND "votes"."vote_scope" IS NULL ORDER BY "votes"."id" DESC LIMIT 1 [["votable_id", 10], ["votable_type", "Pin"], ["voter_id", 2], ["voter_type", "User"]]
(0.2ms) begin transaction
(0.1ms) commit transaction
(0.2ms) SELECT COUNT(*) FROM "votes" WHERE "votes"."votable_id" = ? AND "votes"."votable_type" = ? AND "votes"."vote_flag" = ? AND "votes"."vote_scope" IS NULL [["votable_id", 10], ["votable_type", "Pin"], ["vote_flag", "t"]]
Rendered pins/upvote.js.erb (4.4ms)
Completed 200 OK in 25ms (Views: 9.4ms | ActiveRecord: 1.8ms)
I'm sure it's something really simple but ajax/js are even newer to me than rails.
Let me know if there's anything else I need to post. Any help will be so greatly appreciated!
Also for future reference, what would make the link load a page with the js code instead of executing the code? Since I don't feel like I changed anything that drastic today, I don't know how I got past that. I'm glad I did, but it would be helpful to know HOW.
It's doing that because your JS is technically targeting all the links with that class. Instead you could append the model's id of the individual pin to the link's class or create a data attribute but it's entirely up to you. In doing so it'll only target the one link that's clicked.
UPDATE:
Essentially you'll want to assign a class (instance's id) to a container div holding the vote button: pin-#{pin.id} for index view and pin-#{#pin.id} for show view.
Controller
def upvote
#pin.upvote_by current_user
respond_to do |format|
format.js { render "upvote.js.erb" }
end
end
Routes
...
resources :pins do
member do
put :upvote
end
end
Index View
...
%div{class: "btn-group pull-right pin-#{pin.id}"}
= render "upvote", pin: pin
Show View
...
.col-md-6
%div{class: "btn-group pull-right pin-#{#pin.id}"}
= render "upvote", pin: #pin
-if user_signed_in?
= link_to "Edit", edit_pin_path, class: "btn btn-default"
= link_to "Delete", pin_path, method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-default"
_upvote.html.haml
= link_to upvote_pin_path(pin), method: :put, remote: :true, class: "btn btn-default btn-like" do
%span.glyphicon.glyphicon-heart
= pin.get_upvotes.size
upvote.js.erb
$(".pin-<%= #pin.id %>").html("<%= escape_javascript(render 'upvote', pin: #pin) %>");

How do I return an error message with format.js and update my UI?

In my index.html.erb, I have this partial rendering like this:
<div class="card-comments">
<%= render partial: "nodes/comment", collection: node.comments.order(created_at: :desc) %>
</div>
That partial - _comment.html.erb - looks like this:
<div id="comment-<%= comment.id %>">
<%= comment.message %> | <%= comment.user.name %> | <%= time_ago_in_words(comment.created_at) %><br />
</div>
My CommentsController#Create action looks like this:
def create
#node = Node.find(params[:node_id])
#comment = current_user.comments.new(comment_params)
#comment.node = #node
#card_id = params[:card_id]
respond_to do |format|
if #comment.save and #node.save
format.js
else
format.js
end
end
end
As you can see, this just renders js only, so that calls this create.js.erb:
$("#<%= #card_id %> .card-comments").prepend("<%= j (render partial: 'nodes/comment', locals: { comment: #comment}) %>");
$("#card-input-field-<%= #card_id %>").val('');
This is my comment model with the rules:
class Comment < ActiveRecord::Base
validates_presence_of :message, message: "You can't submit an empty comment."
belongs_to :user
belongs_to :node, counter_cache: true
def type?
"Comment"
end
end
So all of that works wonderfully in the case when there is a valid comment (i.e. it doesn't invalidate any of my validation rules). However, I want to be able to handle errors gracefully.
When validation rule is violated, say entering a blank comment, it returns this error in the server.log but does nothing in the UI:
Started POST "/nodes/101/comments" for 127.0.0.1 at 2015-07-14 00:56:30 -0500
Processing by CommentsController#create as JS
Parameters: {"utf8"=>"✓", "comment"=>{"message"=>""}, "card_id"=>"card-2", "node_id"=>"101"}
User Load (4.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = 57 ORDER BY "users"."id" ASC LIMIT 1
FamilyTree Load (1.0ms) SELECT "family_trees".* FROM "family_trees" WHERE "family_trees"."user_id" = $1 LIMIT 1 [["user_id", 57]]
Role Load (1.6ms) SELECT "roles".* FROM "roles" INNER JOIN "users_roles" ON "roles"."id" = "users_roles"."role_id" WHERE "users_roles"."user_id" = $1 AND (((roles.name = 'admin') AND (roles.resource_type IS NULL) AND (roles.resource_id IS NULL))) [["user_id", 57]]
Node Load (1.1ms) SELECT "nodes".* FROM "nodes" WHERE "nodes"."id" = $1 LIMIT 1 [["id", 101]]
(1.1ms) BEGIN
(1.8ms) ROLLBACK
Rendered nodes/_comment.html.erb (21.5ms)
Rendered comments/create.js.erb (23.7ms)
Completed 500 Internal Server Error in 325ms (ActiveRecord: 51.2ms)
NoMethodError - undefined method `>' for nil:NilClass:
actionview (4.1.12) lib/action_view/helpers/date_helper.rb:74:in `distance_of_time_in_words'
actionview (4.1.12) lib/action_view/helpers/date_helper.rb:153:in `time_ago_in_words'
app/views/nodes/_comment.html.erb:2:in `_app_views_nodes__comment_html_erb___2666929257313190166_70115251186960'
actionview (4.1.12) lib/action_view/template.rb:145:in `block in render'
activesupport (4.1.12) lib/active_support/notifications.rb:161:in `instrument'
So, what I want to happen is, whenever an error occurs, I want it to tell the user in the UI (I guess in the create.js.erb) what the issue was.
How do I do that?
Use #comment or #node in create.js.erb to check if there are errors or not. Like to check validation error:
<% if #comment.errors.present? %>
//validation error change you ui here
<% else %>
//Success, change you ui here
<% end %>

Rails: redirect_to:back not redirecting in spite of showing correct logs

My html
<div class="modal-body center">
<form method="post">
<textarea ng-model = "post.review" rows="10" cols="50" name="review" placeholder="something thougtful and helpful" style = "color: red;" class="fsField fsRequired fsTextAreaMaxLength", maxlength = "116"></textarea>
<input ng-model = "post.rating" type="integer" name="rating" placeholder="0-10" style="color: red;" class="fsField fsRequired fsTextAreaMaxLength", maxlength = "116">
<button ng-click = "submitReview(product)" class="btn btn-success" type="submit">Submit</button>
<div ng-show = "duplicate_status" class = "submitStatus">*You have already submitted review for this Product</div>
<div ng-show = "success_status" class = "submitStatus">*Your Review is successfully submitted</div>
</div>
My javascript
$scope.submitReview = (product)->
$scope.review =
'review': $scope.post.review
'rating': $scope.post.rating
'product_id': product.id
$http.post("/products/#{product.id}/review/new",$scope.review).success((data) ->
if data == 'duplicate'
$scope.duplicate_status = true
if data == 'success'
$scope.success_status = true
)
rails controller
def create
if !current_user.present?
redirect_to :controller => 'user_sessions', :action => 'create'
return
end
if Review.where(:user_id => current_user.id, :product_id => params[:product_id]).first.present?
render plain: "duplicate"
end
#review = Review.new(:review => params[:review],
:rating => params[:rating],
:product_id => params[:product_id],
:user_id => current_user.id,
:date => Time.now.to_date)
if #review.save
redirect_to :back
else
redirect_to :back
end
end
The problem is that on successful operation the i.e., on #review.save the redirect_to :back doesn't refresh the page.
This is in spite of the logs being shown correctly.
logs
Started POST "/products/2/review/new" for 192.168.1.88 at 2015-05-26 16:27:37 +0530
Processing by ReviewsController#create as JSON
Parameters: {"review"=>"Test Review", "rating"=>"1", "product_id"=>"2"}
User Load (0.6ms) SELECT "users".* FROM "users" WHERE "users"."id" = 3 LIMIT 1
Review Load (0.6ms) SELECT "reviews".* FROM "reviews" WHERE "reviews"."user_id" = 3 AND "reviews"."product_id" = 2 ORDER BY "reviews"."id" ASC LIMIT 1
(0.3ms) BEGIN
SQL (0.4ms) INSERT INTO "reviews" ("created_at", "date", "product_id", "rating", "review", "updated_at", "user_id") VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING "id" [["created_at", "2015-05-26 10:57:37.245018"], ["date", "2015-05-26"], ["product_id", 2], ["rating", 1.0], ["review", "Test Review"], ["updated_at", "2015-05-26 10:57:37.245018"], ["user_id", 3]]
(13.0ms) COMMIT
Redirected to http://192.168.1.88:7001/product/2
Completed 302 Found in 23ms (ActiveRecord: 15.0ms)
Started GET "/product/2" for 192.168.1.88 at 2015-05-26 16:27:37 +0530
Started GET "/" for 192.168.1.88 at 2015-05-26 16:27:37 +0530
Processing by HomeController#index as JSON
User Load (0.6ms) SELECT "users".* FROM "users" WHERE "users"."id" = 3 LIMIT 1
Rendered shared/_invite_modal.html.erb (0.9ms)
Rendered shared/_confirm_modal.html.erb (0.4ms)
Rendered shared/_header.html.erb (10.8ms)
Rendered shared/_footer.html.erb (0.4ms)
Rendered home/index.html.erb (40.2ms)
Completed 200 OK in 43ms (Views: 42.3ms | ActiveRecord: 0.6ms)
The problem is that control gets unnecessarily transferred to GET "/"
Am I missing a trick with return somewhere ?
Your problem is that your are redirecting only the XHR request made by javascript -- the page shown to the user remains unaffected.
To look at this in more detail, when your form is submitted, you trigger some code. This code makes an HTTP post to the server. The server does stuff, then redirects that request. Your javascript follows that redirect behind the scenes, and loads the new page. All of this is happening hidden from the user, only affecting the HTTP request that javascript fired off, not the main page itself.
In order to make the main page window change, you'll have to explicitly tell the page go somewhere else using window.location.
For example:
$http.post("/products/#{product.id}/review/new",$scope.review).success((data) ->
...
window.location = data.redirect_url
And in the controller:
render :json, {status: "duplicate", redirect_url: "/product/2"}

Ajax form not rendering partial

I'm trying to implement a follow/unfollow form to create/destroy a relationship between a user, and a company on a site. I'm having problems rendering a new partial on form submit, to change the relationship button from "follow" to "unfollow"
Currently, I render a different partial depending on whether or not a relationship already exists:
`<% unless current_user.id == #company.user_id %>
<div id="#relationship">
<% if current_user.following?(#company) %>
<%= render :partial => 'unfollow' %>
<% else %>
<%= render :partial => 'follow' %>
<% end %>
</div>
<% end %>`
The follow partial looks like:
<%= form_for(current_user.relationships.build(followed_id: #company.id), remote: true) do |f| %>
<div><%= f.hidden_field :followed_id %></div>
<%= f.submit "Follow",class: "btn-transparent btn-hollow btn-huge" %>
<% end %>
While the unfollow partial looks like:
<%= form_for(current_user.relationships.find_by(followed_id: #company.id), html: { method: :delete }, remote: true) do |f| %>
<%= f.submit "Unfollow", class: "btn-transparent btn-hollow btn-huge" %>
<% end %>
These use Create and Destroy methods in my Relationships_Controller:
class RelationshipsController < ApplicationController
before_filter :authenticate_user!
def create
#company = Company.find(params[:relationship][:followed_id])
current_user.follow!(#company)
respond_to do |format|
format.html { redirect_to #company }
format.js {render layout: false}
end
end
def destroy
#company = Relationship.find(params[:id]).followed
current_user.unfollow!(#company)
respond_to do |format|
format.html { redirect_to #company }
format.js{render layout: false}
end
end
end
If I set remote: false, the form works as expected, creating and destroying relationships, and the button changes on page reload. When I try to use AJAX by setting remote: true, and use the code below for relationships/create.js.erb and relationships/destroy.js.erb
$("#relationship").html("<%= j render('companies/unfollow') %>");
$("#relationship").html("<%= j render('companies/follow') %>");
However, now when I reload my page - I can click on the button once to create/destroy a relationship object. If I click again, I get a 500 error. The new partial is never loaded.
Although I'm a bit of a noob, this error in seems to point me to this line in the jquery source in chrome dev tools:
xhr.send( ( options.hasContent && options.data ) || null );
I'm using Rails 4, Jquery-Turbolinks and Devise - if any of them bare any relevance to the problem.
Incredibly frustrated now, if anyone could help that would be greatly appreciated!
Update
The log output is below. The first DELETE says that it has rendered my partial, however it has not. The second DELETE is what is happening on clicking unfollow a second time - it rightly points out that the Relationship with that id number no longer exists, as it was deleted on the first action.
Started DELETE "/relationships/429" for 127.0.0.1 at 2014-10-28 14:34:59 +0000
Processing by RelationshipsController#destroy as JS
Parameters: {"utf8"=>"✓", "commit"=>"Unfollow", "id"=>"429"}
[1m[36mUser Load (1.0ms)[0m [1mSELECT "users".* FROM "users" WHERE "users"."id" = 1 ORDER BY "users"."id" ASC LIMIT 1[0m
[1m[35mRelationship Load (0.4ms)[0m SELECT "relationships".* FROM "relationships" WHERE "relationships"."id" = $1 LIMIT 1 [["id", 429]]
[1m[36mCompany Load (0.3ms)[0m [1mSELECT "companies".* FROM "companies" WHERE "companies"."id" = $1 LIMIT 1[0m [["id", 1]]
[1m[35mRelationship Load (0.3ms)[0m SELECT "relationships".* FROM "relationships" WHERE "relationships"."follower_id" = $1 AND "relationships"."followed_id" = 1 LIMIT 1 [["follower_id", 1]]
[1m[36m (0.2ms)[0m [1mBEGIN[0m
[1m[35mSQL (3.6ms)[0m DELETE FROM "relationships" WHERE "relationships"."id" = $1 [["id", 429]]
[1m[36m (0.5ms)[0m [1mCOMMIT[0m
Rendered companies/_follow.html.erb (2.5ms)
Rendered relationships/destroy.js.erb (5.3ms)
Completed 200 OK in 19ms (Views: 7.0ms | ActiveRecord: 6.3ms)
Started DELETE "/relationships/429" for 127.0.0.1 at 2014-10-28 14:35:04 +0000
Processing by RelationshipsController#destroy as JS
Parameters: {"utf8"=>"✓", "commit"=>"Unfollow", "id"=>"429"}
[1m[35mUser Load (0.9ms)[0m SELECT "users".* FROM "users" WHERE "users"."id" = 1 ORDER BY "users"."id" ASC LIMIT 1
[1m[36mRelationship Load (0.3ms)[0m [1mSELECT "relationships".* FROM "relationships" WHERE "relationships"."id" = $1 LIMIT 1[0m [["id", 429]]
Completed 404 Not Found in 4ms
ActiveRecord::RecordNotFound - Couldn't find Relationship with 'id'=429:
You have a hash in the name of your ID.
Try changing
<div id="#relationship">
to
<div id="relationship">
When use remote: true you must change:
format.js{render layout: false}
with
format.js
Otherwise: you never render relationships/create.js.erb and relationships/destroy.js.erb

Can't find the error in my dependent select drop down on Active Admin( Rails 3.2, Active Admin 1.0)

I'm trying to build a RoR app, with three models:
Games that can be classified in a Sector(called GameSector) and in a subsector (called GameSubsector)
A sector is made up of many subsectors.
a Subsector.
Here are my basic models relationships:
models/game.rb
belongs_to :game_sector, :foreign_key => 'game_sector_id', :counter_cache => true
belongs_to :game_subsector, :foreign_key => 'game_subsector_id',:counter_cache => true
I use Active Admin to input the Games, Sectors or subsectors information.
I have a very basic form when I create a game and I'd just like to make the second select drop down (game_subsector) adjust on the choice of the first select (gamesector) so that I don't the the whole (very long) list of game_subsectors but only those that belong to the game_sector I choose.
After dozens of tests and techniques tried but failing, I've finally used this dev's advice that appeared relevant to me: http://samuelmullen.com/2011/02/dynamic-dropdowns-with-rails-jquery-and-ajax/.
But it still does not work.
Here is the form on Active Admin which is located on admin/game.rb
ActiveAdmin.register Game do
menu :parent => "Campaigns", :priority => 1
controller do
with_role :admin_user
def game_subsectors_by_game_sector
if params[:id].present?
#game_subsectors = GameSector.find(params[:id]).game_subsectors
else
#game_subsectors = []
end
respond_to do |format|
format.js
end
end
end
form do |f|
f.inputs "Details" do
f.input :name
f.input :game_sector_id,
:label => "Select industry:",
:as => :select, :collection => GameSector.all(:order => :name),
:input_html => { :rel => "/game_sectors/game_subsectors_by_game_sector" }
f.input :game_subsector_id, :as => :select, :collection => GameSubsector.all(:order => :name)
f.actions
end
I feel the javascript is even maybe not fired.
The jquery I use is located on app/assets/javascript/admin/active_admin.js (I changed config so it loads this javascript when loading active admin pages)
jQuery.ajaxSetup({
'beforeSend': function(xhr) { xhr.setRequestHeader("Accept", "text/javascript"); }
});
$.fn.subSelectWithAjax = function() {
var that = this;
this.change(function() {
$.post(that.attr('rel'), {id: that.val()}, null, "script");
});
};
$("#game_game_sector_id").subSelectWithAjax(); //it it found in my view???
Finally I created a view as this expert adviced: in app/views/layout/ game_subsectors_by_game_sector.js.erb
$("#game_game_subsector_id").html('<%= options_for_select(#game_subsectors.map {|sc| [sc.name, sc.id]}).gsub(/n/, '') %>');
I'm not sure I have out it in the right place though...
What you need is:
Inspect with your web browser console your selects, and use a CSS selector to create a jQuery object for the sector select, something like:
$('#sector_select')
Append to this object a handler, so when it changes AJAX request is fired:
$('#sector_select').change(function(){
$.ajax('/subsectors/for_select', {sector_id: $(this).val()})
.done(function(response){ // 3. populate subsector select
$('#subsector_select').html(response);
});
});
See 3 in code, you need to inspect to get the right CSS selector. Be sure you are getting the expected response in the Network tab of your web browser inspector(if using Chrome).
You need a controller that answers in /subsectors/for_select, in the file app/controllers/subsectors_controller.rb:
class SubsectorsController < ApplicationController
def for_select
#subsectors = Subsector.where sector_id: params[:sector_id]
end
end
You need a view that returns the options to be populated app/views/subsectors/for_select.html.erb:
<% #subsectors.each do |ss| %>
<option value="<%= ss.id %>"><%= ss.name %></option>
<% end %>
You need a route:
get '/subsectors/for_select', to: 'subsectors#for_select'

Categories

Resources