JSFiddle - React, Tailwind, and code Playground
by jlspake
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="AttrValidationDiv">
<span data-bind="text: validations"></span>
</div>
<table>
<tbody>
<!--ko foreach: AttrsViewModels-->
<tr>
<td>
<span data-bind="text: 'Level-' + Level()"></span>
</td>
<td>
<select data-bind="options:$root.optionsViewModel, optionsText:'ProductName', optionsValue:'ProductId',value:ServiceGroup, optionsCaption:'Select'"></select>
</td>
</tr>
<!--/ko-->
</tbody>
</table>
JavaScript
function viewModel(){
var self = this;
this.optionsViewModel = [
{ ProductId: 1, ProductName: 'product 1' },
{ ProductId: 2, ProductName: 'product 2' },
{ ProductId: 3, ProductName: 'product 3' }
];
this.AttrsViewModels = ko.observableArray([
{ ServiceGroup: ko.observable(), Level: ko.observable(1) },
{ ServiceGroup: ko.observable(), Level: ko.observable(1) },
{ ServiceGroup: ko.observable(), Level: ko.observable(1) },
{ ServiceGroup: ko.observable(), Level: ko.observable(2) },
{ ServiceGroup: ko.observable(), Level: ko.observable(2) },
{ ServiceGroup: ko.observable(), Level: ko.observable(2) },
]);
this.LevelGroups = ko.computed(function(){
var levelMap = {};
//group viewmodels with a dictionary object by level
self.AttrsViewModels().map(function(item){
levelMap[item.Level()] = levelMap[item.Level()] || [];
levelMap[item.Level()].push(item);
});
return levelMap;
});
this.validations = ko.computed(function(){
var groups = self.LevelGroups();
for(var i=0; i<self.optionsViewModel.length; i++){
var option = self.optionsViewModel[i];
for(var x in groups){
if(groups.hasOwnProperty(x)){
var matches = groups[x].filter(function(item){
return item.ServiceGroup() === option.ProductId;
});
if(matches.length >= 2){
return option.ProductName + ' is selected more than once in level ' + x.toString();
}
}
}
}
return '';
});
}
ko.applyBindings(new viewModel());