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}}'
}
});
Related
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);
});
}
}
});
I've created some basic directive. It works well if I use it with some objects in html file
<some-directive some-data="123"></some-directive>
But if I dynamically load this object to my webpage:
//getting html source as a string, then appending it to DOM:
elem.html("<some-directive some-data='123'></some-directive>");
The directive doesn't work (object is being added properly to DOM)
app.directive('someDirective', function (notes, parts) {
return {
restrict: 'AE',
scope: {
someData: '='
},
link: function (scope, elem, attrs) {
console.log("directive fired");
}
};
});
What can I do to make it work properly?
For dynamic directives, you have to use $compile service that compiles scope into template. Look at sample below, <some-directive-wrapper /> will add <some-directive /> element into itself and compile scope value
var app = angular.module('app', []);
app.directive('someDirective', function () {
return {
restrict: 'AE',
scope: {
someData: '='
},
template: '<h2>someDirective compiled, someData is {{someData}}</h2>',
link: function (scope, elem, attrs) {
console.log("directive fired");
}
};
});
app.directive('someDirectiveWrapper', function ($compile) {
return {
restrict: 'AE',
link: function (scope, elem, attrs) {
//get value from ajax maybe
//and set to scope
scope.data = 123;
//replace directive with another directive
elem.html('<h1>someDirectiveWrapper compiled </h1>\n<some-directive some-data="data"></some-directive>');
//compile scope value into template
$compile(elem.contents())(scope);
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<some-directive-wrapper></some-directive-wrapper>
</div>
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 have a directive with a bound doc model from controller. But the directive does not show the document?
HTML:
<div ng-app="myApp">TestApp:
<div ng-controller="TestController as Test">Testdoc controller: {{Test.doc}}
<br>
<test-me ng-init="abc=123" my-model="Test.doc">Testdoc directive: {{myDoc}} | {{abc}}</test-me>
</div>
</div>
JS:
var app = angular.module("myApp", []);
app.controller("TestController", function () {
this.doc = {
name: 'testname',
id: null
};
});
app.directive("testMe", function () {
return {
scope: {
myDoc: '=myModel'
},
link: function(scope, elem, attrs){
}
}
});
output HTML is still:
TestApp:
Testdoc controller: {"name":"testname","id":null}
Testdoc directive: <missing output of doc here>| 123
jsfiddle: http://jsfiddle.net/kx97y93k/14/
What i am doing wrong?
Solution was not to call {{myDoc}} in html directly but inside a template.
app.directive("testMe", function () {
return {
restrict: 'E',
template: '<div>Testdirective: {{myDoc}}<div>',
scope: {
myDoc: '=myModel'
},
link: function(scope, elem, attrs){
}
}
});
See updated jsfiddle: http://jsfiddle.net/kx97y93k/18/
Many thanks to #Mr_Green for that hint
add restrict : 'E' to the DDO, see below. Angular 1.2 uses restrict : 'A' if not explicitly set otherwise. For Angular 1.3 that became 'EA' and your directive would have worked. To make it work in your fiddle, change it as follows:
app.directive("testMe", function () {
return {
restrict : 'E',
scope: {
myDoc: '=myModel'
},
link: function(scope, elem, attrs){
}
}
});
I'm trying to bind to ng-change on an element created by a directive up through two other directives that wrap it to a method on the controller using & bindings in an isolate scope, but I can't figure out how to get arguments to pass all the way through. Here's a plunk that demonstrates the problem.
In short, I have an HTML structure like this:
<body ng-app="ExampleApp">
<div ng-controller="Controller">
<button ng-click="doSomething('Called directly')">Call Function Directly</button>
<br />
<outer on-outer-model-changed="doSomething('Called from Outer in HTML')"></outer>
</div>
</body>
The controller:
var app = angular.module('ExampleApp', []);
app.controller('Controller', ['$scope',
function($scope) {
$scope.doSomething = function(one, two, three) {
console.log(arguments);
};
}
]);
The outer directive:
app.directive('outer', function($compile) {
return {
restrict: 'E',
scope: {
outerModelChanged: '&onOuterModelChanged'
},
link: function(scope, element, attrs) {
var innerElement = angular.element('<inner></inner>');
innerElement.attr('on-inner-model-changed', 'outerModelChanged(\'Called from Outer\')');
element.after(innerElement);
$compile(innerElement)(scope);
console.log(arguments);
}
}
});
And the inner directive that the outer directive creates:
app.directive('inner', function() {
return {
scope: {
innerModelChanged: '&onInnerModelChanged'
},
restrict: 'E',
template: '<button ng-click="innerModelChanged(\'Called from Inner\')">Call from Inner</button>'
}
});
I understand that I'm getting the output ["Called from Outer in HTML"] because this is hardcoded into the <outer> tag. What I don't understand is how to pass arguments all the way up from the inner directive.
I'm not sure I 100% get what you want to accomplish but this is how you would make the ["Called from Inner"] message appear.
Change the html so the on-outer-model-changed expression does not use a hardcoded string.
<body ng-app="ExampleApp">
<div ng-controller="Controller">
<button ng-click="doSomething('Called directly')">Call Function Directly</button>
<br />
<outer on-outer-model-changed="doSomething(outerParam)"></outer>
</div>
</body>
Then change the outer directive to call outerModelChanged with a parameter. And set the outerParam to the innerParam.
app.directive('outer', function($compile) {
return {
restrict: 'E',
scope: {
outerModelChanged: '&onOuterModelChanged'
},
link: function(scope, element, attrs, controller) {
var innerElement = angular.element('<inner></inner>');
innerElement.attr('on-inner-model-changed', 'outerModelChanged({outerParam:innerParam})');
element.after(innerElement);
$compile(innerElement)(scope);
console.log(arguments);
}
}
});
Finally call the innerModelChanged from the inner directive with the innerParam set to your message.
app.directive('inner', function() {
return {
scope: {
innerModelChanged: '&onInnerModelChanged'
},
restrict: 'E',
template: '<button ng-click="innerModelChanged({innerParam:\'Called from Inner\'})">Call from Inner</button>'
}
});
Here is a plunk to the above code.