Reputation: 327
I am building a website targeted mostly at browsers using Ionic React
.
I am trying to use a react-hook-form
to upload a list of files (among other data) and save them in a FieldArray together with other data.
I have implemented file upload following this answer in Ionic Forum, using an IonButton and an input.
<input
type="file"
id="file-upload"
style={{ display: "none" }}
onChange={() => { setFile(index);}}/>
<IonButton
onClick={() => {openFileDialog();}}
disabled={files === undefined || files[index] === undefined}>
<IonLabel>Upload file</IonLabel>
<IonIcon slot="start" />
</IonButton>
Code:
function openFileDialog() {
(document as any).getElementById("file-upload").click();
}
const setFile = (index: number) => (_event: any) => {
console.log(`Getting file for index ${index}`);
let f = _event.target.files![0];
var reader = new FileReader();
reader.onload = function () {
setValue(`files.${index}.file`, reader.result);
};
reader.onerror = function () {
console.log("File load failed");
};
reader.readAsDataURL(f);
};
Full example code: codesandbox
The file is correctly uploaded but I have not been able to add it to the correct field in the FieldArray. Files are always added to element 0. I assume this is related to input not been modified directly in the form but in function openFileDialog(). As a result, the function onChange() of input does not receive the correct value of index. Is it or is there a different source of error?
A solution would be to wait for the file to be loaded in the method onClick() of IonButton but I cannot send the index when calling (document as any).getElementById("file-upload").click();
.
Another solution could be to use only one component for file upload instead of two. However, it looks like Ionic does not have a component for this. IonInput type="file" does not work. The documentation is confusing: "file" does not appear in the list of accepted values for property type but it is mentioned in the description of properties multiple and accepted.
How can I save the file correctly?
Upvotes: 2
Views: 8308
Reputation: 33345
I found a few issues with your approach, I also removed the reading of the file into a blob, dont think you should do that until the user actually submits since they could delete the file.
First Issue - you were not passing index into this function
const setFile = (index: number, _event: any) => {
methods.setValue(`files[${index}].file` as any, _event.target.files[0]);
methods.setValue(
`files[${index}].title` as any,
_event.target.files[0]?.name
);
};
The second issue, you were not creating unique identifiers for the upload button, you need to include the index there also
<IonItem>
<input
type="file"
id={`file-upload-${index}`} // <== NEW CODE
style={{ display: "none" }}
onChange={(e) => {
console.log("In input", index);
setFile(index, e);
}}
/>
<IonButton
onClick={() => {
openFileDialog(index); // <== NEW CODE
}}
disabled={files === undefined || files[index] === undefined}
>
<IonLabel>Upload file</IonLabel>
<IonIcon slot="start" />
</IonButton>
</IonItem>
and then you need the index in openFileDialog
so you can click the appropriate button.
function openFileDialog(index: any) {
(document as any).getElementById("file-upload-" + index).click();
}
See the complete solution here in this sandbox
Upvotes: 6