New User not showing via Ajax in Rails - javascript

I was trying to work through Section 5 of Working with JavaScript in Rails http://guides.rubyonrails.org/working_with_javascript_in_rails.html .
Issue I have is that the new user isn't displayed in the show page until a refresh the entire page. I think I've replicated all the code correctly, but can't see what's wrong.
users_controller
class UsersController < ApplicationController
before_action :set_user, only: [:show, :edit, :update, :destroy]
def index
#users = User.all
#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 :new }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
user/index.html.erb
<b>Users</b>
<ul id="users">
<%= render #users %>
</ul>
<br>
<%= form_with(model: #user) do |f| %>
<%= f.label :name %><br>
<%= f.text_field :name %>
<%= f.submit %>
<% end %>
user/create.js.erb
$("<%= escape_javascript(render #user) %>").appendTo("#users");
logs
started POST "/users" for 127.0.0.1 at 2018-03-30 17:23:36 +0100
Processing by UsersController#create as JS
Parameters: {"utf8"=>"✓", "authenticity_token"=>"xMBe+h9zi5rWE8JXltYw1fGwQUP8AL2iWaaaGVqwbPvYavJgv7eQ0J5hwRL9xkb2S0pnose3X6oo+YXywvlcaQ==", "user"=>{"name"=>"Dave"}, "commit"=>"Create User"}
(0.2ms) BEGIN
SQL (0.7ms) INSERT INTO "users" ("name", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["name", "Dave"], ["created_at", "2018-03-30 16:23:36.474360"], ["updated_at", "2018-03-30 16:23:36.474360"]]
(6.3ms) COMMIT
Rendering users/create.js.erb
Rendered users/_user.html.erb (0.3ms)
Rendered users/create.js.erb (3.7ms)
Completed 200 OK in 28ms (Views: 13.6ms | ActiveRecord: 7.3ms)
Started GET "/users" for 127.0.0.1 at 2018-03-30 17:23:38 +0100
Processing by UsersController#index as HTML
Rendering users/index.html.erb within layouts/application
User Load (0.3ms) SELECT "users".* FROM "users"
Rendered collection of users/_user.html.erb [3 times] (0.4ms)
Rendered users/index.html.erb within layouts/application (5.4ms)
Completed 200 OK in 54ms (Views: 49.7ms | ActiveRecord: 0.3ms)

Related

Error when integrating Actioncable with Rails

I am attempting to build a group chat system where members of the group are able to leave comments in the group's chat section. I am using Actioncable with Rails 5 to implement this in real time, but am unable to overcome a particular hurdle. Chat comments are posted to the database as usual, but there is an error somewhere with the rendering and real-time functionality. Below are all of the affiliated code samples and the error from my terminal. I have been working on this for ages. I am in dire need of enlightenment.
The error: (The entire error message is very long, but this is where the error begins.)
Finished "/cable/" [WebSocket] for 127.0.0.1 at 2018-06-25 18:11:13 -0500
GroupChatCommentsChannel stopped streaming from group:5:group_chat_comments
Started GET "/cable" for 127.0.0.1 at 2018-06-25 18:11:13 -0500
Started GET "/cable/" [WebSocket] for 127.0.0.1 at 2018-06-25 18:11:13 -0500
Successfully upgraded to WebSocket (REQUEST_METHOD: GET, HTTP_CONNECTION: Upgrade, HTTP_UPGRADE: websocket)
GroupChatCommentsChannel is transmitting the subscription confirmation
GroupChatCommentsChannel#follow({"group_id"=>5})
GroupChatCommentsChannel is streaming from group:5:group_chat_comments
Started POST "/groups/5/group_chat_comments" for 127.0.0.1 at 2018-06-25 18:11:50 -0500
Processing by GroupChatCommentsController#create as JS
Parameters: {"utf8"=>"✓", "group_chat_comment"=>{"body"=>"2"}, "commit"=>"Comment", "group_id"=>"5"}
Group Load (0.4ms) SELECT `groups`.* FROM `groups` WHERE `groups`.`id` = 5 LIMIT 1
User Load (0.2ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 ORDER BY `users`.`id` ASC LIMIT 1
(0.2ms) BEGIN
Group Load (0.3ms) SELECT `groups`.* FROM `groups` WHERE `groups`.`id` = 5 LIMIT 1
SQL (5.8ms) INSERT INTO `group_chat_comments` (`body`, `user_id`, `group_id`, `created_at`, `updated_at`) VALUES ('2', 1, 5, '2018-06-25 23:11:50', '2018-06-25 23:11:50')
(7.2ms) COMMIT
[ActiveJob] Enqueued RenderGroupChatCommentJob (Job ID: ddceb77d-7c5c-42a3-8570-55d09a569c9e) to Async(default) with arguments: #<GlobalID:0x007f87af0ff7d8 #uri=#<URI::GID gid://news/GroupChatComment/4>>
Rendering group_chat_comments/create.js.erb
Rendered group_chat_comments/_new.html.erb (1.8ms)
Rendered group_chat_comments/create.js.erb (31.6ms)
Completed 200 OK in 201ms (Views: 75.1ms | ActiveRecord: 36.5ms)
GroupChatComment Load (0.2ms) SELECT `group_chat_comments`.* FROM `group_chat_comments` WHERE `group_chat_comments`.`id` = 4 LIMIT 1
[ActiveJob] [RenderGroupChatCommentJob] [ddceb77d-7c5c-42a3-8570-55d09a569c9e] Performing RenderGroupChatCommentJob (Job ID: ddceb77d-7c5c-42a3-8570-55d09a569c9e) from Async(default) with arguments: #<GlobalID:0x007f87ab6a1960 #uri=#<URI::GID gid://news/GroupChatComment/4>>
[ActiveJob] [RenderGroupChatCommentJob] [ddceb77d-7c5c-42a3-8570-55d09a569c9e] Rendered group_chat_comments/_group_chat_comment.html.erb (26.1ms)
[ActiveJob] [RenderGroupChatCommentJob] [ddceb77d-7c5c-42a3-8570-55d09a569c9e] Error performing RenderGroupChatCommentJob (Job ID: ddceb77d-7c5c-42a3-8570-55d09a569c9e) from Async(default) in 60.85ms: ActionView::Template::Error (No route matches {:action=>"show", :controller=>"group_chat_comments", :group_id=>nil, :id=>"4"}, missing required keys: [:group_id]):
The code:
Routes:
resources :groups do
resources :group_chat_comments
end
mount ActionCable.server => '/cable'
Model:
class GroupChatComment < ApplicationRecord
belongs_to :user
belongs_to :group
after_create_commit { RenderGroupChatCommentJob.perform_later self }
end
Controller:
class GroupChatCommentsController < ApplicationController
before_action :set_group
before_action :authenticate_user!
def create
#group_chat_comment = GroupChatComment.create! body: params[:group_chat_comment][:body], group_id: #group.id, user: current_user
end
def destroy
#group_chat_comment = GroupChatComment.find(params[:id])
#group_chat_comment.destroy
redirect_to #group
end
private
def set_group
#group = Group.find(params[:group_id])
end
end
Job:
class RenderGroupChatCommentJob < ApplicationJob
queue_as :default
def perform(group_chat_comment)
ActionCable.server.broadcast "group:#{group_chat_comment.group_id}:group_chat_comments", foo: render_group_chat_comment(group_chat_comment)
end
private
def render_group_chat_comment(group_chat_comment)
ApplicationController.renderer.render(partial: 'group_chat_comments/group_chat_comment', locals: { group_chat_comment: group_chat_comment })
end
end
Channel:
class GroupChatCommentsChannel < ApplicationCable::Channel
def follow(params)
stop_all_streams
stream_from "group:#{params['group_id'].to_i}:group_chat_comments"
end
def unfollow
stop_all_streams
end
end
Channel CoffeeScript:
App.group_chat_comments = App.cable.subscriptions.create "GroupChatCommentsChannel",
collection: -> $('#group_chat_comments')
connected: ->
setTimeout =>
#followCurrentGroup()
, 1000
disconnected: ->
followCurrentGroup: ->
groupId = #collection().data('group-id')
if groupId
#perform 'follow', group_id: groupId
else
#perform 'unfollow'
received: (data) ->
#collection().append(data['group_chat_comment'])
Cable.js:
//= require action_cable
//= require_self
//= require_tree ./channels
(function() {
this.App || (this.App = {});
App.cable = ActionCable.createConsumer();
}).call(this);
The views:
Create.js:
$('#new_group_chat_comment').replaceWith('<%=j render 'group_chat_comments/new', group: #group %>');
_group_chat_comment partial:
<% cache group_chat_comment do %>
<div class="group_chat_comment">
<p>
<%= group_chat_comment.body %>
<%= link_to 'Delete', group_group_chat_comment_path(#group, group_chat_comment), method: :delete %>
</p>
</div>
<% end %>
_group_chat_comments partial:
<%= render 'group_chat_comments/new', group: #group %>
<section id="group_chat_comments" data-group-id="<%= #group.id %>">
<%= render #group.group_chat_comments %>
</section>
_new partial:
<%= form_for [ #group, GroupChatComment.new ], remote: true do |f| %>
<div class="group-comment">
<%= f.text_area :body %>
</div>
<br>
<div class="group-comment-submit">
<%= f.submit 'Comment' %>
</div>
<% end %>

load more comment in rails use javascript

I want to load more comments but it is not working as I wanted, I've checked the console, it is showing no change, here is the code for my _post.html.erb:
<div class="posts-wrapper">
<div class="post">
<div class="post-head">
<div class="thumb-img"></div>
<div class="user-name">
<%= post.user.name %>
<span class="badge"><%= time_ago_in_words(post.created_at) %></span>
</div>
</div>
<div class="image center-block">
<%= link_to (image_tag post.image.url(:medium), class:'img-responsive'), post_path(post) %>
</div>
<div class="post-bottom">
<div class="caption">
<div class="user-name">
<%= post.user.name %>
</div>
<span class="label label-info">caption:</span><span style="color: black"><%= post.caption %></span>
</div>
<div class="comments" id="comments_<%= post.id %>">
<% if post.comments.any? %>
<div class="paginator" id="comments-paginator-<%= post.id %>">
<% unless post.comments.count <= 4 %>
<%= link_to "view all #{post.comments.count} comments", post_comments_path(post), remote: true, class: 'more-comments', data: {post_id: "#{post.id}", type: "html"} %>
<% end %>
</div>
<%= render post.comments.last(4), post: post %>
<% end %>
</div>
</div>
<div class="comment-like-form row">
<div class="like-button col-sm-1">
<span class="glyphicon glyphicon-heart-empty"></span>
</div>
<div class="comment-form col-sm-11">
<%= form_for([post, post.comments.build], remote: true) do |f| %>
<%= f.text_field :content, placeholder: 'Add a comment...',class: "form-control comment_content", id: "comment_content_#{post.id}"%>
<% end %>
</div>
</div>
</div>
</div>
and here is _comment.html.erb
<div class="comment">
<div class="user-name">
<% if comment.user %>
<%= comment.user.name %>
<% end %>
</div>
<div class="comment-content">
<%= comment.content %>
<span class="label label-warning"><%= time_ago_in_words(comment.created_at)%></span>
</div>
<% if comment.user == current_user %>
<%= link_to post_comment_path(post, comment),id:"post#{post.id}comment#{comment.id}", method: :delete, data: { confirm: "Are you sure?" },remote: true do %>
<span class="glyphicon glyphicon-trash"></span>
<% end %>
<% end %>
</div>
and here is comment_controller
class CommentsController < ApplicationController
before_action :set_post
def index
#comments = #post.comments.order("created_at ASC")
respond_to do |format|
format.html { render :layout => !request.xhr? }
end
end
def create
#comment = #post.comments.build(comment_params)
#comment.user_id = current_user.id
if #comment.save
respond_to do |format|
format.html { redirect_to posts_path }
format.js
end
else
flash[:alert] = "Check the comment form, something went wrong."
render posts_path
end
end
def destroy
#comment = #post.comments.find(params[:id])
#comment.destroy
respond_to do |format|
format.html { redirect_to posts_path }
format.js
end
# flash[:success] = "Comment deleted :("
# redirect_to posts_path
end
private
def comment_params
params.require(:comment).permit(:content)
end
def set_post
#post = Post.find(params[:post_id])
end
end
and here is post controller
class PostsController < ApplicationController
before_action :set_post,only: [:edit,:show,:update,:destroy]
before_action :logged_in_user, only: [:create, :destroy,:new]
before_action :owned_post, only: [:edit, :update, :destroy]
def index
#posts = Post.all.order('created_at DESC').page(params[:page]).per_page(3)
# #posts = Post.all.order('created_at DESC').paginate(page: params[:page])
respond_to do |format|
format.html
format.js
format.json { render json: #projects }
end
end
def new
#post = current_user.posts.build
end
def create
if #post = current_user.posts.create(post_params)
flash[:success] = "Your post has been created!"
redirect_to posts_path
else
flash.now[:alert] = "Your new post couldn't be created! Please check the form."
render :new
end
end
def show
end
def destroy
#post.destroy
redirect_to posts_path
end
def edit
end
def update
if #post.update(post_params)
flash[:success] = "Post updated."
redirect_to posts_path
else
flash.now[:alert] = "Update failed. Please check the form."
render :edit
end
end
private
def owned_post
unless current_user == #post.user
flash[:alert] = "That post doesn't belong to you!"
redirect_to root_path
end
end
def set_post
#post = Post.find(params[:id])
end
def post_params
params.require(:post).permit(:image, :caption)
end
end
and here is JavaScript in asset to load more comment loadmorecomment.js
$( document ).ready(function() {
$('.more-comments').click( function() {
// alert("ok roi day");
$(this).on('ajax:success', function(event, data, status, xhr) {
event.preventDefault();
var postId = $(this).data("post-id");
$("#comments_" + postId).html(data);
$("#comments-paginator-" + postId).html("<a id='more-comments' data-post-id=" + postId + " data-type='html' data-remote='true' href='/posts/" + postId + "/comments'>show more comments</a>");
console.log("<a id='more-comments' data-post-id=" + postId + " data-type='html' data-remote='true' href='/posts/" + postId + "/comments'>show more comments</a>");
});
});
});
but when I click nothing happens, but in the console it says:
Started GET "/posts/25/comments" for 127.0.0.1 at 2017-08-12 18:39:49 +0700
Processing by CommentsController#index as HTML
Parameters: {"post_id"=>"25"}
Post Load (0.2ms) SELECT "posts".* FROM "posts" WHERE "posts"."id" = ? LIMIT ? [["id", 25], ["LIMIT", 1]]
Rendering comments/index.html.erb
Comment Load (0.3ms) SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = ? ORDER BY created_at ASC [["post_id", 25]]
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 53], ["LIMIT", 1]]
CACHE User Load (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 53], ["LIMIT", 1]]
CACHE User Load (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 53], ["LIMIT", 1]]
CACHE User Load (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 53], ["LIMIT", 1]]
CACHE User Load (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 53], ["LIMIT", 1]]
CACHE User Load (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 53], ["LIMIT", 1]]
Rendered collection of comments/_comment.html.erb [5 times] (14.1ms)
Rendered comments/index.html.erb (16.6ms)
Completed 200 OK in 20ms (Views: 17.0ms | ActiveRecord: 0.8ms)

