brianlmerritt
brianlmerritt

Reputation: 2852

ES2015 Singleton or Service Provider or Module for Knockout.js components

I have a large number of knockout components, which are currently using KO.postbox to communicate.

I now want to create a central service provider / repository / singleton which centrally stores data for and provides initialization and api and other functions for all of these components.

The page is only one window / session, but each one has 3-7 Knockout components and in some cases the same component is loaded multiple times on the page. The result is a lot of chatter between the component that has loaded data via API and those that also need the same data.

My current approach is to use the singleton pattern (other approaches happily considered). The only non-changeable requirements are:

  1. Store and retrieve data from one central "repository" for multiple KO components
  2. Written in ES2015 and able to work with Babel
  3. Loadable and exportable as a module

The problem with the current code is

a. This is set to undefined by babel, e.g. this.instance = null raises an error of cannot set instance of undefined. b. I am not really sure this is the best method or that I can make it work

The code is below

const ko = require('knockout')
    , moment = require('moment')
    , postbox = require('knockout-postbox')
    , aja = require('aja');


const myServiceSingleton = () =>{ 

    this.instance = null;
    this.array1 = ko.observable([]);
    this.array2 = ko.observable([]);

    // revealing module pattern that handles initialization of our new singleton service provider
    const initializeNewModule = () => {

        const setArray1 = (array) => {
            console.info( 'Set Array1 Called' );
            this.array1(array);
        };

        const getArray1 = () => {
            console.info( 'Get Array1 Called' );
            return this.array1();
        };

        const setArray2 = (array) => {
            console.info( 'Set Array2 Called' );
            this.array2(array);
        };

        const getArray2 = () => {
            console.info( 'Get Array2 Called' );
            return this.array2();
        };

        const myAwesomeFunction = () => {
            // Perform some amazing computations on Array1 and Array 2
        };

        return {
            setArray1 : setArray1,
            getArray1 : getArray1,
            setArray2 : setArray2,
            getArray2 : getArray2,
            myAwesomeFunction : myAwesomeFunction
        };
    };

    // handles the prevention of additional instantiations
    const getInstance = () => {
        if( ! this.instance ) {
            this.instance = new initializeNewModule();
        }
        return this.instance;
    };

    return {
        getInstance : getInstance
    };

};
module.exports = myServiceSingleton;

---------EDIT----------

In the hopes this helps someone else...

const ko = require('knockout')
    , moment = require('moment')
    , postbox = require('knockout-postbox')
    , aja = require('aja');

const array1 = ko.observable([]);
const array2 = ko.observable([]);
const secretFlag = ko.observable(false);

const myAmazingSingleton = {

    setArray1(newArray) {
        console.info( newArray);
        array1(newArray);
    },

    getArray1() {
        console.info( 'Get Array1 Called' );
        return array1();
    },

    getArray2() {
        return array2();
    },

    setArray2(newArray) {
        console.info('Set Array2 Called');
        array2(newArray);
    },

    getArray1Observable() {
        return array1 ;
    },

    myAwesomeFunction() {
        // Perform some amazing computations on Array1 and Array 2
        array1.map //etc etc
    }
};

export default myAmazingSingleton ;

To use is quite simple:

import ArrayFunctions from 'myAmazingSingleton';
let array1 = ArrayFunctions.getArray1();

And the data is available across multiple Knockout components

Upvotes: 0

Views: 771

Answers (2)

Bergi
Bergi

Reputation: 664569

You cannot use an arrow function as a constructor. You really should just use a simple object literal:

const myServiceSingleton = {
    array1: ko.observable([]),
    array2: ko.observable([]),
    setArray1(array) {
        console.info( 'Set Array1 Called' );
        this.array1(array);
    },
    getArray1() {
        console.info( 'Get Array1 Called' );
        return this.array1();
    },
    setArray2(array) {
        console.info( 'Set Array2 Called' );
        this.array2(array);
    },
    getArray2() {
        console.info( 'Get Array2 Called' );
        return this.array2();
    },
    myAwesomeFunction() {
        // Perform some amazing computations on Array1 and Array 2
    }
};

If you insist, you can make an

export function getInstance() {
    return myServiceSingleton;
}

or even lazy-initialise it, but usually you should just export default it.

Upvotes: 1

Roy J
Roy J

Reputation: 43881

I think the singleton you want to use is a master viewmodel, by which I mean just the usual way of setting up Knockout. Use the components' params to pass from the master viewmodel any observables that your components need to share.

Upvotes: 1

Related Questions