Why transclusion scope is not sibling with directive scope? - angularjs

This is my code:
<html ng-app="myApp">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.js"></script>
</head>
<body>
<div ng-controller="Ctrl">
<testel title="{{data.title}}">
<input ng-model="data.title">
<h3>Transclude title: {{title}}</h3>
<h3>Transclude data title: {{data.title}}</h3>
</testel>
</div>
<script>
angular.module('myApp',[])
.controller('Ctrl',function($scope){
$scope.data={};
$scope.data.title="Gas";
})
.directive('testel', function(){
return {
restrict: 'E',
scope: {
title: '#'
},
transclude: true,
templateUrl: "./transclude.html",
link: function(scope, element, attrs, ctrl, transcludeFn) {
console.log(scope.$$nextSibling);
}
}
});
</script>
</body>
</html>
and transclude.html:
<h3>Template title: {{title}}</h3>
<h3>Template data title:{{data.title}}</h3>
<ng-transclude></ng-transclude>
Why am I getting null from console.log(scope.$$nextSibling)? I was waiting the transclusion scope instead.
Also, how may I console.log the transclusion scope itself?

From Angular 1.3+ it is a child of isolated scope. Prior to that, they were siblings.

Related

I am facing problem in this code of custom directive

I am written this code for angularjs custom directive, but not geeting the output. Please help me.strong text
<html>
<head>
<title>Directives</title>
<script src="https://code.angularjs.org/1.6.9/angular.js"></script>
<script src="angular-1.7.6\angular.js"></script>
<script src="angular-1.7.6\angular.min.js"></script>
<script>
var app=angular.module("arrayApp", [])
app.controller("arrayCtrl", function ($scope) {
$scope.colors = ["RED","GREEN","BLUE","YELLOW" ];
});
app.directive('mycolor', function() {
return {
restrict: 'E',
transclude: 'true',
template: '<span ng-transclude></span>',
link: function(scope, element, attr){
element.append("<strong>"+attr.title+"</strong>");
}
};
});
</script>
</head>
<body ng-app="arrayApp">
<div ng-controller="arrayCtrl">
<div ng-repeat="c in colors">
<my-c title="{{c}}">
BASIC COLOR:
</my-c>
</div>
</div>
</body>
</html>
Iam not getting where I am wrong.
I am getting the output as
Output
The code is as below :
<html>
<head>
<title>Directives</title>
<script src="https://code.angularjs.org/1.6.9/angular.js"></script>
<script>
var app=angular.module("arrayApp", [])
app.controller("arrayCtrl", function ($scope) {
$scope.colors = ["RED","GREEN","BLUE","YELLOW" ];
});
app.directive('mycolor', function() {
return {
restrict: 'E',
transclude: 'true',
template: '<span ng-transclude></span>',
link: function(scope, element, attr){
element.append("<strong>"+attr.title+"</strong>");
}
};
});
</script>
</head>
<body ng-app="arrayApp">
<div ng-controller="arrayCtrl">
<div ng-repeat="c in colors">
<mycolor title="{{c}}">
BASIC COLOR:
</mycolor>
</div>
</div>
</body>
</html>
Firstly change the name of the directive into camelCase, it's a good convention for naming directives. So your directive name should be myColor.
Then use the directive like - my-color. Below is the whole code -
<html>
<head>
<title>Directives</title>
<script src="https://code.angularjs.org/1.6.9/angular.js"></script>
<script>
var app=angular.module("arrayApp", [])
app.controller("arrayCtrl", function ($scope) {
$scope.colors = ["RED","GREEN","BLUE","YELLOW" ];
});
app.directive('myColor', function() {
return {
restrict: 'E',
transclude: 'true',
template: '<span ng-transclude></span>',
link: function(scope, element, attr){
element.append("<strong>"+attr.title+"</strong>");
}
};
});
</script>
</head>
<body ng-app="arrayApp">
<div ng-controller="arrayCtrl">
<div ng-repeat="c in colors">
<my-color title="{{c}}">
BASIC COLOR:
</my-color>
</div>
</div>
</body>
</html>
This is the output -

Can't get transclude data binding working

