knockout-decorators
Advanced tools
Comparing version 0.2.0 to 0.3.0
@@ -16,9 +16,46 @@ export interface ComponentConstructor { | ||
export declare function component(name: string, template: TemplateConfig, styles: string | string[], options?: Object): ComponentDecorator; | ||
export interface Disposable { | ||
dispose(): void; | ||
} | ||
/** | ||
* Property decorator that creates hidden ko.observable with ES6 getter and setter for it | ||
*/ | ||
export declare function observable(target: Object, key: string | symbol): void; | ||
export declare function observable(prototype: Object, key: string | symbol): void; | ||
/** | ||
* Accessor decorator that wraps ES6 getter and setter to hidden ko.pureComputed | ||
*/ | ||
export declare function computed(target: Object, key: string | symbol): void; | ||
export declare function computed(prototype: Object, key: string | symbol): void; | ||
export interface ObservableArray<T> extends Array<T> { | ||
replace(oldItem: T, newItem: T): void; | ||
remove(item: T): T[]; | ||
remove(removeFunction: (item: T) => boolean): T[]; | ||
removeAll(): T[]; | ||
removeAll(items: T[]): T[]; | ||
destroy(item: T): void; | ||
destroy(destroyFunction: (item: T) => boolean): void; | ||
destroyAll(): void; | ||
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; | ||
} | ||
/** | ||
* Property decorator that creates hidden ko.observableArray with ES6 getter and setter for it | ||
*/ | ||
export declare function observableArray(prototype: Object, key: string | symbol): void; | ||
/** | ||
* Replace original method with factory that produces ko.computed from original method | ||
*/ | ||
export declare function observer(autoDispose: boolean): MethodDecorator; | ||
export declare function observer(prototype: Object, key: string | symbol): void; | ||
/** | ||
* Apply extenders to decorated @observable | ||
*/ | ||
export declare function extend(extenders: Object): PropertyDecorator; | ||
export declare function extend(extendersFactory: () => Object): PropertyDecorator; | ||
/** | ||
* 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; |
@@ -7,5 +7,13 @@ (function (global, factory) { | ||
var extend = ko.utils.extend; | ||
var assign = ko.utils.extend; | ||
var objectForEach = ko.utils.objectForEach; | ||
var defProp = Object.defineProperty.bind(Object); | ||
var getDescriptor = Object.getOwnPropertyDescriptor.bind(Object); | ||
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()` | ||
*/ | ||
@@ -29,3 +37,3 @@ function component(name, template, styles, options) { | ||
return function (constructor) { | ||
ko.components.register(name, extend({ | ||
ko.components.register(name, assign({ | ||
viewModel: constructor.length < 2 ? constructor : { | ||
@@ -42,22 +50,81 @@ createViewModel: function (params, _a) { | ||
} | ||
var defProp = Object.defineProperty.bind(Object); | ||
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); | ||
} | ||
}; | ||
} | ||
/** | ||
* Property decorator that creates hidden ko.observable with ES6 getter and setter for it | ||
*/ | ||
function observable(target, key) { | ||
defProp(target, key, { | ||
function observable(prototype, key) { | ||
defProp(prototype, key, { | ||
get: function () { | ||
var observable = ko.observable(); | ||
defProp(this, key, { | ||
get: observable, | ||
set: observable, | ||
}); | ||
var observable = applyDecorators(this, key, ko.observable()); | ||
defProp(this, key, { get: observable, set: observable }); | ||
return observable(); | ||
}, | ||
set: function (value) { | ||
var observable = ko.observable(value); | ||
defProp(this, key, { | ||
get: observable, | ||
set: observable, | ||
}); | ||
var observable = applyDecorators(this, key, ko.observable()); | ||
defProp(this, key, { get: observable, set: observable }); | ||
observable(value); | ||
}, | ||
@@ -69,44 +136,156 @@ }); | ||
*/ | ||
function computed(target, key) { | ||
var _a = Object.getOwnPropertyDescriptor(target, key), get = _a.get, set = _a.set; | ||
if (!set) { | ||
defProp(target, key, { | ||
get: function () { | ||
var computed = ko.pureComputed(get, this); | ||
defProp(this, key, { | ||
get: computed, | ||
}); | ||
return computed(); | ||
function computed(prototype, key) { | ||
var _a = getDescriptor(prototype, key), get = _a.get, set = _a.set; | ||
defProp(prototype, key, { | ||
get: function () { | ||
var computed = ko.pureComputed(get, this); | ||
defProp(this, key, { get: computed, set: set }); | ||
return computed(); | ||
} | ||
}); | ||
// 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 insideObsArray = false; | ||
defProp(instance, key, { | ||
get: obsArray, | ||
set: function (array) { | ||
if (array) { | ||
arrayMethods.forEach(function (fnName) { return defProp(array, fnName, { | ||
enumerable: false, | ||
value: function () { | ||
if (insideObsArray) { | ||
return Array.prototype[fnName].apply(array, arguments); | ||
} | ||
insideObsArray = true; | ||
var result = obsArray[fnName].apply(obsArray, arguments); | ||
insideObsArray = false; | ||
return result; | ||
} | ||
}); }); | ||
observableArrayMethods.forEach(function (fnName) { return defProp(array, fnName, { | ||
enumerable: false, | ||
value: function () { | ||
insideObsArray = true; | ||
var result = obsArray[fnName].apply(obsArray, arguments); | ||
insideObsArray = false; | ||
return result; | ||
} | ||
}); }); | ||
} | ||
}); | ||
insideObsArray = true; | ||
obsArray(array); | ||
insideObsArray = false; | ||
} | ||
}); | ||
} | ||
/** | ||
* Property decorator that creates hidden ko.observableArray with ES6 getter and setter for it | ||
*/ | ||
function observableArray(prototype, key) { | ||
defProp(prototype, key, { | ||
get: function () { | ||
defObservableArray(this, key); | ||
return this[key]; | ||
}, | ||
set: function (value) { | ||
defObservableArray(this, key); | ||
return this[key] = value; | ||
}, | ||
}); | ||
} | ||
/** | ||
* 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 observer(prototypeOrAutoDispose, key) { | ||
var autoDispose; | ||
if (typeof prototypeOrAutoDispose === "boolean" && key === void 0) { | ||
autoDispose = prototypeOrAutoDispose; // @observer(false) | ||
return decorator; // onSomethingChange() {} | ||
} | ||
else if (typeof prototypeOrAutoDispose === "object" && key !== void 0) { | ||
autoDispose = true; // @observer | ||
decorator(prototypeOrAutoDispose, key); // onSomethingChange() {} | ||
} | ||
else { | ||
defProp(target, key, { | ||
get: function () { | ||
var computed = ko.pureComputed({ | ||
read: get, | ||
write: set, | ||
owner: this, | ||
}); | ||
defProp(this, key, { | ||
get: computed, | ||
set: computed, | ||
}); | ||
return computed(); | ||
}, | ||
set: function (value) { | ||
var computed = ko.pureComputed({ | ||
read: get, | ||
write: set, | ||
owner: this, | ||
}); | ||
defProp(this, key, { | ||
get: computed, | ||
set: computed, | ||
}); | ||
computed(value); | ||
}, | ||
}); | ||
throw new Error("Can not use @observer decorator this way"); | ||
} | ||
function decorator(prototype, key) { | ||
var original = prototype[key]; | ||
prototype[key] = function () { | ||
var _this = this; | ||
var args = slice(arguments); | ||
var computed = ko.computed(function () { return original.apply(_this, args); }); | ||
if (autoDispose) { | ||
getSubscriptions(this).push(computed); | ||
} | ||
return computed; | ||
}; | ||
if (autoDispose) { | ||
redefineDispose(prototype); | ||
} | ||
} | ||
} | ||
/** | ||
* Apply extenders to decorated @observable | ||
* @extendersOrFactory { Object | Function } Knockout extenders definition or factory that produces definition | ||
*/ | ||
function extend(extendersOrFactory) { | ||
return function (prototype, key) { | ||
getDecorators(getMetaData(prototype), key).push({ | ||
type: DecoratorType.Extend, | ||
value: 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; } | ||
return function (prototype, key) { | ||
var _a = 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); | ||
} | ||
}; | ||
} | ||
@@ -116,2 +295,6 @@ exports.component = component; | ||
exports.computed = computed; | ||
exports.observableArray = observableArray; | ||
exports.observer = observer; | ||
exports.extend = extend; | ||
exports.subscribe = subscribe; | ||
@@ -118,0 +301,0 @@ Object.defineProperty(exports, '__esModule', { value: true }); |
@@ -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,i({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,o){u(e,o,{get:function(){var e=t.observable();return u(this,o,{get:e,set:e}),e()},set:function(e){var n=t.observable(e);u(this,o,{get:n,set:n})}})}function r(e,o){var n=Object.getOwnPropertyDescriptor(e,o),r=n.get,i=n.set;i?u(e,o,{get:function(){var e=t.pureComputed({read:r,write:i,owner:this});return u(this,o,{get:e,set:e}),e()},set:function(e){var n=t.pureComputed({read:r,write:i,owner:this});u(this,o,{get:n,set:n}),n(e)}}):u(e,o,{get:function(){var e=t.pureComputed(r,this);return u(this,o,{get:e}),e()}})}var i=t.utils.extend,u=Object.defineProperty.bind(Object);e.component=o,e.observable=n,e.computed=r,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 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,y({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[g];return e.hasOwnProperty(g)||(e[g]=t=y({},t),h(t,function(e,o){t[e]=o.slice()})),t}function r(e,t){return e[t]||(e[t]=[])}function i(e){return e[w]||(e[w]=[])}function u(e,t,o){var n=e[g],r=n&&n[t];return r&&r.forEach(function(t){switch(t.type){case v.Extend:var n=t.value instanceof Function?t.value.call(e):t.value;o=o.extend(n);break;case v.Subscribe:var r=o.subscribe(t.value,e,t.event);t.dispose&&i(e).push(r)}}),o}function s(e){if(!e[E]){e[E]=!0;var t=e.dispose;e.dispose=function(){var e=this[w];if(e&&e.forEach(function(e){e.dispose()}),t)return t.apply(this,arguments)}}}function c(e,o){m(e,o,{get:function(){var e=u(this,o,t.observable());return m(this,o,{get:e,set:e}),e()},set:function(e){var n=u(this,o,t.observable());m(this,o,{get:n,set:n}),n(e)}})}function f(e,o){var n=_(e,o),r=n.get,i=n.set;m(e,o,{get:function(){var e=t.pureComputed(r,this);return m(this,o,{get:e,set:i}),e()}})}function a(e,o){var n=u(e,o,t.observableArray()),r=!1;m(e,o,{get:n,set:function(e){e&&(S.forEach(function(t){return m(e,t,{enumerable:!1,value:function(){if(r)return Array.prototype[t].apply(e,arguments);r=!0;var o=n[t].apply(n,arguments);return r=!1,o}})}),j.forEach(function(t){return m(e,t,{enumerable:!1,value:function(){r=!0;var e=n[t].apply(n,arguments);return r=!1,e}})})),r=!0,n(e),r=!1}})}function p(e,t){m(e,t,{get:function(){return a(this,t),this[t]},set:function(e){return a(this,t),this[t]=e}})}function l(e,o){function n(e,o){var n=e[o];e[o]=function(){var e=this,o=k(arguments),u=t.computed(function(){return n.apply(e,o)});return r&&i(this).push(u),u},r&&s(e)}var r;if("boolean"==typeof e&&void 0===o)return r=e,n;if("object"!=typeof e||void 0===o)throw new Error("Can not use @observer decorator this way");r=!0,n(e,o)}function d(e){return function(t,o){r(n(t),o).push({type:v.Extend,value:e})}}function b(e,t,o){return void 0===o&&(o=!0),function(i,u){var c,f,a=_(i,u),p=a.value,l=a.get;if("function"==typeof p){if("string"!=typeof e&&"symbol"!=typeof e)throw new Error("Subscription target should be a key in decorated ViewModel");c=e,f=p}else if("function"==typeof l)if("function"==typeof e)c=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");c=u,f=i[e]}r(n(i),c).push({type:v.Subscribe,value:f,event:t,dispose:o}),o&&s(i)}}var v,y=t.utils.extend,h=t.utils.objectForEach,m=Object.defineProperty.bind(Object),_=Object.getOwnPropertyDescriptor.bind(Object),k=Function.prototype.call.bind(Array.prototype.slice),g="undefined"!=typeof Symbol?Symbol("ko_decorators"):"__ko_decorators_",w="undefined"!=typeof Symbol?Symbol("ko_decorators_subscriptions"):"__ko_decorators_subscriptions_",E="undefined"!=typeof Symbol?Symbol("ko_decorators_disposable"):"__ko_decorators_disposable_";!function(e){e[e.Extend=0]="Extend",e[e.Subscribe=1]="Subscribe"}(v||(v={}));var S=["pop","push","reverse","shift","sort","splice","unshift"],j=["remove","removeAll","destroy","destroyAll","replace","subscribe"];e.component=o,e.observable=c,e.computed=f,e.observableArray=p,e.observer=l,e.extend=d,e.subscribe=b,Object.defineProperty(e,"__esModule",{value:!0})}); | ||
//# sourceMappingURL=knockout-decorators.min.js.map |
{ | ||
"name": "knockout-decorators", | ||
"version": "0.2.0", | ||
"version": "0.3.0", | ||
"description": "Decorators for use Knockout JS in TypeScript and ESNext environments", | ||
@@ -5,0 +5,0 @@ "main": "dist/knockout-decorators.js", |
178
README.md
@@ -36,5 +36,10 @@ # Knockout Decorators | ||
* [@observable](#knockout-decorators-observable) | ||
* [@observableArray](#knockout-decorators-observableArray) | ||
* [@computed](#knockout-decorators-computed) | ||
* [@observer](#knockout-decorators-observer) | ||
* [@extend](#knockout-decorators-extend) | ||
* [@subscribe](#knockout-decorators-subscribe) | ||
* [@component](#knockout-decorators-component) | ||
#### <a name="knockout-decorators-observable"></a> @observable | ||
@@ -49,7 +54,40 @@ Property decorator that creates hidden `ko.observable` with ES6 getter and setter for it | ||
ko.computed(() => { console.log(model.field); }); // [console] ➜ 123 | ||
model.field = 456; // [console] ➜ 456 | ||
model.field = 456; // [console] ➜ 456 | ||
``` | ||
<br> | ||
#### <a name="knockout-decorators-observableArray"></a> @observableArray | ||
Property decorator that creates hidden `ko.observableArray` with ES6 getter and setter for it | ||
```js | ||
class Model { | ||
@observableArray array = [1, 2, 3]; | ||
}; | ||
let model = new Model(); | ||
ko.computed(() => { console.log(model.field); }); // [console] ➜ [1, 2, 3] | ||
model.field = [4, 5, 6]; // [console] ➜ [4, 5, 6] | ||
``` | ||
Functions from `ko.observableArray` (both Knockout-specific `remove`, `removeAll`, `destroy`, `destroyAll`, `replace`<br> | ||
and redefined `Array.prototype` functions `pop`, `push`, `reverse`, `shift`, `sort`, `splice`, `unshift`) | ||
are also presents in decorated poperty.<br> | ||
They works like if we invoke them on hidden `ko.observableArray`. | ||
And also decorated array has a `subscribe` function from `ko.subscribable` | ||
```js | ||
class Model { | ||
@observableArray array = [1, 2, 3]; | ||
}; | ||
let model = new Model(); | ||
model.array.subscribe((changes) => { console.log(changes); }, null, "arrayChange"); | ||
model.array.push(4); // [console] ➜ [{ status: 'added', value: 4, index: 3 }] | ||
model.array.remove(val => val % 2 === 0); // [console] ➜ [{ status: 'deleted', value: 2, index: 1 }, | ||
// { status: 'deleted', value: 4, index: 3 }] | ||
``` | ||
<br> | ||
#### <a name="knockout-decorators-computed"></a> @computed | ||
Accessor decorator that wraps ES6 getter and setter (if defined) to hidden (maybe writeable) `ko.pureComputed` | ||
Accessor decorator that wraps ES6 getter and setter (if defined) to hidden `ko.pureComputed` | ||
```js | ||
@@ -71,3 +109,120 @@ class Person { | ||
<br> | ||
#### <a name="knockout-decorators-observer"></a> @observer | ||
Replace original method with factory that produces `ko.computed` from original method | ||
```js | ||
@observer | ||
@observer(autoDispose: boolean) | ||
``` | ||
| Argument | Default | Description | | ||
|:------------|:--------|:-------------------------------------------------------------------------------| | ||
| autoDispose | `true` | if true then computed will be disposed when entire decorated class is disposed | | ||
Method that decorated with `@observer` 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 | ||
@observer 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 | ||
Apply extenders to decorated `@observable` | ||
```js | ||
@extend(extenders: Object) | ||
@extend(extendersFactory: () => Object) | ||
``` | ||
Extenders can be defined by plain object or by calling method, that returns extenders-object.<br> | ||
Note that `extendersFactory` invoked with ViewModel instance as `this` argument. | ||
```js | ||
class ViewModel { | ||
rateLimit: 50; | ||
@extend({ notify: "always" }) | ||
@observable first = ""; | ||
@extend(ViewModel.prototype.getExtender) | ||
@observable second = ""; | ||
getExtender() { | ||
return { rateLimit: this.rateLimit }; | ||
} | ||
} | ||
``` | ||
<br> | ||
#### <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 | ||
Shorthand for registering Knockout component by decorating ViewModel class | ||
```js | ||
@@ -78,12 +233,11 @@ @component(name: string, options?: Object); | ||
``` | ||
Shorthand for registering Knockout component by decorating ViewModel class | ||
| argument | description | | ||
|:---------|:-------------------------------------------------------------------| | ||
| name | Name of component | | ||
| template | Knockout template definition | | ||
| styles | Ignored parameter (used for `require()` styles by webpack etc.) | | ||
| options | Another options that passed directly to `ko.components.register()` | | ||
| Argument | Default | Description | | ||
|:---------|:------------------------|:-------------------------------------------------------------------| | ||
| name | | Name of component | | ||
| template | `"<!---->"` | Knockout template definition | | ||
| styles | | Ignored parameter (used for `require()` styles by webpack etc.) | | ||
| options | `{ synchronous: true }` | Another options that passed directly to `ko.components.register()` | | ||
By default components registered with `synchronous` flag. | ||
By default components registered with `synchronous` flag.<br> | ||
It can be overwritten by passing `{ synchronous: false }` as __options__. | ||
@@ -109,4 +263,4 @@ | ||
If ViewModel constructor accepts two or three arguments, | ||
then `createViewModel:` factory is created and | ||
`{ element, templateNodes }` are passed as arguments to ViewModel constructor. | ||
then `createViewModel:` factory is created<br> | ||
and `{ element, templateNodes }` are passed as arguments to ViewModel constructor. | ||
```js | ||
@@ -113,0 +267,0 @@ @component("my-component", |
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
74954
371
287
1