knockout-decorators
Advanced tools
Comparing version 0.7.1 to 0.8.0
@@ -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
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
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
72338
376
348