Skip to content

Commit

Permalink
fix: v-slot required flag always true #2843 (#3318)
Browse files Browse the repository at this point in the history
Co-authored-by: Andrew Mandruk <andrew.mandruk@fenetre.nl>
Co-authored-by: Abdelrahman Awad <logaretm1@gmail.com>
Co-authored-by: Manuel Guitarte <35596079+MHGuitarte@users.noreply.github.com>
  • Loading branch information
4 people authored Jun 5, 2021
1 parent 377e15a commit bf811bb
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 5 deletions.
17 changes: 17 additions & 0 deletions src/components/Provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,12 @@ export const ValidationProvider = (Vue as withProviderPrivates).extend({
invalid: !result.valid
});

if (result.required !== undefined) {
this.setFlags({
required: result.required
});
}

return result;
},
setErrors(errors: string[]) {
Expand All @@ -325,6 +331,12 @@ export const ValidationProvider = (Vue as withProviderPrivates).extend({
},
registerField() {
updateRenderingContextRefs(this);
},
checkComputesRequiredState(): boolean {
const rules = { ...this._resolvedRules, ...this.normalizedRules };

const isRequired = Object.keys(rules).some(RuleContainer.isRequireRule);
return isRequired;
}
}
});
Expand Down Expand Up @@ -442,10 +454,15 @@ function watchCrossFieldDep(ctx: ProviderInstance, depName: string, withHooks =

if (!isCallable(ctx._veeWatchers[depName]) && providers[depName]) {
ctx._veeWatchers[depName] = providers[depName].$watch('value', () => {
const isComputesRequired = ctx.checkComputesRequiredState();
if (ctx.flags.validated) {
ctx._needsValidation = true;
ctx.validate();
}
// Validate dependent field silently if it has rules with computesRequired
if (isComputesRequired && !ctx.flags.validated) {
ctx.validateSilent();
}
});
}
}
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export interface ValidationResult {
errors: string[];
failedRules: Record<string, string>;
regenerateMap?: Record<string, () => string>;
required?: boolean;
}

export type VueValidationContext = Vue & {
Expand Down
17 changes: 12 additions & 5 deletions src/validate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export async function validate(

return {
valid: result.valid,
required: result.required,
errors,
failedRules,
regenerateMap
Expand All @@ -77,10 +78,11 @@ export async function validate(
* Starts the validation process.
*/
async function _validate(field: FieldContext, value: any, { isInitial = false } = {}) {
const { shouldSkip, errors } = await _shouldSkip(field, value);
const { shouldSkip, required, errors } = await _shouldSkip(field, value);
if (shouldSkip) {
return {
valid: !errors.length,
required,
errors
};
}
Expand All @@ -104,6 +106,7 @@ async function _validate(field: FieldContext, value: any, { isInitial = false }
if (field.bails) {
return {
valid: false,
required,
errors
};
}
Expand All @@ -112,6 +115,7 @@ async function _validate(field: FieldContext, value: any, { isInitial = false }

return {
valid: !errors.length,
required,
errors
};
}
Expand All @@ -122,21 +126,20 @@ async function _shouldSkip(field: FieldContext, value: any) {
const errors: ReturnType<typeof _generateFieldError>[] = [];
const isEmpty = isNullOrUndefined(value) || value === '' || isEmptyArray(value);
const isEmptyAndOptional = isEmpty && field.skipIfEmpty;
let isRequired = false;
let isRequired;

for (let i = 0; i < length; i++) {
const rule = requireRules[i];
const result = await _test(field, value, {
name: rule,
params: field.rules[rule]
});

if (!isObject(result)) {
throw new Error('Require rules has to return an object (see docs)');
}

if (result.required) {
isRequired = true;
if (result.required !== undefined) {
isRequired = result.required;
}

if (!result.valid && result.error) {
Expand All @@ -145,6 +148,7 @@ async function _shouldSkip(field: FieldContext, value: any) {
if (field.bails) {
return {
shouldSkip: true,
required: result.required,
errors
};
}
Expand All @@ -154,6 +158,7 @@ async function _shouldSkip(field: FieldContext, value: any) {
if (isEmpty && !isRequired && !field.skipIfEmpty) {
return {
shouldSkip: false,
required: isRequired,
errors
};
}
Expand All @@ -162,13 +167,15 @@ async function _shouldSkip(field: FieldContext, value: any) {
if (!field.bails && !isEmptyAndOptional) {
return {
shouldSkip: false,
required: isRequired,
errors
};
}

// skip if the field is not required and has an empty value.
return {
shouldSkip: !isRequired && isEmpty,
required: isRequired,
errors
};
}
Expand Down
30 changes: 30 additions & 0 deletions tests/targets.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,4 +163,34 @@ describe('cross-field syntax', () => {
});
expect(result.valid).toBe(true);
});

test('should change required flag state', async () => {
extend('requiredIf', {
params: ['target'],
validate(val, { target }) {
console.log(target, val, 'target, val');
return {
valid: target === val,
required: !!target
};
},
computesRequired: true
});

let result = await validate('text', 'requiredIf:@field', {
values: {
field: ''
}
});

expect(result.required).toBe(false);

result = await validate('text', 'requiredIf:@field', {
values: {
field: 'text'
}
});

expect(result.required).toBe(true);
});
});

0 comments on commit bf811bb

Please sign in to comment.