How to use this angular factory in controller? - javascript

Factory:
factory('cordovaReady', function () {
return function (fn) {
var queue = [];
var impl = function () {
queue.push(Array.prototype.slice.call(arguments));
};
document.addEventListener('deviceready', function () {
queue.forEach(function (args) {
fn.apply(this, args);
});
impl = fn;
}, false);
return function () {
return impl.apply(this, arguments);
};
};
})
I used this factory in another factory like this:
return {
getCurrentPosition: cordovaReady(function (onSuccess, onError, options) {
//
}
}
The cordovaReady factory will execute the passed callback when the deviceReady event was fired. My question is how do I use it in controller?
I tried with just
.controller( 'HomeCtrl', function HomeController($scope, cordovaReady) {
cordovaReady(function(){
//do stuff
});
});
But it did not work. No console errors. Any ideas?

I solved it by wrapping the factor like this
.factory('aUseCase', function ($q, $rootScope, cordovaReady) {
return {
doSomething: cordovaReady(function () {
//do stuff
})
};
})

better version of #artworkad :
.factory('aUseCase', ['$q', '$rootScope', 'cordovaReady', function ($q, $rootScope, cordovaReady) {
return {
doSomething: cordovaReady(function () {
//do stuff
})
};
}])
Don't forget to inject dependencies explicitly otherwise you will have problem when minifying this snippet.

Are you sure your dependency is being injected into your controller?
var MyController = function($scope, cordovaReady) {
...
}
MyController.$inject = ['$scope', 'cordovaReady'];

In controller you need to declare a function to use cordovaReady
myApp.controller("salaryCalculatorCtr", ['$scope', 'cordovaReady'
, function ($scope, cordovaReady) {
var initApp= cordovaReady(function () {
//do something
});
initApp();
}]);

Related

Unit-testing $timeout execution inside an async call

I'm trying to unit-test a piece of my code contained inside an async call, like this:
function init() {
asyncFunc().then(function (data) {
$timeout(function () {
$scope.isInside = true;
});
});
}
The whole code:
(function (angular) {
// Create module
var myApp = angular.module('myApp', []);
// Controller which counts changes to its "name" member
myApp.controller('MyCtrl', ['$scope', '$http', '$timeout', function ($scope, $http, $timeout) {
$scope.isInside = false;
init();
function init() {
asyncFunc().then(function (data) {
$timeout(function () {
$scope.isInside = true;
});
});
}
function asyncFunc() {
return new Promise((resolve, reject) => {
$http.get('https://httpbin.org/get').success((data) => {
resolve(data);
}).error((error) => {
reject(error);
});
});
}
}]);
})(angular);
My intention is to test the $scope.isInside value. This is my test:
describe('myApp', function () {
var scope,
controller;
beforeEach(function () {
module('myApp');
});
describe('MyCtrl', function () {
var $httpBackend, endpointCall, $timeout;
beforeEach(inject(function ($rootScope, $controller, _$httpBackend_, _$timeout_) {
$httpBackend = _$httpBackend_;
$timeout = _$timeout_;
endpointCall = $httpBackend.expect('GET', 'https://httpbin.org/get').respond({data: 'data'});
scope = $rootScope.$new();
controller = $controller('MyCtrl', {
'$scope': scope
});
}));
it('initializes', function () {
$httpBackend.flush();
$timeout.flush();
expect(scope.isInside).toBe(true);
});
});
});
The test is failing, the value is "false", but I'm expecting to see a "true" there.
JSFiddle here: http://jsfiddle.net/KarmaCop213/tj6akcyk/1/

How to test function wrapped in a promis

My ctrl is like this:
(function() {
'use strict';
angular
.module('App')
.controller('DeviceStatesCtrl', DeviceStatesCtrl);
function DeviceStatesCtrl( $rootScope, $scope, $translate,DeviceStatesService) {
var vm = this;
DeviceStatesService.getObject().then(function(response){
vm.init(response);
});
vm.init= function(response){
$translate(['table.title']).then(function(translate){
some stuff here
}));
}
}})();
My jasmine test is like this:
describe('app module', function() {
//var controller = null;
var $controller, $translate,$compile,createController,DeviceStatesService,$translate, scope;
var mockInit= sth;
beforeEach(function () {
module('App');
});
// Provide will help us create fake implementations for our dependencies, do not useful
module(function($provide) {
// Fake StoreService Implementation returning a promise
//nothing works :(
$provide.value('DeviceStatesService', {
getStatesObject: function() {
return {
then: function(callback) {
return callback([{ some: "thing", hoursInfo: {isOpen: true}}]);
}
};
}
});
});
return null;
});
beforeEach(inject(function($controller,$rootScope, _$translate_, _DeviceStatesService_) {
scope = $rootScope.$new();
//for 'controller as' syntax
$controller('DeviceStatesCtrl as deviceStat', {
$scope: scope
});
createController = function(params) {
return $controller("DeviceStatesCtrl as deviceStat", {
$scope: scope,
$stateParams: params || {}
});
};
}));
describe("Unit:Device States controller", function() {
//test init function
it("init function get called correctly", function() {
//spyOn(DeviceStatesService, 'getStatesObject').and.callThrough();
//createController();
//expect(DeviceStatesService.getStatesObject).toHaveBeenCalled();
expect(scope.deviceStat.init).toBeDefined();
//in init, all things are warpped in the $translate
//spyOn(scope.deviceStat, 'init');
scope.deviceStat.init(mockInit);
scope.deviceStat.setChart('All');
//expect(scope.deviceStat.totalNum).toEqual(22);
});
});
});
My question is how to test the init function and the stuff in it? The init function is in a promise, which I do not know how to call it. As my code scope.deviceStat.init(mockInit), it do not work. Another question is in the $translate promise, how to pass parameter in it?
You use the done function, which is a parameter passed to the spec via the jasmine function it.. Example..
describe("My Test set", function() {
it("My Test", function(done) {
doAsync().then(function(result) {
done();
}).catch(function(err) {
fail();
}
}
}
Hope this helps.

Jasmine controller testing, expected spy to have been called

I have a method defined in AngularJS controller which is called on initialization. I want to test it using Jasmine ("jasmine-core": "^2.3.4", "karma": "^0.12.37"). I follow some tutorials on the Internet and StackOverflow questions, but I cannot find the right answer. Please take a look at this code:
Controller usersAddUserController:
(function () {
'use strict';
angular.module('app.users.addUser')
.controller('usersAddUserController', ['$scope', 'usersAddUserService', function ($scope, usersAddUserService) {
usersAddUserService.getCountryPhoneCodes().then(function (phoneCodes) {
$scope.phoneCodes = phoneCodes;
});
}]);
}());
Jasmine test:
(function () {
'use strict';
describe('usersAddUserControllerUnitTest', function () {
var scope, deferred, objectUnderTest, mockedAddUserService;
beforeEach(module('app'));
beforeEach(inject(function ($rootScope, $q, $controller) {
scope = $rootScope.$new();
function emptyPromise() {
deferred = $q.defer();
return deferred.promise;
}
mockedAddUserService = {
getCountryPhoneCodes: emptyPromise
};
objectUnderTest = $controller('usersAddUserController', {
$scope: scope,
usersAddUserService: mockedAddUserService
});
}));
it('should call getCountryPhoneCodes method on init', function () {
//when
spyOn(mockedAddUserService, 'getCountryPhoneCodes').and.callThrough();
deferred.resolve();
scope.$root.$digest();
//then
expect(mockedAddUserService.getCountryPhoneCodes).toHaveBeenCalled();
});
});
}());
After running the tests, the error message is:
PhantomJS 1.9.8 (Windows 7 0.0.0) usersAddUserControllerUnitTest should call getCountryPhoneCodes method on init FAILED
Expected spy getCountryPhoneCodes to have been called.
I obviously missing something, but I cannot figure out what it is. Any help will be appreciated.
You are spying on the mock after it has been passed into the instantiated controller.
Try this:
describe('usersAddUserControllerUnitTest', function () {
var scope, deferred, objectUnderTest, mockedAddUserService, $controller;
beforeEach(module('app'));
beforeEach(inject(function ($rootScope, $q, _$controller_) {
scope = $rootScope.$new();
function emptyPromise() {
deferred = $q.defer();
return deferred.promise;
}
mockedAddUserService = {
getCountryPhoneCodes: emptyPromise
};
$controller = _$controller_;
}));
function makeController() {
objectUnderTest = $controller('usersAddUserController', {
$scope: scope,
usersAddUserService: mockedAddUserService
});
}
it('should call getCountryPhoneCodes method on init', function () {
//when
spyOn(mockedAddUserService, 'getCountryPhoneCodes').and.callThrough();
makeController();
deferred.resolve();
scope.$root.$digest();
//then
expect(mockedAddUserService.getCountryPhoneCodes).toHaveBeenCalled();
});
});
EDIT Thanks #juunas for noticing the bug in my solution
You can provide the mock like this:
mockedAddUserService = {
getCountryPhoneCodes: emptyPromise
};
beforeEach(function () {
module(function ($provide) {
$provide.value('usersAddUserService', mockedAddUserService);
});
});
EDIT:
The code should look (as i cannot test it) like this:
(function () {
'use strict';
describe('usersAddUserControllerUnitTest', function () {
beforeEach(module('app'));
var emptyPromise = function() {
var deferred = $q.defer();
return deferred.promise;
}
var mockedAddUserService = {
getCountryPhoneCodes: emptyPromise
};
beforeEach(function () {
module(function ($provide) {
$provide.value('usersAddUserService', mockedAddUserService);
});
});
var scope;
beforeEach(inject(function ($rootScope, $q, $controller) {
scope = $rootScope.$new();
$controller('usersAddUserController', {
$scope: scope
});
}));
it('should call getCountryPhoneCodes method on init', function () {
spyOn(mockedAddUserService, 'getCountryPhoneCodes').and.callThrough();
scope.$root.$digest();
expect(mockedAddUserService.getCountryPhoneCodes).toHaveBeenCalled();
});
});
}());

Angular Promise ($q) is not working

I am trying to use Angular with SignalR in my demo application. I am trying to use $q service to use promises. Don't know whats wrong in my code but its not working.
SERVICE
var boardConsole = $.connection.builtinboard;
var chat = angular.module('chat', []);
chat.factory('board', ['$q', '$timeout', function ($q, $timeout) {
var board = {};
board.startBoard = function (callback) {
$.connection.hub.start(function () {
if (angular.isFunction(callback)) {
callback();
}
});
};
board.loadAllMessages = function () {
var deferred = $q.defer();
boardConsole.server.loadAllMessages().done(function (messages) {
deferred.resolve(messages);
}).fail(function () {
deferred.reject(function () {
//SHOW NOTHING FOUND
});
});
return deferred.promise;
};
return board;
} ]);
CONTROLLER
chat.controller('chatController', ['$scope', 'board', function ($scope, board) {
$scope.Messages = [];
board.startBoard(function () {
board.loadAllMessages().then(function (messages) {
alert('1');
$scope.Messages = messages;
});
});
} ]);
its not working
Just wrap it in a $timeout. This will perform a safe $apply if necessary.
$timeout(function(){
deferred.resolve(messages);
});

Broadcasting across AngularJS controllers?

How do I broadcast a message between controllers?
Here is what I have tried:
function Ctrl1($scope) {
$scope.$broadcast('Update');
}
Ctrl1.$inject = ['$scope'];
function Ctrl2($scope) {
$scope.updated = false;
$scope.$on('Update', function () {
$scope.updated = true;
});
}
Ctrl2.$inject = ['$scope'];
To see it running: view the Plnkr.
Instead of using $broadcast() a shared service and $watch() might be a better alternative.
var myApp = angular.module('myApp', []);
myApp.factory("MyService", function () {
return {
updated: false
};
});
function Ctrl1($scope, MyService, $timeout) {
$timeout(function () { //Some work occurs and sets updated to true
MyService.updated = true;
}, 1000)
}
Ctrl1.$inject = ['$scope', "MyService", "$timeout"];
function Ctrl2($scope, MyService) {
$scope.$watch(function () {
return MyService.updated;
}, function (oldValue, newValue) {
$scope.updated = MyService.updated;
});
}
Ctrl2.$inject = ['$scope', "MyService"];
Updated Plnkr
It depends on the scopes hierarchy and therefore on where you bootstrap your Ctrl1 and Ctrl2 in your dom.
Say Ctrl1 is the parent of Ctrl2. $broadcast will transmit the event to child scopes: Ctrl2 in this case will notice it (use $on).
If you need to transmit an event from Ctrl2 to Ctrl1, use $emit that transmit the event to parent scopes.

Categories

Resources