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
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.
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 http://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.