Last active
December 31, 2015 01:29
-
-
Save janfoeh/7914187 to your computer and use it in GitHub Desktop.
Nested viewmodels for Knockout
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
ko.bindingHandlers.childVm = { | |
init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { | |
var vmName = ko.unwrap(valueAccessor()), | |
childVm; | |
childVm = ko.utils.arrayFirst(viewModel._childVms(), function(item) { | |
return item._viewmodelName() === vmName; | |
}); | |
// 'with' returns {controlsDescendants: true}, so we have to pass it up - otherwise, | |
// the descendant bindings will be bound twice ("You cannot apply bindings multiple times to the same element") | |
return ko.bindingHandlers.with.init(element, function() { return childVm; }, allBindingsAccessor, viewModel, bindingContext); | |
}, | |
update: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { | |
var vmName = ko.unwrap(valueAccessor()), | |
childVm; | |
childVm = ko.utils.arrayFirst(viewModel._childVms(), function(item) { | |
return item._viewmodelName() === vmName; | |
}); | |
ko.bindingHandlers.with.update(element, function() { return childVm; }, allBindingsAccessor, viewModel, bindingContext); | |
} | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function(ko, $) { | |
"use strict"; | |
ko.bindingHandlers.modal = { | |
init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { | |
var options = ko.unwrap(valueAccessor()), | |
childVmName, | |
parentVm, | |
childVmSetupContext, | |
templateToRender, | |
childVm, | |
clickHandler; | |
// we... | |
// - search for a child viewmodel called 'childVmName' | |
// - by asking 'parentVm' | |
// - and initialize the child with an object given us as 'childVmSetupContext' | |
childVmName = options.viewmodelName; | |
parentVm = options.parentViewmodel ? options.parentViewmodel : bindingContext.$parent; | |
childVmSetupContext = options.setupViewmodelWith ? options.setupViewmodelWith : bindingContext.$data; | |
templateToRender = options.templateName | |
childVm = parentVm.getVm(childVmName); | |
// we want our modal to be set up, rendered and displayed when the user clicks | |
// on the element we are bound to | |
clickHandler = function clickHandler() { | |
// set up the child viewmodel with whatever object we have been given, | |
// if the child wants to be set up | |
if (typeof childVm.setup === 'function') { | |
childVm.setup(childVmSetupContext); | |
} | |
// initialize our modal | |
var modal = new MyCoolModalLibrary() | |
// get a reference to the DOM element our modal library stores the | |
// content to be displayed in | |
var modalDomContainer = modal.getContainer(); | |
// tell Knockout to render the template, with our child viewmodel | |
// as the context, into the modals DOM container | |
ko.renderTemplate(templateToRender, childVm, {}, modalDomContainer); | |
modal.display(); | |
}; | |
ko.bindingHandlers.click.init(element, function() { return clickHandler; }, allBindingsAccessor, viewModel, bindingContext); | |
} | |
} | |
}; | |
})(ko, $); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function(ko, $) { | |
"use strict"; | |
ko.bindingHandlers.waitForVm = { | |
init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { | |
var vmName = ko.unwrap(valueAccessor()), | |
subscription, | |
childVm; | |
childVm = ko.utils.arrayFirst(viewModel._childVms(), function(item) { | |
return item._viewmodelName() === vmName; | |
}); | |
// if the child viewmodel isn't available yet... | |
if (!childVm) { | |
// subscribe to the list of child viewmodels | |
subscription = viewModel._childVms.subscribe(function() { | |
childVm = ko.utils.arrayFirst(viewModel._childVms(), function(item) { | |
return item._viewmodelName() === vmName; | |
}); | |
// and if it has become available now, remove the subscription, | |
// apply the bindings to our child elements and make ourselves visible | |
if (childVm) { | |
subscription.dispose(); | |
ko.applyBindingsToDescendants(bindingContext, element); | |
ko.bindingHandlers.visible.update(element, function() { return childVm; }, allBindingsAccessor, viewModel, bindingContext); | |
} | |
}); | |
} | |
// hide ourselves and our children if the viewmodel isn't | |
// immediately available on init(). | |
ko.bindingHandlers.visible.update(element, function() { return childVm; }, allBindingsAccessor, viewModel, bindingContext); | |
// prevent our descendant elements from being bound if the | |
// viewmodel isn't immediately available on init(). We will | |
// take care of that later ourselves through ko.applyBindingsToDescendants | |
return {controlsDescendantBindings: !childVm}; | |
} | |
}; | |
})(ko, $); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Sub-viewmodel handling. Use with borrowed constructor pattern | |
var Parent = function Parent() { | |
var that = this; | |
this._childVms = ko.observableArray(); | |
this.addChildVm = function addChildVm(vm) { | |
var vmName = vm; | |
vm = new app.viewmodels[vmName](); | |
vm.initChildFromParent(that); | |
that._childVms.push( vm ); | |
return vm; | |
}; | |
this.getVm = function getVm(name) { | |
return ko.utils.arrayFirst(that._childVms(), function(item) { | |
return item._viewmodelName() === name; | |
}); | |
}; | |
}; | |
app.viewmodels.Parent = Parent; | |
// Parent-viewmodel handling. Use with borrowed constructor pattern | |
var Child = function Child() { | |
var that = this; | |
this._viewmodelName = function _viewmodelName() { | |
throw "Viewmodel does not implement _viewmodelName"; | |
}; | |
this._parentVm = null; | |
this.initChildFromParent = function initChildFromParent(parentVm) { | |
that._parentVm = parentVm; | |
if (typeof that.init === 'function') { | |
that.init(); | |
} | |
}; | |
}; | |
app.viewmodels.Child = Child; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment