DoneJS StealJS jQuery++ FuncUnit DocumentJS
4.3.0
5.0.0 3.13.1 2.3.35
  • About
  • Guides
  • API Docs
  • Community
  • Contributing
  • Bitovi
    • Bitovi.com
    • Blog
    • Design
    • Development
    • Training
    • Open Source
    • About
    • Contact Us
  • About
  • Guides
  • API Docs
    • Observables
      • can-bind
      • can-compute
      • can-debug
      • can-define
      • can-define/list/list
      • can-define/map/map
      • can-define-backup
      • can-define-stream
      • can-define-stream-kefir
      • can-event-queue
      • can-kefir
      • can-list
      • can-map
      • can-map-define
      • can-observation
      • can-observation-recorder
      • can-observe
      • can-simple-map
      • can-simple-observable
      • can-stream
      • can-stream-kefir
      • can-value
    • Data Modeling
      • can-connect
      • can-connect-feathers
      • can-fixture
      • can-fixture-socket
      • can-ndjson-stream
      • can-set
    • Views
      • can-component
      • can-stache
      • can-stache-bindings
      • can-stache-converters
      • can-stache-route-helpers
      • can-view-autorender
      • can-view-callbacks
      • can-view-import
      • can-view-live
      • can-view-model
      • can-view-nodelist
      • can-view-parser
      • can-view-scope
      • can-view-target
      • react-view-model
      • react-view-model/component
      • steal-stache
    • Routing
      • can-deparam
      • can-param
      • can-route
      • can-route-hash
      • can-route-mock
      • can-route-pushstate
    • JS Utilities
      • can-assign
      • can-define-lazy-value
      • can-diff
      • can-globals
      • can-join-uris
      • can-key
      • can-key-tree
      • can-make-map
      • can-parse-uri
      • can-queues
      • can-string
      • can-string-to-any
      • can-util
      • can-zone
      • can-zone-storage
    • DOM Utilities
      • can-ajax
      • can-attribute-encoder
      • can-child-nodes
      • can-control
      • can-dom-data
      • can-dom-events
      • can-dom-mutate
      • can-event-dom-enter
      • can-event-dom-radiochange
      • can-fragment
    • Data Validation
      • can-define-validate-validatejs
      • can-validate
      • can-validate-interface
      • can-validate-legacy
      • can-validate-validatejs
    • Typed Data
      • can-cid
      • can-construct
      • can-construct-super
      • can-data-types
      • can-namespace
      • can-reflect
      • can-reflect-dependencies
        • addMutatedBy
        • deleteMutatedBy
        • getDependencyDataOf
      • can-reflect-promise
      • can-types
    • Polyfills
      • can-symbol
      • can-vdom
    • Core
    • Infrastructure
      • can-global
      • can-test-helpers
    • Ecosystem
    • Legacy
  • Community
  • Contributing
  • GitHub
  • Twitter
  • Chat
  • Forum
  • News
Bitovi

can-reflect-dependencies

  • Edit on GitHub

Functions to keep track of observable dependencies.

Object

The can-reflect-dependencies package provides methods used to register and retrieve observable dependencies.

Exports an object with the following methods:

{
    addMutatedBy,        // Register observable mutation dependencies
    deleteMutatedBy,     // Delete observable mutation dependencies
    getDependencyDataOf // Get the dependencies of an observable
}

Use

There are two steps to keep in mind in order to reliably keep track of observable dependencies:

  • Register what affects the observable and,
  • Register what the observable affects (the opposite of the first step)

Register what affects the observable

If the observable derives its value from other observables internally, at least one of the following symbols must be implemented:

  • @@can.getKeyDependencies: The key dependencies of the observable
  • @@can.getValueDependencies: The value dependencies of the observable

In the following example MyCustomObservable uses an Observation instance internally to derive its value:

import canSymbol from "can-symbol";
import observation from "can-observation";

