Rails 6 Drag and Drop ( Sortable ) - javascript

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

Related

How do I increment elapsed time without refreshing the page

I need the elapsed_time to be updated every second, but it only updates when refreshing the page. I am using setTimeout in the script. I need the counter to be updated every second in the script below the following code:
shifts_controller.rb
class User::ShiftsController < ApplicationController
before_action :authenticate_user!
before_action :set_shift, only: %i[show edit update destroy]
# GET /shifts or /shifts.json
def index
#shifts = current_user.shifts.order(id: :desc)
#shift = current_user.shifts.where(clock_out: nil).where.not(clock_in: nil).first
end
# GET /shifts/1 or /shifts/1.json
def show; end
# GET /shifts/new
def new
#shift = Shift.new
end
# POST /shifts
def create
#shift = Shift.new(user: current_user) # quando criar ja liga ao id
respond_to do |format|
if #shift.save
format.html do
redirect_to action: 'index', user_id: current_user,
notice: 'Shift was successfully created.'
end
else
format.html { render :new, status: :unprocessable_entity }
end
end
end
# GET /shifts/1/edit
def edit; end
# PATCH/PUT /shifts/1
def update
#shift.set_clock_out
respond_to do |format|
if #shift.save
format.html do
redirect_to action: 'index', user_id: current_user,
notice: 'Shift was successfully updated.'
end
else
format.html { render :edit, status: :unprocessable_entity }
end
end
end
# DELETE /shifts/1 or /shifts/1.json
def destroy
#shift.destroy
respond_to do |format|
format.html do
redirect_to action: 'index', user_id: current_user,
notice: 'Shift was successfully destroyed.'
end
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_shift
#shift = Shift.find(params[:id])
end
# Only allow a list of trusted parameters through.
def shift_params
params.require(:shift).permit(:clock_in, :clock_out, :user_id, :user_name)
end
end
index.html.erb
<div class='container'>
<% if #shift&.clock_in %>
<div class='card text-center'>
<div id='time'>
<%time_diff = #shift.clock_in - 0.1.second.ago%>
<%elapsed_time = Time.at(time_diff.to_i.abs).strftime("%H:%M:%S")%>
</div>
</div>
<div class='card'>
<br /><%= link_to 'Clock Out', user_shift_path(#shift.id), method: :put, class: "btn btn-success mb-4"%>
</div>
<% else %>
<div class='card'>
<br /><%= link_to 'Clock In', user_user_shifts_path(current_user.id), method: :post, class: "btn btn-success mb-4"%>
</div>
<% end %>
<%= render 'shifts', shifts: #shifts %>
</div>
<script>
(function timer () {
document.getElementById("time").innerHTML = "<%=elapsed_time%>";
setTimeout(timer, 1000); // recalls the function after 1000 ms
})();
</script>

No template found for SomeController#create rendering head :no_content

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?

Full Calendar: ActiveRecord::RecordNotFound (Couldn't find Event with 'id'=update)

I am trying to include FullCalendar into my rails app. But I am having difficulty with saving after drag-and-drop.
I have 2 models - Event and Workout. Under event, there is workout.
I am able to get the full calendar up and running and save events, but when I drag and drop, then refresh, the events would revert to their original time.
In my console, it says:
Couldn't find Event with 'id'=update.
When the events are created and saved through a form, they are properly displayed and saved on calendar. It's just when I try to drag-and-drop.
Here is my Event controller.
class EventsController < ApplicationController
before_action :set_event, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!
def index
#events = current_user.events
end
def show
#event = Event.find(params[:id])
end
def new
#event = current_user.events.new
end
def edit
#event = current_user.events.find(params[:id])
end
def create
#event = current_user.events.new(event_params)
respond_to do |format|
if #event.save
format.html { redirect_to #event, notice: 'Event was successfully created.' }
format.json { render :show, status: :created, location: #event }
else
format.html { render :new }
format.json { render json: #event.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #event.update(event_params)
format.html { redirect_to #event, notice: 'Event was successfully updated.' }
format.json { render :show, status: :ok, location: #event }
else
format.html { render :edit }
format.json { render json: #event.errors, status: :unprocessable_entity }
end
end
end
def destroy
#event.destroy
respond_to do |format|
format.html { redirect_to events_url, notice: 'Event was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_event
#event = Event.find(params[:id])
end
def event_params
params.require(:event).permit(:title, :description, :start_time, :end_time, :workouts_attributes => [:id, :name, :category, :_destroy])
end
end
Below is application.js file that controls the calendar.
$(document).on('turbolinks:load', function() {
$('#calendar').fullCalendar({
editable: true,
events: '/events.json',
eventDrop: function(event, delta) {
$.ajax({
type: "PUT",
url: '/events/update',
dataType: 'json',
data: {id:event.id, start:event.start_time, end: event.end_time}
});
},
});
});
When I try to drag and drop, I get a message below:
ActiveRecord::RecordNotFound (Couldn't find Event with 'id'=update):
app/controllers/events_controller.rb:74:in `set_event'
When I check my console, all the events have IDs. But I'm guessing maybe when the event is being drag-and-dropped, the new event doesn't have an ID?
Rails app is assuming update as :id when you are hitting this URL /events/update
$ rake routes
event PUT /events/:id(.:format) events#update
change the url in ajax request to
$.ajax({
type: 'PUT',
url: '/events/' + event.id,
dataType: 'json',
data: { event: { start_time:event.start_time, end_time: event.end_time } }
});

How update record for Ajax deletion in rails?

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);

endless scrolling does not work

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 %>

Categories