I'm using HotTowel to build an SPA but I'm having problems getting Knockout's checked binding to work with a Breeze collection.
I have a list of users, and a list of groups. I want to be able to add or remove users to groups by checking a box.
If I understand it correctly I should be able to use a checked binding on an observableArray with the checkedValue option and the it will automatically add or remove items to that array according to whether the boxes are checked or not.
I have the following entities at the server side.
public class Group
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public ICollection<GroupUser> Users { get; set; }
}
public class User
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public ICollection<GroupUser> Groups { get; set; }
}
And since Breeze can't handle many to many relationships without an explicit mapping table, I also have
public class UserGroup
{
[Key, Column(Order=0)]
[ForeignKey("User")]
public int UserId { get; set; }
[Key, Column(Order=1)]
[ForeignKey("Group")]
public int GroupId { get; set; }
public Group Group { get; set; }
public User User { get; set; }
}
Since I have this mapping table, the User.Groups property is an observableArray of UserGroups not Groups, therefore I know I can't just bind the Groups property to the checkboxes directly.
I've tried creating a custom constructor for my User entity so that I can produce a computed observableArray of just the Groups for me to use instead. However when the constructor is created, none of the User properties seem to exist, and my computed observable does not update once they do.
Is this possible or am I going the wrong way about it?
Here's a cutting of my failed javascript attempt:
var vm = {
user: ko.observable(),
groups: ko.observableArray();
save: save,
cancel: cancel
};
/* User entity constructor */
var User = function () {
this.mappedGroups= ko.computed(function () {
var target = this.Groups;
var groupsArray = ko.observableArray([]);
ko.utils.arrayForEach(target, function(userGroup) {
groupsArray.push(userGroup.Group);
});
return groupsArray;
}, this);
};
var serviceName = '/breeze/useradmin';
var store = new breeze.MetadataStore();
store.registerEntityTypeCtor('User', User);
var manager = new breeze.EntityManager({ serviceName: serviceName, metadataStore: store });
...
function loadGroups() {
var query = breeze.EntityQuery.from('Groups');
return manager.executeQuery(query).then(
function (data) {
vm.groups(data.results);
}
);
}
And the html:
<form>
<input class="input-xxlarge "type="text"
name="name" placeholder="User Name" data-bind="value: Name" />
<p>Groups</p>
<div class="well well-small scroll-panel" data-bind="foreach: groups">
<label class="checkbox">
<input type="checkbox" data-bind="checkedValue: $data, checked:
$root.user.mappedGroups, attr: { value: Id }"/>
<p data-bind="text: Description"></p>
</label>
</div>
<div class="form-actions">
<button class="btn btn-primary" data-bind="click: save">Save</button>
<button class="btn" data-bind="click: cancel">Cancel</button>
</div>
</form>