function MyCustomObservable( value ) {
    this.observation = new Observation( /* ... */ );
}

MyCustomObservable.prototype.get = function() {
    return this.observation.get();
};

Since MyCustomObservable is a value-like observable, it has to implement @@can.getValueDependencies so this dependency is visible to getDependencyDataOf.

import canReflect from "can-reflect";

function MyCustomObservable() { /* ... */ }

canReflect.assignSymbols( MyCustomObservable, {
    "can.getValueDependencies": function() {
        return {
            valueDependencies: new Set( [ this.observation ] )
        };
    }
} );

It's possible that a specific instance's value of MyCustomObservable is set by another observable in a specific context, this kind of dependecy won't be registered by the symbols discussed so far.

The following example shows two observables, a map-like instance someMap and a value-like instance myObservable. When the foo property of someMap changes, it sets the value of myObservable, in order to keep track of this dependency, addMutatedBy has to be used as follows:

const someMap = new SomeMap();
const myObservable = new MyCustomObservable();
import canReflectDeps from "can-reflect-dependencies";

// when the foo property changes, update myObservable
someMap.on( "foo", function() {
    myObservable.set( /* some value */ );
} );

// Register that `myObservable` is affected by the `foo` property of `someMap`
canReflectDeps.addMutatedBy( myObservable, {
    keyDependencies: new Map( [ [ someMap, new Set( [ "foo" ] ) ] ] )
} );

If this dependency is conditional, it's important to call deleteMutatedBy to remove the dependency from can-reflect-dependencies internal registry, e.g:

/* code omitted for brevity */

if ( hasToStopListeningToFooChanges ) {

    // remove the event listener
    someMap.off( "foo", onFooChange );

    // remove the dependency from `can-reflect-dependencies`
    canReflectDeps.deleteMutatedBy( myObservable, {
        keyDependencies: new Map( [ [ someMap, new Set( [ "foo" ] ) ] ] )
    } );
}

Register the observable changes

In the previous section, addMutatedBy was used to register that someMap.foo affects myObservable's value; in its current form can-reflect-dependencies can only see the dependency from myObservable, that means:

// this works!
canReflectDeps.getDependencyDataOf( myObservable );

// but this does not, it returns `undefined` :(
canReflectDeps.getDependencyDataOf( someMap, "foo" );

In order to register the dependency in the opossite direction, the following things need to happen:

  • SomeMap must implement the @@can.getWhatIChange symbol
  • Event handlers must keep track of the observables affected by implementing the @@can.getChangesDependencyRecord symbol.

CanJS observables make this easier by attaching event handling capabilities through can-event-queue mixins, adding in the value mixin to SomeMap's prototype will add a base implementation of @@can.getWhatIChange which iterates over the registered handlers and calls @@can.getChangesDependencyRecord on each.

Having @@can.getWhatIChange implemented by can-event-queue, the next thing to do is to implement @@can.getChangesDependencyRecord on the event handler that mutates myObservable.

/* code omitted for brevity */

// Bind the callback to a variable to make adding the symbol easier
const onFooChange = function() {
    myObservable.set( /* some value */ );
};

canReflect.assignSymbols( onFooChange, {
    "can.getChangesDependencyRecord": function() {
        return {
            valueDependencies: new Set( [ myObservable ] )
        };
    }
} );

someMap.on( "foo", onFooChange );

With this in place the following code should work now:

canReflectDeps.getDependencyDataOf( someMap, "foo" ); // ...myObservable

Note: This implementation requires can-event-queue/value/value mixin to be added to SomeMap's prototype, if your observable uses custom event handling logic you need to implement @@can.getWhatIChange and keep track of what the event handlers are mutating manually.

CanJS is part of DoneJS. Created and maintained by the core DoneJS team and Bitovi. Currently 4.3.0.

On this page

Get help

  • Chat with us
  • File an issue
  • Ask questions
  • Read latest news