Custom Form - handling attachments

I have a Custom Form on my site which is designed to collect data and also a couple of attached files. The form works and when someone completes the form I can see the attachments in Content Manager

I have written js / jaw / PageCode to send an email to the site owner via SendGrid as documented here:

I can thus trigger an email that includes basic Form elements

I am trying to also include the Files uploaded via the form. I asked chatGPT how to code this step and was sent this code snippet:

\rFile Upload: ${$w(“#fileUpload1”).attachmentUrls.join(“, “)}
\rImage Upload: ${$w(”#imageUpload1”).attachmentUrls.join(", ")}`;

I modified the code to match the form labels that my site uses but this does not work

Ideas appreciated ?

Why you don’t simply show the code you have just right now working on your site?
Isn’t is much easier to understand your problem, when knowing the code, which is directly workin on your site?

Why the helper should now do all the work for you???

  1. Look here …
  1. Go there…

  2. Aussume which CODE i could have???

What the hell ?

First start to learn how to describe your Problem the right way.
Any of the HELPER here, will waste all that time just to help you for free!!!

Provide a good, detailed and qualitative description of your issue, including all the important informations related to your issue in DETAIL.

  1. What page are you working on ? (live site already online to take a look at)?
  2. What is the structure of your database?
  3. Do you use a DATASET or are you working with Wix-Data?
  4. Which elements are used in your example?
  5. Are you working on a dynamic or non-dynamic-page?
  6. Do you use a repeater?
  7. Are your elements inside a REPEATER ?
  8. Show your code.

The only really detailed information in your description i have found is, that you are using a ----------------------> CUSTOM-FORM . (maybe you want to show an pic of that form) ?
9) Surely you have also some CODE on BACKEND (did you mentioned this?)
10) Show also a pic of your SETUP, so your HELPER can imagine your situation a little bit better (visual).

Surely i could provide more QUESTIONS.
All these questions are just example-questions, to show you what it means to describe your ISSUE in DETAIL.

Now first try again and generate a good detailed and logical description of your project.
Maybe then, there will be someone, who will be willing to help you for → FREE ← (if the problem is not to ------> TIME-CONSUMING <-----

EDIT: → Sorry if this sounds a little bit → RUDE <–, but if you want qualitative answers —> you should also provide a qualitative ISSUE-DESCRIPTION !

Many thanks for taking the time to respond.

Sorry if you thought my post was not helpful !! I was trying to be economical with what I wrote ! So in answer…the live site is this one:

The form I am trying to use is this one (none dynamic):

https://www.stayontrackvetphysiotherapy.co.uk/new-referral-form

I have a Collection in my site directly generated by that form…most fields are freeform text / a couple of Address fields / a date field…some dropdown choices…it’s not a huge form

I am not sure what a REPEATER is in this context so I would assume I do not use one !

There are 3 code elements: 2 in the BackEnd (email.jsw and sendGrid.js) and Page Code (as described in the article I referenced)

BackEnd code elements (some stuff anonymised):

//email.jsw

import {sendWithService} from ‘backend/sendGrid’;

export function sendEmail(subject, body) {
const key = “#############”;
const sender = "sender email ";
const recipient = “my email”;
return sendWithService(key, sender, recipient, subject, body);
}

export function sendEmailWithRecipient(subject, body, recipient) {
const key = “SSG.WMIyqFcATdqhNAOee_c7RA.fICFX4Bh7xpkAHrisdNJ5gUHtz1jDfE5iNSen7shjyo”;
const sender = “sender email”;
return sendWithService(key, sender, recipient, subject, body);
}

//sendGrid.js

import {fetch} from ‘wix-fetch’;

export function sendWithService(key, sender, recipient, subject, body) {
const url = “https://api.sendgrid.com/api/mail.send.json”;

const headers = {
“Authorization”: "Bearer " + key,
“Content-Type”: “application/x-www-form-urlencoded”
};

const data = from=${sender}&to=${recipient}&subject=${subject}&text=${body};

const request = {
“method”: “post”,
“headers”: headers,
“body”: data
};

return fetch(url, request)
.then(response => response.json());
}

PageCode: (this version ignores the File Upload content but I tried to use the syntax from my earlier post and it failed)

import {sendEmail, sendEmailWithRecipient} from ‘backend/email’;

$w.onReady(function () {
$w(“#dataset1”).onAfterSave(sendFormData);
});

function sendFormData() {
const subject = New Vet Referral Form from ${$w("#input1").value};
const body = New Vet Referral from ${$w("#input1").value} \rOwners Name: ${$w("#input1").value} \rOwners Email: ${$w("#input2").value} \rOwners Contact Number: ${$w("#input3").value} \rOwners Address and Post Code: ${$w("#addressInput3").streetAddress} \rAnimals Name: ${$w("#input4").value} \rAnimal Species: ${$w("#dropdown1").value} \rAnimal Breed: ${$w("#input5").value} \rReferring Vet: ${$w("#input6").value} \rReferring Vet Practice: ${$w("#input7").value} \rPractice Address and Postcode: ${$w("#addressInput2").value} \rVet Email: ${$w("#input8").value} \rVet Phone: ${$w("#input9").value} \rBrief Description of the Problem: ${$w("#input10").value} \rPreference for making this appointment: ${$w("#dropdown3").value};
const recipient = $w(“#input8”).value;

sendEmailWithRecipient(subject, body, recipient)
.then(response => console.log(response));

sendEmail(subject, body)
.then(response => console.log(response));
}

The first thing what you will do…
Please DELETE your —> KEY <— immediately. (const key = "SSG.WMIyqFcATd…)

Done - thanks !!

Good catch

Ok!

  1. The form I am trying to use is this one (none dynamic):
    Right! —> NON-DYNAMIC-FORM.

I have a Custom Form on my site which is designed to collect data and also a couple of attached files. The form works and when someone completes the form I can see the attachments in Content Manager

$w.onReady(function () {
  $w("#dataset1").onAfterSave(sendFormData);
});

So that means you have a CUSTOM-FORM connected by an NON-DYNAMIC-DATASET (because your form is on a non-dynamic page).

Your Dataset-ID ---> $w("#dataset1")

Your problem/issue:

I am trying to also include the Files uploaded via the form.

Your Backend-Code for eMail:

import {sendWithService} from 'backend/sendGrid';

export function sendEmail(subject, body) {
  const key = "#############";
  const sender = "sender email ";
  const recipient = "my email";
  return sendWithService(key, sender, recipient, subject, body);
}

export function sendEmailWithRecipient(subject, body, recipient) {
  const key = "xxxxxxjDfE5ixxxxxxxxxxx";
  const sender = "sender email";
  return sendWithService(key, sender, recipient, subject, body);
}

Your Backend-Code for SendGrid…

import {fetch} from 'wix-fetch';

export function sendWithService(key, sender, recipient, subject, body) {
  const url = "https://api.sendgrid.com/api/mail.send.json";
  const headers = {
    "Authorization": "Bearer " + key,
    "Content-Type": "application/x-www-form-urlencoded"
  };

  const data = `from=${sender}&to=${recipient}&subject=${subject}&text=${body}`;

  const request = {
    "method": "post",
    "headers": headers,
    "body": data
  };
  return fetch(url, request)
   .then(response => response.json());
}

Your Page-Code…

import {sendEmail, sendEmailWithRecipient} from 'backend/email';

$w.onReady(function () {
    $w("#dataset1").onAfterSave(sendFormData);
});


function sendFormData() {
    const subject = `New Vet Referral Form from ${$w("#input1").value}`;
    const body = `New Vet Referral from ${$w("#input1").value}
    \rOwners Name: ${$w("#input1").value}
    \rOwners Email: ${$w("#input2").value}
    \rOwners Contact Number: ${$w("#input3").value}
    \rOwners Address and Post Code: ${$w("#addressInput3").streetAddress}
    \rAnimals Name: ${$w("#input4").value}
    \rAnimal Species: ${$w("#dropdown1").value}
    \rAnimal Breed: ${$w("#input5").value}
    \rReferring Vet: ${$w("#input6").value}
    \rReferring Vet Practice: ${$w("#input7").value}
    \rPractice Address and Postcode: ${$w("#addressInput2").value}
    \rVet Email: ${$w("#input8").value}
    \rVet Phone: ${$w("#input9").value}
    \rBrief Description of the Problem: ${$w("#input10").value}
    \rPreference for making this appointment: ${$w("#dropdown3").value}`;
    const recipient = $w("#input8").value;

    sendEmailWithRecipient(subject, body, recipient)
    .then((response)=> {console.log(response);});
        
    sendEmail(subject, body)
    .then((response)=> {console.log(response)});

}

You click onto your SUBMIT-BUTTON, which is surely also connected to your dataset inside the property-panel.

The saving-process starts…and calls the → sendFormData ← function.

$w.onReady(function () {
    $w("#dataset1").onAfterSave(sendFormData);
});

Before the next call of a backend-function you generate your subject + body data…

function sendFormData() {
    const subject = `New Vet Referral Form from ${$w("#input1").value}`;
    const body = `New Vet Referral from ${$w("#input1").value}
    \rOwners Name: ${$w("#input1").value}
    \rOwners Email: ${$w("#input2").value}
    \rOwners Contact Number: ${$w("#input3").value}
    \rOwners Address and Post Code: ${$w("#addressInput3").streetAddress}
    \rAnimals Name: ${$w("#input4").value}
    \rAnimal Species: ${$w("#dropdown1").value}
    \rAnimal Breed: ${$w("#input5").value}
    \rReferring Vet: ${$w("#input6").value}
    \rReferring Vet Practice: ${$w("#input7").value}
    \rPractice Address and Postcode: ${$w("#addressInput2").value}
    \rVet Email: ${$w("#input8").value}
    \rVet Phone: ${$w("#input9").value}
    \rBrief Description of the Problem: ${$w("#input10").value}
    \rPreference for making this appointment: ${$w("#dropdown3").value}`;
    const recipient = $w("#input8").value;

    sendEmailWithRecipient(subject, body, recipient)
    .then((response)=> {console.log(response);});
        
    sendEmail(subject, body)
    .then((response)=> {console.log(response)});
}

Questions:

  1. Till here your code works?
  2. The issue is that you can not include the following information into your BODY-Object???
\rFile Upload: ${$w("#fileUpload1").attachmentUrls.join(", ")}

  \rImage Upload: ${$w("#imageUpload1").attachmentUrls.join(", ")}`;
  1. This is your UPLOAD-SECTION, you want to upload?

  1. You know, that you have first to upload the file or image, before you can get any real data you can use for your further process?

Your way to success…

  1. Click the SUBMIT-BUTTON after all fields are filled out.

  2. First thing what should happen is the UPLOAD-PROCESS.
    You should await for the end of the UPLOAD-PROCESS, before you can process all your next steps, because you will need the resulting data to include in your mail, right?

  3. Why you do not work with CONSOLE-LOGS ???


I can’t see any LOGs on your site.

Try to work more with console-logs, this will helpt you to understand your own code better. This way you will see more whats happening in all your site’s functions.

So back to your problem:
You are using an onAfter-Save()-HOOK, but why you do not use also an onBeforeSave()-HOOK ???

$w.onReady(function () {
	$w("#dataset1").onBeforeSave(async()=>{
		let awaitingData = await myUploadFunction();
	});
    	$w("#dataset1").onAfterSave(sendFormData);
});


function myUploadFunction(){ 
	console.log("This function will give back results from UPLOAD-PROCESS, first, before the next function starts!");
	here your upload-button function --> with returning resul!!!!!
	
	...
	...
	return data
}

I hope i understood your issue the right way. And i hope i gave you the right direction.

Edit: uploadFiles - Velo API Reference - Wix.com

Thanks for a great reply ! Lot’s to digest and I will and send response and act on your suggestions

I just need to make sure I understand all of them first

Sorry but I am more of a “code hacker” than a pro coder and I am sure it shows !!

I took what you suggested / read the link / borrowed code from it and (with some syntax checking from ChatGPT) I now have code that seems to have no Syntax errors:

import {sendEmail, sendEmailWithRecipient} from 'backend/email';

$w.onReady(function () {
    $w("#dataset1").onBeforeSave(async () => {
        let awaitingData = await myUploadFunction();
    });
    $w("#dataset1").onAfterSave(sendFormData);
});

async function myUploadFunction() {
    console.log("This function will give back results from UPLOAD-PROCESS, first, before the next function starts!");
    $w("#uploadButton1").fileType = "Image"; // Site visitor can choose an image
    $w('#uploadButton1').onClick(async () => {
        if ($w("#uploadButton1").value.length > 0) {  // Site visitor chose a file
            console.log("Uploading the following files:")
            try {
                const uploadedFiles = await $w("#uploadButton1").uploadFiles();
                uploadedFiles.forEach(uploadedFile => {
                    console.log("File url:" + uploadedFile.url);
                })
                console.log("Upload successful.");
            } catch (uploadError) {
                console.log("File upload error: " + uploadError.errorCode);
                console.log(uploadError.errorDescription);
            }
        } else {  // Site visitor clicked button but didn't choose an Image file
            console.log("Please choose an Image file to upload.")
        }
    });
    $w("#uploadButton2").fileType = "Document"; // Site visitor can choose a Document
    $w('#uploadButton2').onClick(async () => {
        if ($w("#uploadButton2").value.length > 0) {  // Site visitor chose a file
            console.log("Uploading the following files:")
            try {
                const uploadedFiles = await $w("#uploadButton2").uploadFiles();
                uploadedFiles.forEach(uploadedFile => {
                    console.log("File url:" + uploadedFile.url);
                })
                console.log("Upload successful.");
            } catch (uploadError) {
                console.log("File upload error: " + uploadError.errorCode);
                console.log(uploadError.errorDescription);
            }
        } else {  // Site visitor clicked button but didn't choose a Document file
            console.log("Please choose a Document file to upload.")
        }
    });
}

function sendFormData() {
  const subject = `New Vet Referral Form from ${$w("#input1").value}`;
  const body = `New Vet Referral from ${$w("#input1").value}
    \rOwners Name: ${$w("#input1").value}
    \rOwners Email: ${$w("#input2").value}
    \rOwners Contact Number: ${$w("#input3").value}
    \rOwners Address and Post Code: ${$w("#addressInput3").streetAddress}
    \rAnimals Name: ${$w("#input4").value}
    \rAnimal Species: ${$w("#dropdown1").value}
    \rAnimal Breed: ${$w("#input5").value}
    \rReferring Vet: ${$w("#input6").value}
    \rReferring Vet Practice: ${$w("#input7").value}
    \rPractice Address and Postcode: ${$w("#addressInput2").value}
    \rVet Email: ${$w("#input8").value}
    \rVet Phone: ${$w("#input9").value}
    \rBrief Description of the Problem: ${$w("#input10").value}
    \rPreference for making this appointment: ${$w("#dropdown3").value}`;
  const recipient = $w("#input8").value;

  sendEmailWithRecipient(subject, body, recipient)
    .then(response => console.log(response));

  sendEmail(subject, body)
    .then(response => console.log(response));
};

I also managed to get an email sent:

New Vet Referral from v
Owners Name: v
Owners Email: my_email
Owners Contact Number: my_mobile
Owners Address and Post Code: undefined
Animals Name: n
Animal Species: Canine
Animal Breed: lion
Referring Vet: c
Referring Vet Practice: g
Practice Address and Postcode: [object Object]
Vet Email:
Vet Phone:
Brief Description of the Problem: hhh
Preference for making this appointment: Contact you first (referring Vet)

I did not managed to ATTACH any images or DOCUMENTS and in Console I see this:

I think the key here is: “onClick is not a function” ?

ChatGPT again to the rescue with the following suggestion:

The error “TypeError: $w(…).onClick is not a function” is triggered because onClick is not a function in the Wix Code API. Instead, you can use the onChange event to detect when a file has been selected for upload.

With the code example provided I now have this Page Code:

import {sendEmail, sendEmailWithRecipient} from 'backend/email';

$w.onReady(function () {
    $w("#dataset1").onBeforeSave(async () => {
        let awaitingData = await myUploadFunction();
    });
    $w("#dataset1").onAfterSave(sendFormData);
});

async function myUploadFunction() {
    console.log("This function will give back results from UPLOAD-PROCESS, first, before the next function starts!");
    $w("#uploadButton1").fileType = "Image"; // Site visitor can choose an image
    $w('#uploadButton1').onChange(async () => {
        if ($w("#uploadButton1").value.length > 0) {  // Site visitor chose a file
            console.log("Uploading the following files:")
            try {
                const uploadedFiles = await $w("#uploadButton1").uploadFiles();
                uploadedFiles.forEach(uploadedFile => {
                    console.log("File url:" + uploadedFile.url);
                })
                console.log("Upload successful.");
            } catch (uploadError) {
                console.log("File upload error: " + uploadError.errorCode);
                console.log(uploadError.errorDescription);
            }
        } else {  // Site visitor clicked button but didn't choose an Image file
            console.log("Please choose an Image file to upload.")
        }
    });
    $w("#uploadButton2").fileType = "Document"; // Site visitor can choose a document
    $w('#uploadButton2').onChange(async () => {
        if ($w("#uploadButton2").value.length > 0) {  // Site visitor chose a file
            console.log("Uploading the following files:")
            try {
                const uploadedFiles = await $w("#uploadButton2").uploadFiles();
                uploadedFiles.forEach(uploadedFile => {
                    console.log("File url:" + uploadedFile.url);
                })
                console.log("Upload successful.");
            } catch (uploadError) {
                console.log("File upload error: " + uploadError.errorCode);
                console.log(uploadError.errorDescription);
            }
        } else {  // Site visitor clicked button but didn't choose a Document file
            console.log("Please choose a Document file to upload.")
        }
    });
}

function sendFormData() {
  const subject = `New Vet Referral Form from ${$w("#input1").value}`;
  const body = `New Vet Referral from ${$w("#input1").value}
    \rOwners Name: ${$w("#input1").value}
    \rOwners Email: ${$w("#input2").value}
    \rOwners Contact Number: ${$w("#input3").value}
    \rOwners Address and Post Code: ${$w("#addressInput3").streetAddress}
    \rAnimals Name: ${$w("#input4").value}
    \rAnimal Species: ${$w("#dropdown1").value}
    \rAnimal Breed: ${$w("#input5").value}
    \rReferring Vet: ${$w("#input6").value}
    \rReferring Vet Practice: ${$w("#input7").value}
    \rPractice Address and Postcode: ${$w("#addressInput2").value}
    \rVet Email: ${$w("#input8").value}
    \rVet Phone: ${$w("#input9").value}
    \rBrief Description of the Problem: ${$w("#input10").value}
    \rPreference for making this appointment: ${$w("#dropdown3").value}`;
  const recipient = $w("#input8").value;

  sendEmailWithRecipient(subject, body, recipient)
    .then(response => console.log(response));

  sendEmail(subject, body)
    .then(response => console.log(response));
};




Console looks like this:

Email comes through but still nothing attached

I also have the 2 errors in the address handlers which I am also going to ask CGPT about