Rails AJAX a Destroy Action - ajax

I'm trying to use AJAX on a friendships list where the "Request Friendship" button turns into a "Cancel Request" button and then back again if clicked a second time. The first click works, the ajax on the "Cancel Request" button does not.
On line 2 of the destroy.js.erb I get a:
undefined method `id' for nil:NilClass
I know it's not working because the user_id is not passed in the URL for the destroy as it is for the create, but I don't know how to fix it.
Friendships Controller
before_action :authenticate_user!
before_action :set_user, only: [:create]
before_action :set_friendship, only: [:destroy, :accept]
def create
#friendship = current_user.request_friendship(#user)
respond_to do |format|
format.html {redirect_to users_path, notice: "Friendship Requested"}
format.js
end
end
def destroy
#friendship.destroy
respond_to do |format|
format.html {redirect_to users_path, notice: "Friendship Removed"}
format.js
end
end
def accept
#friendship.accept_friendship
#friendship.create_activity key: 'friendship.accepted', owner: #friendship.user, recipient: #friendship.friend
#friendship.create_activity key: 'friendship.accepted', owner: #friendship.friend, recipient: #friendship.user
respond_to do |format|
format.html {redirect_to users_path, notice: "Friendship Accepted"}
format.js
end
end
private
def set_user
#user = User.find(params[:user_id])
end
def set_friendship
#friendship = Friendship.find(params[:id])
end
Create JS
$('.js-not-friend-btn').bind('ajax:success', function() {
$('.action-button-for-<%= #user.id %>').html('<%= j action_buttons(#user) %>');
});
Destroy JS
$('.js-cancel-request-btn').bind('ajax:success', function() {
$('.action-button-for-<%= #user.id %>').html('<%= j action_buttons(#user) %>');
});
Users Helper
def action_buttons(user)
case current_user.friendship_status(user) when "friends"
link_to "Cancel Friendship", friendship_path(current_user.friendship_relation(user)), method: :delete, :remote => true, class: "js-cancel-friendship-btn btn btn-danger btn-xs mr10"
when "pending"
link_to "Cancel Request", friendship_path(current_user.friendship_relation(user)), method: :delete, :remote => true, class: "js-cancel-request-btn btn btn-danger btn-xs mr10"
when "requested"
link_to("Accept Friendship", accept_friendship_path(current_user.friendship_relation(user)), method: :put, :remote => true, class: "js-accept-friendship-btn btn btn-success btn-xs mr10") +
link_to("Decline", friendship_path(current_user.friendship_relation(user)), method: :delete, :remote => true, class: "js-decline-friendship-btn btn btn-default btn-xs mr10")
when "not_friends"
link_to "Add as Friend", friendships_path(user_id: user.id), method: :post, :remote => true, class: "js-not-friend-btn btn btn-success btn-xs mr10"
end
end
Thanks for any help you can offer.

I had to add the following line to my destroy action in order to use #user in the destroy.js.erb:
#user = #friendship.user == current_user ? #friendship.friend : #friendship.user

Related

Stimulus ajax:response not returning the server response data

I am starting a new Rails 7 app and recently found out that we can't use ujs anymore :( I found a plugin called mrujs which is working correctly to send my forms remotely to the server. I am also using stimulus to handle various javascript functions on the page.
The issue that I'm having is my response back after ajax:success processes is not iterable:
TypeError: object is not iterable (cannot read property Symbol(Symbol.iterator))
Below is my HTML, Rails, and Stimulus Code:
HTML
<%= form_with model: Article.new, html: { data: { remote: true, type: "html", "action": "ajax:success->modal-forms#onPostSuccess ajax:error->modal-forms#onPostError" } } do |f| %>
<%= f.label :title, "Article Title" %>
<%= f.text_field :title, class: "form-control" %>
<%= f.submit "Save Article", class: "btn btn-primary" %>
<% end %>
Ruby / Rails
def create
#article = Article.create(article_params)
if #article.errors.any?
render partial: 'error', article: #article, status: :bad_request
else
render #article
end
end
This returns a basic html page that would be inserted into another location within the page.
<li><%= #article.title %></li>
Stimulus Action
onPostSuccess(event) {
event.preventDefault()
const [data, status, xhr] = event.detail
// This is where I get the issue of 'Not Iterable'
}
event.detail gives me the not iterable error. Does anyone know how I can get my response from rails to be formatted so the [data, status, xhr] section will actually work?
If hotwire or turbo is needed for this an example would be extremely helpful :)
Thanks
Eric
This may not be the correct way but it does work:
html
<div data-container="remote">
<%= form_with model: Person.new, html: { data: { "action": "submit->remote#submit" } } do |f| %>
<%= f.text_field :first_name %>
<%= f.submit :submit %>
<% end %>
</div>
peole rails controller
def create
#person = Person.new(person_params)
if #person.save
render json: #person
else
render partial: 'people/person', locals: { person: #person }, status: :unprocessable_entity
end
end
remote stimulus controller
submit(event) {
event.preventDefault()
fetch(event.target.action, {
method: 'POST',
body: new FormData(event.target),
}).then(response => {
console.log(response);
if(response.ok) {
return response.json()
}
return Promise.reject(response)
}).then(data => {
console.log(data)
}).catch( error => {
console.warn(error)
});
}

Rails Scaffold Index Filtering

I am working on a CAD app (Rails 4, Ruby 2.2). On the Calls index Page I have 2 panels side by side.
My goal is to sort the panels into Active and Unassigned based on a DB attribute, which is currently names Status and is a "string"
I'm just not sure how to do this at all I've tried to search it out with not much luck. I am using Postgresql as my DB and below is the code for the index page I have not modified my controller but i will add the code for reference.
Index.html.erb code:
<div class="panel panel-success" id="active-pnl">
<div class="panel-heading"><center><h4>Assigned Calls: </h4></center></div>
<table class="table" id="assign-table">
<thead>
<tr>
<th><center>Call Number</center></th>
<th><center>Address</center></th>
<th><center>Responding</center></th>
<th><center>Call Type</center></th>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<% #calls.each do |call| %>
<tr>
<td><center><%= call.call_num %></center></td>
<td><center><%= call.address %></center></td>
<td><center><strong><%= call.unit_1 %></strong> | <%= call.unit_2 %> | <%= call.unit_3 %> | <%= call.unit_4 %></center></td>
<td><center><%= call.primary_type %> | <%= call.secondary_type %></center></td>
<td><center><%= link_to 'View', call, class: 'btn btn-success btn-sm', id: 'view-btn' %></center></td>
<td><center><%= link_to 'Update', edit_call_path(call), class: 'btn btn-primary btn-sm', id: 'update-btn' %></center></td>
<td><center><%= link_to 'Cancel', call, class: 'btn btn-danger btn-sm', id: 'cancel-btn', method: :delete, data: { confirm: 'Are you sure?' } %></center></td>
</tr>
<tr>
</tr>
<% end %>
</tbody>
</table>
</div>
<div class="panel panel-danger" id="unactive-pnl">
<div class="panel-heading"><center><h4>Unassigned Calls:</h4></center></div>
<table class="table" id="nonassign-table">
<thead>
<tr>
<th><center>Call Number</center></th>
<th><center>Address</center></th>
<th><center>Status</center></th>
<th><center>Call Type</center></th>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<% #calls.each do |call| %>
<tr>
<td><center><%= call.call_num %></center></td>
<td><center><%= call.address %></center></td>
<td><center><%= call.status %></center></td>
<td><center><%= call.primary_type %> | <%= call.secondary_type %></center></td>
<td><center><%= link_to 'View', call, class: 'btn btn-success btn-sm', id: 'view-btn' %></center></td>
<td><center><%= link_to 'Update', edit_call_path(call), class: 'btn btn-primary btn-sm', id: 'update-btn' %></center></td>
<td><center><%= link_to 'Cancel', call, class: 'btn btn-danger btn-sm', id: 'cancel-btn', method: :delete, data: { confirm: 'Are you sure?' } %></center></td>
</tr>
<% end %>
</tbody>
</table>
</div>
<br>
<center>
<%= link_to new_call_path, class: "btn btn-success btn-lg", id: 'newcall-btn' do %>
<i class="glyphicon glyphicon-plus"> NewCall</i>
<% end %>
</center>
Calls Controller:
class CallsController < ApplicationController
before_action :set_call, only: [:show, :edit, :update, :destroy]
# GET /calls
# GET /calls.json
def index
#calls = Call.all
end
# GET /calls/1
# GET /calls/1.json
def show
end
# GET /calls/new
def new
#call = Call.new
end
# GET /calls/1/edit
def edit
end
# POST /calls
# POST /calls.json
def create
#call = Call.new(call_params)
respond_to do |format|
if #call.save
format.html { redirect_to #call, notice: 'Call was successfully created.' }
format.json { render :show, status: :created, location: #call }
else
format.html { render :new }
format.json { render json: #call.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /calls/1
# PATCH/PUT /calls/1.json
def update
respond_to do |format|
if #call.update(call_params)
format.html { redirect_to #call, notice: 'Call was successfully updated.' }
format.json { render :show, status: :ok, location: #call }
else
format.html { render :edit }
format.json { render json: #call.errors, status: :unprocessable_entity }
end
end
end
# DELETE /calls/1
# DELETE /calls/1.json
def destroy
#call.destroy
respond_to do |format|
format.html { redirect_to calls_url, notice: 'Call was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_call
#call = Call.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def call_params
params.require(:call).permit(:call_time, :status, :primary_type, :secondary_type, :site, :address, :unit_1, :unit_2, :unit_3, :unit_4, :call_details, :unit_on_scene, :unit_clear, :call_num, :site_id)
end
end
So to recap: I am looking for something that will sort the calls into either of the tables based on its current status. If ACTIVE i would like it in the Active calls Table if Unassigned i would like it in the unassigned table. Any help here would be greatly appreciated.
Assume you have two values 1 and 0 in the field status of model Call. In your controller action:
def index
#active_calls = Call.where(status: 1)
#inactive_calls = Call.where(status: 0)
end
Then you can access two arrays #active_calls and #inactive_calls in your view, just replaces
<% #calls.each do |call| %>
to
<% #active_calls.each do |call| %>
or
<% #inactive_calls.each do |call| %>
based on where you would like to display them.

Ruby on Rails, Redirect After Creating New Post in Note Taking App

In my RoR note taking application notes are called Statuses.
When a user creates and posts a new Status, I would like them to be redirected to the Satuses page, an index of all of their Statuses/Notes.
Right now they are redirected to a page that shows just that new Status/Note.
I thought this would be a simple rediect in the Statuses controller, the Create action, but it isn't. A friend coded this part of the app and the JSON and render commands there have thrown me off a bit.
Here is the code from the controller:
# POST /statuses
# POST /statuses.json
def create
#status = current_user.statuses.new(params[:status])
respond_to do |format|
if #status.save
format.html { redirect_to #status, notice: 'Status was successfully created.' }
format.json { render json: #status, status: :created, location: #status }
else
format.html { render action: "new" }
format.json { render json: #status.errors, status: :unprocessable_entity }
end
end
end
And from the entry form partial:
<%= f.text_area :content %>
<div class="row">
<div class = "small-3 small-centered columns text-center">
<%= f.submit "Post", class: "radius button text-center" %>
</div>
</div>
<% end %>
Here you are using redirect_to #status which redirects to show page of status.
To redirect to index page use following code
respond_to do |format|
if #status.save
format.html { redirect_to statuses_path, notice: 'Status was successfully created.' }
format.json { render json: #status, status: :created, location: #status }
else
format.html { render action: "new" }
format.json { render json: #status.errors, status: :unprocessable_entity }
end

Hartl's Rails Tutorial (rails-4-0) section 10.3.2 param not found

I've been struggling with this for a while now and can't figure out what is happening. I'm working my way through Michael Hartl's Rails tutorial and have come to have an issue with some of my tests. These are the failing test results:
Failures:
1) User pages signup with invalid information should not create a user
Failure/Error: expect { click_button submit }.not_to change(User, :count)
ActionController::ParameterMissing:
param not found: user
# ./app/controllers/users_controller.rb:61:in `user_params'
# ./app/controllers/users_controller.rb:21:in `create'
# ./spec/requests/user_pages_spec.rb:89:in `block (5 levels) in <top (required)>'
# ./spec/requests/user_pages_spec.rb:89:in `block (4 levels) in <top (required)>'
2) User pages signup with invalid information after submission
Failure/Error: before { click_button submit }
ActionController::ParameterMissing:
param not found: user
# ./app/controllers/users_controller.rb:61:in `user_params'
# ./app/controllers/users_controller.rb:21:in `create'
# ./spec/requests/user_pages_spec.rb:93:in `block (5 levels) in <top (required)>'
3) User pages signup with invalid information after submission
Failure/Error: before { click_button submit }
ActionController::ParameterMissing:
param not found: user
# ./app/controllers/users_controller.rb:61:in `user_params'
# ./app/controllers/users_controller.rb:21:in `create'
# ./spec/requests/user_pages_spec.rb:93:in `block (5 levels) in <top (required)>'
4) User pages signup with valid information should create a user
Failure/Error: before { valid_signup(user) }
Capybara::ElementNotFound:
Unable to find field "Name"
# ./spec/support/utilities.rb:10:in `valid_signup'
# ./spec/requests/user_pages_spec.rb:102:in `block (4 levels) in <top (required)>'
5) User pages signup with valid information after saving the user
Failure/Error: before { valid_signup(user) }
Capybara::ElementNotFound:
Unable to find field "Name"
# ./spec/support/utilities.rb:10:in `valid_signup'
# ./spec/requests/user_pages_spec.rb:102:in `block (4 levels) in <top (required)>'
6) User pages signup with valid information after saving the user
Failure/Error: before { valid_signup(user) }
Capybara::ElementNotFound:
Unable to find field "Name"
# ./spec/support/utilities.rb:10:in `valid_signup'
# ./spec/requests/user_pages_spec.rb:102:in `block (4 levels) in <top (required)>'
7) User pages signup with valid information after saving the user
Failure/Error: before { valid_signup(user) }
Capybara::ElementNotFound:
Unable to find field "Name"
# ./spec/support/utilities.rb:10:in `valid_signup'
# ./spec/requests/user_pages_spec.rb:102:in `block (4 levels) in <top (required)>'
Finished in 9.1 seconds
119 examples, 7 failures
Failed examples:
rspec ./spec/requests/user_pages_spec.rb:88 # User pages signup with invalid information should not create a user
rspec ./spec/requests/user_pages_spec.rb:95 # User pages signup with invalid information after submission
rspec ./spec/requests/user_pages_spec.rb:96 # User pages signup with invalid information after submission
rspec ./spec/requests/user_pages_spec.rb:104 # User pages signup with valid information should create a user
rspec ./spec/requests/user_pages_spec.rb:113 # User pages signup with valid information after saving the user
rspec ./spec/requests/user_pages_spec.rb:115 # User pages signup with valid information after saving the user
rspec ./spec/requests/user_pages_spec.rb:116 # User pages signup with valid information after saving the user
So Here's my user_pages_spec.rb:
require 'spec_helper'
describe "User pages" do
subject { page }
describe "index" do
let(:user) { FactoryGirl.create(:user) }
before do
sign_in user
visit users_path
end
it { should have_title('All users') }
it { should have_content('All users') }
describe "pagination" do
before(:all) { 30.times { FactoryGirl.create(:user) } }
after(:all) { User.delete_all }
it { should have_selector('div.pagination') }
it "should list each user" do
#User.all.each do |user|
User.paginate(page: 1).each do |user|
expect(page).to have_selector('li', text: user.name)
end
end
end
describe "delete links" do
it { should_not have_link('delete') }
describe "as an admin user" do
let(:admin) { FactoryGirl.create(:admin) }
before do
sign_in admin
visit users_path
end
it { should have_link('delete', href: user_path(User.first)) }
it "should be able to delete another user" do
expect do
click_link('delete', match: :first)
end.to change(User, :count).by(-1)
end
it { should_not have_link('delete', href: user_path(admin)) }
end
end
end
describe "profile page" do
let(:user) { FactoryGirl.create(:user) }
let!(:m1) { FactoryGirl.create(:micropost, user: user, content: "Foo") }
let!(:m2) { FactoryGirl.create(:micropost, user: user, content: "Bar") }
before { visit user_path(user) }
it { should have_content(user.name) }
it { should have_title(user.name) }
describe "microposts" do
it { should have_content(m1.content) }
it { should have_content(m2.content) }
it { should have_content(user.microposts.count) }
end
end
describe "signup page" do
before { visit signup_path }
it { should have_content('Sign up') }
it { should have_title(full_title('Sign up')) }
end
describe "signup" do
before { visit signup_path }
let(:submit) { "Create my account" }
describe "with invalid information" do
it "should not create a user" do
expect { click_button submit }.not_to change(User, :count)
end
describe "after submission" do
before { click_button submit }
it { should have_title('Sign up') }
it { should have_content('error') }
end
end
describe "with valid information" do
let(:user) {FactoryGirl.build(:user)}
before { valid_signup(user) }
it "should create a user" do
expect { click_button submit }.to change(User, :count).by(1)
end
describe "after saving the user" do
before { click_button submit }
#let(:user) { User.find_by(email: 'user#example.com') }
it { should have_title(user.name) }
#it { should have_selector('title', text: user.name) }
it { should have_selector('div.alert.alert-success', text: 'Welcome') }
it { should have_link('Sign out') }
end
end
end
describe "edit" do
let(:user) { FactoryGirl.create(:user) }
before do
sign_in user
visit edit_user_path(user)
end
describe "page" do
it { should have_content("Update your profile") }
it { should have_title("Edit user") }
it { should have_link('Change', href: 'http://gravatar.com/emails') }
end
describe "with invalid information" do
before { click_button "Save changes" }
it { should have_content('error') }
end
describe "with valid information" do
let(:new_name) { "New Name" }
let(:new_email) { "new#example.com" }
before do
fill_in "Name", with: new_name
fill_in "Email", with: new_email
fill_in "Password", with: user.password
fill_in "Confirm Password", with: user.password
click_button "Save changes"
end
it { should have_title(new_name) }
it { should have_selector('div.alert.alert-success') }
it { should have_link('Sign out', href: signout_path) }
specify { expect(user.reload.name).to eq new_name }
specify { expect(user.reload.email).to eq new_email }
end
describe "forbidden attributes" do
let(:params) do
{ user: {admin: true, password: user.password, password_confirmation: user.password } }
end
before do
sign_in user, no_capybara: true
patch user_path(user), params
end
specify { expect(user.reload).not_to be_admin }
end
end
end
users_controller.rb:
class UsersController < ApplicationController
before_action :signed_in_user, only: [:index, :edit, :update, :destroy]
before_action :correct_user, only: [:edit, :update]
before_action :admin_user, only: :destroy
before_filter :signed_in_user_filter, only: [:new, :create]
def index
#users = User.paginate(page: params[:page])
end
def show
#user = User.find(params[:id])
#microposts = #user.microposts.paginate(page: params[:page])
end
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
sign_in #user
flash[:success] = "Welcome to the Sample App!"
redirect_to #user
else
render 'new'
end
end
def destroy
#user = User.find(params[:id])
if current_user == #user
flash[:error] = "You must not delete yourself."
#redirect_to users_path
else
#user.destroy
flash[:success] = "User deleted."
#redirect_to users_url
end
redirect_to users_path
end
def edit
##user = User.find(params[:id])
end
def update
##user = User.find(params[:id])
if #user.update_attributes(user_params)
flash[:success] = "Profile updated"
redirect_to #user
else
render 'edit'
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation)
end
#Before filters
def signed_in_user
unless signed_in?
store_location
redirect_to signin_url, notice: "Please sign in."
end
end
def signed_in_user_filter
redirect_to root_path, notice: "Already logged in" if signed_in?
end
def correct_user
#user = User.find(params[:id])
redirect_to(root_url) unless current_user?(#user)
end
def admin_user
redirect_to(root_url) unless current_user.admin?
end
end
I'm not sure what else would be releveant but here's app/views/users/new.html.erb:
<% provide(:title, 'Sign up') %>
<h1>Sign up</h1>
<div class="row">
<div class="span6 offset3">
<%= form_for(#user) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.submit "Create my account", class: "btn btn-large btn-primary" %>
<% end %>
</div>
</div>
To be honest I'm not 100% sure where to look for this problem. It just happened with these latest changes I've made in this chapter. I try to do all the exercises so sometimes there's variation in what I'm supposed to edit so I don't know if I've messed something up or perhaps just done something dumb. When I visit the app from a browser, and click the "Sign me up" link to register a new user I get the red rails error page that says ActionController::ParameterMissing in UsersController#create but I don't know exactly what that means. I didn't really think I changed any code that would affect that, but I've tinkered around with some stuff to try to complete previous exercises so perhaps that's whats happening.
Thanks for any help! This is my first SO post so hopefully I didn't make a huge mistake in etiquette/formatting. I searched for someone with similar problems, as usual, but couldn't find anything.
Edit: I wanted to add that I've been working off the previous version of this tutorial as I wasn't done when the new edition came out. You can find it here: http://rails-4-0.railstutorial.org/book/user_microposts
I think the answer is pretty simple. It looks like you deleted all of the fields out of your form when he only wanted you to add in the error rendering component. Your sign-up form should be something like:
<% provide(:title, 'Sign up') %>
<h1>Sign up</h1>
<div class="row">
<div class="span6 offset3">
<%= form_for(#user) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :email %>
<%= f.text_field :email %>
<%= f.label :password %>
<%= f.password_field :password %>
<%= f.label :password_confirmation, "Confirmation" %>
<%= f.password_field :password_confirmation %>
<%= f.submit "Create my account", class: "btn btn-large btn-primary" %>
<% end %>
</div>
</div>

param is missing or the value is empty: new_vid

As a noob, I have done this a few times, but I always struggle with the strong params. I rolled a new model and controller for uploading a link to a video.
Here is my code:
new_vids_controller.rb
class NewVidsController < ApplicationController
def index
#vids = NewVid.all
end
def new
#vid = NewVid.new(vid_params)
end
def create
if current_user
#vid=NewVid.new(vid_params)
#vid.user_id = current_user.id
if #vid.save
flash[:notice] = "Video Submitted"
redirect_to videos_path
else
flash[:error] = "Error: Vid not Submitted"
redirect_to videos_path
end
else
flash[:error] = "You Must Be Signed In To Submit Videos!"
store_location
redirect_to signin_path
end
end
end
private
def vid_params
params.require(:new_vid).permit(:vid_link, :vid_body, :user_id)
end
new.html.erb
<%= form_for #vid do |f| %>
<%= f.text_field :vid_link, title: "Video Link", placeholder: "Video Link" %>
<%= f.text_field :vid_body, title: "Description", placeholder: "Video Description" %>
<%= f.submit "Submit Link", class: "button tiny" %>
Thanks for helping a noob!
Your new action should be:
#vid = NewVid.new
There are no params to validate there.

Resources