Reputation: 2354
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..
name
field in viewmodel.myevents
array is uniquename
field in viewmodel.myevents[].properties
array is uniqueoptionvalue
field in viewmodel.myevents[].properties[].optionvalues
array is uniqueI found isUnique
rule amongst user contributed rules here but am unable to figure out how to use the same.
Upvotes: 1
Views: 1691
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
:
ko.validation
script is loaded and extended with the custom rule. .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