Rails 5.0 - Comments not working

I'm implementing comments as a nested resource for an events app and hitting one issue after another. Initially it worked fine, however, the only functionality they had was create & destroy. I want to add an edit function using Ajax/remote: true for same page editing (never done it before) and I've hit a wall. The edit link_to doesn't/has never worked and now even the create function doesn't work. This is what's coming through on the development log -
Processing by CommentsController#create as JS
Parameters: {"utf8"=>"✓", "comment"=>{"body"=>"Comment."}, "commit"=>"Create Comment", "event_id"=>"27"}
[1m[36mComment Load (0.1ms)[0m [1m[34mSELECT "comments".* FROM "comments" WHERE "comments"."id" = ? LIMIT ?[0m [["id", nil], ["LIMIT", 1]]
Completed 404 Not Found in 1ms (ActiveRecord: 0.1ms)
ActiveRecord::RecordNotFound (Couldn't find Comment with 'id'=):
I've tried all sorts of different parameters via trial and error but the 'id' issue keeps springing up. Here's my code -
comments_controller.rb
class CommentsController < ApplicationController
before_action :set_comment, only: [:show, :create, :edit, :update, :destroy]
def create
#event = Event.find(params[:event_id])
#comment = #event.comments.create(comment_params)
#comment.user_id = current_user.id
if #comment.save
redirect_to #event
else
render 'new'
end
end
# GET /comments/1/edit
def edit
#event = #comment.event
#comment = #event.comments.find(params[:id])
respond_to do |f|
f.js
f.html
end
end
def show
end
def update
if #comment.update(comment_params)
redirect_to #event, notice: "Comment was successfully updated!"
else
render 'edit'
end
end
def destroy
#event = Event.find(params[:event_id])
#comment = #event.comments.find(params[:id])
#comment.destroy
redirect_to event_path(#event)
end
private
def set_comment
#comment = Comment.find(params[:id])
end
def set_event
#event = Event.find(params[:event_id])
end
def comment_params
params.require(:comment).permit(:name, :body)
end
end
_comment.html.erb
<div class="comment clearfix">
<div class="comment_content">
<div id="<%=dom_id(comment)%>" class="comment">
<p class="comment_name"><strong><%= comment.name %></strong></p>
<p class="comment_body"><%= comment.body %></p>
</div>
<p><%= link_to 'Edit', edit_event_comment_path([comment.event, comment]), id: "comment", remote: true %></p>
<p><%= link_to 'Delete', [comment.event, comment],
method: :delete,
class: "button",
data: { confirm: 'Are you sure?' } %></p>
</div>
</div>
_form.html.erb
<%= simple_form_for([#event, #comment], remote: true) do |f| %>
<%= f.label :comment %><br>
<%= f.text_area :body %><br>
<br>
<%= f.button :submit, label: 'Add Comment', class: "btn btn-primary" %>
<% end %>
edit.js.erb
$('#comment').append('<%= j render 'form' %>');
I think I'm getting mixed up with the 'id's for this thing and how to get the remote: true function working on the page. I don't want to accept defeat but I may have to if I don't get this working.
UPDATE -
When I try and edit an existing comment I get this in my development log -
Started GET "/events/27%2F32/comments/27/edit" for ::1 at 2017-05-24 12:28:20 +0100
Processing by CommentsController#edit as JS
Parameters: {"event_id"=>"27/32", "id"=>"27"}
[1m[36mComment Load (0.1ms)[0m [1m[34mSELECT "comments".* FROM "comments" WHERE "comments"."id" = ? LIMIT ?[0m [["id", 27], ["LIMIT", 1]]
Completed 404 Not Found in 1ms (ActiveRecord: 0.1ms)
ActiveRecord::RecordNotFound (Couldn't find Comment with 'id'=27):
The route doesn't make sense - "/events/27%2F32/comments/27/edit" - the comment id should be 32 and the event id 27.
routes.rb
Rails.application.routes.draw do
devise_for :users, :controllers => { omniauth_callbacks: "omniauth_callbacks", registrations: "registrations" }
resources :users
# the above resource draws out routes for user profiles
resources :events do
resources :comments
resources :bookings
end
root 'events#index'
change
before_action :set_comment, only: [:show, :create, :edit, :update, :destroy]
to
before_action :set_comment, only: [:show, :edit, :update, :destroy]
you can't set comment when you are creating it.
Also, as discussed in the comments, your edit link should be,
<%= link_to 'Edit', [comment.event, comment], id: "comment", remote: true %>

AJAX not rendering update prior to manual page refresh

I'm working through Agile Web Development with Rails 4, stuck on Chapter 11, Iteration F4. The goal of the section is to have a shopping cart div display only when there are items in the cart, otherwise hide it via display: none. The cart does properly hide itself when the Empty Cart button is clicked, but when an item is then added to it, the cart doesn't display unless the page is then manually refreshed.
Here's what the server output looks like when an item is added to an empty cart:
Started POST "/line_items?product_id=2" for ::1 at 2015-05-10 18:51:44 -0700
Processing by LineItemsController#create as JS
Parameters: {"authenticity_token"=>"v2zcRr2CPsfZP3/qI8l5m0HWdDoOiiyl5oiZxvpYKXp7K2ecXizzCZA37DLm7PwYuSAgemogwjjnDHz4NbavGA==", "product_id"=>"2"}
Cart Load (0.2ms) SELECT "carts".* FROM "carts" WHERE "carts"."id" = ? LIMIT 1 [["id", 21]]
Product Load (0.1ms) SELECT "products".* FROM "products" WHERE "products"."id" = ? LIMIT 1 [["id", 2]]
LineItem Load (0.1ms) SELECT "line_items".* FROM "line_items" WHERE "line_items"."cart_id" = ? AND "line_items"."product_id" = ? LIMIT 1 [["cart_id", 21], ["product_id", 2]]
(0.1ms) begin transaction
SQL (0.4ms) INSERT INTO "line_items" ("product_id", "cart_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["product_id", 2], ["cart_id", 21], ["created_at", "2015-05-11 01:51:44.341300"], ["updated_at", "2015-05-11 01:51:44.341300"]]
(1.1ms) commit transaction
LineItem Load (0.2ms) SELECT "line_items".* FROM "line_items" WHERE "line_items"."cart_id" = ? [["cart_id", 21]]
Product Load (0.2ms) SELECT "products".* FROM "products" WHERE "products"."id" = ? LIMIT 1 [["id", 2]]
Rendered line_items/_line_item.html.erb (2.0ms)
Rendered carts/_cart.html.erb (6.7ms)
Rendered line_items/create.js.erb (8.9ms)
And here's what the output looks like when I refresh:
Started GET "/" for ::1 at 2015-05-10 18:57:17 -0700
Processing by StoreController#index as HTML
Cart Load (0.1ms) SELECT "carts".* FROM "carts" WHERE "carts"."id" = ? LIMIT 1 [["id", 21]]
Product Load (2.2ms) SELECT "products".* FROM "products" ORDER BY "products"."updated_at" DESC LIMIT 1
Product Load (0.2ms) SELECT "products".* FROM "products" ORDER BY "products"."title" ASC
Rendered store/index.html.erb within layouts/application (17.9ms)
LineItem Exists (0.2ms) SELECT 1 AS one FROM "line_items" WHERE "line_items"."cart_id" = ? LIMIT 1 [["cart_id", 21]]
LineItem Load (0.2ms) SELECT "line_items".* FROM "line_items" WHERE "line_items"."cart_id" = ? [["cart_id", 21]]
Product Load (0.1ms) SELECT "products".* FROM "products" WHERE "products"."id" = ? LIMIT 1 [["id", 2]]
Rendered line_items/_line_item.html.erb (1.6ms)
Rendered carts/_cart.html.erb (6.2ms)
Completed 200 OK in 129ms (Views: 124.7ms | ActiveRecord: 3.0ms)
Here's my LineItemsController, containing the create action triggered when I add the item to the empty cart:
class LineItemsController < ApplicationController
include CurrentCart
before_action :set_cart, only: [:create]
before_action :set_line_item, only: [:show, :edit, :update, :destroy]
# GET /line_items
# GET /line_items.json
def index
#line_items = LineItem.all
end
# GET /line_items/1
# GET /line_items/1.json
def show
end
# GET /line_items/new
def new
#line_item = LineItem.new
end
# GET /line_items/1/edit
def edit
end
# POST /line_items
# POST /line_items.json
def create
product = Product.find(params[:product_id])
#line_item = #cart.add_product(product.id)
respond_to do |format|
if #line_item.save
format.html { redirect_to store_url }
format.js { #current_item = #line_item }
format.json { render action: 'show', status: :created, location: #line_item }
else
format.html { render action: 'new' }
format.json { render json: #line_item.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /line_items/1
# PATCH/PUT /line_items/1.json
def update
respond_to do |format|
if #line_item.update(line_item_params)
format.html { redirect_to #line_item, notice: 'Line item was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #line_item.errors, status: :unprocessable_entity }
end
end
end
# DELETE /line_items/1
# DELETE /line_items/1.json
def destroy
#line_item.destroy
respond_to do |format|
format.html { redirect_to line_items_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_line_item
#line_item = LineItem.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def line_item_params
params.require(:line_item).permit(:product_id)
end
end
And this is my StoreController, containing the index action triggered by the manual refresh:
class StoreController < ApplicationController
include CurrentCart
before_action :set_cart
def index
#products = Product.order(:title)
end
end
It seems that when I empty the cart and inspect the div, the display: none attribute is applied (as it should be) from application.html.erb:
<!DOCTYPE html>
<html>
<head>
<title>Pragprog Books Online Store</title>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %>
<%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
<%= csrf_meta_tags %>
</head>
<body class='<%= controller.controller_name %>'>
<div id="banner">
<%= image_tag("logo.png") %>
<%= #page_title || "Pragmatic Bookshelf" %>
</div>
<div id="columns">
<div id="side">
<%= hidden_div_if(#cart.line_items.empty?, id: 'cart') do %>
<%= render #cart %>
<% end %>
<ul>
<li>Home</li>
<li>Questions</li>
<li>News</li>
<li>Contact</li>
</ul>
</div>
<div id="main">
<%= yield %>
</div>
</div>
</body>
</html>
The hidden_div_if method is in ApplicationHelper.rb:
module ApplicationHelper
def hidden_div_if(condition, attributes = {}, &block)
if condition
attributes["style"] = "display: none"
end
content_tag("div", attributes, &block)
end
end
But when I inspect the cart div after adding an item, display: none is still applied. What am I missing?
If it matters, here's my CartsController:
class CartsController < ApplicationController
before_action :set_cart, only: [:show, :edit, :update, :destroy]
rescue_from ActiveRecord::RecordNotFound, with: :invalid_cart
# GET /carts
# GET /carts.json
def index
#carts = Cart.all
end
# GET /carts/1
# GET /carts/1.json
def show
end
# GET /carts/new
def new
#cart = Cart.new
end
# GET /carts/1/edit
def edit
end
# POST /carts
# POST /carts.json
def create
#cart = Cart.new(cart_params)
respond_to do |format|
if #cart.save
format.html { redirect_to #cart, notice: 'Cart was successfully created.' }
format.json { render :show, status: :created, location: #cart }
else
format.html { render :new }
format.json { render json: #cart.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /carts/1
# PATCH/PUT /carts/1.json
def update
respond_to do |format|
if #cart.update(cart_params)
format.html { redirect_to #cart, notice: 'Cart was successfully updated.' }
format.json { render :show, status: :ok, location: #cart }
else
format.html { render :edit }
format.json { render json: #cart.errors, status: :unprocessable_entity }
end
end
end
# DELETE /carts/1
# DELETE /carts/1.json
def destroy
#cart.destroy if #cart.id == session[:cart_id]
session[:cart_id] = nil
respond_to do |format|
format.html { redirect_to store_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_cart
#cart = Cart.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def cart_params
params[:cart]
end
def invalid_cart
logger.error "Attempt to access invalid cart #{params[:id]}"
redirect_to store_url, notice: 'Invalid cart'
end
end
After a couple of days of spinning my wheels, I finally noticed that the jQuery selectors in my create.js.erb were missing the #, i.e.:
if ($('cart tr').length == 1) {
$('cart').show('blind', 1000);
}
Instead of:
if ($('#cart tr').length == 1) {
$('#cart').show('blind', 1000);
}
Adding them fixed it.

Rails 4 Ajax rendering partial multiple times on same page each time submit is hit

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.

Categories

Resources