74533

Global variables for a single web page application using Durandal

Question:

My application has three things that are "global": a user location with lat and lng values, if the user is logged in or not, and the activity that they have chosen (think of it like a category).

What I'd like to happen is for these values to be used across the application. Each screen will use some set of this date, so loading it per page isn't the right answer. The information also will not change unless the user creates an event: different location, activity, or signs in/out.

How should I set this up?

My thinking is that there should be an initial load upon startup, in my main.js file I load each and then app.start like so:

<strong>siteService.js</strong>

define([...], function (...) { return { init: function () { return $.when( activityService.getStoredActivity(), locationService.getStoredLocation(), userService.getUsername() ); } } });

<strong>main.js</strong>

define([..., 'modules/siteService'], function (..., site) { ...

site.init().then(function () { app.start().then(function () { ... app.setRoot('viewmodels/shell', 'entrance'); }); });

});

This does make sure that the data is loaded. From there in each of my services I store the information into local storage so I don't have to keep firing off AJAX calls. If local storage isn't there it will.

The problem comes later when I need this data in other pages. I'm ending up calling my site.init().then() within each page's activate method. Which is pretty sloppy.

<strong>shell.js</strong>

self.activate = function () { ... siteService.init().then(function (activity, location, username) { self.activity(activity); self.location(location); setUsername(username); }); return router.activate(); };

and then again in:

<strong>welcome.js</strong>

this.activate = function () { site.init().then(function(activity, location) { loc = location; act = activity; load(); }); });

I'd like to set the values initially, each page initially loading from those values, and then use the pub/sub of Durandal to react to any changes after the fact.

I was thinking of solely using the pub/sub, but then felt that I would run into chicken and the egg issues when the data is loaded in relation to the page loads (IE If my AJAX loaded first, and my page wasn't loaded, the event fires, but the page never gets the update).

Is it possible to setup something like a static class in javascript to pull the data initially and then share it throughout the viewmodels? Or, is there a way to guarantee that the value will be there when I initially load a page, like a app.on('value', { initialize: true })?

Answer1:

When a require.js module returns an object, that object is a singleton, so you can have something like

<strong>settings.js</strong>

define(function() { return { location: { lat: 0, long: 0 }, loggedin: false, activity: "" } });

Then any time you name settings as a dependency, you'll always get a reference to the same object and you can get/set its fields as you wish.

Answer2:

Changed <strong>siteService.js</strong> to:

define([...], function (...) { var module = function () { }; module.prototype.activity = {}; module.prototype.location = {}; module.prototype.username = {}; module.prototype.init = function () { return $.when( activityService.getStoredActivity(), locationService.getStoredLocation(), userService.getUsername() ).then(function (activity, location, username) { module.prototype.activity = activity; module.prototype.location = location; module.prototype.username = username; }); }; return module; });

<strong>main.js</strong> to

define([..., 'modules/siteService'], function(..., Site) { site = new Site(); site.init().then(function () { app.start().then(function () { ... app.setRoot('viewmodels/shell', 'entrance'); }); });

and <strong>shell.js</strong> and <strong>welcome.js</strong> now just use:

var site = new Site(); self.activity(site.activity); self.location(site.location); setUsername(site.username);

and

var site = new Site(); loc = site.location; act = site.activity;

Recommend