I have a vote button, which simply displays the amount of votes and by clicking on it, a vote is automatically added. Now I would like to add an Ajax request, so that the page doesn't refresh. Unfortunately I have never used Ajax before and therefor have no idea how to use it with Rails. I tried going through a few tutorials, but nothing seems to help.
view:
<%= link_to vote_path(:the_id => Amplify.all.where(question_id: question.id)[0].id, :the_user_id => #current_user.id), :remote => true do %>
<button class="fn-col-2 fn-col-m-3 amplify">
<%= image_tag "logo-icon-white.png" %>
<p class="count"><%= Amplify.all.where(question_id: question.id)[0].users.count %></p>
</button>
<% end %>
controller:
def vote
#amplify = Amplify.find(params[:the_id])
#current_user = User.find(params[:the_user_id])
if #amplify.users.where(:id => #current_user.id).count != 0
#amplify.users.delete(#amplify.users.where(:id => #current_user.id).first)
else
#amplify.users << #current_user
end
#question = Question.where(id: #amplify.question_id)[0]
#amplify.update(:votes => #amplify.users.count)
#question.update_attributes(:votes => #amplify.votes)
redirect_to root_path
end
routes:
get '/vote' => "amplifies#vote", :as => "vote"
jQuery(document).ready(function($){
$('.amplify').on('click', function(){
var that = $(this);
$.ajax({
url: '/vote',
data: {id: 'your id'},
/**
* Response from your controller
*/
success: function(response) {
that.siblings('.count').first().text(response);
}
});
})
})
I like calling it like waldek does but with coffeescript.
However, in your example, you are using :remote => true which is the unobtrusive way
Basically you are then going into the controller where you will need a format.js
respond_to do |format|
format.html # if you are using html
format.js # this will call the .js.erb file
end
Then create a file vote.js.erb where you can then access your instance variables and write js
console.log('you are here');
Related
I have a form that renders partial 'form'.
= form_for(#booking, :as => :booking, :url => admin_bookings_path, :html => { :multipart => true }) do |f|
= render partial: "form", locals: { f: f }
Inside the partial form again another partial is rendered based on new_record?.
- if f.object.new_record?
#extra-products
= render partial: 'new_booking_extra_product', locals: { f: f }
- else
= render partial: 'extra_product', locals: { f: f }
In bookings#new, when the user selects particular car_id, I want to show associated products with it through ajax. For that I have used ajax to make a request to bookings#fetch_extra_product.
# Ajax request to fetch product
$('#booking_car_id').on('change', function(e){
var selectedCarId = $("#booking_car_id option:selected").val();
var url = "/admin/bookings/" + selectedCarId + "/fetch_extra_product";
$.ajax(url,{
type: 'GET',
success: function(msg){
},
error: function(msg) {
console.log("Error!!!");
}
});
});
# Bookings#fetch_extra_product
def fetch_extra_product
#car_id = params[:car_id] || Car.all.order('id desc').first.id
#extra_product = Car.find(#car_id).extra_products
respond_to do |format|
format.js
end
end
The fetch_extra_product.js.erb looks as follow:
$('#extra-products').html('$("<%= j render(:partial => 'new_booking_extra_product', :locals => {:f => f}) %>")');
But the form object (f) is undefined on this state. What's the best way to fetch the object or the best way to get through this problem?
You'll want to render a partial view with the associated products server-side when the Ajax request is received. Then you can send the HTML from that partial as part of your response and use the Ajax success callback to attach the partial view to the DOM however you want it. Then you can have (in your controller) code something like this:
def fetch_extra_product
# Unless you're using #car_id in a view or something, I'd just
# just use a local variable instead of an instance variable
#
car_id = params[:car_id] || Car.all.order('id desc').first.id
#extra_products = Car.find(car_id).extra_products
respond_to do |format|
format.js { render partial: 'extra_products_div', status: :ok }
end
Then your partial code is something like this:
<div class="extra-products">
<% #extra_products.each do |product| %>
<h2><%= product.name %></h2>
...
<% end %>
</div>
That way, your JavaScript can then go along the lines of:
$.ajax(url, {
type: 'GET',
success: function(msg) {
$('#foo').attach(msg);
},
error: function(msg) {
console.log('Error!!!');
}
});
One other comment: If your API is only going to get a request to this route via Ajax, you don't need the respond_to block. In that case you can just put the render partial: 'extra_products_div', status: :ok under the line where you
define #extra_products.
I want to create a wizard in js.
steps :first_step,
:second_step
In my 'controller_step'
def show
case step
when :first_step
#r = R.new
when :second_step
end
render_wizard
end
def update
case step
when :first_step
#r = R.new(r_params)
when :second_step
end
render_wizard #r
end
I have problems after the update of the first step. I'm receive the following error message:
"Missing template controller_step/second_step, application/second_step
with {:locale=>[:en], :formats=>[:html], :variants=>[],
:handlers=>[:erb, :builder, :raw, :ruby, :coffee, :jbuilder]}. ".
How can I force loading of js templete? I would load "second_step.js.erb".
I tried to change the update method:
respond_to do |format|
format.js { render :js => ( render_wizard #r ) }
end
Of course I get the following error:
"AbstractController::DoubleRenderError in ...Controller#update Render
and/or redirect were called multiple times in this action. Please note
that you may only call render OR redirect, and at most once per
action. Also note that neither redirect nor render terminate execution
of the action, so if you want to exit an action after redirecting, you
need to do something like "redirect_to(...) and return"."
I also tried to change the code (in update):
respond_to do |format|
format.js { render :js => ( render_wizard #room_types and return ) }
end
I'm get the same error ( ... application/second_step with {:locale=>[:en], :formats=>[:html] .... )
P.S.
In view of the first step:
<%= form_for(#r, url: wizard_path, method: :put, remote: true) do |f| %>
....
<%= f.submit "Submit", class: "btn btn-default" %>
<% end %>
How do I fix ? thanks in advance
The #render_wizard method defined in the Wicked::Controller::Concerns::RenderRedirect is a wrapper method around the ActionController::Base#render method. It
accepts a hash of options and passes it down to the controller's regular #render method.
This is the source code from the Wicked library:
def render_wizard(resource = nil, options = {})
...
if #skip_to
...
else
render_step wizard_value(step), options
end
end
def render_step(the_step, options = {})
if the_step.nil? || the_step.to_s == Wicked::FINISH_STEP
...
else
render the_step, options #<-- Here
end
end
Your code:
respond_to do |format|
format.js { render :js => ( render_wizard #r ) }
end
is basically doing:
respond_to do |format|
format.js { render :js => ( render #r ) }
end
which is in fact calling the render method twice.
As it is searching for a .html template rather than a .js.erb one, try adding a formats: 'js' option to the render_wizard method. It should prepend ['js'] to the :formats=>[:html] we see in the Missing template error message.
respond_to do |format|
format.js { render_wizard(#r, formats: 'js') }
end
also, make sure the template's filename follows the rails convention and start with a _. (ie: _second_step.js.erb)
About the double render error, you are correct. You must return from the controller #show or #update method and prevent further code from calling the #render method a second time. You seem to have fixed that problem already.
EDIT#1
It seems like you may be able to call this method directly in your controller.. :
def render_step(the_step, options = {})
# Wicked::FINISH_STEP = "wicked_finish"
if the_step.nil? || the_step.to_s == Wicked::FINISH_STEP
redirect_to_finish_wizard options
else
render the_step, options
end
end
I believe the_step will be the partial's name. I think you should be able to call the #render_step method from your controller.
You may be able to do:
def show
respond_to do |f|
f.js do
case step
when :first_step
#r = R.new
render_step(step) and return
when :second_step
...
end
end
end
end
I solved in this way:
in r model:
validates :x, presence: true
in step controller :
...
steps :first_step,
:second_step,
:finish_step
...
def show
case step
when :first_step
#room_types = R.new
end
render_wizard
end
def update
case step
when :view_new_room_type_step
#r = R.create(r_params)
when :view_desc_step
#r = R.find(params[:r][:id])
#r.update(r_params)
end
respond_to do |format|
if !#r.errors.any?
format.js { !next_step?(:finish_step) ? ( render next_wizard_path ) : ( redirect_to_finish_wizard ) }
else
format.js { render wizard_path, r: #r }
end
end
end
private
...
def redirect_to_finish_wizard
redirect_to r_index_path , notice: "Ok"
end
in first_step.js.erb
$("#modal").html("<%= escape_javascript(render 'form_first_step') %>")
in _form_first_step.html.erb:
<% if #r.errors.any? %>
<div class="alert fade in alert-danger alert-dismissable"><button aria-hidden="true" class="close" data-dismiss="alert" type="button">×</button>
<ul>
<% #r.errors.full_messages.each do |msg| %>
<%= content_tag :li, msg, :id => "error_#{msg}" if msg.is_a?(String) %>
<% end %>
</ul>
</div>
<% end %>
<%= form_for(#room_types, url: wizard_path, method: :put, remote: true) do |f| %>
...
<% end %>
in second form ( of second step ):
<%= form_for(#room_types, url: wizard_path(:second_step), method: :put, remote: true) do |f| %>
....
<%= f.hidden_field :id %>
<% end %>
Idea: to validate the data in the steps you can use jquery-form-validator-rails
I have OrdersController show action (with order_info partial), which displays current status of the order ("paid", "canceled", etc.).
meanwhile, I have callback action order_callback, which executes update action and changes status of the order in the database when it receives the callback from a payment processor.
What I want to achieve is to update show action in real-time to capture changes in order status (e.g. order paid successfully).
I tried to use unobtrusive javascript, but did not succeed.
update.js.erb
$("#order").html("<%= escape_javascript(render 'order_info') %>")
show.html.erb
<div id="order">
<%= render 'order_info' %>
</div>
orders_controller.rb
def update
if #order.update_attributes(order_params)
flash[:success] = "Order updated."
redirect_to #order
else
render 'edit'
end
end
api/orders_controller.rb
def order_callback
signature = request.headers['X-Signature']
request_uri = URI(env['REQUEST_URI']).request_uri rescue env['REQUEST_URI']
if $processor.callback_valid?(signature, request_uri)
#order = Order.find(params["id"])
#order.update_attributes(status: params["status"])
render status: :ok,
json: { success: true,
info: "Successfully updated order." }
else
render status: :unprocessable_entity,
json: { success: false }
end
end
I am using rails 4.2.2 with turbolinks enabled.
I was able to resolve it with javascript polling. The critical line was to explicitly say which .js partial to render in respond_to block
show.js.erb
$("#order").html("<%= j render 'orders/order_info', locals: {order: #order} %>");
OrderPoller.poll();
orders_controller.rb
def show
#order = Order.find(params[:id])
respond_to do |format|
format.js { render "orders/show.js.erb" }
format.html
end
end
orders.coffee
#OrderPoller =
poll: ->
setInterval #request, 5000
request: ->
$.get($('#order').data('url'))
jQuery ->
if $('#order').length > 0
OrderPoller.poll()
show.html.erb
<%= content_tag :div, id: "order", data: { url: order_path(#order) } do %>
<%= render 'order_info' %>
<% end %>
i'd like to make a like/dislike ability on my RoR application. How can i make it via Ajax-requests ?
dislike and like - are integer how can i make an Ajax-request, than i can send the data of what i want to increment either "like" or "dislike" counter in my methods
I have a table with posts :
#app/views/dashboard/view.html.erb
<table>
<%if #post.count!=0%>
<%#post.each do |p|%>
<%if !p.text.nil?%>
<tr>
<td><b class="margin"><h4><%=p.text%></b></h4></td>
<td>by <%=p.user.username%> </td>
<td><span class="glyphicon glyphicon-thumbs-up likeAction"><%= link_to p.like, dashboard_like_path, :remote => true, :id => 'likecount' %> </td>
<td><span class="glyphicon glyphicon-thumbs-down"><%= link_to p.dislike, dashboard_dislike_path, :remote => true, :id => 'dislikecount' %> </td>
<%end%>
<% end %>
<%else%>
There's no posts yet, but you can add <%=link_to "one", dashboard_posts_create_a_post_path%>
<%end%>
</table>
My js file
#app/views/dashboard/view.js
$('#likecount').text(#post.like);
$('#dislikecount').text(#post.dislike);
my methods in controller :
#app/controller/dahsboard_controller.rb
def like
#post.increment!(:like)
respond_to do |format|
format.html
format.js
end
end
def dislike
#post.increment!(:dislike)
respond_to do |format|
format.html
format.js
end
end
My dashboard.js in assets/javascripts
jQuery(function($) {
$("likeAction").click(function(){
$.ajax({
url: dashboard_like_path,
type: 'POST',
success: function(){
$('#linkcount').text(data);
}
error: function(error){
alert(error);
}
});
});
});
You already have Rails built-in AJAX functionality, so no need for calling $.ajax. Simply set remote: true on your link_to 'Like', ..., remote: true and respond with the same code you have in app/views/dashboard/view.js: format.js { render action: 'view' }
EDIT: As long as like and dislike are set as member routes on posts:
dislike_post POST /posts/:id/dislike(.:format) posts#dislike
like_post POST /posts/:id/like(.:format) posts#like
You will have a params[:id] (if you send one) to do something like #post = Post.find(params[:id]), if you share this code with show, like and dislike. You can create a set_post before filter, so you don't repeat yourself.
You'll probably want to look at a gem called acts_as_votable
This sets much of your model functionality up - allowing you to use the likes of #post.downvote_from #user2 etc. I'll let you look into that, as it's what you need in the backend I think.
In regards the front-end (especially Ajax), you'll have to set up a controller action, and then hit it with a JS request:
#config/routes.rb
resources :posts do
match :vote, via: [:post,:delete]
end
#app/controllers/posts_controller.rb
class PostsController < ApplicationController
respond_to :js, :html, only: :vote
def vote
if request.delete?
#downvote
elsif request.post?
#upvote
end
end
end
This will allow you to use the following:
#app/views/posts/vote.js.erb
$(".element").html("<%=j render partial: "post/vote_count", object: #post %>");
#app/views/posts/index.html.erb
<% #posts.each do |post| %>
<%= render partial: "post/vote_count", object: :post %>
<% end %>
#app/views/posts/_vote_count.html.erb
<% method = #post.liked_by(current_user)
<%= link_to post.likes, post_vote_path(post), method: :post, remote: true %>
--
The Ajax functionality is pre-built into Rails; you have to be wary of which controller action it's going to send you to, as well as the response given.
My above code uses the respond_to block to invoke the .js.erb response -- allowing you to perform some actions when you send your request.
I am trying to upload files using a Rails form where the remote is set to true. I'm using Rails 4.1.1. Let's say that my model is a Message, and it is using JavaScript so that the user could easily send multiple messages without reloading the page. The form is set like this:
<%= form_for #message, url: {action: "create"}, html: {:class => "message-form", multipart: true}, remote: true do |f| %>
The user can upload images with the Message, if they wish to do so. MessageImage acts as a nested attribute in the form, and is declared like this (http://railscasts.com/episodes/196-nested-model-form-revised way):
<%= f.fields_for :message_images do |builder| %>
<%= render 'message_image_fields', f: builder %>
<%= link_to_add_fields "Add an image", f, :message_images %>
<% end %>
On my controller the action is roughly like this:
if #message.save
flash.now[:success] = "Message sent"
else
flash.now[:alert] = "Error sending the message"
end
respond_to do |format|
format.html { render 'new' }
format.js { render 'new' }
end
Now, this works perfectly as long as the user doesn't send any images, but if they do, it uses format.html instead of format.js. Removing the format.html gives ActionController::UnknownFormat-exception.
Now, this obviously has to do with the fact that you can't submit files with remote set to true. I tried searching a bit, and found this gem https://github.com/JangoSteve/remotipart , which seems to be exactly what I'm looking for. I installed it following the instructions, but for some reason it still doesn't work and gives ActionController::UnknownFormat-exception if I remove the format.html. However, I couldn't find any example of it involving nested attributes. Are there any alternatives for this gem or any other way to fix this, or should I just set that it renders HTML if the user submits files?
JQuery
I don't know how to get the nested model aspect of this, but we've done file uploading with JQuery / asynchronicity before here (register for account, log into profile):
We used the jquery-file-upload gem - basically allowing you to pass the files through Ajax to your controller backend. To give you a clear idea of how we did this:
--
Code
#app/assets/javascripts/application.js
$('#avatar').fileupload({
url: '/profile/' + $(this).attr('data_id'),
dataType: 'json',
type: 'post',
add: function (e, data) {
$(this).avatar_loading('avatar_loading');
data.submit();
},
success: function (data, status) {;
$("#avatar_img").fadeOut('fast', function() {
$(this).attr("src", data.avatar_url).fadeIn('fast', function(){
$(this).avatar_loading('avatar_loading');
});
});
}
});
#app/views/users/index.html.erb
<%= form_for :upload, :html => {:multipart => true, :id => "avatar"}, :method => :put, url: profile_path(current_user.id), "data_id" => current_user.id do |f| %>
<div class="btn btn-success fileinput-button avatar" id="avatar_container">
<%= f.file_field :avatar, :title => "Upload New" %>
<%= image_tag(#user.profile.avatar.url, :width=> '100%', :id => "avatar_img", :alt => name?(#user)) %>
</div>
<% end %>
#app/controllers/profile_controller.rb
Class ProfileController < ApplicationController
def update
def update
#profile = User.find(current_user.id)
#profile.profile.update(upload_params)
respond_to do |format|
format.html { render :nothing => true }
format.js { render :partial => 'profiles/update.js' }
format.json {
render :json => #profile.profile.as_json(:only => [:id, :avatar], :methods => [:avatar_url])
}
end
def upload_params
params.require(:upload).permit(:avatar, :public, :description)
end
end
end
--
Implementation
For your implementation, I would recommend firstly creating the message, and then getting the user to append some images to it in another action
After you've got that working, you could get it to work as one form