I want to post with the Fetch API and call an action from my controller but the action is not being loaded like when I would do it with submitting a post form.
function postQuery() {
let query = document.getElementById("query").value;
fetch('/actionName', {
method: 'POST',
body: query,
headers:
{
"Content-Type": "application/x-www-form-urlencoded"
}
})
.then(response => {
console.log(response);
})
.then(data => {
console.log('Success:', data);
})
}
/actionName is not being loaded, I am supposed to be directed to a different page.
When I would submit a form like that:
<form action="/actionName" method="post">
the public function actionName would be called but with fetch its not working.
When i try to see the $_POST with var_dump($_POST) inside of actionName, I am getting an empty array...I dont understand this
I see two questions here:
Why is the data not accessible to the server
Why is the brower not redirected to /actionName
Answer to #1:
Make sure the content type header matches the data you are sending, if it is raw json, you should use application/json rather then application/x-www-form-urlencoded. If you want to send a form using fetch API, you would need to either serialize to form to a URL encoded format, or use FormData, for example:
var fd = new FormData(document.getElementById('myForm'))
fetch('/actionName', {
method: 'POST',
headers: {
'Content-Type': 'multipart/form-data;'
},
body: fd
})
Answer to #2:
Submitting HTML Forms will direct the browser to the forms action, so if I submit a form to /actionName, I will end up seeing the HTML that is returned by the server for that route. Using Fetch API to submit a form is a type of AJAX, which means that it is capable of communicating with the server without needing to load a new page.
With that in mind, you have a few options:
Use a regular form so that the desired default behavior happens
Manually redirect the user somewhere after the fetch promise resolves, something like:
fetch(/*..*/).then(data => {
console.log('Success:', data);
window.location.href = '/otherPage'
})
Render the response HTML without redirecting the user, something like:
fetch(/*..*/).then(data => {
console.log('Success:', data);
data.text().then(rawHTML => {
document.body.parentElement.innerHTML = rawHTML
})
})
My personal intuition would be to go with the first option, as it suits your requirements and is the simplest.
Related
login.js file:
const form = document.getElementById('loginForm');
form.addEventListener('submit',async(e)=>{
e.preventDefault();
return await fetch(`localhost:8000/route`, {
method: "get",
headers: {
"Content-Type": "application/json",
},
});
}
app.js file:
app.get('/index',(req,res)=>{
res.render('route.ejs');
})
I only get the rendered html as a response but can't render it.I know that using form submission works.But can anyone plz tell me how to render the page without submitting form?Thank you in Advance.
The reason you get HTML as a response and not rendered because you use an AJAX call when using fetch. It doesn't instruct the browser what to do with that response.
You have few options.
Either when you get the response back you handle it in your callback like so:
return await fetch(`localhost:8000/route`, {
method: "get",
headers: {
"Content-Type": "application/json",
},
}).then(res=> {
let htmlText = await response.text();
document.getElementsByTagName('body')[0].innerHTML(bodyContent);
});
Or in fact if you just want to load a new page from your ejs template, set the window to the new location and browser will automatically render the response. Something like so in your login.js:
document.location.href = "/route_that_returns_html";
Note that since your question is generic you can take one of the above approach. However as your code suggests if you are trying to implement a login then the right way to do it is in following steps:
Let the form submit as usual ( not using ajax/fetch)
On server side using express, execute the login routine to authenticate the user.
If the authentication passes issue a res.redirect() to the authenticated page that renders using ejs or if fails, redirect back to login page with error.
Finally this DigitalOcean Article explains how to use ejs properly and is worth a read.
I would like to ask difference between calling Control's action via html FORM and JavaScript.
If I use NetCore calls the Action Method and returns View() obj into View page.
But when I call the same Action, webpage stays in the same page.
For example, you have a Employee List page where all employees are listed and wanted to see on of the specific employees details in Emplpoyee Details page.
With html
<form method='post' action ='EmployeeDetails'> </form> It works and browser opens the new page.
But with Javascript
function postSelectedCustomerData(path, method = 'post') {
let data = {
SearchText: 'SearchText1',
EmpName: '1',
DealsOnly: true,
PageNumber: 1
}
fetch("../Employee/EmployeeDetailsPost", {
method: "POST",
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
}).then(res => {
console.log("Request complete! response:", res);
});
}
NOTE: I know I can redirect the page from fetch Return with window.location.href. But I want to know is there any way i can call action method with JavaScript and return to the action Method page?
When you use standard <form> framework functionality, the server posts back to the client an HTTP response that's being interpreted by the browser as a new web page to be loaded.
When you skip this using a simple javascript fetch, the handling of the response it's all up to you. Just from a DOM point of view (let's suppose you only get '200' response!) you should identify the parent of the existing tree that will be replaced, remove said tree, insert the new one from the response.
fetch("../Employee/EmployeeDetailsPost", {
method: "POST",
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
}).then(res => {
let parent = document.getElementById('parent');
let root = document.getElementById('root');
root.remove();
parent.append(res);
});
}
Fairly new to Javascript. I've got a HTML form with onsubmit attribute equal to my JavaScript function getResponse(). In the getResponse() function I do a fetch (POST) to retrieve data from my API. I want to append the data to the page for the user to see, however, it seems to get wiped from the screen within milliseconds of it appearing on the screen.
My function getResponse():
fetch('/', {
method: "POST",
body: JSON.stringify(data),
headers: {
'Content-Type': 'application/json',
},
})
.then(json)
.then(function (data) {
$(".formResponse").append(`<p>Response: ${data.user}`)
})
.then(function (error) {
console.error(error);
});
It's a similar situation to if I console log the data it will disappear unless I 'preserve' the log in dev tools. Any suggestions, what am I doing wrong here?
You need to prevent the default behavior of the form submitting. you can do this by calling preventDefault() on the event passed from onsubmit:
function getResponse(event) {
// Prevent the default behavior of the form submitting.
event.preventDefault()
fetch('/', {
method: "POST",
body: JSON.stringify(data),
headers: {
'Content-Type': 'application/json',
},
})
.then(json)
.then(function (data) {
$(".formResponse").append(`<p>Response: ${data.user}`)
})
.then(function (error) {
console.error(error);
});
}
The <form> onsubmit is passed an event argument, so you would then do the following on your form:
<form onsubmit="getResponse(event);"></form>
I can't quite see from this code why the data would disappear.
You should use a GET method to retrieve data. POST is used to create a record or save something.
The CRUD methods associated with http requests are the following.
GET / RETRIEVE - to retrieve data from a remote server. Like 'Open'.
POST / CREATE - to save or create data to a database. Like 'Save As'.
PUT / UPDATE - to update or save extra data to an already existing record. Like 'Save'.
DELETE / DESTROY - to delete a record.
If that doesn't work you might have to add some more of your code so we can see what's going on.
I'm using the React template when creating a new project in Visual Studio. It has something like this for GET request:
fetch('api/SampleData/WeatherForecasts')
.then(response => response.json())
.then(data => {
this.setState({ forecasts: data, loading: false });
});
I'm learning how to do a POST request, so I modified the code to:
const formData = new FormData();
formData.append('values', this.state.values);
fetch('api/SampleData/WeatherForecasts', {
method: 'POST',
body: formData
}).then(response => response.json())
But not sure how to retrieve the formData on ASP.Net:
[HttpPost("[action]")]
public string WeatherForecasts()
{
// How to print out the values from formData here
// System.Diagnostics.Debug.WriteLine(Request.Form.ToString());
return "Hello";
}
Edit: I also don't know how to return a Json result from ASP.Net:
[HttpPost("[action]")]
public JsonResult WeatherForecasts()
{
// How to print out the values from formData here
// System.Diagnostics.Debug.WriteLine(Request.Form.ToString());
// How to return a Json here
// return {hello: 'Hello'};
}
My answer presumes that your this.state.values property contains a string for testing purposes only just like the following:
this.setState({
values: 'testingtext'
});
I guess you are on the right track, from front-end side I assume the data is being sent. You can additionally check in the browser's network tab how the data has been passed in the HTTP request's body.
I would modify the WeatherForcecasts() action as the following to capture the data on the back-end side:
[HttpPost]
public string WeatherForecasts([FromBody] string values)
{
// here you can check what has been passed to the values variable
return values;
}
About [FromBody] why you need to include into the attribute:
By default, Web API tries to get simple types from the request URI. The FromBody attribute tells Web API to read the value from the request body.
Source: Sending Simple Types
If you want to override the action name in your route then you can achieve that by adding into your method the [ActionName("YOUR_NEW_ACTION_NAME")] attribute but it is not a mandatory one:
You can override the action name by using the [ActionName] attribute.
Source: Routing by Action Name
I hope this helps! I guess this gives you an idea how to proceed further.
For some reason, I can't get file transfers to work via fetch. My code is really simple:
export function updateProfilePicture(apiKey, userID, picture) {
let data = new FormData();
data.append('picture', picture);
return fetch(`${API_URL}/v1/${ROOT}/update_profile_picture/${userID}`,{
headers: {'Authorization': `Token token=${apiKey}`},
method: 'POST',
data: data
}).then(response => response.json());
}
However, the resulting request does not seem to include any file whatsoever. Am I missing something? In the above example, picture is instance an of File
There could be two reasons:
data field in Fetch API is called body. Use this instead of data property.
(optional) You need to add another header: 'Content-Type', 'multipart/form-data'
Read more about Fetch API:
https://developers.google.com/web/updates/2015/03/introduction-to-fetch?hl=en
https://developer.mozilla.org/en/docs/Web/API/Fetch_API
Fetch polyfill