How to perform POST operation in Rails using AJAX - ruby-on-rails

Scenario
I have created a simple application for learning Rails and Bootstrap. What this application does is it allows users to give nicknames to celebrities and like the nicknames as well. You can check it at http://celebnicknames.herokuapp.com
I have got a link_to button for posting likes for a celebrity in my home/index.html.erb as:
<%= link_to home_updatelike_path(id: nickname.id, search: #search ), method: :post, remote: true, class: "update_like btn btn-xs btn3d pull-right" do %>
<i class="glyphicon glyphicon-thumbs-up"></i>
<% end %>
The corresponding code in my home_controller.rb for the post method is:
def updatelike
#like = Like.new(:nickname_id => params[:id], :ip_address => request.remote_ip)
#like.save
respond_to do |format|
format.html { redirect_to root_path("utf8"=>"✓", "search"=>params[:search], "commit"=>"Search") }
format.json { head :no_content }
format.js { render :layout => false }
end
end
The code in my updatelike.js.erb is as:
$('.update_like').bind('ajax:success', function() {
});
Now, what I want to achieve is to submit a like without performing a page reload.
Problem:
Whenever I press the button, the database operation is done, but the change is not reflected in the page unless I reload it. Please guide me through this noob question and also provide links to resources which explain using AJAX with Rails.
Edit
Here is the entire home/index.html.erb
<div class="jumbotron col-md-6 col-md-offset-3">
<h1 class='text-center'>Nicknamer</h1>
</div>
<div class="container">
<div class="row">
<div class="col-sm-8 col-sm-offset-2 bottom50">
<%= form_tag(root_path, :method => "get", id: "search-form", type: "search") do %>
<div class = "form-group" >
<div class = "col-sm-9 " >
<%= text_field_tag :search, params[:search], placeholder: "Name", class: "form-control" %>
</div>
<div class = "col-sm-3" >
<%= submit_tag "Search", class: "btn btn-default btn-block btn-success"%>
</div>
</div>
<% end %>
</div>
</div>
<% #names.each do |name| %>
<div class="col-md-6 col-xs-8 col-md-offset-3 col-xs-offset-2 bottom50">
<!-- ===== vCard Navigation ===== -->
<div class="row w">
<div class="col-md-4">
<% if name.sex == true %>
<%= image_tag("mal.jpg", :alt => "", class: "img-responsive") %>
<% else %>
<%= image_tag("fem.jpg", :alt => "", class: "img-responsive") %>
<% end %>
</div><!-- col-md-4 -->
<!-- ===== vCard Content ===== -->
<div class="col-md-8">
<div class="row w1">
<div class="col-xs-10 col-xs-offset-1">
<h3><%= "#{name.name}" %></h3>
<hr>
<h5>Also known as</h5>
<% name.nicknames.each do |nickname| %>
<div class = "row w2 bottom10">
<% if nickname.name_id == name.id %>
<div class="col-xs-7">
<%= nickname.nickname %>
</div>
<div class="col-xs-3 text-right">
<%= pluralize(nickname.likes.count, 'like') %>
</div>
<div class="col-xs-1">
<%= link_to home_updatelike_path(id: nickname.id, search: #search ), method: :post, remote: true, class: "update_like btn btn-xs btn3d pull-right" do %>
<i class="glyphicon glyphicon-thumbs-up"></i>
<% end %>
</div>
<% end %><!-- if -->
</div><!-- row w2 -->
<% end %><!-- do -->
<div class = "row w3 bottom30">
<%= form_for #addnickname, as: :addnickname, url: {action: "addnickname"} do |f| %>
<div class = "form-group" >
<%= f.hidden_field :name_id, :value => name.id %>
<%= f.hidden_field :search, :value => #search %>
<div class = "col-xs-9">
<%= f.text_field :nickname , :required => true, class: "form-control" %>
</div>
<div class = "col-xs-3">
<%= f.submit "Add", class: "btn btn-default btn-info"%>
</div>
</div>
<% end %>
</div>
</div><!-- col-xs-10 -->
</div><!-- row w1 -->
</div><!-- col-md-8 -->
</div><!-- row w -->
</div><!-- col-lg-6 -->
<% end %>
</div><!-- /.container -->
and this is the entire home_controller.rb
class HomeController < ApplicationController
def index
#addnickname = Nickname.new
if params[:search]
#search = params[:search]
#names = Name.search(params[:search])
else
#names = Name.all
end
end
def addnickname
#addnickname = Nickname.new(:nickname => params[:addnickname][:nickname], :name_id => params[:addnickname][:name_id])
if #addnickname.save
redirect_to root_path("utf8"=>"✓", "search"=>params[:addnickname][:search], "commit"=>"Search")
else
render :new
end
end
def updatelike
#like = Like.new(:nickname_id => params[:id], :ip_address => request.remote_ip)
#like.save
respond_to do |format|
format.html { redirect_to root_path("utf8"=>"✓", "search"=>params[:search], "commit"=>"Search") }
format.json { head :no_content }
format.js { render :layout => false }
end
end
private
def addnickname_params
params.require(:addnickname).permit(:name_id, :nickname, :search)
end
end

Ok, so you need a way to identify the div with the number that needs to change:
<div class="col-xs-3 text-right">
<%= pluralize(nickname.likes.count, 'like') %>
</div>
The easiest way to do this would be to have an id attribute on the div that corresponds to the (unique) database id of the nickname. Rails has a helper method for this called div_for. So you could change the above code to this:
<%= div_for(nickname, class: "col-xs-3 text-right") do %>
<%= pluralize(nickname.likes.count, 'like') %>
<% end %>
That will generate a div with an id like nickname_1 (if this id of the nickname is 1).
Now in your javascript template you can easily target the div that needs to be updated:
$("nickname_#{#like.nickname.id}").html(#like.nickname.likes.count);
The ajax:success binding is unnecessary here since you are rendering a javascript template. In the template you can access instance variables set in the controller - just like you can from an HTML template. The above code is meant to replace the content of the div that contains the likes count with the new count.

Related

undefined local variable or method `form' for Class

When I'm attempting to show a department view on my product#new page, this is my product#new page
<% #produto.errors.full_messages.each do |msg| %>
<div class="alert alert-danger" role="alert">
<%= msg %>
</div>
<% end %>
<%= form_for #produto do |produto| %>
<div class="row">
<div class="col-sm-3">
<div class="form-group">
<%= produto.label :nome %>
<%= produto.text_field :nome, class:"form-control" %>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-3">
<div class="form-group">
<%= produto.label :descricao %>
<%= produto.text_area :descricao, class:"form-control", rows:"4" %>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-2">
<div class="form-group">
<%= form.label :departamento %>
<%= form.collection_select :departamento_id, #departamentos, :id, :nome, {}, class:"form-control" %>
</div>
</div>
</div>
<%= produto.submit "Criar",class:"btn btn-success" %>
<% end %>
This is my product_controller.rb
class ProdutosController < ApplicationController
def index
#produtos = Produto.order(nome: :asc).limit 5
#produtos_com_desconto = Produto.order(:preco).limit 1
end
def new
#produto = Produto.new
#departamentos = Departamento.all
end
def create
valores_produto = params.require(:produto).permit(:nome, :descricao, :preco, :quantidade)
#produto = Produto.new valores_produto
#Produto.create valores_produto
if #produto.save
flash[:notice] = "Produto criado com sucesso!"
redirect_to root_url
else
render :new
end
end
def destroy
id = params[:id]
Produto.destroy id
redirect_to root_url
end
def search
#nome = params[:nome]
#produtos = Produto.where "nome like ?", "%#{#nome}%"
end
end
So, when i'm trying to access my product/new page , i'm receiving the error NameError in Produtos#new: undefined local variable or method `form' for #<#<Class.
<%= form.label :departamento %> is looking for the form variable but there is none defined. Instead <%= form_for #produto do |produto| %> has defined producto and uses it as <%= produto.label :nome %>. So you probably want <%= producto.label :departamento %>
Note the variable should probably be form. The variable is a FormBuilder, not a Produto. Calling it producto is confusing.

Undefined local variable or method `plane' for #<#<Class:0x4409040>:0x43d7720> (Ruby on Rails)

I'm trying to do an edit object in Ruby on Rails using a modal form, when I try to do the link_to edit_plane_path(plane) in planes.html.erb I get the following error:
undefined local variable or method `plane' for #<#:0xd580e98>
Did you mean? #plane
plane_url
#planes
However, trying any of those doesn't really change the fact that I still get an error on the page and I can't even load it.
planes.html.erb
<table>
<thead>
<tr>
<th>Image</th>
<th>Operating agency</th>
<th>Date</th>
<th>Call sign</th>
<th>Country</th>
<th>Info</th>
<th>Action</th>
</tr>
<thead>
<tbody>
<% #planes.each do |p| %>
<tr class="gradeC">
<td>
<%= image_tag(p.image, size: "150x150")%></td>
<td class="w-25"><%= p.provider%>
</td>
<td><%= p.brand%></td>
<td class="center"><%= p.year%></td>
<td class="center"><%= p.call_sign%></td>
<td><%= p.country%></td>
<td><%= p.info%></td>
<td class="text-right">
<div class="btn-group">
<%= link_to 'Edit', edit_plane_path(plane), remote: true, :class =>'btn white btn btn-xs' %>
<button class="btn-white btn btn-xs">Delete</button>
</div>
</td>
</tr>
</tbody>
<% end %>
</table>
_form.html.erb
<!-- FORM EDIT -->
<div id="edit-plane" class="modal fade" aria-hidden="true">
<div class="modal-dialog modal-md">
<div class="modal-content">
<div class="modal-body">
<div class="row">
<div class="col-sm-12"><h3 class="m-t-none m-b">Edit plane</h3>
</div>
</div>
<%= form_for #plane, url: planes_path, remote: true do |f| %>
<div class="form-group row">
<div class="col-sm-6">
<label>Brand</label>
<%= f.text_field :brand, class:"form-control"%>
</div>
<div class="col-sm-5">
<label>Model</label>
<%= f.text_field :model, class:"form-control"%>
</div>
</div>
<div class="form-group row">
<div class="col-sm-5">
<label>Year</label>
<%= select_tag :hyear, options_for_select(["2015","2016","2017","2018","2019"], "2019"), class:"select2_demo_1 form-control" %>
<%= f.hidden_field :year %>
</div>
<div class="col-sm-6">
<label>Country</label>
<%= select_tag :hcountry, nil, class:"select2_demo_1 form-control" %>
<%= f.hidden_field :country %>
</div>
<script language="javascript">
populateCountries("hcountry");
</script>
</div>
<div class="form-group row">
<div class="col-sm-6">
<label>Provider</label>
<%= select_tag :hprovider, options_for_select([ "Test", "Test2"], "Test"), class:"select2_demo_1 form-control"%></select>
<%= f.hidden_field :provider %>
</div>
<div class="col-sm-5">
<label>Tactical call sign</label>
<%= f.text_field :call_sign, class:"form-control"%>
</div>
</div>
<div class="form-group row">
<div class="col-sm-6">
<label>Extra info</label>
<%= f.text_area :info %>
</div>
<div class="col-sm-5">
<label>Image</label>
<%= f.file_field :image %>
</div>
</div>
<!-- SUBMIT -->
<button class="btn btn-primary btn-lg float-right ml-2" id="cancelbtn">Cancel</button>
<%= f.submit "Submit", id:"edpla", class: 'btn btn-primary btn-lg float-right'%>
</div>
<% end %>
</div>
</div>
</div>
</div>
</div>
</div>
edit.js.erb
// Add the dialog title
$('#edit-plane h3').html("<i class=' glyphicon glyphicon-pencil'></i> Edit Plane");
// Render the edit form
$('.modal-body').html('<%= j render("form") %>');
// Show the dynamic dialog
$('#edit-plane').modal("show");
// Set focus to the first element
$('#edit-plane').on('shown.bs.modal', function () {
$('.first_input').focus()
})
update.js.erb
$('#edit-plane').modal('toggle');
$('#customer_<%= #plane.id %>').replaceWith('<%= j render (#plane) %>')
planes_controller.rb
class PlanesController < ApplicationController
def planes
#plane = Plane.new
#planes = Plane.all
end
def create
#plane = Plane.new(plane_params)
if #plane.save
flash[:success] = "Plane successfully added"
redirect_to :planes => 'post', :action => 'planes'
else
flash[:error] = "Something went wrong"
render 'planes'
end
end
def edit
#plane = Plane.find(params[:id])
end
def update
respond_to do |format|
if #plane.update(plane_params)
format.json { head :no_content }
format.js
else
format.json { render json: #customer.errors.full_messages, status: :unprocessable_entity }
end
end
end
private
def plane_params
params.require(:plane).permit(:brand, :model, :provider, :call_sign, :user, :country, :image, :info, :year)
end
end
My goal is to edit a plane in the table through a modal form, but I can't even show the page at the moment.
Here:
<% #planes.each do |p| %>
...you are passing each plane as p. Therefore, try:
<%= link_to 'Edit', edit_plane_path(p), remote: true, class: 'btn white btn btn-xs' %>

Render a Rails view in a Bootstrap modal

I have hit a brick wall. I've been hacking at this problem for so long now, I'm not even sure how I got to where I am. All I can say is, I've tried all of the below without success:
How to add bootstrap modal with link_to so the link content open in modal ?
How to show twitter bootstrap modal via JS request in rails?
rails link_to :remote
http://apidock.com/rails/ActionView/Helpers/PrototypeHelper/link_to_remote
How do I render "new", "edit" and "delete" views within Bootstrap modals on the "index" view rather than linking to separate pages for each?
Here is my code as it stands now. For now, lets ignore "edit" and "delete" and just focus on "new". When I click the "New" button, a modal with the string "<%= j render "items/new" %>" appears (instead of the form that that ruby statement should render). What am I doing wrong?:
items_controller.rb:
class ItemsController < ApplicationController
def index
#items = Item.all
end
def new
respond_to do |format|
format.js {}
end
end
def create
#item = Item.new(item_params)
if #item.save
flash[:notice] = "'#{#item.name}' saved!"
redirect_to items_path
else
flash[:notice] = "Something went wrong :("
render "index"
end
end
def edit
#item = Item.find(params[:id])
respond_to do |format|
format.js {}
end
end
def update
#item = Item.find(item_params[:id])
if #item.update_attributes(item_params)
flash[:notice] = "Successfully updated #{#item.name}."
redirect_to items_path
else
flash[:notice] = "Oops"
# render "edit"
end
end
private
def item_params
params.require(:item).permit(:name, :bid, :uuid)
end
end
items/index.html.erb
<div class="row">
<div class="col-xs-12">
<%= link_to "New", new_item_path, remote: true, class: "btn btn-success pull-right new", data: { toggle: "modal", target: "#newModal" } %>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<table class="table table-hover items">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>UUID</th>
<th colspan="2">Links</th>
</tr>
</thead>
<tbody>
<% #items.each do |item| %>
<tr>
<td><%= item.id %></td>
<td><%= item.name %>
<!-- edit/remove icons -->
<span class="edit-remove">
<%= link_to edit_item_path(item.id), remote: true, data: { toggle: "modal", target: "#editModal" } do %>
<span class="glyphicon glyphicon-pencil text-muted"></span>
<% end %>
<a href="#">
<span class="glyphicon glyphicon-remove text-muted"></span>
</a>
</span>
</td>
<td><%= item.uuid %></td>
<td><%= link_to "XXX", "http://xxx" %></td>
<td><%= link_to "XXXX", "http://xxx", target: "_blank" %></td>
</tr>
<% end %>
</tbody>
</table>
</div>
</div>
<!-- newModal skeleton -->
<div class="modal fade" id="newModal">
<div class="modal-dialog">
<div class="modal-content">
</div>
</div>
</div>
<!-- editModal skeleton -->
<div class="modal fade" id="editModal">
<div class="modal-dialog">
<div class="modal-content">
</div>
</div>
</div>
<!-- deleteModal skeleton -->
<div class="modal fade" id="deleteModal">
<div class="modal-dialog">
<div class="modal-content">
</div>
</div>
</div>
items/new.html.erb
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span>×</span></button>
<h4 class="modal-title">New Item</h4>
</div>
<div class="modal-body">
<%= form_for :item, url: { action: "create" } do |f| %>
<div class="form-group">
<%= f.label :name, "Name" %>
<%= f.text_field :name, { class: "form-control" } %>
</div>
<div class="form-group">
<%= f.label :bid, "BID" %>
<%= f.text_field :bid, { class: "form-control" } %>
</div>
<div class="form-group">
<%= f.label :uuid, "UUID" %>
<%= f.text_field :uuid, { class: "form-control" } %>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<%= submit_tag "Save", class: "btn btn-primary" %>
<% end %>
</div>
javascripts/items.js
$(document).on("page:change", function() {
$("#newModal .modal-content").html('<%= j render "items/new" %>');
});
In the case of new for instance, you want to render a javascript file. For this, you'll need to create items/new.js.erb.
Also, remove ", data: { toggle: "modal", target: "#newModal" }" from your link, we will do that in the javascript.
# new.js.erb
$("#newModal .modal-content").html('<%= j render "items/form" %>');
$("#newModal").modal(); // Or whatever the Bootstrap function is to render the modal
# items/_form.html.slim
# Here you'll put your form
You cannot use "render" on views directly, you should render partials and not views (this is why I asked you to put your form into a partial).
I threw this together and it puts a big '3' in my document 3 seconds after loading it:
<script>
setTimeout(function() {
$("#holder").html("<%= j render(:file => 'things/test.html.erb') %>");
}, 3000);
</script>
<div id="holder></div>
app/views/things/test.html.erb:
<h1><%= 1 + 2 %></h1>
That should get you going.

How do I make the like counter to go down when I click the dislike button and vice versa?

this is the controller with like action:
def like
like = Like.create(like: params[:like], user: current_user, story: #story)
respond_to do|format|
if like.valid?
format.js
else
format.js {render status: 403, js: "alert('You can only like/dislike a story once')"}
end
end
this is the model that has the counter from model:
def thumbs_up_total
self.likes.where(like: true).size
end
def thumbs_down_total
self.likes.where(like: false).size
end
this is the View. I am getting the counter from the model. 'thumbs up' and 'thumbs down':
<div class="pull-right">
<%= link_to like_story_path(story, like: true), method: :post, data: { remote: true } do %>
<div class="likes"></div>
<% end %>
<div id = "like-<%= story.id %>">
<%= story.thumbs_up_total %>
</div>
<%= link_to like_story_path(story, like: false), method: :post, data: { remote: true } do %>
<div class="dislikes"></div>
<% end %>
<div id="dislike-<%= story.id %>">
<%= story.thumbs_down_total %>
</div>
</div>
I guess you are asking for something like this, correct?
<div class="pull-right">
<%= link_to like_story_path(story, like: true), method: :post, data: { remote: true } do %>
<div class="likes"></div>
<% end %>
<div id = "like-<%= story.id %>">
<%= story.thumbs_up_total - story.thumbs_down_total%>
</div>
<%= link_to like_story_path(story, like: false), method: :post, data: { remote: true } do %>
<div class="dislikes"></div>
<% end %>
<div id="dislike-<%= story.id %>">
<%= story.thumbs_down_total - story.thumbs_up_total %>
</div>
</div>
Move parts of your view to partials, createlike.js and fill it with code to render the partials.
First change your view code to this
<div class="pull-right">
<%= link_to like_story_path(story, like: true), method: :post, data: { remote: true } do %>
<div class="likes"></div>
<% end %>
<%= render "thumbs_up" %>
<%= link_to like_story_path(story, like: false), method: :post, data: { remote: true } do %>
<div class="dislikes"></div>
<% end %>
<%= render "thumbs_down" %>>
</div>
Then create two new partials.
# _thumbs_up.html..erb
<div id = "like-<%= story.id %>" class="thumbs-up">
<%= story.thumbs_up_total %>
</div>
# _thumbs_down.html..erb
<div id = "like-<%= story.id %>" class="thumbs-down">
<%= story.thumbs_down_total %>
</div>
Add a file called like.js
# like.js
$(".thumbs-up").html("<%= j(render("thumbs_up")) %>");
$(".thumbs-down").html("<%= j(render("thumbs_down")) %>");
like.js will then be called when your links are clicked. It will re-render your partials and update your thumb counts on click.

passing dynamic variable to partial for rendering in a modal

I have the following Code on a partial that dynamically generates a list of associated steps to a pin. Both pins and steps have image(s). Users are able to add multiple steps to a pin and then this view dynamically generates a view of these steps. For each step there are associated image(s) which I'd like to pop-up as a modal. The challenge is I keep getting the following error:
"undefined local variable or method `step' for #<#:0x00000102f498d8>"
When I stub out rendering the partial, the modal appears basically blank but works. Any ideas How do I do this?
<div class="col-md-6">
<% (0..(Step.where("pin_id = ?", params[:id]).count)-1).each do |step| %>
<div class="col-md-12">
<div class="well">
<ul class="nav pull-right">
<% if current_user == #pin.user %>
<%= link_to edit_step_path((Step.where("pin_id = ?", params[:id]).fetch(step)), :pin_id => #pin.id) do %>
<span class="glyphicon glyphicon-edit"></span>
Edit
<% end %> |
<%= link_to Step.where("pin_id = ?", params[:id]).fetch(step), method: :delete, data: { confirm: 'Are you sure?' } do %>
<span class="glyphicon glyphicon-trash"></span>
Delete
<% end %> |
<%= link_to new_step_image_path(:step_id => Step.where("pin_id = ?", params[:id]).fetch(step), :pin_id => #pin.id) do %>
Add
<span class="glyphicon glyphicon-picture"></span>
<% end %>
<% end %>
<% if StepImage.where("step_id = ?", Step.where("pin_id = ?", params[:id]).pluck(:id).fetch(step)).count == 0 %>
<% else %>
| <a href="#StepImageModal" data-toggle="modal">
<span class="glyphicon glyphicon-picture"></span> (<%= StepImage.where("step_id = ?", Step.where("pin_id = ?", params[:id]).pluck(:id).fetch(step)).count %> ) </strong>
</a>
<% end %>
</ul>
<strong> Step <%= step+1 %>
<p>
<%= Step.where("pin_id = ?", params[:id]).pluck(:description).fetch(step) %>
</p>
</div>
</div>
<%end %>
</div>
<div class="modal fade" id="StepImageModal">
<div class="modal-header">
<a class="close" data-dismiss="modal">×</a>
<h3>Modal Header</h3>
</div>
<div class="modal-body">
<%= render :partial => 'pins/step_images',
:locals => { :step => step } %>
</div>
<div class="modal-footer">
Close
</div>
</div>
I don't get what you're trying to do here
Loop
As mentioned in the comments, you've got a loop which goes through your steps. However, outside the loop, you want to display the partial
The solution to your woes will either be to set an instance variable (not desired), or use another persistent data type. I'd do this:
#app/controllers/steps_controller.rb
def action
#step = {}
end
#app/views/steps/action.html.erb
<% (0..(Step.where("pin_id = ?", params[:id]).count)-1).each do |step| %>
<% #step[step.id.to_sym] = step.details %>
<% end %>
<%= render :partial => 'pins/step_images', :locals => { :step => #step } %>
unless
Some refactoring for you:
<% unless StepImage.where("step_id = ?", Step.where("pin_id = ?", params[:id]).pluck(:id).fetch(step)).count == 0 %>
| <%= link_to "#StepImageModal", data: { toggle: "modal"} do %>
<span class="glyphicon glyphicon-picture"></span>(<%= StepImage.where("step_id = ?", Step.where("pin_id = ?", params[:id]).pluck(:id).fetch(step)).count %> ) </strong>
<% end %>
<% end %>

Resources