Response is coming as */* instead of turbo_stream in rails 7 - javascript

when hitting the stimulus controller it's going to the rails stimulus controller but the request is coming as */* but I want this request as turbo_stream, so how can i do that.
app/views/stimulus/index.html.erb
<div data-controller="toggle" data-toggle-url-value="<%= root_path %>">
<div>
<p class="mt-5">Copy text content</p>
<input type="text" data-toggle-target="txt"/>
<button data-action="click->toggle#copy">Copy</button>
</div>
app/controllers/stimulus_controller.rb
def index
end
app/javascript/controllers/stimulus_controller.js
static targets = ["txt"]
static values = { url: String }
connect() {
this.hideBtnTarget.hidden = true
console.log(this.urlValue);
fetch(this.urlValue).then((success) => {
console.log(success);
}).catch((e) => {
console.log(e);
})
}
copy(){
let txt = this.txtTarget
txt.select()
document.execCommand("copy")
txt.value = ""
}
I want a response as turbo_stream instead of */* .
thanks!

To get request as turbo_stream you need to add code inside fetch method headers.
fetch("your url path", {
method: "get",
headers: {
Accept: "text/vnd.turbo-stream.html"
},
});

Related

Laravel 405 error when passing parameters with Ajax

I'm pretty new to Laravel, I've been trying to pass parameters to the destroy() action in my controller with Ajax, the action runs as the pictures data gets removed both from the database and the picture from storage as well, however it gives back 405 error in the console for some reason. I've tried multiple solutions posted here, but none has worked unfortunately. The destroy() action works if I'm using it purely with PHP, but I'm trying to learn a bit of Ajax as well, also I'm doing it this way because I want to do it like there are multiple photos on the site at once, and each one has its own delete button, and I want to delete the photo based on which delete button has been pressed.
It says in the response headers that only GET, HEAD allowed.
Any help would be greatly appreciated, and thank you all in advance!
Routes
Route::get('/photos/create/{albumId}', [PhotosController::class, 'create'])->name('photo-create');
Route::post('/photos/store', [PhotosController::class, 'store'])->name('photo-store');
Route::get('/photos/{id}', [PhotosController::class, 'show'])->name('photo-show');
Route::delete('/photos/{id}', [PhotosController::class, 'destroy'])->name('photo-destroy');
Controller
public function destroy($id)
{
$photo = Photo::find($id);
if (Storage::delete('/public/albums/' . $photo->album_id . '/' . $photo->photo ))
{
$photo->delete();
return redirect('/')->with('success', 'Photo deleted successfully');
}
}
Ajax
$(document).on('click', '.deletePhoto', function(e) {
var photo = $(this).val();
$('#deletePhotoId').val(photo);
$('#photoDelete').modal('show');
});
$(document).on('click', '.confirmDelete', function(e) {
var photo = $('#deletePhotoId').val();
$.ajax({
type: "DELETE",
url: "/photos/"+photo,
data: {
_token: '{{ csrf_token() }}',
},
/* success: function(response){
alert(response);
},
error: function(response) {
alert('Error' + response);
} */
})
});
Also if I have added the csrf as in the Laravel documentation like this:
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
Then it gave me 419 error no matter where I have placed it.
Here's the error
XHRDELETEhttp://photo-project.test/
[HTTP/1.0 405 Method Not Allowed 61ms]
DELETE
http://photo-project.test/
Status405
Method Not Allowed
VersionHTTP/1.0
Transferred8.43 kB (8.19 kB size)
Referrer Policystrict-origin-when-cross-origin
Allow
GET, HEAD
Cache-Control
no-cache, private
Connection
close
Content-Type
application/json
Date
Sat, 03 Jul 2021 20:21:15 GMT
Server
Apache/2.4.46 (Win64) PHP/7.3.21
X-Powered-By
PHP/7.3.21
Accept
*/*
Accept-Encoding
gzip, deflate
Accept-Language
en-GB,en;q=0.5
Connection
keep-alive
Content-Length
47
Content-Type
application/x-www-form-urlencoded; charset=UTF-8
Cookie
XSRF-TOKEN=eyJpdiI6IlR0bitkaitPMFhhWEdnQ1ZqL1VpTXc9PSIsInZhbHVlIjoibVREOG9JRGpOQjBqZG40QU9vWVJsS2xtT2J3OXZJK3ZjVzNzZHNKNWdQakowK1lMZ1o0RStSQWFzTVFYZ1R5cFEvNjQ2bm9ZNklYbW8xcW54ZVlzOG9sVXJXN1Z3dmU0Lys0UXRWNWZLY29Femxjb2EvS09qM0hzbm9SSndOYXIiLCJtYWMiOiI2MWJlOTc3YWFhY2NkY2VhZGM5YWZhYmE0MjcyYTc5MmRiNmQwMjU0ZmFlZmMxYzEzNTExMGU4ZjlhMTY3OTYwIn0%3D; laravel_session=eyJpdiI6IldCNU9MSHRGbnNJRlEvWDBrMmZzSmc9PSIsInZhbHVlIjoidzhoc0VFajBhZXk2dkFSa2VmNkU2UmVReVZaOFFUeGJPam1pOXI3T3gvR0FFM3crd21SODI1ZWFJZk44UThDM0VjNFdsL2V6bzNvcHk0NG9vQlpoTEtIRlNQOStxaDlvVFUvS01iOEJIUDJzODFyck11ckpZRTRzMHhVYXhHZlYiLCJtYWMiOiI5NGJmMTBlMDhlOWU0OTU4ZDkyZWRhMzlhYzIwNzFkOTAzZWI3M2RjOTEzNzI5NTYyOWFkZWIyOWMyM2E3MmM2In0%3D
Host
photo-project.test
Origin
http://photo-project.test
Referer
http://photo-project.test/albums/12
User-Agent
Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0
X-Requested-With
XMLHttpRequest
Thank you again if you can help me in any measure as I'm kind of stuck here unfortunately.
Double-check that your photo JS variable actually has a value. If it's null for some reason, then the route being constructed would end up being the 'index' one, "/photos/", which of course does not have a DELETE http method associated with it.
Check your browser Network history to be sure. You'll see the route that it's attempting to hit, and can also double-check that against the results of php artisan route:list.
public function destroy(Request $request, $id){
$photo = Photo::find($id);
if (Storage::delete('/public/albums/' . $photo->album_id . '/' .$photo->photo )){
$photo->delete();
//if request is via ajax
if($request->ajax()){
return response()->json(['status'=>true,'message'=>'Photo Deleted Successfully']);
}
//otherwise redirect
return redirect('/')->with('success', 'Photo deleted successfully');
}
}
On Ajax Side
$(document).on('click', '.deletePhoto', function(e) {
var photo = $(this).val();
$('#deletePhotoId').val(photo);
$('#photoDelete').modal('show');
});
$(document).on('click', '.confirmDelete', function(e) {
var photo = $('#deletePhotoId').val();
$.ajax({
type: "DELETE",
url: "/photos/"+photo,
data: {
_token: '{{ csrf_token() }}',
},
dataType:'JSON',
cache:false,
success: function(response){
if(response.status){
console.log('Action Successfull....');
console.log('Response from server : ', response.message)
}
},
error: function(response) {
alert('Error' + response);
}
})
});
Just check the request type while returning response from server, If it is ajax then return the response data otherwise redirect to the page.

No response from API

I have created an API call in excel to get data from a Wix database.
The call:
Dim http As Object, JSON As Object
Set http = CreateObject("MSXML2.XMLHTTP")
http.Open "GET", "https://username.wixsite.com/mysite/_functions/Functionname", False
http.setRequestHeader "Authorization", "myauthkey"
http.Send
MsgBox (http.responseText)
The javascript http backend file on Wix:
import { ok, notFound, serverError } from 'wix-http-functions';
import wixData from 'wixdata';
export function get_Wixdata() {
let options = {
"headers": {
"content-type": "application/json"
}
};
return wixData.query("wix data collection name")
.find()
.then(results => {
if (results.items.length > 0) {
options.body ={
"items": results.items
}
return ok(options);
}
})
}
I tested the call (without authorisation) on JSON place holder and it worked fine.
Just trying to debug what's happening as I am getting "" as a response.
Even if I enter the wrong API key I still get "", even a wrong url it's still a "" response.
I take it I am clearly way off the mark with what I am trying to do..
Did you tried put both headers in your request, like the following:
let headers = new Headers({
'Content-Type': 'application/json',
'Authorization': '....'
});
The issue was with the VBA call, the header was not needed.
Dim https As Object, JSON As Object
Set https = CreateObject("MSXML2.XMLHTTP")
With CreateObject("Microsoft.XMLHTTP")
.Open "GET", "end point url", False
.send
response = .responseText
End With

How perform PATCH request from html form using NodeJS?

This was my attempt to perform a patch request on an HTML form from the frontend to MongoDB database via NodeJS API.
This is a Nodejs API structure
app.route("/requests/:rollno")
.get(function(req, res) {
// get method to find a query
})
.patch(function(req, res) {
Request.update({
rollno: req.params.rollno
}, {
$set:req.body
},
function(err) {
if (!err) {
res.send("Successfully updated requests");
}else{
res.send(err);
}
}
);
});
On the frontend side of the form
<%- include('partials/header') %>
<h1>Recent requests</h1>
<% for(let i = 0; i < posts.length; i++) { %>
<article>
<form action="/requests/21" method="PATCH">
<input type="text" name="status[0]">
<div id="participant"></div>
<br>
<button type="button" onclick="addParticipant()">Add</button>
<button type="button" onclick="delParticipant()">Remove</button>
<div class="btn-block">
<button type="submit" href="/success">Submit Request</button>
</div>
</form>
</article>
<% } %>
The form is not actually updating the information on the actual server. But with the postman, the data is getting updated.What's the best resolution for the same or is there any alternate method for the same ?
<form action="/requests/B194057EP" method="PATCH">
This is invalid, HTML forms method attribute only supports GET and POST method. <form method="put"> is invalid HTML and will be treated like a GET request.
Now to solve this, you can make use of Jquery AJAX to send data from the client-side to your backend.
$.ajax({
url: '<api url>',
type: 'PATCH',
data: {
data: sendData
},
success: function () {
}
})
;
In vanilla JS, you can use promises, as well as async/await to do so, this is a basic example of it.
async patching() {
const responseFlow = await fetch(URL, {
method: "PATCH",
headers: {
'Content-Type': 'application/json'
},
// The content to update
body: JSON.stringify({
name: "New name"
})
})
}

Is there a way to render a js.erb after a fetch call?

In my Rails app, I want to render a js.erb partial after a fetch call and for some reasons I do not understand it does not work. If you can help me, it would be great.
In a view, an event trigger this function that basically do a fetch request:
function updateState(id, state){
const csrfToken = document.querySelector('meta[name="csrf-token"]').attributes
.content.value;
fetch(window.location.origin + "/update_state", {
method: "POST",
headers: {
'Accept': "JS",
"Content-Type": "application/json",
"X-CSRF-Token": csrfToken
},
body: JSON.stringify({'id': id, 'state': state}),
credentials: "same-origin"
})
}
Then in my controller:
def update_state
#model = Model.find(params[:id])
authorize #model
#model.update(state: params[:state])
respond_to do |format|
format.html { redirect_to xxx }
format.js
end
end
In my js.erb files:
console.log('hello');
However, in that case, I get an error from the server:
ActionController::UnknownFormat
at the line 'respond_to do |format|
I have the feeling that the server do not understand the header of the fetch:
'Accept': "JS"
When I look at the logs of the server:
Started POST "/update_state" for 127.0.0.1 at 2019-04-04 11:22:49 +0200
Processing by ModelsController#update_state as JS
But I think that Rails does not recognize it. How do I do?
I tried also this in the controller:
def update_state
#model = Model.find(params[:id])
authorize #model
#model.update(state: params[:state])
render 'update_state.js', layout: false
end
Which does not fire errors. I received in the client side the js.erb. However, it is not executed (console.log does not execute).
Do you have any idea? Thank a lot for your help.
I manage the other way around. I never succeded with js.erb.
So I use a fetch call with a response in text:
function updateState(id, state){
const csrfToken = document.querySelector('meta[name="csrf-token"]').attributes
.content.value;
fetch(window.location.origin + "/update_state", {
method: "POST",
headers: {
'Accept': "JS",
"Content-Type": "application/json",
"X-CSRF-Token": csrfToken
},
body: JSON.stringify({'id': id, 'state': state}),
credentials: "same-origin"
}).then(response => response.text())
.then(data => {
div.inserAdjacentHTML('XXX', data);
});
}
Then in the controller/action:
def update_state
#model = Model.find(params[:id])
authorize #model
#model.update(state: params[:state])
render 'update_state.js', layout: false, content_type: "text/plain"
end
That way, it works. Hope it helps.
Edit : look at the edit for a fully working solution on triggering manually a .js.erb file.
Had the same issue and there are actually two problems here. Your are right, your headers are not. These headers to trigger the .js.erb worked for me and seems pretty right :
'dataType': 'json',
'contentType': 'application/json; charset=utf-8',
'X-Requested-With': 'XMLHttpRequest',
'X-CSRF-Token': - your CSRF method to get the token -
But secondly to trigger the js response, your respond_to block :
respond_to do |format|
format.html { redirect_to xxx }
format.js
end
Has to have the format.js first. As rails has a response for the HTML it will respond with HTML first and seems to not even look for the next format block.
respond_to do |format|
format.js
format.html { redirect_to xxx }
end
Hope it helps :-)
Edit 13/11/2018
Hey, long time no see ! The same issue happened to me again and my own previous reply wasn't solving the issue. Like you, the .js.erb file was rendered but nothing happened and the console.log was not displayed. So I created a link_to with remote: true that triggered my .js.erb file and compared the requests. Everything was the same except one thing : the type of the request. One was a fetch, the other was an xhr. It appears that the .js.erb files are triggered only by the xhr requests. So the solution is to build an old xhr request instead of a fetch, like this :
const url = 'your_custom_path' // Build the url to fetch
const csrf = document.querySelector('meta[name="csrf-token"]').getAttribute('content')
let xhr = new XMLHttpRequest();
// Will be a GET request here
xhr.open('GET', url, false);
​
// Setting the headers
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
xhr.setRequestHeader('X-CSRF-Token', csrf);
xhr.setRequestHeader('dataType', 'script');
​
// What to do with the response
xhr.onload = function() {
let responseObj = xhr.response
eval(responseObj) // You need to eval() manually the response for it to be executed
}
​
// Sending the request
xhr.send()
Hope it helps, even some months later! ;)
I ran into this issue in Rails 6 using fetch. My issue was solved by simply placing format.js above format.html. Thanks #holino. The following fetch works.
fetch(event.currentTarget.dataset.destroyPath, {
method: 'DELETE',
dataType: 'script',
credentials: 'include',
headers: {
'X-CSRF-Token': csrf
},
})

Laravel TokenMismatchException and dropzone

I'm trying to upload pictures via dropzone, but I get tokenmismatch error even tho I added csrf token everywhere needed, i'm getting quite desperate...
My form
{!! Form::open(['route' => 'photo.upload', 'id' => 'hello', 'method' => 'POST', 'class' => 'dropzone no-margin dz-clickable']) !!}
<div class="dz-default dz-message"><span>Drop files here to upload</span></div></form>
{!! Form::close() !!}
my script
Dropzone.autoDiscover = false;
Dropzone.options.hello = {
paramName: "file", // The name that will be used to transfer the file
maxFilesize: 5, // MB
parallelUploads: 2, //limits number of files processed to reduce stress on server
addRemoveLinks: true,
headers: {
'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content'),
},
accept: function(file, done) {
// TODO: Image upload validation
done();
},
sending: function(file, xhr, formData) {
// Pass token. You can use the same method to pass any other values as well such as a id to associate the image with for example.
formData.append("_token", $('input[name="_token"]').val() ); // Laravel expect the token post value to be named _token by default
},
init: function() {
this.on("success", function(file, response) {
// On successful upload do whatever :-)
});
}
};
// Manually init dropzone on our element.
var myDropzone = new Dropzone("#hello", {
url: $('#hello').attr('action')
});
Request headers
...
X-CSRF-Token:P4wc9NVVZJe1VjalPwO6d6WQXZ9eEqPd84ICpToG
...
Request Payload
------WebKitFormBoundarySKMUFNO6dbgzeQVK
Content-Disposition: form-data; name="_token"
P4wc9NVVZJe1VjalPwO6d6WQXZ9eEqPd84ICpToG
------WebKitFormBoundarySKMUFNO6dbgzeQVK
Content-Disposition: form-data; name="_token"
P4wc9NVVZJe1VjalPwO6d6WQXZ9eEqPd84ICpToG
------WebKitFormBoundarySKMUFNO6dbgzeQVK
Content-Disposition: form-data; name="file"; filename="Screen Shot 2016-01-14 at 18.27.40.png"
Content-Type: image/png
------WebKitFormBoundarySKMUFNO6dbgzeQVK--
and When I look in the generated form THERE IS the csrf field
<input name="_token" type="hidden" value="P4wc9NVVZJe1VjalPwO6d6WQXZ9eEqPd84ICpToG">
Do you have any idea why it's not working even when I put crsf token where I should?
thank you for your time.
Simply place hidden field within your form like as
<input type="hidden" name="_token" value="{{csrf_token()}}">
You can make it different way by passing value of token using ajax call like as
$(function () {
$.ajaxSetup({
headers: { 'X-CSRF-TOKEN': $('meta[name="_token"]').attr('content') }
});
});
Instead of creating new element which is kinda dirty. You can just include it inside your manually init of dropzone.
var myDropzone = new Dropzone("#hello", {
url: $('#hello').attr('action'),
headers: {
'x-csrf-token': document.querySelectorAll('meta[name=csrf-token]')[0].getAttributeNode('content').value,
}
});
For more detailed integration with laravel dropzone you can refer to this tutorial Integrating Dropzone.js in Laravel 5 applications

Categories

Resources