I am creating a guest list app using Knockout.js, and so far things are going swimmingly. However I have a best-practices question. My app has several different types of objects: guests and tags among them. Guests can have multiple tags, and tags can have multiple guests. At different points in the app, I need to display both arrays individually. For example, I have a "Guests" view where one can see all the guests along with their associated tags, and I also have a "Tags" view where one can see all tags and their associated guests. At present, the code for me to add a tag to a guest looks something like this:
var tag = function(opts) {
this.guests = ko.observableArray()
// Other tag code here...
}
var guest = function(opts) {
this.tags = ko.observableArray()
// Other guest code here...
var self = this
this.addTag = function(tag) {
self.tags.push(tag)
tag.guests.push(self)
}
}
I know there must be a better way of doing this kind of many-to-many relationship in Knockout other than updating each observableArray independently. This also leads to a kind of recursive app structure where a guest has a tags property/array which contains a tag, which has a guest property/array which contains a guest, which has a tags property... you get the picture.
Right now, the ViewModel structure is like so:
- Parent Object
- Guests ObservableArray
- Guest Object
- Tag Object as property of Guest
- Tags ObservableArray
- Tag Object
- Guest Object as property of Tag
So I guess my question is twofold: 1) Is there a better way to structure my ViewModel to avoid recursive arrays? and 2) how can I better use Knockout.js to update my ViewModel in a DRY manner, rather than updating both the tags array AND the guests array individually? Thanks!
There are probably other ways to do this, but this method has pretty minimal duplication, without sacrificing proper modeling. A server should have no trouble generating the data in this format.
Here it is in a (crudely styled) fiddle. Note, clicking a tag or guest will cause the selections below it to update (the first is selected by default).
Basically, store the relationships by id, and use
computed
array's to represent associations. Here is a basic viewmodel:UPDATE
So I have made a new fiddle to demonstrate a different kind of mapping, but this code could easily co-exist with the above viewmodel; its only seperate for demonstration. Instead of working off selection, it offers a general lookup, so that any code can consume it. Below is the HTML from Tags (guests is symmetrical), and the
guestMap
function that was added to the viewmodel.You will note that the names are
input
s now, so you can change the names and watch all the bindings stay up to date. Let me know what you think:I had the same kind of problem. Displaying many to many related stuff. I had to do 'grid' style display and have an update mechanism in it.
I ended up replicating the structure I had in the backend DB. Two tables of items with join table in between. Pulled the data out from those in three arrays and kept updating the 'join' array. The test data and fiddle of my testings with it below.
Join table fiddle
I'm not really sure how efficient this method is with lots of data but did the stuff I needed it to do.