Rails Stimulus delete resource from DB - ruby-on-rails

I am trying to implement a "delete" action via Stimulus in order to replace the boring JS validation windows with sweetalert2. I have the below code, which results in two errors:
While the resource (a book) indeed IS deleted, there is no redirect happening, i. e. I don't see any change (nor the notification that the book has been deleted etc)
I see an error in the JS console saying "DELETE http://127.0.0.1:3000/books 404 (Not Found)"; in the rails console, I see that after the book deletion request, there is indeed another deletion request fired to /books (which of course does not work).
I don't understand why there is an error, and I don't quite get what to do to redirect, i. e. to have the "destroy" method running properly?
Controller (excerpt):
def destroy
#book.destroy
flash[:alert] = "Book removed"
redirect_to books_path
end
View (excerpt):
<%= button_to "delete_path", method: :delete, title: "Remove Book",
form: { data: {
turbo_confirm: "Are you sure?",
controller: "sweetalert",
action: 'click->sweetalert#confirm',
"sweetalert-item-value": item_name,
"sweetalert-url-value": url,
} },
class: "delete-icon nobutton" do %>
<svg>......
Stimulus controller code:
import { Controller } from "#hotwired/stimulus";
import Swal from "sweetalert2";
import { getMetaValue } from "../helpers/index";
// Connects to data-controller="sweetalert"
export default class extends Controller {
static values = {
item: String,
url: String,
};
confirm(event) {
event.preventDefault();
const csrfToken = getMetaValue("csrf-token");
Swal.fire({
titleText: "Are you sure?",
text: `You are about to delete "${this.itemValue}"`,
icon: "warning",
showCancelButton: true,
confirmButtonColor: "#991c00",
cancelButtonColor: "#407bbf",
confirmButtonText: "Yes, delete it!",
}).then((result) => {
if (result.isConfirmed) {
fetch(this.urlValue, {
method: "DELETE",
credentials: "same-origin",
headers: {
"X-CSRF-Token": csrfToken,
},
});
}
});
}
}

Related

How use rails wikedpdf gem with ajax

I'm using the wikedpdf gem on a rails project to get my reports.
But to one report I need to use an ajax request
$.ajax({
type: "POST",
url: url,
dataType: 'pdf',
data: { ids: getIdsJavascriptMethod() },
success(data) { $("#load-app").hide(); return false; },
error(data) { $("#load-app").hide(); return false; }
});
I'm calling the ajax reques wth a link_to
<%= link_to "PDF", "#", onclick: "pdf()", target: :_blank %>
But are rendering the same page "#" on a _blank target, how to render a PDF file using wikedpdf gem with an ajax request?
It's opening a new tab because that's the default behavior of clicking a link with target="_blank". Use e.preventDefault() first thing in your pdf() function to prevent this behavior.
function pdf(e) {
e.preventDefault();
// make Ajax request
}
And replace "#" with the js call on link_to:
= link_to "PDF", "javascript:pdf()", target: :_blank
Or, better yet in IMO, add an id to your link event:
<%= link_to "PDF", "#", id: "pdf-link", target: :_blank %>
And listen to the click (code assumes you have jQuery loaded):
$(document).ready(function() {
$('#pdf-link').click(function(e) {
e.preventDefault();
pdf();
});
});

Rails 6 Create new record and print to printer after comfirmation

I have this form_with that creates a new record Post. I am trying to open the print window when the user clicks on the submit button. If rails predicts it should be something like adding onconfirm:printpage() to the Submit button but I cant seem to find how to do it.
= form.submit data: { disable_with: false, confirm: "Are you sure?" onconfirm: printpage() }
and then
function printpage(){
window.print()
}
I think you can do just like this:-
= form.submit, :class => "form_submit" ## add class to submit button
:javascript
$(document).ready(function () {
$("#submit").click(function(event) {
if( !confirm('Are you sure that you want to submit the form') ){
event.preventDefault();
}else{
window.print();
$('#myForm').submit(); ## your form id
}
});
});

