Options
All
  • Public
  • Public/Protected
  • All
Menu

Class ModelValidator<MODEL_T>

Each FormModel has corresponding ModelValidator. Model Validator is responsible mostly for

  • validating a model
  • accessing and setting model's errors
example

Object flow basic usage:

// this will not only initialize a FormModel with the initial data, but as well will instantiate instance of this
// class.
const foo = makeFormModel({email: '', name: 'bar'},
{
email: (value) => validatePattern(value, regex)
name: (value) => validateIsRequired(value)
}
)

foo.validator // instance of ModelValidator<FormModel<{email: '', name: 'bar'}>>

// above will create instance methods email and name, so whenever you call them and on their return values, the errors
// will be set or removed on the model
foo.email = 'email@foo.com'
// validation succceeds nothing happens.
foo.validator.email() // {valid: true}
foo.email = 'asd'
// validation fails, so if your validation method returns {valid: false, ...}, instance takes care of handling what
// to do with errors
foo.validator.email() // {valid: false, ...}
foo.errors // {email: ["yourMessage"]}
foo.email = 'email@foo.com'
foo.validator.email() // {valid: true}
foo.errors // {} | undefined,

// in makeFormModel flow, the methods that you provide for validations, will be used for
// default validations. aka whenever you call <a href="ModelValidator.html#validateDefault">validateDefault</a>.
// this as well will be called by <a href="FormState.html">FormState</a> in it's validate method.
foo.validator.validateDefault() // will call both validator.email() and validator.name()
example

custom "recommended" flow

