Can't get transclude data binding working - angularjs

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

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 -

undefined ng-repeat variable when passed from directive template to a controller

I have a directive with a template that uses ng-repeat on an anchor tag creating a gallery of anchors. Each anchor also has an ng-click which when clicked calls a parent controller function. To this function is passed the ng-repeat item.
Problem : This item when accessed inside the parent controller method is undefined
Here is a test scenario to simulate the similar situation
<testdirective func="show(x)" items="buttons"></testdirective>
http://plnkr.co/edit/43aNqFS71Jn9vOdh6AG2?p=preview
There are two changes you need to make.
in your index.html make a refrence to your function:
<testdirective func="show" items="buttons"></testdirective>
and in your testdirective change your template like so:
template: '<button ng-repeat="item in items" ng-click="func()(item)" id="submit" />{{item}}</button>',
Notice the change in the ng-click - first brackets is to get a refrence to the function itself and the second is to invoke the function with the paramater.
I also made a fork of your plunker:
http://plnkr.co/edit/nECPbL8YoToi0jP9HJHQ?p=preview
Please tell me if that's what you wanted to achieve
Your directive should bind to a controller, change it as follows,
app.directive("testdirective", function() {
return {
restrict: "E",
controller: 'testController',
template: '<button ng-repeat="item in items" ng-click="show(item)" id="submit" />{{item}}</button>',
scope: {
items: "=",
func: '&'
}
};
});
DEMO
// Code goes here
var app = angular.module('test', []);
app.controller('testController', function($scope) {
$scope.buttons = ['b1', 'b2', 'b3', 'b4', 'b5'];
$scope.show = function(x) {
alert(x);
}
})
app.directive("testdirective", function() {
return {
restrict: "E",
controller: 'testController',
template: '<button ng-repeat="item in items" ng-click="show(item)" id="submit" />{{item}}</button>',
scope: {
items: "=",
func: '&'
}
};
});
<!DOCTYPE html>
<html ng-app='test'>
<head>
<script data-require="angular.js#*" data-semver="1.3.0-rc.1" src="https://code.angularjs.org/1.3.0-rc.1/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body>
<div ng-controller="testController">
<h1>Hello Plunker!</h1> {{ testValue }}
<testdirective func="show(x)" items="buttons"></testdirective>
</div>
</body>
</html>

Why transclusion scope is not sibling with directive scope?

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.

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.

Alert directive not showing after it has been rendered once

I have a directive to display an alert message every time a post call succeeds/fails. The alert for the first post always succeeds but if I close the alert, and trigger another post the alerts don't show anymore.
Some code below:
Include tag in html
<alert message="notification"></alert>
Directive
app.directive("alert", function(){
return{
restrict: 'EA',
templateUrl: "alert.html",
replace: false,
transclude: false,
scope: {
message: "=",
close: "&"
},
link: function(){
}
};
});
Template
<div class="alert alert-{{message.type}}" ng-show="message.text">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
<div>{{message.text}}</div>
</div>
UPDATE: http://plnkr.co/edit/mECmQNSgW0EdXZGPmkNx?p=preview
Any thoughts?
Thanks in advance.
Instead of using data-dismiss="alert" you can have your close button call the close function. The close function can then clear the notifications since you have $scope.notification in the isolated scope as scope.message:
var app = angular.module('app', []);
app.controller('AlertDemoCtrl', function($scope) {
$scope.notification = {};
$scope.alertNotif = function() {
$scope.notification.type = "error";
$scope.notification.text = "demo text";
};
});
app.directive("alert", function() {
return {
restrict: 'EA',
template: '<div class="alert alert-{{message.type}}" ng-show="message.text">' +
'<button type="button" class="close" ng-click="close()" aria-hidden="true">×</button>' +
'<div>{{message.text}}</div>' +
'</div>',
replace: false,
transclude: false,
scope: {
message: "=",
close: "&"
},
link: function(scope) {
scope.close = function() {
scope.message = {};
}
}
};
});
<!DOCTYPE html>
<html ng-app="app">
<head>
<!-- jQuery -->
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.4/angular.js"></script>
<script src="script.js"></script>
</head>
<body>
<div ng-controller="AlertDemoCtrl">
<button ng-click="alertNotif()">test</button>
<alert message="notification"></alert>
</div>
</body>
</html>
Per the Bootstrap documentation (emphasis mine):
… Closing an alert removes it from the DOM.
Because it's removed from the DOM it will not show up again...

Resources