Tying in the PayPal smart buttons with a Ruby on Rails form_for and controller/method?

I have the smart buttons "working" in sandbox but I can't think of any way to attach the smart buttons success to the order form which creates the order. With Stripe Elements, it's pretty plug and play because it's on the page and a part of the form itself, but with PayPal with the redirects, I can't seem to think of a way.
Does this require javascript or can I do this without it, aside from what's already there?
Form:
<%= form_for(#order, url: listing_orders_path([#listing, #listing_video]), html: {id: "payment_form-4"} ) do |form| %>
<%= form.label :name, "Your Name", class: "form-label" %>
<%= form.text_field :name, class: "form-control", required: true, placeholder: "John" %>
#stripe code here (not important)
<%= form.submit %>
<div id="paypal-button-container"></div>
<!-- Include the PayPal JavaScript SDK -->
<script src="https://www.paypal.com/sdk/js?client-id=sb&currency=USD"></script>
<script>
// Render the PayPal button into #paypal-button-container
paypal.Buttons({
// Set up the transaction
createOrder: function(data, actions) {
return actions.order.create({
purchase_units: [{
amount: {
value: <%= #listing.listing_video.price %>
}
}]
});
},
// Finalize the transaction
onApprove: function(data, actions) {
return actions.order.capture().then(function(details) {
// Show a success message to the buyer
alert('Transaction completed by ' + details.payer.name.given_name + '!');
});
}
}).render('#paypal-button-container');
</script>
Create Method in Controller:
require 'paypal-checkout-sdk'
client_id = Rails.application.credentials[Rails.env.to_sym].dig(:paypal, :client_id)
client_secret = Rails.application.credentials[Rails.env.to_sym].dig(:paypal, :client_secret)
# Creating an environment
environment = PayPal::SandboxEnvironment.new(client_id, client_secret)
client = PayPal::PayPalHttpClient.new(environment)
#amount_paypal = (#listing.listing_video.price || #listing.listing_tweet.price)
request = PayPalCheckoutSdk::Orders::OrdersCreateRequest::new
request.request_body(
{
intent: 'AUTHORIZE',
purchase_units: [
{
amount: {
currency_code: 'USD',
value: "#{#amount_paypal}"
}
}
]
}
)
begin
# Call API with your client and get a response for your call
response = client.execute(request)
# If call returns body in response, you can get the deserialized version from the result attribute of the response
order = response.result
puts order
#order.paypal_authorization_token = response.id
rescue BraintreeHttp::HttpError => ioe
# Something went wrong server-side
puts ioe.status_code
puts ioe.headers['debug_id']
end
How can I tie in the PayPal smart buttons with the form so once the payment is completed, it creates an order if successful?
UPDATE:::::::
Created a PaypalPayments controller and model:
controller:
def create
#paypal_payment = PaypalPayment.new
#listing = Listing.find_by(params[:listing_id])
require 'paypal-checkout-sdk'
client_id = "#{Rails.application.credentials[Rails.env.to_sym].dig(:paypal, :client_id)}"
client_secret = "#{Rails.application.credentials[Rails.env.to_sym].dig(:paypal, :client_secret)}"
# Creating an environment
environment = PayPal::SandboxEnvironment.new(client_id, client_secret)
client = PayPal::PayPalHttpClient.new(environment)
#amount_paypal = #listing.listing_video.price
request = PayPalCheckoutSdk::Orders::OrdersCreateRequest::new
#paypal_payment = request.request_body({
intent: "AUTHORIZE",
purchase_units: [
{
amount: {
currency_code: "USD",
value: "#{#amount_paypal}"
}
}
]
})
begin
# Call API with your client and get a response for your call
response = client.execute(request)
# If call returns body in response, you can get the deserialized version from the result attribute of the response
order = response.result
puts order
# #order.paypal_authorization_token = response.id
rescue BraintreeHttp::HttpError => ioe
# Something went wrong server-side
puts ioe.status_code
puts ioe.headers["debug_id"]
end
# if #paypal_payment.create
# render json: {success: true}
# else
# render json: {success: false}
# end
end
Javascript in view:
paypal.Buttons({
createOrder: function() {
return fetch('/paypal_payments', {
method: 'post',
headers: {
'content-type': 'application/json'
}
}).then(function(res) {
return res.json();
}).then(function(data) {
return data.orderID;
});
},
onApprove: function(data) {
return fetch('/orders', {
method: 'post',
headers: {
'content-type': 'application/json'
},
body: JSON.stringify({
orderID: data.orderID
})
}).then(function(res) {
return res.json();
}).then(function(details) {
alert('Authorization created for ' + details.payer_given_name);
});
},
}).render('#paypal-button-container');
With this, the paypal box appears but then goes away right after it loads with this in the CMD:
#<OpenStruct id="1Pxxxxxxx394U", links=[#<OpenStruct href="https://api.sandbox.paypal.com/v2/checkout/orders/1P0xxxxxxx394U", rel="self", method="GET">, #<OpenStruct href="https://www.sandbox.paypal.com/checkoutnow?token=1P07xxxxxxx94U", rel="approve", method="GET">, #<OpenStruct href="https://api.sandbox.paypal.com/v2/checkout/orders/1Pxxxxxxx4U", rel="update", method="PATCH">, #<OpenStruct href="https://api.sandbox.paypal.com/v2/checkout/orders/1P07xxxxxxx394U/authorize", rel="authorize", method="POST">], status="CREATED">
No template found for PaypalPaymentsController#create, rendering head :no_content
Completed 204 No Content in 2335ms (ActiveRecord: 15.8ms)
I have not used smart buttons. However, you should not have "a ton more code" in a create action. If you are following MVC and rails conventions. It would seem that you need a seperate controller action to handle the payment authorization separately from the create action. But if you can get to this point in your javascript, here is example of how you would send the data from paypal javascript back to your controller, this will need some work but hopefully it points you in the right direction:
// Finalize the transaction
onApprove: function(data, actions) {
return actions.order.capture().then(function(details) {
// Show a success message to the buyer
alert('Transaction completed by ' + details.payer.name.given_name + '!');
// here is where you should send info to your controller action via ajax.
$.ajax({
type: "POST",
url: "/orders",
data: data,
success: function(data) {
alert(data); // or whatever you wanna do here
return false;
},
error: function(data) {
alert(data); // or something else
return false;
}
});
});
}
This is most likely far too late, but ill add what worked for me.
You need to return the response ID to the PayPal script as a json object. All you need to do is update your create function like so :
...
begin
# Call API with your client and get a response for your call
response = client.execute(request)
# If call returns body in response, you can get the deserialized version from the result attribute of the response
order = response.result
render json: { orderID: order.id }
# #order.paypal_authorization_token = response.id
rescue BraintreeHttp::HttpError => ioe
# Something went wrong server-side
puts ioe.status_code
puts ioe.headers["debug_id"]
end
...

