import { observable , extendObservable , action , computed , ObservableMap , toJS } from 'mobx' ; import { forEach , every } from './utils' ; export interface template T template R export interface IValidatorOptions < T > { field : string ; value : any ; obj : T ; } export interface template T template R export interface IValidator < T > { ( opts ) : boolean | string ; } export interface template T export interface IRule { msg ? : string ; } export interface template T export type IValidationSchema < T > = { [ P in keyof T ] ? : Array < IValidator < T > > ; } ; export interface export interface IValidationErrors { [ key ] : Array < string > ; } export interface export interface IValidationContext { errors : IValidationErrors ; isValid : boolean ; reset ( ) : this ; validate < T > ( obj , schema ) : this ; addErrors ( errors ) : this ; getErrors ( field ) : string [ ] ; getError ( field ) : string | undefined ; clearErrors ( field ) : this ; } /**
 * Validation context with the object already bound to the validate function.
 */ export interface IBoundValidationContext < T > extends IValidationContext { validate ( schema ) : this ; getErrors ( field ) : string [ ] ; getError ( field ) : string | undefined ; } /**
 * Validation context with the object already bound to the validate function.
 */ export interface ISchemaBoundValidationContext < T > extends IBoundValidationContext < T > { validate ( ) : this ; } export class implements IValidationContext export class ValidationContext implements IValidationContext { readonly type { IValidationErrors } memberOf readonly errors : IValidationErrors ; readonly type { boolean } memberOf readonly isValid : boolean ; private memberOf private errorsMap : ObservableMap < string , string [ ] > ; /**
     * Initializes a new instance of ValidationContext.
     */ constructor ( ) { this . reset = action ( this . reset ) ; this . addErrors = action ( this . addErrors ) ; this . clearErrors = action ( this . clearErrors ) ; this . validate = action ( this . validate ) ; extendObservable ( this , { errorsMap : observable . map < string , string [ ] > ( ) , get errors ( ) { return toJS ( this . errorsMap ) ; } , get isValid ( ) { return every ( this . errors , ( arr ) => arr . length === 0 ) ; } } , { errors : computed , isValid : computed } ) ; } returns { IValidationContext } memberOf reset ( ) { this . errorsMap . clear ( ) ; return this ; } template T param { T } obj param { IValidationSchema < T > } schema returns { IValidationContext } memberOf validate < T > ( obj , schema ) { forEach ( schema , ( validators , field ) => { const errors = this . ensureErrors ( field ) ; const value = ( obj as any ) [ field ] ; forEach ( validators , ( validator ? ) => { if ( ! validator ) { return ; } const opts = { field , value , obj } ; let msg = 'This field is invalid.' ; const result = validator ( opts ) ; if ( result === true ) { return ; } else if ( result !== false ) { msg = result ; } errors . push ( msg ) ; } ) ; } ) ; this . cleanupErrors ( ) ; return this ; } /**
     * Adds errors to the context.
     */ addErrors ( errors ) { forEach ( errors , ( arr , field ) => { this . ensureErrors ( field ) . push ( ... arr ) ; } ) ; return this ; } /**
     * Gets the errors for the given field.
     */ getErrors ( field ) { const errors = this . errors [ field ] ; if ( ! errors ) { return [ ] ; } return errors . slice ( ) ; } /**
     * Gets the first error for the given field.
     * If not found, returns undefined.
     */ getError ( field ) { return this . getErrors ( field ) [ 0 ] ; } /**
     * Removes errors for a particular field.
     */ clearErrors ( field ) { this . errorsMap . set ( field , [ ] ) ; return this ; } private param { string } field returns memberOf private ensureErrors ( field ) { let errors = this . errorsMap . get ( field ) ; if ( ! errors ) { errors = observable . array ( [ ] ) ; this . errorsMap . set ( field , errors ) ; } return errors ; } private memberOf private cleanupErrors ( ) { const entries = Array . from ( this . errorsMap . entries ( ) ) . filter ( ( [ , value ] ) => value . length === 0 ) ; entries . forEach ( ( [ key ] ) => this . errorsMap . delete ( key ) ) ; } } export function validationContext < T > ( objectToValidate , schema ) ; export function validationContext < T > ( objectToValidate ) ; export function validationContext ( ) ; export function validationContext < T > ( objectToValidate ? , schema ? ) { const v = new ValidationContext ( ) ; if ( objectToValidate !== null && objectToValidate !== undefined ) { if ( schema ) { v . validate = v . validate . bind ( v , objectToValidate , schema ) ; } else { v . validate = v . validate . bind ( v , objectToValidate ) ; } } return v ; }