Share Service Between Two Different Modules and two different js files - javascript

I have checked many questions but no one clearly give examples which uses different js files.
I am working on angular js, and I'm stuck in the following issue.
Issue is, I want to Call functions which is in different js file and has different modules.
Now I Create "Service",which handles both modules and both functions,here is my Code,
First Module in first.js
var TaskAPP = angular.module('TaskApp', []);
TaskAPP.factory("TestService", function () {
var users = ["ad", "bf", "tt"];
return {
all: function () {
return users;
},
first: function () {
return users[0];
}
};
});
second module in second.js
var LabAPP = angular.module('LabApp', ['TaskApp', 'TestService']);
LabAPP.controller("LabController", function ($scope, TestService) {
$scope.aa = TestService.first();
});
but here on this page,error occur Error: $injector:modulerr
Module Error
Can any one help?

Your TestService is defined on TaskAPP, which is why it can't be used in LabApp. The solution is to create a module just for services, you want shared across modules.
For example, create a module just to hold shareable services:
angular.module('SharedServices', [])
.service('TestService', function(){
// code
}
Then inject SharedServices module into whichever module you want to access the service from:
angular.module('TaskAPP', ['SharedServices'])
Then access the service you want using SharedServices.TestService.fooMethod()

Your mistake is to use the service name TestService as a module dependency - this line:
var LabAPP = angular.module('LabApp', ['TaskApp', 'TestService']);
This fails, because there is no module called TestService, there is only a service inside the module TaskApp with that name. This should work:
var LabAPP = angular.module('LabApp', ['TaskApp']);
LabAPP.controller("LabController", function ($scope, TestService) {
$scope.aa = TestService.first();
});
Love from Switzerland :)

Related

How inject external javascript file in angularjs controller

I need to use this NoSleep.js inside my AngularJs controller. I installed NoSleep using bower but I need to inject it to my angularjs controller, but I have no idea on how do it, NoSleep isn't an angular service and I need to use this declaration var noSleep = new NoSleep(); in order to use it.
I'm using babel, webpack, gulp, karma
Any advise?
Typically when using non-angularjs libraries in an Angularjs application they are wrapped in a module. Then you inject the module into your app and then inject the service/factory you've wrapped with this into your controllers as needed, so you'd create a module that looks something like this:
(() => {
"use strict"
angular.module('nosleep.module', []).factory('nosleep', ['', function () {
return new NoSleep()
}])
})()
Then you'd inject the module into your main application module with something like:
angular.module('myApp', '[nosleep-module')
Then in your controllers that require access to nosleep you'd inject nosleep.
class myController {
constructor(nosleep) {
this.nosleep = nosleep
}
}
Then from within your control your can address it with this.nosleep.
It looks like NoSleep just declares a function in the global scope, so you could do something like this:
NoSleepService.js
(function () {
// place noSleep minified code here
angular.module('myApp').service('noSleep', NoSleep);
})()
That will create a NoSleep object within the enclosing function so that it doesn't pollute the global scope and register a new instance of it to your angular app.
If you have any JS lib and it's not an Angularjs lib, just add it using script tag in the html file and call it like you used to before they create AngularJs.
Have you tried it ?
Or go to the other way and wrap the lib with AgnularJs service (write an AngularJs service and call the lib method inside it), then inject this new service anywhere you want.
Update:
What #Mike Feltman and #richbai90 said in their answers was good and maybe enough for you, but I think it's better to build your service and call a methods you created, but inside these method use what ever library you want (it'll be like using interfaces in C#):
(function() {
'use strict';
angular.module('nosleep.module', []).service('nosleep', ['', nosleepFunc]);
function nosleepFunc() {
var nosleepObj = new NoSleep();
var service = {
method1: method1,
method2: method2
//... etc
};
function method1() {
return nosleepObj.method1();
}
// ..... other methods
return service;
}
})();
This way if you wanted to change nosleep lib to another one, and that new one lib has another method names, you need only to change in your service code.

AngularJS communication between modules in different html files

