Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

robrogers3's avatar

Help me fix a Vue.js Train Wreck (well entire js trainwreck)

I got myself into a jam by using multiple Vue instances throughout my app.

Here's the deal (it's a little complicated).

  • The app is a CMS, and before submitting data it needs some validation/sanity-checking.
  • BUT, each form is different; it's dynamically rendered based on the schema of the backend table (for starters).
  • So in order to handle this we load a validation script based on the type of object being edited.
  • the scripts are a mix of: jquery, vanilla js, and Vue. (New js is now module based using es6)
  • an example: if an Event (like a concert, say) is being submitted then:
    • if the event is a Weekend event then start and end times must be 48hrs apart.
    • if the event is a Evening dinner event then the duration cannot be more than say 4hrs.
    • or, before registering for an Event it's availability must be checked. (ajax)
  • OH, and yeah, some validaton is not a hard failure some are just warnings. Like a special weekend event could be longer than normal ones. Here we just want to display a warning. And the User can decide to basically submit as is, or fix the potential problem.

Now I'm trying to unify all validation that requires ajax to go through one Vue instance. Where it handles: All ajax requests for validation, displays Errors and Warnings, as well as calling validation that doesn't require a server call.

So what's the problem? Some of the validation is done from Vue.js and is triggered on the current validation handler. Which means, that the Object specific Vue based validator instance and the new Vue ajax validation handler instance have to be mounted on the same DOM element. Which means, they conflict and clobber each other. Well one wins, usually the the Object specific one. But even so it's data is gone (v-model specific data). And of course the Vue ajax validation instance is gone.

So what are my options? So far I've come up with:

  • Using a mixin. It's probably the best. But some validation handlers have no associated Vue instance. They are crusty old jquery mostly. Which means there's nothing to mixin to.
  • Using a plugin but it has the same problems and it's probably not as good as the mixin.
  • Making the validator with vanilla js. But I lose so many of the benefits I get from Vue.
  • I can't see a Component working.

So, in short, what do I do? I'm in a pickle.

0 likes
4 replies
burlresearch's avatar

It's hard to know even where to begin here - obviously this is a complex tool-chain. My first reaction to this is that it must be Vuex territory more than just Vue - as you say in your last point, "I can't see a Component working" ... I agree, I don't think this a Vue component problem.

I don't understand Vuex well enough to suggest a solution, other than your issue seems to fall in that space.

Plus, it seems like you have a lot of logic confused into frontend validation. This is a flag for me too, much of the logic for questions like "is the Event over a [long] weekend", "is it after hours", ... seem more appropriate for a backend Repository or Service or something, not so much for JavaScript validation.

Just some second-hand opinions...

robrogers3's avatar

@burlresearch thanks for the reply. We want to hit the repo, and the end points for ajax work just fine.

it's this damn legacy js that's killing me. I don't know how Vuex can help here -- outside of data management. It's the functionality I need. i.e. methods.

robrogers3's avatar
robrogers3
OP
Best Answer
Level 37

Well I came up with an answer just plain js, that can work with the ugly Vue instances.


function Validator() {
    "use strict";
    this.errors = [];
    this.objectType = getQueryVariable('item');
    this.id = getQueryVariable('item_detail');
    this.form = null;
    this.currentHandler = null;
}

Validator.prototype.validate = function () {
    this.currentHandler = this.handleErrors;

    let formData = this.collectFormData(true);

    formData.append('validate', true);

    axios.post('/admin/validate.php', formData)
        .then((response) => {
            console.log(response.data);
            this.currentHandler = this.displayWarnings;
            this.verify();
        }).catch(error => {
        this.handleCatch(error)
    })
};
Validator.prototype.collectFormData = function ($validate) {

    let formData = new FormData(this.form);

    formData.append('object_type', this.objectType);

    formData.append('id', this.id);

    if ($validate) {
        formData.append('validate', true);
    }

    return formData;
};

Validator.prototype.submitForm = function () {
    this.form.submit();
};

Validator.prototype.handleWarnings = function () {
    this.displayWarnings(this.errors)
        .then(() => {
            this.errors = [];
        })
        .catch((data) => {
            if (data === 'cancel') {
                this.submitForm();
            } else {
                swal('very weird data really can only be cancel');
            }
        })
};

Validator.prototype.handleErrors = function () {
    this.displayErrors(this.errors);
    this.errors = [];
};
Validator.prototype.verify = function () {
    let formData = this.collectFormData(false);

    this.currentHandler = this.handleWarnings;

    axios.post('/admin/validate.php', formData)
        .then(() => {
            this.submitForm();
        }).catch(error => {
        this.handleCatch(error);
    });
};


Validator.prototype.displayWarnings = function (messages) {
    return swal({
        title: "You've got potential (problems).",
        text: messages,
        type: "warning",
        width: 800,
        showCancelButton: true,
        allowEscapeKey: false,
        allowOutsideClick: false,
        cancelButtonText: "I've got my reasons.",
        confirmButtonText: "Let's fix it."
    });
};

Validator.prototype.displayErrors = function (messages) {
    swal({
        title: 'Ouch!',
        html: messages,
        width: 800,
        type: 'error'
    })
        .catch(() => {
            console.log('no oop')
        })
};

Validator.prototype.handleCatch = function (error) {
    if (error.response) {
        this.errors = error.response.data;

        if (error.response.status === 422 || error.response.status === 422) {
            this.errors = error.response.data.join('<br>');
        }

        this.currentHandler();
    } else if (error.request) {
        // The request was made but no response was received
        // `error.request` is an instance of XMLHttpRequest in the browser and an instance of
        //swal("Wow we have problems", error.response.data, 'error');

        console.log(error.request);
    }
};

Validator.prototype.handleSubmission = function (e) {

    this.form = document.querySelector('form.contentdetail');
    this.validate();

    validate_submit(e, () => {    
        this.validate();
    });
};


then I call it on submitting the form. via addEventListener


    $('[data-submit-call-back]').on('submit', function (e) {
        e.preventDefault();

            let validation = new Validator();
            validation.handleSubmission(e);
        }
    });

1 like
burlresearch's avatar

Thanks for posting the code - following this up.

I really wouldn't have predicted this is the answer you wanted - but it's nice code. I read it through - good stuff.

Please or to participate in this conversation.