admin管理员组文章数量:1352266
I have a form that submits data with the javascript fetch()
API to MySQL database with PHP.
In the code below, when the form is submitted a success message is outputted on the page and a hard refresh is prevented thanks to the fetch()
API.
The board module itself is initially shown via a click event on an 'Add To Board' element.
Because the boards list is outputted onto the page in a while
loop, I'd like it so the new board name is also outputted in the loop without the page refresh. I thought I could do this by adding a simple GET
request in a separate fetch()
function. This isn't working though (I'm not getting any error messages either).
When a hard refresh of the page happens the new board that is added onto the outputted list and appears on the page as expected, so I know the PHP is working all OK on the backend.
** EDIT **
I've put in the original code I tried, which is basically the same as @willgardner's answer.
Because I'm relatively new to fetch()
and AJAX in general - am I meant to construct (with JavaScript) a new button element in the form that will show the updated result from the get
request? I've assumed the PHP loop would output this onto the page when the get
request happens? Like it does when the page is initially loaded?
I had also missed off the input element in the HTML that is used to post
a board name to the database which is then fetched back with the get
request. This has now been added and is the create-board-name
input element.
JavaScript
// Use fetch() to prevent page doing hard refresh when a new board name is created
let boardModuleForm = document.querySelector('.board-module-form'),
// URL details
myURL = new URL(window.location.href),
pagePath = myURL.pathname
if (boardModuleForm) {
boardModuleForm.addEventListener('submit', function (e) {
if (e.submitter && e.submitter.classList.contains('js-fetch-button')) {
e.preventDefault();
const formData = new FormData(this);
formData.set(e.submitter.name, e.submitter.value);
fetch(pagePath, {
method: 'post',
body: formData
})
.then(function(response) {
if (response.status === 200) {
fetch(pagePath, {
method: 'get',
})
.then(function(response) {
return response.text();
}).catch(function(error) {
console.error(error);
})
}
return response.text();
})
.catch(function(error) {
console.error(error);
})
}
})
}
HTML and some PHP This all works OK because the page returns the correct data when a hard page refresh occurs
<form class="board-module-form" method="post">
<?php
if (isset($_SESSION['logged_in'])) {
$board_stmt = $connection->prepare("SELECT * FROM `boards` WHERE `user_id` = :id ORDER BY id DESC");
$board_stmt -> execute([
':id' => $db_id // variable created when user logs in
]);
while ($board_row = $board_stmt->fetch()) {
$db_board_id = htmlspecialchars($board_row['id']);
$db_board_name = htmlspecialchars($board_row['board_name']);
$db_board_user_id = htmlspecialchars($board_row['user_id']);
?>
<button class="board-list-item" name="board-name" type="submit">
<?php echo $db_board_name; ?>
</button>
<?php
}
}
?>
<div class="submit-wrapper">
<input id="board-name" name="create-board-name" type="text">
<button type="submit" name="submit-board-name" class="js-fetch-button">Submit Board</button>
</div>
</form>
I have a form that submits data with the javascript fetch()
API to MySQL database with PHP.
In the code below, when the form is submitted a success message is outputted on the page and a hard refresh is prevented thanks to the fetch()
API.
The board module itself is initially shown via a click event on an 'Add To Board' element.
Because the boards list is outputted onto the page in a while
loop, I'd like it so the new board name is also outputted in the loop without the page refresh. I thought I could do this by adding a simple GET
request in a separate fetch()
function. This isn't working though (I'm not getting any error messages either).
When a hard refresh of the page happens the new board that is added onto the outputted list and appears on the page as expected, so I know the PHP is working all OK on the backend.
** EDIT **
I've put in the original code I tried, which is basically the same as @willgardner's answer.
Because I'm relatively new to fetch()
and AJAX in general - am I meant to construct (with JavaScript) a new button element in the form that will show the updated result from the get
request? I've assumed the PHP loop would output this onto the page when the get
request happens? Like it does when the page is initially loaded?
I had also missed off the input element in the HTML that is used to post
a board name to the database which is then fetched back with the get
request. This has now been added and is the create-board-name
input element.
JavaScript
// Use fetch() to prevent page doing hard refresh when a new board name is created
let boardModuleForm = document.querySelector('.board-module-form'),
// URL details
myURL = new URL(window.location.href),
pagePath = myURL.pathname
if (boardModuleForm) {
boardModuleForm.addEventListener('submit', function (e) {
if (e.submitter && e.submitter.classList.contains('js-fetch-button')) {
e.preventDefault();
const formData = new FormData(this);
formData.set(e.submitter.name, e.submitter.value);
fetch(pagePath, {
method: 'post',
body: formData
})
.then(function(response) {
if (response.status === 200) {
fetch(pagePath, {
method: 'get',
})
.then(function(response) {
return response.text();
}).catch(function(error) {
console.error(error);
})
}
return response.text();
})
.catch(function(error) {
console.error(error);
})
}
})
}
HTML and some PHP This all works OK because the page returns the correct data when a hard page refresh occurs
<form class="board-module-form" method="post">
<?php
if (isset($_SESSION['logged_in'])) {
$board_stmt = $connection->prepare("SELECT * FROM `boards` WHERE `user_id` = :id ORDER BY id DESC");
$board_stmt -> execute([
':id' => $db_id // variable created when user logs in
]);
while ($board_row = $board_stmt->fetch()) {
$db_board_id = htmlspecialchars($board_row['id']);
$db_board_name = htmlspecialchars($board_row['board_name']);
$db_board_user_id = htmlspecialchars($board_row['user_id']);
?>
<button class="board-list-item" name="board-name" type="submit">
<?php echo $db_board_name; ?>
</button>
<?php
}
}
?>
<div class="submit-wrapper">
<input id="board-name" name="create-board-name" type="text">
<button type="submit" name="submit-board-name" class="js-fetch-button">Submit Board</button>
</div>
</form>
Share
Improve this question
edited Aug 25, 2022 at 16:50
VLAZ
29.1k9 gold badges63 silver badges84 bronze badges
asked May 4, 2022 at 22:24
pjk_okpjk_ok
97710 gold badges53 silver badges110 bronze badges
7
- 2 Probably because the get request is sent before the post request has finished. fetch returns a promise. So you would either have to do the get request inside the .then(), or you could change the event listener function to an async function and use await fetch. – jakobS Commented May 4, 2022 at 22:47
- 1 hi @jakobS - I did try that initially but it still doesn't work – pjk_ok Commented May 7, 2022 at 12:52
-
2
@paulo_cam can you update the above snippet with the get request inside the .then() of the post request? What are you doing with
response.text()
from the get request? Are you displaying it? – Jay Surya Commented May 7, 2022 at 13:08 -
1
I have the same question as jay - what do you see if you
console.log(response)
instead of returnresponse.text()
in the get method callback? If you see something normal (after making the changes that jakobS and willgardner remended), perhaps the problem is in the way you're updating the UI based off a successful request? – Andrew Stegmaier Commented May 9, 2022 at 17:46 -
1
@JaySurya I had assumed (and this may well be incorrect) that the response to the
get
request would automatically be output in the loop on the page in the HTML that outputs the list of buttons in the form? Do I have to physically construct / add a new button to hold the response with JavaScript? When I console.log the response it does seem to working correctly – pjk_ok Commented May 9, 2022 at 18:12
4 Answers
Reset to default 2 +50This looks like an issue with your promises in the JavaScript. I've added some ments below to show where the issue is.
Essentially, the GET fetch request is run before the POST fetch request has finished, so the GET fetch request is not returning the new data that has been POSTed, because it doesn't yet exist in the database.
if (boardModuleForm) {
boardModuleForm.addEventListener('submit', function (e) {
if (e.submitter && e.submitter.classList.contains('js-fetch-button')) {
e.preventDefault();
const formData = new FormData(this);
formData.set(e.submitter.name, e.submitter.value);
/**
* This is asynchronous. To ensure code is run after the (response) promise
* has resolved, it needs to be within the .then() chain.
*/
fetch(pagePath, {
method: 'post',
body: formData
})
.then(function (response) {
if (response.status === 200) {
// output success message
}
return response.text();
})
.catch(function(error) {
console.error(error);
})
// ----- GET REQUEST TO 'FETCH' NEW BOARD NAME FROM DATABASE
/**
* This will run immediately after the fetch method above begins.
* So it will run before the data you POST to the PHP is saved
* to the db, hence when you fetch it, it doesn't return
* the new data.
*/
fetch(pagePath, {
method: 'get',
})
.then(function (response) {
return response.text();
})
.catch(function(error) {
console.error(error);
})
}
})
}
You can solve this by moving the GET fetch request into the chained promises of the POST request:
// Use fetch() to prevent page doing hard refresh when a new board name is created
let boardModuleForm = document.querySelector(".board-module-form"),
// URL details
myURL = new URL(window.location.href),
pagePath = myURL.pathname;
if (boardModuleForm) {
boardModuleForm.addEventListener("submit", function (e) {
if (e.submitter && e.submitter.classList.contains("js-fetch-button")) {
e.preventDefault();
const formData = new FormData(this);
formData.set(e.submitter.name, e.submitter.value);
/**
* This is asynchronous. To ensure code is run after the (response) promise
* has resolved, it needs to be within the .then() chain.
*/
fetch(pagePath, {
method: "post",
body: formData
})
.then(function (response) {
if (response.status === 200) {
// ----- GET REQUEST TO 'FETCH' NEW BOARD NAME FROM DATABASE
/**
* This will now run after the POST request promise has resolved
* and new data successfully added to the db.
*/
fetch(pagePath, {
method: "get"
})
.then(function (response) {
return response.text();
})
.catch(function (error) {
console.error(error);
});
}
return response.text();
})
.catch(function (error) {
console.error(error);
});
}
});
}
If you feel like this is getting a bit messy and you want to avoid callback hell you could switch to using async/await syntax instead of .then() but this is of course entirely optional!
Requirements
From what I understand, you have,
- A page that lists some boards from the DB.
- POST request adds new data to the DB.
What you are trying is,
- Do a post request that adds the data to DB
- After it's done do a GET request to get new markup
- (You are missing this step) Use the newly obtained markup and replace the page content with the new markup.
Brief
Requests made to a URL with fetch
resolves with the content of that page. What you do with that content is up to you.
In your code you are not doing anything with it.
Idea
So after your get request, you need to get the data and populate it in your document.
fetch( pagePath, {method: "get"} )
.then( function ( response ) {
return response.text();
} )
.then( htmlResponse => {
// Use the data to populate into current document
populateHTMLDataToPage( htmlResponse );
} );
Function populateHTMLDataToPage
But wait, we don't have any populateHTMLDataToPage
function!
That function is the key to the problem. The function is supposed to parse newly received data and put it into the page.
A quick but dirty way would be to replace everything on the page with new content received. This is easy but it's dirty because it will remove all event handlers you have added before.
function populateHTMLDataToPage( htmlResponse ) {
document.open();
document.write( htmlResponse );
document.close();
}
This would mean you'd need to reattach your event handlers to the page because all elements have changed.
What you need is a better implementation of populateHTMLDataToPage
function. Ideally you would want to target only the element with updated content. With class/ID on your list/wrap that contains the loop data. For simplicity let's assume all your loop data is inside <div id='boards'>
and </div>
,
- Parse
htmlResponse
as html, relatively easy with jQuery. - Find your
#boards
element inhtmlResponse
and get its innerHTML. - Find
#boards
in your page and replace it's innerHTML with what's the new one.
So you can test the plausibility of the solution by usnig the provided populateHTMLDataToPage
function. Go on a path where you reattach the event handlers after replacing everything. Or develop your nicer populateHTMLDataToPage
which only updates the part it needs to update.
PHP code only runs on the server when you load the page initially. When you handle the fetch
GET
request manually in JavaScript on the client-side, the PHP code won't be run at all. So, yes, you need to manually construct the DOM elements in JavaScript, populate them with the information acquired from the server response, and then insert them into the DOM.
It's hard to provide an exact example without knowing the exact shape of your server response and your DOM, but here's how you would read a "name" property out of a json-based server response, and insert it into a new <li>
that's at the end of a <ul id="myList">
element.
fetch(pagePath, { method: "get" }).then(async function (response) {
const { name } = await response.json(); // This will only work if your server returns JSON in the body. If there's another shape to the data, you'll need to experiment/post additional details to your question.
const newListItem = document.createElement("li")
newListItem.innerText = name;
document.getElementById("myList").append(newListItem);
})
You are sending a POST request and in the .then()
handler you send a GET request. Assuming that the POST is successfully doing its thing on the server and the GET is also correctly getting the response, your issue should be assumed to be client-side only. However, before you assume this, it would not hurt to check (again) that everything works on the server by looking at the Network tab of your developer tools in the browser (right-click anywhere and in the menu that pops up, click on "inspect" or the text that's similar to it).
Please perform the (unsuccessful) test again, perform the operations you have outlined as the repro steps and look at the response of the POST and the GET elements and make sure that it's correct before you proceed to fix the client-side issue. In this answer I'm assuming that the server-side is either correct or you know how to fix it and will focus on the client-side.
This is the relevant part of your code:
fetch(pagePath, {
method: 'post',
body: formData
})
.then(function(response) {
if (response.status === 200) {
fetch(pagePath, {
method: 'get',
})
.then(function(response) {
return response.text(); //incorrect
}).catch(function(error) {
console.error(error);
})
}
return response.text(); //incorrect
})
.catch(function(error) {
console.error(error);
})
I have marked the problematic lines as //incorrect
. You are returning response.text()
instead of applying the response on the UI. Instead of returning it, you will need to refresh your UI with them.
At this point the following were not clarified in the question yet:
- what your response looks alike
- what should the UI do with the response
- what your UI looks alike
So, since these elements are missing from the question, I'm providing you a more abstract answer that nevertheless should help you:
- your POST request receives a
response
, maybe that's also containing important information that you may ignore - you might have an error that you and we are unaware of, it is advisable to check the Console tab of your browser's Dev Tools to see whether your scripts errors out
- your GET request contains information that you do nothing with (you only
return
it in a callbackfunction
) - your HTML needs to be refreshed
So, you will need to carefully study the response of your GET request and write a Javascript code/function that takes the response of the GET as an input and performs the necessary changes on your HTML. If you need help with this, you will need to provide:
- a sample response of the server as an example
- an HTML code
- optionally CSS styling
- description of what should happen to the UI based on the response
Of course, it is strongly advised to provide these in a reproducible and minimal snippet added to your question.
- your UI n
本文标签:
版权声明:本文标题:javascript - Using A fetch() GET Request After A fetch() POST Request Has Been Submitted To Output Database Data Without A Hard 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1743887569a2556336.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论