Checkbox in index view live updating

I'm currently learning rails and working on what I'm sure is everyone's first rails app, a simple todo list. I need to implement a checkbox next to the items to indicate whether they are complete or not. Each item has a boolean attribute called "completed" in their model. I have found a couple checkbox questions while searching but none explain the syntax very easily in the context of the index view.
Also, I really want the checkbox to work without a submit button. I know I could accomplish something like this using AngularJS's ng-model but I don't think it would be practical to implement angular for such a small thing and I don't know how angular works with rails.
If anyone could give me a pointer in the right direction, it would be greatly appreciated. Here's my index.html.erb for reference.
<h1>
To Do List
</h1>
<table>
<tr>
<% #todo_items.each do |item| %>
<!-- Checkbox here -->
<tc style="<%= 'text-decoration: line-through' if item.completed %>">
<%= link_to item.title, item %>
</tc>
<tc>
<%= item.description %>
</tc>
<tc>
<%= link_to "Edit", edit_todo_item_path(item) %>
</tc>
<tc>
<%= link_to "Delete",item, data:{:confirm => "Are you sure you want to delete this item?"}, :method => :delete %>
</tc>
<hr/>
<% end %>
</tr>
</table>
<p>
<%= link_to "Add Item", new_todo_item_path %>
</p>
This is my way, I don't know this way is right direction or not but this works for me (also different case but same of concept).
views for checkbox
You could put an id item or something into attribute of checkbox for find an object in controller if you send data to controller for get record of object, and you could define if attribute completed of record is true or false:
<%= check_box_tag :completed_item, 1, item.completed? ? true : false, { class: 'name-of-class', data: { id: item.id} } %>
controller
You need two action call set_completed and remove_completed, and also you don't need templates for them, just use format as json:
before_action :set_item, only [:set_completed, :remove_completed, :other_action]
def set_completed
#item.set_completed!
respond_to do |format|
format.json { render :json => { :success => true } }
end
end
def remove_completed
#item.remove_completed!
respond_to do |format|
format.json { render :json => { :success => true } }
end
end
private
def set_item
#item = Item.find params[:id]
end
Model
For set_completed! and remove_completed! you could define in your model
def set_default!
self.update_attributes(:completed => true)
end
def remove_default!
self.update_attributes(:completed => false)
end
routes
resources :address do
collection do
post 'set_completed'
post 'remove_completed'
end
end
Also, you need help JavaScript for handle send request from view to controller event click of checkbox:
jQuery
$(".completed_item").click(function(){
var check = $(this).is(":checked");
if (check == true){
set_completed($(this).attr('data-id'));
} else{
remove_completed($(this).attr('data-id'));
}
});
function set_completed(data_id) {
$.ajax({
type: 'POST',
url: "/items/set_completed",
data: { id: data_id},
dataType: 'json',
success: function(response){
if(response){
}else{
alert('error');
}
}
})
}
function remove_compelted(data_id) {
$.ajax({
type: 'POST',
url: "/items/set_completed",
data: { id: data_id},
dataType: 'json',
success: function(response){
if(response){
}else{
alert('error');
}
}
})
}

