2
votes

This is a similar to this but we want to define validation logic in model level but the following doesn't show validation message.

user-model.js (not working)

import {transient, inject} from 'aurelia-framework';
import {ensure} from 'aurelia-validation';

@transient()
export class UserModel {
  @ensure(function(it) { it.isNotEmpty().hasLengthBetween(3,10) })
  firstName = "";
  constructor() {
    this.firstName = "";
  }
}

user.js (not working)

import {inject} from 'aurelia-framework';
import {Validation} from 'aurelia-validation';
import {UserModel} from 'models/user-model';

@inject(Validation, UserModel)
export class User {
  
  constructor(validation, userModel) {
    this.userModel = userModel;
    this.validation = validation.on(this);
  }
}

user.html (not working)

<form role="form" validate.bind="validation">
  <div class="form-group">
    <label>First Name</label>
    <input type="text" validate="model.firstName" value.bind="model.firstName" class="form-control" >
  </div>
</form>

Notice that validate="model.firstName" is used in user.html, which kind of makes the validation work, meaning I see 'has-success' class gets added to form-group div when user input is valid, but it doesn't display message when it's NOT valid input. But, if I take out the validation logic outside of the user-model.js and put it in user.js like below, it works just fine.

user-model.js (working)

import {transient, inject} from 'aurelia-framework';

@transient()
export class UserModel {
  constructor() {
    this.firstName = "";
  }
}

user.js (working)

import {inject} from 'aurelia-framework';
import {Validation} from 'aurelia-validation';
import {UserModel} from 'models/user-model';

@inject(Validation, UserModel)
export class User {
  
  constructor(validation, userModel) {
    this.model = userModel;
    this.validation = validation.on(this)
      .ensure('model.firstName')
        .isNotEmpty()
        .hasLengthBetween(3,10);
  }
}

user.html (working)

<form role="form" validate.bind="validation">
  <div class="form-group">
    <label>First Name</label>
    <input type="text" value.bind="model.firstName" class="form-control" >
  </div>
</form>

We are trying to define validation logic in user model itself so that when we need to use it in other UIs, we have the centralized location to validate it instead of copy & paste the logic everywhere. It's possible that I am doing something wrong, but if anyone knows how to accomplish this, any help is appreciated!

1

1 Answers

2
votes

From the aurelia-validation docs,

As your viewmodels become more complex or if you start using binding converters, the binding path you used to set up the validation rules might be different than the binding path you used in your view, so you'll need to give the validate custom attribute some extra clues as to which elements should be matched against which validation rules. Consider this more complex example...

Basically, the validation rule was created against the firstName property in your UserModel, but the binding for the input element has a different binding path: value.bind="userModel.firstName". Because of this, you need to add a validate="firstName" attribute onto the input element to help aurelia-validation know which HTML element to match on for the validation messages.

Here's how you can do it (with Plunkr)

user-model.js

import {transient} from 'aurelia-framework';
import {ensure} from 'aurelia-validation';

@transient()
export class UserModel{
  @ensure(function(it) { it.isNotEmpty().hasLengthBetween(3,10) })
  firstName = "";

  constructor() {
    this.firstName = "";
  }
}

user.js

import {inject} from 'aurelia-framework';
import {Validation} from 'aurelia-validation';
import {UserModel} from 'user-model';

@inject(Validation, UserModel)
export class User {

  constructor(validation, userModel) {
    this.userModel = userModel;
    this.validation = validation.on(this.userModel);
  }

}

user.html

<template>
  <form role="form" validate.bind="validation">
    <div class="form-group">
      <label>First Name</label>
      <input type="text" value.bind="userModel.firstName" validate="firstName" class="form-control">
    </div>
  </form>
</template>