Directive with isolated scope unexpectedly accessing parent scopes - angularjs

I have created a directive my-directive with isolated scope, but it looks like it is able to access property div1 of $rootScope and property div2 of its parent scope $scope of controller1.
What am I missing?
Javascript:
angular.module('app', [])
.controller('controller1', ['$scope',
function ($scope) {
}])
.directive('myDirective', [
function () {
return{
restrict: 'A',
replace: true,
scope: {
myDirective:'='
}
};
}]);
HTML:
<body>
<div id="1" ng-app="app" ng-init="div1='div1'">
<div id="2" ng-controller="controller1" ng-init="div2='div2'">
<div id="4" my-directive="value" ng-init="div4='div4'">
{{div4}}<br/>
{{div1}}<br/>
{{div2}}<br/>
</div>
</div>
</div>
</body>
Output:
div4
div1
div2

You should use transclude function inside the directive, otherwise that values will be bound to parent scope. Check this: http://pucksart.com/transclude-big-mistery/

The scope is isolated within the directive's template. Give your directive a template or write a link function and you will see that the scope is isolated. When you are access the directive scope's ancestors, this is happening outside the directive and then the parsed html is being 'transcluded' into your directive template, which is otherwise empty.
It is an interesting question though. I had not until now realized that omitting the template of a directive is effectively the same as including transclude: true, template: '<div ng-transclude></div>' in the directive definition.
Though you didn't include two of these properties, you actually did this:
.directive('myDirective', [
function () {
return{
restrict: 'A',
replace: true,
transclude: true,
template: '<div ng-transclude></div>',
scope: {
myDirective:'='
}
};
}]);
Also note myDirective: '=' should be myDirective: '#' because it is just a string.

Related

AngularJS Directive Transclude parent scope

I'm working on a directive and I'm using transclude so the inner element of the directive use is used inside it.
Let's say that this is my directive's view:
<div>
<div ng-repeat="opt in options">
<ng-transclude></ng-transclude>
</div>
</div>
Directive:
app.directive("myDirective", function(){
return {
restrict: "E",
transclude: true,
templateUrl: 'my-directive.html',
scope: {
options: '='
}
};
});
And a simple use of it:
<my-directive options="someOptions">
<p>{{someObject[$parent.opt]}}</p>
</my-directive>
This works just fine. My problem with this solution is that that $parent.opt is not very readable and clear...
Is there any other option?
Thanks
Your directive seems to be a very specific one.
How about passing the parent to the directive too?
<my-directive options="someOptions" object="someObject"></my-directive>
In the directive:
<div>
<div ng-repeat="opt in options">
<p>{{object[opt]}}</p>
</div>
</div>
And then add object: '<' in your isolated scope declaration.

AngularJs transclude not working in Directive template or templateURL

