Angular, how to assign controller to markup before ng-repeat - angularjs

Can anyone tell me if is possible to assign a controller from a directive to the markup before ng-repeat does his thing?
The code below is just an example of what i want to do
<body ng-app="App">
<div my-t>
<div ng-repeat="obj in List">
{{obj.Name}}
</div>
</div>
angular.module('App',[])
.directive('myT',[function(){
return {
replace : true,
transclude : true,
template : '<div ng-controller="listCtrl"><div ng-transclude></div></div>'
};
}])
.controller('listCtrl', ['$scope', function($scope){
$scope.List = [
{Name: 'a'},{Name: 'b'}
];
}]);
Plunker
The problem is that when my directive changes the template, assigning the ng-controller the ng-repeat has already compiled and so it will display no data. Moving the ng-controller to the markup it´s not an option.
Best.

You can also put the controller to your directive, with controller: 'listCtrl', you can then also remove the ng-controller from your template.

<div my-t>
<div ng-repeat="obj in List">
{{obj.Name}}
</div>
</div>
Controller
angular.module('App',[])
.directive('myT',[function(){
return {
restrict: 'A',
replace : true,
transclude : true,
template : '<div><div ng-transclude></div></div>',
controller : 'listCtrl'
};
}])
.controller('listCtrl', ['$scope', function($scope){
$scope.List = [
{Name: 'a'},{Name: 'b'}
];
}]);

Related

Controller function not called from directive template in AngularJS

Directive template (items.html)
<li ng-repeat="item in itemCart">
{{item.title}} <br>
{{item.category}} &nbsp
{{ formatCurrencyFunction({cost: item.price}) }}
</li>
This custom directive is used in Second.html
<h1>
This is Second.
{{header}}
</h1>
<hr>
<ul>
<items-list item-cart="items" format-currency-function="formatPrice(cost)"></items-list>
</ul>
The code for Controller is:
myApp.directive("itemsList", function(){
return{
templateUrl:"contents/Views/Directives/Items.html",
replace: true,
scope:{
itemCart: "=",
formatCurrencyFunction: "&"
},
restrict:"EACM" // E-Element A-Attribute C-Class M-Comments
}
})
myApp.controller('secondController', ['$scope', '$log', '$http','$routeParams', function($scope, $log, $http, $routeParams) {
$scope.formatPrice = function(price){
return "₹ "+parseFloat(price).toFixed(2);
};
$scope.header = 'Second ' + ($routeParams.num || "");
$scope.testSecond = "Second";
$http.get("/items")
.success(function(data){
$scope.items = data;
});
}]);
The function formatPrice is not called from the directive Items.html
What has to be corrected in my code to get it working?
Inside the directive's link function, you'll want to call the method like so:
scope.someFn({arg: someValue});
myApp.directive("itemsList", function(){
return{
templateUrl:"contents/Views/Directives/Items.html",
replace: true,
scope:{
itemCart: "=",
formatCurrencyFunction: "&formatCurrencyFunction"
},
link:function(scope, element, attrs){
scope.formatPrice({arg: 35}); //scope.someFn({arg: 35});
},
restrict:"EACM" // E-Element A-Attribute C-Class M-Comments
}
})
Got this working by using ng-repeat in the Second.html page instead of the directive.
Directive template (items.html)
<li>
{{item.title}} <br>
{{item.category}} &nbsp
{{ formatCurrencyFunction({cost: item.price}) }}
</li>
This custom directive is used in Second.html
<h1>
This is Second.
{{header}}
</h1>
<hr>
<ul>
<items-list item="item" format-currency-function="formatPrice(cost)" ng-repeat="item in items"></items-list>
</ul>
Code in JS file
myApp.directive("itemsList", function(){
return{
templateUrl:"contents/Views/Directives/Items.html",
replace: true,
scope:{
item: "=",
formatCurrencyFunction: "&"
},
restrict:"EACM" // E-Element A-Attribute C-Class M-Comments
}
})
Inside the directive's link function, you need to call the method by applying some event to that attribute
Controller function 'formatCurrencyFunction()' can't be called by just placing it in an expression.
To use the function you need to call it based on an even, in your case ng-init will work.
<li ng-repeat="item in itemCart" ng-init="formatCurrencyFunction({cost: item.price})">
{{item.title}} <br>
{{item.category}}
</li>

Angular directive referencing wrong element

