Ruby newbie here. I'm going through Agile Web Development With Rails. In chapter 11 it challenges you to add a 'decrease quantity' button to items in the shopping cart. I went ahead and tried to implement an increase link as well.
The problem is it's not doing anything when I click on the links.
line_items_controller.rb
def decrease
#cart = current_cart
#line_item = #cart.decrease(params[:id])
respond_to do |format|
if #line_item.save
format.html { redirect_to store_path, notice: 'Item was successfully updated.' }
format.js { #current_item = #line_item }
format.json { head :ok }
else
format.html { render action: "edit" }
format.json { render json: #line_item.errors, status: :unprocessable_entity}
end
end
end
def increase
#cart = current_cart
#line_item = #cart.increase(params[:id])
respond_to do |format|
if #line_item.save
format.html { redirect_to store_path, notice: 'Item was successfully updated.' }
format.js { #current_item = #line_item }
format.json { head :ok }
else
format.html { render action: "edit" }
format.json { render json: #line_item.errors, status: :unprocessable_entity }
end
end
end
cart.rb
def decrease(line_item_id)
current_item = line_items.find(line_item_id)
if current_item.quantity > 1
current_item.quantity -= 1
else
current_item.destroy
end
current_item
end
def increase(line_item_id)
current_item = line_items.find(line_item_id)
current_item.quantity += 1
current_item
end
routes.rb
resources :line_items do
put 'decrease', on: :member
put 'increase', on: :member
end
_line_item.html.erb
<% if line_item == #current_item %>
<tr id="current_item">
<% else %>
<tr>
<% end %>
<td><%= line_item.quantity %> ×</td>
<td><%= line_item.product.title %></td>
<td class="item_price"><%= number_to_currency(line_item.total_price) %></td>
<td><%= link_to "-", decrease_line_item_path(line_item), method: :put, remote: true %></td>
<td><%= link_to "+", increase_line_item_path(line_item), method: :put, remote: true %></td>
<td><%= button_to 'Remove Item', line_item, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
/line_items/increase.js.erb
$('#cart').html("<%= escape_javascript(render(#cart)) %>");
$('#current_item').css({'background-color':'#88ff88'}).animate({'background-color':'#114411'}, 1000);
/line_items/decrease.js.erb
$('#cart').html("<%= escape_javascript(render(#cart)) %>");
$('#current_item').css({'background-color':'#88ff88'}).animate({'background-color':'#114411'}), 1000);
if ($('#cart tr').length==1) {
// Hide the cart
$('#cart').hide('blind', 1000);
}
Let me know if I forgot anything crucial. Thanks in advance!
----EDIT----
I changed the code to what Rich posted, and this is what shows up in the console when I click the '+' link.
Started GET "/line_items/25/qty" for ::1 at 2016-01-30 23:49:11 -0600
ActionController::RoutingError (No route matches [GET] "/line_items/25/qty"):
So I see that it needs a route for qty but I'm not quite sure how to set that up. I'm guessing the JS alert we set up isn't firing because it's snagging up at this point?
----EDIT 2----
Now I'm passing the links as POST and getting this name error, from both the up and down links:
Started POST "/line_items/25/qty" for ::1 at 2016-01-31 09:49:04 -0600
Processing by LineItemsController#qty as JS
Parameters: {"id"=>"25"}
Completed 500 Internal Server Error in 38ms (ActiveRecord: 0.0ms)
NameError (undefined local variable or method `current_cart' for #<LineItemsController:0x007fbfb11ea730>):
app/controllers/line_items_controller.rb:70:in `qty'
What confuses me here is that current_cart works in the increase and decrease methods but not in qty.
Some time passed since you asked this - did you solved it? I have one answer to this issue - I managed to increase/decrease numbers in cart, but without Ajax calls - everytime button clicked - page reloads (I removed remote: true). I have not followed #Richard Peck solution, even dough it might be a better way. So, as he pointed out, routes had to use get method, instead of put (I am not very sure why get instead of post should be used, but if Rails asks it I kindly do it):
resources :line_items do
get 'decrease', on: :member
get 'increase', on: :member
end
Also, I have been noticed by other guy on StackExchange that i forgot to allow increase / decrease actions in controller, since we restrict most of them by default:
line_items_controller.rb:
before_action :set_cart, only: [:create, :decrease, :increase]
So, I guess these two issues was your problem (just like mines).
line_items_controller.rb:
def decrease
product = Product.find(params[:product_id])
#line_item = #cart.remove_product(product)
respond_to do |format|
if #line_item.save
format.html { redirect_to cart_path, notice: 'Line item was successfully updated.' }
format.js
format.json { render :show, status: :ok, location: #line_item }
else
format.html { render :edit }
format.json { render json: #line_item.errors, status: :unprocessable_entity }
end
end
end
def increase
product = Product.find(params[:product_id])
#line_item = #cart.add_product(product)
respond_to do |format|
if #line_item.save
format.html { redirect_to :back, notice: 'Line item was successfully updated.' }
format.js
format.json { render :show, status: :ok, location: #line_item }
else
format.html { render :edit }
format.json { render json: #line_item.errors, status: :unprocessable_entity }
end
end
end
cart.rb:
def add_product(product)
current_item = line_items.find_by(product_id: product.id)
if current_item
current_item.quantity += 1
else
current_item = line_items.build(product_id: product.id)
end
current_item
end
def remove_product(product)
current_item = line_items.find_by(product_id: product.id)
if current_item.quantity > 1
current_item.quantity -= 1
elsif current_item.quantity = 1
current_item.destroy
end
current_item
end
and links:
<td><%= link_to "-", decrease_line_item_path(product_id: line_item.product), class:"btn btn-danger" %></td>
<td><%= link_to "+", increase_line_item_path(product_id: line_item.product), class:"btn btn-success" %></td>
Would this make what you need? And maybe any ideas why Ajax is not working? Here a link to full my question:
https://stackoverflow.com/questions/40551425/rails-ajax-jquery-cant-make-to-render-changes-without-page-refresh
With this type of pattern, you're best DRYing up your logic into a single action:
#config/routes.rb
resources :line_items do
match :qty, action: :qty, via: [:post, :delete], on: :member #-> url.com/line_items/qty
end
#app/models/line_item.rb
class LineItem < ActiveRecord::Base
after_update :check_qty, if: "qty_changed?"
private
def check_qty
self.destroy if self.qty.zero?
end
end
#app/controllers/line_items_controller.rb
class LineItemsController < ApplicationController
def qty
#cart = current_cart
#item = #cart.line_items.find params[:id]
if request.post? #-> increment
method = "increment"
elsif request.delete? #-> decrement
method = "decrement"
end
#item.send(method, :qty, params[:qty])
respond_to do |format|
if #item.save
format.html { redirect_to store_path, notice: 'Item was successfully updated.' }
format.js { #current_item = #line_item }
format.json { head :ok }
else
format.html { render action: "edit" }
format.json { render json: #line_item.errors, status: :unprocessable_entity}
end
end
end
end
This will allow you to pass a single link (with the potential of qty) to your controller. If you leave it blank, it will just use 1 as the qty:
<%= button_to "+", line_items_qty_path(#line_item), params: { qty: 5 } %>
<%= link_to "-", line_items_qty_path(line_item), method: :post, remote: true %>
<%= link_to "+", line_items_qty_path(line_item), method: :delete, remote: true %>
Debugging
Since you're new, you need to understand about debugging.
When doing something like this, there are many places it could "go wrong". Like many inexperienced devs, you've basically said "it's not working"... the problem is that many experienced devs know that there has to be a problem somewhere.
The best thing you can do is find out where it's going wrong. This is a tedious process (test each part); you should start with your JS:
#app/views/line_items/qty.js.erb
alert("test");
If the above fires, it means you're doing everything right up to that point.
If you add the above file with my recommended code, we'll have a much better idea as to what the problem may be. You'll also want to post your console logs for the requests sent to your line_items controller (this will indicate whether Rails treats the request as successful).
Once you've found the problem, you can then pinpoint what needs to be done to fix it, which is where many people expect a question to be based.
As an aside, we've built a cart before (it uses sessions rather than db):
I could write up how to do it if you want. It uses a session-based model.
Update
The error you're seeing is because you're sending a GET request through your link; my routes were POST & DELETE respectively. You need something like the following:
<%= link_to "-", line_items_qty_path(line_item), method: :post, remote: true %>
I think I got it wrong in my post (apologies) - you have to make sure you're passing the method as POST or DELETE
Update 2
To initialize the current_cart method, you need to make sure you have it available.
Ryan Bates alludes to this in his "Session Model" Railscast --
#app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
before_filter :current_cart
helper_method :current_cart
private
def current_cart
#current_cart = session[:cart_id] ? Cart.find(session[:cart_id]) : Cart.create
session[:cart_id] = #current_cart.id if #current_cart.new_record?
end
end
Related
_form.html.erb
<div class="form-group">
<%= f.label :description %><br>
<%= f.select(:description, options_for_select([['', ''],['METRO', 'METRO'], ['BUS', 'BUS'], ['TAXI', 'TAXI'], ['OTHERS', 'OTHERS']]), {}, {class: "form-control", id: "expense_description"}) %>
<br>
<div id="otherDesc">
<%= f.text_field :description_other, class: "form-control" %>
</div>
</div>
index.html.erb
<% #expenses.each do |expense| %>
<tr class="tr-<%= cycle('odd', 'even') %>">
<td class="col-1"><%= (expense.description_other.present? ? expense.description_other : expense.description) %></td>
</tr>
<% end %>
expenses_controller.rb
class ExpensesController < ApplicationController
before_action :set_expense, only: [:show, :edit, :update, :destroy]
# GET /expenses
# GET /expenses.json
def index
#expenses = Expense.all
end
# GET /expenses/1
# GET /expenses/1.json
def show
end
# GET /expenses/new
def new
if Expense.last.present?
#expense = Expense.last.dup
else
#expense = Expense.new
end
end
# GET /expenses/1/edit
def edit
end
# POST /expenses
# POST /expenses.json
def create
#expense = Expense.new(expense_params)
respond_to do |format|
if #expense.save
format.html { redirect_to #expense, notice: 'Expense was successfully created.' }
format.json { render :show, status: :created, location: #expense }
else
format.html { render :new }
format.json { render json: #expense.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /expenses/1
# PATCH/PUT /expenses/1.json
def update
respond_to do |format|
if #expense.update(expense_params)
format.html { redirect_to #expense, notice: 'Expense was successfully updated.' }
format.json { render :show, status: :ok, location: #expense }
else
format.html { render :edit }
format.json { render json: #expense.errors, status: :unprocessable_entity }
end
end
end
# DELETE /expenses/1
# DELETE /expenses/1.json
def destroy
#expense.destroy
respond_to do |format|
format.html { redirect_to expenses_url, notice: 'Expense was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_expense
#expense = Expense.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def expense_params
params.require(:expense).permit(:description, :description_other)
end
end
expense.js
$(document).ready(function () {
$('#expense_description').on('change',function(){
var selectedValue = $(this).val();
selectedValue == "OTHERS" ? $("#otherDesc").show() : $("#otherDesc").hide()
});
});
general.scss
#otherDesc {
display:none;
}
Everything works fine except my index page where I get two values for 'OTHERS' selected option as OTHERS + MY OWN DESCRIPTION. For example in the image it is OTHERS WHITE GLUE. But I would like to have only WHITE GLUE as the description.
Please find attached the image for your reference.
I have tried too hard but unable to get the desired result.
Any suggestions are most welcome.
Thank you in advance.
Found it - I'm kind of blind sometimes...
index.html.erb
<td class="col-1"><%= expense.description %> <%= link_to expense.description_other,{}, {:style => 'color: #CC3366'} %></td>
You've got two <%= ... %>'s in there ...
<%= expense.description %>
And then
<%= link_to expense.description_other,{}, {:style => 'color: #CC3366'} %>
Get ride of the first one when OTHER is selected, using an if conditional(the trinary operator we talked about <expression> ? <if true do this happens>: <if false this happens> The conditional would have to contain both statements inside one <%= ... %> block.
Also, you have an issue here ... should have a conditional of some sort ... probably the ||= & drop the Expense.new or the Expense.last.dup
def new
#expense = Expense.new
#expense = Expense.last.dup
end
(I have been coding for barely a month so apologies if it's a stupid question). I have a user model just with name, email and type. I have created an index form that you can filter by type and it should show the results.
Form and filters show as expected by I have 2 problems:
1. The usertype is duplicated. For example, if I have 5 users (created with the faker gem) each one of them is customer or supplier, the filter shows customer and supplier 5 times instead of twice
2. When I select a filter, it all results are shown and they are not filtered.
This is my model:
class User < ActiveRecord::Base
has_many :microposts
scope :by_userType, -> userType { where(:userType => userType) }
This is my Controller
class UsersController < ApplicationController
before_action :set_user, only: [:show, :edit, :update, :destroy]
has_scope :by_userType, type: :array #, :using => [:userType]
# GET /users
# GET /users.json
#def index
# #users = User.all
#end
def index
#users = apply_scopes(User).all
end
# GET /users/1
# GET /users/1.json
def show
end
# GET /users/new
def new
#user = User.new
end
# GET /users/1/edit
def edit
end
# POST /users
# POST /users.json
def create
#user = User.new(user_params)
respond_to do |format|
if #user.save
format.html { redirect_to #user, notice: 'User was successfully created.' }
format.json { render :show, status: :created, location: #user }
else
format.html { render :new }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /users/1
# PATCH/PUT /users/1.json
def update
respond_to do |format|
if #user.update(user_params)
format.html { redirect_to #user, notice: 'User was successfully updated.' }
format.json { render :show, status: :ok, location: #user }
else
format.html { render :edit }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
# DELETE /users/1
# DELETE /users/1.json
def destroy
#user.destroy
respond_to do |format|
format.html { redirect_to users_url, notice: 'User was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_user
#user = User.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def user_params
params.require(:user).permit(:name, :email, :userType)
end
end
This is my form:
<p id="notice"><%= notice %></p>
<h1>Listing Users</h1>
<%= form_tag users_path, method: 'get', id: "users_search" do%>
<% #users = User.all %>
<% #users.each do |user|%>
<%= check_box_tag "by_userType[]", user.userType %><%= user.userType %><br>
<% end %>
<%= submit_tag "submit" %>
<% end %>
<div id="users"><%= render 'user_list' %></div>
<script type="text/javascript">
$(function () {
$('input[type=checkbox]').change(function(){
$.get($('#users_search').attr('action'),
$('#users_search').serialize(), null, 'script');
return false;
});
$('users_search').submit(function() {
$.get(this.action, $(this).serialize(), null, 'script');
return false;
});
});
</script>
Thank you in advance for your time!
So a couple of things here. First of all, welcome to rails, and welcome to Stack Overflow.
+1 for asking a question and providing code examples for what you've done so far.
Please note that for standardization, case is important in rails. For declaration of scopes you should use snake case. UserModel is the class name, user_model would be snake case.
Now as far as the actual implementation, I would do it somewhat differently. If you notice most of the ajax-filtering of one model based on a field doesn't use the same model as the parameter, it's not hte ONLY way, but I prefer it, as it allows flexibility for adding extra fields to user_type later. Meaning, if you extract user type into it's own model, then you can easily filter your users from the user_type attribute of :name.
So your user model would have:
class User < ActiveRecord::Base
has_many :microposts
belongs_to :user_type
scope :by_user_type, -> user_type { where(:user_type => user_type) }
end
Then create a new model called user_model
rails g scaffold user_model name:string slug:string avatar:string
** Note that the additional fields are just examples, name is hte only necessary one. But if you want to let users do url searches, the slug is easy to use. i.e. yoursite.com/user_type/sellers
Now create a migration to remove your existing userType field in users and a new one for the relationship.
rails g migration modify_user_type_in_user
And the contents of that file would be
class ModifyUserTypeInUser < ActiveRecord::Migration
def change
remove_column :users, :userType
add_reference :users, :user_type, index: true
end
end
Now edit the new user_type.rb model and add the relationship for users
class UserType < ActiveRecord::Base
has_many :users
end
You also need to use UJS, which you didn't mention in your post. When your form field is clicked on, it's sending a javascript (ajax) request. This means that in order to change the data, you'll need a javascript response template.
So add the file app/views/users/index.js.erb and put inside it these contents:
$("#users").html('<%= j(render("user_list", users: #users)) %>');
$("table tbody tr:nth-of-type(even)").css("background","#BD9FB1");
Lastly, you'll need to change your form, so it represents the correct searchable model. So edit 'app/views/users/index.html.erb'
<p id="notice"><%= notice %></p>
<h1>User Filter</h1>
<%= form_tag users_path, method: 'get', id: "users_search" do%>
<% #user_types = UserType.all %>
<% #user_types.each do |user_type|%>
<%= check_box_tag "by_user_type[]", user_type.id %><%= user_type.name %><br>
<% end %>
<%= submit_tag "submit" %>
<% end %>
<div id="users"><%= render 'user_list' %></div>
<script type="text/javascript">
$(function () {
$('input[type=checkbox]').change(function(){
$.get($('#users_search').attr('action'),
$('#users_search').serialize(), null, 'script');
return false;
});
$('users_search').submit(function(e) {
e.preventDefault();
$.get(this.action, $(this).serialize(), null, 'script');
return false;
});
});
</script>
Every time I hit submit in my rails app it sends the request 2x. I can get rid of the second partial by hitting refresh. This is a nested app. Todo has_many Items. I included the controllers and the create and the partials.
I included a photo to make a bit more clear.
create.js.erb
$('.todo-items').prepend("<%= escape_javascript(render(#item)) %>");
Items Controller:
class ItemsController < ApplicationController
before_action :set_item, only: [:show, :edit, :update, :destroy]
before_action :set_todo
respond_to :html, :js
# GET /items
# GET /items.json
def index
#items = Item.all
end
# GET /items/1
# GET /items/1.json
def show
#item = Item.find(params[:id])
end
# GET /items/new
def new
#item = #todo.items.build
end
# GET /items/1/edit
def edit
#item = Items.find(params[:id])
end
# POST /items
# POST /items.json
def create
#item = #todo.items.build(item_params)
respond_with(#item) do |format|
if #item.save
format.html { redirect_to [#todo], notice: 'Item was successfully created.' }
format.json { render :show, status: :created, location: #item }
else
format.html { render :new }
format.json { render json: #item.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /items/1
# PATCH/PUT /items/1.json
def update
#item = Item.find(params[:id])
respond_to do |format|
if #item.update(item_params)
format.html { redirect_to [#todo, #item], notice: 'Item was successfully updated.' }
format.json { render :show, status: :ok, location: #item }
else
format.html { render :edit }
format.json { render json: #item.errors, status: :unprocessable_entity }
end
end
end
# DELETE /items/1
# DELETE /items/1.json
def destroy
#item = Item.find(params[:id])
#item.destroy
respond_to do |format|
format.html { redirect_to items_path }
format.json { head :no_content }
format.js { render layout: false }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_item
#item = Item.find(params[:id])
end
def set_todo
#todo = Todo.find(params[:todo_id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def item_params
params.require(:item).permit(:content, :todo_id)
end
end
Todos Controller
class TodosController < ApplicationController
respond_to :html, :js
before_action :set_todo, only: [:show, :edit, :update, :destroy]
# GET /todos
# GET /todos.json
def index
#todos = Todo.all
#todo = Todo.new
end
# GET /todos/1
# GET /todos/1.json
def show
#todo= Todo.find(params[:id])
end
# GET /todos/new
def new
#todo = Todo.new
#3.times{#todo.items.build}
end
# GET /todos/1/edit
def edit
end
# POST /todos
# POST /todos.json
def create
#todo = Todo.new(todo_params)
##todo.items.build
respond_to do |format|
if #todo.save
format.html { redirect_to todo_path, notice: 'Todo was successfully created.' }
format.json { render :show, status: :created, location: #todo }
else
format.html { render :new }
format.json { render json: #todo.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /todos/1
# PATCH/PUT /todos/1.json
def update
#todo = Todo.find(params[:id])
respond_to do |format|
if #todo.update(todo_params)
format.html { redirect_to todos_url, notice: 'Todo was successfully updated.' }
format.json { render :show, status: :ok, location: #todo }
else
format.html { render :edit }
format.json { render json: #todo.errors, status: :unprocessable_entity }
end
end
end
# DELETE /todos/1
# DELETE /todos/1.json
def destroy
#todo.destroy
#todo.items.destroy
respond_to do |format|
format.html { redirect_to todos_url, notice: 'Todo was successfully destroyed.' }
format.json { head :no_content }
end
end
def todo_completed
#todo = Todo.find(params[:id])
#todo.completed = true
if #todo.save
flash[:notice] = "Task was completed."
else
flash[:error] = "There was an error completing the task."
end
#redirect_to tasks_path
respond_with(#todo) do |f|
f.html { redirect_to todos_path }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_todo
#todo = Todo.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def todo_params
params.require(:todo).permit(:title, :completed, items_attributes: [:content,:completed, :_destroy])
end
end
This is the show page the one in the photo for Todo. It is rendering the items underneath that specific todo.
<p>Here are all the things you need to complete</p>
<div class="todo-items">
<%= render partial: 'items/item' %>
</div>
<br>
<div class='new-item'>
<%= render 'items/form'%>
</div>
<%= link_to 'Back', todos_path %>
Items/item
<p>todo/show this is the partial <%= #todo.title %></p>
<table class='table table-bordered'>
<thead>
<tr>
<th>Description</th>
<th> Time Left </th>
<th> Mark Complete </th>
</tr>
</thead>
<tbody>
<% #todo.items.each do |item| %>
<tr>
<td><%= link_to item.content, [#todo, item] %></td>
<td><%= item.days_left %>
<td>
<%= link_to todo_item_path(#todo,item), method: :delete, data: { confirm: 'Are you sure?' }, remote: true, class: 'delete_item' do %>
<i class="fa fa-check"></i>
<% end %>
</td>
</tr>
<% end %>
</tbody>
</table>
<br>
<p>end of partial item</p>
Item/form
<p>
<strong>Todo:</strong>
<%= #todo.title %>
</p>
<%= form_for [#todo, #todo.items.build], remote: true do |f| %>
<div class="field">
<%= f.label :content %><br>
<%= f.text_field :content %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
A little more info from the console. It starts when I click on the list(Groceries) and end with me hitting submit to add the item
Started GET "/todos/25" for 127.0.0.1 at 2014-10-08 19:19:58 -0500
Processing by TodosController#show as HTML
Parameters: {"id"=>"25"}
Todo Load (0.3ms) SELECT "todos".* FROM "todos" WHERE "todos"."id" = ? LIMIT 1 [["id", 25]]
CACHE (0.0ms) SELECT "todos".* FROM "todos" WHERE "todos"."id" = ? LIMIT 1 [["id", "25"]]
Item Load (0.4ms) SELECT "items".* FROM "items" WHERE "items"."todo_id" = ? [["todo_id", 25]]
Rendered items/_item.html.erb (42.9ms)
Rendered items/_form.html.erb (17.3ms)
Rendered todos/show.html.erb within layouts/application (66.6ms)
Completed 200 OK in 346ms (Views: 339.5ms | ActiveRecord: 1.3ms)
Started POST "/todos/25/items" for 127.0.0.1 at 2014-10-08 19:20:12 -0500
Processing by ItemsController#create as JS
Parameters: {"utf8"=>"✓", "item"=>{"content"=>"Cereal"}, "commit"=>"Create Item", "todo_id"=>"25"}
Todo Load (0.2ms) SELECT "todos".* FROM "todos" WHERE "todos"."id" = ? LIMIT 1 [["id", 25]]
(0.3ms) begin transaction
Todo Load (0.2ms) SELECT "todos".* FROM "todos" WHERE "todos"."id" = ? LIMIT 1 [["id", 25]]
SQL (0.5ms) INSERT INTO "items" ("content", "created_at", "todo_id", "updated_at") VALUES (?, ?, ?, ?) [["content", "Cereal"], ["created_at", "2014-10-09 00:20:12.060569"], ["todo_id", 25], ["updated_at", "2014-10-09 00:20:12.060569"]]
(148.1ms) commit transaction
Item Load (0.2ms) SELECT "items".* FROM "items" WHERE "items"."todo_id" = ? [["todo_id", 25]]
Rendered items/_item.html.erb (9.8ms)
Rendered items/create.js.erb (12.5ms)
Completed 200 OK in 180ms (Views: 15.2ms | ActiveRecord: 149.4ms)
The problem is with your create.js.erb file. You have below code:
$('.todo-items').prepend("<%= escape_javascript(render(#item)) %>");
In this code you are pre-pending html in todo-items you should replace items each time instead. like below :
$('.todo-items').html("<%= escape_javascript(render(#item)) %>");
This will replace html each time with new html.
So I've been searching for an answer to this, but I've been having a lot of trouble. I'm trying to have a form_for in rails that goes to the create action in the User controller, which updates the color of the submit button depending on whether it fails submitting or not. Here is my code, some of which is directly from a tutorial I found online:
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(user_params)
respond_to do |format|
if #user.save
format.html { redirect_to #user, notice: 'User was successfully created.' }
format.js {}
format.json { render json: #user, status: :created, location: #user }
else
format.html { render action: "new" }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
private
def user_params
params.require(:user).permit(:email)
end
And here is my create.js.coffee.erb file.
if(!isValidEmailAddress($('#e_submit').val())){
$('.button').text("Invalid Email");
$('.button').animate({"background-color":"#FFBF00"},300);
$('.button').hover(function(){
$(this).css("background-color","#FFBF00");
}, function(){
$(this).css("background-color","#FFBF00");
});
$('#e_submit').css("box-shadow","0 0 0px rgba(0,0,0,0)");
$('#e_submit').val("");
}
Basically, I want different js events to happen depending on the submitted information, and I don't know how to do this in rails.
You could check if there are any errors on #user in your js template to know if save is success or not, create.js.coffee.erb:
<% if #user.errors.empty? %>
# successfully saved
<% else %>
# failed to save
<% end %>
I'm a beginner in ruby on rails and I was practicing Ajax but unfortunately I can't seem to get my ajax to work. I'm trying to Ajaxify my delete in the scaffold.
Here's my controller:
class PlantsController < ApplicationController
# GET /plants
# GET /plants.json
def index
#plants = Plant.all
respond_to do |format|
format.html #index.html.erb
format.json { render json: #plants }
end
end
# GET /plants/1
# GET /plants/1.json
def show
#plant = Plant.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #plant }
end
end
# GET /plants/new
# GET /plants/new.json
def new
#plant = Plant.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #plant }
end
end
# GET /plants/1/edit
def edit
#plant = Plant.find(params[:id])
end
# POST /plants
# POST /plants.json
def create
#plant = Plant.new(params[:plant])
respond_to do |format|
if #plant.save
format.html { redirect_to #plant, notice: 'Plant was successfully created.' }
format.json { render json: #plant, status: :created, location: #plant }
else
format.html { render action: "new" }
format.json { render json: #plant.errors, status: :unprocessable_entity }
end
end
end
# PUT /plants/1
# PUT /plants/1.json
def update
#plant = Plant.find(params[:id])
respond_to do |format|
if #plant.update_attributes(params[:plant])
format.html { redirect_to #plant, notice: 'Plant was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #plant.errors, status: :unprocessable_entity }
end
end
end
# DELETE /plants/1
# DELETE /plants/1.json
def destroy
# binding.pry
#plant = Plant.find(params[:id])
#plant.destroy
respond_to do |format|
format.html { redirect_to plants_url }
format.js
format.json { head :no_content }
end
end
end
And here's the view for it:
<h1>Listing plants</h1>
<table class="show">
<tr>
<th>Name</th>
<th>Comment</th>
<th></th>
<th></th>
<th></th>
</tr>
<%= render "plants/plant" %>
</table>
<br />
<%= link_to 'New Plant', new_plant_path %>
here's my partial:
<% #plants.each do |plant| %>
<tr>
<td><%= plant.name %></td>
<td><%= plant.comment %></td>
<td><%= link_to 'Show', plant %></td>
<td><%= link_to 'Edit', edit_plant_path(plant) %></td>
<td><%= link_to 'Destroy', plant, confirm: 'Are you sure?', :method => :delete, :remote => true %></td>
</tr>
<% end %>
and lastly, here's the supposed destroy.js.erb that would refresh the DOM:
$('.show').html("<%= render #plant %>");
Anyone can shine a light on this please?
Additional Info:
The thing is, when I click the Destroy link... The record is being deleted, but the DOM isn't rendering and I have to refresh to see the changes :(
After a grueling night of thinking and eating here's the solution I have formulated.
First I will describe the root of my problem. It seems that this piece of code $('.show').html("<%= render #plant %>"), rails is trying to render route '/plants/:id/' but unfortunately, you just deleted it. The work around I made is to add #plants = Plant.all below the #plant.destroy code and in the destroy.js.erb i rendered the partial and it goes something like this:
$('.show').html("<%= render 'plants/plant' %>");
with that code, it will render the partial with the new set of queries values. Hopefully I explained it right.
Upon multiple trials I have tried a new tutorial which works fine for me. The link of the tutorial is at the bottom. I will still update this post if ever I found out the solution of this question.
tutorial link ---> Link here