JSFiddle - React, Tailwind, and code Playground
by lamarant
HTML
<script src="https://raw.github.com/SteveSanderson/knockout.mapping/master/build/output/knockout.mapping-latest.debug.js"></script>
Solution #2 to: <a href="http://stackoverflow.com/questions/16137887" target="_blank">http://stackoverflow.com/questions/16137887</a>
<hr />
<h1>All Foo's</h1>
<div data-bind="template: { name: 'MyTemplate', data: { data: Foo } }"></div>
<hr />
<h1>All Poo's</h1>
<div data-bind="template: { name: 'MyTemplate', data: { data: Poo } }"></div>
<hr />
<script type="text/html" id="MyTemplate">
<div class="addnew">
Add Member »
<select data-bind="options: $root.filterObjects(data), optionsText: 'ID', value: data.Selected"></select>
<button data-bind="click: $root.addSelected(data, data.Selected) ">+</button>
</div>
<div data-bind="foreach: data.Objects">
<div>
Member #<span data-bind="text: ID"></span> »
<input type="text" data-bind="value: Title, valueUpdate: 'afterkeydown'" />
<button data-bind="click: function () { $parent.data.removeObject($data); }">x</button>
</div>
</div>
</script>
<h1 class="Debug">Debug</h1>
<strong>viewModel._AllObjects</strong>
<pre data-bind="text: ko.toJSON($data._AllObjects, null, 2)"></pre>
<strong>viewModel.Foo</strong>
<pre data-bind="text: ko.toJSON($data.Foo, null, 2)"></pre>
<strong>viewModel.Poo</strong>
<pre data-bind="text: ko.toJSON($data.Poo, null, 2)"></pre>
CSS
body{color: #444; font-family: Arial; font-size: 14px;}
input{width: 50%; padding: 5px;}
button{cursor: pointer;}
select{padding: 5px;}
h1{font-size: 16px;}
h1.Debug{font-size: 14px; background: red; color: #fff;padding: 5px;}
pre, strong{font-size: 12px;}
.addnew{background: #eee;padding: 5px;margin-bottom: 10px;}
JavaScript
var model = {
_AllObjects: [
{ ID: 100, Title: 'This is item number 100' },
{ ID: 776, Title: 'This is item number 776' },
{ ID: 456, Title: 'This is item number 456' },
{ ID: 999, Title: 'This is item number 999' }
],
_FooRefs: [100],
_PooRefs: [100, 776]
};
ko.extenders.references = function (target, mappedObjects) {
target.Objects = ko.computed(function () {
var refs = ko.utils.unwrapObservable(target);
return ko.utils.arrayMap(refs, function (id) {
return mappedObjects.mappedGet({ ID: id });
});
});
target.addObject = function (obj) {
if (obj) {
var id = ko.utils.unwrapObservable(obj.ID);
if (mappedObjects.mappedIndexOf({ ID: id }) === -1) {
mappedObjects.mappedCreate(obj);
}
target.push(id);
}
}.bind(target);
target.removeObject = function (obj) {
if (obj) {
var id = ko.utils.unwrapObservable(obj.ID);
target.remove(id);
}
}.bind(target);
return target;
};
function ViewModel(data) {
ko.mapping.fromJS(data, {
ignore: ['_AllObjects', '_FooRefs', '_PooRefs']
}, this);
var _AllObjects = ko.mapping.fromJS(data._AllObjects, {
key: function (obj) {
return obj.ID;
}
});
this.Foo = ko.observableArray(data._FooRefs).extend({ references: _AllObjects });
this.Poo = ko.observableArray(data._PooRefs).extend({ references: _AllObjects });
this.Foo.Selected = ko.observable();
this.Poo.Selected = ko.observable();
this.filterObjects = function (references) {
return ko.computed(function () {
var allObjects = ko.utils.unwrapObservable(_AllObjects);
return ko.utils.arrayFilter(allObjects, function (obj) {
var refs = ko.utils.unwrapObservable(references),
id =...