Say I have the following two directives:
angular.module('foo').directive('outer', [function(){
return {
restrict: 'E',
scope: {
inner: '#',
innneParams: '#'
},
template: "<div {{inner}}{{innerParams}}></div>",
link: function(scope, elem, attrs){
console.debug("I AM IN YOUR OUTER DIRECTIVE PASSING YOUR D00DZ!")
}
}
}]);
angular.module('foo').directive('innerDir', [function(){
return {
restrict: 'EA',
scope: {
innerParam: '='
},
template: "<div>{{massagedInner}}</div>",
link: function(scope, elem, attrs){
console.debug('I AM IN YOUR INNER DIRECTIVE MASSAGING YOUR D00DS!')
scope.massagedInner = scope.innerParam + "FROM YOUR DOGE!"
}
}
}]);
And the following HTML:
<outer inner="inner-dir" my-awesome-scope-value="myAwesomeScopeValue" inner-params="inner-param='myAwesomeScopeValue'"></outer>
The outer directive console debug triggers, the inner one does not. Is there a good way for me to achieve this kind of behaviour?
Plunk: http://plnkr.co/edit/jXbtWvYvtFXCTWiIZUDW?p=preview
There are quite a few things you're doing wrong. I've made the changes that I thought are close to what you wanted it to do and you can change the code from here.
Here's a working version
And this is what script.js now looks like;
angular.module('foo', []);
angular.module('foo').controller('fooController', ['$scope', function(scope){
scope.myAwesomeScopeValue = 'O HAI THERE'
}]);
angular.module('foo').directive('outer', [function(){
return {
restrict: 'E',
scope: {
// inner: '#',
// innnerParams: '#'
innerParam: '#'
},
template: "<div inner {{inner}} {{inner-param}}></div>",
link: function (scope) {
console.log('OUTER', scope.innerParam);
}
}
}]);
angular.module('foo').directive('inner', [function(){
return {
restrict: 'A',
// scope: {
// innerParam: '='
// },
replace: false,
template: "<div>{{massagedInner}}</div>",
link: function(scope, elem, attrs){
scope.massagedInner = scope.innerParam + "FROM YOUR DOGE!"
console.log('INNER');
}
}
}]);
For brevity, I've left some of your lines commented out. I hope this helps.
Related
Is it possible to access in Directive the ng-model that is linked to the Controller?
<input type="text" ng-model="ctrl.valuelist.value" />
Controller:
ctrl.valuelist.value = 'initial value';
In directive:
function Directive($window) {
return {
restrict: 'E',
scope: {
},
controller: 'ctrl as ctrl',
link: function(scope, elt, attrs){
// something like this:
scope.valuelist.value = 'New value';
}
};
}
function Directive($window) {
return {
restrict: 'E',
scope: {
ngModel: '=' //<----- access to the model by this
},
controller: 'ctrl as ctrl',
link: function(scope, elt, attrs){
// something like this:
scope.ngModel = 'New value';
}
};
}
I'm a pleb, so be kind.
You can pass attributes to your directive by two way('=') this mean when you make change from directive will reflect in controller as well
function Directive($window) {
return {
restrict: 'E',
scope: {
list: '='
},
controller: 'ctrl as ctrl',
link: function(scope, elt, attrs){
// something like this:
scope.list.value = 'New value';
}
};
}
And you must pass valuelist to your directive example:
<directive list='valuelist'></directive>
Let's assume that i have a 2 directives: outer and inner. In first directive, i have some data and i want to insert second directive with ng-repeat in it. So my directives looks like:
outer.js
directive('outer', function($compile) {
return {
restrict: 'E',
template: '<div class="options"></div>',
scope: true,
bindToController: {
options: '='
},
link: function(scope, element) {
var list = $(element).find('.options');
// Here we dynamically insert html for second directive
$('<inner><span ng-bind="$select.getValue(item)"></span></inner>').appendTo(list);
$compile(list.contents())(scope);
},
controllerAs: '$select',
controller: function($rootScope) {
this.items = ['aaa', 'bbb'];
this.getValue = function(item) {
$rootScope.log += '\n' + item;
return item;
};
}
};
}
inner.js
directive('second', function($compile) {
return {
restrict: 'E',
require: '^first',
replace: true,
transclude: true,
template: '<ul><li><span></span></li></ul>',
link: function(scope, element, attrs, $select, transclude) {
var choices = $(element.find('li')[0]);
choices.attr('ng-repeat', 'item in $select.items');
var inner = $(choices.find('span')[0]);
transclude(scope, function(clone) {
inner.append(clone);
});
$compile(choices)(scope);
}
};
});
So, in my example, in each $digest cycle $select.getValue should be called twice: for 'aaa' and for 'bbb'. But actually it calls 3 times and first time is called for undefined, but i can't understand why
Any ideas?
See example:
var myApp = angular.module('myApp', []);
myApp.directive('outer', function($compile) {
return {
restrict: 'E',
template: '<div class="options"></div>',
scope: true,
bindToController: {
options: '='
},
link: function(scope, element) {
var list = $(element).find('.options');
$('<inner><span ng-bind="$select.getValue(item)"></span></inner>').appendTo(list);
$compile(list.contents())(scope);
},
controllerAs: '$select',
controller: function($rootScope) {
this.items = ['aaa', 'bbb'];
this.getValue = function(item) {
$rootScope.log += '\n' + item;
return item;
};
}
};
})
.directive('inner', function($compile) {
return {
restrict: 'E',
require: '^outer',
replace: true,
transclude: true,
template: '<ul><li><span></span></li></ul>',
link: function(scope, element, attrs, $select, transclude) {
var choices = $(element.find('li')[0]);
choices.attr('ng-repeat', 'item in $select.items');
var inner = $(choices.find('span')[0]);
transclude(scope, function(clone) {
inner.append(clone);
});
$compile(choices)(scope);
}
};
});
.log {
white-space: pre;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body ng-app="myApp">
<span class="log" ng-bind="log"></span>
<outer></outer>
</body>
Suppose, I have controller:
angular.module('tf').controller('Ctrl', function($scope){
$scope.params = {
orderBy: null
};});
And a directive "common":
angular.module('tf').directive("common", function() {
return {
restrict: 'E',
replace: true,
template: '<div><outer order-by="orderBy"><inner order-by-field="name1"></inner><inner order-by-field="name2"></inner></outer></div>',
controller: function ($scope) {
},
scope: {
orderBy: '='
},
link: function (scope, element, attrs) {
}
}});
Controller is using directive within it's template:
<div ng-app="tf">
<div ng-controller="Ctrl">
<common order-by="params.orderBy"></common>
<div style="color:red">{{params.orderBy}}</div>
</div>
This directive is using directive "outer":
angular.module('tf').directive("outer", function() {
return {
restrict: 'E',
transclude: true,
replace: true,
template: '<div ng-transclude></div>',
controller: function ($scope) {
this.order = function (by) {
$scope.orderBy = by
};
},
scope: {
orderBy: '=',
}
}});
Which is parent for the directive "inner":
angular.module('tf').directive("inner", function() {
return {
require: '^outer',
restrict: 'E',
transclude: true,
replace: true,
template: '<div ng-click="onClicked()">{{orderByField}}</div>',
controller: function ($scope) {
$scope.onClicked = function () {
$scope.outer.order($scope.orderByField);
}
},
scope: {
orderByField: '#'
},
link: function (scope, element, attrs, outer) {
scope.outer = outer;
}
}});
The directive "outer" shares "order" method with directive "inner" by it's controller. The directive "inner" is accessing it by using "require" mechanism.
For some reason, this is not working as expected (Property of the controller isn't updated each time it's changed by directive). If I place "orderBy" into object (e.g. {"order": {"by": null }} ) and use object instead of string value, everything is working as expected ( controller scope is properly updated by the directive). I know about "always use dots" best practices principle, but I don't wanna use it here, because it would make my directive's API less intuitive.
Here is jsfiddle:
http://jsfiddle.net/A8Vgk/1254/
Thanks
I'm trying to move my template from being in-line to it's own file. Everything was working before I changed from template to templateUrl
Glenn.directive('test', function($compile) {
return {
restrict: 'A',
priority: 1000,
terminal: true,
templateUrl: function(tElement, tAttrs) {
return ('test.html');
},
link: function(scope, element, attrs) {
attrs.$set('editable-text', 'content.' + attrs.edit + '.data');
attrs.$set('edit', null);
$compile(element)(scope);
}
}
});
test.html
{{ 'content.' + tAttrs.edit + '.data' }}
<button ng-click="' + tAttrs.edit + '_form'+ '.$show()" ng-hide="' + tAttrs.edit + '_form'+ '.$visible">edit</button>
Why isn't the tAttrs being passed to my template test.html?
I never seen this that way, i always pass an string to the templateUrl property like
...
templateUrl: './foodirective.tmpl.html'
...
You could assign the attrs from your directive element in the linking function:
myApp.directive('fooDirective', function(){
return{
restrict: 'E',
scope: true,
templateUrl: './foodirective.tmpl.html',
link: function(scope, elem, attrs){
// do stuff
scope.tAttrs = attrs;
}
}
})
I've prepared a plunk for you.
I wrote two directives in angularjs, one is embedded in the other one.
below is the scripts for the directives:
module.directive('foo', [
'$log', function($log) {
return {
restrict: 'E',
replace: true,
transclude: true,
template: '<div id="test" ng-transclude></div>',
controller: function($scope) {
this.scope = $scope;
return $scope.panes = [];
},
link: function(scope, element, attrs) {
return $log.log('test1', scope.panes);
}
};
}
]);
module.directive('bar', [
'$log', function($log) {
return {
restrict: 'E',
replace: true,
transclude: true,
require: '^?foo',
controller: function($scope) {
return this.x = 1;
},
template: '<div ng-transclude></div>',
link: function(scope, element, attrs, fooCtrl) {
return $log.log('test2', fooCtrl);
}
};
}
]);
below is the piece of html:
<foo ng-controller="IndexController">
<bar></bar>
</foo>
below is the element generated, inspected from the chrome developer tools
<div id="test" ng-transclude="" ng-controller="IndexController" class="ng-scope">
<div ng-transclude="" class="ng-scope"></div>
</div>
below is chrome console output:
test2
Array[0]
length: 0
__proto__: Array[0]
angular.js:5930
test1
Array[0]
length: 0
__proto__: Array[0]
Question:
The child directive can not get the parent directive's controller, so the fourth parameter "fooCtrl" for link function of "bar" is an empty array. what's the thing that I do it wrong?
update and answer:
At last I found the reason that made the weird result. It's just a stupid mistake that I have made:
// in directive "foo"
controller: function($scope) {
this.scope = $scope;
// This line below is wrong. It is here
// because I transcompiled coffeescript to js.
// return $scope.panes = [];
// It should be like below:
$scope.panes = []
// I should have written .coffee like below
// controller: ($scope) ->
// #scope = $scope
// $scope.panes = []
// return # prevent coffeescript returning the above expressions.
// # I should rather have added the above line
}
After correcting the mistake, I tried and found there's nothing to prevent using controller or providing empty content in child directives.
AFAIK, you cannot have a controller in the child directive.
Demo: http://plnkr.co/edit/kv9udk4eB5B2y8SBLGQd?p=preview
app.directive('foo', [
'$log', function($log) {
return {
restrict: 'E',
replace: true,
transclude: true,
template: '<div id="test" ng-transclude></div>',
controller: function($scope) {
$scope.panes = ['Item1','Item2','Item3']
return {
getPanes: function() { return $scope.panes; }
};
},
link: function(scope, element, attrs, ctrl) {
$log.log('test1', ctrl, ctrl.getPanes(), scope.panes);
}
};
}
]);
I removed the child controller.
app.directive('bar', [
'$log', function($log) {
return {
restrict: 'E',
replace: true,
transclude: true,
require: '^?foo',
template: '<div ng-transclude></div>',
link: function(scope, element, attrs, ctrl) {
scope.x = 1;
$log.log('test2', ctrl, ctrl.getPanes(), scope.panes, scope.x);
}
};
}
]);