Custom directive inside ng-repeat - javascript

I have this custom directive.
I call my directive like bellow, inside a ng-repeat.
selectedMealCalc.calculated_foods as 'items', is an array of objects
<!-- DIRECTIVE -->
<div ng-repeat="option in [0,1,2,3,4]">
<meal-option option="{{option}}"
items="selectedMealCalc.calculated_foods"
selectedmealcalc="selectedMealCalc"></meal-option> </div>
<!-- DIRECTIVE -->
Then I created this directive in angularjs.
'use strict';
angular.module('nutriApp').directive('mealOption', ['$compile', function($compile) {
return {
restrict: 'E',
templateUrl: 'views/checkins/meal-options.html',
scope: {
option: "#",
items: "=",
selectedmealcalc: "="
},
controller: ['$scope', 'Food', function($scope, Food) {
$scope.sumFood = {};
$scope.summerizeOption = function(foods) {
if(foods.length > 0){
$scope.sumFood = Food.summerize(foods);
}
return $scope.sumFood;
};
}]
};
}]);
And this HTML directive.
<div class="row" ng-init="filteredItems = ( items | filter: { food_option: option } )" ng-controller="CheckinsPlansCtrl">
<div class="col-md-12" ng-show="filteredItems.length > 0">
Opção {{ option }}
<table class="table table-calculo table-striped">
<thead>
<tr>
<th>Alimento</th>
<th>Quantidade</th>
<th></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="foodCalculation in filteredItems track by $index">
<td>{{foodCalculation.food.name}}</td>
<td>{{foodCalculation.gram_amount}} g</td>
</tr>
</tbody>
</table>
</div>
</div>
When I update the selectedMealCalc.calculated_foods the custom directive is not updating.
I have to close the modal and open again in my page to saw the new line.

As this comment Custom directive inside ng-repeat in this post.
I removed the ng-init because ng-init: "Only to initialize a property on the scope. I would recommend not to use it." as this another answer Does ng-init watch over change on instantiated property like ng-model does?

Related

Angular call controller method from directive template

I create simple project with angular and I use directive to create a simple grid like this code :
my directive :
app.directive('dpGrid',()=>{
return{
restrict:"E",
scope:{
items:"="
}
templateUrl: 'template/dbgrid.directive.html'
}
});
my controller :
app.controller('mainCtrl',($scope)=>{
$scope.data=[{fname:"david",lname:"orman",age:24,id:"234"}];
$scope.update=(id)=>{
console.log("ID :",id);
};
});
my directive template :
<table class="table">
<thead class="thead-inverse">
<tr>
<th>#</th>
<th>fname</th>
<th>lname</th>
<th>age</th>
<th>opt</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in items" >
<th>{{$index}}</th>
<td>{{item.fname}}</td>
<td>{{item.lname}}</td>
<td>{{item.age}}</td>
<td><a class="btn btn-danger" ng-click="update(item.id)">update</a></td>
</tr>
</tbody>
</table>
and I use directive like this :
<dp-grid items="data" ></dp-grid>
I want call update() method from directive template, but dont call update() method when click on update btn
You simply need to specify the controller your directive should use, then access it in template.
return {
restrict:"E",
scope:{
items:"="
},
controller: 'mainCtrl',
templateUrl: 'template/dbgrid.directive.html'
}
Then you will be able to access the function in the templates. If you are accessing it from a child isolate scope, you may need to access it using $parent.

How to include <tr> element in angular directive (transclude)?

