top of page

Power Pages One Step Submit: Uploading Files and Binding Attachments

  • Writer: Sofia Ng
    Sofia Ng
  • Aug 29
  • 2 min read

We’ve now got a parent record being created reliably with tokens, payloads, and even lookup fields. That was the backbone. Now it’s time to add the other half - uploading files and linking them to the record in one go.

A platypus wearing goggles is juggling several flying documents and folders, while a progress bar floats above its head. The files land neatly into a box labeled “Order,” symbolising parallel uploads with feedback.

The flow

Here’s what happens once the parent record is created:

  1. Create a child row in the orderattachments table.

  2. Upload the file into its File column (filecontent).

  3. Bind the attachment back to the parent record through the relationship.

Do that for each file in the picker, and you’ve got a clean, single-step user experience.


Creating the child row

We start by creating an empty row that will hold the file.


async function createChildRow(filename){
  const r = await apiJson(`/_api/orderattachments`, "POST", { "name": filename || "file" });
  const id = (r.headers.get("entityid")||"").replace(/[{}]/g,"");
  if (!id) throw new Error("No entityid returned for attachment");
  return id;
}


Uploading the file

Next, we upload the binary data to the File column.

async function uploadFile(childId, file){
  const r = await apiFetch(`/_api/orderattachments(${childId})/filecontent`, {
    method: "PATCH",
    headers: {
      "Content-Type": "application/octet-stream",
      "If-None-Match": "null",
      "x-ms-file-name": file.name
    },
    body: file
  });
  if (!r.ok) throw await r.json().catch(() => ({ error:{message:r.statusText, status:r.status} }));
}

Linking to the parent

Finally, connect the child back to the parent record.

async function bindChildToParent(childId, parentId){
  const base = window.location.origin + "/_api";
  const url  = `/_api/orders(${parentId})/orders_orderattachments/$ref`;
  const body = { "@odata.id": `${base}/orderattachments(${childId})` };
  const r = await apiFetch(url, {
    method:"POST",
    headers:{ "Content-Type": "application/json" },
    body: JSON.stringify(body)
  });
  if (!r.ok) throw new Error("Link failed");
}

Running uploads in parallel

If a user selects several files, we don’t want them stuck waiting for one file at a time. A simple queue with a parallel limit keeps things quick but controlled:

async function runQueue(tasks, limit=3){
  let i=0;
  const workers = Array(Math.min(limit, tasks.length)).fill(0).map(async ()=>{
    while(i<tasks.length){ const idx=i++; await tasks[idx](); }
  });
  await Promise.all(workers);
}

User feedback

To keep users informed:

  • Show a progress overlay with a spinner and a counter.

  • Update the message as each file completes.

  • Close the overlay once all uploads finish.

This avoids the “click and hope” problem of the default form.


Production guardrails

Before rolling this out widely:

  • Add an allow-list of file types and size caps.

  • Handle duplicate names gracefully.

  • Decide whether to fail all files if one fails, or continue and report the error.

  • Make sure your Web Role has the right permissions (Create, Append, Append To, Write on File).


What we’ve built

With all three posts combined you now have:

  • A custom Submit button that creates the record and uploads files in one click.

  • Resilient token handling and lookups.

  • Parallel uploads with progress feedback.

  • Guardrails to keep the system safe and user friendly.


No more two-step process. Just one smooth action.


Looking to streamline your own Power Pages forms? If you’d like help implementing this kind of one-step process in your environment, get in touch - we’d be happy to chat.


Comments

Rated 0 out of 5 stars.
No ratings yet

Add a rating

Contact Us

QUESTIONS?

WE'RE HERE TO HELP

  • LinkedIn

© 2023 by Ava Technology Solutions. Proudly created with Wix.com

bottom of page