I just made an example of how I am trying to use transclude data binding. Well, after binding an object (message) from controller scope its "undefined" in directive's link function.
<html ng-app="myApp">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.min.js"></script>
<script>
angular.module('myApp', []).controller('myAppController', function($scope)
{
$scope.message = { text: 'hello'}
});
angular.module('myApp').directive('myDirective', function() {
return {
restrict: 'E',
template: "<div><div ng-transclude></div></div>",
transclude: true,
scope: { message: "=" },
link: function (scope) {
// Its undefined here
console.log(scope.message);
}
};
})
</script>
</head>
<body ng-controller="myAppController">
<my-directive>
{{message.text}}
</my-directive>
</body>
</html>
Any help would be appreciated, Thanks
[EDIT]
Maybe I have not been too clear. Actually my code is kinda:
Home.html
Sorry, maybe I have not been too clear. Actually my code is KINDA:
<html ng-app="myApp">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.min.js"></script>
<script>
angular.module('myApp', []).controller('myAppController', function($scope)
{
$scope.model = { id: 100, name: "name"}
});
angular.module('myApp').directive('myDirective', function() {
return {
restrict: 'E',
templateUrl: "Modal.html",
transclude: true,
scope: { model: "=" },
link: function (scope) {
// Its undefined here
console.log(scope.model);
}
};
})
</script>
</head>
<body ng-controller="myAppController">
<my-directive>
<div>
id:<input type="text" ng-model="model.id" />
name:<input type="text" ng-model="model.name" />
<button class="btn btn-primary" ng-click="doIt()">do something</button>
</div>
</my-directive>
</body>
</html>
Modal.html
<div class="modal-header">
<h3 class="modal-title">Edit modal</h3>
</div>
<div class="modal-body">
<div ng-transclude>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-primary" ng-click="btnModalGravar()">Gravar</button>
<button class="btn btn-warning" ng-click="btnModalCancelar()">Cancelar</button>
</div>
Im just wondering why "console.log(scope.model)" returns "undefined" in directive's link function after using scope: { model: "=" }
angular.module('myApp').directive('myDirective', function() {
return {
restrict: 'E',
template: "<div><div ng-transclude></div></div>",
transclude: true,
scope: { message: "=" },
link: function (scope) {
}
};
})
//Html
<my-directive message="message">
{{message.text}}
</my-directive>
If you are trying to pass some data to directive it's not a translusion but an isolated scope. You've almost got it. Just use your directive like this:
<body ng-controller="myAppController">
<my-directive message="message"></my-directive>
<!-- This way you're referencing your $scope.message object, so it's like you've had:
<my-directive message="{text: 'hello'}"></my-directive>
-->
</body>
Transclusion is used to pass not some data but some markup (i.e. html, other directives).
If you want to use transcluded content inside your linking function, you should use it as a 5th parameter of this function, like this:
// Directive excerpt
return {
...
transclude: true,
link: function(scope, el, attrs, ctrl, transclude) {
var transcluded = transclude();
console.log(transcluded);
}
};
// View excerpt
<my-directive message="message">
<span>{{ message.text }}</span>
</my-directive>
You can take a look at Isolating the Scope of a Directive section in angular documentation Creating Custom Directives

ng-hide breaking my directive in angular 1.3.16

I'm trying to migrate from angular 1.2.28 to angular 1.3.16, however my code broke.
Angular 1.2.28 working: http://plnkr.co/edit/XfVakwA3Upm7Z2wosHCQ?p=preview
Angular 1.3.16 not working: http://plnkr.co/edit/4VxcHL0MHddobkmu9DMG?p=preview
JS
var app = angular.module('app', []);
app.run(function($rootScope, $timeout){
$rootScope.loading = true;
$timeout(function(){
$rootScope.items = ['Angular', '1.3.16', 'doesnt work'];
$rootScope.loading = false;
}, 3000);
});
app.directive('refresh', function(){
return {
restrict: 'A',
require: '^myDirective',
link: function(scope, element, attrs, ctrl){
if(scope.$last)
ctrl.init();
}
};
});
app.directive('myDirective', function(){
return {
restrict: 'E',
replace: true,
transclude: true,
template: '<div class="my-directive"><p>Height: {{myHeight}}</p> <div ng-transclude></div></div>',
controller: function($scope, $element){
this.init = init;
function init(){
$scope.myHeight = $('.my-directive').height();
}
}
};
});
HTML
<!DOCTYPE html>
<html ng-app="app">
<head>
<script data-require="angular.js#1.3.16" data-semver="1.3.16" src="https://code.angularjs.org/1.3.16/angular.js"></script>
<script data-require="jquery#1.11.0" data-semver="1.11.0" src="//cdnjs.cloudflare.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body>
<h1>Angular 1.3.16</h1>
<div ng-show="loading">Loading...</div>
<my-directive ng-hide="loading">
<div ng-repeat="item in items" refresh>
<p>{{item}}</p>
</div>
</my-directive>
</body>
</html>
The idea is to only run certain code when the inner html is outputted.
Height is 0 in angular 1.3.16.
However, if I remove ng-hide="loading" from <my-directive ng-hide="loading"> in angular 1.3.16, height gets the appropriated value.
Any ideas how can I solve this?
Inject $timeout into your directive and put the init code block in $timeout(function(){ ... }) like this:
app.directive('myDirective', function($timeout){
return {
restrict: 'E',
replace: true,
transclude: true,
template: '<div class="my-directive"><p><b>Height: {{myHeight}}</b></p> <div ng-transclude></div></div>',
controller: function($scope, $element){
this.init = init;
function init(){
$timeout(function(){
$scope.myHeight = $('.my-directive').height();
});
}
}
};
});
var app = angular.module('app', []);
app.run(function($rootScope, $timeout) {
$rootScope.loading = true;
$timeout(function() {
$rootScope.items = ['Angular', '1.3.16', ' work'];
$rootScope.loading = false;
}, 1000);
});
app.directive('myDirective', function($timeout) {
return {
restrict: 'E',
replace: true,
transclude: true,
template: '<div class="my-directive"><p>Height: {{myHeight}}</p> <div ng-transclude></div></div>',
link: function($scope, $element) {
$element.on('DOMAttrModified DOMSubtreeModified', init);
function init() {
$scope.$apply(function() {
$scope.myHeight = $element.height();
});
}
}
};
});
<!DOCTYPE html>
<html ng-app="app">
<head>
<script data-require="jquery#1.11.0" data-semver="1.11.0" src="//cdnjs.cloudflare.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script data-require="angular.js#1.3.16" data-semver="1.3.16" src="https://code.angularjs.org/1.3.16/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body>
<h1>Angular 1.3.16</h1>
<div ng-show="loading">Loading...</div>
<my-directive ng-hide="loading">
<div ng-repeat="item in items" refresh>
<p>{{item}}</p>
</div>
</my-directive>
</body>
</html>
You have to set the height in the correct angular directive phase/lifecycle. You should set the hight in the link or even postlink phase. Usually the two phases are the same if you don't use prelink This is when all the content has already been rendered. See angular $compile or google for angular post link
The controller is for the logic and the link is for html/dom manipulations.
EDIT:
You can bind 'DOMAttrModified DOMSubtreeModified` events to trigger changes.

