3

I have a component in Vue which I use a substitute for submit buttons. I can pass a handler to it, which the component calls after disabling itself and settings it status as loaded, and it can recover (enable itself again) after errors and display a nifty success animation if all goes well. This works well, with the code below:

// Submit.vue
<template>
    <button :type="type" :class="classes" :disabled="loading" @click="onClick">
        <span class="flag" v-if="flag"></span>
        <slot></slot>
    </button>
</template>

<script>
    import _ from 'lodash'
    export default {
        name: 'submit',
        props: {
            brand: {
                type: String,
                default: 'primary'
            },
            // If no handler is provided, will fallback to normal submit button
            handler: {
                type: Function,
                required: false
            },
            flag: {
                type: Boolean,
                default: false
            }
        },
        data () {
            return {
                loading: false,
                success: false
            }
        },
        computed: {
            type () {
                return typeof this.handler !== 'undefined' ? 'button' : 'submit'
            },
            classes () {
                return [
                    `btn btn-${this.brand}`,
                    this.loading && !this.success ? 'loading' : null,
                    this.success ? 'success' : null
                ]
            }
        },
        methods: {
            onClick (event) {
                if (this.success) {
                    event.preventDefault()
                    return
                }
                this.loading = true
                if (typeof this.handler !== 'undefined') {
                    // Handler must return a Promise
                    this.handler.call()
                        .then(_.bind(() => {
                            this.onSuccess()
                        }, this))
                        .catch(() => {})
                        .then(_.bind(() => {
                            this.loading = false
                        }, this))
                }
            },
            resetSuccess () {
                this.success = false
            },
            onSuccess () {
                this.success = true
                setTimeout(this.resetSuccess, 2000)
            }
        }
    }
</script>

It falls back to a normal submit button if no handler is passed, assuming all you want is to auto-disable the button when the form is submitted. The only problem is the form is not submitted when I click the button created from the component.

I think it would be fairly easy to force the submission via JS with the onClick method, but I wanted to understand why it doesn't. Is it a browser issue? A security issue? A Vue issue? Or something else I'm missing that might be right in front of me?

Here's a JSFiddle for quick testing: https://jsfiddle.net/03fgwgy5/ The code is not exactly the same, as I'm using single-file components, but the gist is the same and the behaviour can be observed easily.

2 Answers 2

2

You are setting loading to false before you check for the existence of the handler. Since you bind the button's disabled attribute to loading, you are disabling the button and preventing the native click event from firing.

Just set the loading property after you've checked for the handler:

if (typeof this.handler !== 'undefined') {
  this.loading = true;
  ...
}

Here's a fiddle.

Sign up to request clarification or add additional context in comments.

2 Comments

One of these times it'll be right! Man I had one like that earlier this morning.
Ah, good catch. I had a feeling it was something obvious. The only problem is that I do want the button to be disabled and have the loading class regardless of having a handler or not. I'll end up using JS to force submission, but you solved the riddle :)
1

For the record, here's what I ended up doing:

onClick (event) {
    if (this.success) {
        event.preventDefault()
        return
    }
    this.loading = true
    if (typeof this.handler !== 'undefined') {
        // Handler must return a Promise
        this.handler.call()
            .then(_.bind(() => {
                this.onSuccess()
            }, this))
            .catch(() => {})
            .then(_.bind(() => {
                this.loading = false
            }, this))
    } else {
        this.$el.form.submit()
    }
}

This allows me disable the form on click and show the loading status, but still force submission.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.