Where this stands in 2026
SupersededUse the official options now. The Power Pages Web API is generally available and handles create, read, update and delete from JavaScript. Server logic (generally available since April 2026) runs your own code on the server, and the Client API’s $pages.ajax (in preview) wraps those calls for you. The postback trick in this post was never supported and may break.
Official docs for today’s approach
- Overview of the Power Pages Web APIGenerally available. Create, read, update and delete records.
- Compose HTTP requests and handle errorsIncludes Microsoft’s safeAjax wrapper for the request token.
- Power Pages server logic overviewRun JavaScript on the server. Generally available since April 2026.
- Client API: server endpoints and cloud flows (preview)$pages.ajax for calling server logic, flows and the Web API.
Written in 2021 for PowerApps Portals. See what things are called now.
Microsoft has since renamed the product and most of its parts. The text keeps the original terms.
| In this note | Today |
|---|---|
| PowerApps Portals | Power Pages |
| Entity | Table |
| Entity Form | Basic form |
Overview
Have you ever wanted to submit an Entity Form without forcing your users to wait for the screen to completely refresh?
Have you ever wanted to add in a timer-based auto-save functionality?
Have you ever dug through the client-side code and noticed that there’s a hidden option, just out of reach, indicating an async save process exists?
Well, I have, and after a few attempts over the years, I’ve found it really doesn’t take much code to actually implement.
Minimum Requirements
At its simplest, all you really need is the following code snippet. This identifies the form expecting a POST method (as opposed to the GET of the Search functionality), identifies the target URL, adds the necessary __EVENTTARGET data and submits the form.
var $form = $("form[method='post']");
$form[0].__EVENTTARGET.value = $(".submit-btn").attr('name');
$.post(
$form[0].action,
$form.serialize()
).done(function(data) {
console.log("success!");
}).fail(function (data) {
console.log("fail :(");
});Gap Considerations
- No pre-submit validation
- No post-submit validation
- No status updates about where the process is at
- It won’t really work on Insert Entity Forms
Most of these we could fix quickly, while others range from complex to very complex to correct.
If you need to work around a large number of Required Fields, you could look to implement some of the suggestions from my “Save vs. Submit” post.
With that in mind, you could consider setting up the following:
- Auto-save functionality (timer or after each field is changed) that only checks server-side validations (“Business Required” fields, Entity Form Metadata “Required”, etc.)
- Standard Submit button that fills a hidden field to indicate “completion” of the record
- Overriding
entityFormClientValidateto check a specific Validation Group, which is created on load (consider the simplified & extensive logic found on the Code Snippets page)
With this, the form would save as users complete fields so that their data isn’t lost. Once they’ve submitted it, you could verify the submission and look to lock the form – now you’d only need one button.
Improving the Code
To improve this code, we should look to do a few things:
- Check Client-Side Validation Pre-Submit
- Check Validations Post-Submit
- I’ve checked against the first level of validations below, but the code could be updated to include the custom plugin messages that typically appear at the top of the page as well
- Have an area dedicated to asynchronous responses (when needed)
- Add some defensive coding to protect against issues (such as the form not being present)
// Enhanced
$(document).ready(function () {
var $updateBtn = $("#UpdateButton");
var $saveBtn = $updateBtn
.clone()
.attr("id", "SaveButton")
.removeAttr("onclick")
.removeClass("btn-primary")
.addClass("btn-default")
.val("Save Async")
.insertBefore($updateBtn);
$updateBtn.attr(
"onclick",
"$('#new_submitdatefield').next('div.datetimepicker').datetimepicker().data('DateTimePicker').date(new moment());" +
$updateBtn.attr("onclick"));
var $asyncStatus = $("<div></div>")
.addClass("alert alert-block alert-error alert-danger")
.attr("id", "AsyncStatus")
.attr("role", "alert")
.hide()
.insertAfter($("div.actions"));
$saveBtn.click(function () {
$asyncStatus
.text("")
.hide();
if (Page_IsValid === false) {
$asyncStatus
.text("The form has failed initial validation.")
.show();
return;
}
var saveBtn = this;
saveBtn.value = "Saving..";
saveBtn.disabled = true;
var $form = $("form[method='post']");
if ($form.length === 0) {
this.value = "Error";
return;
}
$('#new_submitdatefield')
.next('div.datetimepicker')
.datetimepicker()
.data('DateTimePicker')
.date(null);
$form[0].__EVENTTARGET.value = $(".submit-btn").attr('name');
$.post(
$form[0].action,
$form.serialize()
).done(function(data) {
var $submittedValidations = $(data).find(".validation-summary");
if ($submittedValidations.children().length > 0 ||
$submittedValidations.text().trim().length > 0) {
$asyncStatus
.text("Please see below as to why the form couldn't be saved.")
.append("<hr/>")
.append($submittedValidations)
.show();
}
}).fail(function (data) {
$asyncStatus
.text("Something went wrong and we are unable to save the form.")
.show();
}).always(function () {
saveBtn.value = "Save Async";
saveBtn.disabled = false;
});
});
});Async in Action
Below you will find examples where I’ve updated my Save as Draft work to replace the Save button with a “Save Async” button. They showcase the happy path, a failed client validation, and a failed server validation.
Recreated illustration: Async Save Success: clicking Save Async posts the form in the background without a page reload, and refreshing the page shows the change was saved.
- Callout 1: While the background POST runs, the button reads “Saving..” and is disabled.
- Callout 2: After a manual refresh, the new value is there: it was saved without a full postback.
Recreated illustration. The original screenshot was lost with the old site; this drawing follows the article text.
Refresh shows persistence
Recreated illustration: Async Save - Server Error: the server rejects the save, and the validation summary from the response is copied into the #AsyncStatus alert.
- Callout 1: The #AsyncStatus alert, inserted after the form actions.
- Callout 2: The .validation-summary lifted from the server’s HTML response.
Recreated illustration. The original screenshot was lost with the old site; this drawing follows the article text.
Transfers Validation Summary
Recreated illustration: Async Save - Client Error: client-side validation fails before anything is posted, and a standard message is shown; Page_Validate() could be used to show the normal error list instead.
- Callout 1: Page_IsValid is false, so nothing is posted.
- Callout 2: A generic message. Calling Page_Validate() would show the portal’s normal validation list instead.
Recreated illustration. The original screenshot was lost with the old site; this drawing follows the article text.
Standard Message – Use Page_Validate() for normal error
Closing Thoughts
Alternatively, you could look to implement the same using the Portal’s native Web API support. As of this writing, it’s still very cumbersome to set up and, in my opinion, would require far more development effort. I’ve also seen developers come up with really interesting ways to AJAX GET an Entity Form via the standard FetchXML-as-an-API trick, in order to post it asynchronously. Unfortunately, this typically requires writing code to fill specific fields or do the mapping.
My hope is that this code sheds some light on additional possibilities. Feel free to let me know if it works for you, or if you’ve found something better.
Restored in 2026 from the Internet Archive at its original address. Figures are recreated illustrations of lost screenshots; the text was lightly copy-edited.