I am building a rails app that creates a like(create) or unlike(destroy), on a link click with a remote: true ajax request
The Post goes through and the transaction is committed but the the js response is not going through and I receive a Error in the server console
No template found for ForumThreads::ForumPosts::LikesController#create, head :no_content
the view structure is
forum_threds/show.html.haml
....
= render #forum_thread.forum_posts
....
to render a view of every post that is in the thread
than
forum_posts/_forum_post.html.haml
....
%div{id: "fourm_post_#{forum_post.id}" }
= render partial: "forum_posts/likes", locals: { post: forum_post }
....
which is rendering
forum_posts/_likes.html.haml
- if user_signed_in? && current_user.likes?(post)
= link_to "UnLike", forum_post_likes_path(post), method: :delete,
remote: true
- else
= link_to "Like", forum_post_likes_path(post), method: :post, remote:
true
and my controller is
class ForumThreads::ForumPosts::LikesController < ApplicationController
before_action :authenticate_user!
before_action :set_forum_post
before_action :set_forum_thread
def create
#forum_post.likes.where(user_id: current_user.id).first_or_create
respond_to do |format|
format.js
format.html { redirect_to #forum_thread}
end
end
def destroy
#forum_post.likes.where(user_id: current_user.id).destroy_all
respond_to do |format|
format.js
format.html { redirect_to #forum_thread}
end
end
private
def set_forum_thread
#forum_thread = ForumThread.find(#forum_post.forum_thread_id)
end
def set_forum_post
#forum_post = ForumPost.find(params[:forum_post_id])
end
end
With remote: false this works as expected with the page reload but when i put remote: true I receive the no template error
my file structure for the views
forum_threads/
show.html.haml
forum_posts/
show.html.haml
_likes.html.haml
likes/
create.js.erb
destroy.js.erb
and the create.js.erb and destroy.js.erb
$("#forum_post_<%= forum_post.id %>").html(<%= j render
'forum_posts/likes' %>);
but i know that this file isn't even being called
is there a way from the format.js that i may be able to call the file i need directly? or something?
Related
I have problem with my HTML sortable update in rails 6. I drag and drop some portfolio images through web page and that is working good but when i want to update the web page with updated position i got NoMethodError (undefined method `each' for nil:NilClass):
I use this script for HTML Sortable
https://github.com/jordanhudgens/devcamp-portfolio/blob/master/app/assets/javascripts/html.sortable.js
portfolio_controller:
class PortfoliosController < ApplicationController
before_action :set_portfolio_item, only: [:edit, :show,:update, :destroy]
layout "portfolio"
access all: [:show, :index, :angular], user: {except: [:destroy, :new, :create, :update, :edit]}, site_admin: :all
def index
#portfolio_items = Portfolio.by_position
end
def sort
params[:order].each do |key, value|
Portfolio.find(value[:id]).update(position: value[:position])
end
head :ok
end
def angular
#angular_portfolio_items = Portfolio.angular
end
def new
#portfolio_item = Portfolio.new
3.times { #portfolio_item.technologies.build }
end
def create
#portfolio_item = Portfolio.new(portfolio_params)
respond_to do |format|
if #portfolio_item.save
format.html { redirect_to portfolios_path, notice: 'Your Portfolio item is now live.' }
else
format.html { render :new }
end
end
end
def edit
end
def update
respond_to do |format|
if #portfolio_item.update(portfolio_params)
format.html { redirect_to portfolios_path, notice: 'The Record successfully updated.' }
else
format.html { render :edit }
end
end
end
def show
end
def destroy
#Destroy/delete the record
#portfolio_item.destroy
#Redirect
respond_to do |format|
format.html { redirect_to portfolios_url, notice: 'Record was removed' }
end
end
private
def set_portfolio_item
#portfolio_item = Portfolio.find(params[:id])
end
def portfolio_params
params.require(:portfolio).permit(:title,
:subtitle,
:body,
technologies_attributes: [:name]
)
end
end
routes.rb:
Rails.application.routes.draw do
devise_for :users, path: '', path_names: { sign_in: 'login', sign_out: 'logout', sign_up: 'register'}
resources :portfolios, except: [:show] do
put :sort, on: :collection
end
get 'angular-items', to: 'portfolios#angular'
get 'portfolio/:id', to: 'portfolios#show', as: 'portfolio_show'
get 'about-me', to: 'pages#about'
get 'contact', to: 'pages#contact'
resources :blogs do
member do
get :toggle_status
end
end
root to: 'pages#home'
end
Here is my portfolio.js :
var ready, set_position;
ready = void 0;
set_position = void 0;
set_position = function() {
$(".card").each(function(i) {
$(this).attr('data-pos', i + 1);
});
};
ready = function() {
set_position();
$('#sortable').sortable();
$('#sortable').sortable().bind('sortupdate', function(e, ui) {
var updated_order;
updated_order = [];
set_position();
$(".card").each(function(i) {
updated_order.push;
({
id: $(this).data('id'),
position: i + 1
});
});
$.ajax({
type: 'PUT',
url: '/portfolios/sort',
data: {
order: updated_order
}
});
});
};
$(document).ready(ready);
_portfolio_item.html.erb :
<div class="card" data-id="<%= portfolio_item.id %>">
<%= image_tag portfolio_item.thumb_image unless portfolio_item.thumb_image.nil? %>
<p class="card-text">
<span>
<%= link_to portfolio_item.title, portfolio_show_path(portfolio_item) %>
</span>
<%= portfolio_item.subtitle %>
</p>
</div>
Thank you for your helps!
The only place I see an each statement is in the sort method of your PortfoliosController.
params[:order].each
You need to set your params[:order] or prevent this error from happening by checking if it is set. You could use the save navigation operator:
params[:order]&.each
Or you could check if the order parameter is set with the present? method.
if params[:order].present?
params[:order]&.each
I am trying to use cocoon gem to build nested forms.
I have models for Organisation, Package::Bip and Tenor.
The associations are:
Organisation
has_many :bips, as: :ipable, class_name: Package::Bip
accepts_nested_attributes_for :bips, reject_if: :all_blank, allow_destroy: true
Package::Bip (polymorphic)
belongs_to :ipable, :polymorphic => true, optional: true, inverse_of: :bip
has_one :tenor, as: :tenor
accepts_nested_attributes_for :tenor, reject_if: :all_blank, allow_destroy: true
Tenor (polymorphic)
belongs_to :tenorable, :polymorphic => true, optional: true
The forms have:
In my organisations/_form.html.erb, I have:
<%= f.simple_fields_for :bips do |f| %>
<%= f.error_notification %>
<%= render 'package/bips/bip_fields', f: f %>
<% end %>
<%= link_to_add_association 'Add another intellectual property resource', f, :bips, partial: 'package/bips/bip_fields' %>
In my bip_fields.html.erb nested form, I have:
<%# if #package_bips.tenor.blank? %>
<%= link_to_add_association 'Add timing', f, :tenor, partial: 'tenors/tenor_fields' %>
<%# end %>
<%= f.simple_fields_for :tenor do |tenor_form| %>
<%= f.error_notification %>
<%= render 'tenors/tenor_fields', f: tenor_form %>
<% end %>
Javascript
The cocoon docs suggest adding a js file to specify association-insertion-node as a function. In my tenor_subform.js I have:
$(document).ready(function() {
$(".add_tenor a").
data("association-insertion-method", 'append').
data("association-insertion-node", function(link){
return link.closest('.row').next('.row').find('.tenor_form')
});
});
Controllers
In my organisation controller, I have:
def new
#organisation = Organisation.new
#organisation.bips
end
Note: I'm not sure if I need to add another line to my new action to create the organisation.bip.tenor instance. I'm also unsure if im supposed to add has_one through association on the organisation.rb that references the tenor.
def organisation_params
params.fetch(:organisation, {}).permit(:title, :comment,
bips_attributes: [:id, :status, :_destroy,
tenor_attributes: [:id,:commencement, :expiry, :_destroy]
],
In my tenor controller, I have:
def tenor_params
params.require(:tenor).permit( :commencement, :expiry)
end
ERRORS
I am not sure if I need to add tenor actions to the organisation controller (the ultimate parent of bip which in turn is the parent of tenor).
When I save all of this and try it, I get an error that says:
unknown attribute 'tenor_id' for Tenor.
When I see other SO posts with this error, its often because the :id attribute hasn't been whitelisted in the parent class. I have done that.
Can anyone see what I've done wrong?
Tenor controller
class TenorsController < ApplicationController
before_action :set_tenor, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!
# after_action :verify_authorized
def index
#tenors = Tenor.all
# authorize #tenors
end
def show
end
def new
#tenor = Tenor.new
# authorize #tenor
end
def edit
end
def create
#tenor = Tenor.new(tenor_params)
# authorize #tenor
respond_to do |format|
if #tenor.save
format.html { redirect_to #tenor }
format.json { render :show, status: :created, location: #tenor }
else
format.html { render :new }
format.json { render json: #tenor.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #tenor.update(tenor_params)
format.html { redirect_to #tenor }
format.json { render :show, status: :ok, location: #tenor }
else
format.html { render :edit }
format.json { render json: #tenor.errors, status: :unprocessable_entity }
end
end
end
def destroy
#tenor.destroy
respond_to do |format|
format.html { redirect_to action: :index }
format.json { head :no_content }
end
end
private
def set_tenor
#tenor = Tenor.find(params[:id])
# authorize #tenor
end
def tenor_params
params.require(:tenor).permit(:express_interest, :commencement, :expiry, :enduring, :repeat, :frequency)
end
end
You has_one relation is wrongly declared. Because you say as: :tenor makes it look for a tenor_id.
You have to declare it as follows:
has_one :tenor, as: :tenorable
Your model does not see the id of nested_attr.Add :inverse_of => #{model}.
Example:
class Tenor < ActiveRecord::Base
has_many :traps, :inverse_of => :bips
end
For more information look this or this documentation.
Vehicles_controller.rb
class VehiclesController < ApplicationController
before_action :set_vehicle, only: [:show, :edit, :update, :destroy]
load_and_authorize_resource
# skip_before_action :verify_authenticity_token
# GET /vehicles
# GET /vehicles.json
def index
#q = Vehicle.search(params[:q])
#vehicles = #q.result(:distinct => true).order_by([:updated_at, :desc]).page(params[:page]).per(5)
#vehicle = Vehicle.new
end
# GET /vehicles/1
# GET /vehicles/1.json
def show
end
# GET /vehicles/new
def new
end
# GET /vehicles/1/edit
def edit
end
# POST /vehicles
# POST /vehicles.json
def create
params[:vehicle][:name] = params[:vehicle][:name].upcase if !params[:vehicle][:name].nil?
#vehicle = Vehicle.new(vehicle_params)
#vehicles = Vehicle.all.order_by([:updated_at, :desc]).page(params[:page]).per(5)
respond_to do |format|
if #vehicle.save
format.html { redirect_to :back, notice: 'Vehicle was successfully created.' }
format.json { render action: 'index', status: :created, location: #vehicle }
format.js
else
format.js
format.html { render action: 'new' }
format.json { render json: #vehicle.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /vehicles/1
# PATCH/PUT /vehicles/1.json
def update
params[:vehicle][:name] = params[:vehicle][:name].upcase if !params[:vehicle][:name].nil?
respond_to do |format|
if #vehicle.update(vehicle_params)
format.html { redirect_to #vehicle, notice: 'Vehicle was successfully updated.' }
format.json {render json: #vehicle, status: :ok}
else
format.html { render action: 'edit' }
format.json { render json: #vehicle.errors, status: :unprocessable_entity }
end
end
end
# DELETE /vehicles/1
# DELETE /vehicles/1.json
def destroy
#vehicle.destroy
respond_to do |format|
format.html { redirect_to vehicles_url, notice: "#{#vehicle.name} deleted successfully" }
format.json { head :no_content }
format.js { render :layout => false}
end
end
def vehicle_search
#q = Vehicle.search(params[:q])
#vehicles = #q.result(:distinct => true).order_by([:updated_at, :desc]).page(params[:page]).per(5)
respond_to do |format|
format.html
format.js
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_vehicle
#vehicle = Vehicle.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def vehicle_params
params.require(:vehicle).permit(:name, :created_at, :updated_at)
end
end
index.html.erb
<% #vehicles.each do |vehicle| %>
<tr>
<td><%= best_in_place vehicle,:name, class: "v_name", id: vehicle.id%></td>
<td><%= link_to '<i class="fa fa-trash-o"></i>'.html_safe, vehicle, method: :delete, remote: true, data: { confirm: "Are you sure to delete <b>\"#{vehicle.name}\"?</b>", commit: "OK" }, title: "Delete Vehicle", class: "btn btn-danger delete_vehicle" %>
</td>
</tr>
<%end%>
vehicle.rb
class Vehicle
include Mongoid::Document
include Mongoid::Timestamps
field :name, type: String
validates :name, presence: true, uniqueness: true, :format => {:with => /[1-9a-zA-Z][0-9a-zA-Z ]{3,}/}
has_many :pre_processings
has_many :batch_counts
end
destroy.js.erb
$('.delete_vehicle').bind('ajax:success', function() {
$(this).closest('tr').fadeOut();
});
$(".alert").show();
$(".alert").html("Vehicle \"<b><%=#vehicle.name %></b>\" deleted successfully.")
$(".alert").fadeOut(5000);
Here i am using destroy.js.erb to delete vehicle name. It works fine.
Here i am using Inline Edit for Best_in_place. After update the Vehicle name, The ajax alert shows previous vehicle name. Not update vehicle name. So how to show updated vehicle name alert.
$(".alert").html("Vehicle \"<b><%=#vehicle.name %></b>\" deleted successfully.")
The above alert i will show current vehicle name to delete. But if i update using inline edit after i want to delete i'll shows previous record.
Example:
Vehicle Name: MH P5 2312 , I want to delete it, The alert shows are you delete "MH P5 2312 " Vehicle.
After inline edit i will change Vehicle Name: AP 16 1234, So i want to delete Vehicle Name: AP 16 1234, but the ajax alert shows, Vehicle Name: MH P5 2312 delete vehicle name.
Advance Thanks.
This is because you need to update the alert message on ajax request success
in your destroy.js.erb
you should ditch the .erb and fetch the car name from an html element instead
your js code should be as the following:
$('.delete_vehicle').bind('ajax:success', function() {
var vehicleName = $('vehicle_link').data('vehicle_name');
// here I assume that you have a link for initiating the delete process and you can add an attribute to this anchor tag element called data-vehicle_name and set its value to the car name.
$(this).closest('tr').fadeOut();
$(".alert").show();
$(".alert").html("Vehicle \"<b>" + vehicleName + "</b>\" deleted successfully.");
$(".alert").fadeOut(5000);
});
Firstly, if you're going to use the ajax:success callback (which you don't in this instance), you'll need to bind the alert functions to it:
$('.delete_vehicle').on('ajax:success', function() {
$(this).closest('tr').fadeOut();
$(".alert").show().html("Vehicle \"<b><%=#vehicle.name %></b>\" deleted successfully.").fadeOut(5000);
});
Secondly, if you're not getting an updated #vehicle.name because - I think - your Rails instance will have already rendered it. I'm not exactly sure why, but the key will be to pass your data through the ajax request if you were still going to use it:
#app/controllers/vehicles_controller.rb
class VehiclesController < ApplicationController
def destroy
...
format.js { render :layout => false, json: #vehicle }
end
end
#app/assets/javascripts/application.js
$('.delete_vehicle').on('ajax:success', function(event, data, status, xhr) {
vehicle = JSON.parse(data);
$(this).closest('tr').fadeOut();
$(".alert").show().html("Vehicle \"<b>" + + "</b>" deleted successfully.").fadeOut(5000);
});
--
Bottom line is that js.erb is meant to give you actionable Javascript functionality from the controller action. I think your main problem is you're using ajax within this, likely causing a conflict.
Above is how I'd use Ajax (IE put the code in the javascript assets folder); if you wanted to use destroy.js.erb still, you could use the following:
#app/views/vehicles/destroy.js.erb
$("<%= #vehicle.id %>").closest('tr').fadeOut();
$(".alert").show();
$(".alert").html("Vehicle \"<b><%=#vehicle.name %></b>\" deleted successfully.")
$(".alert").fadeOut(5000);
I'm trying to implement Dropzone.js to my Rails 4 app. I have the little box going but nothing else seems to be working. While I know it may be a piece of cake for someone on here, I've been spending about 2 days trying to figure this out.
Here's what I have and done so far:
Added:
gem 'dropzonejs-rails'
Added to application.js:
//= require dropzone
Application.scss
*= require dropzone/dropzone
Here is the form that I want Dropzone.JS on:
What I currently have so far on my form page:
The box appears, but neither drag and drop nor any other function works...
Additional information: I'm using Paperclip and I want to be able to upload and save multiple images to each post I'm having.
I'm not sure if this is necessary but
Post.rb:
post_controller.js
class PostsController < ApplicationController
before_action :find_posts, only: [:show, :edit, :update, :destroy, :upvote, :downvote]
before_action :authenticate_user!, except: [:index, :show, :home]
def home
end
def index
if params[:category].blank?
#posts = Post.all.order("created_at DESC")
else
#category_id = Category.find_by(name: params[:category]).id
#posts = Post.where(category_id: #category_id).order("created_at DESC")
end
end
def show
#inquiries = Inquiry.where(post_id: #post).order("created_at DESC")
#random_post = Post.where.not(id: #post).order("RANDOM()").first
end
def new
#post = current_user.posts.build
end
def create
#post = current_user.posts.build(post_params)
if #post.save
redirect_to #post
else
render 'new'
end
end
def edit
end
def update
if #post.update(post_params)
redirect_to #post
else
render 'edit'
end
end
def destroy
#post.destroy
redirect_to root_path
end
def upvote
#post.upvote_by current_user
redirect_to #post
end
def downvote
#post.downvote_by current_user
redirect_to #post
end
private
def find_posts
#post = Post.find(params[:id])
end
def post_params
params.require(:post).permit(:title, :price, :description, :location, :category_name, :contact_number, :image)
end
end
What I need help with:
Implementing Dropzone.JS so I can upload multiple images to my post _form and have it appear on my post show page.
Thank you in advance!
UPDATE:
This is what appears:
Based on the DropzoneJS documentation you should put the class dropzone on form element instead of the file upload element. After this the DropzoneJS finds all the file inputs and changes them inside the form.
So change the form generation line to
= simple_form_for #Post, html: { multipart: true } do |f|
to
= simple_form_for #Post, html: { multipart: true, class: 'dropzone' } do |f|
#Loi Huynh : hi Loi! You can see examples at this site : http://sudharti.github.io/articles/dropzone-rails/
I having problems updating nested form ... if you want us to discuss this issue.
this my code :
<%= form_for(#product, html: {class: "dropzone", multipart: true}) do |f| %>
<div id="previews" class="dropzone-previews">
<div class="dz-default dz-message" align="center">
<span class="btn btn-primary">Click vào đây để thêm Ảnh</span>
</div>
</div>
<div class="col-md-4 col-md-offset-4" align="center">
<br/>
<div class="actions">
<%= f.submit "Hoàn Tất", id: "submit-all", class: "btn btn-primary btn-sm" %>
<%= link_to 'Quay về', products_path, class: "btn btn-primary btn-sm" %>
</div>
</div>
and my products_controllers :
def create
#product = Product.new(product_params)
respond_to do |format|
if #product.save
format.html { redirect_to products_url, notice: 'Tạo mới thành công.!.' }
format.json { render :show, status: :created, location: #product }
else
render json: { error: #post.errors.full_messages.join(',')}, :status => 400
end
end
end
and my upload.js
$(".dropzone").dropzone({
autoProcessQueue : false,
paramName: "product[photos_attributes][][image]",
previewsContainer: "#previews",
init: function() {
var myDropzone = this;
this.element.querySelector("#submit-all").addEventListener("click", function(e) {
e.preventDefault();
e.stopPropagation();
myDropzone.processQueue();
});
},
});
Update: My Github Repository https://github.com/Marc585/smartforce2
i just finished the onemonthrails tutorial. at the last chapter its about endless scrolling. i tripple checked my code but it just doesn't work. I don't get an error or anything. It just doesn't do anything. I'm building a pinterest clone and after i scroll to the bottom it should load the next page of pins.
This is my pins.js.coffee
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
jQuery ->
$('#pins').imagesLoaded ->
$('#pins').masonry itemSelector: ".box"
if $('.pagination').length
$(window).scroll ->
url = $('.pagination .next_page a').attr('href')
if url && $(window).scrollTop() > $(document).height() - $(window).height() - 50
# What to do at the bottom of the page
$('.pagination').text("Fetching more pins...")
$.getScript(url)
$(window).scroll()
This is my index.js.erb
$boxes = $('<%= j render(#pins) %>')
$('#pins').append( $boxes ).imagesLoaded( function(){
$('#pins').masonry( 'reload');
});
<% if #pins.next_page %>
$('.pagination').replaceWith('<%= j will_paginate(#pins) %>');
<% else %>
$('.pagination').remove();
<% end %>
This is my pins controller:
class PinsController < ApplicationController
before_filter :authenticate_user!, except: [:index]
# GET /pins
# GET /pins.json
def index
#pins = Pin.order("created_at desc").page(params[:page]).per_page(20)
respond_to do |format|
format.html # index.html.erb
format.json { render json: #pins }
format.js
end
end
# GET /pins/1
# GET /pins/1.json
def show
#pin = Pin.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #pin }
end
end
# GET /pins/new
# GET /pins/new.json
def new
#pin = current_user.pins.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #pin }
end
end
# GET /pins/1/edit
def edit
#pin = current_user.pins.find(params[:id])
end
# POST /pins
# POST /pins.json
def create
#pin = current_user.pins.new(params[:pin])
respond_to do |format|
if #pin.save
format.html { redirect_to #pin, notice: 'Pin was successfully created.' }
format.json { render json: #pin, status: :created, location: #pin }
else
format.html { render action: "new" }
format.json { render json: #pin.errors, status: :unprocessable_entity }
end
end
end
# PUT /pins/1
# PUT /pins/1.json
def update
#pin = current_user.pins.find(params[:id])
respond_to do |format|
if #pin.update_attributes(params[:pin])
format.html { redirect_to #pin, notice: 'Pin was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #pin.errors, status: :unprocessable_entity }
end
end
end
# DELETE /pins/1
# DELETE /pins/1.json
def destroy
#pin = current_user.pins.find(params[:id])
#pin.destroy
respond_to do |format|
format.html { redirect_to pins_url }
format.json { head :no_content }
end
end
end
Error Message from my developer tools
https://www.dropbox.com/s/odqknmw7np1f4cv/Bildschirmfoto%202013-08-31%20um%2014.34.40.png
You are missing
format.js in your show action in the pins_controller.rb. This is from the onemonth rails notes
Note: You'll have to create an analogous show.js.erb and add the format.js option to your users#show action to get endless scroll to work on users' profile pages as well
def show
#pin = Pin.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #pin }
format.js
end
end
Your show.js.erb should look like this
boxes = $('<%= j render(#pins) %>')
$('#pins').append( $boxes ).imagesLoaded( function(){
$('#pins').masonry( 'reload');
});
<% if #pins.next_page %>
$('.pagination').replaceWith('<%= j will_paginate(#pins) %>');
<% else %>
$('.pagination').remove();
<% end %>