2

I have an Angular factory called 'DashboardState', which I am currently modifying to handle an API call via my dashboardcontext Angular service layer.

Currently all my widget data is persisted to the localStorage object; however, I am now hooking into a new c# API layer which sets/gets the layout widgets to/from permanent storage (i.e. Sql Server database in this case).

My primary issue is what happens when the promise is returned at return this._getItemFromAPI($rootScope).then of the load: function.

In the load: section, this is a valid object with methods on the stack; however, within the .then section I suddenly lose access to this..

It's a problem for me because I can no longer call this._handleSyncLoad. And as you can see, I've tried a hack by assigning var handleSync = this._handleSyncLoad;, however it doesn't solve the scope issue.:

  if (dashboardId != null && dashboardId != undefined) {

                  this.storage.setItem("defaultDashboardId", dashboardId);

                  var handleSync = this._handleSyncLoad;
                  return this._getItemFromAPI($rootScope).then(function (data) {                           
                      // save current current dashboard id for next time - 06/11/2015 BM:      

                      return handleSync(data, true);    

                   });                      
              }

Here's the full listing of 'DashboardState' :

angular.module('ui.dashboard')
.factory('DashboardState', ['$log', '$q', 'dashboardcontext', '$rootScope', function ($log, $q, dashboardcontext, $rootScope) {
function DashboardState(storage, id, hash, widgetDefinitions, stringify) {
  this.storage = storage;
  this.id = id;
  this.hash = hash;
  this.widgetDefinitions = widgetDefinitions;
  this.stringify = stringify;
}

DashboardState.prototype = {
  /**
   * Takes array of widget instance objects, serializes, and saves state.
   * 
   * @param  {Array} widgets  scope.widgets from dashboard directive
   * @return {Boolean}        true on success, false on failure
   */
 save: function (widgets) {             
      // CODE OMITTED FOR SAVE FUNCTION
      return true;
  },                   
 load: function (dashboardId) {        // sent in from navigation-controller call 

      var useLocalStorage = false;     // retrieve from localStorage or via API layer

      var serialized;

      if (useLocalStorage) {           // retrieve dashboard layout from localStorage                  
	      // COME CODE OMITTED FOR BREVITY
	      serialized = this.storage.getItem(dashboardId);                  
	  }
      }
      else {                  

	  // FETCH DASHBOARD HERE !! 
	  if (dashboardId != null && dashboardId != undefined) {

	      // *** "this" is available on the scope at this point ***
	      this.storage.setItem("defaultDashboardId", dashboardId);

	      var handleSync = this._handleSyncLoad;
	      return this._getItemFromAPI($rootScope).then(function (data) {                                                         
		  // *** "this" IS NO LONGER AVAILABLE ON THE SCOPE - i.e. I can no longer call this._handleSyncLoad from here ***
		  return handleSync(data, true);    

	       });                      
	  }
	  else {
	      // revert to original line; see dashboardOptions to main-controller
	      serialized = this.storage.getItem(this.id);
	  }                  
      }

      if (serialized) {
	  // check for promise
	  if (angular.isObject(serialized)) {    // && angular.isFunction(serialized.then)) {    // modifed line 09/04/2015 BM:
	      return this._handleAsyncLoad(serialized);
	  }
	  // otherwise handle synchronous load
	  return this._handleSyncLoad(serialized);
      } else {
	  return null;
      }

  },         

  _getItemFromAPI: function ($rootscope) {
      // SERVER-SIDE API CALL TO PERSIST DASHBOARD TO STORAGE - 09/03/2015 BM:
      var sid = $rootScope.rageSessionVars.sessionID;
      var userid = $rootScope.rageSessionVars.userID;
      var dashboardId = this.id;

      return dashboardcontext.getDashboardImage(sid, userid, dashboardId).then(function (data) {
	  if (data.status == "FAIL") {
	      window.alert("Failed to retrieve dashboard. " + data.messages);
	      return false;
	  }
	  else {                      
	      return data;
	  }
      });            
  },

  _handleSyncLoad: function (serialized, isParsed) {


      // **** MUST HANDLE THE isParsed PARAM; serialized object is alredy parsed ****

      var deserialized, result = [];

      if (!serialized) {
	  return null;
      }

      if (this == undefined) {    // problem if coming from .then of this._getItemFromAPI in load:  - 09/04/2015 BM:

	  deserialized = JSON.parse(serialized);

      }
      else {
	  if (this.stringify) {
	      try { // to deserialize the string

		  deserialized = JSON.parse(serialized);

	      } catch (e) {

		  // bad JSON, log a warning and return
		  $log.warn('Serialized dashboard state was malformed and could not be parsed: ', serialized);
		  return null;

	      }
	  }
	  else {
	      deserialized = serialized;
	  }
      }

      // check hash against current hash
      if (deserialized.hash !== this.hash) {

	  $log.info('Serialized dashboard from storage was stale (old hash: ' + deserialized.hash + ', new hash: ' + this.hash + ')');
	  this.storage.removeItem(this.id);
	  return null;

      }

      // Cache widgets
      var savedWidgetDefs = deserialized.widgets;

      // instantiate widgets from stored data
      for (var i = 0; i < savedWidgetDefs.length; i++) {

	  // deserialized object
	  var savedWidgetDef = savedWidgetDefs[i];

	  // widget definition to use
	  var widgetDefinition = this.widgetDefinitions.getByName(savedWidgetDef.name);

	  // check for no widget
	  if (!widgetDefinition) {
	      // no widget definition found, remove and return false
	      $log.warn('Widget with name "' + savedWidgetDef.name + '" was not found in given widget definition objects');
	      continue;
	  }

	  // check widget-specific storageHash
	  if (widgetDefinition.hasOwnProperty('storageHash') && widgetDefinition.storageHash !== savedWidgetDef.storageHash) {
	      // widget definition was found, but storageHash was stale, removing storage
	      $log.info('Widget Definition Object with name "' + savedWidgetDef.name + '" was found ' +
		'but the storageHash property on the widget definition is different from that on the ' +
		'serialized widget loaded from storage. hash from storage: "' + savedWidgetDef.storageHash + '"' +
		', hash from WDO: "' + widgetDefinition.storageHash + '"');
	      continue;
	  }

	  // push instantiated widget to result array
	  result.push(savedWidgetDef);
      }

      return result;
  },

  _handleAsyncLoad: function (promise) {
      var self = this;
      var deferred = $q.defer();
      promise.then(
	// success
	function (res) {
	    var result = self._handleSyncLoad(res);
	    if (result) {
		deferred.resolve(result);
	    } else {
		deferred.reject(result);
	    }
	},
	// failure
	function (res) {
	    deferred.reject(res);
	}
      );

      return deferred.promise;
  }

};

return DashboardState;
}]);

Your advice is greatly appreciated.

regards, Bob

1 Answer 1

1

this always refers to the current function so if you nest function definitions you have to work around it somehow.

This is a typical pattern that should solve your issue:

function someFunction() {
  var that = this;
  doSomething().then(function() {
    // 'that' is your 'this'
  });
}
Sign up to request clarification or add additional context in comments.

2 Comments

Okay, I think you're onto something here. I assigned that = this above the constructor function DashboardState(storage, id, hash, widgetDefinitions, stringify) - and that now contains the valid object.
Yes, it worked. The key was to add var that = this; at the top of my factory code; but, OUTSIDE my constructor. Then reassigned that = this; inside the load: function.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.