// in case you need more control, you can easily achieve same by introducing you own ModelValidators
// here we recreate example from above
class UserModelValidator extends ModelValidtor<UserModel> {

// in order the validation methods with <a href="../interfaces/ValidationReturn.html">ValidationReturn</a> get handled they must be wrapped in {@link validatesProperty}
email = () => this.validateProperty('email', (value) => validateIsRequired)
name = () => this.validateProperty('name', (value) => validateIsRequired)

// you can call methods and they're result than will be handled by validator.
// in order so these methods as well auto called on validator.validateDefault()
// you need to refernce them in default validations.
// now it's fully connected to any form state, and will be called automatically
defaultValidations = {
email: this.email,
name: this.name
}
}
// if you need a controll, or e.g. have some logic which validations need to be run,
// you don't put them into default validations but call them conditionally in you form states.
SomeFormState {
validatePaymentStep = () => {
this.model.validator.paymentMethod()
this.model.validator.tosAcceptance()
}
example

very customized usage: nothing stands in your way, you can do whatever you want:

if (random === foo) {
model.validator.addError('name', 'bad')
else model.validator.removeSpecificError('name', 'bad')
}
you can do custom validation methods and whatever.

Type parameters

Hierarchy

  • ModelValidator

Index

Constructors

  • new ModelValidator<MODEL_T>(model: MODEL_T, argValidations?: any): ModelValidator<MODEL_T>

Properties

argValidations: any

this exists for cloning. Because the default usage is "object" based, this needs to be saved during initialization as the defaultValidationValidations do not exist on prototype in order so it could be fed to a clone.

defaultValidations: {} = {}

validateDefault takes values of an object under this property and calls all of them. When you use "object flow", the validation methods in constructor will be enriched with validateProperty, and set on instance, as well their references will be set on this property

example
//effectively whenever you use <a href="../modules.html#makeFormModel">makeFormModel</a>
const makeFormModel({email: '', foo: 'bar'},
{
email: (value) => validateIsRequired(value)
foo: (value) => validateIsRequired(value)
}
)
// default model validator constructor be called with following:
const foo = new ModelValidator(model, {email: (value) => validateIsRequired(value), foo: (value) => validateIsRequired(value)})

// the validation methods will be wrapped with <a href="ModelValidator.html#validateProperty">validateProperty</a>
// so (value) => validateIsRequired(value) will be wrapped like:
this.validateProperty('email', (value) => validateIsRequired(value))
// than under same name it will be set on instance:
email: () => this.validateProperty('email', (value) => validateIsRequired(value))
// in order to now which shall be called on <a href="ModelValidator.html#validateDefault">validateDefault</a>
it will also land here:
defaultValidations = {
email: this.email
}
remarks

if you use recommended custom validator flow, just put here the references that should be validated by default. So you basically do manually what's done auto in "object flow" but with full control and typing etc.:

example
class MyValidator extends ModelValidator {
name = () => this.validateProperty('name', (value) => validateIsRequired(value)
defaultValidations = {
name: this.name
}
}
example

if you use object flow, and want specify what runs on validateDefault (e.g. if you want to manually run some validator, but still have default validations) you can do it like:

const makeFormModel({email: '', foo: 'bar'},
{
email: (value) => validateIsRequired(value)
foo: (value) => validateIsRequired(value),
validateDefault: ['email']
}
)
// both email and foo will present on instance, but only email will be called on 'validateDefault()'.
// foo can be still validated, but you need to do it conditionally.

Type declaration

    model: MODEL_T

    each model instance has own instance of validator. model is passed in constructor, so validator can operate on it.

    valuesCache: Record<string, any> = {}

    is used for caching values, so if optimization is neccessary you can e.g. check, if new value is not different than old one, you can skip expensive validation logic. the last value will be added on cache in @validates decorator. if you use @validates decorator it's happening automatically if not you can do it manually

    remark

    cache is ignored by default.

    example
    
    

    Methods

    • addError(property: keyof MODEL_T, errorMessage: string): void
    • sets an error on model. if model already has same message it is ignored. errors are set on model.modelData internal object: so all error accessing methods are basically proxies that read from that internal object

      example
      model.errors // undefined
      model.modelData.errors // undefined
      model.validator.addError('email', 'bad')
      model.errors // {email: ['bad']}
      model.modelData.errors // {email: ['bad']}
      model.validator.addError('email', 'uncool')
      model.errors // {email: ['bad', 'uncool']}
      model.modelData.errors // {email: ['bad', 'uncool']}
      model.validator.addError('email', 'uncool') // will not be set two times
      model.errors // {email: ['bad', 'uncool']}
      model.modelData.errors // {email: ['bad', 'uncool']}

      Parameters

      • property: keyof MODEL_T

        property for which an error shall be set on model

      • errorMessage: string

        error message for property

      Returns void

    • addRuntimeDefaultValidation(property: string, validateFunc: ValidateFunction): void
    • assignValidations(argValidations: any): void
    • relevant to makeFormModel flow

      example

      whenever you pass in constructor of ModelValidator following:

      // e.g. if you create call
      const formState = makeFormStateWithModel({
      validations: {
      name: () => return validate...
      email: () => return ...
      }
      })

      // or same arg to makeFormModel
      // methods specified in 'validations' arg will be passed to initialize instance of ModelValidator as arg.
      // those validations, will be set on instance.
      // if you do not provide a 'validateDefault' list, all validations will be run whenever validator.validateDefault() is called
      // so for example above, calling:
      formState.model.validator.validateDefault()
      // both name and email validations will be run.

      // e.g. if you create with these args
      const formState = makeFormStateWithModel({
      validations: {
      name: () => return validate...
      email: () => return ...
      baz: () => return validate...,
      validateDefault: ['name', 'baz']
      }
      })
      // it will as well set all validation methods on instance, but on call validateDefault(), only name and baz
      // validations will run. So you can run email e.g. conditionally in logic or, whatever.
      // e.g.:
      validateAll:
      formState.validateAll() // this is effectively same as formState.model.validator.validateDefault()
      if (something) {
      formState.model.validator.email() // will validate email separately.
      }
      see

      defaultValidations

      Parameters

      • argValidations: any

      Returns void

    • getErrorsFor(property: keyof MODEL_T): undefined | string[]
    • getFirstErrorFor(property: keyof MODEL_T): undefined | string
    • because there may be multiple errors, they are contained in array, but normally you need only first one, e.g. to show in component. This returns it

      example
      model.validator.addError('name', 'uncool') // errors === {name: ['uncool']}
      model.validator.addError('name', 'bad') // errors === {name: ['uncool', 'bad']}
      model.validator.getFirstErrorFor('name') // uncool
      model.validator.removeErrors('name') // errors === undefined
      model.validator.getFirstErrorFor('name') // undefined

      Parameters

      • property: keyof MODEL_T

      Returns undefined | string

    • getPreviousValue(property: keyof MODEL_T): any
    • returns last validated cachec value for property. Usefull if you need to skip validation.

      remarks

      methods that automatically populate cache are validateProperty and methods decorated with @validates

      example
      @validates
      name()..

      model.name = 'Joe'
      model.validator.getPreviousValue('name') // undefined
      model.validator.name()
      model.validator.getPreviousValue('name') // 'Joe'
      model.name = 'Baz'
      model.validator.getPreviousValue('name') // 'Joe'
      model.validator.name()
      model.validator.getPreviousValue('name') // 'Baz'

      Parameters

      • property: keyof MODEL_T

        property which value will be returned from cache

      Returns any

    • depending on result returned by validation function, handles addition/removal of errors on model

      example
      // will set error if it's not there on model
      handleValidationResult('email', {valid: false, errors: 'invalid'})

      // removes errors for property
      handleValidationResult('email', {valid: true})

      Parameters

      • propertyName: keyof MODEL_T

        property name for which validation result is handled in question

      • result: ValidationReturn

        a return result of validation function of type ValidationReturn if valid: removes all errors for property if valid and single error: adds error if not there, other errors (e.g. from other validators are not removed) above is useful when different methods validate the field, so they do not interfere with each other if valid and multiple: removes all errors, sets errors contained in result.errors above is useful when you have single method that validates property and you support multiple errors

      Returns undefined | ValidationReturn

    • isPropertyValid(property: keyof MODEL_T): boolean
    • isValid(): boolean
    • removeErrors(property: keyof MODEL_T): void
    • example
      model.validator.addError('email', 'invalid')
      model.validator.addError('email', 'uncool')
      model.validator.getErrorsFor('email') // ['invalid', 'uncool']
      model.validator.removeErrors('email')
      model.validator.getErrorsFor('email') // undefined
      model.errors.email // undefined
      model.modelData.errors.email // undefined

      Parameters

      • property: keyof MODEL_T

        property for which all errors will be unset on model

      Returns void

    • removeSpecificError(property: keyof MODEL_T, errorToDelete: string): void
    • example
      model.validator.addError('firstName', 'too long')
      model.validator.getErrorsFor('firstName') // ['too long']
      model.validator.addError('firstName', 'uncool')
      model.validator.getErrorsFor('firstName') // ['too long', 'uncool']
      model.validator.removeSpecificError('firstName', 'too long')
      model.validator.getErrorsFor('firstName') // ['uncool']

      Parameters

      • property: keyof MODEL_T

        property for which error shall be removed

      • errorToDelete: string

        if error with value exists for model, it will be removed

      Returns void

    • resetErrors(): void
    • validate(...methods: ({ [ K in string | number | symbol]?: (value: MODEL_T[K]) => void } | keyof ModelValidator<MODEL_T>)[]): void
    • example
      const foo = makeFormModel({email: '', name: 'bar', shipping: {shippingMethod: ''}},
      {
      paymentMethod: (value) => validateRequired(value, regex)
      name: (value) => validateIsRequired(value)
      address: (value) => validateIsRequired(value)
      },
      tap: (data) => ({
      shipping: makeFormModel(data.shipping, {shippingMethod: () => validateIsRequired(value)})
      })
      )

      validateAddressStep() {
      model.validator.validate('name', 'address')
      }

      validatePaymentStep() {
      model.validator.validate('paymentMethod')
      }

      validateShippingStep() {
      model.validator.validate('address', {shipping: (shippingModel) => shippingModel.validate('shipping') })
      }

      validateAll() {
      model.validator.validateDefault
      }

      Parameters

      • Rest ...methods: ({ [ K in string | number | symbol]?: (value: MODEL_T[K]) => void } | keyof ModelValidator<MODEL_T>)[]

        will call any methods with specified name provided in arg.

      Returns void

    • validateDefault(validateNested?: boolean): void
    • runs validation function, and handles it's result. behind the scenes will define if validation needs to be run by comparing cache values if validation unnecessary - skips otherwise runs validation and handles result, via adding/removing errors for property and updating cache.

      remarks

      effectively this method does exactly same as the method decorated with @validates so in case you can't decorate, or e.g. use it outside of validator (e.g. due code splitting/whatever), you can achieve same behavior as with @validates this method is used for edgecases and when your usecase requires customization.

      example


      // using with defined validation function
      validator.validateProperty('email', (value) => return validateEmail(value))

      // if you need to use something from scope
      validator.validateProperty('email', (value) => return validationPattern(value, someRegexFromScope, 'invalid'))

      // as event handler
      button onClick=(e) => {
      model.email = e.target.value
      model.validator.validateProperty('email', (value) => validateEmail(value, 'email invalid')
      }

      Parameters

      • property: keyof MODEL_T

        property that's being validated

      • validationFunc: (value: any) => ValidationReturn

        any function that accepts value and returns ValidationReturn. Value will be read from model's property

      • Optional options: { skipCached: boolean }

        various options

        • skipCached: boolean

      Returns undefined | ValidationReturn

    • valueIsSameAndValidated(property: keyof MODEL_T): boolean
    • compares value on cache and on model. true if values are same AND value was at least once validated

      Parameters

      • property: keyof MODEL_T

        property to compare with cache

      Returns boolean

    Generated using TypeDoc