I'm trying to get a service working using AMD loading with require.js. I have my controllers all working and can load $rootScope to store app-wide variables/data but I would also like to add some methods to modify or refresh some of the app-wide data when needed. Thus I was trying to create a service object in require.js
So I create this sort of a thing:
MyService.js
define( ["angular"], function (angular) {
var MyService = function($scope, $rootScope) {
this.$scope = $scope;
this.$rootScope = $rootScope
}
MyService.$inject = ["$scope"];
MyService.prototype = {
constructor: MyService,
// comma separated list of methods and properties
}
return MyService
})
I similarly define a couple of controllers (all already working fine) then access them in my require.js config something like:
requirejs(
[ "angular", "MyService", "MyController" ],
function(angular, MyService, MyController) {
let myAppHandle = angular.module('myApp',[])
.service("myService", ['$scope','$rootScope', MyService])
// the following throws an error:
// angular.js:15697 Error: [$injector:unpr]
// Unknown provider: $scopeProvider <- $scope <- myService
myAppHandle.controller('MyController', ['$scope', 'myService', MyController])
}
)
Any help is appreciated
Ahh, I was guessing in the dark at how to create a service properly in require.js and after some tinkering and banging my face on the keyboard, I eventually figured out the problem was not with the controller getting the service, but with the service getting $scope
The examples I found for require.js dealt mostly with controllers, components and directives and and in just about every case, $scope was the first thing passed in.
I took $scope out of the .service() in the definition and in the constructor for the class and it's now working.
define( ["angular"], function (angular) {
var MyService = function($rootScope) {
// using $rootScope to store app-wide variables
// accessible directly or via this service
// potentially updated/populated by methods in this service
this.$rootScope = $rootScope
}
MyService.prototype = {
constructor: MyService,
// comma separated list of methods and properties
}
return MyService
})
and
requirejs(
[ "angular", "MyService", "MyController" ],
function(angular, MyService, MyController) {
let myAppHandle = angular.module('myApp',[])
.service("myService", ['$rootScope', MyService])
myAppHandle.controller('MyController', ['$scope', 'myService', MyController])
}
)
for reference, this is what the controller looks like:
define( ["angular"], function (angular) {
var MyController = function($scope, myService) {
this.$scope = $scope
// make service handle available as class property
this.myService = myService
}
MyController.$inject = ["$scope"];
MyController.prototype = {
constructor: MyController,
// comma separated list of methods and properties
}
return MyController
})
(Hopefully this will save someone else the 2-3 hours I took figuring this out)
Related
I have this controller A which I'm trying to inject in every other controller.
What controller A does is, it communicates with a factory (which does some authentication services, communicates with database)
My factory looks like this and I named it myFactoryServices.js and included the link to it in my index page.
(function() {
angular
.module('myApp.myFactoryServices', [])
.factory('FactoryService', ["$http", "$location", function($http, $location){
var my = this;
my.someFunction = function()
{
//communiate with backend and return data
}
return my;
}]);
})();
and my Controller A looks like this:
(function() {
angular
.module('myApp.ControlA', [])
.controller('ControllerA', function($scope,$routeParams, FactoryService) {
var my = this;
FactoryService.someFunction();
});
})();
And I am trying to inject this controller in every other controller, but it does not work. I am pretty new to this kind of programming, can anyone tell me where I made mistake?
This is how I tried injecting a controller into another.
(function() {
angular
.module('myApp.ControlB', [])
.factory('ControllerBService', function($http) {
var baseUrl = 'backendurl/';
return {
getInfo: function() {
return $http.get(baseUrl+ 'getInfo');
}
};
})
.controller('ControllerB', function($scope,$routeParams, ControllerBService,ControllerA) {
var my = this;
});
})();
No error is coming, and the controller is not getting injected as I am not able to use those factory services. is this the correct method?
First of all you cannot inject controller to another controller, and One simple solution would be, instead of having each angular modules for each components, declare a module and add the factory service to controllers as dependency.
Code:
var app = angular.module('myApp', []);
app.factory('FactoryService', ["$http", "$location", function($http, $location){
var my = this;
my.someFunction = function()
{
//communiate with backend and return data
}
return my;
}]);
app.controller('ControllerA', function($scope,$routeParams, FactoryService)
{
var my = this;
FactoryService.someFunction();
});
app.controller('ControllerB', function($scope,$routeParams, FactoryService)
{
var my = this;
FactoryService.someFunction();
});
Controllers are not injectable, because controller is not singleton. Controllers are constructor functions used to create instances of controllers. For example you can have multiple instances of one controller in your app:
angular.module('app', []);
angular
.module('app')
.controller('Example', function () {
this.x = Math.random();
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular.js"></script>
<div ng-app="app">
First instance: <br>
<div ng-controller="Example as Ex1">
{{Ex1.x}}
</div>
Second instance: <br>
<div ng-controller="Example as Ex2">
{{Ex2.x}}
</div>
</div>
So if you want to share some behavior between controller you should use factory instead.
To inject a controller into another controller, use the $controller service.
app.controller('ControllerB', function($scope,$routeParams, $controller) {
var my = this;
$scope.ctrlA = $controller("ControllerA", {$scope: $scope});
});
The above example creates an instance of ControllerA as ctrlA with its $scope injected with the scope of the parent controller.
For more information, see AngularJS $controller Service API Reference.
Not really sure where I'm going wrong here. I'm trying to simply inject a service and use it in controller
app.js
var app = angular.module('myApp', [
'myApp.controllers.myCtrl',
'myApp.services.myService'
]);
js/controllers/myCtrl.js
var app = angular.module('myApp.controllers.myCtrl', []);
app.controller('homeController', function ($scope, MyService) {
...
});
js/services/myService.js
var app = angular.module('myApp.services.myService', []);
app.service("MyService", function ($http, $q) {
...
});
This results in the following error
Here you are trying to access myService service component of myApp.services.myService module in myApp.controllers.myCtrl module, without injecting the module will obviously going to get failed.
If you wanted to access the service in myApp.controllers.myCtrl module then you need to inject myApp.services.myService in the [](dependency array where you can all dependent module).
var app = angular.module('myApp.controllers.myCtrl', ['myApp.services.myService']);
By doing above line you do have all component(services) in myApp.controllers.myCtrl module.
js/controllers/myCtrl.js
var app = angular.module('myApp.controllers.myCtrl', ['myApp.services.myService']);
app.controller('homeController', function ($scope, MyService) {
...
});
Then not need to inject services in app.js again.
var app = angular.module('myApp', ['myApp.controllers.myCtrl']);
I'm just about to write tests for my angularjs-app. However when Im trying to run the test which is very simpel one i get the following error.
Error: [ng:areq] Argument 'MyPageController' is not a function, got undefined
I'll provide code for the setup of my controllers, config etc.
Route
var myPageApp = angular.module('myPageApp', ['ngRoute', 'ngAnimate', 'ngSanitize', 'app.controller', 'app.service', 'app.filter', 'app.config'])
.config(['$routeProvider', function ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'App_AngularJs/partials/myPage/myPage.htm',
controller: 'MyPageController',
reloadOnSearch: false,
});
}]);
Controller
var myPageApp = angular.module('app.controller', ['oci.treeview', 'ui.bootstrap'])
.controller('MyPageController', ['$scope', '$routeParams', '$location', '$timeout', '$filter', '$modal', 'myPageService',
function ($scope, $routeParams, $location, $timeout, $filter, $modal, myPageService) {
init();
function init() {
$scope.page = { value: $routeParams.page || 1 };
}
}]);
Simpel test
'use strict';
describe('Testing MyPageController', function(){
var scope;
//mock Application to allow us to inject our own dependencies
beforeEach(angular.mock.module('myPageApp'));
//mock the controller for the same reason and include $rootScope and $controller
beforeEach(angular.mock.inject(function($rootScope, $controller){
//create an empty scope
scope = $rootScope.$new();
//declare the controller and inject our empty scope
$controller('MyPageController', { $scope: scope });
}));
// tests start here
it('should map routes to controllers', function () {
module('myPageApp');
inject(function ($route) {
expect($route.routes['/'].controller).toBe('MyPageController');
expect($route.routes['/'].templateUrl).
toEqual('App_AngularJs/partials/myPage/myPage.htm');
});
});
it('should have variable assigned = "1"', function(){
expect(scope.page.value).toBe(1);
});
});
My wildest and best guess is that i need to mock app.controller but how? Everything starts out with myPageApp which holds references to service, controller etc etc..
I think your issue is that routing and the controller are trying to load different modules viz "myPageApp" and "app.controller" and in your test with beforeEach you are trying to load 'myPageApp' module to which router is associated but not the controller.
So it seems to me that either you use same module for both router and controllers. Or try loading both modules in the test. Still I believe associating the router and controller with same module makes more sense.
An small example as below. Extract the application module in common js file (may be call it app.js)
var myApp = angular.module(
'myApp');
Now define router as
myApp.config(function ($routeProvider) {
$routeProvider
.when('/testUrl', {
templateUrl: '/myApp/views/test-view',
controller: 'TestViewController'
})
Similary define controller as
myApp.controller('TestViewController',[your dependencies goes here])
And now in your tests
beforeEach(module('myApp'));
This will work for sure. Hope it helps.
I can test normal controllers fine. I cannot test controllers that inherit a base controller.
This is how we've been defining subclassed-controllers:
app.NavController = app.BaseController.extend({
...
});
This is the base:
app.BaseController = Class.extend({
$scope: null,
init: function($scope) {
this.$scope = $scope;
this.defineListeners();
this.defineScope();
},
defineListeners: function() {
// this.$scope.$on('$destroy',this.destroy.bind(this));
},
...
});
app.controller('BaseController', ['$scope', function($scope) {
return new app.BaseController($scope);
}]);
However, running Karma produces:
TypeError: 'undefined' is not an object (evaluating 'app.BaseController.extend')
Is there a different way of doing this? I've removed IIFE wrappers for testing. Class.js is included in my Karmaconfig. I'm using John Resig's Class inheritance.
by adding this to instantiate the sub-classes prototype, we can get access to base class by simply injecting $controller as a dependency.
app.controller('SubController', ['$scope','$location','$controller',
function($scope, $location, $controller) {
var controller = {
...
controller.prototype = $controller('BaseController', {
$scope: $scope
});
controller.init($location);
return controller;
i get the error "Unknown provider: {0}", if i try to call a service of the submodule inside the submodule
here the mainModule script
var mainApp = angular.module("mainApp", ["categoriesApp","ui.router"]);
//a service in the mainmodule which i can call with no problems in the submodule
mainApp.factory("getDataSvc", ["$http", "$q", "path", function ($http, $q, path) {
return{
...
some $http
....
}]);
and now the submodule
var categoriesApp = angular.module("categoriesApp", []);
//i can inject and use getDataSvc with no problems from the mainmodule
categoriesApp.controller("listCtrl", ["$scope", "getDataSvc", function ($scope, getDataSvc){
getDataSvc.categories().then(function(data){
...
})
}])
//my service in the submodule
categoriesApp.factory("sharedDataSvc", ["$scope", function ($scope){
return{
getValue: function(){
return "oioioioi";
}
}
}])
//in this line i get the error, if i try to inject the sharedDataSvc
//if i dont inject it, i get no errors, but cant use the service ;)
categoriesApp.controller("addBtnCrtl", ["$scope", "sharedDataSvc", function ($scope, sharedDataSvc){
console.log(sharedDataSvc.getValue());
}])
hope somebody can tell me what i`m doing wrong ;)
The problem is with sharedDataSvc factory
You cannot inject $scope to a factory because $scope is not a registered provider.
$scope is only injected to controllers ( within a locals object ).
categoriesApp.factory("sharedDataSvc", [ function (){
return{
getValue: function(){
return "oioioioi";
}
}
}])
each controller may be instantiated multiple time ( with each instance $scope is a different scope)
each service is instantiated only once and then cached ( the same instance injected everywhere)