I've got an angular-fullstack project that I've been working on. Everything has been working fine, until I tried to make a directive. At first I thought my directive was too complex, but I've since tried with a stock, generated directive and I'm still getting the same error.
Directive is here:
'use strict';
angular.module('angularApp')
.directive('test', function () {
return {
templateUrl: 'app/test/test.html',
restrict: 'EA',
link: function (scope, element, attrs) {
}
};
});
I inject it here in main.js
'use strict';
angular.module('angularApp')
.controller('MainCtrl', function ($scope, $http, socket, characterService, test) {
$scope.path = new Array();
$scope.heroes = new Array();
characterService.all().success(function(data) {
$scope.heroes = data;
});
});
Everything works fine as long as I don't have test in the list of injected stuff, but if it's there, I get the error.
angular.js:11607Error: [$injector:unpr] Unknown provider: testProvider <- test <- MainCtrl
Is there somewhere else I need to add it? I've tried adding it to the dependency list like this:
angular.module('angularApp')
.controller('MainCtrl', ['test', function ($scope, $http, socket, characterService, test)
...
But that doesn't seem to make any difference.
What would be the point of injecting a directive in a ... controller?
It strictly makes no sense, since directive aims to build a new HTML attribute/markup:
<test></test>
for instance.
Besides, a directive can define/use a controller in its implementation.
Don't confuse the whole ;)
Related
*Note: the code was written with TypeScript and the code below is the compiled javascript - I can provide the TypeScript if necessary.
I am trying to minify a file called bundle.js using grunt-contrib-uglify (mangle: true, beautify: true) which contains all compiled TypeScript we need for our AngularJs application. When running the application I run into the error "Unknown provider: bProvider <- b" and have been able to find the offending directive.
The directive below did not have inline annotations so I updated the code to have them:
appDirectives.directive('emailShareModal', ['$rootScope', '$compile', '$timeout', 'httpService', 'shareFromEmailService', function ($rootScope, $compile, $timeout, httpService, shareFromEmailService) {
return {
scope: {
type: '#type',
id: "=id",
defaultBody: "=body",
title: '#title'
},
link: function ($scope, element, attr) { return new emailShareModalDirective($scope, $rootScope, element, attr, $compile, $timeout, httpService); },
controller: function ($scope) { return new experienceShareForm($rootScope, $scope, shareFromEmailService); }
};
}]);
Now if I remove the 'controller' attribute, the directive does not throw an exception, telling me the link attribute is configured properly. Here is the experienceShareForm object being instantiated by the controller attribute:
var experienceShareForm = (function () {
function experienceShareForm($rootScope, $scope, shareFromEmailService) {
var _this = this;
this.$rootScope = $rootScope;
this.$scope = $scope;
this.shareFromEmailService = shareFromEmailService;
}
return experienceShareForm;
})();
I am at a loss to what may be the problem with the code - does anyone see anything amiss? Thanks much in advance.
Your controller is trying to use DI but you are not specifying what is going to be injected. The minification is changing $scope to the variable b and the exception is saying that no bProvider can be found.
Change the following:
controller: function ($scope) { return new experienceShareForm($rootScope, $scope, shareFromEmailService); }
to the following:
controller: ['$scope', function ($scope) { return new experienceShareForm($rootScope, $scope, shareFromEmailService); }]
Note that you do not need to do this for the link function, because it is always called with the same objects. The controller function can be injected with different arguments, specified by you.
I am struggling to get the controller from within a directive for unit testing. Here is my angular app:
angular.module('test-app', [])
.controller('loadingCtr', ['$scope', function ($scope) {
}])
.directive('loading', function() {
return {
restrict: 'E',
controller: 'loadingCtr'
};
});
Here is my unit test code:
describe('loading', function () {
beforeEach(inject(function($rootScope, $compile) {
var fooElement = $compile('<loading></loading>')($rootScope);
var fooController = fooElement.controller('loading');
$rootScope.$digest();
console.log(fooController);
}));
describe('loading: testing loading directive', function() {
it('should create loading directive', function() {
});
});
});
Here is a plnkr to mess around with: http://plnkr.co/edit/38cc5HQFgeHDhnC8OMo8?p=info
fooController always returns as undefined. I've tried using the following examples I've found online, but I always get the same results:
Unit testing a directive that defines a controller in AngularJS
http://daginge.com/technology/2014/03/03/testing-angular-directive-controllers-with-jasmine-and-karma/
Is there something obvious here that I am missing?
Only issue i can see is that you are not loading the module test-app in your fixture, which means that the compiled html code does not really compile the directive loading since it is not available in the injector. So try loading the module in the beforeEach block. Loading the module ensures that directives, controllers, services etc registered under the module is available in the injector otherwise it will just use the module as ng which does not know anything about the loading directive.
i.e
describe('loading', function () {
var fooController;
//Load the module
beforeEach(module('test-app'));
beforeEach(inject(function($rootScope, $compile) {
var fooElement = $compile('<loading></loading>')($rootScope);
fooController = fooElement.controller('loading');
$rootScope.$digest();
console.log(fooController);
}));
describe('loading: testing loading directive', function() {
it('should create loading directive', function() {
expect(fooController).toBeDefined();
});
});
});
Demo
Also note that if you are registering the controller with .controller you can directly get the controller instance by $controller(ctrlName) construct. If you are using controllerAs syntax with bindToController:true in your directive then you can get it from the scope with the property name same as the alias as well.
I'm using Videogular in an Angular app I'm working on. I wrote a plugin directive for it that listens to an event broadcast from $rootScope, and, if a video is playing, automatically pauses it when the event is broadcast.
omgYesDirectives.directive('vgAutoPause',
['$rootScope',
function($rootScope) {
return {
restrict: 'E',
require: '^videogular',
link: function($scope, $elem, $attr, $API) {
$rootScope.$on('onGameEnable', onGameEnable);
function onGameEnable(event, data)
{
$API.pause();
}
}
}
}
]);
But I'm having trouble figuring out how to unit test it. I can't seem to properly inject Videogular itself into my test. I've tried variations on this:
describe('vgAutoPause', function () {
var scope, compile, elm, videogular;
beforeEach(inject(function ($compile, $rootScope, videogularDirective) {
videogular = videogularDirective;
scope = $rootScope.$new();
compile = $compile;
}));
it('should instantiate as an HTML element', function () {
elm = compile('<videogular><vg-auto-pause></vg-auto-pause></videogular>')(scope);
scope.$digest();
expect(elm.html()).toContain('vg-auto-pause');
});
});
but Karma keeps complaining about it:
Error: [$injector:unpr] Unknown provider: videogularDirectiveProvider <- videogularDirective
Am I doing it wrong? Do you have any thoughts or suggestions on what I ought to be doing instead?
In AngularJS You can't inject a directive, you must create the HTML and then $compile it to start the $digest cycle.
For example, this is a simple videogular testing:
'use strict';
describe('Directive: Videogular', function () {
var element;
var scope;
beforeEach(module('myApp'));
beforeEach(inject(function ($compile, $rootScope) {
scope = $rootScope;
element = angular.element("<div><videogular><video></video></videogular></div>");
$compile(element)($rootScope);
}));
describe("videogular", function() {
it("should have videogular", function() {
scope.$digest();
expect(element.html()).toContain('<video></video>');
});
});
});
Maybe you need to understand first how to test directives, there's a lot of good info out there. You can start with this links:
http://docs.angularjs.org/guide/unit-testing
https://egghead.io/lessons/angularjs-unit-testing-a-directive
http://angular-tips.com/blog/2013/08/watch-how-the-apply-runs-a-digest/
I am a first time angularjs user and I am trying to create a directive but I cannot get around this error:
Error: Unknown provider: $scopeProvider <- $scope <- someDirectiveDirective createInjector/providerInjector<#http://localhost:4242/js/lib/angular/angular.js:2734 getService#http://localhost:4242/js/lib/angular/angular.js:2862 createInjector/instanceCache.$injector<#http://localhost:4242/js/lib/angular/angular.js:2739 getService#http://localhost:4242/js/lib/angular/angular.js:2862 ... ... ...
I create my angular app as follows:
var app = angular.module(
"myApp",
...
I then try and create my directive in another file:
app.directive('someDirective', function($http, $scope, $element, $attrs) {
return {
restrict: "A",
Then I use the directive:
I am sure that I am doing something really dumb but I have no idea.
Try this:
app.directive('someDirective', function($http) {
return {
restrict: 'A',
link: function(scope, element, attrs) {
}
};
});
The reason for your error is that Angular does not allow you to inject $scope into a directive.
If you want to access $scope from inside a directive then you refer to it as the first argument in the link function (see Epokk's answer).
I'm new to AngularJs and having problem trying to test a directive with dependency (although the directive itself works as expected). I was unable to find any answers here or on the other resources.
Here is my code:
Directive:
angular.module('MyApp')
.directive('appVersion', ['config', function (config) {
return function (scope, elm) {
elm.text(config.version);
};
}]);
Service (value):
angular.module('MyApp')
.value('config', {
version: '0.1'
});
Test:
describe('Directive: AppVersion', function () {
beforeEach(module('MyApp'));
var element;
it('should have element text set to config value', inject(function ($rootScope, $compile, config) {
var scope = $rootScope;
element = $compile('<app-version></app-version>')(scope);
expect(element.text()).toBe(config.version);
}));
});
My test is failing with message:
Error: Expected '' to be '0.1'.
meaning that config value got injected properly, but $complile was not using it. I would really appreciate any help on this. Thanks.
You didn't specify the restrict attribute of the directive.
When you don't specify it, that means angular looks for app-version declared as an attribute, not an element.
So you can either add the restrict attribute to the directive or change your template :
element = $compile('<div app-version></div>')(scope);