How to bind variables from directive's scope into transcluded template?
app.directive('foo', function(){
return {
restrict: 'E',
transclude: true,
template: '<div ng-transclude></div>',
link: function (scope) {
scope.num = 5;
}
}
})
<div ng-app="app">
<foo>
{{num}}
</foo>
</div>
You're missing an app module. Also I added a modified class so that you could see that the template is being applied:
var app = angular.module("app", []);
app.directive('foo', function(){
return {
restrict: 'E',
transclude: true,
template: '<div class="modified" ng-transclude></div>',
link: function (scope) {
scope.num = 5;
}
}
});
See plnkr: http://plnkr.co/edit/x9NE6A4kkqspKbO08yhq?p=preview
Related
I'm using compile in my directive controller to get the first directive element and compile it and then use it for other purpose I don't want to use the linking method of my directive, is there anyway to get rid this error ?
I've reproduced the issue in this JSFIDDLE:
var app = angular.module('app', []);
app.directive('panel', function ($compile) {
return {
restrict: "E",
replace: true,
transclude: true,
template: "<div><h1>handrouss</h1><div ng-transclude ></div></div>",
controller: function($scope, $element) {
var el = $compile($element.find('div')[0])($scope); // <--- this causing the issue
$scope.handrouss = el.html();
},
link: function (scope, elem, attrs) {
}
}
});
app.directive('panel1', function ($compile) {
return {
restrict: "E",
replace:true,
transclude: true,
template:"<div ng-transclude></div>",
link: function (scope, elem, attrs) {
elem.children().wrap("<div>");
}
}
});
HTML :
<div data-ng-app="app">
<panel1>
<panel>
<input type="text" ng-model="firstName" />{{firstName}}
</panel>
<input type="text" ng-model="lastname" />
</panel
Remove the ng-transclude attribute from the element before compiling in the controller.
app.directive('panel', function ($compile) {
return {
restrict: "E",
replace: true,
transclude: true,
template: "<div><h1>handrouss</h1><div ng-transclude ></div></div>",
controller: function($scope, $element) {
var div = $element.find('div');
//REMOVE ng-transclude attribute
div.removeAttr('ng-transclude');
var el = $compile(div[0])($scope);
$scope.handrouss = el.html();
},
link: function (scope, elem, attrs) {
}
}
});
As the transclusion has already been done in the compile phase of the directive the ng-transclude directive is no longer needed when compiling in the controller.
The DEMO on JSFiddle
How can I pass a child attribute directive's scope or attr value to a parent directive?
Given widget directive, with in-viewport attribute directive, I want to update the attribute inView each time the document is scrolled, and pass the updated value to the parent directive widget:
<widget in-viewport></widget>
In Viewport directive: passed in as an attribute of parent directive "widget"
angular.module('app').directive('inViewport', function() {
return {
restrict: 'A',
scope: false, // ensure scope is same as parents
link: function(scope, element, attr) {
angular.element(document).on('scroll', function() {
// I've tried binding it to attr and parent scope of "widget" directive
attr.inView = isElementInViewport(element);
scope.inView = isElementInViewport(element);
});
}
};
});
Widget Directive:
angular.module('app').directive('widget', function() {
return {
restrict: 'AE',
scope: {
inView: '='
},
transclude: false,
templateUrl: 'directives/widgets/widgets.tpl.html',
link: function(scope) {
console.log('In Viewport: ', scope.inView); // Null
Here is the way you can access parent directive variables,
angular.module('myApp', []).directive('widget', function() {
return {
restrict: 'E',
template: '<viewport in-view="variable"></viewport> <h1>{{variable}}</h1>',
link: function(scope, iAttrs) {
scope.variable = 10;
}
}
}).directive('viewport', function() {
return {
restrict: 'E',
scope: {
inView: "=",
},
template: '<button ng-click="click()">Directive</button>',
link: function(scope, iElement, iAttrs) {
scope.click = function() {
scope.inView++;
}
}
}
});
HTML
<div ng-app="myApp" ng-controller="Ctrl1">
<widget></widget>
</div>
Here is the working jsfiddle
http://jsfiddle.net/p75DS/784/
If you have any question, ask in the comment box
Here is a working fiddle using your directive structure:
http://jsfiddle.net/ADukg/9591/
Markup is like this:
<div ng-controller="MyCtrl" style="height: 1200px;">
{{name}}
<hr>
<widget in-viewport></widget>
</div>
Just scroll the window to trigger the event. Note that the parent directive has a watch just to prove that the var gets updated...
var myApp = angular.module('myApp',[]);
myApp.directive('inViewport', function($timeout) {
return {
restrict: 'A',
scope: false, // ensure scope is same as parents
link: function(scope, element, attr) {
angular.element(window).bind('scroll', function() {
console.log('Called');
$timeout(function() {
scope.inView++;
}, 0);
});
}
};
});
myApp.directive('widget', function() {
return {
restrict: 'AE',
transclude: false,
template: '<p>This is a widget</p>',
link: function(scope) {
scope.inView = 0;
console.log('In Viewport: ', scope.inView); // Null
scope.$watch('inView', function(newVal, oldVal) {
console.log('Updated by the child directive: ', scope.inView);
});
}
}
});
function MyCtrl($scope) {
$scope.name = 'Angular Directive Stuff';
}
You can expose an API on your parent directive and use isolateScope() to access it.
Here's a working fiddle.
var app = angular.module("app",[]);
app.directive("widget", function($rootScope){
return {
template: "<div>Scroll this page and widget will update. Scroll Y: {{scrollPosY}}</div>",
scope: {}, // <-- Creating isolate scope on <widget>. This is REQUIRED.
controller: ['$scope', function DirContainerController($scope) {
$scope.scrollPosY = 0;
// Creating an update function.
$scope.update = function(position) {
$scope.scrollPosY = position;
$scope.$digest();
};
}],
}
});
app.directive("inViewport", function($window, $timeout, $rootScope){
return {
restrict: 'A',
link:function(scope, element, attrs, parentCtrl){
// Get the scope. This can be any directive.
var parentScope = element.isolateScope();
angular.element(document).on('scroll', function() {
// As long as the parent directive implements an 'update()' function this will work.
parentScope.update($window.scrollY);
console.log('parentScope: ', parentScope);
});
}
}
});
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
Here is my plunkr: http://plnkr.co/edit/A0s3kafWD1WIaFcwimIT?p=preview
I am trying to nest a directive inside another directive but it is not working.
Angular:
app.directive('test', function() {
return {
restrict: 'E',
template: '<div>Hello Parent</div>'
}
})
app.directive('childtest', function() {
return {
restrict: 'E',
template: '<div>Hello Child</div>'
}
})
HTML:
<test>
<childtest>
</childtest>
</test>
How do I correctly do this?
I think it very much depends on your actual use-case, but you have some options:
Don't give the parent directive a template:
app.directive('test', function() {
return {
restrict: 'E'
}
});
app.directive('childtest', function() {
return {
restrict: 'E',
template: '<div>Hello Child</div>'
}
});
HTML
<test>
<childtest>
</childtest>
</test>
Put the children in the template of the parent:
app.directive('test', function() {
return {
restrict: 'E',
template: '<div>Hello parent <childtest></childtest></div>'
}
});
app.directive('childtest', function() {
return {
restrict: 'E',
template: '<div>Hello Child</div>'
}
});
HTML
<test></test>
Give the parent a template, and use transclusion to move the child directive to inside the template
app.directive('test', function() {
return {
restrict: 'E',
transclude: true,
template: '<div>Hello Parent <div ng-transclude></div></div>'
}
});
app.directive('childtest', function() {
return {
restrict: 'E',
template: '<div>Hello Child</div>'
}
});
HTML
<test>
<childtest>
</childtest>
</test>
Basically I think what you are looking for here is ng-transclude. What it does is it allows you to keep any child elements inside of your custom directive - all the while still allowing you to add elements either before or after those child elements.
Including this, basically your parent directive changes to:
app.directive('test', function() {
return {
restrict: 'E',
transclude: true,
template: '<div>Hello Parent<div ng-transclude></div></div>'
}
});
The <div ng-translcude></div> tells angular exactly where you want the child included at and adding the transclude: true tells angular that you want to have this behavior. Take a look at your updated plunker here: http://plnkr.co/edit/HL9PD6U4V7p56T3Cqx5F?p=preview
I'm using Scope Isolation in one of my directives. However, this doesn't seem to work:
<div ng-controller="MyCtrl">
Hello, {{name}}!
<dir info='spec'>
{{ data }}
</dir>
</div>
var myApp = angular.module('myApp',[]);
//myApp.directive('myDirective', function() {});
//myApp.factory('myService', function() {});
function MyCtrl($scope) {
$scope.name = 'Superhero';
$scope.spec = 'Super';
}
myApp.directive('dir', function(){
return {
restrict: 'AE',
scope:{
data: '=info'
}
}
});
Fiddle: enter link description here
Here is a fiddle: http://jsfiddle.net/WA5t5/
Since this commit(1.2) Child elements that are defined either in the application template or in some other
directives template do not get the isolate scope.
You can do this instead:
myApp.directive('dir', function(){
return {
restrict: 'AE',
scope:{
data: '=info'
},
template: "{{ data }}"
}
});
If you want to alter this behavior check my other answer: Why I can't access the right scope?
Try:
myApp.directive('dir', function(){
return {
restrict: 'AE',
scope:{
data: '=info'
},
template:"<div>{{data}}</div>"
}
});
DEMO
Another solution using transclusion to bind the scope yourself:
myApp.directive('dir', function(){
return {
restrict: 'AE',
scope:{
data: '=info'
},
transclude:true,
compile: function (element, attr, linker) {
return function (scope, element, attr) {
linker(scope, function(clone){
element.append(clone); // add to DOM
});
};
}
}
});
You can still use the same html as before:
<div ng-controller="MyCtrl">
Hello, {{name}}!
<dir info='spec'>
{{data}}
</dir>
</div>
DEMO
You should have a template defined in your directive where you show the data scope variable. The html code does not know what the data scope variable is, it's only known in the directive's template. See this demo
myApp.directive('dir', function () {
return {
restrict: 'AE',
scope: {
data: '=info'
},
link: function (scope, element, attrs) {
console.log(scope.data);
},
template: 'Hello {{data}}'
}
});