2015-09-04 50 views
0

我正在使用從localStorage加載JSON數據的Angular directive異步調用函數的角度問題

這很容易用localStorage.getItem(id)完成,但現在我試圖通過API調用(反過來從數據庫中提取)使其工作。

我有Angular工廠代碼工作,和http請求工作,但在指令代碼中有一個_handleAsyncLoad()功能,這是扔我。換句話說,我試圖使用API​​層返回的serialized對象的內置承諾。

前/我寫了新的匿名函數,_getItemFromAPI:,但不確定是否需要使用_handleAsyncLoad:函數。如果沒有,最好的方法是確保我的serialized對象在返回之前填充數據。

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 = { 
 
     
 
load: function (dashboardId) { 
 

 
     if (!this.storage) { 
 
\t return null; 
 
     } 
 

 
     var serialized; 
 

 
     // fetch dashboard layout from storage 
 
     if (dashboardId != null && dashboardId != undefined) { 
 
\t //serialized = this.storage.getItem(dashboardId); \t // OLDER, SIMPLER WAY 
 
\t 
 
\t serialized = this._getItemFromAPI($rootScope); \t // NEW WAY, PULL DATA VIA API ! \t 
 
     } 
 
     else { 
 
\t // revert to original line; see dashboardOptions to main-controller 
 
\t serialized = this.storage.getItem(this.id); 
 
     } 
 

 
     if (serialized) { 
 
\t // check for promise 
 
\t if (angular.isObject(serialized)) { 
 
\t  return this._handleAsyncLoad(serialized); 
 
\t } 
 
\t // otherwise handle synchronous load 
 
\t return this._handleSyncLoad(serialized); 
 
     } else { 
 
\t 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; 
 

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

 
    _handleSyncLoad: function (serialized) { 
 

 
     var deserialized, result = []; 
 

 
     if (!serialized) { 
 
\t return null; 
 
     } 
 

 
     if (this.stringify) { 
 
\t try { // to deserialize the string 
 

 
\t  deserialized = JSON.parse(serialized); 
 

 
\t } catch (e) { 
 

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

 
\t } 
 
     } 
 
     else { 
 
\t deserialized = serialized; 
 
     }  
 

 
     // Cache widgets 
 
     var savedWidgetDefs = deserialized.widgets;  
 

 
     return result; 
 
    }, 
 

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

 
     return deferred.promise; 
 
    } 
 
}; 
 
     
 
return DashboardState; 
 
}]);

databoardcontext工廠代碼:

function getDashboardImage(sid, userid, id) { 
 
    var rageVars = $rootScope.rageSessionVars; 
 
    var url = "http://" + rageVars.domainName + ":" + rageVars.port + "/api/dashboards"; 
 
    var sid = rageVars.sessionID; 
 
    var apiCall = "getDashboardImage"; 
 
    var cssClass = "html"; 
 
    var req = { 
 
\t method: 'POST', 
 
\t url: url, 
 
\t headers: { 
 
\t  'Content-Type': 'application/json', // application/x-www-form-urlencoded 
 
\t }, 
 
\t data: { sid: sid, apiCall: apiCall, userid: userid, id: id } 
 
    }; 
 
    var deferred = $q.defer(); 
 
    deferred.notify("Retrieving dashboard image..."); 
 

 
    $http(req).success(function (data, status, headers, config) { 
 

 
\t deferred.resolve(data); 
 

 
    }).error(function (data, status, headers, config) { 
 

 
\t console.log('Error retrieving dashboard '); 
 
\t deferred.resolve(); 
 
    }); 
 

 
    return deferred.promise; 
 
}

******** UPDATE 2015年9月8日下午2點55分:由於該誰提供答案,我發佈一些更新的代碼,以顯示現在的工作。 ********

angular.module('ui.dashboard') 
 
