Knockout Decorators
Decorators for use Knockout JS in TypeScript and ESNext environments
Example
; @ @observable firstName: string; @observable lastName: string; @observable age: string; @computed { return thisfirstName + " " + thislastName; } { thisfirstName = firstName; thislastName = lastName; thisage = age; }
Documentation
- @observable
- @computed
- @observableArray
- @extend
- @component
- @autobind
- @event
- subscribe
- unwrap
- Disposable() mixin
@observable
Property decorator that creates hidden ko.observable
with ES6 getter and setter for it
If initialized by Array then hidden ko.observableArray
will be created (see @observableArray)
@;@observable;
By default, shallow observable will be created
; @observable field = 123; @observable collection = ;;let model = ; ko; // [console] ➜ 123modelfield = 456; // [console] ➜ 456
If { deep: true }
option is provided then all nested object properties
are recursively converted to @observable
; @ deepObservable = // like @observable firstName: "Clive Staples" // like @observable lastName: "Lewis" // like @observable array: // like @observableArray object: // like @observable foo: "bar" // like @observable reference: null // like @observable const vm = ; vmdeepObservableobjectreference = firstName: "Clive Staples" // make @observable lastName: "Lewis" // make @observable; vmdeepObservablearray;
If { expose: true }
option is provided then hidden ko.observable
will be
exposed as non-enumerable property with same name prefixed by _
.
; @ field = 123;; const model = ;const hiddenObservable = model_field; // ko.observable
@computed
Accessor decorator that wraps ES6 getter to hidden ko.computed
or ko.pureComputed
@;@computed;
By default it creates hidden ko.pureComputed
Setter is not wrapped to hidden ko.pureComputed
and stays unchanged
; @observable firstName = ""; @observable lastName = ""; @computed { return thisfirstName + " " + thislastName; } { thisfirstName thislastName = value; } @ { return thisfirstName + "." + thisLastName+ "."; }let person = ; ko; personfullName = " John Smith " // [console] ➜ "John Smith"
@observableArray
Property decorator that creates hidden ko.observableArray
with ES6 getter and setter for it
@;@observableArray;
By default, shallow observableArray will be created
; @observableArray array = 1 2 3;;let model = ; ko; // [console] ➜ [1, 2, 3]modelfield = 4 5 6; // [console] ➜ [4, 5, 6]
Functions from ko.observableArray
(both Knockout-specific remove
, removeAll
, destroy
, destroyAll
, replace
and redefined Array.prototype
functions pop
, push
, reverse
, shift
, sort
, splice
, unshift
)
are also presents in decorated property.
They works like if we invoke them on hidden ko.observableArray
.
And also decorated array has:
- a
subscribe(callback: (value: any[]) => void)
function fromko.subscribable
,
; @observableArray array = 1 2 3 as ObservableArray<number>;;let model = ;modelarray; modelarray; // [console] ➜ [{ status: 'added', value: 4, index: 3 }]modelarray; // [console] ➜ [{ status: 'deleted', value: 2, index: 1 }, // { status: 'deleted', value: 4, index: 3 }]
- a new
mutate(callback: () => void)
function that runs callback in which we can mutate array directly,
; @observableArray array = 1 2 3 as ObservableArray<number>;; let model = ; modelarray;
- a new
set(i: number, value: any): any
function that sets a new value at specified index and returns the old value.
; @observableArray array = 1 2 3 as ObservableArray<number>;; let model = ; let oldValue = modelarray // this change is observed console; // [console] ➜ [1, 2, 300]console; // [console] ➜ 3
@extend
Apply extenders to decorated @observable
, @observableArray
or @computed
@;@;
Extenders can be defined by plain object or by calling method, that returns extenders-object.
Note that extendersFactory
invoked with ViewModel instance as this
argument.
; rateLimit: 50; @ @observable first = ""; @ @observable second = ""; @ @computed { return thisfirst + " " + thissecond; } { return rateLimit: thisrateLimit ; }
Caveats
@extend({ notify: "always" })
will not work with: subscribe()
function.
Instead we can use unwrap()
. But other extenders should work.
const vm = ;// this subscription will run only when `vm.first` actually changed;// use `unwrap()` function to get RAW ko.observable();
@component
Shorthand for registering Knockout component by decorating ViewModel class
@;@;@;
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.
It can be overwritten by passing { synchronous: false }
as options.
If template is not specified then it will be replaced by HTML comment <!---->
If ViewModel constructor accepts zero or one arguments,
then it will be registered as viewModel:
in config object.
; @ {}// ▼▼▼ results to ▼▼▼kocomponents;
If ViewModel constructor accepts two or three arguments,
then createViewModel:
factory is created
and { element, templateNodes }
are passed as arguments to ViewModel constructor.
; @ {}// ▼▼▼ results to ▼▼▼kocomponents;
@autobind
Bind class method to class instance. Clone of core-decorators.js @autobind
; @ @observable array = 1 2 3 as ObservableArray<number>; @autobind { thisarray; }
@event
Create subscribable function that invokes it's subscribers when it called.
All arguments that passed to @event
function are translated to it's subscribers.
Internally uses hidden ko.subscribable
.
Subscribers can be attached by calling .subscribe()
method of EventType
type or by subscribe()
utility.
; @event myEvent: EventType; { producermyEvent; // `subscription` type is `ko.Subscription` const subscription = producermyEvent; } @autobind { console; } const producer = ;const consumer = producer; // emit @eventproducer;// [console] ➜ lambda: 123 "test"// [console] ➜ method: 123 "test"
subscribe
Subscribe to @observable
(or @computed
) dependency with creation of hidden ko.computed()
subscribe<T> T void options?: once?: boolean event?: string : koSubscription;
Or subscribe to some @event
property
subscribe<T1 T2 ...> void void options?: once?: boolean : koSubscription;
Argument | Default | Description |
---|---|---|
dependencyOrEvent | (1) Function for getting observable property (2) @event property | |
callback | Callback that handle dependency changes or @event notifications | |
options | null |
Options object |
options.once | false |
If true then subscription will be disposed after first invocation |
options.event | "change" |
Event name for passing to Knockout native subscribe() |
Subscribe to @observable
changes
; @observable field = 123; { ; ; ; }
; @event void; { ; ; // `subscription` type is `ko.Subscription` const subscription = ; // unsubscribe from @event subscription; // emit @event this }
unwrap
Get hidden ko.observable()
for property decodated by @observable
or hidden ko.pureComputed()
for property decodated by @computed
: any;unwrap<T>instance: Object key: string | symbol: koObservable<T>;
Argument | Default | Description |
---|---|---|
instance | Decorated class instance | |
key | Name of @observable property |
Using { expose: true }
:
; @ @ myField = ""; { ; }
check
Using unwrap()
:
; @ @observable myField = ""; { ; } // pass `unwrap` function to data-bindings { return ; } // from TypeScript 2.1 you can use keyof // to restrict to keys of the given type { return ; }
check
Disposable() mixin
Mixin that injects to class shorthands for utility functions and provides automatic disposing of created subscriptions (see MDN or TypeScript 2.2 docs)
Disposable.subscribe(...)
Shorthand forsubscribe()
utility function that also store created subscription in hidden class property.Disposable.dispose()
Automatically dispose all subscriptions created byDisposable.subscribe(...)
method.Disposable.unwrap()
Shorthand forunwrap()
utility function that returns hidden Knockout observable for decorated class property.
; Base @observable text = ""; @ { return thistext; } { super; // subscribe to computed changes // and store created subscription in hidden class property this; } { // dispose all subscriptions that created by this.subscribe() super; // unwrap and dispose hidden Knockout computed this; } // Base class is optional
Usage without module loaders (in global scope)
layout.html
script.ts
namespace MyTypescriptNamespace // import from TypeScript namespace (JavaScript global variable) const observable computed = KnockoutDecorators; @observable field = "";