Socket
Socket
Sign inDemoInstall

knockout-decorators

Package Overview
Dependencies
Maintainers
1
Versions
25
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

knockout-decorators

Decorators for use Knockout JS in TypeScript and ESNext environments


Version published
Weekly downloads
225
decreased by-29.69%
Maintainers
1
Weekly downloads
 
Created
Source

Knockout Decorators

Decorators for use Knockout JS in TypeScript and ESNext environments

Build Status GitHub license npm version

Example

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;
  }
}

Documentation

Work with KnockoutValidation

Usage without module loaders

@observable

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

@observableArray

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 }]

@computed

Accessor decorator that wraps ES6 getter and setter (if defined) to hidden ko.pureComputed

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"

@reaction

Replace original method with factory that produces ko.computed from original method

@reaction
@reaction(autoDispose: boolean)
ArgumentDefaultDescription
autoDisposetrueif 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)

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();
  }
}

@extend

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 = "";
  
  getExtender() {
    return { rateLimit: this.rateLimit };
  }
}

@subscribe

Subscribe to @observable by name or by specifying callback explicitely

@subscribe(callback: (value: any) => void, event?: string, autoDispose?: boolean)
@subscribe(targetOrCallback: string | symbol, event?: string, autoDispose?: boolean)
ArgumentDefaultDescription
callbackSubscription handler method
targetOrCallbackName of subscription handler method or name of @observable property
eventnullKnockout subscription event
autoDisposetrueif 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)

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

class ViewModel {
  operationLog = [];
  
  @subscribe(ViewModel.prototype.onArrayChange, "arrayChange")
  @observableArray array = [1, 2, 3]
  
  onArrayChange(changes) {
    this.operationLog.push(...changes);
  }
}

@component

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);
ArgumentDefaultDescription
nameName of component
template"<!---->"Knockout template definition
stylesIgnored 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
});

unwrap

Get internal ko.observable() for property decodated by @observable

unwrap(instance: Object, key: string | symbol): any;
unwrap<T>(instance: Object, key: string | symbol): KnockoutObservable<T>;
ArgumentDefaultDescription
instanceDecorated class instance
keyName of @observable property

KnockoutValidation example

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>

Usage without module loaders (in global scope)

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 = "";
  }
}

Keywords

FAQs

Package last updated on 22 Jan 2017

Did you know?

Socket

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.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc