Support

Account

Home Forums General Issues Does ACF have a JS API? Reply To: Does ACF have a JS API?

  • Thank you very much. I’m doing a similar thing at the moment:
    – I use ACF hooks in order to run callback when an image field value is changed
    – Then I read the image field data,
    – Then I read the EXIF data from the image field,
    – Then I update other fields values based on the EXIF data.

    So, as long as it’s just a matter of updating existing fields, it’s not a big problem. But if I wanted to create some taxonomy fields on the fly, then it would be tricky, because there’s no way to re-render a field and/or a field group.

    I will probably end up with writing my own ACF custom field that will handle taxonomies update. This will be a simpler and a cleaner solution because I want to have as little DOM traversing in my code as possible. I don’t want my code to break if ACF HTML structure will change.

    Thank you for your help anyway! I hope that in future ACF will be more flexible in this area. I would love to see a real, non-DOM-dependant JS API. I would love to be able to render ACF fields anywhere, with a custom HTML templates, etc. But I know how much work it requires. At the moment this part of ACF code is very monolithic and DOM-oriented. I dream of a MVC-like architecture where models and collections are separated from the view layer (like Backbone Models/Collection/Views).

    If it was implemented then ACF could become something more than just a powerful custom fields manager. If ACF has such an API and architecture then It would be even possible to build something like a page builder (like visual composer) on top of ACF!

    Sorry for the offtopic 🙂

    This is a piece of code I wrote:

    
    import $ from 'jquery';
    
    export default () => {
    
        const $$ = {
            template: $('#js-exif-data')
        };
    
        const nonce = $$.template.data('nonce');
    
        // ACF fields identifiers
        const imageInputName = 'acf[field_56d99d7de115e]';
        const orientationInputName = 'acf[field_56d8b43f73de1]';
    
        const allowedExifData = {
            created_timestamp: 'Data',
            iso: 'ISO',
            focal_length: 'Ogniskowa',
            shutter_speed: 'Migawka',
            aperture: 'Przysłona',
            keywords: 'Tagi'
        };
    
        const utils = {
            /**
             * @param {int} exifValue
             * @returns {string} - <code>landscape</code> or <code>portrait</code>
             */
            getOrientationByExifValue(exifValue) {
                const orientations = [{
                    name: 'landscape',
                    values: [1, 2, 3, 4]
                }, {
                    name: 'portrait',
                    values: [5, 6, 7, 8]
                }];
    
                const orientation = orientations
                    .filter(orientation => {
                        return orientation.values.indexOf(exifValue) !== -1;
                    });
    
                return orientation.length === 0 ? 'unknown' : orientation[0].name;
            },
            /**
             * @param {int} attachmentId
             * @returns {Promise}
             */
            loadExifData(attachmentId) {
                return $.ajax({
                    type: 'post',
                    url: acf.o.ajaxurl,
                    data: {
                        nonce,
                        attachmentId,
                        action: 'get_image_meta',
                    }
                });
            },
            /**
             * @returns {Promise}
             */
            getOrientationTerms() {
                return $.ajax({
                    type: 'post',
                    url: acf.o.ajaxurl,
                    data: {
                        nonce,
                        action: 'get_orientation_terms',
                    }
                });
            },
            /**
             * @param {object} response
             */
            renderExifData(response) {
                $.each(response.data, (k, v) => {
    
                    if (!allowedExifData.hasOwnProperty(k)) {
                        return true;
                    }
    
                    // Convert array values to coma-separated string.
                    const value = (function () {
                        if (v.constructor === Array) {
                            return v.join(', ');
                        }
                        return v;
                    }());
    
                    const $p = $('<li />').html(<code><strong>${allowedExifData[k]}</strong>: ${value}</code>);
                    $$.template.append($p);
    
                });
            },
            /**
             * @param {object} response
             */
            setOrientation(response) {
    
                if (!response.data.hasOwnProperty('orientation')) {
                    return;
                }
    
                utils.getOrientationTerms().done(termsResponse => {
    
                    const wpTerms = termsResponse.data;
    
                    if (wpTerms.constructor !== Array) {
                        return;
                    }
    
                    const exifOrientation = parseInt(response.data.orientation, 10);
                    const orientationHumanReadable = utils.getOrientationByExifValue(exifOrientation);
                    const wpTerm = wpTerms.filter(term => {
                        return term.name === orientationHumanReadable;
                    });
    
                    if (wpTerm.length === 0) {
                        return;
                    }
    
                    const orientationTermId = parseInt(wpTerm[0].id, 10);
                    const $radios = $(<code>input[name=&quot;${orientationInputName}&quot;]</code>);
    
                    const $radio = $radios.filter((index, el) => {
                        return parseInt($(el).val(), 10) === orientationTermId;
                    });
    
                    $radio
                        .prop('checked', true)
                        .trigger('click')
                });
            }
        };
    
        const handlers = {
            imageOnLoad() {
                const $input = $(<code>input[name=&quot;${imageInputName}&quot;]</code>);
                const attachmentId = $input.val();
    
                if ($input.length === 0 || attachmentId === '') {
                    return;
                }
    
                $$.template.empty();
                utils.loadExifData(attachmentId).done(utils.renderExifData);
            },
            /**
             * @param {object} $input - an array of jQuery DOM objects
             */
            imageInputOnChange($input) {
                const attachmentId = $input.val();
    
                if ($input.context.name !== imageInputName) {
                    return;
                }
    
                $$.template.empty();
    
                if (attachmentId === '') {
                    return;
                }
    
                utils.loadExifData(attachmentId).done(response => {
                    utils.renderExifData(response);
                    utils.setOrientation(response);
                })
    
            }
        };
    
        acf.add_action('change', handlers.imageInputOnChange);
        acf.add_action('load', handlers.imageOnLoad);
    }