I want to include <tr> and <td> and apparently I can't do that with directive. It keeps ignoring <td> or <td> as if they don't exists. here's what I trying to accomplish:
<my-table>
<tr>
<td>hello</td>
<td>world</td>
</tr>
</my-table>
Here's the javascript:
angular.module('app', [])
.controller('Controller', ['$scope', function($scope) {
}])
.directive('myTable', function() {
return {
restrict: 'E',
transclude: true,
templateUrl: 'my-table.html'
};
});
my-table.html :
<table ng-transclude>
</table>
the code above resulted in:
<my-table class="ng-isolate-scope"><table ng-transclude="">
hello <-- no <tr> nor <td> here just plain text
world
</table></my-table>
example : PLUNKR
It's not a transclude problem. It's a problem with invalid html, because <tr> without a table is invalid. So angular gets from a browser text, not DOM elements. So you need to have <table> tag inside an html:
<my-table>
<table>
<tr>
<td>hello</td>
<td>world</td>
</tr>
</table>
</my-table>
Then you'll be able to get access to tbody element created by a browser along with tr's in link function and process it:
link: function(scope,element,attrs,ctrls,transclude) {
var html = transclude();
element.find('table').append(html[1].firstElementChild);
}
or use ng-transclude in your template as you did. However, I may presume that you'll want to reuse the transcluded part later, so accessing it in link function makes more sense to me.
Adding into my comment earlier, you can achieve somewhat similar like this this if you wan to use ng-trasclude
angular.module('app', [])
.controller('Controller', ['$scope', function($scope) {
}])
.directive('myTable', function() {
return {
restrict: 'E',
transclude: true,
scope: {
'close': '&onClose'
},
templateUrl: 'my-dialog-close.html'
};
});
template
index.html
<my-table>
<table>
<tr>
<td>hello</td>
<td>world</td>
</tr>
</table>
</my-table>
Plunker : https://plnkr.co/edit/u85h0sJL50k2gESfI6RT?p=preview

accessing other scope variables inside custom template

I'm trying to display files inside each folder/directory.
I'm using custom directive to disply each of the directories as follows (this part works).
But it fails to resolve the {{file}} variable inside custom template folderListing.html. Can somebody please correct me where I'm going wrong ?
folderListing.js
app.directive('folderListing', function(){
return {
restrict: 'E',
scope: {
listing:'='
},
templateUrl: 'js/directives/folderListing.html'
};
});
RdaController.js
app.controller('RdaController', ['$scope','RdaService', function($scope,RdaService) {
$scope.folders = ['RDA Server Folder','CTP Outbox', 'CTP Inbox', 'CTP Jobs'];
$scope.sendToCTP = function(file){
return RdaService.SendFileToCTP(file);
};
$scope.listOfFiles = ['learn_javascript.pdf', 'HTML Made Easy.pdf', 'AngularJS for everybody.pdf'];
}]);
index.html
<folder-listing listing="folder" ng-repeat="folder in folders"></folder-listing>
folderListing.html
<div class="row">
<div class="col-md-3" id="{{listing}}">
<table class="table table-striped">
<h3> {{ listing }} </h3>
<div class="row">
<div class="col-md-3" ng-repeat="file in listOfFiles">
{{file}}
</div>
</div>
<td><span class="btn-group" role="group"><button type="button" class="btn btn-default" ng-click="sendToCTP(file)">Execute</button></span></td>
</table>
</div>
</div>
With this:
scope: {
listing:'='
},
You have created an isolate scope passing only listing to the directive. You need to change this to:
scope: {
listing: '=',
listOfFiles: '=',
sendToCTP: '&sendToCtp'
},
To pass the function you'll have to add a send-to-ctp="sendToCTP(file)" attribute on your directive. However, in your template your button with ng-click="sendToCTP(file)" is outside your ng-repeat so file will always be undefined.

How to pass parameters in nested controllers in Angular JS

