In my app, I wish to do just like in the admin panel. I have a form that have other datas + files and I want to create everything at once. How can I do that if I don't know the RefID since the new entry is not yet created. I have tried alot of things and so far nothing works. Here is code example :
Form :
jobsForm = this.fb.group({
nom: [null],
date: [null],
address_chargement: [null],
address_dechargement: [null],
client: [null],
pleted: [false],
status: ['waiting'],
ment_user: [null],
ment_admin: [null],
files: [null],
});
Form is dispatched to NGRX Store create action which call my service as follow :
public createJob = (payload: IJob): Observable<IJob> => {
const data = new FormData();
Object.entries(payload).forEach(([key, value]) => {
data.append(key, value);
});
return this.http.post<IJob>(`${this.env.backendUrl}/jobs`, data);
}
I get errors such as :
[2019-06-16T20:10:03.573Z] error Error: SQLITE_ERROR: table jobs has no column named fields
Or it just does not upload.
In my app, I wish to do just like in the admin panel. I have a form that have other datas + files and I want to create everything at once. How can I do that if I don't know the RefID since the new entry is not yet created. I have tried alot of things and so far nothing works. Here is code example :
Form :
jobsForm = this.fb.group({
nom: [null],
date: [null],
address_chargement: [null],
address_dechargement: [null],
client: [null],
pleted: [false],
status: ['waiting'],
ment_user: [null],
ment_admin: [null],
files: [null],
});
Form is dispatched to NGRX Store create action which call my service as follow :
public createJob = (payload: IJob): Observable<IJob> => {
const data = new FormData();
Object.entries(payload).forEach(([key, value]) => {
data.append(key, value);
});
return this.http.post<IJob>(`${this.env.backendUrl}/jobs`, data);
}
I get errors such as :
[2019-06-16T20:10:03.573Z] error Error: SQLITE_ERROR: table jobs has no column named fields
Or it just does not upload.
Share Improve this question asked Jun 16, 2019 at 20:39 Jean-Philippe DufourJean-Philippe Dufour 1514 silver badges15 bronze badges3 Answers
Reset to default 4This is possible to do that.
By default it's not, you will have to do some updates in your controllers / services.
The content manager have this ability to manage entry creation and file upload in the same request. So let's copy this logic in your create action.
Note: you will have to send the request with a FormData
- was not this type of request before.
Here is the controller function that create entry https://github./strapi/strapi/blob/master/packages/strapi-plugin-content-manager/controllers/ContentManager.js#L61
Here is the service function that create and upload the files https://github./strapi/strapi/blob/master/packages/strapi-plugin-content-manager/services/ContentManager.js#L63
First you will have to create a new service function named addAndUpload
.
async addAndUpload(ctx) {
// Get form data
let values = ctx.request.body;
// Silent recursive parser.
const parser = value => {
try {
value = JSON.parse(value);
} catch (e) {
// Silent.
}
return _.isArray(value) ? value.map(obj => parser(obj)) : value;
};
// Get files
const files = values.files;
// Get entry data
values = Object.keys(values.fields).reduce((acc, current) => {
acc[current] = parser(values.fields[current]);
return acc;
}, {});
// Create the entry without files
const entry = await strapi.api.job.services.add(values)
// Then, request plugin upload.
if (Object.keys(files).length > 0) {
// Upload new files and attach them to this entity.
// Here `job` have to be the model name
await strapi.plugins.upload.services.upload.uploadToEntity(
{
id: entry.id || entry._id,
model: 'job',
},
files
);
}
return strapi.api.job.services.job.findOne({
id: entry.id || entry._id,
});
}
And in your create
controller function your will have to call strapi.api.job.services.addAndUpload(ctx)
instead of the add
function.
I think itsn't possible make like that. I suggest break create Job
proccess to two steps:
- Create
File
->POST /upload
. - Create
Job
withid
fromFileResponse
->POST /job
.
SQLITE_ERROR inform you that Job
model doesn't have fields
, becouse Job
models accepts refId
to File
.
You can use switchMap
to merge two Observable.
File Upload
You can also add files during your entry creation.
For example, given the Restaurant
model attributes:
path: ./src/api/restaurant/content-types/restaurant/schema.json
{
// ...
"attributes": {
"name": {
"type": "string"
},
"cover": {
"type": "media",
"multiple": false,
}
}
// ...
}
<form>
<!-- Can be multiple files if you setup "collection" instead of "model" -->
<input type="text" name="name" />
<input type="file" name="cover" />
<input type="submit" value="Submit" />
</form>
<script type="text/javascript">
const form = document.querySelector('form');
form.addEventListener('submit', async (e) => {
e.preventDefault();
const data = {};
const formData = new FormData();
form.elements
.forEach(({ name, type, value, files, ...element }) => {
if (!['submit', 'file'].includes(type)) {
data[name] = value;
} else if (type === 'file') {
files.forEach((file) => {
formData.append(`files.${name}`, file, file.name);
});
}
});
formData.append('data', JSON.stringify(data));
await fetch('/api/restaurants', {
method: 'post',
body: formData
});
});
</script>
Your entry data
has to be contained in a data key and you need to JSON.stringify
this object. The keys for files need to be prefixed with files
(e.g. for a cover attribute: files.cover
).
NOTE: You have to send FormData in your request body.
Reference - strapi-v4 doc