Security News
RubyGems.org Adds New Maintainer Role
RubyGems.org has added a new "maintainer" role that allows for publishing new versions of gems. This new permission type is aimed at improving security for gem owners and the service overall.
knockout-decorators
Advanced tools
Decorators for use Knockout JS in TypeScript and ESNext environments
Decorators for use Knockout JS in TypeScript and ESNext environments
import { observable, computed, component } from "knockout-decorators";
@component("person-view", `
<div>Name: <span data-bind="text: fullName"></span></div>
<div>Age: <span data-bind="text: age"></span></div>
`)
class PersonView {
@observable firstName: string;
@observable lastName: string;
@observable age: string;
@computed get fullName() {
return this.firstName + " " + this.lastName;
}
constructor({ firstName, lastName, age }, element, templateNodes) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}
}
Property decorator that creates hidden ko.observable
with ES6 getter and setter for it
class Model {
@observable field = 123;
};
let model = new Model();
ko.computed(() => { console.log(model.field); }); // [console] ➜ 123
model.field = 456; // [console] ➜ 456
Accessor decorator that wraps ES6 getter to hidden ko.pureComputed
Setter is not wrapped to hidden ko.pureComputed
and stays unchanged
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"
Property decorator that creates hidden ko.observableArray
with ES6 getter and setter for it
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
and redefined Array.prototype
functions pop
, push
, reverse
, shift
, sort
, splice
, unshift
)
are also presents in decorated poperty.
They works like if we invoke them on hidden ko.observableArray
.
And also decorated array has a subscribe
function from ko.subscribable
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 }]
Apply extenders to decorated @observable
@extend(extenders: Object)
@extend(extendersFactory: () => Object)
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.
class ViewModel {
rateLimit: 50;
@extend({ notify: "always" })
@observable first = "";
@extend(ViewModel.prototype.getExtender)
@observable second = "";
@extend({ rateLimit: 500 })
@computed get both() {
return this.first + " " + this.second;
}
getExtender() {
return { rateLimit: this.rateLimit };
}
}
Shorthand for registering Knockout component by decorating ViewModel class
@component(name: string, options?: Object);
@component(name: string, template: any, options?: Object);
@component(name: string, template: any, styles: any, options?: Object);
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.
@component("my-component")
class Component {
constructor(params: any) {}
}
// ▼▼▼ results to ▼▼▼
ko.components.register("my-component", {
viewModel: Component,
template: "<!---->",
synchronous: true,
});
If ViewModel constructor accepts two or three arguments,
then createViewModel:
factory is created
and { element, templateNodes }
are passed as arguments to ViewModel constructor.
@component("my-component",
require("./my-component.html"),
require("./my-component.css"), {
synchronous: false,
additionalData: { foo: "bar" } // consider non-standard field
})
class Component {
constructor(
private params: any,
private element: Node,
private templateNodes: Node[]
) {}
}
// ▼▼▼ results to ▼▼▼
ko.components.register("my-component", {
viewModel: {
createViewModel(params, { element, templateNodes }) {
return new Component(params, element, templateNodes);
}
},
template: require("./my-component.html"),
synchronous: false,
additionalData: { foo: "bar" } // consider non-standard field
});
Subscribe to @observable
or @computed
dependency with creation of hidden ko.computed()
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() |
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" });
}
}
Get internal ko.observable()
for property decodated by @observable
or internal ko.pureComputed()
for property decodated by @computed
unwrap(instance: Object, key: string | symbol): any;
unwrap<T>(instance: Object, key: string | symbol): KnockoutObservable<T>;
Argument | Default | Description |
---|---|---|
instance | Decorated class instance | |
key | Name of @observable property |
class MyViewModel {
@extend({ required: "MyField is required" })
@observable myField = "";
checkMyField() {
alert("MyField is valid: " + unwrap(this, "myField").isValid());
}
// pass `unwrap` function to data-bindings
unwrap(key: string) {
return unwrap(this, key);
}
}
<div>
<input type="text" data-bind="value: myField"/>
<button data-bind="click: checkMyField">check</button>
<p data-bind="validationMessage: unwrap('myField')"></p>
</div>
layout.html
<script src="/{path_to_vendor_scrpts}/knockout.js"></script>
<script src="/{path_to_vendor_scrpts}/knockout-decorators.js"></script>
script.ts
namespace MyTypescriptNamespace {
// import from TypeScript namespace (JavaScript global variable)
const { observable, computed } = KnockoutDecorators;
export class MyClass {
@observable field = "";
}
}
@subscribe
decorator@reaction
decoratorsubscribe(() => this.observableProp, (value) => { ... })
functionunwrap(this, "observablePropName")
functionNative 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:
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":
class ViewModel {
@computed get computedProp() { ... }
constructor() {
subscribe(() => this.computedProp, (value) => { ... });
}
}
[0.8.0] - 2017-01-22
subscribe(() => this.observableProp, (value) => { ... })
function@extend
decorator now can be combined with @computed
decorator@subscribe
decorator@reaction
decoratorNative ko.computed
with side effects can be used in all places where we use
@reaction
or @observer
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:
class ViewModel {
@computed get computedProp() { ... }
constructor() {
ko.pureComputed(() => this.computedProp).subscribe((value) => { ... });
}
}
So from v0.8.0 instead of @subscribe
decorator there is shorthand function subscribe
with some extra functionality like "subscribe once":
class ViewModel {
@computed get computedProp() { ... }
constructor() {
subscribe(() => this.computedProp, (value) => { ... });
}
}
FAQs
Decorators for use Knockout JS in TypeScript and ESNext environments
The npm package knockout-decorators receives a total of 0 weekly downloads. As such, knockout-decorators popularity was classified as not popular.
We found that knockout-decorators demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
RubyGems.org has added a new "maintainer" role that allows for publishing new versions of gems. This new permission type is aimed at improving security for gem owners and the service overall.
Security News
Node.js will be enforcing stricter semver-major PR policies a month before major releases to enhance stability and ensure reliable release candidates.
Security News
Research
Socket's threat research team has detected five malicious npm packages targeting Roblox developers, deploying malware to steal credentials and personal data.