render some partial after destroy method. Rails, haml

I have a problem with destroy method. My idea is re-render partial, but partial should to do the check if #driving_informations.present?. So when I click link "delete" page do nothing, and data is staying in the page, when I press F5 and page reload everything ok - data not exist!
This is my controller method
def destroy
#driving_information.destroy
#user = #driving_information.user
#driving_informations = #user.driving_informations
render '_list.html.haml', layout: false
end
and View
%h4= t("driving_information.driving_info")
-if #driving_informations.present?
=render ("list")
-if #user == current_user
- #driving_informations.each do |driving_information|
= link_to t('common.edit'), edit_driving_information_path(driving_information), remote: true, data: { toggle: "modal", target: "#ajax-modal" }
= link_to t('common.delete'), driving_information_path(driving_information), method: :delete, remote: true, data: { confirm: t('common.confirm') }
-else
= link_to t('common.add'), new_driving_information_path, remote: true, data: { toggle: "modal", target: "#ajax-modal" } if #user == current_user
%p= t('driving_information.not_exists') if #user != current_user
:javascript
$(function(){
$("table.driving-information .destroy-btn").on("ajax:success", function(event, data, status, xhr){
$('#driving-information-block').html(data);
Any ideas?
I'd suggest that in your AJAX for on success of the destroy, target the form using jQuery and clear it. Assuming your form ID is form:
#('#form').find("input[type=text], textarea").val("");
Source: Clear form fields with jQuery

Resources