Controller function not called from directive template in AngularJS - 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>

Related

Pass controller complex data to directive after render

just started learning angular, and having some trouble passing complex data from controller to directive that is executed afterRender. I have seen countless other posts but none seem to be my exact situations.
On HTML side:
<ul after-render="AssignSubCatExpnasion" class="inner">
<li class="titleLevelSecond" ng-repeat="SubCat1 in Categories[0].Children">
<div class="titleLevelSecondDiv toggle">
<span class="title"> {{ SubCat1.Name }} </span><span class="numbers">{{ SubCat1.Count }}</span>
</div>
<ul class="inner">
<li class="titleLevelThird" ng-repeat="SubCat2 in SubCat1.Children">
<div class="titleLevelThirdDiv">
<span class="title">{{ SubCat2.Name }}</span> <span class="numbers">{{ SubCat2.Count }}</span>
</div>
</li>
</ul>
</li>
On Angular Side:
<script>
var appGetBetCategories = angular.module('BetCategories', []);
appGetBetCategories.controller('GetBetCategories', function ($scope, $http) {
$http.get("http://localhost:49780/api/betcategory").then(function (response) {
$scope.Categories = response.data;
});
});
appGetBetCategories.directive('afterRender', ['$timeout', function ($timeout) {
var def = {
restrict: 'EA',
terminal: true,
transclude: false,
scope: {
Categories: '=',
},
link: function (scope, element, attrs) {
$timeout(scope.$eval(attrs.afterRender), 0);
}
};
return def;
}]);
Without a directive, all expressions execute properly, but I need to run js function called 'AssignSubCatExpnasion' after I am done with API call and have rendered my controls. I know I need to pass my controller scope to directive somehow, just can't wrap my head on how, since I already calling a function inside after-render="".
Any help or suggestions is greatly appreciated.

how to access controller dataset in custom directive

