Arnab
Arnab

Reputation: 2354

Using knockout validation to ensure values in an array are unique

My html code:

<button  data-bind="click: addmyEvent">
Add New Event</button><p />
<div class="row" data-bind="foreach: myevents">
 <input type="text" data-bind="value: name" />
<button  data-bind="click: $root.addProperty">Add Property</button><p />
<div data-bind="foreach: properties">
<input type="text" data-bind="value: name" />
<button data-bind='click: addOptionValue'>Add Option </button>
        <div data-bind="foreach: optionvalues">
          <div class="form-inline">
            <input type="text" data-bind="value: optionvalue" />
          </div>
        </div>
</div>
</div>
<pre data-bind="text: JSON.stringify(ko.toJS($root), null, 2)"></pre>

My javascript code:

function myEvent(data){
    var self = this
    self.name=ko.observable(data.name);
    self.properties = ko.observableArray(data.propertydetails);
}

 function Property(data) {
     var self = this
     self.name=ko.observable(data.name);
     self.optionvalues = ko.observableArray(data.optionvalue);
     self.addOptionValue = function (properties) {
         properties.optionvalues.push({ optionvalue: "" });
     }
 }
 var myViewModel = function () {
      var self = this
            self.myevents = ko.observableArray([]);  

    self.addmyEvent = function () {
        self.myevents.push(new myEvent({name:"", properties: ko.observableArray() }));
    }
    self.addProperty = function (ev) {
        ev.properties.push(new Property({name:"", optionvalues: ko.observableArray()}));
    };
}
ko.applyBindings(new myViewModel());

Jsfiddle : https://jsfiddle.net/o6juohxL/9/ 1

I would like to use knockout validation and ensure..

I found isUnique rule amongst user contributed rules here but am unable to figure out how to use the same.

Upvotes: 1

Views: 1691

Answers (1)

user3297291
user3297291

Reputation: 23372

The isUnique contribution you found should work for your use case. I'll try to explain how the validator works, so you can implement it for all your use cases.

The basics for ko.validation:

  1. First, you'll have to make sure the ko.validation script is loaded and extended with the custom rule.
  2. Extend your observable that needs validation using .extend( /* options */ )

Now, we can have a look at the isUnique implementation. In the documentation you can see that you need to provide either an array/ko.observableArray or Object with an array property. Let's keep it simple and go with the first one.

function myEvent(data) {
  var self = this;
  var eventNames = ko.observableArray([]);

  self.name = ko.observable(data.name).extend({
    required: true,
    isUnique: eventNames
  });
};

If you'd place a breakpoint in the custom rule, you'll be able to check if the rule correctly runs. But it will use an empty array to check for uniques. Let's fix that.

A myEvent instance doesn't know anything about the other events' names. The set of events lives in myViewModel. Also, it's an observable array of objects rather than event names. We'll make a new computed variable to create an array of event names, and we'll pass a reference on to our myEvent constructors:

// Create a computed array of names
var myEventNames = ko.computed(function() {
  return self.myevents().map(function(event) { return event.name(); });
});

// When creating a new event, give it access to this array
self.addmyEvent = function() {
  self.myevents.push(new myEvent({
    name: "",
    properties: ko.observableArray()
  }, myEventNames));
};

Now, we can go back to myEvent and remove the eventNames declaration from the constructor. It is now a parameter that is passed by myViewModel.

function myEvent(data, eventNames) {
  var self = this;
  self.name = ko.observable(data.name).extend({
    required: true,
    isUnique: eventNames
  });

  // Other code ...
};

Does this enable you to figure out the other implementations by yourself? Let me know what you think.

Here's a fiddle with the example I gave: https://jsfiddle.net/ute3uem6/

Edit: if you don't want to litter your view model with computeds, you can use the predicate functionality in the isUnique rule:

This is how it works: if you provide a method that tells isUnique how to compare the current value to the values in an array, you do not need to map the myevents observable to an array of strings. It does the same thing, but uses a different approach. I guess it's personal preference which way you choose.

isUnique: {
    array: events,
    predicate: function(currentEvent, currentValue) {
      return currentEvent.name() === currentValue;
    }
}

Upvotes: 1

Related Questions