knockout-decorators
Advanced tools
Comparing version 0.8.0 to 0.9.0
/// <reference types="knockout" /> | ||
export interface ComponentConstructor { | ||
new (params?: any, element?: Node, templateNodes?: Node[]): any; | ||
} | ||
export declare type ComponentDecorator = (constructor: ComponentConstructor) => void; | ||
export declare type TemplateConfig = (string | Node[] | DocumentFragment | { | ||
require: string; | ||
} | { | ||
element: string | Node; | ||
}); | ||
/** | ||
* Register Knockout component by decorating ViewModel class | ||
* Property decorator that creates hidden (shallow) ko.observable with ES6 getter and setter for it | ||
* If initialized by Array then hidden (shallow) ko.observableArray will be created | ||
*/ | ||
export declare function component(name: string, options?: Object): ComponentDecorator; | ||
export declare function component(name: string, template: TemplateConfig, options?: Object): ComponentDecorator; | ||
export declare function component(name: string, template: TemplateConfig, styles: string | string[], options?: Object): ComponentDecorator; | ||
export declare function observable(prototype: Object, key: string | symbol): void; | ||
/** | ||
* Property decorator that creates hidden ko.observable with ES6 getter and setter for it | ||
* Property decorator that creates hidden (deep) ko.observable with ES6 getter and setter for it | ||
* If initialized by Array then hidden (deep) ko.observableArray will be created | ||
*/ | ||
export declare function observable(prototype: Object, key: string | symbol): void; | ||
export declare function reactive(prototype: Object, key: string | symbol): void; | ||
/** | ||
@@ -30,3 +21,3 @@ * Accessor decorator that wraps ES6 getter to hidden ko.pureComputed | ||
/** | ||
* Property decorator that creates hidden ko.observableArray with ES6 getter and setter for it | ||
* Property decorator that creates hidden (shallow) ko.observableArray with ES6 getter and setter for it | ||
*/ | ||
@@ -47,2 +38,11 @@ export declare function observableArray(prototype: Object, key: string | symbol): void; | ||
subscribe(callback: (val: any[]) => void, callbackTarget: any, event: string): KnockoutSubscription; | ||
/** | ||
* Run mutator function that can write to array at some index (`array[index] = value;`) | ||
* Then notify about observableArray changes | ||
*/ | ||
mutate(mutator: (arrayValue: T[]) => void): void; | ||
/** | ||
* Replace value at some index and return old value | ||
*/ | ||
set(index: number, value: T): T; | ||
} | ||
@@ -53,4 +53,28 @@ /** | ||
export declare function extend(extenders: Object): PropertyDecorator; | ||
/** | ||
* Apply extenders to decorated @observable | ||
*/ | ||
export declare function extend(extendersFactory: () => Object): PropertyDecorator; | ||
export interface ComponentConstructor { | ||
new (params?: any, element?: Node, templateNodes?: Node[]): any; | ||
} | ||
export declare type ComponentDecorator = (constructor: ComponentConstructor) => void; | ||
export declare type TemplateConfig = (string | Node[] | DocumentFragment | { | ||
require: string; | ||
} | { | ||
element: string | Node; | ||
}); | ||
/** | ||
* Register Knockout component by decorating ViewModel class | ||
*/ | ||
export declare function component(name: string, options?: Object): ComponentDecorator; | ||
/** | ||
* Register Knockout component by decorating ViewModel class | ||
*/ | ||
export declare function component(name: string, template: TemplateConfig, options?: Object): ComponentDecorator; | ||
/** | ||
* Register Knockout component by decorating ViewModel class | ||
*/ | ||
export declare function component(name: string, template: TemplateConfig, styles: string | string[], options?: Object): ComponentDecorator; | ||
/** | ||
* Like https://github.com/jayphelps/core-decorators.js @autobind but less smart and complex | ||
@@ -61,5 +85,12 @@ * Do NOT use with ES6 inheritance! | ||
/** | ||
* Subscribe callback to dependency changes | ||
* Define hidden ko.subscribable, that notifies subscribers when decorated method is invoked | ||
*/ | ||
export declare function subscribe<T>(getDependency: () => T, callback: (value: T) => void, options?: { | ||
export declare function event(prototype: Object, key: string | symbol): void; | ||
export declare type EventProperty = Function & { | ||
subscribe(callback: Function): KnockoutSubscription; | ||
}; | ||
/** | ||
* Subscribe callback to `@observable` or `@computed` dependency changes or to some `@event` | ||
*/ | ||
export declare function subscribe<T>(dependencyOrEvent: () => T, callback: (value: T) => void, options?: { | ||
once?: boolean; | ||
@@ -69,6 +100,27 @@ event?: string; | ||
/** | ||
* Subscribe callback to some `@event` | ||
*/ | ||
export declare function subscribe<T>(event: (arg: T) => void, callback: (arg: T) => void, options?: { | ||
once?: boolean; | ||
}): KnockoutSubscription; | ||
/** | ||
* Subscribe callback to some `@event` | ||
*/ | ||
export declare function subscribe<T1, T2>(event: (arg1: T1, arg2: T2) => void, callback: (arg1: T1, arg2: T2) => void, options?: { | ||
once?: boolean; | ||
}): KnockoutSubscription; | ||
/** | ||
* Subscribe callback to some `@event` | ||
*/ | ||
export declare function subscribe<T1, T2, T3>(event: (arg1: T1, arg2: T2, arg3: T3, ...args: any[]) => void, callback: (arg1: T1, arg2: T2, arg3: T3, ...args: any[]) => void, options?: { | ||
once?: boolean; | ||
}): KnockoutSubscription; | ||
/** | ||
* Get internal ko.observable() for object property decodated by @observable | ||
*/ | ||
export declare function unwrap(instance: Object, key: string | symbol): any; | ||
/** | ||
* Get internal ko.observable() for object property decodated by @observable | ||
*/ | ||
export declare function unwrap<T>(instance: Object, key: string | symbol): KnockoutObservable<T>; | ||
export as namespace KnockoutDecorators; | ||
export as namespace KnockoutDecorators; |
@@ -7,136 +7,98 @@ (function (global, factory) { | ||
/** | ||
* Copyright (c) 2016 Dmitry Panyushkin | ||
* Available under MIT license | ||
*/ | ||
var assign = ko.utils.extend; | ||
var extendObject = ko.utils.extend; | ||
var objectForEach = ko.utils.objectForEach; | ||
var defProp = Object.defineProperty.bind(Object); | ||
var getDescriptor = Object.getOwnPropertyDescriptor.bind(Object); | ||
var defineProperty = Object.defineProperty.bind(Object); | ||
var getPrototypeOf = Object.getPrototypeOf.bind(Object); | ||
var getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor.bind(Object); | ||
var hasOwnProperty = Function.prototype.call.bind(Object.prototype.hasOwnProperty); | ||
var slice = Function.prototype.call.bind(Array.prototype.slice); | ||
/** | ||
* Register Knockout component by decorating ViewModel class | ||
* @param name { String } Name of component | ||
* @param template { Any } Knockout template definition | ||
* @param styles { Any } Ignored parameter (used for `require()` styles by webpack etc.) | ||
* @param options { Object } Another options that passed directly to `ko.components.register()` | ||
*/ | ||
function component(name, template, styles, options) { | ||
if (options === void 0) { | ||
if (styles === void 0) { | ||
if (typeof template === "object" | ||
&& template.constructor === Object | ||
&& !("require" in template) | ||
&& !("element" in template)) { | ||
options = template; | ||
template = void 0; | ||
} | ||
} | ||
else if (typeof styles === "object") { | ||
options = styles; | ||
styles = void 0; | ||
} | ||
var arraySlice = Function.prototype.call.bind(Array.prototype.slice); | ||
var EXTENDERS_KEY = typeof Symbol !== "undefined" | ||
? Symbol("ko_decorators_extenders") : "__ko_decorators_extenders__"; | ||
function applyExtenders(instance, key, target) { | ||
var dictionary = instance[EXTENDERS_KEY]; | ||
var extenders = dictionary && dictionary[key]; | ||
if (extenders) { | ||
extenders.forEach(function (extender) { | ||
var koExtender = extender instanceof Function | ||
? extender.call(instance) : extender; | ||
target = target.extend(koExtender); | ||
}); | ||
} | ||
return function (constructor) { | ||
ko.components.register(name, assign({ | ||
viewModel: constructor.length < 2 ? constructor : { | ||
createViewModel: function (params, _a) { | ||
var element = _a.element, templateNodes = _a.templateNodes; | ||
return new constructor(params, element, templateNodes); | ||
} | ||
}, | ||
template: template || "<!---->", | ||
synchronous: true, | ||
}, options)); | ||
}; | ||
return target; | ||
} | ||
/*===========================================================================*/ | ||
/** | ||
* Property decorator that creates hidden ko.observable with ES6 getter and setter for it | ||
*/ | ||
function observable$1(prototype, key) { | ||
defProp(prototype, key, { | ||
configurable: true, | ||
get: function () { | ||
var observable$$1 = applyExtenders(this, key, ko.observable()); | ||
defProp(this, key, { | ||
configurable: true, | ||
enumerable: true, | ||
get: observable$$1, | ||
set: observable$$1, | ||
}); | ||
return observable$$1(); | ||
}, | ||
set: function (value) { | ||
var observable$$1 = applyExtenders(this, key, ko.observable()); | ||
defProp(this, key, { | ||
configurable: true, | ||
enumerable: true, | ||
get: observable$$1, | ||
set: observable$$1, | ||
}); | ||
observable$$1(value); | ||
}, | ||
}); | ||
function defineExtenders(prototype, key, extendersOrFactory) { | ||
var dictionary = prototype[EXTENDERS_KEY]; | ||
// if there is no ExtendersDictionary or ExtendersDictionary lives in base class prototype | ||
if (!hasOwnProperty(prototype, EXTENDERS_KEY)) { | ||
// clone ExtendersDictionary from base class prototype or create new ExtendersDictionary | ||
prototype[EXTENDERS_KEY] = dictionary = extendObject({}, dictionary); | ||
// clone Extenders arrays for each property key | ||
objectForEach(dictionary, function (key, extenders) { | ||
dictionary[key] = extenders.slice(); | ||
}); | ||
} | ||
// get existing Extenders array or create new array | ||
var extenders = dictionary[key] || (dictionary[key] = []); | ||
// add new Extenders | ||
extenders.push(extendersOrFactory); | ||
} | ||
/*===========================================================================*/ | ||
/** | ||
* Accessor decorator that wraps ES6 getter to hidden ko.pureComputed | ||
* | ||
* Setter is not wrapped to hidden ko.pureComputed and stays unchanged | ||
* | ||
* But we can still extend getter @computed by extenders like { rateLimit: 500 } | ||
* Copyright (c) 2016-2017 Dmitry Panyushkin | ||
* Available under MIT license | ||
*/ | ||
function computed$1(prototype, key, desc) { | ||
var _a = desc || (desc = getDescriptor(prototype, key)), get = _a.get, set = _a.set; | ||
desc.get = function () { | ||
var computed$$1 = applyExtenders(this, key, ko.pureComputed(get, this)); | ||
defProp(this, key, { | ||
configurable: true, | ||
get: computed$$1, | ||
set: set | ||
}); | ||
return computed$$1(); | ||
}; | ||
return desc; | ||
// TODO: make @computed extendable (by @extend decorator) | ||
} | ||
var arrayMethods = ["pop", "push", "reverse", "shift", "sort", "splice", "unshift"]; | ||
var observableArrayMethods = ["remove", "removeAll", "destroy", "destroyAll", "replace", "subscribe"]; | ||
function defObservableArray(instance, key) { | ||
var deepArrayMethods = ["pop", "reverse", "shift", "sort"]; | ||
var allArrayMethods = deepArrayMethods.concat(["push", "splice", "unshift"]); | ||
var deepObservableArrayMethods = ["remove", "removeAll", "destroy", "destroyAll", "replace", "subscribe"]; | ||
var allObservableArrayMethods = deepObservableArrayMethods.concat(["replace"]); | ||
var allMethods = allArrayMethods.concat(allObservableArrayMethods, ["mutate", "set"]); | ||
function defineObservableArray(instance, key, value, deep) { | ||
var obsArray = applyExtenders(instance, key, ko.observableArray()); | ||
var insideObsArray = false; | ||
defProp(instance, key, { | ||
defineProperty(instance, key, { | ||
configurable: true, | ||
enumerable: true, | ||
get: obsArray, | ||
set: function (value) { | ||
var lastValue = obsArray.peek(); | ||
// if we got new value | ||
if (lastValue !== value) { | ||
if (Array.isArray(lastValue)) { | ||
// if lastValue array methods were already patched | ||
if (hasOwnProperty(lastValue, "subscribe")) { | ||
// clear patched array methods on lastValue (see unit tests) | ||
clearArrayMethods(lastValue); | ||
} | ||
set: setter, | ||
}); | ||
setter(value); | ||
function setter(newValue) { | ||
var lastValue = obsArray.peek(); | ||
// if we got new value | ||
if (lastValue !== newValue) { | ||
if (Array.isArray(lastValue)) { | ||
// if lastValue array methods were already patched | ||
if (hasOwnProperty(lastValue, "mutate")) { | ||
// clear patched array methods on lastValue (see unit tests) | ||
allMethods.forEach(function (fnName) { | ||
delete lastValue[fnName]; | ||
}); | ||
} | ||
if (Array.isArray(value)) { | ||
// if new value array methods were already connected with another @observableArray | ||
if (hasOwnProperty(value, "subscribe")) { | ||
// clone new value to prevent corruption of another @observableArray (see unit tests) | ||
value = slice(value); | ||
} | ||
if (Array.isArray(newValue)) { | ||
// if new value array methods were already connected with another @observable | ||
if (hasOwnProperty(newValue, "mutate")) { | ||
// clone new value to prevent corruption of another @observable (see unit tests) | ||
newValue = newValue.slice(); | ||
} | ||
// if deep option is set | ||
if (deep) { | ||
// make all array items reactive | ||
for (var i = 0; i < newValue.length; ++i) { | ||
newValue[i] = prepareReactiveValue(newValue[i]); | ||
} | ||
// call ko.observableArray.fn[fnName] instead of Array.prototype[fnName] | ||
patchArrayMethods(value); | ||
} | ||
// call ko.observableArray.fn[fnName] instead of Array.prototype[fnName] | ||
patchArrayMethods(newValue); | ||
} | ||
insideObsArray = true; | ||
obsArray(value); | ||
insideObsArray = false; | ||
} | ||
}); | ||
// update obsArray contents | ||
insideObsArray = true; | ||
obsArray(newValue); | ||
insideObsArray = false; | ||
} | ||
function patchArrayMethods(array) { | ||
arrayMethods.forEach(function (fnName) { return defProp(array, fnName, { | ||
var arrayMethods = deep ? deepArrayMethods : allArrayMethods; | ||
arrayMethods.forEach(function (fnName) { return defineProperty(array, fnName, { | ||
configurable: true, | ||
@@ -153,3 +115,4 @@ value: function () { | ||
}); }); | ||
observableArrayMethods.forEach(function (fnName) { return defProp(array, fnName, { | ||
var observableArrayMethods = deep ? deepObservableArrayMethods : allObservableArrayMethods; | ||
observableArrayMethods.forEach(function (fnName) { return defineProperty(array, fnName, { | ||
configurable: true, | ||
@@ -163,61 +126,282 @@ value: function () { | ||
}); }); | ||
if (deep) { | ||
defineProperty(array, "push", { | ||
configurable: true, | ||
value: function () { | ||
if (insideObsArray) { | ||
return Array.prototype.push.apply(array, arguments); | ||
} | ||
var args = arraySlice(arguments); | ||
for (var i = 0; i < args.length; ++i) { | ||
args[i] = prepareReactiveValue(args[i]); | ||
} | ||
insideObsArray = true; | ||
var result = obsArray.push.apply(obsArray, args); | ||
insideObsArray = false; | ||
return result; | ||
} | ||
}); | ||
defineProperty(array, "unshift", { | ||
configurable: true, | ||
value: function () { | ||
if (insideObsArray) { | ||
return Array.prototype.unshift.apply(array, arguments); | ||
} | ||
var args = arraySlice(arguments); | ||
for (var i = 0; i < args.length; ++i) { | ||
args[i] = prepareReactiveValue(args[i]); | ||
} | ||
insideObsArray = true; | ||
var result = obsArray.unshift.apply(obsArray, args); | ||
insideObsArray = false; | ||
return result; | ||
} | ||
}); | ||
defineProperty(array, "splice", { | ||
configurable: true, | ||
value: function () { | ||
if (insideObsArray) { | ||
return Array.prototype.splice.apply(array, arguments); | ||
} | ||
var result; | ||
insideObsArray = true; | ||
switch (arguments.length) { | ||
case 0: | ||
case 1: | ||
case 2: { | ||
result = obsArray.splice.apply(obsArray, arguments); | ||
break; | ||
} | ||
case 3: { | ||
result = obsArray.splice(arguments[0], arguments[1], prepareReactiveValue(arguments[2])); | ||
break; | ||
} | ||
default: { | ||
var args = arraySlice(arguments); | ||
for (var i = 2; i < args.length; ++i) { | ||
args[i] = prepareReactiveValue(args[i]); | ||
} | ||
result = obsArray.splice.apply(obsArray, arguments); | ||
break; | ||
} | ||
} | ||
insideObsArray = false; | ||
return result; | ||
} | ||
}); | ||
defineProperty(array, "replace", { | ||
configurable: true, | ||
value: function (oldItem, newItem) { | ||
insideObsArray = true; | ||
var result = obsArray.replace(oldItem, prepareReactiveValue(newItem)); | ||
insideObsArray = false; | ||
return result; | ||
} | ||
}); | ||
defineProperty(array, "mutate", { | ||
configurable: true, | ||
value: function (mutator) { | ||
var array = obsArray.peek(); | ||
obsArray.valueWillMutate(); | ||
mutator(array); | ||
for (var i = 0; i < array.length; ++i) { | ||
array[i] = prepareReactiveValue(array[i]); | ||
} | ||
obsArray.valueHasMutated(); | ||
} | ||
}); | ||
defineProperty(array, "set", { | ||
configurable: true, | ||
value: function (index, newItem) { | ||
return obsArray.splice(index, 1, prepareReactiveValue(newItem))[0]; | ||
} | ||
}); | ||
} | ||
else { | ||
defineProperty(array, "mutate", { | ||
configurable: true, | ||
value: function (mutator) { | ||
obsArray.valueWillMutate(); | ||
mutator(obsArray.peek()); | ||
obsArray.valueHasMutated(); | ||
} | ||
}); | ||
defineProperty(array, "set", { | ||
configurable: true, | ||
value: function (index, newItem) { | ||
return obsArray.splice(index, 1, newItem)[0]; | ||
} | ||
}); | ||
} | ||
} | ||
} | ||
// moved outside of defObservableArray function to prevent creation of unnecessary closure | ||
function clearArrayMethods(array) { | ||
arrayMethods.forEach(function (fnName) { | ||
delete array[fnName]; | ||
/** | ||
* Copyright (c) 2016-2017 Dmitry Panyushkin | ||
* Available under MIT license | ||
*/ | ||
function defineObservableProperty(instance, key, value, deep) { | ||
var observable$$1 = applyExtenders(instance, key, ko.observable()); | ||
var setter = observable$$1; | ||
if (deep) { | ||
setter = function (newValue) { | ||
observable$$1(prepareReactiveValue(newValue)); | ||
}; | ||
} | ||
defineProperty(instance, key, { | ||
configurable: true, | ||
enumerable: true, | ||
get: observable$$1, | ||
set: setter, | ||
}); | ||
observableArrayMethods.forEach(function (fnName) { | ||
delete array[fnName]; | ||
setter(value); | ||
} | ||
function prepareReactiveValue(value) { | ||
if (typeof value === "object") { | ||
if (Array.isArray(value) || value === null) { | ||
// value is Array or null | ||
return value; | ||
} | ||
else if (value.constructor === Object) { | ||
// value is plain Object | ||
return prepareReactiveObject(value); | ||
} | ||
else if (hasOwnProperty(value, "constructor")) { | ||
var prototype = getPrototypeOf(value); | ||
if (prototype === Object.prototype || prototype === null) { | ||
// value is plain Object | ||
return prepareReactiveObject(value); | ||
} | ||
} | ||
} | ||
// value is primitive, function or class instance | ||
return value; | ||
} | ||
var REACTIVE_KEY = typeof Symbol !== "undefined" | ||
? Symbol("ko_decorators_reactive") : "__ko_decorators_reactive__"; | ||
function prepareReactiveObject(instance) { | ||
if (!hasOwnProperty(instance, REACTIVE_KEY)) { | ||
// mark instance as ObservableObject | ||
defineProperty(instance, REACTIVE_KEY, { | ||
configurable: true, | ||
value: void 0, | ||
}); | ||
// define deep observable properties | ||
objectForEach(instance, function (key, value) { | ||
if (Array.isArray(value)) { | ||
defineObservableArray(instance, key, value, true); | ||
} | ||
else { | ||
defineObservableProperty(instance, key, value, true); | ||
} | ||
}); | ||
} | ||
return instance; | ||
} | ||
/** | ||
* Copyright (c) 2016-2017 Dmitry Panyushkin | ||
* Available under MIT license | ||
*/ | ||
function defineEventProperty(instance, key) { | ||
var subscribable$$1 = new ko.subscribable(); | ||
var event = function () { | ||
var eventArgs = arraySlice(arguments); | ||
subscribable$$1.notifySubscribers(eventArgs); | ||
}; | ||
event.subscribe = function (callback) { | ||
return subscribable$$1.subscribe(function (eventArgs) { | ||
callback.apply(null, eventArgs); | ||
}); | ||
}; | ||
defineProperty(instance, key, { | ||
configurable: true, | ||
value: event, | ||
}); | ||
return event; | ||
} | ||
/** | ||
* Property decorator that creates hidden ko.observableArray with ES6 getter and setter for it | ||
* Copyright (c) 2016-2017 Dmitry Panyushkin | ||
* Available under MIT license | ||
* Version: 0.9.0 | ||
*/ | ||
function observableArray$1(prototype, key) { | ||
defProp(prototype, key, { | ||
/** | ||
* Property decorator that creates hidden (shallow) ko.observable with ES6 getter and setter for it | ||
* If initialized by Array then hidden (shallow) ko.observableArray will be created | ||
*/ | ||
function observable$1(prototype, key) { | ||
defineProperty(prototype, key, { | ||
configurable: true, | ||
get: function () { | ||
defObservableArray(this, key); | ||
this[key] = []; | ||
return this[key]; | ||
throw new Error("@observable property '" + key.toString() + "' was not initialized"); | ||
}, | ||
set: function (value) { | ||
defObservableArray(this, key); | ||
this[key] = value; | ||
if (Array.isArray(value)) { | ||
defineObservableArray(this, key, value, false); | ||
} | ||
else { | ||
defineObservableProperty(this, key, value, false); | ||
} | ||
}, | ||
}); | ||
} | ||
/*===========================================================================*/ | ||
var DECORATORS_KEY = typeof Symbol !== "undefined" | ||
? Symbol("ko_decorators") : "__ko_decorators__"; | ||
function getOrCreateMetaData(prototype) { | ||
var metaData = prototype[DECORATORS_KEY]; | ||
if (!prototype.hasOwnProperty(DECORATORS_KEY)) { | ||
// clone MetaData from base class prototype | ||
prototype[DECORATORS_KEY] = metaData = assign({}, metaData); | ||
// clone extenders arrays for each property key | ||
objectForEach(metaData, function (key, extenders) { | ||
metaData[key] = extenders.slice(); | ||
}); | ||
} | ||
return metaData; | ||
/*---------------------------------------------------------------------------*/ | ||
/** | ||
* Property decorator that creates hidden (deep) ko.observable with ES6 getter and setter for it | ||
* If initialized by Array then hidden (deep) ko.observableArray will be created | ||
*/ | ||
function reactive(prototype, key) { | ||
defineProperty(prototype, key, { | ||
configurable: true, | ||
get: function () { | ||
throw new Error("@reactive property '" + key.toString() + "' was not initialized"); | ||
}, | ||
set: function (value) { | ||
if (Array.isArray(value)) { | ||
defineObservableArray(this, key, value, true); | ||
} | ||
else { | ||
defineObservableProperty(this, key, value, true); | ||
} | ||
}, | ||
}); | ||
} | ||
function getOrCreateExtenders(metaData, key) { | ||
return metaData[key] || (metaData[key] = []); | ||
} | ||
function applyExtenders(instance, key, target) { | ||
var metaData = instance[DECORATORS_KEY]; | ||
var extenders = metaData && metaData[key]; | ||
if (extenders) { | ||
extenders.forEach(function (extender) { | ||
var koExtender = extender instanceof Function | ||
? extender.call(instance) : extender; | ||
target = target.extend(koExtender); | ||
/*---------------------------------------------------------------------------*/ | ||
/** | ||
* Accessor decorator that wraps ES6 getter to hidden ko.pureComputed | ||
* | ||
* Setter is not wrapped to hidden ko.pureComputed and stays unchanged | ||
* | ||
* But we can still extend getter @computed by extenders like { rateLimit: 500 } | ||
*/ | ||
function computed$1(prototype, key, desc) { | ||
var _a = desc || (desc = getOwnPropertyDescriptor(prototype, key)), get = _a.get, set = _a.set; | ||
desc.get = function () { | ||
var computed$$1 = applyExtenders(this, key, ko.pureComputed(get, this)); | ||
defineProperty(this, key, { | ||
configurable: true, | ||
get: computed$$1, | ||
set: set, | ||
}); | ||
} | ||
return target; | ||
return computed$$1(); | ||
}; | ||
return desc; | ||
} | ||
/*---------------------------------------------------------------------------*/ | ||
/** | ||
* Property decorator that creates hidden (shallow) ko.observableArray with ES6 getter and setter for it | ||
*/ | ||
function observableArray$1(prototype, key) { | ||
defineProperty(prototype, key, { | ||
configurable: true, | ||
get: function () { | ||
throw new Error("@observableArray property '" + key.toString() + "' was not initialized"); | ||
}, | ||
set: function (value) { | ||
defineObservableArray(this, key, value, false); | ||
}, | ||
}); | ||
} | ||
/** | ||
* Apply extenders to decorated @observable | ||
@@ -228,9 +412,43 @@ * @extendersOrFactory { Object | Function } Knockout extenders definition or factory that produces definition | ||
return function (prototype, key) { | ||
var medaData = getOrCreateMetaData(prototype); | ||
var extenders = getOrCreateExtenders(medaData, key); | ||
extenders.push(extendersOrFactory); | ||
defineExtenders(prototype, key, extendersOrFactory); | ||
}; | ||
} | ||
/*===========================================================================*/ | ||
/** | ||
* Register Knockout component by decorating ViewModel class | ||
* @param name { String } Name of component | ||
* @param template { Any } Knockout template definition | ||
* @param styles { Any } Ignored parameter (used for `require()` styles by webpack etc.) | ||
* @param options { Object } Another options that passed directly to `ko.components.register()` | ||
*/ | ||
function component(name, template, styles, options) { | ||
if (options === void 0) { | ||
if (styles === void 0) { | ||
if (typeof template === "object" | ||
&& template.constructor === Object | ||
&& !("require" in template) | ||
&& !("element" in template)) { | ||
options = template; | ||
template = void 0; | ||
} | ||
} | ||
else if (typeof styles === "object") { | ||
options = styles; | ||
styles = void 0; | ||
} | ||
} | ||
return function (constructor) { | ||
ko.components.register(name, extendObject({ | ||
viewModel: constructor.length < 2 ? constructor : { | ||
createViewModel: function (params, _a) { | ||
var element = _a.element, templateNodes = _a.templateNodes; | ||
return new constructor(params, element, templateNodes); | ||
} | ||
}, | ||
template: template || "<!---->", | ||
synchronous: true, | ||
}, options)); | ||
}; | ||
} | ||
/*---------------------------------------------------------------------------*/ | ||
/** | ||
* Like https://github.com/jayphelps/core-decorators.js @autobind but less smart and complex | ||
@@ -240,3 +458,3 @@ * Do NOT use with ES6 inheritance! | ||
function autobind(prototype, key, desc) { | ||
var _a = desc || (desc = getDescriptor(prototype, key)), value = _a.value, configurable = _a.configurable, enumerable = _a.enumerable; | ||
var _a = desc || (desc = getOwnPropertyDescriptor(prototype, key)), value = _a.value, configurable = _a.configurable, enumerable = _a.enumerable; | ||
return { | ||
@@ -250,3 +468,3 @@ configurable: configurable, | ||
var bound = value.bind(this); | ||
defProp(this, key, { | ||
defineProperty(this, key, { | ||
configurable: true, | ||
@@ -259,23 +477,56 @@ value: bound, | ||
} | ||
/*===========================================================================*/ | ||
/*---------------------------------------------------------------------------*/ | ||
/** | ||
* Subscribe callback to dependency changes | ||
* Define hidden ko.subscribable, that notifies subscribers when decorated method is invoked | ||
*/ | ||
function subscribe(getDependency, callback, options) { | ||
function event(prototype, key) { | ||
defineProperty(prototype, key, { | ||
configurable: true, | ||
get: function () { | ||
return defineEventProperty(this, key); | ||
}, | ||
}); | ||
} | ||
/** | ||
* Subscribe callback to `@observable` or `@computed` dependency changes or to some `@event` | ||
*/ | ||
function subscribe(dependencyOrEvent, callback, options) { | ||
var once = options && options.once || false; | ||
var event = options && options.event || "change"; | ||
var dependency = ko.computed(getDependency); | ||
var subscription = dependency.subscribe(callback, null, event); | ||
var originalDispose = subscription.dispose; | ||
// dispose hidden computed with subscription | ||
subscription.dispose = function () { | ||
originalDispose.call(this); | ||
dependency.dispose(); | ||
}; | ||
if (once) { | ||
dependency.subscribe(function () { | ||
subscription.dispose(); | ||
}); | ||
if (hasOwnProperty(dependencyOrEvent, "subscribe")) { | ||
// subscribe to @event | ||
var event_1 = dependencyOrEvent; | ||
if (once) { | ||
var subscription_1 = event_1.subscribe(function () { | ||
subscription_1.dispose(); | ||
callback.apply(null, arguments); | ||
}); | ||
return subscription_1; | ||
} | ||
else { | ||
return event_1.subscribe(callback); | ||
} | ||
} | ||
return subscription; | ||
else { | ||
// subscribe to @observable, @reactive or @computed | ||
var event_2 = options && options.event || "change"; | ||
var computed_1 = ko.computed(dependencyOrEvent); | ||
var handler = void 0; | ||
if (once) { | ||
handler = function () { | ||
subscription_2.dispose(); | ||
callback.apply(null, arguments); | ||
}; | ||
} | ||
else { | ||
handler = callback; | ||
} | ||
var subscription_2 = computed_1.subscribe(handler, null, event_2); | ||
var originalDispose_1 = subscription_2.dispose; | ||
// dispose hidden computed with subscription | ||
subscription_2.dispose = function () { | ||
originalDispose_1.call(this); | ||
computed_1.dispose(); | ||
}; | ||
return subscription_2; | ||
} | ||
} | ||
@@ -290,11 +541,13 @@ /** | ||
} | ||
return getDescriptor(instance, key).get; | ||
return getOwnPropertyDescriptor(instance, key).get; | ||
} | ||
exports.component = component; | ||
exports.observable = observable$1; | ||
exports.reactive = reactive; | ||
exports.computed = computed$1; | ||
exports.observableArray = observableArray$1; | ||
exports.extend = extend; | ||
exports.component = component; | ||
exports.autobind = autobind; | ||
exports.event = event; | ||
exports.subscribe = subscribe; | ||
@@ -301,0 +554,0 @@ exports.unwrap = unwrap; |
@@ -1,2 +0,2 @@ | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("knockout")):"function"==typeof define&&define.amd?define(["exports","knockout"],t):t(e.KnockoutDecorators=e.KnockoutDecorators||{},e.ko)}(this,function(e,t){"use strict";function n(e,n,r,o){return void 0===o&&(void 0===r?"object"!=typeof n||n.constructor!==Object||"require"in n||"element"in n||(o=n,n=void 0):"object"==typeof r&&(o=r,r=void 0)),function(r){t.components.register(e,v({viewModel:r.length<2?r:{createViewModel:function(e,t){var n=t.element,o=t.templateNodes;return new r(e,n,o)}},template:n||"<!---->",synchronous:!0},o))}}function r(e,n){y(e,n,{configurable:!0,get:function(){var e=f(this,n,t.observable());return y(this,n,{configurable:!0,enumerable:!0,get:e,set:e}),e()},set:function(e){var r=f(this,n,t.observable());y(this,n,{configurable:!0,enumerable:!0,get:r,set:r}),r(e)}})}function o(e,n,r){var o=r||(r=g(e,n)),i=o.get,u=o.set;return r.get=function(){var e=f(this,n,t.pureComputed(i,this));return y(this,n,{configurable:!0,get:e,set:u}),e()},r}function i(e,n){function r(e){k.forEach(function(t){return y(e,t,{configurable:!0,value:function(){if(i)return Array.prototype[t].apply(e,arguments);i=!0;var n=o[t].apply(o,arguments);return i=!1,n}})}),A.forEach(function(t){return y(e,t,{configurable:!0,value:function(){i=!0;var e=o[t].apply(o,arguments);return i=!1,e}})})}var o=f(e,n,t.observableArray()),i=!1;y(e,n,{configurable:!0,enumerable:!0,get:o,set:function(e){var t=o.peek();t!==e&&(Array.isArray(t)&&m(t,"subscribe")&&u(t),Array.isArray(e)&&(m(e,"subscribe")&&(e=j(e)),r(e))),i=!0,o(e),i=!1}})}function u(e){k.forEach(function(t){delete e[t]}),A.forEach(function(t){delete e[t]})}function c(e,t){y(e,t,{configurable:!0,get:function(){return i(this,t),this[t]=[],this[t]},set:function(e){i(this,t),this[t]=e}})}function s(e){var t=e[O];return e.hasOwnProperty(O)||(e[O]=t=v({},t),h(t,function(e,n){t[e]=n.slice()})),t}function a(e,t){return e[t]||(e[t]=[])}function f(e,t,n){var r=e[O],o=r&&r[t];return o&&o.forEach(function(t){var r=t instanceof Function?t.call(e):t;n=n.extend(r)}),n}function l(e){return function(t,n){var r=s(t),o=a(r,n);o.push(e)}}function b(e,t,n){var r=n||(n=g(e,t)),o=r.value,i=r.configurable,u=r.enumerable;return{configurable:i,enumerable:u,get:function(){if(this===e)return o;var n=o.bind(this);return y(this,t,{configurable:!0,value:n}),n}}}function p(e,n,r){var o=r&&r.once||!1,i=r&&r.event||"change",u=t.computed(e),c=u.subscribe(n,null,i),s=c.dispose;return c.dispose=function(){s.call(this),u.dispose()},o&&u.subscribe(function(){c.dispose()}),c}function d(e,t){return m(e,t)||e[t],g(e,t).get}var v=t.utils.extend,h=t.utils.objectForEach,y=Object.defineProperty.bind(Object),g=Object.getOwnPropertyDescriptor.bind(Object),m=Function.prototype.call.bind(Object.prototype.hasOwnProperty),j=Function.prototype.call.bind(Array.prototype.slice),k=["pop","push","reverse","shift","sort","splice","unshift"],A=["remove","removeAll","destroy","destroyAll","replace","subscribe"],O="undefined"!=typeof Symbol?Symbol("ko_decorators"):"__ko_decorators__";e.component=n,e.observable=r,e.computed=o,e.observableArray=c,e.extend=l,e.autobind=b,e.subscribe=p,e.unwrap=d,Object.defineProperty(e,"__esModule",{value:!0})}); | ||
!function(e,r){"object"==typeof exports&&"undefined"!=typeof module?r(exports,require("knockout")):"function"==typeof define&&define.amd?define(["exports","knockout"],r):r(e.KnockoutDecorators=e.KnockoutDecorators||{},e.ko)}(this,function(e,r){"use strict";function t(e,r,t){var n=e[x],o=n&&n[r];return o&&o.forEach(function(r){var n=r instanceof Function?r.call(e):r;t=t.extend(n)}),t}function n(e,r,t){var n=e[x];k(e,x)||(e[x]=n=m({},n),A(n,function(e,r){n[e]=r.slice()}));var o=n[r]||(n[r]=[]);o.push(t)}function o(e,n,o,i){function a(e){var r=l.peek();if(r!==e&&(Array.isArray(r)&&k(r,"mutate")&&F.forEach(function(e){delete r[e]}),Array.isArray(e))){if(k(e,"mutate")&&(e=e.slice()),i)for(var t=0;t<e.length;++t)e[t]=u(e[t]);c(e)}s=!0,l(e),s=!1}function c(e){var r=i?E:S;r.forEach(function(r){return _(e,r,{configurable:!0,value:function(){if(s)return Array.prototype[r].apply(e,arguments);s=!0;var t=l[r].apply(l,arguments);return s=!1,t}})});var t=i?M:P;t.forEach(function(r){return _(e,r,{configurable:!0,value:function(){s=!0;var e=l[r].apply(l,arguments);return s=!1,e}})}),i?(_(e,"push",{configurable:!0,value:function(){if(s)return Array.prototype.push.apply(e,arguments);for(var r=O(arguments),t=0;t<r.length;++t)r[t]=u(r[t]);s=!0;var n=l.push.apply(l,r);return s=!1,n}}),_(e,"unshift",{configurable:!0,value:function(){if(s)return Array.prototype.unshift.apply(e,arguments);for(var r=O(arguments),t=0;t<r.length;++t)r[t]=u(r[t]);s=!0;var n=l.unshift.apply(l,r);return s=!1,n}}),_(e,"splice",{configurable:!0,value:function(){if(s)return Array.prototype.splice.apply(e,arguments);var r;switch(s=!0,arguments.length){case 0:case 1:case 2:r=l.splice.apply(l,arguments);break;case 3:r=l.splice(arguments[0],arguments[1],u(arguments[2]));break;default:for(var t=O(arguments),n=2;n<t.length;++n)t[n]=u(t[n]);r=l.splice.apply(l,arguments)}return s=!1,r}}),_(e,"replace",{configurable:!0,value:function(e,r){s=!0;var t=l.replace(e,u(r));return s=!1,t}}),_(e,"mutate",{configurable:!0,value:function(e){var r=l.peek();l.valueWillMutate(),e(r);for(var t=0;t<r.length;++t)r[t]=u(r[t]);l.valueHasMutated()}}),_(e,"set",{configurable:!0,value:function(e,r){return l.splice(e,1,u(r))[0]}})):(_(e,"mutate",{configurable:!0,value:function(e){l.valueWillMutate(),e(l.peek()),l.valueHasMutated()}}),_(e,"set",{configurable:!0,value:function(e,r){return l.splice(e,1,r)[0]}}))}var l=t(e,n,r.observableArray()),s=!1;_(e,n,{configurable:!0,enumerable:!0,get:l,set:a}),a(o)}function i(e,n,o,i){var a=t(e,n,r.observable()),c=a;i&&(c=function(e){a(u(e))}),_(e,n,{configurable:!0,enumerable:!0,get:a,set:c}),c(o)}function u(e){if("object"==typeof e){if(Array.isArray(e)||null===e)return e;if(e.constructor===Object)return a(e);if(k(e,"constructor")){var r=w(e);if(r===Object.prototype||null===r)return a(e)}}return e}function a(e){return k(e,z)||(_(e,z,{configurable:!0,value:void 0}),A(e,function(r,t){Array.isArray(t)?o(e,r,t,!0):i(e,r,t,!0)})),e}function c(e,t){var n=new r.subscribable,o=function(){var e=O(arguments);n.notifySubscribers(e)};return o.subscribe=function(e){return n.subscribe(function(r){e.apply(null,r)})},_(e,t,{configurable:!0,value:o}),o}function l(e,r){_(e,r,{configurable:!0,get:function(){throw new Error("@observable property '"+r.toString()+"' was not initialized")},set:function(e){Array.isArray(e)?o(this,r,e,!1):i(this,r,e,!1)}})}function s(e,r){_(e,r,{configurable:!0,get:function(){throw new Error("@reactive property '"+r.toString()+"' was not initialized")},set:function(e){Array.isArray(e)?o(this,r,e,!0):i(this,r,e,!0)}})}function f(e,n,o){var i=o||(o=j(e,n)),u=i.get,a=i.set;return o.get=function(){var e=t(this,n,r.pureComputed(u,this));return _(this,n,{configurable:!0,get:e,set:a}),e()},o}function p(e,r){_(e,r,{configurable:!0,get:function(){throw new Error("@observableArray property '"+r.toString()+"' was not initialized")},set:function(e){o(this,r,e,!1)}})}function b(e){return function(r,t){n(r,t,e)}}function v(e,t,n,o){return void 0===o&&(void 0===n?"object"!=typeof t||t.constructor!==Object||"require"in t||"element"in t||(o=t,t=void 0):"object"==typeof n&&(o=n,n=void 0)),function(n){r.components.register(e,m({viewModel:n.length<2?n:{createViewModel:function(e,r){var t=r.element,o=r.templateNodes;return new n(e,t,o)}},template:t||"<!---->",synchronous:!0},o))}}function y(e,r,t){var n=t||(t=j(e,r)),o=n.value,i=n.configurable,u=n.enumerable;return{configurable:i,enumerable:u,get:function(){if(this===e)return o;var t=o.bind(this);return _(this,r,{configurable:!0,value:t}),t}}}function d(e,r){_(e,r,{configurable:!0,get:function(){return c(this,r)}})}function g(e,t,n){var o=n&&n.once||!1;if(k(e,"subscribe")){var i=e;if(o){var u=i.subscribe(function(){u.dispose(),t.apply(null,arguments)});return u}return i.subscribe(t)}var a=n&&n.event||"change",c=r.computed(e),l=void 0;l=o?function(){s.dispose(),t.apply(null,arguments)}:t;var s=c.subscribe(l,null,a),f=s.dispose;return s.dispose=function(){f.call(this),c.dispose()},s}function h(e,r){return k(e,r)||e[r],j(e,r).get}var m=r.utils.extend,A=r.utils.objectForEach,_=Object.defineProperty.bind(Object),w=Object.getPrototypeOf.bind(Object),j=Object.getOwnPropertyDescriptor.bind(Object),k=Function.prototype.call.bind(Object.prototype.hasOwnProperty),O=Function.prototype.call.bind(Array.prototype.slice),x="undefined"!=typeof Symbol?Symbol("ko_decorators_extenders"):"__ko_decorators_extenders__",E=["pop","reverse","shift","sort"],S=E.concat(["push","splice","unshift"]),M=["remove","removeAll","destroy","destroyAll","replace","subscribe"],P=M.concat(["replace"]),F=S.concat(P,["mutate","set"]),z="undefined"!=typeof Symbol?Symbol("ko_decorators_reactive"):"__ko_decorators_reactive__";e.observable=l,e.reactive=s,e.computed=f,e.observableArray=p,e.extend=b,e.component=v,e.autobind=y,e.event=d,e.subscribe=g,e.unwrap=h,Object.defineProperty(e,"__esModule",{value:!0})}); | ||
//# sourceMappingURL=knockout-decorators.min.js.map |
{ | ||
"name": "knockout-decorators", | ||
"version": "0.8.0", | ||
"version": "0.9.0", | ||
"description": "Decorators for use Knockout JS in TypeScript and ESNext environments", | ||
@@ -13,4 +13,5 @@ "main": "dist/knockout-decorators.js", | ||
"build-dist": "rollup -c & rollup -c rollup.config.min.js", | ||
"build-typings": "tsc -p tsconfig.typings.json && shx rm -rf __temp && npm run fix-typings", | ||
"fix-typings": "shx cat src/knockout-decorators.d.ts.tmpl >> dist/knockout-decorators.d.ts", | ||
"build-typings": "tsc -p tsconfig.typings.json && npm run move-typings && npm run fix-typings", | ||
"move-typings": "shx cp __temp/knockout-decorators.d.ts dist/knockout-decorators.d.ts && shx rm -rf __temp", | ||
"fix-typings": "shx echo \"export as namespace KnockoutDecorators;\" >> dist/knockout-decorators.d.ts", | ||
"test": "jest --no-cache" | ||
@@ -17,0 +18,0 @@ }, |
247
README.md
@@ -37,5 +37,8 @@ # Knockout Decorators | ||
* [@computed](#knockout-decorators-computed) | ||
* [@reactive](#knockout-decorators-reactive) | ||
* [@observableArray](#knockout-decorators-observableArray) | ||
* [@extend](#knockout-decorators-extend) | ||
* [@component](#knockout-decorators-component) | ||
* [@autobind](#knockout-decorators-autobind) | ||
* [@event](#knockout-decorators-event) | ||
* [subscribe](#knockout-decorators-subscribe) | ||
@@ -51,6 +54,10 @@ * [unwrap](#knockout-decorators-unwrap) | ||
#### <a name="knockout-decorators-observable"></a> @observable | ||
Property decorator that creates hidden `ko.observable` with ES6 getter and setter for it | ||
Property decorator that creates hidden `ko.observable` with ES6 getter and setter for it<br> | ||
If initialized by Array then hidden `ko.observableArray` will be created (see [@observableArray](#knockout-decorators-observableArray)) | ||
```js | ||
import { observable } from "knockout-decorators"; | ||
class Model { | ||
@observable field = 123; | ||
@observable collection = []; | ||
}; | ||
@@ -66,5 +73,7 @@ let model = new Model(); | ||
#### <a name="knockout-decorators-computed"></a> @computed | ||
Accessor decorator that wraps ES6 getter to hidden `ko.pureComputed` | ||
Accessor decorator that wraps ES6 getter to hidden `ko.pureComputed`<br> | ||
Setter is not wrapped to hidden `ko.pureComputed` and stays unchanged | ||
```js | ||
import { observable, computed } from "knockout-decorators"; | ||
class Person { | ||
@@ -87,5 +96,43 @@ @observable firstName = ""; | ||
#### <a name="knockout-decorators-reactive"></a> @reactive | ||
Like [@observable](#knockout-decorators-observable), but creates "deep observable" property (see example below)<br> | ||
If initialized by Array then hidden `ko.observableArray` will be created (see [@observableArray](#knockout-decorators-observableArray)) | ||
```js | ||
import { reactive } from "knockout-decorators"; | ||
class ViewModel { | ||
@reactive deepObservable = { // like @observable | ||
firstName: "Clive Staples", // like @observable | ||
lastName: "Lewis", // like @observable | ||
array: [], // like @observableArray | ||
object: { // like @observable | ||
foo: "bar", // like @observable | ||
reference: null, // like @observable | ||
}, | ||
} | ||
} | ||
const vm = new ViewModel(); | ||
vm.deepObservable.object.reference = { | ||
firstName: "Clive Staples", // make @observable | ||
lastName: "Lewis", // make @observable | ||
}; | ||
vm.deepObservable.array.push({ | ||
firstName: "Clive Staples", // make @observable | ||
lastName: "Lewis", // make @observable | ||
}); | ||
``` | ||
<br> | ||
#### <a name="knockout-decorators-observableArray"></a> @observableArray | ||
Property decorator that creates hidden `ko.observableArray` with ES6 getter and setter for it | ||
```js | ||
import { observableArray } from "knockout-decorators"; | ||
class Model { | ||
@@ -99,3 +146,3 @@ @observableArray array = [1, 2, 3]; | ||
``` | ||
Functions from `ko.observableArray` (both Knockout-specific `remove`, `removeAll`, `destroy`, `destroyAll`, `replace`<br> | ||
Functions from `ko.observableArray` (both Knockout-specific `remove`, `removeAll`, `destroy`, `destroyAll`, `replace` | ||
and redefined `Array.prototype` functions `pop`, `push`, `reverse`, `shift`, `sort`, `splice`, `unshift`) | ||
@@ -105,6 +152,9 @@ are also presents in decorated poperty.<br> | ||
And also decorated array has a `subscribe` function from `ko.subscribable` | ||
And also decorated array has: | ||
* a `subscribe(callback: (value: any[]) => void)` function from `ko.subscribable`, | ||
```js | ||
import { observableArray, ObservableArray } from "knockout-decorators"; | ||
class Model { | ||
@observableArray array = [1, 2, 3]; | ||
@observableArray array = [1, 2, 3] as ObservableArray<number>; | ||
}; | ||
@@ -118,10 +168,39 @@ let model = new Model(); | ||
``` | ||
* a new `mutate(callback: () => void)` function that runs callback in which we can mutate array directly, | ||
```js | ||
import { observableArray, ObservableArray } from "knockout-decorators"; | ||
class Model { | ||
@observableArray array = [1, 2, 3] as ObservableArray<number>; | ||
}; | ||
let model = new Model(); | ||
model.array.mutate(() => { | ||
model.array[1] = 200; // this changes are observed | ||
model.array[2] = 300; // when mutation callback stops execution | ||
}); | ||
``` | ||
* a new `set(i: number, value: any): any` function that sets a new value at specified index and returns the old value. | ||
```js | ||
import { observableArray, ObservableArray } from "knockout-decorators"; | ||
class Model { | ||
@observableArray array = [1, 2, 3] as ObservableArray<number>; | ||
}; | ||
let model = new Model(); | ||
let oldValue = model.array.set(2, 300) // this change is observed | ||
console.log(model.array); // [console] ➜ [1, 2, 300] | ||
console.log(oldValue); // [console] ➜ 3 | ||
``` | ||
<br> | ||
#### <a name="knockout-decorators-extend"></a> @extend | ||
Apply extenders to decorated `@observable` | ||
Apply extenders to decorated `@observable`, `@reactive`, `@observableArray` or `@computed` | ||
```js | ||
@extend(extenders: Object) | ||
@extend(extendersFactory: () => Object) | ||
@extend(extenders: Object); | ||
@extend(extendersFactory: () => Object); | ||
``` | ||
@@ -132,2 +211,4 @@ | ||
```js | ||
import { observable, computed, extend } from "knockout-decorators"; | ||
class ViewModel { | ||
@@ -178,2 +259,4 @@ rateLimit: 50; | ||
```js | ||
import { component } from "knockout-decorators"; | ||
@component("my-component") | ||
@@ -195,2 +278,4 @@ class Component { | ||
```js | ||
import { component } from "knockout-decorators"; | ||
@component("my-component", | ||
@@ -224,26 +309,101 @@ require("./my-component.html"), | ||
#### <a name="knockout-decorators-autobind"></a> @autobind | ||
Bind class method to class instance. Clone of [core-decorators.js](https://github.com/jayphelps/core-decorators.js#autobind) `@autobind` | ||
```js | ||
import { observable, component, autobind } from "knockout-decorators"; | ||
@component("my-component", ` | ||
<ul data-bind="foreach: array"> | ||
<li data-bind="click: $component.remove">remove me</li> | ||
</ul> | ||
`) | ||
class MyComponent { | ||
@observable array = [1, 2, 3] as ObservableArray<number>; | ||
@autobind | ||
remove(item: number) { | ||
this.array.remove(item); | ||
} | ||
} | ||
``` | ||
<br> | ||
#### <a name="knockout-decorators-event"></a> @event | ||
Create subscribable function that invokes it's subscribers when it called.<br> | ||
All arguments that passed to `@event` function are translated to it's subscribers.<br> | ||
Internally uses hidden `ko.subscribable`.<br> | ||
Subscribers can be attached by calling `.subscribe()` method of `EventProperty` type or by `subscribe()` [utility](#knockout-decorators-subscribe-event). | ||
```js | ||
import { event, EventProperty } from "knockout-decorators"; | ||
class Producer { | ||
@event myEvent: EventProperty; | ||
} | ||
class Consumer { | ||
constructor(producer: Producer) { | ||
producer.myEvent.subscribe((arg1, arg2) => { | ||
console.log("lambda:", arg1, arg2); | ||
}); | ||
// `subscription` type is `KnockoutSubscription` | ||
const subscription = producer.myEvent.subscribe(this.onEvent); | ||
} | ||
@autobind | ||
onEvent(arg1, arg2) { | ||
console.log("method:", arg1, arg2); | ||
} | ||
} | ||
const producer = new Producer(); | ||
const consumer = new Consumer(producer); | ||
// emit @event | ||
producer.myEvent(123, "test"); | ||
// [console] ➜ lambda: 123 "test" | ||
// [console] ➜ method: 123 "test" | ||
``` | ||
<br> | ||
#### <a name="knockout-decorators-subscribe"></a> subscribe | ||
Subscribe to `@observable` or `@computed` dependency with creation of hidden `ko.computed()` | ||
Subscribe to `@observable` (or `@computed`) dependency with creation of hidden `ko.computed()` | ||
```js | ||
subscribe<T>(getDependency: () => T, callback: (value: T) => void, options?: { | ||
once?: boolean, | ||
event?: string, | ||
}): KnockoutSubscription | ||
subscribe<T>( | ||
dependency: () => T, | ||
callback: (value: T) => void, | ||
options?: { once?: boolean, event?: string } | ||
): KnockoutSubscription; | ||
``` | ||
Or subscribe to some `@event` property | ||
```js | ||
subscribe<T1, T2, ...>( | ||
event: (arg1: T1, arg2: T2, ...) => void, | ||
callback: (arg1: T1, arg2: T2, ...) => void, | ||
options?: { once?: boolean } | ||
): KnockoutSubscription; | ||
``` | ||
| Argument | Default | Description | | ||
|:--------------|:-----------|:--------------------------------------------------------------------| | ||
| getDependency | | Function for getting observeble property | | ||
| callback | | Callback that handle dependency changes | | ||
| options | `null` | Options object | | ||
| options.once | `false` | If `true` then subscription will be disposed after first invocation | | ||
| optons.event | `"change"` | Event for passing to Knockout native `subscribe()` | | ||
| Argument | Default | Description | | ||
|:------------------|:-----------|:--------------------------------------------------------------------| | ||
| dependencyOrEvent | | (1) Function for getting observeble property (2) @event property | | ||
| callback | | Callback that handle dependency changes or @event notifications | | ||
| options | `null` | Options object | | ||
| options.once | `false` | If `true` then subscription will be disposed after first invocation | | ||
| optons.event | `"change"` | Event name for passing to Knockout native `subscribe()` | | ||
Subscribe to `@observable` changes | ||
```js | ||
import { observable, subscribe } from "knockout-decorators"; | ||
class ViewModel { | ||
@observable field = ""; | ||
@observable field = 123; | ||
constructor() { | ||
subscribe(() => this.field, (value) => { | ||
console.log(value); | ||
console.log(value); // TypeScript detects that `value` type is `number` | ||
}); | ||
@@ -257,12 +417,41 @@ | ||
console.log(value); | ||
}, { event: "beforeChange" }); | ||
}, { event: "beforeChange" }); | ||
} | ||
} | ||
``` | ||
<a name="knockout-decorators-subscribe-event"></a>Subscribe to `@event` property | ||
```js | ||
import { event, subscribe } from "knockout-decorators"; | ||
class ViewModel { | ||
@event myEvent: (arg: string) => void; | ||
constructor() { | ||
subscribe(this.myEvent, (arg) => { | ||
console.log(arg); // TypeScript detects that `arg` type is `string` | ||
}); | ||
subscribe(this.myEvent, (arg) => { | ||
console.log(arg); | ||
}, { once: true }); | ||
// `subscription` type is `KnockoutSubscription` | ||
const subscription = subscribe(this.myEvent, (arg) => { | ||
console.log(arg); | ||
}); | ||
// unsubscribe from @event | ||
subscription.dispose(); | ||
// emit @event | ||
this.myEvent("event argument") | ||
} | ||
} | ||
``` | ||
<br> | ||
#### <a name="knockout-decorators-unwrap"></a> unwrap | ||
Get internal `ko.observable()` for property decodated by `@observable` | ||
or internal `ko.pureComputed()` for property decodated by `@computed` | ||
Get hidden `ko.observable()` for property decodated by `@observable` | ||
or hidden `ko.pureComputed()` for property decodated by `@computed` | ||
```js | ||
@@ -281,2 +470,4 @@ unwrap(instance: Object, key: string | symbol): any; | ||
```js | ||
import { observable, extend, unwrap } from "knockout-decorators"; | ||
class MyViewModel { | ||
@@ -328,3 +519,3 @@ @extend({ required: "MyField is required" }) | ||
### <a name="knockout-decorators-changelog"></a> | ||
### Breaking changes from v0.7.1 to 0.8.0 | ||
### Breaking changes from v0.7.1 | ||
@@ -352,3 +543,3 @@ 1. Removed `@subscribe` decorator | ||
So in 0.8.0 instead of `@subscribe` decorator there is shorthand function `subscribe` | ||
So from v0.8.0 instead of `@subscribe` decorator there is shorthand function `subscribe` | ||
with some extra functionality like "subscribe once": | ||
@@ -363,2 +554,2 @@ ```js | ||
} | ||
``` | ||
``` |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
133988
687
540
1