Angular JS factory and variable access - javascript

I recently changed my factory FROM
app.factory('controllerComm', function($rootScope)
{
var showVforum = {};
showVforum.result = false;
showVforum.prepBroadcast = function(val)
{
this.result = val;
this.broadcastVal();
}
showVforum.broadcastVal = function()
{
$rootScope.$broadcast('toggleVforum')
}
return showVforum;
});
To THIS
app.factory('controllerComm', ['$rootScope', function($rootScope)
{
var showVforum = {};
showVforum.result = false;
showVforum.prepBroadcast = function(val)
{
this.result = val;
this.broadcastVal();
}
showVforum.broadcastVal = function()
{
$rootScope.$broadcast('toggleVforum')
}
return showVforum;
}]);
I did this for JS minification reasons. Before I changed it, I had this working in one of my controllers:
$scope.$on('toggleVforum', function()
{
$scope.isVisible = controllerComm.result;
$('#vforum').verticalAlign();
player.play();
});
controllerComm.result is now returning undefined since I changed my factory and I cannot figure out why. Any ideas?
edit
the error:
TypeError: Object function e(e,f,i){var j=c.defer(),k=j.promise,l=y(i)&&!i,f=a.defer(function(){try{j.resolve(e())}catch(a){j.reject(a),d(a)}l||b.$apply()},f),i=function(){delete g[k.$$timeoutId]};
k.$$timeoutId=f;g[f]=j;k.then(i,i);return k} has no method 'prepBroadcast'
at Object.$scope.hideVforum (http://localhost/aventos/resources/js/aventos.js:645:20)
at http://localhost/aventos/resources/js/angular.min.js:72:251
at http://localhost/aventos/resources/js/angular.min.js:144:140
at Object.e.$eval (http://localhost/aventos/resources/js/angular.min.js:88:347)
at Object.e.$apply (http://localhost/aventos/resources/js/angular.min.js:88:454)
at HTMLButtonElement.<anonymous> (http://localhost/aventos/resources/js/angular.min.js:144:122)
at HTMLButtonElement.x.event.dispatch (http://localhost/aventos/resources/js/jquery-1.10.2.min.js:5:14129)
at HTMLButtonElement.v.handle (http://localhost/aventos/resources/js/jquery-1.10.2.min.js:5:10866)

Try passing the result variable with the broadcast.
$rootScope.$broadcast('toggleVForum',{result: this.result});
controllerComm is not defined in the $on listener even though you may be injecting it into the controller where the listener is defined.
$scope.$on('toggleVForum',function(evt,args){
$scope.isVisible = args.result;
...
});

Related

How to fix the unit test issue in my case?

I am trying to unit test two functions codes and keep getting error of undefined object.
my controller
vm = this;
//always fire first in the app
vm.getCompany = function() {
api.getCompany(function(res){
//do stuff
})
}
//always fire second in the app
vm.getEmployee = function() {
api.getEmployee(function(res){
//do stuff
})
}
api service
var company;
function getCompany() {
var company;
var q = $q.defer();
var url = ‘something.com’;
anotherApi.getCompany(url).then(function(comp){
company = comp;
q.resolve(company)
})
}
function getEmployee = function() {
var name = company.name
var url = ‘something.com/’ + name;
var q = $q.defer();
anotherApi.getEmployee(url).then(function(employee){
q.resolve(employee)
})
}
unit test.
beforeEach(function(){
module(‘myApp);
inject(function ($injector) {
$controller = $injector.get('$controller');
$rootScope = $injector.get('$rootScope');
$scope = $rootScope.$new();
$httpBackend = $injector.get('$httpBackend');
api = $injector.get('api');
});
vm = $controller'myCtrl', {
$scope : $scope
});
})
describe (‘test’, function(){
it(‘should get company’, function(){
vm.getCompany();
$httpBackend.flush();
// stuff and works
})
it(‘should get Employee’, function(){
vm.getEmployee()
$httpBackend.flush();
//getting error says
//'undefined' is not an object (evaluating 'company.name’)
})
})
I am getting 'undefined' is not an object (evaluating 'company.name’)
under getEmployee function in service.
I have tried many different ways but still not sure how to fix it, can someone help me about it? Thanks!
What is the expected behavior of the service if getEmployee is called before getCompany is called? You should at least check for company being null before attempting to use it. Also, you may want to consider storing the company in a property that you can access in your service. NOTE: I'm prefixing the property name with an underscore just to make a distinction between the public api and this pseudo-private property:
{
_company: null,
getCompany: function() {
var self = this;
var url = '...';
return $http.get(url).then(function(comp){
self._company = comp;
return self._company;
});
},
getEmployee: function() {
var self = this;
if (!self._company) {
return null; //or throw error or return rejected promise: $q.reject('company is null')
} else {
var url = '...';
var name = self._company.name;
return http.get(url);
}
}
}
Lastly, you can (and should) test your service separately from your controller now. In your controller test, you can just spyOn your service methods without it calling through to the server. And when you test your service, you can just set the service._company to a mock value when testing the getEmployee method.
Issue is in your Service. "company" should be the object literal since you access .name over it else it will through an error which you have specified.
Try below code:
Service
var company = {};
function getCompany() {
$http.get(url).then(function(comp){
company = comp;
return company;
})
}
function getEmployee = function() {
var name = company.name
$http.get(url).then(function(employee){
// do stuff
}
}
It should work.

Angular two way data binding not working when $scope is updated

I have a really serious problem, I'm updating, editing, deleting data, and the two-way data binding is not working.
This is one of my controllers:
'use strict';
var EventController = function($timeout, $scope, $state, EventModel) {
this.$timeout = $timeout;
this.$scope = $scope;
this.$state = $state;
this.EventModel = EventModel;
/**
* When the page is requested, retrieve all the data.
*
*/
this.retrieve();
};
EventController.prototype = {
create: function(event) {
var that = this;
this.EventModel.Model.insert(event)
.then(function() {
that.refresh();
});
},
retrieve: function() {
var that = this;
this.EventModel.Model.find()
.then(function(result) {
that.$scope.events = result;
});
},
one: function(id) {
var that = this;
this.EventModel.Model.one(id)
.then(function(result) {
that.$scope.event = result;
});
},
update: function(id, event, state) {
if (state !== undefined) {
event.is_active = state;
}
var that = this;
this.EventModel.Model.update(id, event)
.then(function() {
that.refresh();
});
},
delete: function(id) {
var check = $('[data-controller-input]:checked');
var that = this;
$.each(check, function() {
var target = $(this);
var id = target.prop('id');
that.EventModel.Model.remove(id)
.then(function() {
that.refresh();
});
});
},
clear: function() {
this.$scope.event = angular.copy(this.$scope.initial);
},
refresh: function() {
this.$state.go(this.$state.current, {}, {reload: true});
}
};
angular
.module('adminApp')
.controller('EventController',
[
'$timeout',
'$scope',
'$state',
'EventModel',
EventController
]
);
In the create, update and delete methods I need to update the HTML without refreshing the page, I already tried using, $scope.apply, $scope.digest, $timeout after the result came, but not happens in the HTML.
If I try $scope.apply and $scope.digest the error will be:
Prevent error $digest already in progress when calling $scope.$apply()
So I was trying to wrap the $scope.$apply or $digest with the $timeout, same result, nothing happens.
Thanks.
First of all, your refresh method will never update your controller.it will simply fail just because this.$state.current won't be able to resolve any url ,template or controller.
And this is the main reason you are not able to see updated data ,just check your console you might be getting Error: Cannot transition to abstract state '[object Object]' error.
Update : I have create a plnkr.as i don't have access to event model code i simply removed it and try to create the same scenario.
http://plnkr.co/edit/RsI3TgKwcjGEXcTMKoQR?p=preview
see if this can help you
I am not sure, but try using the following function which checks the current phase before executing your function. It may solve the issue.
$scope.safeApply = function(fn) {
var phase = this.$root.$$phase;
if(phase == '$apply' || phase == '$digest') {
if(fn && (typeof(fn) === 'function')) {
fn();
}
} else {
this.$apply(fn);
}
};
Usage:
$scope.safeApply(function() {
//Your lines
});

Passing an array from service to controller

I cannot seem to figure out how to pass an array from service to a controller.
I have a simple service
.service('test', function() {
var array = []
return array;
})
And a controller where I call this function when a button is pressed
$scope.testArray = function() {
$scope.test = test.array;
console.log("test: ", $scope.test);
};
I get an error test is undefined. Can anyone explain to me please why this doesn't work and how to fix it? I tried storing that array in a separate object but no luck either. THanks
(See also: this SO question about Angular providers)
A service should put properties directly on this. So instead of
.service('test', function() {
var array = [];
return array;
})
try
.service('test', function() {
this.array = [];
})
(code style notwithstanding; many would suggest preferring function access over direct object access)
.service('test', function() {
var array = [];
this.getArray = function(){
return array;
};
})
Just change test.arraywith test:
JSFiddle
.controller('youCtrl', ['$scope', 'test', function ($scope, test) {
$scope.testArray = function() {
$scope.test = test;
console.log("test: ", $scope.test);
};
});
Add the array variable to your service.
angular.module('moduleName').service('test', function() {
this.array = [];
});
Inject your service into your controller.
angular.module('moduleName').controller('controllerName', function(test) {
$scope.test = test.array;
console.log("test: ", $scope.test);
});

Calling a service from within another service in AngularJS

I'm attempting to call a service from within another service, then use the returned object to perform some operations. I keep running into a TypeError: getDefinitions is not a function error, however.
Below is my service is called, the service doing the calling, and my relevant controller code:
definitions.service.js:
'use strict';
angular.module('gameApp')
.factory('definitionsService', ['$resource',
function($resource) {
var base = '/api/definitions';
return $resource(base, {}, {
get: {method: 'GET', url: base}
});
}]);
utilities.service.js:
'use strict';
angular.module('gameApp')
.factory('utilitiesService', ['definitionsService', function(definitionsService) {
return {
description: description,
detail: detail,
severity: severity,
};
function description(account) {
var key = angular.isDefined(getDefinitions().ABC[account.code]) ? account.code : '-';
return getDefinitions().IDV[key].description;
}
function detail(account) {
var key = angular.isDefined(getDefinitions().ABC[account.code]) ? account.code : '-';
return getDefinitions().IDV[key].detail;
}
function severity(account) {
var key = angular.isDefined(getDefinitions().ABC[account.code]) ? account.code : '-';
return getDefinitions().IDV[key].severity;
}
var getDefinitions = function() {
definitionsService.get().$promise.then(function(data) {
return data;
});
};
}]);
controller.js:
'use strict';
angular.module('gameApp')
.controller('AccountsController', AccountsController);
AccountsController.$inject = ['$routeParams', 'customersService', 'utilitiesService'];
function AccountsController($routeParams, playersService, utilitiesService) {
var vm = this;
var playerId = $routeParams.playerId;
var getAccounts = function() {
playersService.getAccounts({
playerId: playerId
}).$promise.then(function(accounts) {
for (var i = 0; i < accounts.length; i++) {
if (angular.isDefined(accounts[i].secCode)) {
accounts[i].code = accounts[i].secCode;
accounts[i].severity = utilitiesService.severity(accounts[i]);
accounts[i].detail = utilitiesService.detail(accounts[i]);
accounts[i].description = utilitiesService.description(accounts[i]);
}
}
vm.accounts = accounts;
});
};
var init = function() {
getAccounts();
};
init();
}
Currently your service returns before your variable gets defined. That means the definition is never reached. So it is declared, as the function executes, but is undefined. Just move your variable definition to the top.
This will only prevent the definition error. Another problem is that your getDefinitions function doesn't return anything but you're calling a property on it. One solution I can think of is using a callback, that gets executed when your data is loaded:
angular.module('gameApp')
.factory('utilitiesService', ['definitionsService', function(definitionsService) {
var data;
reload();
var utils = {
description: description,
detail: detail,
severity: severity,
reload: reload,
loaded: null
};
return utils;
function reload() {
definitionsService.get().$promise.then(function(data) {
data = data;
if (utils.loaded && typeof utils.loaded === "function") {
utils.loaded();
}
});
}
function description(account) {
var key = angular.isDefined(data.ABC[account.code]) ? account.code : '-';
return data.IDV[key].description;
}
}]);
Then in your controller you could use the service like this:
utilitiesService.loaded(function(){
accounts[i].description = utilitiesService.description(accounts[i]);
})
old question but still relevant. To expand on Florian Gl's answer above if you have a service with multiple functions and one or more of those functions requires a "pre-service" function to be called for example to load some resource data in like configuration information move that service call to the top, outside of the nested function (in this case below I am dealing with the promise scenario in JavaScript):
angular.module('gameApp')
.factory('utilitiesService', ['definitionsService', function(definitionsService) {
var myFirstConfigValue = '';
// call any all services here, set the variables first
configurationService.GetConfigValue('FirstConfg')
.then(function (response) {
// set the local scope variable here
myFirstConfigValue = response;
},
function() { });
function myTestFunction() {
// make an ajax call or something
// use the locally set variable here
ajaxService.functionOneTwo(myFirstConfigValue)
.then(response) {
// handle the response
},
function(err) {
// do something with the error
});
}
}]);
Key point to note here is that if you need to load in some data you do that first outside of any other functions inside your service (e.g. you want to load some JSON data).

How to get listeners for a variable in AngularJS?

In AngularJS I know I can attach a listener to a variable like this:
$scope.$watch("variableName", listenerFunc);
I'm wondering whether it's possible to query a variable to find out which functions are already listening to it.
Specifically, I'd like to do something like the following
if( listenerIsNotAlreadyAssigned("variableName",listenerFunc) ){
$scope.$watch("variableName", listenerFunc); // assign it
}
Is there a way to implement the code above in Angular? If so, how?
Here's one method that might be considered hacky.. Demo here (click).
app.controller('myCtrl', function($scope) {
function checkWatchString(prop) {
var found = false;
angular.forEach($scope.$$watchers, function(item, i) {
if (item.exp === prop) { found = true; }
});
return found;
}
$scope.$watch('foo', function() {
});
console.log(checkWatchString('bar')); //false
console.log(checkWatchString('foo')); //true;
});
If you store a reference to the creation of a watch, you get a function reference that cancels the watch. You could take the obvious approach and track this manually and also be keeping the cancel function for each watch available.
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
$scope.foo = '123';
var reg = {};
if (!reg.foo) {
reg.foo = $scope.$watch('foo', function() {
console.log('foo changed');
});
}
});
I made an demo using a service for this: http://jsbin.com/AbAwObE/4/edit
Watch service
app.factory('watchService', function() {
var watchService = function(prop, unset) {
if (!watchService[prop]) {
return false;
}
if (unset) {
watchService[prop]();
delete watchService[prop];
}
return true;
};
return watchService;
});
Add $watch
watchService[prop] = $scope.$watch(prop, function() {
++$scope.changeCount;
});
Check $watch
if (watchService(prop)) {
Remove $watch
watchService(prop, true);

Categories