3 var componentLoadingOperationUniqueId = 0;
5 ko.bindingHandlers['component'] = {
6 'init': function(element, valueAccessor, ignored1, ignored2, bindingContext) {
8 currentLoadingOperationId,
9 disposeAssociatedComponentViewModel = function () {
10 var currentViewModelDispose = currentViewModel && currentViewModel['dispose'];
11 if (typeof currentViewModelDispose === 'function') {
12 currentViewModelDispose.call(currentViewModel);
14 currentViewModel = null;
15 // Any in-flight loading operation is no longer relevant, so make sure we ignore its completion
16 currentLoadingOperationId = null;
18 originalChildNodes = ko.utils.makeArray(ko.virtualElements.childNodes(element));
20 ko.utils.domNodeDisposal.addDisposeCallback(element, disposeAssociatedComponentViewModel);
22 ko.computed(function () {
23 var value = ko.utils.unwrapObservable(valueAccessor()),
24 componentName, componentParams;
26 if (typeof value === 'string') {
27 componentName = value;
29 componentName = ko.utils.unwrapObservable(value['name']);
30 componentParams = ko.utils.unwrapObservable(value['params']);
34 throw new Error('No component name specified');
37 var loadingOperationId = currentLoadingOperationId = ++componentLoadingOperationUniqueId;
38 ko.components.get(componentName, function(componentDefinition) {
39 // If this is not the current load operation for this element, ignore it.
40 if (currentLoadingOperationId !== loadingOperationId) {
44 // Clean up previous state
45 disposeAssociatedComponentViewModel();
47 // Instantiate and bind new component. Implicitly this cleans any old DOM nodes.
48 if (!componentDefinition) {
49 throw new Error('Unknown component \'' + componentName + '\'');
51 cloneTemplateIntoElement(componentName, componentDefinition, element);
52 var componentViewModel = createViewModel(componentDefinition, element, originalChildNodes, componentParams),
53 childBindingContext = bindingContext['createChildContext'](componentViewModel, /* dataItemAlias */ undefined, function(ctx) {
54 ctx['$component'] = componentViewModel;
55 ctx['$componentTemplateNodes'] = originalChildNodes;
57 currentViewModel = componentViewModel;
58 ko.applyBindingsToDescendants(childBindingContext, element);
60 }, null, { disposeWhenNodeIsRemoved: element });
62 return { 'controlsDescendantBindings': true };
66 ko.virtualElements.allowedBindings['component'] = true;
68 function cloneTemplateIntoElement(componentName, componentDefinition, element) {
69 var template = componentDefinition['template'];
71 throw new Error('Component \'' + componentName + '\' has no template');
74 var clonedNodesArray = ko.utils.cloneNodes(template);
75 ko.virtualElements.setDomNodeChildren(element, clonedNodesArray);
78 function createViewModel(componentDefinition, element, originalChildNodes, componentParams) {
79 var componentViewModelFactory = componentDefinition['createViewModel'];
80 return componentViewModelFactory
81 ? componentViewModelFactory.call(componentDefinition, componentParams, { 'element': element, 'templateNodes': originalChildNodes })
82 : componentParams; // Template-only component