is there a way to inject ngModel (ctrl) into directive controller?

1,330 views
Skip to first unread message

Nikita Tovstoles

unread,
Mar 27, 2014, 2:34:10 PM3/27/14
to ang...@googlegroups.com
I find it easier to unit-test controllers vs. link() functions since it's easy to inject $scope into former in beforeTest() and thus subsequently access $scope properties and methods for unit-testing. ie:

var scope = {};

beforeEach(function(){
$controller(MyCtrl, {$scope:scope});
})

it('test scope.foo(), function(){
expect(scope.foo()).toBe();///etc.
});

Seems like there is not a way to do the same for link() functions - without modifying build process. Thus, no easy way to unit-test scope.foo(), if declared inside a link() function (right?)

...which may be OK since I can just declare foo() inside controller. One issue with keeping logic inside directive controller vs link() that I encountered is dependency on injected ngModelCtrl (ie for directives which require latter). Is there a way to inject that ctrl instance into directive controller (similar to $element)? I only came up with:

link: function(scope,element, attrs, ngModel){
scope.$ngModel = ngModel; //for use in directive controller
}

Nikita Tovstoles

unread,
Mar 27, 2014, 2:40:32 PM3/27/14
to ang...@googlegroups.com
PS. I suppose I should have titled the below as 'how do I unit-test scope methods declared inside link()?'


--
You received this message because you are subscribed to a topic in the Google Groups "AngularJS" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/angular/1yiCh41gb28/unsubscribe.
To unsubscribe from this group and all its topics, send an email to angular+u...@googlegroups.com.
To post to this group, send email to ang...@googlegroups.com.
Visit this group at http://groups.google.com/group/angular.
For more options, visit https://groups.google.com/d/optout.

John Walker

unread,
Mar 28, 2014, 10:23:16 AM3/28/14
to ang...@googlegroups.com
Its pretty easy. Its really just the scope that you $compiled your directive with in the unit test in the first place. I'll just copy in one of my tests, if it doesn't make sense i'lll explain it. Sorry its in coffeescript if that bothers you.

Fyi this directive sets up a watcher on two fields to make sure they match.

describe 'directive:watch', ->

beforeEach module 'jw.watch'

scope = null
element = null

setInputsToMatch = ->
scope.user.password = '1234'
scope.user.confirm = '1234'
scope.$digest()

beforeEach inject ($compile, $rootScope) ->
scope = $rootScope
scope.user = {}
element = angular.element("
<form name=\"form\">
<input name=\"one\" ng-model=\"user.password\" required />
<input name=\"two\" ng-model=\"user.confirm\" jw-watch=\"user.password\" required />
</form>
")

$compile(element)(scope)
scope.$digest()


it 'should test the default validity state', ->
expect(scope.form.one.$valid).toBeFalsy()
expect(scope.form.two.$valid).toBeFalsy()


it 'should test validity when inputs do not match', ->

scope.user.password = '1234'
scope.user.confirm = '123'
scope.$digest()
expect(scope.form.one.$valid).toBeTruthy()
expect(scope.form.two.$valid).toBeFalsy()

Is this what you are looking for? In my link function i am watching scope variables, the ng-models from the html.

John Walker

unread,
Mar 28, 2014, 10:33:49 AM3/28/14
to ang...@googlegroups.com
Sorry, i just realized you were asking about scope functions, not scope properties. If that doesn't give you what you need, let me know and i'll throw a function in the unit test.

Matjaž Lipuš

unread,
Mar 28, 2014, 11:16:54 AM3/28/14
to ang...@googlegroups.com
you can test whole directive with $compile - as John demonstrated.

directive controller can not directly access other controllers. you can workaround this, to pass them via scope.

Nikita Tovstoles

unread,
Mar 28, 2014, 11:25:01 AM3/28/14
to ang...@googlegroups.com
Sorry, John, I am not sure the above quite does it. This may or may not matter, but I was thinking of a directive with isolate scope, ie:

app.directive('foobar', function(){
return {
restrict: 'E',
scope: {},
link: function(scope){
  scope.doStuff = function(){};
}
};
});

how would you unit-test doStuff()? in your example, you're asserting on the same scope as was used to compile directive, but in this case scope is a child of that scope - how does one get at it?

-nikita


On Fri, Mar 28, 2014 at 7:33 AM, John Walker <john.wa...@gmail.com> wrote:
Sorry, i just realized you were asking about scope functions, not scope properties. If that doesn't give you what you need, let me know and i'll throw a function in the unit test.

Matjaž Lipuš

unread,
Mar 28, 2014, 11:48:02 AM3/28/14
to ang...@googlegroups.com
on isolate scope you don't assert scope values. but directive behaviour.
so in unit test you trigger some event or sth. that calls doStruff and assert expected result of doStruff.

Nikita Tovstoles

unread,
Mar 28, 2014, 11:55:12 AM3/28/14
to ang...@googlegroups.com

Ok thanks

John Walker

unread,
Mar 28, 2014, 6:07:40 PM3/28/14
to ang...@googlegroups.com
You could have functions within an isolated scope that are passed in via the '&' or just local functions directives would use. They just are not tied to a controller. Is that something you need help with?

Nikita Tovstoles

unread,
Mar 29, 2014, 10:18:42 AM3/29/14
to ang...@googlegroups.com
No thanks, John. I really was just curious about unit-testing directive methods like doStuff()  as I described below. Although I understand Matjaž's point re: testing those implicitly by testing entire directive behavior, in more complex directives, it is often natural to split functionality into several smaller units and it'd be nice to be able to unit-test those without having to move them out of link() into a directive controller.

-nikita

John Walker

unread,
Mar 29, 2014, 12:00:12 PM3/29/14
to ang...@googlegroups.com
I think we are talking about the same thing. scope.doStuff() could be tested from within a link function using Jasmine and the spy test methods.

Nikita Tovstoles

unread,
Mar 29, 2014, 1:20:31 PM3/29/14
to ang...@googlegroups.com

Sorry if I am being thick them. Looking at the example below how would you invoke doStuff() - and only fiXture() - in a unit test?

Jeremy Smith

unread,
Mar 30, 2014, 1:09:01 AM3/30/14
to ang...@googlegroups.com
Here is how you can unit test a function defined in a link function in a directive with isolate scope:


Basically, get a reference to the isolate scope by calling the isolateScope() method on your compiled directive.  From that, you can call your function or set up spies.

Hope this helps!

Nikita Tovstoles

unread,
Mar 30, 2014, 3:09:55 PM3/30/14
to ang...@googlegroups.com
Excellent - just what I was looking for. Thanks, Jeremy.

-nikita
Reply all
Reply to author
Forward
0 new messages