KnockoutJS to ES2015/ES6

TODO: use the new Proxy for ko.observable
This commit is contained in:
djmaze 2020-10-04 21:58:13 +02:00
parent a0f8ac0dad
commit 72ed2b08b2
56 changed files with 941 additions and 1873 deletions

View file

@ -1,12 +1,12 @@
(function(undefined) {
(() => {
var componentLoadingOperationUniqueId = 0;
ko.bindingHandlers['component'] = {
'init': function(element, valueAccessor, ignored1, ignored2, bindingContext) {
'init': (element, valueAccessor, ignored1, ignored2, bindingContext) => {
var currentViewModel,
currentLoadingOperationId,
afterRenderSub,
disposeAssociatedComponentViewModel = function () {
disposeAssociatedComponentViewModel = () => {
var currentViewModelDispose = currentViewModel && currentViewModel['dispose'];
if (typeof currentViewModelDispose === 'function') {
currentViewModelDispose.call(currentViewModel);
@ -24,7 +24,7 @@
ko.virtualElements.emptyNode(element);
ko.utils.domNodeDisposal.addDisposeCallback(element, disposeAssociatedComponentViewModel);
ko.computed(function () {
ko.computed(() => {
var value = ko.utils.unwrapObservable(valueAccessor()),
componentName, componentParams;
@ -42,7 +42,7 @@
var asyncContext = ko.bindingEvent.startPossiblyAsyncContentBinding(element, bindingContext);
var loadingOperationId = currentLoadingOperationId = ++componentLoadingOperationUniqueId;
ko.components.get(componentName, function(componentDefinition) {
ko.components.get(componentName, componentDefinition => {
// If this is not the current load operation for this element, ignore it.
if (currentLoadingOperationId !== loadingOperationId) {
return;
@ -64,7 +64,7 @@
var componentViewModel = createViewModel(componentDefinition, componentParams, componentInfo),
childBindingContext = asyncContext['createChildContext'](componentViewModel, {
'extend': function(ctx) {
'extend': ctx => {
ctx['$component'] = componentViewModel;
ctx['$componentTemplateNodes'] = originalChildNodes;
}

View file

@ -1,7 +1,7 @@
(function (undefined) {
(() => {
// Overridable API for determining which component name applies to a given node. By overriding this,
// you can for example map specific tagNames to components that are not preregistered.
ko.components['getComponentNameForNode'] = function(node) {
ko.components['getComponentNameForNode'] = node => {
var tagNameLower = ko.utils.tagNameLower(node);
if (ko.components.isRegistered(tagNameLower)) {
// Try to determine that this node can be considered a *custom* element; see https://github.com/knockout/knockout/issues/1603
@ -11,7 +11,7 @@
}
};
ko.components.addBindingsForCustomElement = function(allBindings, node, bindingContext, valueAccessors) {
ko.components.addBindingsForCustomElement = (allBindings, node, bindingContext, valueAccessors) => {
// Determine if it's really a custom element matching a component
if (node.nodeType === 1) {
var componentName = ko.components['getComponentNameForNode'](node);
@ -27,7 +27,7 @@
var componentBindingValue = { 'name': componentName, 'params': getComponentParamsFromCustomElement(node, bindingContext) };
allBindings['component'] = valueAccessors
? function() { return componentBindingValue; }
? () => componentBindingValue
: componentBindingValue;
}
}
@ -42,10 +42,10 @@
if (paramsAttribute) {
var params = nativeBindingProviderInstance['parseBindingsString'](paramsAttribute, bindingContext, elem, { 'valueAccessors': true, 'bindingParams': true }),
rawParamComputedValues = ko.utils.objectMap(params, function(paramValue, paramName) {
return ko.computed(paramValue, null, { disposeWhenNodeIsRemoved: elem });
}),
result = ko.utils.objectMap(rawParamComputedValues, function(paramValueComputed, paramName) {
rawParamComputedValues = ko.utils.objectMap(params, paramValue =>
ko.computed(paramValue, null, { disposeWhenNodeIsRemoved: elem })
),
result = ko.utils.objectMap(rawParamComputedValues, paramValueComputed => {
var paramValue = paramValueComputed.peek();
// Does the evaluation of the parameter value unwrap any observables?
if (!paramValueComputed.isActive()) {
@ -58,12 +58,8 @@
// This means the component doesn't have to worry about multiple unwrapping. If the value is a
// writable observable, the computed will also be writable and pass the value on to the observable.
return ko.computed({
'read': function() {
return ko.utils.unwrapObservable(paramValueComputed());
},
'write': ko.isWriteableObservable(paramValue) && function(value) {
paramValueComputed()(value);
},
'read': () => ko.utils.unwrapObservable(paramValueComputed()),
'write': ko.isWriteableObservable(paramValue) && (value => paramValueComputed()(value)),
disposeWhenNodeIsRemoved: elem
});
}
@ -77,11 +73,10 @@
}
return result;
} else {
// For consistency, absence of a "params" attribute is treated the same as the presence of
// any empty one. Otherwise component viewmodels need special code to check whether or not
// 'params' or 'params.$raw' is null/undefined before reading subproperties, which is annoying.
return { '$raw': {} };
}
// For consistency, absence of a "params" attribute is treated the same as the presence of
// any empty one. Otherwise component viewmodels need special code to check whether or not
// 'params' or 'params.$raw' is null/undefined before reading subproperties, which is annoying.
return { '$raw': {} };
}
})();

View file

@ -1,4 +1,4 @@
(function(undefined) {
(() => {
// The default loader is responsible for two things:
// 1. Maintaining the default in-memory registry of component configuration objects
@ -12,7 +12,7 @@
var defaultConfigRegistry = {};
ko.components.register = function(componentName, config) {
ko.components.register = (componentName, config) => {
if (!config) {
throw new Error('Invalid configuration for ' + componentName);
}
@ -24,37 +24,35 @@
defaultConfigRegistry[componentName] = config;
};
ko.components.isRegistered = function(componentName) {
return Object.prototype.hasOwnProperty.call(defaultConfigRegistry, componentName);
};
ko.components.isRegistered = componentName =>
Object.prototype.hasOwnProperty.call(defaultConfigRegistry, componentName);
ko.components.unregister = function(componentName) {
ko.components.unregister = componentName => {
delete defaultConfigRegistry[componentName];
ko.components.clearCachedDefinition(componentName);
};
ko.components.defaultLoader = {
'getConfig': function(componentName, callback) {
'getConfig': (componentName, callback) => {
var result = ko.components.isRegistered(componentName)
? defaultConfigRegistry[componentName]
: null;
callback(result);
},
'loadComponent': function(componentName, config, callback) {
'loadComponent': (componentName, config, callback) => {
var errorCallback = makeErrorCallback(componentName);
possiblyGetConfigFromAmd(errorCallback, config, function(loadedConfig) {
resolveConfig(componentName, errorCallback, loadedConfig, callback);
});
possiblyGetConfigFromAmd(errorCallback, config, loadedConfig =>
resolveConfig(componentName, errorCallback, loadedConfig, callback)
);
},
'loadTemplate': function(componentName, templateConfig, callback) {
resolveTemplate(makeErrorCallback(componentName), templateConfig, callback);
},
'loadTemplate': (componentName, templateConfig, callback) =>
resolveTemplate(makeErrorCallback(componentName), templateConfig, callback)
,
'loadViewModel': function(componentName, viewModelConfig, callback) {
resolveViewModel(makeErrorCallback(componentName), viewModelConfig, callback);
}
'loadViewModel': (componentName, viewModelConfig, callback) =>
resolveViewModel(makeErrorCallback(componentName), viewModelConfig, callback)
};
var createViewModelKey = 'createViewModel';
@ -68,7 +66,7 @@
function resolveConfig(componentName, errorCallback, config, callback) {
var result = {},
makeCallBackWhenZero = 2,
tryIssueCallback = function() {
tryIssueCallback = () => {
if (--makeCallBackWhenZero === 0) {
callback(result);
}
@ -77,23 +75,23 @@
viewModelConfig = config['viewModel'];
if (templateConfig) {
possiblyGetConfigFromAmd(errorCallback, templateConfig, function(loadedConfig) {
ko.components._getFirstResultFromLoaders('loadTemplate', [componentName, loadedConfig], function(resolvedTemplate) {
possiblyGetConfigFromAmd(errorCallback, templateConfig, loadedConfig =>
ko.components._getFirstResultFromLoaders('loadTemplate', [componentName, loadedConfig], resolvedTemplate => {
result['template'] = resolvedTemplate;
tryIssueCallback();
});
});
})
);
} else {
tryIssueCallback();
}
if (viewModelConfig) {
possiblyGetConfigFromAmd(errorCallback, viewModelConfig, function(loadedConfig) {
ko.components._getFirstResultFromLoaders('loadViewModel', [componentName, loadedConfig], function(resolvedViewModel) {
possiblyGetConfigFromAmd(errorCallback, viewModelConfig, loadedConfig =>
ko.components._getFirstResultFromLoaders('loadViewModel', [componentName, loadedConfig], resolvedViewModel => {
result[createViewModelKey] = resolvedViewModel;
tryIssueCallback();
});
});
})
);
} else {
tryIssueCallback();
}
@ -106,12 +104,12 @@
} else if (templateConfig instanceof Array) {
// Assume already an array of DOM nodes - pass through unchanged
callback(templateConfig);
} else if (isDocumentFragment(templateConfig)) {
} else if (templateConfig instanceof DocumentFragment) {
// Document fragment - use its child nodes
callback(ko.utils.makeArray(templateConfig.childNodes));
} else if (templateConfig['element']) {
var element = templateConfig['element'];
if (isDomElement(element)) {
if (element instanceof HTMLElement) {
// Element instance - copy its child nodes
callback(cloneNodesFromTemplateSourceElement(element));
} else if (typeof element === 'string') {
@ -136,18 +134,14 @@
// By design, this does *not* supply componentInfo to the constructor, as the intent is that
// componentInfo contains non-viewmodel data (e.g., the component's element) that should only
// be used in factory functions, not viewmodel constructors.
callback(function (params /*, componentInfo */) {
return new viewModelConfig(params);
});
callback(params => new viewModelConfig(params));
} else if (typeof viewModelConfig[createViewModelKey] === 'function') {
// Already a factory function - use it as-is
callback(viewModelConfig[createViewModelKey]);
} else if ('instance' in viewModelConfig) {
// Fixed object instance - promote to createViewModel format for API consistency
var fixedInstance = viewModelConfig['instance'];
callback(function (params, componentInfo) {
return fixedInstance;
});
callback(() => fixedInstance);
} else if ('viewModel' in viewModelConfig) {
// Resolved AMD module whose value is of the form { viewModel: ... }
resolveViewModel(errorCallback, viewModelConfig['viewModel'], callback);
@ -160,34 +154,18 @@
if ('template' == ko.utils.tagNameLower(elemInstance)) {
// For browsers with proper <template> element support (i.e., where the .content property
// gives a document fragment), use that document fragment.
if (isDocumentFragment(elemInstance.content)) {
if (elemInstance.content instanceof DocumentFragment) {
return ko.utils.cloneNodes(elemInstance.content.childNodes);
}
}
throw 'Template Source Element not a <template>';
}
function isDomElement(obj) {
if (window['HTMLElement']) {
return obj instanceof HTMLElement;
} else {
return obj && obj.tagName && obj.nodeType === 1;
}
}
function isDocumentFragment(obj) {
if (window['DocumentFragment']) {
return obj instanceof DocumentFragment;
} else {
return obj && obj.nodeType === 11;
}
}
function possiblyGetConfigFromAmd(errorCallback, config, callback) {
if (typeof config['require'] === 'string') {
// The config is the value of an AMD module
if (amdRequire || window['require']) {
(amdRequire || window['require'])([config['require']], function (module) {
(amdRequire || window['require'])([config['require']], module => {
if (module && typeof module === 'object' && module.__esModule && module['default']) {
module = module['default'];
}
@ -202,8 +180,8 @@
}
function makeErrorCallback(componentName) {
return function (message) {
throw new Error('Component \'' + componentName + '\': ' + message);
return message => {
throw new Error('Component \'' + componentName + '\': ' + message)
};
}

View file

@ -1,20 +1,20 @@
(function(undefined) {
(() => {
var loadingSubscribablesCache = {}, // Tracks component loads that are currently in flight
loadedDefinitionsCache = {}; // Tracks component loads that have already completed
ko.components = {
get: function(componentName, callback) {
get: (componentName, callback) => {
var cachedDefinition = getObjectOwnProperty(loadedDefinitionsCache, componentName);
if (cachedDefinition) {
// It's already loaded and cached. Reuse the same definition object.
// Note that for API consistency, even cache hits complete asynchronously by default.
// You can bypass this by putting synchronous:true on your component config.
if (cachedDefinition.isSynchronousComponent) {
ko.dependencyDetection.ignore(function() { // See comment in loaderRegistryBehaviors.js for reasoning
callback(cachedDefinition.definition);
});
ko.dependencyDetection.ignore(() => // See comment in loaderRegistryBehaviors.js for reasoning
callback(cachedDefinition.definition)
);
} else {
ko.tasks.schedule(function() { callback(cachedDefinition.definition); });
ko.tasks.schedule(() => callback(cachedDefinition.definition) );
}
} else {
// Join the loading process that is already underway, or start a new one.
@ -22,9 +22,9 @@
}
},
clearCachedDefinition: function(componentName) {
delete loadedDefinitionsCache[componentName];
},
clearCachedDefinition: componentName =>
delete loadedDefinitionsCache[componentName]
,
_getFirstResultFromLoaders: getFirstResultFromLoaders
};
@ -41,7 +41,7 @@
subscribable = loadingSubscribablesCache[componentName] = new ko.subscribable();
subscribable.subscribe(callback);
beginLoadingComponent(componentName, function(definition, config) {
beginLoadingComponent(componentName, (definition, config) => {
var isSynchronousComponent = !!(config && config['synchronous']);
loadedDefinitionsCache[componentName] = { definition: definition, isSynchronousComponent: isSynchronousComponent };
delete loadingSubscribablesCache[componentName];
@ -57,9 +57,7 @@
// See comment in loaderRegistryBehaviors.js for reasoning
subscribable['notifySubscribers'](definition);
} else {
ko.tasks.schedule(function() {
subscribable['notifySubscribers'](definition);
});
ko.tasks.schedule(() => subscribable['notifySubscribers'](definition));
}
});
completedAsync = true;
@ -69,12 +67,12 @@
}
function beginLoadingComponent(componentName, callback) {
getFirstResultFromLoaders('getConfig', [componentName], function(config) {
getFirstResultFromLoaders('getConfig', [componentName], config => {
if (config) {
// We have a config, so now load its definition
getFirstResultFromLoaders('loadComponent', [componentName, config], function(definition) {
callback(definition, config);
});
getFirstResultFromLoaders('loadComponent', [componentName, config], definition =>
callback(definition, config)
);
} else {
// The component has no config - it's unknown to all the loaders.
// Note that this is not an error (e.g., a module loading error) - that would abort the