knockout-decorators
Advanced tools
Comparing version
@@ -17,5 +17,2 @@ /// <reference types="knockout" /> | ||
export declare function component(name: string, template: TemplateConfig, styles: string | string[], options?: Object): ComponentDecorator; | ||
export interface Disposable { | ||
dispose(): void; | ||
} | ||
/** | ||
@@ -26,2 +23,10 @@ * Property decorator that creates hidden ko.observable with ES6 getter and setter for it | ||
/** | ||
* 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 } | ||
*/ | ||
export declare function computed(prototype: Object, key: string | symbol, desc: PropertyDescriptor): PropertyDescriptor; | ||
/** | ||
* Property decorator that creates hidden ko.observableArray with ES6 getter and setter for it | ||
@@ -40,16 +45,7 @@ */ | ||
destroyAll(items: T[]): void; | ||
subscribe(callback: (val: T[]) => void): Disposable; | ||
subscribe(callback: (val: T[]) => void, callbackTarget: any): Disposable; | ||
subscribe(callback: (val: any[]) => void, callbackTarget: any, event: string): Disposable; | ||
subscribe(callback: (val: T[]) => void): KnockoutSubscription; | ||
subscribe(callback: (val: T[]) => void, callbackTarget: any): KnockoutSubscription; | ||
subscribe(callback: (val: any[]) => void, callbackTarget: any, event: string): KnockoutSubscription; | ||
} | ||
/** | ||
* Accessor decorator that wraps ES6 getter and setter to hidden ko.pureComputed | ||
*/ | ||
export declare function computed(prototype: Object, key: string | symbol, desc: PropertyDescriptor): PropertyDescriptor; | ||
/** | ||
* Replace original method with factory that produces ko.computed from original method | ||
*/ | ||
export declare function reaction(autoDispose: boolean): MethodDecorator; | ||
export declare function reaction(prototype: Object, key: string | symbol, desc: PropertyDescriptor): PropertyDescriptor; | ||
/** | ||
* Apply extenders to decorated @observable | ||
@@ -60,12 +56,2 @@ */ | ||
/** | ||
* Subscribe to @observable by name or by specifying callback explicitely | ||
*/ | ||
export declare function subscribe(callback: (value: any) => void, event?: string, autoDispose?: boolean): PropertyDecorator; | ||
export declare function subscribe(targetOrCallback: string | symbol, event?: string, autoDispose?: boolean): PropertyDecorator; | ||
export declare function subscribe(targetOrCallback: string | symbol, event?: string, autoDispose?: boolean): MethodDecorator; | ||
/** | ||
* Shorthand for ko.pureComputed(dependency).subscribe(callback) | ||
*/ | ||
export declare function subscribe<T>(dependency: () => T, callback: (value: T) => void): KnockoutSubscription; | ||
/** | ||
* Like https://github.com/jayphelps/core-decorators.js @autobind but less smart and complex | ||
@@ -76,2 +62,9 @@ * Do NOT use with ES6 inheritance! | ||
/** | ||
* Subscribe callback to dependency changes | ||
*/ | ||
export declare function subscribe<T>(getDependency: () => T, callback: (value: T) => void, options?: { | ||
once?: boolean; | ||
event?: string; | ||
}): KnockoutSubscription; | ||
/** | ||
* Get internal ko.observable() for object property decodated by @observable | ||
@@ -78,0 +71,0 @@ */ |
@@ -53,67 +53,3 @@ (function (global, factory) { | ||
} | ||
var DECORATORS_KEY = typeof Symbol !== "undefined" | ||
? Symbol("ko_decorators") : "__ko_decorators_"; | ||
var SUBSCRIPTIONS_KEY = typeof Symbol !== "undefined" | ||
? Symbol("ko_decorators_subscriptions") : "__ko_decorators_subscriptions_"; | ||
var DISPOSABLE_KEY = typeof Symbol !== "undefined" | ||
? Symbol("ko_decorators_disposable") : "__ko_decorators_disposable_"; | ||
var DecoratorType; | ||
(function (DecoratorType) { | ||
DecoratorType[DecoratorType["Extend"] = 0] = "Extend"; | ||
DecoratorType[DecoratorType["Subscribe"] = 1] = "Subscribe"; | ||
})(DecoratorType || (DecoratorType = {})); | ||
function getMetaData(prototype) { | ||
var metaData = prototype[DECORATORS_KEY]; | ||
if (!prototype.hasOwnProperty(DECORATORS_KEY)) { | ||
prototype[DECORATORS_KEY] = metaData = assign({}, metaData); | ||
objectForEach(metaData, function (key, decorators) { | ||
metaData[key] = decorators.slice(); | ||
}); | ||
} | ||
return metaData; | ||
} | ||
function getDecorators(metaData, key) { | ||
return metaData[key] || (metaData[key] = []); | ||
} | ||
function getSubscriptions(instance) { | ||
return instance[SUBSCRIPTIONS_KEY] || (instance[SUBSCRIPTIONS_KEY] = []); | ||
} | ||
function applyDecorators(instance, key, target) { | ||
var metaData = instance[DECORATORS_KEY]; | ||
var decorators = metaData && metaData[key]; | ||
if (decorators) { | ||
decorators.forEach(function (d) { | ||
switch (d.type) { | ||
case DecoratorType.Extend: | ||
var extenders = d.value instanceof Function | ||
? d.value.call(instance) : d.value; | ||
target = target.extend(extenders); | ||
break; | ||
case DecoratorType.Subscribe: | ||
var subscription = target.subscribe(d.value, instance, d.event); | ||
if (d.dispose) { | ||
getSubscriptions(instance).push(subscription); | ||
} | ||
break; | ||
} | ||
}); | ||
} | ||
return target; | ||
} | ||
function redefineDispose(prototype) { | ||
if (prototype[DISPOSABLE_KEY]) { | ||
return; | ||
} | ||
prototype[DISPOSABLE_KEY] = true; | ||
var original = prototype["dispose"]; | ||
prototype["dispose"] = function dispose() { | ||
var disposables = this[SUBSCRIPTIONS_KEY]; | ||
if (disposables) { | ||
disposables.forEach(function (s) { s.dispose(); }); | ||
} | ||
if (original) { | ||
return original.apply(this, arguments); | ||
} | ||
}; | ||
} | ||
/*===========================================================================*/ | ||
/** | ||
@@ -126,3 +62,3 @@ * Property decorator that creates hidden ko.observable with ES6 getter and setter for it | ||
get: function () { | ||
var observable$$1 = applyDecorators(this, key, ko.observable()); | ||
var observable$$1 = applyExtenders(this, key, ko.observable()); | ||
defProp(this, key, { | ||
@@ -137,3 +73,3 @@ configurable: true, | ||
set: function (value) { | ||
var observable$$1 = applyDecorators(this, key, ko.observable()); | ||
var observable$$1 = applyExtenders(this, key, ko.observable()); | ||
defProp(this, key, { | ||
@@ -149,6 +85,28 @@ configurable: true, | ||
} | ||
/*===========================================================================*/ | ||
/** | ||
* 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 = 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 obsArray = applyDecorators(instance, key, ko.observableArray()); | ||
var obsArray = applyExtenders(instance, key, ko.observableArray()); | ||
var insideObsArray = false; | ||
@@ -208,11 +166,12 @@ defProp(instance, key, { | ||
} | ||
function clearArrayMethods(array) { | ||
arrayMethods.forEach(function (fnName) { | ||
delete array[fnName]; | ||
}); | ||
observableArrayMethods.forEach(function (fnName) { | ||
delete array[fnName]; | ||
}); | ||
} | ||
} | ||
// moved outside of defObservableArray function to prevent creation of unnecessary closure | ||
function clearArrayMethods(array) { | ||
arrayMethods.forEach(function (fnName) { | ||
delete array[fnName]; | ||
}); | ||
observableArrayMethods.forEach(function (fnName) { | ||
delete array[fnName]; | ||
}); | ||
} | ||
/** | ||
@@ -235,52 +194,31 @@ * Property decorator that creates hidden ko.observableArray with ES6 getter and setter for it | ||
} | ||
/** | ||
* Accessor decorator that wraps ES6 getter and setter to hidden ko.pureComputed | ||
*/ | ||
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 = ko.pureComputed(get, this); | ||
defProp(this, key, { | ||
configurable: true, | ||
get: computed$$1, | ||
set: set | ||
/*===========================================================================*/ | ||
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 computed$$1(); | ||
}; | ||
return desc; | ||
// TODO: make @computed extendable (by @extend decorator) | ||
} | ||
return metaData; | ||
} | ||
/** | ||
* Replace original method with factory that produces ko.computed from original method | ||
* @param autoDispose { Boolean } if true then subscription will be disposed when entire ViewModel is disposed | ||
*/ | ||
function reaction(prototypeOrAutoDispose, key, desc) { | ||
var autoDispose; | ||
if (typeof prototypeOrAutoDispose === "boolean" && key === void 0) { | ||
autoDispose = prototypeOrAutoDispose; // @reaction(false) | ||
return decorator; // onSomethingChange() {} | ||
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); | ||
}); | ||
} | ||
else if (typeof prototypeOrAutoDispose === "object" && key !== void 0) { | ||
autoDispose = true; // @reaction | ||
decorator(prototypeOrAutoDispose, key, desc); // onSomethingChange() {} | ||
} | ||
else { | ||
throw new Error("Can not use @reaction decorator this way"); | ||
} | ||
function decorator(prototype, key, desc) { | ||
var value = (desc || (desc = getDescriptor(prototype, key))).value; | ||
desc.value = function () { | ||
var _this = this; | ||
var args = slice(arguments); | ||
var computed$$1 = ko.computed(function () { return value.apply(_this, args); }); | ||
if (autoDispose) { | ||
getSubscriptions(this).push(computed$$1); | ||
} | ||
return computed$$1; | ||
}; | ||
if (autoDispose) { | ||
redefineDispose(prototype); | ||
} | ||
return desc; | ||
} | ||
return target; | ||
} | ||
@@ -293,60 +231,9 @@ /** | ||
return function (prototype, key) { | ||
getDecorators(getMetaData(prototype), key).push({ | ||
type: DecoratorType.Extend, | ||
value: extendersOrFactory, | ||
}); | ||
var medaData = getOrCreateMetaData(prototype); | ||
var extenders = getOrCreateExtenders(medaData, key); | ||
extenders.push(extendersOrFactory); | ||
}; | ||
} | ||
/*===========================================================================*/ | ||
/** | ||
* Subscribe to @observable by name or by specifying callback explicitely | ||
* @param targetOrCallback { String | Function } name of callback or callback itself | ||
* when observable is decorated and name of observable property when callback is decorated | ||
* @param event { String } Knockout subscription event name | ||
* @param autoDispose { Boolean } if true then subscription will be disposed when entire ViewModel is disposed | ||
*/ | ||
function subscribe(targetOrCallback, event, autoDispose) { | ||
if (autoDispose === void 0) { autoDispose = true; } | ||
if (typeof event === "function") { | ||
// subscribe(() => this.observableField, (value) => { ... }); | ||
return ko.pureComputed(targetOrCallback).subscribe(event); | ||
} | ||
return function (prototype, key, desc) { | ||
var _a = desc || (desc = getDescriptor(prototype, key)), value = _a.value, get = _a.get; | ||
var targetKey; | ||
var callback; | ||
if (typeof value === "function") { | ||
if (typeof targetOrCallback === "string" || typeof targetOrCallback === "symbol") { | ||
targetKey = targetOrCallback; // @subscribe("target") | ||
callback = value; // callback(value) {} | ||
} | ||
else { | ||
throw new Error("Subscription target should be a key in decorated ViewModel"); | ||
} | ||
} | ||
else if (typeof get === "function") { | ||
if (typeof targetOrCallback === "function") { | ||
targetKey = key; // @subscribe(ViewModel.prototype.callback) | ||
callback = targetOrCallback; // @observable target; | ||
} | ||
else if (typeof targetOrCallback === "string" || typeof targetOrCallback === "symbol") { | ||
targetKey = key; // @subscribe("callback") | ||
callback = prototype[targetOrCallback]; // @observable target; | ||
} | ||
else { | ||
throw new Error("Subscription callback should be a function or key in decorated ViewModel"); | ||
} | ||
} | ||
getDecorators(getMetaData(prototype), targetKey).push({ | ||
type: DecoratorType.Subscribe, | ||
value: callback, | ||
event: event, | ||
dispose: autoDispose, | ||
}); | ||
if (autoDispose) { | ||
redefineDispose(prototype); | ||
} | ||
return desc; | ||
}; | ||
} | ||
/** | ||
* Like https://github.com/jayphelps/core-decorators.js @autobind but less smart and complex | ||
@@ -373,3 +260,32 @@ * Do NOT use with ES6 inheritance! | ||
} | ||
/*===========================================================================*/ | ||
/** | ||
* Subscribe callback to dependency changes | ||
*/ | ||
function subscribe(getDependency, 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(); | ||
}); | ||
} | ||
return subscription; | ||
} | ||
/** | ||
* Get internal ko.observable() for object property decodated by @observable | ||
*/ | ||
function unwrap(instance, key) { | ||
if (!hasOwnProperty(instance, key)) { | ||
// invoke getter on instance.__proto__ that defines property on instance | ||
instance[key]; | ||
} | ||
return getDescriptor(instance, key).get; | ||
@@ -380,8 +296,7 @@ } | ||
exports.observable = observable$1; | ||
exports.computed = computed$1; | ||
exports.observableArray = observableArray$1; | ||
exports.computed = computed$1; | ||
exports.reaction = reaction; | ||
exports.extend = extend; | ||
exports.autobind = autobind; | ||
exports.subscribe = subscribe; | ||
exports.autobind = autobind; | ||
exports.unwrap = unwrap; | ||
@@ -388,0 +303,0 @@ |
@@ -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 o(e,o,n,r){return void 0===r&&(void 0===n?"object"!=typeof o||o.constructor!==Object||"require"in o||"element"in o||(r=o,o=void 0):"object"==typeof n&&(r=n,n=void 0)),function(n){t.components.register(e,g({viewModel:n.length<2?n:{createViewModel:function(e,t){var o=t.element,r=t.templateNodes;return new n(e,o,r)}},template:o||"<!---->",synchronous:!0},r))}}function n(e){var t=e[j];return e.hasOwnProperty(j)||(e[j]=t=g({},t),m(t,function(e,o){t[e]=o.slice()})),t}function r(e,t){return e[t]||(e[t]=[])}function i(e){return e[S]||(e[S]=[])}function u(e,t,o){var n=e[j],r=n&&n[t];return r&&r.forEach(function(t){switch(t.type){case h.Extend:var n=t.value instanceof Function?t.value.call(e):t.value;o=o.extend(n);break;case h.Subscribe:var r=o.subscribe(t.value,e,t.event);t.dispose&&i(e).push(r)}}),o}function c(e){if(!e[x]){e[x]=!0;var t=e.dispose;e.dispose=function(){var e=this[S];if(e&&e.forEach(function(e){e.dispose()}),t)return t.apply(this,arguments)}}}function s(e,o){_(e,o,{configurable:!0,get:function(){var e=u(this,o,t.observable());return _(this,o,{configurable:!0,enumerable:!0,get:e,set:e}),e()},set:function(e){var n=u(this,o,t.observable());_(this,o,{configurable:!0,enumerable:!0,get:n,set:n}),n(e)}})}function a(e,o){function n(e){A.forEach(function(t){return _(e,t,{configurable:!0,value:function(){if(c)return Array.prototype[t].apply(e,arguments);c=!0;var o=i[t].apply(i,arguments);return c=!1,o}})}),O.forEach(function(t){return _(e,t,{configurable:!0,value:function(){c=!0;var e=i[t].apply(i,arguments);return c=!1,e}})})}function r(e){A.forEach(function(t){delete e[t]}),O.forEach(function(t){delete e[t]})}var i=u(e,o,t.observableArray()),c=!1;_(e,o,{configurable:!0,enumerable:!0,get:i,set:function(e){var t=i.peek();t!==e&&(Array.isArray(t)&&w(t,"subscribe")&&r(t),Array.isArray(e)&&(w(e,"subscribe")&&(e=E(e)),n(e))),c=!0,i(e),c=!1}})}function f(e,t){_(e,t,{configurable:!0,get:function(){return a(this,t),this[t]=[],this[t]},set:function(e){a(this,t),this[t]=e}})}function l(e,o,n){var r=n||(n=k(e,o)),i=r.get,u=r.set;return n.get=function(){var e=t.pureComputed(i,this);return _(this,o,{configurable:!0,get:e,set:u}),e()},n}function b(e,o,n){function r(e,o,n){var r=(n||(n=k(e,o))).value;return n.value=function(){var e=this,o=E(arguments),n=t.computed(function(){return r.apply(e,o)});return u&&i(this).push(n),n},u&&c(e),n}var u;if("boolean"==typeof e&&void 0===o)return u=e,r;if("object"!=typeof e||void 0===o)throw new Error("Can not use @reaction decorator this way");u=!0,r(e,o,n)}function p(e){return function(t,o){r(n(t),o).push({type:h.Extend,value:e})}}function d(e,o,i){return void 0===i&&(i=!0),"function"==typeof o?t.pureComputed(e).subscribe(o):function(t,u,s){var a,f,l=s||(s=k(t,u)),b=l.value,p=l.get;if("function"==typeof b){if("string"!=typeof e&&"symbol"!=typeof e)throw new Error("Subscription target should be a key in decorated ViewModel");a=e,f=b}else if("function"==typeof p)if("function"==typeof e)a=u,f=e;else{if("string"!=typeof e&&"symbol"!=typeof e)throw new Error("Subscription callback should be a function or key in decorated ViewModel");a=u,f=t[e]}return r(n(t),a).push({type:h.Subscribe,value:f,event:o,dispose:i}),i&&c(t),s}}function y(e,t,o){var n=o||(o=k(e,t)),r=n.value,i=n.configurable,u=n.enumerable;return{configurable:i,enumerable:u,get:function(){if(this===e)return r;var o=r.bind(this);return _(this,t,{configurable:!0,value:o}),o}}}function v(e,t){return k(e,t).get}var h,g=t.utils.extend,m=t.utils.objectForEach,_=Object.defineProperty.bind(Object),k=Object.getOwnPropertyDescriptor.bind(Object),w=Function.prototype.call.bind(Object.prototype.hasOwnProperty),E=Function.prototype.call.bind(Array.prototype.slice),j="undefined"!=typeof Symbol?Symbol("ko_decorators"):"__ko_decorators_",S="undefined"!=typeof Symbol?Symbol("ko_decorators_subscriptions"):"__ko_decorators_subscriptions_",x="undefined"!=typeof Symbol?Symbol("ko_decorators_disposable"):"__ko_decorators_disposable_";!function(e){e[e.Extend=0]="Extend",e[e.Subscribe=1]="Subscribe"}(h||(h={}));var A=["pop","push","reverse","shift","sort","splice","unshift"],O=["remove","removeAll","destroy","destroyAll","replace","subscribe"];e.component=o,e.observable=s,e.observableArray=f,e.computed=l,e.reaction=b,e.extend=p,e.subscribe=d,e.autobind=y,e.unwrap=v,Object.defineProperty(e,"__esModule",{value:!0})}); | ||
!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})}); | ||
//# sourceMappingURL=knockout-decorators.min.js.map |
{ | ||
"name": "knockout-decorators", | ||
"version": "0.7.1", | ||
"version": "0.8.0", | ||
"description": "Decorators for use Knockout JS in TypeScript and ESNext environments", | ||
@@ -5,0 +5,0 @@ "main": "dist/knockout-decorators.js", |
219
README.md
@@ -36,8 +36,7 @@ # Knockout Decorators | ||
* [@observable](#knockout-decorators-observable) | ||
* [@computed](#knockout-decorators-computed) | ||
* [@observableArray](#knockout-decorators-observableArray) | ||
* [@computed](#knockout-decorators-computed) | ||
* [@reaction](#knockout-decorators-reaction) | ||
* [@extend](#knockout-decorators-extend) | ||
* [@subscribe](#knockout-decorators-subscribe) | ||
* [@component](#knockout-decorators-component) | ||
* [subscribe](#knockout-decorators-subscribe) | ||
* [unwrap](#knockout-decorators-unwrap) | ||
@@ -49,2 +48,4 @@ | ||
[Changes from v0.7.1](#knockout-decorators-changelog) | ||
#### <a name="knockout-decorators-observable"></a> @observable | ||
@@ -64,2 +65,23 @@ Property decorator that creates hidden `ko.observable` with ES6 getter and setter for it | ||
#### <a name="knockout-decorators-computed"></a> @computed | ||
Accessor decorator that wraps ES6 getter to hidden `ko.pureComputed` | ||
Setter is not wrapped to hidden `ko.pureComputed` and stays unchanged | ||
```js | ||
class Person { | ||
@observable firstName = ""; | ||
@observable lastName = ""; | ||
@computed | ||
get fullName() { return this.firstName + " " + this.lastName; } | ||
set fullName(value) { [this.firstName, this.lastName] = value.trim().split(/\s+/g); } | ||
} | ||
let person = new Person(); | ||
ko.pureComputed(() => person.fullName).subscribe(console.log.bind(console)); | ||
person.fullName = " John Smith " // [console] ➜ "John Smith" | ||
``` | ||
<br> | ||
#### <a name="knockout-decorators-observableArray"></a> @observableArray | ||
@@ -96,59 +118,2 @@ Property decorator that creates hidden `ko.observableArray` with ES6 getter and setter for it | ||
#### <a name="knockout-decorators-computed"></a> @computed | ||
Accessor decorator that wraps ES6 getter and setter (if defined) to hidden `ko.pureComputed` | ||
```js | ||
class Person { | ||
@observable firstName = ""; | ||
@observable lastName = ""; | ||
@computed | ||
get fullName() { return this.firstName + " " + this.lastName; } | ||
set fullName(value) { [this.firstName, this.lastName] = value.trim().split(/\s+/g); } | ||
} | ||
let person = new Person(); | ||
ko.pureComputed(() => person.fullName).subscribe(console.log.bind(console)); | ||
person.fullName = " John Smith " // [console] ➜ "John Smith" | ||
``` | ||
<br> | ||
#### <a name="knockout-decorators-reaction"></a> @reaction | ||
Replace original method with factory that produces `ko.computed` from original method | ||
```js | ||
@reaction | ||
@reaction(autoDispose: boolean) | ||
``` | ||
| Argument | Default | Description | | ||
|:------------|:--------|:-------------------------------------------------------------------------------| | ||
| autoDispose | `true` | if true then computed will be disposed when entire decorated class is disposed | | ||
Method that decorated with `@reaction` evaluates once when explicitely invoked (this call creates hidden `ko.computed`) | ||
and every times when it's observable (or computed) dependencies are changed. | ||
Hidden `ko.computed` will be disposed when entire decorated class is disposed (if we don't set `autoDispose` to `false`) | ||
```js | ||
class BlogPage { | ||
@observable postId = 0; | ||
@observable pageData: any; | ||
constructor(blogId: ko.Observable<number>) { | ||
const computed = this.onRoute(blogId); | ||
// subscribe onRoute handler to changes | ||
// then we can do whatever with created computed | ||
} | ||
// 'dispose()' method is redefined such that it disposes hidded 'onRoute' computed | ||
// if original class already has 'dispose()' method then it would be wrapped by new method | ||
@reaction async onRoute(blogId: ko.Observable<number>) { | ||
const resp = await fetch(`/blog/${ blogId() }/post/${ this.postId }`); | ||
this.pageData = await resp.json(); | ||
} | ||
} | ||
``` | ||
<br> | ||
#### <a name="knockout-decorators-extend"></a> @extend | ||
@@ -172,2 +137,7 @@ Apply extenders to decorated `@observable` | ||
@observable second = ""; | ||
@extend({ rateLimit: 500 }) | ||
@computed get both() { | ||
return this.first + " " + this.second; | ||
} | ||
@@ -182,52 +152,2 @@ getExtender() { | ||
#### <a name="knockout-decorators-subscribe"></a> @subscribe | ||
Subscribe to `@observable` by name or by specifying callback explicitely | ||
```js | ||
@subscribe(callback: (value: any) => void, event?: string, autoDispose?: boolean) | ||
@subscribe(targetOrCallback: string | symbol, event?: string, autoDispose?: boolean) | ||
``` | ||
| Argument | Default | Description | | ||
|:-----------------|:--------|:-------------------------------------------------------------------------------| | ||
| callback | | Subscription handler method | | ||
| targetOrCallback | | Name of subscription handler method or name of `@observable` property | | ||
| event | `null` | Knockout subscription event | | ||
| autoDispose | `true` | if true then computed will be disposed when entire decorated class is disposed | | ||
We can define name of handler when we decorate `@observable` or define name of `@observable` when decorate handler. | ||
Subscriptions will be disposed when entire decorated class is disposed (if we don't set `autoDispose` to `false`) | ||
```js | ||
class ViewModel { | ||
// specify callback | ||
@subscribe("onFirstChanged") | ||
@observable first = ""; | ||
onFirstChanged(value) {} | ||
@observable second = ""; | ||
// specify observable | ||
@subscribe("second") | ||
onSecondChanged(value) {} | ||
} | ||
``` | ||
Also whe can pass subscription handler directly. | ||
Then it will be invoked with ViewModel instance as `this` argument. | ||
And we can specify subscription `event` | ||
```js | ||
class ViewModel { | ||
operationLog = []; | ||
@subscribe(ViewModel.prototype.onArrayChange, "arrayChange") | ||
@observableArray array = [1, 2, 3] | ||
onArrayChange(changes) { | ||
this.operationLog.push(...changes); | ||
} | ||
} | ||
``` | ||
<br> | ||
#### <a name="knockout-decorators-component"></a> @component | ||
@@ -300,4 +220,44 @@ Shorthand for registering Knockout component by decorating ViewModel class | ||
#### <a name="knockout-decorators-subscribe"></a> subscribe | ||
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 | ||
``` | ||
| 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()` | | ||
```js | ||
class ViewModel { | ||
@observable field = ""; | ||
constructor() { | ||
subscribe(() => this.field, (value) => { | ||
console.log(value); | ||
}); | ||
subscribe(() => this.field, (value) => { | ||
console.log(value); | ||
}, { once: true }); | ||
subscribe(() => this.field, (value) => { | ||
console.log(value); | ||
}, { event: "beforeChange" }); | ||
} | ||
} | ||
``` | ||
<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` | ||
```js | ||
@@ -338,3 +298,2 @@ unwrap(instance: Object, key: string | symbol): any; | ||
<br> | ||
@@ -360,1 +319,39 @@ | ||
``` | ||
<br> | ||
### <a name="knockout-decorators-changelog"></a> | ||
### Breaking changes from v0.7.1 to 0.8.0 | ||
1. Removed `@subscribe` decorator | ||
2. Removed `@reaction` decorator | ||
3. Added `subscribe(() => this.observableProp, (value) => { ... })` function | ||
4. Added `unwrap(this, "observablePropName")` function | ||
Native `ko.computed` with side effects can be used in all places | ||
where we use `@reaction` decorator. | ||
In v0.7.1 and earlier `@subscribe` decorator can be used only with `@observable` | ||
but not with `@computed`. To avoid this restriction we can create `ko.pureComputed` | ||
and subscribe to it: | ||
```js | ||
class ViewModel { | ||
@computed get computedProp() { ... } | ||
constructor() { | ||
ko.pureComputed(() => this.computedProp).subscribe((value) => { ... }); | ||
} | ||
} | ||
``` | ||
So in 0.8.0 instead of `@subscribe` decorator there is shorthand function `subscribe` | ||
with some extra functionality like "subscribe once": | ||
```js | ||
class ViewModel { | ||
@computed get computedProp() { ... } | ||
constructor() { | ||
subscribe(() => this.computedProp, (value) => { ... }); | ||
} | ||
} | ||
``` |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
72338
-21%376
-20.51%348
-1.14%