angular.injector.invoke makes my controller undefined - angularjs

While learning angular, read a blog which elaborate that
we can access factory/service outside of controller using angular.injector() , But when I try this, it gives my main controller is undefined error and everything stops working.
see the working plunker with commented culprit code block.Below is main code of lines using angular.injector(["app"]).invoke
var app = angular.module('AlphaModule', []);
// service added
app.service('tea', function(){
return {
teaType: function(teaType) {
console.log('Service => Morning tea must be ' + teaType);
},
sayHello: function(msg) {
console.log('Service => Hello ' + msg);
}
};
});
// ERROR in console when uncomment this code block
angular.injector(["app"]).invoke(function(tea){
tea.sayHello('Yellow');
});
// main controller definition
var AlphaController = function($injector, tea) {
var vm = this;
vm.timeNow = new Date().getTime();
....
};
app.controller('AlphaController', AlphaController );
Please highlight what am I missing/ wrong doing here?
My Guess:
I also read that $injector are singleton . What exactly this means? can we use $injector only once! or may be this is the issue as i have used $injector in .controller also

Your module name mentioned is wrong. module name is actualy AlphaModule not app.
angular.injector(["AlphaModule"]).invoke(function(tea){
tea.sayHello('Yellow');
});

Related

Issues injecting Angular factories and services

