npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@purtuga/observables

v2.4.0

Published

observe things and get notified when those things mutate

Downloads

14

Readme

observables

Enable the ability for Arrays and Objects to notify listeners when their content changes. Use it to create reactive interfaces or trigger logic when data changes. In addition, Computed Properties - properties whose value is generated when dependencies changes - can also be created.

Installation

$ npm install @purtuga/observables --save

Observe Objects

Observable Objects are normal JavaScript Objects whose properties can be "watched" for changes.

import { objectWatchProp } from "observables"

const user = {
    firstName: "Paul", 
    lastName: "Tavares" 
};
const unWatchFirst = objectWatchProp(user, "firstName", () => console.log("first named changed"));

user.firstName = "Jack"; // console: "first named changed"

// Stop listening to event
unWatchFirst();

To get notified of all changes to the object, leave the second param empty

objectWatchProp(user, null, () => console.log("Object changed."));

Observe Arrays

Arrays, like object, can be observed for changes. Note however, that due to the nature of Arrays in javascript, not all changes can be captured - example: direct array member creation (arr[arr.length] = 'new value')can not be detected. Only changes that are done via the Array's mutating methods will notify Watchers of changes.

import { watchArray } from "observables"

const users = [];
const unWatchUsers watchArray(users, () => console.log("array changed"));

users.push("Paul", "Jack"); // Console: "array changed"

// Stop listening for changes
unWatchUsers();

Observed Arrays size Property

Arrays that are made Observable will also have a new Array property (read only) named size which return the size (length) of the array with one added value: any dependency trackers are automatically set as Watchers of the array. This makes it ideal to use with Computed Properties (see below)

import { watchArray } from "observables"

const users = [];
watchArray(users);

users.push(1, 2);
console.log(users.size); // Console: 2

Convert Objects/Arrays to Observables

To recursively convert objects and arrays to observables, use the makeObservable method. This is ideal when wanting to create computed properties (see below) that may have dependencies comming from multiple objects or arrays.

import { makeObservable } from "observable"

const obj = {
    key1: {
        key1_1: {
            key1_1_1: "value 1-1-1"
        },
        keys1_2: [
            {
                k1: "v1"
            }
        ]
    }
};

makeObservable(obj); // Makes all Objects/Arrays observable

Create Computed Properties

Computed properties allow you to create properties whose value is generated via a callback function. If that callback function, in its logic, uses properties from any observable property, then it will notify its watchers and cause the value to be regenerated. Computed properties, like other observable object properties, can be watched for changes.

import {
    objectCreateComputedProp,
    makeObservable
} from "observables"

const user = {
    firstName: "Paul", 
    lastName: "Tavares" 
};

makeObservable(user);

// Create `fullName` computed property
objectCreateComputedProp(user, "fullName", () => `${ user.firstName } ${ user.lastname }`);

console.log(user.fullName); // Console: Paul Tavares

user.firstName = "Jack";
console.log(user.fullName); // Console: Jack Tavares

Computed properties also work with Arrays that have been converted to observables. Here is an example:

import {
    objectCreateComputedProp,
    makeObservable
} from "observables"

const user = {
    firstName: "Paul", 
    lastName: "Tavares",
    permissions: [
        "view",
        "create"
    ]
};

makeObservable(user);

objectCreateComputedProp(user, "summary", () => `${ user.firstName } ${ user.lastname } Has ${ user.permissions.size } Permission(s)`);

console.log(user.summary); // Console: "Paul Tavares Has 2 Permission(s)"

user.permissions.push("modify");
console.log(user.summary); // Console: "Paul Tavares Has 3 Permission(s)"

Decorators

The following decorators are available under src/decorators (but not in the built module).

NOTE: Decorators (as of this writing) are at Stage 2 and continue to undergo heavy changes to its spec. The decorators provided here are supported only in Build chains that use Babel + @babel/plugin-proposal-decorators plugin with the legacy options set to false. They are based from a spec version that was valid around the start of 2019 (this one) If you are using Typescript, this is likely not to work since the version of Decorators Typescript supports is an erlier spec version. For more about decorators, see https://github.com/tc39/proposal-decorators#faq

DependencyTracker Class Decorator

Intercept Class methods and setup observable dependency trackers prior to executing the method.

Example:

import {makeObservable} from "@purtuga/observables";
import {trackObservableDependencies} from "@purtuga/observables/src/decorators/DependencyTracker.js"

//============================================================

@trackObservableDependencies({
    track: {
        // When `getValue()` method is called, adds `_updateValue` as a change notifier
        // for any observable data that is accessed
        getName() { return this._updateValue; }
    },
    stop: {
        // When `destroy()` is called, it will remove `_updateValue` method from any
        // Observable that has it as a change notifier 
        destroy() {
            return this._updateValue;
        }
    }
})
class Person {
    constructor(data) {
        this._data = makeObservable(data);
    }
    
    _value = "";
    _updateValue = () => this._value = `${this._data.first} ${this._data.last}`;
    
    getName() {
        return this._value;
    }
}

const data = {
    first: "Paul",
    last: "Tavares"
};
const person = new Person(data);

console.log(person.getName()); // Paul Tavares

data.first = "Bill";
data.last = "Smith";

console.log(person.getName()); // Bill Smith

This decorator takes the following options:

  • track: {Object} The methods that will be intercepted. The Object's Key is the method name and its value must be a function that returns the callback that will be used as the dependency tracker notifier. The Function will be called with a context (this) of the Class instance and will also be given that class instance as the first argument. NOTE: the returned method should probably (in most cases) be a class bound method since it will be called as if it was a "global" function not a method.
  • stop: {Object} The methods that will be intercepted in order to remove all dependency track notification from a tracker notifier. Just like the above options, this object's key value must be a function that will be called with the class instance.

License

MIT License