I have an issue where a directive i'm using for several similar elements in an ng repeat is showing the wrong element when calling the keyup function.
plunker: http://plnkr.co/edit/ARFlsgPdxikpzLScztxU?p=preview
Here's the same code:
html
<body ng-app="app">
<section ng-controller="MainController" ng-repeat="item in list">
<div ng-repeat="item in list">
<h3>Item {{$index}}</h3>
<div class="aliasContainer">
<input text="text" obj-key="alias" value="{{item.alias}}" ng-keyup="logItem($event, item)">
</div>
<div class="nameContainer">
<input text="text" obj-key="name" value="{{item.name}}" ng-keyup="logItem($event, item)">
</div>
</div>
</section>
<script src="https://code.angularjs.org/1.2.25/angular.js"></script>
<script src="script.js"></script>
</body>
js
var app = angular.module('app', []);
app.controller('MainController', ['$scope', function($scope){
console.log("hello ctrl");
$scope.list = [
{name: 'Dick Grayson', alias: 'Nightwing'},
{name: 'Bruce Wayne', alias: 'Batman'},
{name: 'Jason Todd', alias: 'Robin'}
];
}]);
app.directive('objKey', function() {
return {
restrict: 'A',
link: function(scope, element, attrs) {
scope.logItem = function($event, item) {
console.log(element);
};
}
};
});
The behavior the link function is exhibiting is for each div that is repeated, only the the input in the nameContainer gets passed on keyup (logging the element will show the input in nameContainer of the same parent div even if the input in aliasContainer was the triggering element.)
To reuse your directive and keeping its scope separate from outer scope (controller), you need to have a isolate scope to your directive
app.directive('objKey', function() {
return {
restrict: 'A',
scope: true, // << Isolating scope
link: ....
}
};
You should look into directive's scope: https://docs.angularjs.org/guide/directive
If you don't isolate the scope, your directive's scope will be the same as the scope when it's declared, in this case it will use the child scope created by ng-repeat.
To fix this, just isolate the scope with scope: true

Transclude inside ng-repeat, controller function incorrect transclude param

I created directive that appends transclude value after the directive body. I cant use ng-transclude inside my directive because it creates for the simple text as transclude value and it break my page. I use
controller: function($scope, $element, $transclude) {
$element.append($transclude().contents());
}
to append it. It works great, but when I use my directive inside ng-repeat something goes wrong and $transclude().contents() don't contain my text. Can someone explain this behavior?
Here example:
http://plnkr.co/edit/1y4avkwmgjhiKkuoZlug
I can't explain why $transclude().contents() don't contain your text, but I can show how to easy fix that please code below.
var app = angular.module('app', []);
app.controller('MainCtrl', function($scope) {
});
app.directive('test', function(){
return {
transclude: true,
restrict: 'E',
template: '<div >From directive </div> <div ng-transclude></div><hr/>',
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.22/angular.min.js"></script>
<body ng-app="app">
<div ng-controller="MainCtrl">
<div ng-repeat="a in [1, 2, 3, 4, 5]">
<test>From transclude {{a}}</test>
</div>
</body>

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';
}
};
});

ng-click attribute on angularjs directive

I think it should be easy to use the well known angular attributes on a directive out of the box.
For example if the name of my directive is myDirective I would like to use it this way:
<div ng-controller="myController">
<my-directive ng-click="doSomething()"><my-directive>
</div>
instead of needing to define a custom click attribute (onClick) as in the example below
<div ng-controller="myController">
<my-directive on-click="doSomething()"><my-directive>
</div>
It seems that ng-click can work, but then you need to specify ng-controller on the directive tag too which I don't want. I want to define the controller on a surrounding div
Is it possible to use ng-click on a directive together with a controller defined on a parent html element?
Here is updated code. Maybe is this what you were looking for.
Html:
<div data-ng-app="myApp">
<div data-ng-controller="MyController">
<my-directive data-ng-click="myFirstFunction('Hallo')"></my-directive>
<my-directive data-ng-click="mySecondFunction('Hi')"></my-directive>
</div>
</div>
Angular:
var app = angular.module('myApp', []);
app.directive('myDirective', function(){
return {
restrict: 'EA',
replace: true,
scope: {
eventHandler: '&ngClick'
},
template: '<div id="holder"><button data-ng-click="eventHandler()">Call own function</button></div>'
};
});
app.controller('MyController', ['$scope', function($scope) {
$scope.myFirstFunction = function(msg) {
alert(msg + '!!! first function call!');
};
$scope.mySecondFunction = function(msg) {
alert(msg + '!!! second function call!');
};
}]);
Edit
Check solution that I made in jsFiddler is that what you were looking for?
http://jsfiddle.net/migontech/3QRDt/1/

Resources