Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

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

  • 0.8.0
  • Source
  • npm
  • Socket score

Version published
Weekly downloads
41
decreased by-83.53%
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

Changes from v0.7.1

@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

@computed

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"

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

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

  @extend({ rateLimit: 500 })
  @computed get both() {
    return this.first + " " + this.second;
  }
  
  getExtender() {
    return { rateLimit: this.rateLimit };
  }
}

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

subscribe

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
ArgumentDefaultDescription
getDependencyFunction for getting observeble property
callbackCallback that handle dependency changes
optionsnullOptions object
options.oncefalseIf 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" });
  }  
}

unwrap

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

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:

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) => { ... });
  }
}

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