I have written a custom directive like so, notice I have commented out the template URL that contains the same HTML structure and the template property:
.directive('sillyDirective', function ( ) {
'use strict';
return {
restrict: 'A',
replace: false,
transclude: true,
template: '<h2>Welcome to my site</h2>',
//templateUrl: '/views/hello.html' ,
link: function (scope, element, attrs) {
element.bind('click', function (){
alert('you click me! I am clicked');
});
};
});
In my HTML view I have the following...
<div data-silly-directive>
<div><img src="logo.jpg></div>
<div><h1>My First Website</h1></div>
</div>
The problem is the content of the directive, e.g.:
<div><img src="logo.jpg></div>
<div><h1>My First Website</h1></div>
is being overwritten with the template content even thought I have set transclude to true and replace to false? What am I doing wrong here?
you need to specify ng-transclude in the template of your directive, this will let angular know where to insert the content of the markup.
app.directive("foo", function() {
return {
transclude: true,
template: "<div>the template</div><div ng-transclude></div>"
};
})
html:
<div foo>
Some Content Here
</div>
result:
<div foo>
<div>the template</div>
<div ng-transclude>Some Content Here</div>
</div>
here's a plnkr
source: https://www.accelebrate.com/blog/angularjs-transclusion-part-1/
Your template must contain an element with an ng-transclude attribute. That's where the body will be "pasted" by angular.
See
https://docs.angularjs.org/api/ng/directive/ngTransclude

How to write an angularjs directive that makes use of both scope and attributes and refer it thru compiled partial?

I want to write a directive which takes advantage of custom attributes, as follows:
<plant-stages
title="Exploration<br/>du cycle de<br/>développement<br/>de la plante"
></plant-stages>
The controller is currently as follows:
app.directive('plantStages', function () {
return {
restrict: 'AE',
templateUrl: 'corn.figure.plant.stages.html',
link: function (scope, element, attrs) {
scope.title = attrs.title;
}
};
});
The partial is as follows:
<figure class="cornStages">
<div>
<p>{{title}}</p>
</div>
<div ng-repeat="stage in stages">
<div class="stage{{stage.stage}}"></div>
<div>
BBCH : {{stage.bbch}}<br/>
{{stage.displayName}}
</div>
</div>
</figure>
The partial makes use of some scope model variables.
And {{title}} should support plain HTML injection out of the view which embeds it, hence should be compiled. I tried to support this but without success.
What modification should I make to have the HTML compiled?
A bonus question: when I pass the attribute in, I create a dummy title variable in the scope that persists where it should only be local. How would one make changes to handle this?
If you want to wrap HTML in your custom directive take a look at the transclude option (see docs):
module.directive('myDirective', function() {
return {
restrict: 'E',
transclude: true,
template: '<div ng-transclude></div>'
};
});
This enables you to place HTML within the directive tag which can be used in the template:
<div ng-controller="Controller">
<my-directive>
<h1>Test</h1>
</my-directive>
</div>
In case you really want to pass HTML via an attribute use ng-bind-html. This requires the ngSanitize module:
module.directive('myDirective', function () {
return {
restrict: 'E',
template: '<div ng-bind-html="title"></div>',
scope: {
title:'#'
}
};
});
I added this to your fiddle.

Adding a controller to an element directive

When adding a controller to a element directive, for example:
.directive('hello', function() {
return {
restrict: 'E',
replace: true,
transclude: true,
template: '<div class="hello" ng-transclude></div>'
};
});
I'm unable to access the scope of the controller:
.controller('HelloCtrl', function($scope) {
$scope.hello = "Hello World";
});
<hello ng-controller="HelloCtrl">
<h1>Hello Directive</h1>
<p>{{ hello }}</p>
</hello>
In this case {{ hello }} is undefined. The directive doesn't create a child nor an isolated scope. I also tried accessing the property with {{ $parent.hello }}.
What is happening here?
I created a CodePen to demonstrate this behaviour: http://codepen.io/jviotti/pen/ktpbE
Per the docs...
transclude makes the contents of a directive with this option have access to the scope outside of the directive rather than inside.
Therefore you need to move the ng-controller="HelloCtrl" declaration to an element higher up in scope.
Here is your CodePen fixed http://codepen.io/anon/pen/BjKHG

Using ng-switch in directive with transclude

I am trying to create a template that shows some transcluded content. When I use ng-show everything works fine, but using ng-if or ng-switch gives me problems. I get this error message: Illegal use of ngTransclude directive in the template! No parent directive that requires a transclusion found
I understand that ng-switch creates a new scope. But the transclude should still go up to the parent chain. Is this a defect in angularjs? See http://jsfiddle.net/HgvP7/
Here is my html, modified from the documentation example:
<div ng-app="docsTransclusionExample">
<div ng-controller="Ctrl">
<my-dialog>Check out the contents, {{name}}!</my-dialog>
</div>
<!-- my-dialog.html -->
<script type="text/ng-template" id="my-dialog.html">
<div ng-switch="1+1">
<div ng-switch-when="2">
<div ng-transclude></div>
</div>
</div>
</script>
</div>
And the code:
angular.module('docsTransclusionExample', [])
.controller('Ctrl', function($scope) {
$scope.name = 'Tobias';
})
.directive('myDialog', function() {
return {
restrict: 'E',
transclude: true,
scope: {},
templateUrl: 'my-dialog.html',
link: function (scope, element) {
scope.name = 'Jeff';
}
};
});

Resources