I am new to angular. I have a service where I call a function by another function within the service. But in my controller is shows as undefined. See below
//my Service
myApp.service("LocationService", function() {
var srv = {}
srv.msg = {long:0, lat:0};
srv.onSuccess = function() {
this.msg.lat = 32;
},
srv.onError = function() {
this.msg.long = 99;
},
srv.getGpsFix = function() {
this.onSuccess();//fails in controller
}
return srv;
});
//my Controller
myApp.controller("MusicCtrl", ["$scope", "LocationService", function($scope, LocationService) {
//undefined
console.log(locationService.getGpsFix());
}]);
It is correct that locationService.getGpsFix() would return undefined.
If you intended for it to return a value, then use the return keyword in your function.
srv.getGpsFix = function() {
this.onSuccess();
return 'It worked!!!';
};
locationService.getGpsFix() is undefined. In your controller, your service is available as LocationService. Hence, use LocationService.getGpsFix()
Related
Since I wrote firebase-factory separately from RecipeController, I have an error in my Test.
TypeError: Cannot read property '$loaded' of undefined.
$loaded is a method in firebase...
test.js
describe('RecipeController', function() {
beforeEach(module('leChef'));
var $controller;
beforeEach(inject(function(_$controller_){
$controller = _$controller_;
}));
describe("$scope.calculateAverage", function() {
it("calculates average correctly", function() {
var $scope = {};
var controller = $controller('RecipeController', { $scope: $scope });
$scope.calculateAverage();
expect(average).toBe(sum/(Recipes.reviews.length-1));
});
});
});
firebase-factory.js
app.factory("Recipes", ["$firebaseArray",
function($firebaseArray) {
var ref = new Firebase("https://fiery-inferno-8595.firebaseio.com/recipes/");
return $firebaseArray(ref);
}
]);
recipe-controller.js
app.controller("RecipeController", ["$scope", "toastr", "$location", "$routeParams", "$compile", "Recipes",
function($scope, toastr, $location, $routeParams, $compile, Recipes) {
$scope.recipes.$loaded().then(function(payload) {
$scope.recipe = payload.$getRecord($routeParams.id);
$scope.html = $scope.recipe.instructions;
if (typeof $scope.recipe.reviews === "undefined") {
$scope.recipe.reviews = [{}];
}
$scope.calculateAverage = function(AverageData){
var sum = 0;
if ($scope.recipe.reviews.length > 1) {
for(var i = 1; i < $scope.recipe.reviews.length; i++){
sum += parseInt($scope.recipe.reviews[i].stars, 10);
}
var average = sum/($scope.recipe.reviews.length-1);
var roundedAverage = Math.round(average);
return {
average: roundedAverage,
markedStars: new Array(roundedAverage)
};
} else {
return sum;
}
};
});
]);
In your RecipeController definition, you immediately call:
$scope.recipes.$loaded().then(function(payload) { ... }
...assuming that $scope.recipes is defined and has a property of $loaded -- which is not the case.
In your test:
describe("$scope.calculateAverage", function() {
it("calculates average correctly", function() {
var $scope = {};
var controller = $controller('RecipeController', { $scope: $scope });
$scope.calculateAverage();
expect(average).toBe(sum/(Recipes.reviews.length-1));
});
});
...you define scope as an empty object, then inject it into your controller.
Assuming you are using Jasmine as a test framework, you could create a spy like this:
var $scope = {
recipes: {
$loaded: function() { /* this is a mock function */ }
}
};
var deferred = $q.defer();
deferred.resolve({
/* this is the data you expect back from $scope.recipes.$loaded */
});
var promise = deferred.promise;
spyOn($scope.recipes, '$loaded').and.returnValue(promise);
This is just one of many ways you could stub out that function and control the data you get in your test. It assumes a basic understanding of the $q service and the Promise API.
Best Practices
It is best not to attach data to the $scope service. I would recommend reading up on the controllerAs syntax, if you're not familiar with it.
TL;DR: A controller is just a JavaScript "class", and the definition function is its constructor. Use var vm = this; and then attach variables to the instance reference vm (as in "view model", or whatever you want to call it) instead.
Rather than relying on $scope.recipes to have been defined elsewhere, you should explicitly define it in your controller. If recipes are defined in another controller, create a service that both controllers can share.
I'm trying to setup my angular controllers in the format of closures. But Im having issues with asynchronous behavior. Basically whats returned is an undefined value on the controller.
myApp.controller(['RoleController', RoleController]);
var RoleController = function (MockRest, $scope) {
var roleValues = [];
var setRoleValues = (function () {
return MockRest.getRoles().then(function(data) {
var formattedData = data.plain();
return Immutable.fromJS(formattedData);
});
})();
return {
displayRowCollection: setRoleValues,
roleValues: roleValues,
}
};
Scenario:
I'd like to use this service to share some properties inside my webApp.
This service should have some object inside and the controllers should be able to get and set those objects.
JS:
var oneApp = angular.module('myApp', ['ui.bootstrap', 'ngTouch', 'ngAnimate'])
.service('sharedProps', function() {
var anObjNameDest = '';
var anObjPropName = '';
this.progressBarDynTyped00 = {
dynamic: 0,
dynMultiplier: 10,
max: 100
};
var progressBarDynTyped00 = {
dynamic: 1000,
dynMultiplier: 10010,
max: 100100
};
var test = this.progressBarDynTyped00;
var test2 = progressBarDynTyped00;
return {
getDynObject: function(anObjNameDest) {
return this[anObjNameDest];
},
getDynObjectVal: function(anObjNameDest, anObjPropName) {
return this[anObjNameDest][anObjPropName];
},
setDynObjectVal: function(anObjNameDest, anObjPropName, value) {
this[anObjNameDest][anObjPropName] = value;
},
setDynObject: function(anObjNameDest, ObjSrc) {
this[anObjNameDest] = ObjSrc;
}
}
});
oneApp.directive("progressBarDynTyped", function() {
return {
restrict: "E",
templateUrl: "aTemplateUrl.html",
controller: function(sharedProps) {
this.test = sharedProps.getDynObject('progressBarDynTyped00');
this.dynMultiplier
= sharedProps.getDynObjectVal('progressBarDynTyped00', 'dynMultiplier');
},
controllerAs: "progressBarDynTyped00"
};
});
Problem:
in the code above I have a service where there are two test Objects (previously there were only one of those, the second one was added for test purpose), those test objects should be returned from this service to some contrellers functions, like in the "progressBarDynTyped00" controller, because I need to get and set some values inside those objects.
Calling all of the retuned functions from the service gave me always an Undefined object or a "Cannot read property 'dynMultiplier' of undefined".
Question:
Is there a way to return a dynamic object passing the name of the object to the service function?
Thanks to all.
I think you are confused between two ways of creating service in Angular: using service keyword and factory keyword
For service keyword, the public functions are supposed to be made available by assigning it to this:
angular.module('myApp', ['ui.bootstrap', 'ngTouch', 'ngAnimate']).service('sharedProps', function () {
this.getDynObject = function (anObjNameDest) {
//...
};
}]);
And it doesn't expect to have return value. So with the code above, there's no public functions to be called, and that explains the error
An angular service is a simplified version of an angular factory. So you should use a factory instead of a service
myApp.service('myservice', function() {
this.method = function() {
return "result";
};
});
myApp.factory('myfactory', function() {
return {
method : function() {
return "result";
};
}
});
Simply put, i have a Poller that returns msgdata and newdata variables based on conditions however the returned value in this case is always undefined. The Conditions should be overriding the initial variable initialising correct inside the service?
How can i get the Poller.msgdata and Poller.newdata to Return the TRUE or FALSE to the controller?
Controller:
app.controller('sidemenuController', ['$scope', 'projectsModal', 'sendMessageModal', 'Poller', '$timeout',
function($scope, projectsModal, sendMessageModal, Poller, $timeout) {
var update = function() {
$timeout(update, 5000);
$scope.inbox = Poller.msgdata;
$scope.project = Poller.newdata;
console.log("Updated SideMenu Controller: " + Poller.msgdata);
}
update();
$scope.projects = Poller.projects;
$scope.messages = Poller.messages;
$scope.sendMessage = sendMessageModal.activate;
$scope.showModal = function() {
projectsModal.deactivate();
projectsModal.activate();
};
$scope.toggle = function(){
$scope.checked = !$scope.checked
projectsModal.deactivate();
sendMessageModal.deactivate();
}
}]);
Service:
app.factory('Poller', Poller);
Poller.$inject = ['$http', '$timeout'];
function Poller($http, $timeout) {
var projectcache = { response: [], calls: 0 };
var msgcache = { response: [], calls: 0 };
var newdata;
var msgdata;
var poller = function () {
$timeout(poller, 5000);
$http.get('http://localhost/app/controllers/php/getProjects.php')
.then(function(r) {
if (r.data.projects.length > projectcache.response.length) {
newdata = true;
angular.copy(r.data.projects, projectcache.response);
} else {
newdata = false;
};
console.log(msgdata);
});
$http.get('http://localhost/app/controllers/php/getMessages.php')
.then(function(m) {
if (m.data.messages.length > msgcache.response.length) {
msgdata = true;
angular.copy(m.data.messages, msgcache.response);
} else {
msgdata = false;
};
});
};
poller();
return {
projects: projectcache.response,
messages: msgcache.response,
newdata: newdata,
msgdata: msgdata
};
};
Your polling method reassigns the local variables newdata and msgdata, but it doesn't reassign the fields of the object returned by the service, which are initialized to undefined and never modified after.
You need to keep a reference to the returned object in a variable:
var service = { ... };
...
return service;
and in the polling method, change the values inside the service:
service.newdata = false;
When you do
return {
projects: projectcache.response,
messages: msgcache.response,
newdata: newdata,
msgdata: msgdata
};
The data you get from Poller.newdata should always be the initial value of var newdata, because javascript do not pass by reference.
projects and messages work because you are doing angular.copy, which keeps the same reference.
An easy way to fix this would be passing back an object instead of boolean itself
var checkNew = {};
in poller function
checkNew.newdata = true;
in return
checkNew: checkNew
in controller
$scope.inbox = Poller.checkNew.msgdata;
$scope.project = Poller.checkNew.newdata;
A cleaner way (imo) to do this would be exposing the poller function as a service function to the controller. By this way you don't need to have a timeout on the service, it gets data whenever the controller calls it.
I have a service method and it will change the value of a boolean variable (false is default value) and return back to controller.
Am calling this service method by clicking on a button.
Consider, am clicking this button and now the variable value is true.
now, am accessing this same service method from another one controller and it is false.. why?
Whats wrong?
first controller,
.controller('myController1',....
$scope.value = false;
$scope.buttonClick = function(){
$scope.value = myService.setValue(value);
}
second controller,
.controller('myController2',.....
$scope.value = myService.getValue();
service,
angular.module('myModule', [])
.service('myService', ['OtherTypes',
function (OtherTypes) {
var myServiceVariable = false;
return {
setValue: function (value) {
myServiceVariable = !value;
return (myServiceVariable);
},
getValue: function () {
return myServiceVariable;
}
}
}
]);
Looks like you have a typo: you're separating the two functions with a , and not a ;. Change to:
myServiceVariable = false;
setValue: function (value) {
myServiceVariable = !value;
return (myServiceVariable);
};
getValue: function () {
return myServiceVariable;
};
}
Also, since you're getting the value in the 2nd controller as a return value from a function and not a direct reference to the service variable itself, don't expect it to update automatically. You'll have to call the getValue() function everytime.
hi the following code should show that services are singleton in angularjs.
myApp = angular.module("myApp",[]);
myApp.service("myService", function(){
myServiceVariable = false;
return {
setValue: function (value) {
myServiceVariable = value;
return (myServiceVariable);
},
getValue: function () {
return myServiceVariable;
}
}
});
myApp.controller("controller1", function($scope, myService){
console.log(myService);
$scope.clickHandler = function(){
myService.setValue(!myService.getValue());
}
});
myApp.controller("controller2", function($scope, myService){
console.log(myService);
$scope.getServiceValue = function(){
return myService.getValue();
}
});
For a working demo you can go here