Stumped with Angular module inheritance - javascript

I'm trying to implement John Papa's Angular architecture, but I can't seem to get it to work. I feel like I'm missing something basic about how inheritance works.
I'm just trying to access a factory method =, test.ping, that lives in the base app module, from the feature module, foo.module.js. When I try to call the function, I get an error in the console:
ReferenceError: 'test' is undefined
//app.js
(function () {
angular.module("app", ['app.foo'])
.factory('test', [function () {
var service = {
ping: ping
};
return service
function ping() {
alert('PING Service method called.');
}
}])
})();
//foo.module.js
(function () {
angular.module('app.foo', []);
})();
//foo.js
(function () {
angular
.module('app.foo')
.controller('Foo', Foo);
Foo.$inject = [];
function Foo() {
var vm = this;
alert('Foo loaded'); //the alert is working
vm.ping = function () {
alert('Ping button clicked.'); //the alert is working
test.ping(); //this throws console error
}
}
})();
//html
<body data-ng-controller="Foo as fooCtrl">
<button data-ng-click="fooCtrl.ping()">PING</button>

You forgot to add test service in dependency injection.
Foo.$inject = [];
change this to
Foo.$inject = ['test'];
And also
function Foo() {
this to
function Foo(test) {

Related

Angular factory not being instantiated

I have angular 1.4 app. Im using require.js. An included script have this:
define(['firstFactory', 'secondFactory'],
function(firstFactory, secondFactory){
var app = angular.module('services', []);
app.factory('firstFactory', firstFactory);
app.factory('secondFactory', secondFactory);
});
Inside require.config (in main.js) I have path to both the factories:
"firstFactory" : "factory/firstFactory",
"secondFactory" : "factory/secondFactory"
Finally in my main controller I have this:
function myCtrl(firstFactory, secondFactory) {
Thing is that in firstFactory.js I have this code:
define([], function () {
console.log('inside define firstFactory');
var firstFactory = function () {
console.log('inside function firstFactory');
};
return firstFactory;
});
While in the secondFactory.js I have this:
define([], function () {
console.log('inside define secondFactory');
var secondFactory = function () {
console.log('inside function secondFactory');
};
return firstFactory;
});
In the console I see:
inside define firstFactory
inside function firstFactory
inside define secondFactory
So I dont see
inside function secondFactory
Why? To me the symptom is that angular is not instantiating the second factory. I cannot understand that for the first one it is being instantiated but not for the second, as Im doing the same thing with both. Any clue?
In the secondFactory.js you are returning firstFactory, may be this is the reason.

Jasmine and AngularJS - how to test a factory function when the function is used as a dependency

I have the following simple factory that i would like write a unit test around.
(function () {
"use strict";
angular
.module("math")
.factory("addservice", [addTwoNumbers]);
function addTwoNumbers(a, b) {
return a + b;
}
return { add: addTwoNumbers };
})();
This is what i have so far in my test spec.
describe('adding two numbers', function () {
var addService;
beforeEach(function () {
module('math');
inject(function ($injector) {
addService = $injector.get('addservice');
});
});
it('should add two numbers and get 2', function () {
var result = addService.add(1, 1);
expect(result).toBe(2);
});
});
When trying to run this unit test i get TypeError: addService.add is not a function. I will note that i can get this to work if i change the factory declaration to the code seen below but we ran into a problem where minification was screwing up parameter names and changing them to a,b,c,etc. (when passing in $q, $http, and many other dependencies) and breaking the code so we moved to what is seen above. This is obviously a very basic example here which is why it is so frustrating on why i can't get this to work.
(function () {
"use strict";
angular
.module("math")
.factory('addservice', [function () {
function addTwoNumbers(a, b) {
return a + b;
}
return { add: addTwoNumbers };
}
]);
})();
It seems like I need to reference the addTwoNumbers function somehow but not sure how i would go about doing that. I'm probably missing something very obvious so any help is greatly appreciated.
Ok so i figured out what was wrong with this example. It ends up if i refactored my factory a bit I could simply make a call in my unit test to the property twoNumbersAddedTogether after i get the reference to the factory itself. I've provided the new unit test and factory code below.
factory code
(function () {
"use strict";
angular
.module("math")
.factory("addservice", [addTwoNumbers]);
function addTwoNumbers() {
return {twoNumbersAddedTogether: dotheMath}
function dotheMath(a,b) {
return a + b;
}
}
})();
unit test code
describe('adding two numbers', function () {
var addService;
beforeEach(function () {
module('math');
});
beforeEach(inject(function (_addservice_) {
addService = _addservice_;
}));
it('should add two numbers', function () {
var result = addService.twoNumbersAddedTogether(1, 1);
expect(result).toBe(2);
});});

Utils class in Angular.js

I'd like to create an utility class in Angular.js that can be used by several controllers.
So far I created this:
'use strict';
angular
.module('App')
.factory('AppUtils', AppUtils);
function AppUtils() {
var vm = this;
vm.getPersonOf = getPersonOf;
function getPersonOf(personId, allPersons) {
var person = {};
person.personId = {personId: personId};
allPersons.forEach(function(p) {
if (p.personId === personId) {
person = p;
}
});
return person;
}
}
And I tried to use it in a controller like this:
'use strict';
angular
.module('App')
.controller('AppController', AppController);
function AppController(personId, allPersons, AppUtils) {
var vm = this;
vm.personId = personId;
vm.person = AppUtils.getPersonOf(vm.personId, allPersons);
...
}
But I get this:
PhantomJS 1.9.8 (Windows 7 0.0.0) App should dismiss modal FAILED
Error: [$injector:undef] Provider 'AppUtils' must return a value from $get factory method.
http://errors.angularjs.org/1.5.0/$injector/undef?p0=InvoiceUnitsUtils
(The real names have been renamed to make it easier.)
Why am I getting that error? Am I not declaring properly the Utility module?
The factory is in charge of creating a service and handing its instance to you. To do this, you need to return your utility class:
function AppUtils() {
return {
getPersonOf: getPersonOf
// pass other utility functions...
}
function getPersonOf(personId, allPersons) {
var person = {};
person.personId = {personId: personId};
allPersons.forEach(function(p) {
if (p.personId === personId) {
person = p;
}
});
return person;
}
}
I removed the vm part because we are handing a service which usually has no view model (the controller is in charge of that, service is more of a business logic expert).
Here's some more information about the $get function in Angular's providers:
https://docs.angularjs.org/guide/providers

How to sinon stub a call to external module in a tested function

I am testing a function. That function has a call to an external module which makes my test fail:
element.funcCall();
Where the function looks like this:
function funcCall() {
external.a.b.c.doSomething();
}
It fails on: undefined is not an object (evaluating 'external.a.b.c.doSomething')
How can I stub or fake this so the call is not really make. I do not need it's functionality for this test.
Here's how I do this kind of thing:
var element = require('element'),
external = require('external'),
sinon = require('sinon'),
expect = require('chai').expect;
describe('element.funcCall()', function() {
before(function() {
this.doSomethingStub = sinon.stub(external.a.b.c, 'doSomething');
});
it('should do something external', function() {
element.funcCall();
expect(this.doSomethingStub.called).to.be.true;
});
after(function() {
this.doSomethingStub.restore();
});
});
Here is how I ended up solving this. Since I did not care about that external function being executed, I faked it like so:
var func = function() {};
external =
{ a:
{ b:
{ c:
{doSomething: func}
}
}
}
...
element.funcCall();
expect.....
This was the internal function call was redirected to en empty function and I could test the rest of the method

How to properly overwrite the exceptionHandler in angularjs?

For an app I'm using a skeleton that is very similar to https://github.com/angular/angular-seed.
I've tried something like this, in services.js:
'use strict';
/* Services */
angular.module('mApp.services', []).
factory('$exceptionHandler', function () {
return function (exception, cause) {
alert(exception.message);
}
});
This doesn't do anything, it doesn't seem to overwrite the exceptionHandler.
If I try:
var mod = angular.module('mApp', []);
mod.factory('$exceptionHandler', function() {
return function(exception, cause) {
alert(exception);
};
});
It overwrite the whole app.
How can I properly overwrite the exceptionHandler if I am using a skeleton similar to the default angular app?
It's hard to know for certain without seeing the rest of your app, but I'm guessing angular.module('myApp').factory( ... will work. If you leave out the second parameter (,[]) angular will retrieve an existing module for further configuring. If you keep it angular will create a new module.
try this example
http://jsfiddle.net/STEVER/PYpdM/
var myApp = angular.module('myApp', ['ng']).provider({
$exceptionHandler: function(){
var handler = function(exception, cause) {
alert(exception);
//I need rootScope here
};
this.$get = function() {
return handler;
};
}
});
myApp.controller('MyCtrl', function($scope, $exceptionHandler) {
console.log($exceptionHandler);
throw "Fatal error";
});

Categories