There are a couple of variables I want to share between two html files and two modules. Firstly, I used the service as follows but it did not work.
In vote.js:
var vote = angular.module('vote', []);
vote.service('voteParam', function () {
this.candidates = [];
this.show_result;
this.vote_begin;
this.elec_begin;
this.setValue = function (voteParam) {
this.department = voteParam.department;
this.group = voteParam.group;
this.type = voteParam.type;
this.round = voteParam.round;
this.times = voteParam.times;
this.advance_num = voteParam.advance_num;
}
});
In vote-setting.js:
angular.module('admin')
.controller('vote_setting', function ($scope, $http, voteParam) {
$scope.begin_vote = function () {
init();
startit();
$scope.vote_begin = true;
$scope.elec_begin = true;
voteParam.vote_begin = true;
voteParam.elec_begin = true;
voteParam.show_result = false;
voteParam.setValue($scope.voteParam);
if (voteParam.candidates.length == 0) {
$scope.get_candidate();
}
};
}
When the value is set as true in vote-setting.js, it is showed as undefined in vote.js. I think it's because when I import vote-setting.js in vote-setting.html, variables in the service are initialized again. Is there any other way I can make this work?
The service worked. But the value of variables in it get initialized when the js file is imported again. This is the key of this problem. Thanks
You might want to know the following:
Generally,an angular app should have only one base module. This is the module which you add to your html using ng-app directive. So your html content will know about only one angular app.
You can however create as many other modules as you would like or need. These can be custom modules as in your case, or these can be third party library (modules written by other developers for your ready use.). In any of the above case you need to add extra modules to your base module as dependencies as shown below:
angular.module('baseModule', ['extraModule1, extraModule2]);
Once you are done with this, any component like any directive , service, factory written in any of the extra modules shall be available to you for use, as if they were all written in your baseModule.
Having said this and coming to your question, the code provided by you doesn't seem sufficient to entirely diagnose your issue. However from whatever I get, I suggest you to add vote module to admin module or vice-versa. If you have any other module as a base module, you need to add both vote and admin as dependencies to that module.
Hope this helps you to start fixing the issue. Let me know if any thing seems unclear yet.
Implement your shared data service as a factory.
Implement proper getter and setters.
Encapsulate your variables and only expose the getter, setters.
Example of shared data service (can also be used as a shared Model)
app.factory('voteParam', function() {
var param1;
var param2;
var param3;
return {
setParam1: function(value){
param1 = value;
},
getParam1: function(){
return param1;
},
setParam2: function(value){
param2 = value;
},
getParam2: function(){
return param2;
},
setParam3: function(value){
param3 = value;
},
getParam3: function(){
return param3;
}
}
});
In an application this factory should be initialized once only.
If you are using 2 different angular applications (in your case 'vote' and 'admin') then use local storage or session storage to share data between them

Testing Angular Factory with prototypes in Jasmine

I'm trying to test a angular factory constructed as the following:
angular.module('app')
.factory('PaymentCalculatorFactory', [
function() {
return {
function1: function1,
function2: function2,
// more functions
};
function function1() {
// implementation
}
function function2(){
// implementation
}
}]);
I'm using angular mocks and Jasmine with a tried and true pattern that I've used elsewhere but for some reason I'm getting the DI error
Unknown provider: PaymentCalculatorFactoryProvider <- PaymentCalculatorFactory
Here is the start of my jasmine tests
describe("Payment Calculator Factory", function() {
var factory;
beforeEach(module('app'));
beforeEach(inject(function(PaymentCalculatorFactory) {
factory = PaymentCalculatorFactory;
}));
// more describes and its and such
As far as I can tell all the files are in the correct location for testing. Any suggestions?
It was the ordering in the Grunt config... I had to put the file that contained the "app" module before the file that contained the PaymentCalculatorFactory. Thanks to #MicahWilliamson for pointing me towards the config.

Can I add a method to a requirejs module used as dependency in another module and have it available every time the module is loaded as dependency?

If I have a module like this:
define([
'app'
, 'text!index.html!strip'
, 'css!index'
],
function (App, source) {
var response = {};
App.newMethod = function (foo) {
console.log("foo ="+foo);
};
// return response object
return response;
}
);
I'm wondering how to add methods to a module that is used as a dependency in another module. Sure I can add methods to the object, but will these also update the App object when it is called from another module?
Question:
Is there a way to add methods to a module, which is loaded as a dependency and have these methods available on all modules, which require this dependency?
Short answer:
Yes. The module needs to be an object/instance (not a class) and it will work with requirejs.
Long answer:
When you require a module as a dependence for the first time Requirejs generates an object, and for the next times you requires the module Requirejs will return the object it generated the first time. So all the times you require a module you get always the same reference of the object.
With
define([], function () {
var app = {
//my methods.
};
return app;
});
and
define(['app'], function (app) {
app.newMethod = function (){
// ...
};
});
you can use app like this:
define(['app'], function (app) {
app.newMethod();
});
But injecting methods from one object to an other is a really bad practice. If you need something from an object just add it when creating the object, not by injection.
define([], function () {
var app = {
newMethod: function () {
// ...
},
// my methods.
};
return app;
});
For example if object A injects a new method that will be used in object B, but B is called when A is not loaded then there would be an error Object #<Object> has no method 'newMethod'

Unit test when loading things at app run with AngularJS

I need my app to run some configuration at runtime vi an HTTP endpoint.
I wrote a simple service to do that:
module.factory('config', function ($http, analytics) {
return {
load: function () {
$http.get('/config').then(function (response) {
analytics.setAccount(response.googleAnalyticsAccount);
});
}
}
});
Next, I call this module in a run block of my app module:
angular.module('app').***.run(function(config) {
config.load();
});
All is working well when the app is running but in my unit tests, I get this error: "Error: Unexpected request: GET /config"
I know what it means but I don't know how to mock it when it happens from a run block.
Thanks for your help
EDIT to add spec
Calling this before each
beforeEach(angular.mock.module('app'));
Tried this to mock $httpBackend:
beforeEach(inject(function($httpBackend) {
$httpBackend.expectGET('/config').respond(200, {'googleAnalyticsAccount':});
angular.mock.module('app')
$httpBackend.flush();
}));
But got:
TypeError: Cannot read property 'stack' of null
at workFn (/Users/arnaud/workspace/unishared-dredit/test/lib/angular/angular-mocks.js:1756:55)
TypeError: Cannot read property 'stack' of null
at workFn (/Users/arnaud/workspace/unishared-dredit/test/lib/angular/angular-mocks.js:1756:55)
TypeError: Cannot read property 'stack' of null
at workFn (/Users/arnaud/workspace/unishared-dredit/test/lib/angular/angular-mocks.js:1756:55)
EDIT since update to AngularJS 1.0.6
Since I've updated to AngularJS 1.0.6, advised by Igor from the Angular team, the issue is gone but now I've now got this one, which sounds more "normal" but I still can't figure out how to make it works.
Error: Injector already created, can not register a module!
I struggled with this error for a little while, but managed to come up with an sensible solution.
What I wanted to achieve is to successfully stub the Service and force a response, on controllers it was possible to use $httpBackend with a request stub or exception before initiating the controller.
In app.run() when you load the module it initialises the object and it's connected Services etc.
I managed to stub the Service using the following example.
describe('Testing App Run', function () {
beforeEach(module('plunker', function ($provide) {
return $provide.decorator('config', function () {
return {
load: function () {
return {};
}
};
});
}));
var $rootScope;
beforeEach(inject(function (_$rootScope_) {
return $rootScope = _$rootScope_;
}));
it("defines a value I previously could not test", function () {
return expect($rootScope.value).toEqual('testing');
});
});
I hope this helps your app.run() testing in the future.
I don't know if you are still looking for an answer to this question. But here is some information that might help.
$injector is a singleton for an application and not for a module. However, angular.injector will actually try to create a new injector for each module (I suppose you have a
beforeEach(module("app"));
at the beginning.
I had the same problem while using Angular, RequireJS, Karma and Jasmine and I figured out two ways to solve it. I created a provider for the injector function as a separate dependency in my tests. For example MyInjectorProvider which provides a singleton instance of $injector.
The other way was to move the following statements:
beforeEach(module("app"));
beforeEach(inject(function($injector){
...
})
inside the test suite description. So here is how it looked before:
define(['services/SignupFormValidator'], function(validator){
var validator;
beforeEach(module("app"));
beforeEach(inject(function($injector){
validator = $injector.get("SignupFormValidator");
})
describe("Signup Validation Tests", function(){
it("...", function(){...});
});
});
After applying the fix it looks like this:
define(['services/SignupFormValidator'], function(validator){
var validator;
describe("Signup Validation Tests", function(){
beforeEach(module("app"));
beforeEach(inject(function($injector){
validator = $injector.get("SignupFormValidator");
});
it("...", function(){...});
});
});
Both the solutions worked in my case.
You should mock every HTTP request with ngMock.$httpBackend. Also, here is a guide.
Update
You don't need the angular.mock.module thing, just need to inject your app module. Something like this:
var httpBackend;
beforeEach(module('app'));
beforeEach(inject(function($httpBackend) {
httpBackend = $httpBackend;
$httpBackend.expectGET('/config').respond(200, {'googleAnalyticsAccount': 'something'});
}));
In your tests, when you need the mocked http to answer, you will call httpBackend.flush(). This is why we have a reference to it, so you don't need to inject it in every single test you have.
Note you will need to load angular-mock.js in order to it work.

Categories

Resources