.factory('DashboardState', ['$log', '$q', 'dashboardcontext', '$rootScope', function ($log, $q, dashboardcontext, $rootScope) { 
 

 
var that = this;  // *** CREATED NEW OBJECT HERE. REASSIGN BELOW IN load: FUNCTION *** 
 

 
function DashboardState(storage, id, hash, widgetDefinitions, stringify) { 
 
    this.storage = storage; 
 
    this.id = id; 
 
    this.hash = hash; 
 
    this.widgetDefinitions = widgetDefinitions; 
 
    this.stringify = stringify; 
 
} 
 

 
DashboardState.prototype = { 
 
    save: function (widgets) { 
 
    /// SAVE CODE OMITTED FOR BREVITY 
 
    }, 
 
    
 
    load: function (dashboardId) { 
 
     
 
     var useLocalStorage = false;  // retrieve from localStorage or via API layer - 09/04/2015 BM: 
 

 
     var serialized; 
 

 
     if (useLocalStorage) {   // retrieve dashboard layout from localStorage 
 
\t if (!this.storage) { 
 
\t  return null; 
 
\t } 
 
\t if (dashboardId != null && dashboardId != undefined) { 
 
\t  serialized = this.storage.getItem(dashboardId); 
 

 
\t  // save the current dashboard id for next load 
 
\t  this.storage.setItem("defaultDashboardId", dashboardId); 
 
\t } 
 
     } 
 
     else { \t 
 
\t 
 
\t if (dashboardId != null && dashboardId != undefined) { 
 

 
\t  this.storage.setItem("defaultDashboardId", dashboardId); 
 
\t  
 
\t  that = this; \t // **** VERY IMPORTANT TO REASSIGN *** 
 
\t  
 
// *** RETURN IS VERY IMPORTANT HERE, AS WELL AS THE then() SECTION *** 
 
\t  return this._getItemFromAPI($rootScope).then(function (data) {       \t \t 
 
     \t \t 
 
\t \t return that._handleSyncLoad(data, true); // *** that. IS NOW AVAILABLE ON THE SCOPE *** 
 

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

 
     if (serialized) { 
 
\t // check for promise 
 
\t if (angular.isObject(serialized)) {  
 
\t  return this._handleAsyncLoad(serialized); 
 
\t } 
 
\t // otherwise handle synchronous load 
 
\t return this._handleSyncLoad(serialized); 
 
     } else { 
 
\t 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) { 
 
\t return data.data[0]; 
 
     });    
 
    }, 
 

 
    _handleSyncLoad: function (serialized, isParsed) { 
 
     // @serialized {JSON} - parsed or unparsed Json object 
 
     // @isParsed {Boolean} - false if loaded from localStorage.getItem(); true if loaded from API, Json string already parsed. 
 

 
     var deserialized, result = []; 
 

 
     if (!serialized) { 
 
\t return null; 
 
     } 
 

 
     if (isParsed) { // JSON already deserialzed in load: above; see _getItemFromAPI().then data object - 09/04/2015 BM: 
 

 
\t deserialized = serialized;    
 

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

 
\t \t deserialized = JSON.parse(serialized); 
 

 
\t  } catch (e) { 
 

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

 
\t  } 
 
\t } 
 
\t else { 
 
\t  deserialized = serialized; 
 
\t } 
 
     } 
 

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

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

 
     } 
 

 
     // Cache widgets 
 
     var savedWidgetDefs = deserialized.widgets; 
 

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

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

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

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

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

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

 
     return result; 
 
    }, 
 

 
    _handleAsyncLoad: function (promise) { 
 
     // code same as original post... 
 
    } 
 

 
}; 
 

 
return DashboardState; 
 
}]);

回答

2

只要你有異步操作,你應該異步以及公共API,即使在一些或大多數情況下,它是同步。然後API對用戶是一致的。

而不是「檢查承諾」,將同步呼叫轉換爲承諾。然後

load功能可以簡化爲以下(我離開的一些細節爲簡潔起見,但你應該瞭解的更廣泛的概念):

load: function(dashboardId) { 

    if (isSync()){ 
    // sync 

    var data = this.storage.getItem(dashboardId); 

    // wrap in a promise 
    return $q.resolve(_handleSyncLoad(data)); 

    } else { 
    // async 

    // return the promise generated by _getItemFromAPI().then() 
    return _getItemFromAPI().then(function(data){ 
     return _handleSyncLoad(data); 
    }); 
    } 
} 

注意,我認爲_getItemFromAPI()回報一個承諾(在你的情況並非如此),所以它會像下面這樣:

function _getItemFromAPI(){ 

    // ... 

    // this "return" is important! it returns the promise generated by $http 
    return $http({...}).then(function(response){ 
    return response.data; 
    }) 
} 

這使得使用load一致wheth呃它是同步或異步:

dashboardSvc.load(444).then(function(dashboardData){ 
    $scope.dashboard = dashboardData; 
}); 
+0

@bob,它沒有被返回。它應該是'return dashboardcontext.getDashboardImage()...' –

+0

我添加了'getDashboardImage'函數以供參考;但如果我理解正確,則表示Promise不是從'_getItemFromAPI()'返回的。 –

+2

@bob,是的。我可以看到'getDashboardImage'返回一個承諾,因爲它後面跟着'.then',但是你還需要'返回''.then'的承諾 –