Reputation: 55
I have run into a strange problem while trying out a slightly complex composition scenario using durandal.
I want to dynamically compose the content of each cell of a table. As an example, I have the following files:
welcome.html => contains the code to render html table dynamically
<table>
<thead>
<tr data-bind="foreach: columnNames">
<th><span data-bind="text: $data"></span>
</th>
</tr>
</thead>
<tbody data-bind="foreach: items">
<tr data-bind="foreach: $parent.columnNames">
<!-- <td data-bind="text: $parent[$data]"></td>-->
<td data-bind="compose: { model: $root.activeView($index), activationData: $root.activationData($parentContext.$index), cacheViews: false }"></td>
</tr>
</tbody>
</table>
welcome.js => the corresponding view model
define(['knockout'], function (ko) {
var ctor = function () {
var m_items = [{
'Name': 'John',
'Age': 25
}, {
'Name': 'Morgan',
'Age': 26
}];
this.items = ko.observableArray(m_items);
this.columnNames = ko.computed(function () {
if (m_items.length === 0)
return [];
var props = [];
var obj = m_items[0];
for (var name in obj)
props.push(name);
return props;
});
this.activeView = function (index) {
if (index() == 0)
return "viewmodels/cell-1";
else
return "viewmodels/cell-2";
};
this.activationData = function (rowIndex) {
return m_items[rowIndex()];
}
};
return ctor;
});
cell-1.html => view to display for first column
<span data-bind="text : Name"></span>
cell-1.js => corresponding view model for cell-1
define(['knockout'], function (ko) {
var model = null;
var name = ko.observable();
var ctr = function () {
this.Name = name;
this.activate = function (modelObject) {
model = modelObject;
name(model.Name);
};
};
return ctr;
});
And similarly a view and view model for cell-2.
The problem I am facing is that even though the composition works fine and activate function is called twice for cell-1.js (once for each row), the value of the observable name
would contain the previous value.( i.e. the value would be "John" when it tries to set it to "Morgan"). This is strange as cell-1.js returns a constructor and hence durandal should have created another instance of the view model. To sum up, this results in both the rows containing the same data.
I'm sure that there is something minor that I have to tweak to get it to work, but all my effort has been in vain.
Any help would be greatly appreciated.
Upvotes: 1
Views: 950
Reputation: 139748
The problem is that you have declared your model
and name
variables on the module level in your cell-1.js
.
So even if you return a constructor which gets called twice the module itself only loaded once so your viewmodels are sharing the same state.
To solve it you just need to move your variables inside the ctr
:
define(['knockout'], function (ko) {
var ctr = function () {
var model = null;
var name = ko.observable();
this.Name = name;
this.activate = function (modelObject) {
model = modelObject;
name(model.Name);
};
};
return ctr;
});
Upvotes: 2