0

from what I have seen all the action happens when I press submit of the form that contains:

<ImageInput multiple source="pictures" accept="image/*">
  <ImageField source="src" title="title" />
</ImageInput>

then some code like that is being run:

const addUploadCapabilities = requestHandler => (type, resource, params) => {
    if ( (type === 'CREATE' || type === "UPDATE")) {
        if (params.data.pictures && params.data.pictures.length) {
            const pictures = params.data.pictures.filter(p => p.rawFile instanceof File)

            const url = config.uploadImageEndpoint;
            //the params contain the image as a fileInstance
            params.data.pictures = [];
            for (let picture of pictures) {
                let form = new FormData();
                form.append('file', picture.rawFile);
                let options = APIUtils.createOptionsForImageUpload(form);
                return fetchUtils.fetchJson(url, options)
                .then(res => {
                    params.data.pictures.push({id: res.json.content[0].id});
                    return requestHandler(type, resource, params)
                });
            }
        }
    }

    return requestHandler(type, resource, params);
};

and then the restClient code is run to save the resource of the form on either create or edit..

I am curious how to get the ids after those files are saved in the database.. and then submit the form and connect those ids to the specific ImageInputs..

Edit:

With the above code I get only an array with the first id of my pictures

Thanks!

Michail Michailidis
  • 11,792
  • 6
  • 63
  • 106

3 Answers3

2

Based on kunal pareek's answer this is the full code that collects all the ids and then you can bind them to the parent resource:

const addUploadCapabilities = requestHandler => (type, resource, params) => {
    if ( (type === 'CREATE' || type === "UPDATE")) {
        if (params.data.pictures && params.data.pictures.length) {
            const pictures = params.data.pictures.filter(p => p.rawFile instanceof File)

            const url = "some url";
            //the params contain the image as a fileInstance
            params.data.pictures = [];
            let promises = [];
            for (let picture of pictures) {
                let form = new FormData();
                form.append('file', picture.rawFile);
                let options = // create options without content type and body as form
                let promise = fetchUtils.fetchJson(url, options);
                promises.push(promise);
            }
            return Promise.all(promises).then(results => {
                for (let res of results) {
                    params.data.pictures.push(res.json.content[0].id);
                }
                return requestHandler(type, resource, params);
            });
        }
    }

    return requestHandler(type, resource, params);
};

this is the version I used for multiple dropzones inputs pictures1, pictures2 that doesn't remove existing images during update and passes those ids again to be bound to the resource..

const detectPicturesAndUpload = function(properties, params, requestHandler, type, resource) {
    let promises = [];
    for (let prop of properties) {
        if (params.data[prop] && params.data[prop].length) {
            const pictures = params.data[prop].filter(p => p.rawFile instanceof File);
            const alreadUploadedPics = params.data[prop].filter(p => !p.rawFile);
            const url = config.uploadImageEndpoint;
            //the params contain the image as a fileInstance
            params.data[prop] = [];

            for (let picture of pictures) {
                let form = new FormData();
                form.append('file', picture.rawFile);
                form.append('property', prop); // the endpoint needs to return the prop name
                let options = APIUtils.createOptionsForImageUpload(form);
                let promise = fetchUtils.fetchJson(url, options);
                promises.push(promise);
            }
            for (let uploadedPic of alreadUploadedPics) {
                if (!Array.isArray(params.data[prop])) {
                    params.data[prop] = [];
                }
                params.data[prop].push(parseInt(uploadedPic.id));
            }
        }
    }
    return Promise.all(promises).then(results => {
        for (let res of results) {
            let propName = res.json.content[0].property; // so it can be used here
            if (!Array.isArray(params.data[propName])) {
                params.data[propName] = [];
            }
            params.data[propName].push(parseInt(res.json.content[0].id));
        }
        return requestHandler(type, resource, params);
    });
}


const addUploadCapabilities = requestHandler => (type, resource, params) => {
    if ( (type === 'CREATE' || type === "UPDATE")) {
        return detectPicturesAndUpload(["pictures1", "pictures2"], params, requestHandler, type, resource);
    }

    return requestHandler(type, resource, params);
};

export default addUploadCapabilities;
Michail Michailidis
  • 11,792
  • 6
  • 63
  • 106
1

No clear canonical mechanism for that exists right now. AOR expects us to handle image upload at the API end with 1 call only. Above would have been a useful feature to have I must say.

A possible way to achieve this would be to let the addUploadCapabilities method above also make a fetch call and call requestHandler asynchronously. after response from the server. Then we can modify the request parameters and continue as previously.

const addUploadCapabilities = requestHandler => (type, resource, params) => {
    if ( (type === 'CREATE' || type === "UPDATE")) {
        if (params.data.pictures && params.data.pictures.length) {
            debugger;

const pictures = params.data.pictures.filter(p => p.rawFile instanceof File)
          let form = new FormData()
          const url = imageUploadURL
          const options = {}
          //the params contain the image as a fileInstance
          form.append('image', params.data.pictures[0].rawFile);         
          options.method = 'POST'
          options.body = form
          return fetchUtils.fetchJson(url, options)
          .then(res => {
               params.data.pictureId = res.json.id
               return requestHandler(type, resource, params)
              })

            );
        }
    }

    return requestHandler(type, resource, params);
};
Michail Michailidis
  • 11,792
  • 6
  • 63
  • 106
kunal pareek
  • 1,285
  • 10
  • 21
  • please check my edit - with your approach I was able to get only the first id in the switch case CREATE of my resource.. how can I get the array properly populated before it is being sent.. it seems that it is calling requestHandler after the first id.. but i couldn't make it work by putting it outside of the for loop – Michail Michailidis Sep 28 '17 at 07:49
  • 1
    I am sorry my example above was oversimplified. I was basically sketching an approach. I saw your massive SO rep and thought you would definitely figure it out the details. – kunal pareek Sep 28 '17 at 09:45
  • no worries :) it gave me the initial process - for a more detailed approach I have given a working example in a separate answer - I will accept your though :) – Michail Michailidis Sep 28 '17 at 09:54
1

Another approach would be using a custom input handling the upload. See admin on rest send extra params to rest call

Gildas Garcia
  • 6,966
  • 3
  • 15
  • 29