I don't know what it is about injecting factories, but I am having the most difficult time.
I've simulated what I'm attempting to do via this sample plunk http://plnkr.co/edit/I6MJRx?p=preview, which creates a kendo treelist - it works fine.
I have an onChange event in script.js which just writes to the console. That's also working.
My plunk loads the following:
1) Inits the app module, and creates the main controller myCtrl (script.js)
2) Injects widgetLinkingFactory int myCtrl
3) Injects MyService into widgetLinkingFactory
The order in which I load the files in index.html appears to be VERY important.
Again, the above plunk is NOT the real application. It demonstrates how I'm injecting factories and services.
My actual code is giving me grief. I'm having much trouble inject factories/services into other factories.
For example,
when debugging inside function linking() below, I can see neither 'CalculatorService' nor 'MyService' services. However, I can see the 'reportsContext' service.
(function () {
// ******************************
// Factory: 'widgetLinkingFactory'
// ******************************
'use strict';
app.factory('widgetLinkingFactory', ['reportsContext', 'MyService', linking]);
function linking(reportsContext, MyService) {
var service = {
linkCharts: linkCharts
};
return service;
function linkCharts(parId, widgets, parentWidgetData) {
// *** WHEN DEBUGGING HERE, ***
// I CANNOT SEE 'CalculatorService' AND 'MyService'
// HOWEVER I CAN SEE 'reportsContext'
if (parentWidgetData.parentObj === undefined) {
// user clicked on root node of grid/treelist
}
_.each(widgets, function (wid) {
if (wid.dataModelOptions.linkedParentWidget) {
// REFRESH HERE...
}
});
}
}
})();
A snippet of reportsContext'service :
(function () {
'use strict';
var app = angular.module('rage');
app.service('reportsContext', ['$http', reportsContext]);
function reportsContext($http) {
this.encodeRageURL = function (sourceURL) {
var encodedURL = sourceURL.replace(/ /g, "%20");
encodedURL = encodedURL.replace(/</g, "%3C");
encodedURL = encodedURL.replace(/>/g, "%3E");
return encodedURL;
}
// SAVE CHART DATA TO LOCAL CACHE
this.saveChartCategoryAxisToLocalStorage = function (data) {
window.localStorage.setItem("chartCategoryAxis", JSON.stringify(data));
}
}
})();
One other point is that in my main directive code, I can a $broadcast event which calls the WidgetLinking factory :
Notice how I'm passing in the widgetLinkingFactory in scope.$on. Is this a problem ?
// Called from my DataModel factory :
$rootScope.$broadcast('refreshLinkedWidgets', id, widgetLinkingFactory, dataModelOptions);
// Watcher setup in my directive code :
scope.$on('refreshLinkedWidgets', function (event, parentWidgetId, widgetLinkingFactory, dataModelOptions) {
widgetLinkingFactory.linkCharts(parentWidgetId, scope.widgets, dataModelOptions);
});
I am wasting a lot of time with these injections, and it's driving me crazy.
Thanks ahead of time for your assistance.
regards,
Bob
I think you might want to read up on factories/services, but the following will work:
var app = angular.module('rage')
app.factory('hi', [function(){
var service = {};
service.sayHi = function(){return 'hi'}
return service;
}];
app.factory('bye', [function(){
var service = {};
service.sayBye = function(){return 'bye'}
return service;
}];
app.factory('combine', ['hi', 'bye', function(hi, bye){
var service = {};
service.sayHi = hi.sayHi;
service.sayBye = bye.sayBye;
return service;
}];
And in controller...
app.controller('test', ['combine', function(combine){
console.log(combine.sayHi());
console.log(combine.sayBye());
}];
So it would be most helpful if you created a plunk or something where we could fork your code and test a fix. Looking over your services it doen't seem that they are returning anything. I typically set up all of my services using the "factory" method as shown below
var app = angular.module('Bret.ApiM', ['ngRoute', 'angularFileUpload']);
app.factory('Bret.Api', ['$http', function ($http: ng.IHttpService) {
var adminService = new Bret.Api($http);
return adminService;
}]);
As you can see I give it a name and define what services it needs and then I create an object that is my service and return it to be consumed by something else. The above syntax is TypeScript which plays very nice with Angular as that is what the Angular team uses.

function is undefined error in angular controller

I'm running into a problem trying to setup a fairly simple call using a factory and controller in angularjs. I've been attempting to follow the style guides of John Papa and Todd Motto in setting this up.
First I'm using 2 modules
(function(){
'use strict';
angular.module('app',[
'app.core',
'app.property'
]);
})();
In 'app.core' I define the factory
(function(){
'use strict';
angular.module('app.core')
.factory('dataservice',dataservice);
dataservice.$inject = ['$http','$q'];
function dataservice($http,$q) {
var service = {
getListing: getListing
};
return service;
function getListing() {
var def = $q.defer;
$http.get("http://acme.com/property/1?format=json")
.success(function(data){
service.getListing = data;
def.resolve(data);
});
return def.promise;
}
}
})();
and in 'app.property' I defined the controller
(function(){
'use strict';
angular.module('app.property')
.controller('PropertyCtrl',PropertyCtrl);
PropertyCtrl.$inject = ['dataservice'];
function PropertyCtrl(dataservice) {
var vm = this;
vm.listings = [];
activate();
function activate() {
return getListing().then(function(){});
}
function getListing(){
return dataservice.getListing().then(function(data){
vm.listings = data;
console.log("data is");
console.log(data);
return vm.listings;
});
}
}
})();
the error I get in the console output is
Error: dataservice.getListing(...) is undefined except when I inspect dataservice in chrome I can see
Further on I receive
TypeError: Cannot read property 'then' of undefined
TypeError: def.resolve is not a function
Despite these errors the remote call returns json fine.
Hoping someone with angular chops has an idea on where I went wrong.
You're very close. It should be $q.defer() but you've $q.defer
function getListing() {
var def = $q.defer();
$http.get("http://acme.com/property/1?format=json")
.success(function(data){
service.getListing = data;
def.resolve(data);
});
return def.promise;
}
You have to actually create the modules you are building on:
Put this above your app.property module's objects:
angular.module('app.property', []);
Put this above your app.core module's objects:
angular.module('app.core', []);
You are basically attaching a factory and a controller to modules that don't exist. You are trying to inject modules that don't exist into your primary module.
Here is a plunker showing the issues you were having resolved. Your code has some other issues, but at least it's finding the modules now, which was your original problem.
It should also be noted that mohamedrias is also correct - you had an error in syntax by not putting () on your defer call.
I updated my plunker to include his correction as well.

AngularJS + Jasmine: How to start from fresh module for each spec in a test in suite

Here's my simple code
var app = angular.module('myApp', [])
.value('SimpleValue', {
aaa: '£££'
});
and unit tests
describe('ttt', function() {
beforeEach(module('myApp'));
it('ttt', inject(function(SimpleValue) {
expect(SimpleValue.aaa).toEqual('£££');
SimpleValue.aaa = 4;
}));
it('ttt', inject(function(SimpleValue) { // This doesn't start fresh and fails
expect(SimpleValue.aaa).toEqual('£££');
}));
});
describe('ttt', function() { // Neither does this
var SimpleValue;
beforeEach(module('myApp'));
beforeEach(inject(function(_SimpleValue_) {
SimpleValue = _SimpleValue_;
}));
it('ttt', function() {
expect(SimpleValue.aaa).toEqual('£££');
});
});
That is not the behaviour I was expecting from beforeEach(module('myApp'));. What is the point of using module('myApp') before each spec when according to documentation: https://docs.angularjs.org/api/ngMock/function/angular.mock.module it merely
collects the configuration information which will be used when the injector is created by inject.
? I thought that it would reset myApp to start clean for each it block but it turns out it doesn't.
Can anyone shed some light on this? Please see a plunk:
http://plnkr.co/edit/xMmstHxL2prQukXpXYXm?p=preview
Services registered with .value don't get reset. I can't tell you why, but it does seem unexpected.
Register using .factory for example, and tests pass as expected.
var app = angular.module('myApp', [])
// .value('SimpleValue', {aaa: '£££'});
.factory('SimpleValue', function () {
return {aaa: '£££'};
});
The thing here is that in your first test you are modifying properties of an object. But it is important to realize that there is only one copy of this object in the whole system. Re-writing myApp slightly differently should make it more clear:
var simpleValue = {aaa: '£££'};
var app = angular.module('myApp', []).value('SimpleValue', simpleValue);
See, you were kind of assuming that this part of the script is executed as part of each spec, which is not the case (this fragment of the code is loaded into browser only once).
Hopefully this plunker makes the whole story clear: http://plnkr.co/edit/iLuIPaLyVFgzoTLygFz1?p=preview

AngularJS : Why is my factory always undefined in my controller?

My factory is undefined in my controller and I cannot figure out why. I have created a simple example to illustrate.
Here I create the app:
var ruleApp = angular
.module( "ruleApp", [
"ngRoute",
"ruleApp.NewFactory1",
"ruleApp.NewFactory2",
] );
In this dummy example I'd like to build a factory that does something simple, show an alert box. I'll show two methods of doing this (one works, one does not).
Factory 1:
angular
.module('ruleApp.NewFactory1', [])
.factory('NewFactory1', function() {
return function(msg) {
alert(msg);
};
});
Factory 2:
angular
.module('ruleApp.NewFactory2', [])
.factory('NewFactory2', function() {
var showMessageFunction = function(msg) {
alert(msg);
};
return
{
showMessage: showMessageFunction
};
});
Notice the return type of factory 1 is a function and the return type of factory 2 is an object with a property (which is of type function).
Now look at how I'd like to use both of these factories in my controller:
ruleApp.controller( "RuleController", function( $scope, NewFactory1, NewFactory2 ) {
NewFactory1("1");
NewFactory2.showMessage("2");
});
This is where the problem gets exposed. During execution, I am prompted with the alert box for NewFactory1("1");, but it fails during execution of NewFactory2.showMessage("2"); because NewFactory2 is undefined (TypeError: Cannot call method 'showMessage' of undefined).
Can you help me spot the issue? I want to be able to use factories like NewFactory2 because I want the factories to be able to do more than just one thing (i.e. have more than one single function). By the way, I'm using Angular version 1.2.1.
factory 2 should be (demo)
angular
.module('ruleApp.NewFactory2', [])
.factory('NewFactory2', function() {
var showMessageFunction = function(msg) {
alert(msg);
};
return { // <--------- do not go on a new line after return
showMessage: showMessageFunction
};
});

Angular shared property from an injected service not working

Plunkr
I have this service I inject in my controllers. It is simply a service to share some properties.
angular.module('app', []).
service('sharedProperties', function () {
var list_name = '';
return {
getListName: function() {
return list_name;
},
setListName: function(name) {
list_name = name;
}
};
});
I have two controllers. In the first one, I set the value of list_name. In my second, I want to retried this information.
Here is how are defined my controllers :
function ListCtrl($scope, $http, sharedProperties) {
...
$scope.changeListName = function(list_name) {
sharedProperties.setListName(list_name);
console.log(list_name, sharedProperties.getListName()); # shows ( 'metro', 'metro') == metro being a dummy list_name
...
};
function ItemCtrl($scope, $http, sharedProperties) {
...
$scope.showOnlyList = sharedProperties.getListName();
console.log(this.sharedProperties.getListName()); # empty string
...
};
I logged the variable and checked them in the browser console and noticed that ListCtrl sets the shared Property properly. The issue comes from the ItemCtrl controller. It seems that when I try to access the list_name with sharedProperties.getListName();, the property is empty, or the function returns an empty string.
UPDATE
I thought the problem came from the service. So I decided to use Lungojs' data library.
I got the following code :
In ListCtrl :
$scope.changeListName = function(list_name) {
Lungo.Data.Cache.set("ListName", list_name);
console.log('LIST', Lungo.Data.Cache.get("ListName"));
};
In ItemCtrl :
$scope.showOnlyList = Lungo.Data.Cache.get("ListName");
console.log('ITEM', Lungo.Data.Cache.get("ListName"));
The log in ListCtrl shows that the cache is set to the correct list_name. However, the console for ItemCtrl shows that Lungo.Data.Cache.get("ListName") is undefined even if it was correct on the ListCtrl!
I also tried replacing the cache by HTML5 local storage without success...
Well, I think its because you instantly log your sharedListPropery to the console, right after instantiating your ItemCtrl.
When it is instantiated, sharedPropertyList has no value yet.
EDIT:
Sry, JSFiddle is currently not working, so I have to put this untested code here.
But it should give you an idea
angular.module('app', []).
service('sharedProperties', function () {
var list_name = '';
return {
getListName: function() {
return list_name;
},
setListName: function(name) {
list_name = name;
}
};
}).
controller('ListCtrl',['$scope','sharedProperties',function(scope,shared){
console.log(shared.getListName()); //empty, because nothing set yet.
scope.listname = shared.getListName();
//watching the change and updating the shared
scope.$watch('listname',function(value){
console.log('listname is now '+value);
shared.setListName(value);
})
//watching the shared directly
scope.shared=shared;
scope.$watch('shared.getListName()',function(value){
console.log("sharedProperty has changed to"+value);
})
}]);

Resources