1

Given that there is not much examples about this, I am following the docs as best as I can, but the validation is not reactive.

I declare a schema :

import { Tracker } from 'meteor/tracker';
import SimpleSchema from 'simpl-schema';

export const modelSchema = new SimpleSchema({
  foo: { 
    type: String,
    custom() {
      setTimeout(() => {
        this.addValidationErrors([{ name: 'foo', type: 'notUnique' }]);
      }, 100);  // simulate async
      return false;
    }
  }
}, {
  tracker: Tracker
});

then I use this schema in my component :

export default class InventoryItemForm extends TrackerReact(Component) {

  constructor(props) {
    super(props);

    this.validation = modelSchema.newContext();
    this.state = {
      isValid: this.validation.isValid()
    };
  }

  ...

  render() {
    ...
    const errors = this.validation._validationErrors;

    return (
      ...
    )
  }
}

So, whenever I try to validate foo, the asynchronous' custom function is called, and the proper addValidationErrors function is called, but the component is never re-rendered when this.validation.isValid() is supposed to be false.

What am I missing?

Yanick Rochon
  • 51,409
  • 25
  • 133
  • 214

1 Answers1

2

There are actually two errors in your code. Firstly this.addValidationErrors cannot be used asynchronously inside custom validation, as it does not refer to the correct validation context. Secondly, TrackerReact only registers reactive data sources (such as .isValid) inside the render function, so it's not sufficient to only access _validationErrors in it. Thus to get it working you need to use a named validation context, and call isValid in the render function (or some other function called by it) like this:

in the validation

custom() {
  setTimeout(() => {
    modelSchema.namedContext().addValidationErrors([
      { name: 'foo', type: 'notUnique' }
    ]);
  }, 100);
}

the component

export default class InventoryItemForm extends TrackerReact(Component) {
  constructor(props) {
    super(props);

    this.validation = modelSchema.namedContext();
  }

  render() {
    let errors = [];
    if (!this.validation.isValid()) {
      errors = this.validation._validationErrors;
    }

    return (
      ...
    )
  }
}

See more about asynchronous validation here.

Waiski
  • 9,214
  • 3
  • 21
  • 30
  • 1
    I find that weird (and an anti-pattern) to use a named context inside the schema itself... so to have this validation, it *needs* to always use this same schema? What if I'd have a template where the user can add in batch multiple models; would I use the same context for all of them? What if one fails, the others would display the error as well? I used `this.addValidationErrors` as I supposed that the validation function would receive the context at call time! Are you saying that it is not the case? – Yanick Rochon Sep 01 '16 at 13:10
  • @YanickRochon TBH I don't really understand the pattern either. I see you opened issues with the package, I was about to suggest that. – Waiski Sep 01 '16 at 18:54
  • exactly. This is why I suggested the PR. I can get way with it if I specify the context to the `extendedCustomContext`, but I believe that the `getContext()` proposal is a far better normalized way. Anyway, thank you for your explanations, I re-read `TrackerReact` source code and understand better how it works :) Cheers! – Yanick Rochon Sep 01 '16 at 23:19