BookStackApp/BookStack

View on GitHub
resources/js/components/ajax-form.js

Summary

Maintainability
A
0 mins
Test Coverage
import {onEnterPress, onSelect} from '../services/dom';
import {Component} from './component';

/**
 * Ajax Form
 * Will handle button clicks or input enter press events and submit
 * the data over ajax. Will always expect a partial HTML view to be returned.
 * Fires an 'ajax-form-success' event when submitted successfully.
 *
 * Will handle a real form if that's what the component is added to
 * otherwise will act as a fake form element.
 */
export class AjaxForm extends Component {

    setup() {
        this.container = this.$el;
        this.responseContainer = this.container;
        this.url = this.$opts.url;
        this.method = this.$opts.method || 'post';
        this.successMessage = this.$opts.successMessage;
        this.submitButtons = this.$manyRefs.submit || [];

        if (this.$opts.responseContainer) {
            this.responseContainer = this.container.closest(this.$opts.responseContainer);
        }

        this.setupListeners();
    }

    setupListeners() {
        if (this.container.tagName === 'FORM') {
            this.container.addEventListener('submit', this.submitRealForm.bind(this));
            return;
        }

        onEnterPress(this.container, event => {
            this.submitFakeForm();
            event.preventDefault();
        });

        this.submitButtons.forEach(button => onSelect(button, this.submitFakeForm.bind(this)));
    }

    submitFakeForm() {
        const fd = new FormData();
        const inputs = this.container.querySelectorAll('[name]');
        for (const input of inputs) {
            fd.append(input.getAttribute('name'), input.value);
        }
        this.submit(fd);
    }

    submitRealForm(event) {
        event.preventDefault();
        const fd = new FormData(this.container);
        this.submit(fd);
    }

    async submit(formData) {
        this.responseContainer.style.opacity = '0.7';
        this.responseContainer.style.pointerEvents = 'none';

        try {
            const resp = await window.$http[this.method.toLowerCase()](this.url, formData);
            this.$emit('success', {formData});
            this.responseContainer.innerHTML = resp.data;
            if (this.successMessage) {
                window.$events.emit('success', this.successMessage);
            }
        } catch (err) {
            this.responseContainer.innerHTML = err.data;
        }

        window.$components.init(this.responseContainer);
        this.responseContainer.style.opacity = null;
        this.responseContainer.style.pointerEvents = null;
    }

}