seadrag0n
seadrag0n

Reputation: 848

KOValidation get observable value, $index(), $data etc. in the error message

I have the following JS object:

var TournamentPlayer = function (PlayerName, Handicap, Nationality) {
    var self = this;

    self.PlayerName = ko.observable(PlayerName).extend({
        required: {
            message: 'Player No. ' + $index() + ' is required.' // How to get $index() here?
        }
    });
    self.Handicap = ko.observable(Handicap);
    self.Nationality = ko.observable(Nationality);
}

I need to capture details of four players where PlayerName is required, I am using the following code to add empty TournamentPlayer objects in the parent JS object:

self.TournamentPlayers.push(new TournamentPlayer('', 0, ''));
self.TournamentPlayers.push(new TournamentPlayer('', 0, ''));
self.TournamentPlayers.push(new TournamentPlayer('', 0, ''));
self.TournamentPlayers.push(new TournamentPlayer('', 0, ''));

I want to get KO data like the observable value, $index(), $data() etc in the KOValidation extend function, how can I do this?

Upvotes: 2

Views: 53

Answers (1)

Jeroen
Jeroen

Reputation: 63709

My first thought was to do this by coding the index into the view models, thus making it unit testable:

var TournamentPlayer = function (PlayerName, Handicap, Nationality, GetNr) {
    var self = this;
    self.PlayerName = ko.observable(PlayerName).extend({
        required: {
            message: 'Player No. ' + GetNr(self) + ' is required.'
        }
    });
}

Then instantiate them like this:

var Root = function() {
    self.TournamentPlayers = ko.observableArray([]);
    self.GetNr = function(p) { return self.TournamentPlayers.indexOf(p); };
    self.TournamentPlayers.push(new TournamentPlayer('', 0, '', self.GetNr));
    // etc.
}

But this does not work correctly, because GetNr(self) is called once, upon constructing the Player, before it has been pushed into the array (thus they all get a nr of -1).

Unfortunately, the message of an extension cannot be (AFAICT) an observable. There is formatting for custom rules, where I guess you could "inherit" from the default required rule and give it formattable messages.

With all that, I'm afraid the best way to do what you want is render custom messages in your markup, something like this:

var TournamentPlayer = function (PlayerName, Handicap, Nationality) {
  var self = this;
  self.PlayerName = ko.observable(PlayerName).extend({
    required: {
      message: 'Player Name is required.'
    }
  });
}

var Root = function() {
  self.TournamentPlayers = ko.observableArray([]);
  self.GetNr = function(p) { return self.TournamentPlayers.indexOf(p); };
  self.TournamentPlayers.push(new TournamentPlayer('', 0, ''));
  self.TournamentPlayers.push(new TournamentPlayer('', 0, ''));
  self.TournamentPlayers.push(new TournamentPlayer('', 0, ''));
  self.TournamentPlayers.push(new TournamentPlayer('', 0, ''));
}

ko.applyBindings(new Root());
.validationMessage { display: none; }
.customValidationMessage { color: red; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout-validation/2.0.3/knockout.validation.min.js"></script>

<ul data-bind="foreach: TournamentPlayers">
  <li>
    Player Name: 
    <input data-bind="textInput: PlayerName">
    <span class="customValidationMessage" data-bind="visible: !PlayerName.isValid()">
      Player 
      <span data-bind="text: $index() + 1"></span>
      is required.
    </span>
  </li>
</ul>

Not pretty, and I welcome competing answers, but best I could come up with so far...

Upvotes: 1

Related Questions