DoneJS StealJS jQuery++ FuncUnit DocumentJS
6.6.1
5.33.3 4.3.0 3.14.1 2.3.35
  • Bitovi
    • Bitovi.com
    • Blog
    • Design
    • Development
    • Training
    • Open Source
    • About
    • Contact Us
  • GitHub
  • Twitter
  • Chat
  • Forum
  • News
Bitovi

can-define-connected-singleton

  • Edit on GitHub

can-define-connected-singleton

Singleton decorator for can-define/map/map

Overview

This function is used to extend a DefineMap constructor so that a single instance of persisted data is easily referenced, very much like a singleton. This is useful for situations where you only want access to a single shared instance of a class whose data is loaded from a services. The primary use case for this is for referencing a user's session in a client application.

Usage

import singleton from 'can-define-connected-singleton';
import DefineMap from 'can-define/map/map';

// function wrapper (recommended)
const MyType = singleton(
    DefineMap.extend({ ... })
);

// or as a *legacy* decorator
// SEE: https://github.com/loganfsmyth/babel-plugin-transform-decorators-legacy
const MyType = DefineMap.extend({ ... });

For a practical example of usage refer to the Managing Sessions data guide which uses a can-connect behavior that includes the functionality from this module.

How does it work

Once you have decorated your class, the class will have three new static properties:

  • MyType.current - the current value for the singleton
  • MyType.currentPromise - the promise which should resolve to the value for the singleton
  • MyType.saving - the instance of the singleton currently active as part of an ongoing save request

The first time you read either current or currentPromise, the value will be loaded by calling the static get method on your class. The get method should return a Promise, and this promise will be stored on the static currentPromise property of your class:

const MyType = singleton(
  DefineMap.extend({ ... })
);

// define the static "get" method 
// NOTE: the can-map behavior for can-connect does this for you
MyType.get = function() {
  return Promise.resolve('the value');
}

// triggers a call to MyType.get()
MyType.current;  //-> initially undefined

MyType.currentPromise.then(value => {
  MyType.current === value; //-> true
});

If your service requires you to pass parameters as part of loading the singleton, e.g logging in to retrieve a session model, your application should use the save method on an instance of your class at some point prior to use of current or currentPromise. The save method should return a Promise, and this promise will be stored on the static currentPromise property of the class. The instance being saved will be stored on the static saving property of the class for the duration of the request:

const MyType = singleton(
  DefineMap.extend({   
    // define the static "get" method 
    // NOTE: the can/map behavior for can-connect does this for you
    get: function() {
      return Promise.resolve('the value');
    },
    ...
  }, {
    // define the instance "save" method 
    // NOTE: the can/map behavior for can-connect does this for you
    save: function() {
      return new Promise((resolve, reject) => {
        setTimeout(() => resolve(this), 1000);
      });      
    }
  })
);


const instance = new MyType({ username: 'nils', password: 'foobar' });
const promise = instance.save();

MyType.current;  //-> initially undefined, doesn't start a request since .save is ongoing 
MyType.saving === instance; //-> true
MyType.currentPromise === promise; //-> .currentPromise is the is the ongoing save request 
MyType.currentPromise.then(value => {
  MyType.current === value; //-> true
  MyType.current === instance; //-> true
  MyType.saving; //-> undefined after the .save finished 
});

Configuration options

By default, the singleton decorator uses the following options:

{
  currentPropertyName: 'current',
  savingPropertyName: 'saving',
  fetchMethodName: 'get',
  createMethodName: 'save',
  destroyMethodName: 'destroy'
}

You can specify your own options using the following syntax:

const options = {
  currentPropertyName: 'foo',
  fetchMethodName: 'doFoo'
};

// as a function wrapper (recommended)
const MyType = singleton( options )(
    DefineMap.extend({ ... })
);

// or as a decorator
const MyType = DefineMap.extend({ ... });

Using the above options, your class would be decorated with foo and fooPromise properties instead of current and currentPromise, respectively. Furthermore, the static doFoo method will be invoked instead of the get method for loading the singleton data.

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

On this page

Get help

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