1
votes

I'm trying to generate a table using properties of two viewmodels, which are sub viewmodels of the main viewmodel, which ko.applyBindings() is called using.

The idea is to generate a row for each element in SubVM1 where the first cell is the element's name. Then for every element in SubVM2, an additional cell is added to the row.

The rows generate correctly and the first cell shows the SubVM1 name, but it is only followed by one cell, instead of however many elements are in SubVM2.

Also, the function in the data-bind doesn't work either. I've tried declaring the Value function as a prototype of SubV2 but it errors out as undefined.

Regardless, something I'm not sure about is clearly happening with the binding context, and help would be appreciated.

<tbody  data-bind="foreach: {data: SubViewModel1, as: 'SubVM1'}">
    <tr>
        <td data-bind="text: SubVM1.Name" />
        <!-- ko foreach: {data: $root.SubViewModel2, as: 'SubVM2'} -->
            <td data-bind="text: Value(SubVM1.Type)"></td>
        <!-- /ko -->
    </tr>
</tbody>

Edit: Partially done jsfiddle: http://jsfiddle.net/jgr71o8t/1

1
Include jsfiddle pleaseMichael Crook
Here is a jsfiddle that represents the problem: jsfiddle.net/jgr71o8t/4barthooper
Uncaught Error: Unable to parse bindings. Message: ReferenceError: SubVM1 is not defined; Bindings value: text: SubVM1.NameMichael Crook
jsfiddle.net/jgr71o8t/13 In this one, Name works and it starts to render but it doesn't access SubVM2.Type in the function in the data-bind for the virtual foreach.barthooper

1 Answers

0
votes

There are a couple of things that I could see. The first one is that the <td data-bind='' />.
Knockout does not generally like self closed tags. Always use the closing tag, in this case <td data-bind=''></td>

The second is that anything you want updated on the screen should be an ko.observable or ko.observableArray. any changes to properties after ko.applyBindings will not be reflected on the screen

HTML

<table border="1">
    <tbody data-bind="foreach: {data: subViewModel1, as: 'SubVM1'}">
        <tr>
            <td data-bind="text: name"></td>
             <!-- ko foreach: {data: $root.subViewModel2, as: 'SubVM2'} -->
                <td data-bind="text: SubVM2.Value(SubVM1.Type)"></td>
            <!-- /ko -->
        </tr>
    </tbody>
</table>

JS Fiddle Demo with knockout bindings on all properties

function MasterViewModel() {
    var self = this;
    self.subViewModel1 = ko.observableArray([]);
    self.subViewModel2 = ko.observableArray([]);
}

function SubVM1ViewModel() {
    var self = this;
    self.name = ko.observable("Sub View Model 1");
    self.otherProperty = ko.observable(43);
}

function SubVM2ViewModel() {
    var self = this;
    self.title = ko.observable('Sub View Model 2');
    self.subVM1List = ko.observableArray([]);
}

SubVM2ViewModel.prototype.Value = function (type) {
    for (var i = 0; i < this.subVM1List().length; i++) {
        if (this.subVM1List()[i].Type === type) {
            return this.subVM1List()[i].name();
        }
    }
};

var masterVM = new MasterViewModel();

var subVM2 = new SubVM2ViewModel();
subVM2.subVM1List.push(new SubVM1ViewModel());

masterVM.subViewModel1.push(new SubVM1ViewModel());
masterVM.subViewModel2.push(subVM2);

ko.applyBindings(masterVM);

JS Fiddle Demo with straight javascript properties

function MasterViewModel() {
    var self = this;
    self.subViewModel1 = [];
    self.subViewModel2 = [];
}

function SubVM1ViewModel() {
    var self = this;
    self.name = "Sub View Model 1";
    self.otherProperty =43;
}

function SubVM2ViewModel() {
    var self = this;
    self.title = 'Sub View Model 2';
    self.subVM1List = [];
}

SubVM2ViewModel.prototype.Value = function (type) {
    for (var i = 0; i < this.subVM1List.length; i++) {
        if (this.subVM1List[i].Type === type) {
            return this.subVM1List[i].name;
        }
    }
};

var masterVM = new MasterViewModel();

var subVM2 = new SubVM2ViewModel();
subVM2.subVM1List.push(new SubVM1ViewModel());

masterVM.subViewModel1.push(new SubVM1ViewModel());
masterVM.subViewModel2.push(subVM2);

ko.applyBindings(masterVM);