Im learning AngularJS and his separation of concerns using module.
I have created a simple app that is composed by an index.html that is the view, and two file .js, one for the controller and one for my own service.
The question is the following: How i can inject the .js file that contains the service, into the one for the controller?
That's my controller.js
var myApp = angular.module('myApp',[]);
myApp.controller('myController', function($scope, $window,$interval,$location) {
$scope.appUrl = $location.absUrl();
$scope.port = $location.port();
$scope.protocol = $location.protocol();
$scope.winWidth = $window.innerWidth;
$interval(function(){
$scope.winWidth = $window.innerWidth;
}, 1);
});
and this is the date-time-service.js
var service = angular.module('myApp',[]);
service.factory('dateTimeService', function() {
var dateTimeSvc = {};
dateTimeSvc.getData = function(){
return new Date().toDateString();
}
dateTimeSvc.getTime = function(){
return new Date().toTImeString();
}
return dateTimeSvc;
})
Obviosly i know that i can create a single .js file that contains both and call it into my view, but i want to understand the logic behind the dependencies injection starting with this "stupid" application, and then extends it to a little bit larger applications.
Thanks you all anticipately for all the answers!
We don't inject the js file. We inject dependencies.
If you are looking into how to dynamically inject modules in run time after angular context is initialisized -- its here http://blog.getelementsbyidea.com/load-a-module-on-demand-with-angularjs/
I have implemented this long ago where I dynamically call modules from server and inject it into my angular context based on which screen the user is in.
Related
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
I have this external API on the global scope:
var _viewerApi = function(){
var selectedObject = null;
return {
selectObject: function(obj){
selectedObject = obj;
},
getSelectedObject: function(){
return selectedObject;
}
}
}
viewerApi = _viewerApi();
I have another external non-angular package talking with this api.
How do I attach this so angular checks the getSelectedObject? I want to fire angular events when getSelectedObject returns something
I guess I need to do a service and connect this somehow?
You can save the above code in a separate file. Export that file into the relevant js file where you want to use the service. Inject that service into the angular controller you want to use within. It will create a instance of service to be used within a controller.
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 :)
I'm already some time in the development using AngularJS, and what I do write works, but know I've come to a point where I would like to run unit tests on my AngularJS code.
I have created a very simple service that will inject a stylesheet onto the page,
see the example code below:
var OfficeSuiteStylesheetInjectorModule = angular.module('OfficeSuiteStylesheetInjectorModule', []);
OfficeSuiteStylesheetInjectorModule.factory('stylesheetInjectorService', ['$q', function($q) {
// Returns the service itself.
return {
inject: function(id, uri) {
var deferred = $q.defer();
// Embed the stylesheet into the page, but only when it's non-existing.
if (!angular.element('#' + id).length) {
var link = StylesheetFactory.Create(id, uri);
{
link.onload = deferred.resolve;
angular.element('head').append(link);
}
return deferred.promise;
}
}
}
}]);
It's not a big service, it's just dependend on $q for promises so that I can run additional logic when the stylesheet has been embedded in the page.
Now, I'm using Jasmine (I'm quite new to this) for testing my JavaScript code and I would like to test this module.
I have a skeleton:
// Tests for the angular 'StylesheetInjectorService'.
describe('StylesheetInjectorService', function() {
var stylesheetInjectorService = {};
// This code is executed before every test.
beforeEach(function() {
// Tells jamine that the module we're working on is the 'OfficeSuiteStylesheetInjectorModule'.
angular.module('OfficeSuiteStylesheetInjectorModule');
});
// Ensures that it injects a stylesheet element into the page.
it('Should inject a stylesheet element into the page.', function() {
// How to test here that the stylesheet is injected?
});
});
});
How can I inject te service in the page and ensures that the stylesheet is loaded?
Edit: Loading service now works:
beforeEach(module('OfficeSuiteStylesheetInjectorModule'));
// This code is executed before every test.
beforeEach(function() {
// Inject the required dependencies into the page.
inject(function($injector) {
stylesheetInjectorService = $injector.get('stylesheetInjectorService');
});
});
The same question is still open however. How to test if a stylesheet was embedded in the page?
Any help is highly appreciated.
Kind regards
To write a spec for the attachment of a stylesheet to angular.element('head') I would change the logic a bit to attach it to $document.head.
If you dont want to do that, I would recommend that you change your service into a directive seeing as how injecting a script element, is manipulating the DOM. That way you would kill two birds with one stone, as you would need to inject $compile to test your directive (which would enable you to $compile a custom head element to boot). But this is slightly "over the top" for now.
Implementation:
if (!angular.element('#' + id).length) {
var link = StylesheetFactory.Create(id, uri);
link.onload = deferred.resolve;
$document.head.append(link);
return deferred.promise;
}
}
beforeEach:
/**
* Sorry, this was previously $location which was just
* such a silly mistake.
*/
var $timeout;
beforeEach(function () {
inject(function ($injector) {
$timeout = $injector.get('$timeout');
});
});
it:
it('attaches the stylesheet to $document.head', function () {
styleSheetInjectorService.inject('someId', '/path/to/some/stylesheet');
$timeout.flush(); // flush promises
expect(angular.element($document.head).lastChild[0].nodeName).to.eq('LINK');
});
Something along those lines should get you up and running. Bare in mind that the spec I wrote uses the chai#expect style assertions, and the mocha test framework. Edit the syntax to fit Jasmine if you mean to copy-paste.
I had this doubt a while ago.
To inject your controllers and services into your tests you need to use a tool called Angular Mocks. Here's some official reference about it.
I sugest you use it together with the Karma enviroment.
Here's a great Getting Started tutorial:
https://www.airpair.com/angularjs/posts/unit-testing-angularjs-applications
This other one is for the Ionic Framework, but can still aplly to your case
Hope it can help.
I am working on a project using Angular as front end framework. I put all my code into abc.js under this format:
(function(){
var globalVariable1;
var globalVariable2;
var globalVariable3;
var globalVariable4;
var myApp = angular.module('coolapp',[]);
myApp.controller('Controller1', [$scope, $rootScope, function(){
//blah blah
}]);
myApp.factory('Factory1', function(){
//blah blah
});
//....
})();
Now it is more than 500 lines with quite a few global variables.
I plan to separate them into different files. What should I do? Let's say I created
main.js
(function(){
var globalVariable1;
var globalVariable2;
var globalVariable3;
var globalVariable4;
var myApp = angular.module('coolapp',[]);
})();
Controller1.js
(function(){
var myApp = angular.module('coolapp');
myApp.controller('Controller1', [$scope, $rootScope, function(){
//blah blah
}]);
})();
What should I write in main.js to require or include or inject(not sure what is the difference between them) Controller1.js?
You just need to create another module which handle your all controllers and provide dependency to your main module:-
main.js
var myApp = angular.module('coolapp',['cntrlApp']);
Controller1.js
var myApp = angular.module('cntrlApp');
myApp.controller('Controller1', [$scope, $rootScope, function(){
//blah blah
}]);
Yes. Your way is the right way. You also can look on RequireJS
Assuming your project is going to continue to grow, I would suggest looking in to using require.js with Typescript. We've implemented that and it has greatly increased the robustness of our code, and it's much easier to manage and refactor our code.
Then depending on how complex your application is going to be you might look at structuring it in to a domain model, and then views, services, etc.... Obviously a lot of this depends on how complex of a project you're building and where you think you're project is going.
There are a lot of opinions of how to best structure an Angular app. You seem to be headed in a good direction. Here's how I prefer to do it:
In your index.html, include your scripts individually. The self-invoking anonymous functions will set themselves up with Angular. Use a build process like Grunt or Gulp to minify and concatenate these later.
<script src="main.js"></script>
<script src="Controller1.js"></script>
<script src="Factory1.js"></script>
In your main.js (I call it app.js typically, I think it's user prefererence).
(function() {
'use strict';
angular
.module('MyApp', [
// any module dependencies
]);
})();
In your Controller1.js, set up your controller, define it as an Angular controller and part of the application.
(function() {
'use strict';
angular
.module('MyApp')
.controller('Controller1', Controller1);
function Controller1($scope) {
// do something
}
})();
In your Factory1.js, set up your factory function and define it as an Angular factory and part of the application.
(function() {
'use strict';
angular
.module('MyApp')
.controller('Factory1', Factory1);
function Factory1() {
return function() {
// return something
};
}
})();
After that, there isn't much to do. My main.js usually has a config function in it and handles the routing. You can initialize the app by adding the ng-app="MyApp" to your html or body. You can initialize your Controller1 simply by adding the attribute ng-controller="Controller1" to an element in your DOM. If you need Factory1 to be accessible by Controller1, then when you define the function Controller1 in Controller1.js, do it like so function Controller1($scope, Factory1) {}.
That's all you really need to get it up and running with separate files.