kazupon / vue-validator

:white_check_mark: Validator component for Vue.js
MIT License
2.19k stars 431 forks source link

Pass DOM element to custom validation function #165

Closed pavgra closed 8 years ago

pavgra commented 8 years ago

Sometimes it is useful to have access to DOM element on which validation is being processed. For example, I use plugin intl-tel-input which has function isValidNumber displaying whether the value is OK or not. But I cannot use this function as there is no access to element which hold the validated value.

var ret = validator.call(_this2._vm, _this2._getValue(_this2._el), descriptor.arg); (line 714 in v2.0.0-alpha.21 dist)

For example I could: function (val, el) { return $(el).intlTelInput('isValidNumber'); }

lensgolda commented 8 years ago

+1 or it would be nice to pass field as argument to validator check function, or somehow to get access to current field.

blocka commented 8 years ago

I'm currently trying to implement a validator with intlTelInput. I'm running into an issue whereas the this.el is not what it should be. I'm setting up intlTelInput using a custom directive. With the default priority (with my directive running first), this.el is the containing div created by the plugin. If I set the priority so that it runs after the validator directive, this.el is the the input itself, but not the same exact object. It is lacking the data() from intlTelInput

pavgra commented 8 years ago

I use intlTelInput in the following way:

HTML:

<input type="text" placeholder="Phone number" v-model="phone" v-validate:phone="['intlTelInput']" v-intl-tel-input="{country: 'ru'}" required>

Directive:

Vue.directive('intl-tel-input', {
    bind: function() {
        var paramsGetter = new Function('return ' + this.expression + ';'),
            params = paramsGetter.apply(this.vm),
            $el = $(this.el);

        $el.intlTelInput({
            nationalMode: false,
            preferredCountries: [params.country.toLowerCase()],
            utilsScript: '/js/utils.js'
        });

        $el.intlTelInput('setCountry', params.country.toLowerCase());
    }
});

Validator:

Vue.validator('intlTelInput', function(val) {
    var $input = $(this.el).find('input');
    if ($input.intlTelInput('getNumber')) {
        this.dirty = true;
    }
    return $input.intlTelInput('isValidNumber');
});
blocka commented 8 years ago

Have you gotten this to work with a required validator? Since this.el is not an input, the required validator fails, as there's no value.

blocka commented 8 years ago

@kazupon is there a reason you pass this.frag.node here instead of this.el?

pavgra commented 8 years ago

No. I've tried, but with no success. However isValidNumber returns false (not valid) when it is called on empty string, that's why I feel satisfied with the solution.

blocka commented 8 years ago

I have this working with some less than ideal code.

My directive has priorty 2002 set, so that it runs after the validator.

My validator looks like this:

validPhone(val) {
    const el = this.vm._directives.find(d=>d.name == 'validate' && d.field == this.field).el;

    el.value = val;

    return $(el).intlTelInput("isValidNumber")
}

I'm essentially locating the original this.el (which is still hanging out on the directive), however it doesn't have the value set, so I do that manually here. Now isValidNumber works, and the required validator is happy to, because it has an element.

blocka commented 8 years ago

By the way, you don't need the element at all for this. You can do it all using the intlTelInputUtils library.

For example intlTelInputUtils.isValidNumber("+15555555555","")

If you pass "" as the country code, combined with a + at the beginning, it will parse the country code for you.