I have a situation where I get some data from a REST API in a controller, I render that data using ng-repeat. Then in that loop, I need to run another controller, pass it data from earlier controller, do some operations on it and then again run an ng-repeat on it.
When I do it, "Inspect Element" shows value kept in parent controller's parameter. But the value which is being passed to the nested controller is actually the variable name.
Source code:
HTML:
<div class="checkbox" ng-repeat="bird in birds">
<table>
<tr>
<td>
<a ng-href="/birds/{{bird.Image}}" rel="shadowbox"><img ng-src="/birds/{{bird.Image}}" height="200" width="200"></img></a>
<div ng-controller="imageController" model="{{ bird.AdditionalImages }}">More Images: {{ imageString }}
<div ng-repeat="image in images">
<a ng-href="/birds/{{image}}" rel="shadowbox[{{ bird.Image }}]">a</a>
</div>
</div>
</td>
<td>
<table>
<tr>
<td>
<b>{{ bird.CommonName }}</b>
</td>
</tr>
<tr>
<td>
Spotted at: {{ bird.SpottedAt }}
</td>
</tr>
</table>
</td>
</tr>
</table>
</div>
JavaScript (for nested controller):
anekchidiya.controller('imageController', function($scope, $attrs) {
$scope.imageString = $attrs.model;
console.log("images: " + $scope.imageString);
});
You can perform it by passing your scope into a directive, and you will create an isolated scope.
For example :
Controller
(function(){
function Controller($scope) {
$scope.data = [{
name: 'john',
age: '26'
}, {
name: 'paul',
age: '24'
}, {
name: 'titi',
age: '32'
}];
}
angular
.module('app', [])
.controller('ctrl', Controller);
})();
Directive
(function(){
function customDirective() {
return{
restrict: 'AE',
template: '<h3>Age : {{age}}</h3>',
scope: {
age: '='
}
};
}
angular
.module('app')
.directive('customDirective', customDirective);
})();
And you can call your directive into the ngRepeat for example, by passing some data :
HTML
<body ng-app="app" ng-controller="ctrl">
<div ng-repeat="item in data">
<h2>Name : {{item.name}}</h2>
<custom-directive age="item.age"></custom-directive>
</div>
</body>
So, typical usage of an isolated scope , it is in a directive that creates a complete component, a widget, etc ...
So, you will be able to build some custom components, and to pass specific data.

Angular ng-click load controller

I'm trying to practice angular and I'm stuck this this.
How do I make ng-click load the displayController? Or am I doing this wrong way?
The Angular
var bible = angular.module('bible', []);
// Load the Books
bible.controller('listController', ['$scope', '$http', function($scope, $http) {
$http.get('books.json').success(function(data) {
$scope.books = data
});
}]);
bible.controller('displayController', function($scope) {
$scope.test = "TEST TEXT";
});
The HTML
<div class="row">
<div class="col-md-12" ng-controller="listController">
<table class="table">
<thead>
<th>Testament</th>
<th>Title</th>
<th>Chapters</th>
</thead>
<tbody>
<tr ng-repeat="book in books">
<td>{{book.testament}}</td>
<td>{{book.title}}</td>
<td>{{book.chapters}}</td>
</tr>
</tbody>
</table>
<div class="display-book" ng-controller="displayController">
{{test}}
</div>
</div>
</div>
You don't need the additional controller there. I guess you want to display additional infos about the clicked book.
Use a reference and ngIf
var bible = angular.module('bible', []);
// Load the Books
bible.controller('listController', ['$scope', '$http', function($scope, $http) {
$scope.selectedBook = null;
$http.get('books.json').success(function(data) {
$scope.books = data
});
}]);
And html:
<div class="row">
<div class="col-md-12" ng-controller="listController">
<!-- will be shown, as long as not book is selected -->
<table data-ng-if="selectedBook == null" class="table">
<thead>
<th>Testament</th>
<th>Title</th>
<th>Chapters</th>
</thead>
<tbody>
<tr ng-repeat="book in books">
<td>{{book.testament}}</td>
<td>{{book.title}}</td>
<td>{{book.chapters}}</td>
</tr>
</tbody>
</table>
<!-- will be shown, when a book got selected -->
<div data-ng-if="selectedBook != null" class="display-book">
<!-- display the selection table again -->
<button data-ng-click="selectedBook = null">Back</button>
{{selectedBook.title}}
</div>
</div>
</div>
Why do you want to call a separate controller cant you implement the functionality in a separate function like below ,
bible.controller('listController', ['$scope', '$http', function($scope, $http) {
$http.get('books.json').success(function(data) {
$scope.books = data
});
$scope.display = function(){
// **YOUR CODE COMES HERE**
}
}]);
<td>{{book.title}}</td>

Categories

Resources