1

http://jsfiddle.net/MTzJF/36/

The JSFiddle above is set up to explain the issue. But, basically:

HTML:

<breadcrumb></breadcrumb>
<div ng-view></div>

Angular Directive & Routing:

angular
    .module('app', [])
    .directive('breadcrumb', function() {
        return {
            restrict: 'E',
            template: "<ul class='breadcrumb'><li ng-repeat='node in path'><a ng-href='{{node.url}}'>{{node.label}}</a></li></ul>",
            replace: true,
            controller: Ctrl1
        }
    })
    .config(['$routeProvider', function($routeProvider) {
        $routeProvider
            .when('/', { 
                template: '<h1>{{pgTitle}}</h1>', 
                controller: Ctrl2
            });
    }]);

Controllers

function Ctrl1($scope) {
    $scope.path = [{
            label: 'Home',
            url: '#/'}];
    $scope.pgTitle = "Home"       
}
function Ctrl2($scope, $routeParams) {
    $scope.path = [{
            label: 'Home',
            url: '#/'},{
            label: 'Node 2',
            url: '#/node2'}];
    $scope.pgTitle = "Node 2"
}

​ I expect that changing $scope.path in Ctrl2 will update the breadcrumb directive, but it's not happening. I have to believe it has something to do with their relative scopes, but simply don't understand it well enough to see what. I've read dozens of articles and StackOverflow posts on it, but nothing is specific enough to let me see what I'm missing.

I'm hoping someone can point me in the right direction.

Thanks much!

nz

nathanziarek
  • 619
  • 1
  • 6
  • 16

1 Answers1

5

The reason your fiddle is not working is because ( like you rightly identified ) of scope issue. Your Ctrl1 is the Controller that controls the scope for your directive. The directive is looking for a variable called path which is an array of path's. If we have a look at the path variable in that scope it seems to be containing just 1 value inside of it.

function Ctrl1($scope) {
    $scope.path = [{
            label: 'Home',
            url: '#/'}];
    $scope.pgTitle = "Home"       
}

Now you wish to change this variable path in another controller Ctrl2. I am assuming that you are attempting to have the scope of Ctrl2 "inherit" from the scope of Ctrl1. To achieve this, first check on which element Ctrl2 is defined. Is that element ( html element ) a child of the element of Ctrl1 ?

From your HTML :

Element of Ctrl1 : <breadcrumb></breadcrumb>

Element of Ctrl2 : <div ng-view></div>

For Ctrl2 to be child of Ctrl1 : your HTML structure should look like :

<breadcrumb>
    <div ng-view></div>
</breadcrumb>

If we make this change to your code, it doesnt work yet. This is because when angular looks at the directive for <breadcrumb> it has no idea what it should do with the stuff that is inside of that node. <breadcrumb> is a html node. Since it is a node, it can contain content / other nodes inside of it. When you replace this breadcrumb node with the template, you should also give angular instructions to the effect : "If you find stuff inside of me, put it here". This is how you do it.

Modify your directive code to be :

.directive('breadcrumb', function() {
            return {
                restrict: 'E',
                template: "<div><ul class='breadcrumb'><li ng-repeat='node in path'><a ng-href='{{node.url}}'>{{node.label}}</a></li></ul><div ng-transclude></div></div>",
                replace: true,
                transclude : true,
                controller: Ctrl1
            }
    })

There are few differences / changes here.

  1. Added an attribute called transclude to the directive object and set it to true.
  2. Wrap the whole template so that it can be returned as a single HTML element.
  3. Specify the place where you want the contents of the to go. Notice the ng-transclude. That is the place where the contents go.

You will notice now that the content is getting replaced now. Except the path is not getting updated with the path value from the child controller. Have a look at this https://stackoverflow.com/a/14049482/1057639 answer. It is an amazing explanation of scope / prototypical inheritance. Basically, your write to path is creating a new path variable in the child scope that is overshadowing the parent path ( which is being used by the directive).

Your best bet in this scenario is to create an addPath function in the parent scope and use it ( in child scopes ) to add new Path when you define new subviews.

Here is a fiddle that does all these.

Community
  • 1
  • 1
ganaraj
  • 26,841
  • 6
  • 63
  • 59
  • This is a great explanation, and works great, although I'll be honest that I do not understand why `Ctrl1()` can use `Ctrl2`'s function, but not his variable. Are functions somehow not intwined in the same scoping scheme? Thanks much for the solution! – nathanziarek Dec 31 '12 at 18:58