AngularJS isolate scope with collection does not work

I want to use multiple instances of the same directive to render a collection. I am using an isolate scope to map an array to a name in the isolate scope. The link function in the isolate scope correctly sees the mapped array, however ng-repeat in it does not work.
<html ng-app="MyApp">
<head>
<script type="text/javascript" src="angular.js"></script>
<script type="text/javascript">
var MyCtrl = function($scope) {
$scope.blah = [1,2,4,5];
$scope.bar = [14,52,64,25];
}
angular.module("MyApp", [])
.directive("sephBlah", function($parse) {
return {
scope: {
tags: "=sephBlah"
},
link: function(scope, elem, attrs) {
console.log(scope.tags[0]);
}
}
});
</script>
</head>
<body ng-controller="MyCtrl">
<div seph-blah="blah">
<p data-ng-repeat="t in tags">{{t}}</p><!-- why this renders nothing? -->
</div>
<div seph-blah="bar">
<p data-ng-repeat="t in tags">{{t}}</p><!-- why this renders nothing? -->
</div>
</body>
</html>
I am not sure why the ng-repeat renders nothing. The link function correctly sees the arrays.
You're not supposed to use directives like this. The sort of functionality you're trying to get here should be done with a controller.
When using directives, use the template or templateUrl attribute to provide content. So this will work:
.directive("sephBlah", function($parse) {
return {
scope: {
tags: "=sephBlah"
},
template: '<p data-ng-repeat="t in tags">{{t}}</p>',
link: function(scope, elem, attrs) {
console.log(scope.tags[0]);
}
}
});
And in the html just do:
<div ng-attr-seph-blah="blah"></div>

Transclusion (true) - Combining values

Consider the following code (http://jsbin.com/IfejIWES/1/):
HTML:
<!DOCTYPE html>
<html ng-app="myApp">
<head>
<script src="http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.1.3/angular.min.js"></script>
<meta charset=utf-8 />
<title>JS Bin</title>
</head>
<body>
<div ng-controller="MyCtrl">
<div my-directive>
<button>some button</button>
and a link
</div>
</div>
</body>
</html>
JS:
var myApp = angular.module('myApp',[]);
function MyCtrl($scope) {
//$scope.log=[];
}
myApp.directive('myDirective', function(){
return{
transclude: true,
template: '<div class="something" ng-transclude> This is my directive content</div>'
};
});
Using version 1.1.3 of AngularJS, the output combines the button and anchor from my-directive (in the HTML) with the template inner text 'This is my directive content'.
If I change the version to 1.2.1 the my-directive content replaces the template inner text.
Is there a way to have angular 1.2.1 (and later) do the older behavior?
No. This was a very intentional change. See this commit.
The link provided by Jeff Hubbard (thanks Jeff) started me in the right direction. From the comments of that post somebody (https://github.com/angular/angular.js/commit/eed299a31b5a6dd0363133c5f9271bf33d090c94#commitcomment-4685184) posted a work around: http://plnkr.co/edit/EjO8SpUT91PuXP0RMuJx?p=preview
Essentially, you can get the old behavior back by changing the JavaScript to use the transcludeFn function in a separate directive. See below my updated code:
var myApp = angular.module('myApp',[]);
function MyCtrl($scope) {
}
myApp.directive('myDirective', function(){
return{
transclude: true,
template: '<div class="something" ng-transclude-append> This is my directive content</div>'
};
});
myApp.directive('ngTranscludeAppend', function() {
return function(scope, element, attrs, ctrl, transcludeFn) {
if (!transcludeFn) return;
transcludeFn(function(clone) {
element.append(clone);
});
};
});
Link to my updated jsbin: http://jsbin.com/IfejIWES/3/
One last note, I tried embedding the transcludeFn in my link function directly like:
link: function(scope, element, attrs, ctrl, transcludeFn) {
transcludeFn(function(clone) {
element.append(clone);
});
}
But this had the effect of creating the button and anchor twice. Moving it out into its own directive solved it for me.

Resources