I was playing with angular js scope of custom directive and tried to use controller dataset in custom directive with different attribute name but did not succeed. Kindly see my code and suggest me what is the proper way to access the data and set in ng-repeat in custom directive.
Do I need to set ng-repeat on custom directive or inside the directive template ? I mean
like this
<movie-list ng-repeat="entry in movieData" > </movie-list>
or inside directive template
<movie-list movieArray = "movieData" ></movie-list>
and then
<div>
<ul >
<li ng-repeat="entry in movieArray"> {{...}} </li>
</ul>
</div>
here is my data
app.js
angular
.module('app')
.controller('homeController', function($scope) {
$scope.movieData = [{
name : 'PK',
star : 'Aamir Khan',
releaseYear : '2015'
},
{
name : 'PiKu',
star : 'Irrfan Khan',
releaseYear : '2015'
}
];
});
// custom directive
angular
.module('app')
.directive('movieList', function(){
// Runs during compile
return {
scope: { movieArray : '=movieArray' },
controller: function($scope, $element, $attrs, $transclude) {
},
require: '?ngModel',
restrict: 'E',
templateUrl: 'movie.html',
replace: true,
link: function($scope, element, attr, controller) {
// console.log($scope.$parent);
}
};
});
index.html
<div ng-controller="homeController" >
<div class="col-lg-6">
<movie-list movieArray="movieData"></movie-list>
</div>
movie.html
<div>
<ul class="list-group" >
<li ng-repeat="entry in movieArray" class="list-group-item" >
<h3 class="list-group-item-heading">{{ entry.name }}</h3>
<p class="list-group-item-text">
{{ entry.star }} - Release in {{ entry.releaseYear }}
</p>
</li>
</ul>
</div>
scope: { movieArray : '=movieArray' } >> not working
scope: { movieArray : '=movieData' } >> not working
even I changed the attribute
<movie-list movieArray="movieArray"></movie-list>
but not working
Update attribute name:
<movie-list movieArray="movieData"></movie-list>
To:
<movie-list movie-array="movieData"></movie-list>
Note: Directive or attribute name should be lower case.
If you want to skip scope (delete scope: { movieArray : '=movieArray' }, from your directive ) in your directive you can do that by below code in Index.html:
<div ng-controller="homeController" >
<div class="col-lg-6">
< div movie-list></div>
</div>
And in your controller you need to replace movieData by movieArray:
.controller('homeController', function($scope) {
$scope.movieArray= [{
name : 'PK',
star : 'Aamir Khan',
releaseYear : '2015'
},
{
name : 'PiKu',
star : 'Irrfan Khan',
releaseYear : '2015'
}
];

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

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

How to pass the value to the local scope of the directive?

I'm learning directives, it's cool thing but sometimes a little complicated. Please can somebody explain this:
I have custom directive with template of little form and it own local scope, and want to change the list of items form the main controller.
Please see it:
By clicking on change button I open a custom directive with input form template
<body ng-controller="testCtrl">
<h1>Hello Plunker!</h1>
<ul>
<li ng-repeat="item in list">
<div> {{item}} </div>
<button ng-click="edit()">Change</button>
<change ng-if='editable'></change>
</li>
</ul>
</body>
"Change" is the custom directive with the input form inside the other Html file
.directive('change', function(){
return {
restrict: "E",
replace: true,
scope: {
show: '='
},
templateUrl: "other.html"
}
})
Also there is another directive inside "change" directive. It's a button which I want to use inside "change" directive and inside my main controller. I can see my item list only from scope.$parent.item, but how to pass it in the function of my button directive?
How can I implement this?
.directive('save', function(){
return {
restrict: "E",
replace: true,
template: ' <button class="btn btn-sm btn-warning" ng-click="saving(item)">SAVE</button>',
link: function(scope,element,attr){
scope.saving = function(item){
console.log(item);
console.log(scope.$parent.item)
}
}
}
})
Please see the example: Plnkr
P.S. Sorry for my explanation, I hope that everything is clear
Simply pass in the item to each of your directives that need access to it. For example:
<li ng-repeat="item in list">
//snip
<save item="item"></save>
//snip
</li>
And then define your directive to bind the attribute to the scope:
.directive('save', function(){
return {
//snip
scope: {
item: '=' //two-way binding to 'scope.item'
},
//snip
link: function(scope, element, attr){
scope.saving = function() {
console.log(scope.item);
}
};
});
In angularjs, you have the $emit event.
Dispatches an event name upwards through the scope hierarchy notifying the registered $rootScope.Scope listeners.
$rootScope.Scope
HTML
<body ng-controller="testCtrl">
<h1>Hello Plunker!</h1>
<ul>
<li ng-repeat="item in list">
<div> {{item}} - <input type="text" ng-model="item">
<button ng-click="edit()">Change</button>
</div>
<div>
<change ng-if='editable'></change>
</div>
</li>
</ul>
</body>
Directive
directive('save', function(){
return {
restrict: "E",
replace: true,
template: ' <button class="btn btn-sm btn-warning" ng-click="saving(item, $parent.$index)">SAVE</button>',
link: function(scope,element,attr, controller){
scope.saving = function(item, index){
//Build our object with the index of $scope.list which is updated & the item value
var obj = {
index: index,
item: item
};
//Emit a 'change' event, and we pass our object data
scope.$emit('change', obj)
}
}
}
})
In the "change" directive, we use $emit to pass event, and to notify our $rootScope.Scope.
In the "change" directive template, you can see that we pass the $parent.$index and not the $index, in order to get the current item of the list.
Controller
controller('testCtrl', function($scope){
$scope.list = [1,2,3,4,5,6,7,8,9];
//Listen for 'change' event
$scope.$on('change', function(event,value){
//Set to the list value.index our value.item
$scope.list[value.index] = value.item;
});
$scope.editable = false;
$scope.edit = function(){
$scope.editable = !$scope.editable;
}
})

Angular JS directive, calling another controller

I'm trying to implement a simple <pagination> directive in Angular JS, and having trouble getting it to communicate with another controller to get it to react on changes to the current page.
Here's what my code looks like:
paginationDirective.js
app.controller('paginationCtrl', function ($scope) {
this.init = function (attrs) {
$scope.items = [
{'page': '1', class: 'current'},
{'page': '2', class: ''},
{'page': '3', class: ''},
{'page': '4', class: ''},
{'page': '5', class: ''}
];
};
});
app.directive('pagination', function () {
return {
controller: 'paginationCtrl',
restrict: 'E',
scope: {
count: '#',
page: '#'
},
templateUrl: 'views/partials/pagination.html',
link: function (scope, element, attrs, paginationCtrl) {
paginationCtrl.init(attrs);
}
}
});
pagination.html
<div class="pagination-centered">
<ul class="pagination">
<li class="arrow unavailable">«</li>
<li ng-repeat="item in items" class="{{ item.class }}">
{{ item.page }}
</li>
<li class="arrow">»</li>
</ul>
</div>
somepage.html
<div class="content">
<table>...data for page x...</table>
<pagination onpage=" <!-- Run somepageCtrl.page(x) --> "></pagination>
</div>
somepageCtrl.js
app.controller('somepageCtrl', function($scope, myService){
$scope.currentPage = 1;
$scope.init = function () {
myService.loadData($scope);
};
$scope.navigate = function(page) {
console.log('Navigating to page ' + page);
}
$scope.init();
});
I'm still new to AngularJS, so I'm not entirely sure what I should do to get the pagination directive to tell somepageCtrl to navigate to the next page.
I would like to be able to pass a function that needs to be executed when the user clicks an item in the pagination directive as an attribute on the directive.
Can anyone help and nudge me in the right direction?
I'm going to answer my own question, thanks to Esteban Felix for pointing me into the right direction.
I defined the navigate() method on somepageCtrl:
$scope.navigate = function (page) {
console.log('Lets go to page ' + page);
};
then simply added the function to
<pagination on-change="navigate"></pagination>
The method then gets assigned in the app.directive definition like so:
scope: {
count: '#',
page: '#',
onChange: '&onChange'
},
On the app.controller for paginationCtrl, I created a callback function:
$scope.callback = function (page) {
console.log('Turn to page ' + page);
$scope.onChange()(page);
}
and updated the markup for pagination.html to call the above callback with ng-click like so:
{{ item.page }}
You can use the experession callback & to call your controller back from a directive.
pagination directive
app.directive('pagination', function () {
return {
controller: 'paginationCtrl',
restrict: 'E',
scope: {
count: '#',
page: '#',
onPageChange: '&'
},
templateUrl: 'views/partials/pagination.html',
link: function (scope, element, attrs, paginationCtrl) {
paginationCtrl.init(attrs);
// grab function pointer so it will be called correctly with ng-click
scope.onPageChange = scope.onPageChange();
}
}
});
somepage.html
<div class="content">
<table>...data for page x...</table>
<pagination on-page-change="sompageCtrl.navigate"></pagination>
</div>
pagination.html
<div class="pagination-centered">
<ul class="pagination">
<li class="arrow unavailable">«</li>
<li ng-repeat="item in items" class="{{ item.class }}">
{{ item.page }}
</li>
<li class="arrow">»</li>
</ul>
</div>

Resources