Reputation: 2294
I have read several posts and articles relating to this issue and they have helped me devise some work arounds but do not explain (at least in a way I understand) why this doesn't work.
I created a factory service whose job is to manage a list of continents and a single selected continent. Controllers interact with this service via a public API expressed as an object literal.
var appSvcs = appSvcs || angular.module("app.services", ["app.repositories"]);
appSvcs.factory('ContinentService', ['ContinentRepository',
function (db) {
'use strict';
//#region Private Fields and Variables
var continents = [],
selectedContinent = null;
selectedContinentIndex = -1;
//#endregion Private Fields and Variables
//#region Private Methods
function initContinents() {
return db.getContinents(); // return a promise
};
function setSelectedContinent(continent) {
selectedContinent = angular.copy(continent);
selectedContinentIndex = continents.indexOf(continent);
};
//#endregion Private Methods
//
// Public API
return {
continentService: {
continents: continents,
selectedContinent: selectedContinent,
setSelectedContinent: setSelectedContinent,
initContinents: initContinents,
}
}
}]);
The controller interacts with the continentService via the public API. In the controller I add a reference to the API on the local $scope ...
$scope.svc = svc.continentService;
Since the factory is supposed to be a singleton, what I expect is that when the controller calls the public method "setSelectedContinent()", the selectedContinent and selectedContinentIndex properties will be set and then be available to the host controller and any other controller that implements this factory. This has not proven to be the case. In the second line below a type error is thrown ... "Cannot read property 'Id' of null". This suggests that the selectedContinent property of the service was not set.
$scope.svc.setSelectedContinent(continent);
console.log($scope.svc.selectedContinent.Id + " :: " + $scope.svc.selectedContinent.Name);
When, however, I set the property directly, it works fine - sorta. There is processing that the method setSelectedContinen()t does that needs to be performed and setting it directly obviously does not allow that to happen.
$scope.svc.selectedContinent = continent;
console.log($scope.svc.selectedContinent.Id + " :: " + $scope.svc.selectedContinent.Name);
I have included the complete controller code for reference in case that's helpful.
angular.module("ListMgrApp").controller('ContinentPanelCtrl',
['$scope', 'ContinentService',
function ($scope, svc) {
'use strict';
//#region Fields and Variables
$scope.svc = svc.continentService;
var $uiContinentList = $('.continent-list');
///#endregion Fields and Variables
//#region private methods
function init() {
//
// Show the pane
$scope.svc.initContinents().then(
function (response) {
$scope.svc.continents = response.data;
},
function (reason) {
//TODO: Handle Error
});
};
//#endregion private methods
//#region $scope methods
$scope.setSelectedContinent = function (continent) {
// $scope.svc.selectedContinent = continent; <-- works but not sufficient
$scope.svc.setSelectedContinent(continent); // <-- Needs to work but does not
console.log(continent.Id + " :: " + continent.Name);
$uiContinentList.find('li[data-id="' + continent.Id + '"]').addClass("selected");
}
//#endregion $scope methods
init();
}]);
Upvotes: 0
Views: 99
Reputation: 48982
Quoted your setSelectedContinent
function setSelectedContinent(continent) {
selectedContinent = angular.copy(continent);
selectedContinentIndex = continents.indexOf(continent);
};
Your function sets selectedContinent
and selectedContinentIndex
. But these 2 variables are local variables of function (db)
available to your function via closure, not the properties of your service. That's why you have an error trying to access this:
$scope.svc.selectedContinent.Id
In your second example:
$scope.svc.selectedContinent = continent;
You set directly to a property of the svc
=> getting it back will not have an error for sure. But in this case, you're creating another property on the svc
which is not the same as the selectedContinent
in your declaration region.
var continents = [],
selectedContinent = null;
selectedContinentIndex = -1;
Solution:
If you need to create your variables as private (only accessible via closure). You could provide some getter functions to return these variables:
function getSelectedContinent(continent) {
return selectedContinent;
};
Modify your returned object:
return {
continentService: {
continents: continents, //remove this if you need a true private property
selectedContinent: selectedContinent, //remove this if you need a true private property
setSelectedContinent: setSelectedContinent,
initContinents: initContinents,
getSelectedContinent: getSelectedContinent //return 1 more function
}
}
Upvotes: 2