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

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>

Related

angularjs - access transclude html scope from hosting directive

I have a simple directive with transcluded html.
I want to be able to inject directive scope params to the transclude.
I wrote a simple example in plunker :
https://plnkr.co/edit/jqyiQdgQxbeTrzyidZYF?p=preview
I know in angular 4 it can be done, but I can't find a good way to do it in angularjs.
// Code goes here
var app = angular.module("app", []);
app.controller("mainCtrl", function($scope) {
$scope.users = ["tal", "oren", "orel", "shluki"];
$scope.deleteUser = (user) => {alert("trying to delete", user);}
});
app.directive('myList', function myList() {
return {
restrict: 'E',
transclude: true,
template: "<div><table><tr ng-repeat='item in collection'><td> This is inside myList - user name: {{item}} <ng-transclude></ng-transclude></td></tr></table></div>",
scope: {
collection: "="
},
replace: true
};
});
<!DOCTYPE html>
<html>
<head>
<script data-require="angularjs#1.6.2" data-semver="1.6.2" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.2/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-app="app" ng-controller="mainCtrl">
<h1>Hello Plunker!</h1>
<my-list collection="users">
<h2>This is transclude</h2>
<button ng-click="deleteUser(user)">Delete user: {{user ? user : "User name should be here"}}</button>
</my-list>
</body>
</html>
Will really appreicate some help.
plunker: https://plnkr.co/edit/jqyiQdgQxbeTrzyidZYF?p=preview
Here's a working plunker with your example.
http://plnkr.co/edit/BjSowyQdLXd0xoCZFqZ6?p=preview
The idea is to pass it as contents and not html as string. $compile is here because the link is done after ng-repeats already has transcluded its own template.
var template = '<h1>I am foo</h1>\
<div ng-repeat="item in users">\
<placeholder></placeholder>\
<hr>\
</div>';
var templateEl = angular.element(template);
transclude(scope, function(clonedContent) {
templateEl.find("placeholder").replaceWith(clonedContent);
$compile(templateEl)(scope, function(clonedTemplate) {
element.append(clonedTemplate);
});
});
If you want a proper explanation of what the problem was you should check the detailed answer here : Pass data to transcluded element
Hope this helped you out

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

Using bindToController without a template

I'm trying to learn how isolated scope, bindToController and controllerAs work.
If I make a directive with an isolated scope and a template, it works as expected:
<!DOCTYPE html>
<html ng-app="app">
<head>
<style>
</style>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
<script>
function directiveTest() {
function CtrlTest() {
this.foo = "honk";
this.clearFoo = function() {
this.foo='';
};
}
return {
restrict : 'E',
scope: {
label : '#',
lg : '#'
},
bindToController : true,
controller: CtrlTest,
controllerAs: 'ctrlTest',
template: '<label>\n {{ ctrlTest.label }}\n <input type="text" ng-model="ctrlTest.foo"/>\n</label>\n<button ng-click="ctrlTest.clearFoo()">Clear</button>\n\n<div>{{ ctrlTest.foo }}</div>\n\n<div ng-show="ctrlTest.foo.length> ctrlTest.lg">\n Long string !\n</div>\n'
};
}
var app = angular.module('app',[]);
app.directive('dirTest',[directiveTest]);
</script>
</head>
<body>
<dir-test label="Type something now:" lg="7">
</dir-test>
</body>
</html>
I was trying to do the same without the template, but I can't make it work:
<!DOCTYPE html>
<html ng-app="app">
<head>
<style>
</style>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
<script>
function directiveTest() {
function CtrlTest() {
this.foo = "honk";
this.clearFoo = function() {
this.foo='';
};
}
return {
restrict : 'E',
scope: {
},
bindToController : true,
controller: CtrlTest,
controllerAs: 'ctrlTest'
};
}
var app = angular.module('app',[]);
app.directive('dirTest',[directiveTest]);
</script>
</head>
<body>
<dir-test>
<label>
Type something again :
<input type="text" ng-model="ctrlTest.foo"/>
</label>
<button ng-click="ctrlTest.clearFoo()">Clear</button>
<div>{{ ctrlTest.foo }}</div>
<div ng-show="ctrlTest.foo.length>7">
Long string !
</div>
</dir-test>
</body>
</html>
However that works if I set scope to true instead of an isolate scope.
Can someone explain to me how to make the 2nd example work, or if it's not possible, why ?
Flip the values of bindToController and scope around.
{
....
scope: true,
bindToController: {}
...
}
I have just hit the same issue over the weekend, and made a simple complete example here: bindToController Not Working? Here’s the right way to use it! (Angular 1.4+)

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>

Angular js directive issue

Below is my code
I created a simple page and include a directive in it. And on ng-click in directive i want to call parent scope's method.
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Directive Page</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0/angular.min.js"> </script>
</head>
<body>
<div ng-app="myapp" ng-controller="myController">
<ul-dir on-item-click="itemClick(obj)"></ul-dir>
</div>
</body>
</html>
<script>
var myapp = angular.module("myapp", []);
myapp.directive('ulDir', function() {
return {
restrict: 'E',
replace: 'true',
template: '<div id="container"><ul><li ng-repeat="content in contents">{{content.name}}</li></ul></div>',
controller: function ($scope) {
$scope.contents = [{'name':'Nishu', 'age':'20'},{'name':'Nidhi', 'age':'21'},{'name':'Kirti', 'age':'24'}];
},
scope: {
onItemClick: '&' // Pass a reference to the method
}
}
});
myapp.controller('myController', function($scope) {
$scope.itemClick = function(content){
console.log("Success : "+content);
};
});
</script>
So my console log print as "success : undefined"
So content object not passing from directive scope to parentscope.
Please help me.
I believe your call inside template should either be:
<a href="#" ng-click="onItemClick({'content':content})">
or
<a href="#" ng-click="onItemClick({'obj':content})">
Can you try. For more details see this SO post Can an angular directive pass arguments to functions in expressions specified in the directive's attributes?

Resources