Controllers with isolated scope

69 views
Skip to first unread message

Matthew Simpson

unread,
Apr 30, 2014, 5:10:48 AM4/30/14
to ang...@googlegroups.com
I am currently refactoring a large-ish angular app in an attempt to DRY up the code and slim down some very fat controllers. I have moved almost all my API access into services and it's working very well for me, but something has popped up which is really bothering me.

Essentially, I now have no use for scope inheritance in my controllers whatsoever. All my dependencies are injected, I never use anything from the parent scope. Furthermore, any data I want exposed to my views I explicitly attach to the scope for that controller, because I think it's clearer, more declarative, more testable etc. At the same time, I am aware that a lot of the time the scope will already contain that data because there are certain things I attach to the scope all over the place, e.g. currentUser. I would rather that the scope was not inherited from the enclosing scope, because it's simpler to test & reason about my controllers if I know I'm getting a blank-slate scope every time.

Here's an illustration of what's bothering me:


As you can see, there are two controllers, one inside the other. Each has its dependencies injected and attaches them to the scope for use by the view. They're completely encapsulated. And it works fine. Except, look at the inner controller! I've misspelt the name of my scope property, so the view shouldn't show anything! But of course it does show something, because the currentUser scope property is inherited from the enclosing view. This bugs me.

I've actually tried to write an alternative version of ngController that uses an isolate scope, but I can't get it to work (for some reason angular doesn't attach the scope to the view).

My questions are:

1. Is there an easy way to get an isolate scope in my controllers?
2. Is there a complicated/clever way to get an isolate scope in my controllers?
3. Should I just replace all my controllers with directives? (I don't want to do this)
4. Should I just use the "Controller as" syntax? (I don't want to do this either)
5. Should I just forget about this and get on with my life?

Thanks for reading this long and waffly post.

Sander Elias

unread,
Apr 30, 2014, 9:41:32 AM4/30/14
to ang...@googlegroups.com
Hi Matthew,

  1. no
  2. yes
  3. can be done. Not an good idea in my book
  4. Has no bearing on your issue
  5. yes!
If you want I can elaborate on each one, but if you're up for number 5, why bother ;)

Regards
Sander 

Matthew Simpson

unread,
Apr 30, 2014, 9:50:35 AM4/30/14
to ang...@googlegroups.com
Sander, I would be very grateful if you could elaborate on point 2! Even if it turns out to be more trouble than it's worth, I'm really curious to know how you would achieve it (particularly as I failed miserably in my attempt).

Thanks so much!

Sander Elias

unread,
Apr 30, 2014, 10:15:41 AM4/30/14
to ang...@googlegroups.com
Matthew,

Create a directive with an isolate scope, and transclude the contents. While doing so, make sure you use the isolate scope for the directive, the one that gets used to link the transcluded content to.
Need more?

Regards
Sander

Matthew Simpson

unread,
Apr 30, 2014, 10:39:42 AM4/30/14
to ang...@googlegroups.com
Ah, I'm afraid you've lost me with this: "While doing so, make sure you use the isolate scope for the directive, the one that gets used to link the transcluded content to." Could you explain further?

I thought I should reproduce the directive I already tried in a plunkr, and I found out something weird; you can do it very easily in Angular 1.0.8 but it no longer works in 1.2.16. Check it out:


As you can see, it works fine with 1.0.8. The inner controller scope is bound to the inner view and does not have the inherited attributes. But go into index.html and change the angular version to 1.2.16 and it stops working. Now the view is bound to the outer scope for some reason! Very strange...

Sander Elias

unread,
May 1, 2014, 12:01:47 AM5/1/14
to ang...@googlegroups.com
Hi Matthew,


Regards
Sander

Sander Elias

unread,
May 1, 2014, 12:06:51 AM5/1/14
to ang...@googlegroups.com
Matthew,

As you can see, it works fine with 1.0.8. The inner controller scope is bound to the inner view and does not have the inherited attributes. But go into index.html and change the angular version to 1.2.16 and it stops working. Now the view is bound to the outer scope for some reason! Very strange...

This was a bug that got fixed early on in 1.2 somewhere. The older versions had a bug that leaked the isolated scope into their children. If you need that, you don't need an isolate scope, but a new scope. 
An isolated scope is just that, isolated to the directive that creates it.  

Regards
Sander

Aleck Landgraf

unread,
May 1, 2014, 1:58:15 AM5/1/14
to ang...@googlegroups.com
Hi Matthew,

I agree with Sander and would add that some basic controller test coverage for your inner controller should verify that you misnamed a variable. 

    function create_inner_controller(){
        inner_controller = controller('inner_controller', {
            $scope: inner_controller_scope,
            CurrentUser: 'test user'
        });
    }
    it("should have a proper scope variable name", function() {
        // arrange
        create_inner_controller();

        // act
        inner_controller_scope.$digest();

        // assertions
        expect(inner_controller_scope.currentUser).toBe('test user');
    });

-Aleck

Matthew Simpson

unread,
May 1, 2014, 5:35:39 AM5/1/14
to ang...@googlegroups.com
Thanks for clarifying that Sander. I know that using 'new' for the scope parameter of the directive will bind the scope the way I want it, but it will also bring all the baggage from the parent scope that I don't want. Wish there was something like a "blank" option that worked like new but didn't inherit from the parent.

As to your plunker, it's not working for me. If you look at the output, inner.val is not being shown, suggesting to me that the isolate scope is not being bound to the view.

Thanks so much again, this is all very interesting.

Matthew Simpson

unread,
May 1, 2014, 5:42:56 AM5/1/14
to ang...@googlegroups.com
Hi Aleck, thanks for commenting. You're quite right about needing proper tests of course, but the inherited baggage still annoys me. And I think it makes the tests less reliable, because the scope in the tests will be subtly different from the scope in use - I would like to have *total* control of my scope so I know that my tests and my app are both using the controller in a precisely identical context.

Having said that, I'm definitely open to the possibility that it's more bother than it's worth. At this point I really just want to know if it can be done :)

Sander Elias

unread,
May 1, 2014, 5:48:23 AM5/1/14
to ang...@googlegroups.com
Hi Matthew,

Ah I see, a last minute change I didn't check trough all the way. try it again. I did put the se-isolate and the ng-controller on the same element. Thats not gonna work.

Regards
Sander

Matthew Simpson

unread,
May 1, 2014, 5:54:11 AM5/1/14
to ang...@googlegroups.com
Awesome! I should have spotted that myself. Now I just need to figure out if it's actually worth using...

Thanks again for your kind help!

Sander Elias

unread,
May 1, 2014, 5:57:33 AM5/1/14
to ang...@googlegroups.com
Matthew,

Let me help with that: NO.

Regards
Sander

Matthew Simpson

unread,
May 1, 2014, 6:09:32 AM5/1/14
to ang...@googlegroups.com
Hahahahahah.

Finally, for anybody else following this nonsense, I made another version of Sander's plunk which combines seIsolate and ngController into one ngIsolateController directive:

Aleck Landgraf

unread,
May 1, 2014, 12:28:03 PM5/1/14
to ang...@googlegroups.com
You could always namespace your scopes. 
E.g.
$scope.outer_ctrl = {};
$scope.outer_ctrl.customerName = "bob";
...
$scope.inner_crtl = {};

Aleck

--
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/b1EoEB9Bj1w/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.
Reply all
Reply to author
Forward
0 new messages