Learn about me or read more of my blog
Written by Giulio Canti on 12 Aug 2014
Does the world need another validation library? Probably not, but I think of tcomb more as a tool to build fast and safe domain models. Since I want the models be the single source of truth and it’s hard to keep my models and the UI validation rules synced, I think there is still something to explore. The models should already express those validation rules so I only need to make them explicits. I’ll show you how to achive this goal with a simple example, a sign up form.
You can found a demo here.
For an implementation of the ideas exposed in this post see the tcomb-form library on GitHub.
Let’s design the domain model for the sign up process
// domain.js
// A Reddit like sign up:
// - username: required
// - password: required
// - email: optional
// a username is a string with at least 1 char
var Username = subtype(Str, function (s) {
  return s.length >= 1;
});
// a password is a string with at least 6 chars
var Password = subtype(Str, function (s) {
  return s.length >= 6;
});
// an email is a string that contains '@' :)
var Email = subtype(Str, function (s) {
  return s.indexOf('@') !== -1;
});
// sign up info
var User = struct({
  username: Username,
  password: Password,
  email: maybe(Email) // optional, can be `null`
});
// send signup info to server
User.prototype.signup = function () {
  $.post('/signup', JSON.stringify(this));
};
I’ll use Bootstrap for this
<!-- signup.html -->
<script src="tcomb.js"></script>
<script src="domain.js"></script>
<script src="signup.js"></script>
<form role="form" method="post">
  <div class="form-group">
    <input type="text" id="username" placeholder="Username" class="form-control"/>
  </div>
  <div class="form-group">
    <input type="password" id="password" placeholder="Password" class="form-control"/>
  </div>
  <div class="form-group">
    <input type="text" id="email" placeholder="Email (optional)" class="form-control"/>
  </div>
  <button class="btn btn-primary btn-block">Sign up</button>
</form>
This is the tricky part: the controller must validate the input and show visual feedback to the
user that something went wrong. It’s straightforward to write a general validating function exploiting
the meta.props hash of  structs.
// signup.js
$('form').on('submit', function (evt) {
  evt.preventDefault();
  // getting an instance of User means validation succeded
  var user = validate(User);
  if (user) {
    user.signup();
    alert('Signup info sent.');
  }
});
// configurable validating function
// - visual feedback is more fine grained
// - assume inputs are named like the struct props
function validate(Struct) {
  var values = {};
  var props = Struct.meta.props;
  var isValid = true;
  for (var id in props) {
    if (props.hasOwnProperty(id)) {
      var $input = $('#' + id);
      var value = values[id] = $input.val().trim() || null;
      if (!props[id].is(value)) {
        isValid = false;
        $input.parent().addClass('has-error');
      } else {
        $input.parent().removeClass('has-error');
      }
    }
  }
  return isValid ? new Struct(values) : null;
}
If you decide later that a username must be a string with at least 4 chars and you change the model accordingly, you don’t have to touch the view controller code.