ng-repeat is not working when directive is called - javascript

I'm using BootGrid Data table and JSON file as my data source for the table.
Service
.service('datatableService', ['$resource', function($resource){
this.getDatatable = function(id, email, date) {
var datatableList = $resource("data/data-table.json");
return datatableList.get ({
id: id,
email: email,
date: date
})
}
}])
Controller
.controller('datatableCtrl', function($scope, datatableService){
//Get Data Table Data
$scope.id = datatableService.id;
$scope.email = datatableService.email;
$scope.date = datatableService.date;
$scope.dtResult = datatableService.getDatatable($scope.id, $scope.email, $scope.date);
})
Directive
.directive('bootgrid', ['$timeout', function($timeout){
return {
restrict: 'A',
link: function(scope, element, attr){
$('#data-table-basic').bootgrid({
css: {
icon: 'md icon',
iconColumns: 'md-view-module',
iconDown: 'md-expand-more',
iconRefresh: 'md-refresh',
iconUp: 'md-expand-less'
}
});
}
}
}])
HTML
<div class="table-responsive" data-ng-controller="datatableCtrl">
<table id="data-table-basic" class="table table-striped" data-bootgrid>
<thead>
<tr>
<th data-column-id="id" data-type="numeric">ID</th>
<th data-column-id="sender">Sender</th>
<th data-column-id="received" data-order="desc">Received</th>
</tr>
</thead>
<tbody>
<tr data-ng-repeat="w in dtResult.list">
<td>{{ w.id }}</td>
<td>{{ w.email }}</td>
<td>{{ w.date }}</td>
</tr>
</tbody>
</table>
</div>
When I run this, I'm getting no data inside <tbody> but when I remove the directive, I can see all the JSON data are rendered inside the table. I want both ng-repeat and and directive works together. I tried to set the priority in directive as,
...
return {
restrict: 'A',
priority: 1001,
...
But no luck. http://plnkr.co/edit/rWCVXTjxOGZ49CeyIn9d?p=preview
Please help me fix this. I would appropriate if you could fix the above pen.
Regards

Priority setting will not help here because it is used to regulate order of compilation of directives defined on the same element.
You can delay bootgrid directive initialization until very next digest cycle using $timeout service. You will also need to watch for data object changes since you are loading it with AJAX.
app.directive('bootgrid', function($timeout) {
return {
link: function(scope, element, attr) {
scope.$watch(attr.bootgrid + '.length', function(newVal, oldVal) {
if (newVal !== oldVal && newVal) {
$timeout(function() {
element.bootgrid({
css: {
icon: 'md icon',
iconColumns: 'md-view-module',
iconDown: 'md-expand-more',
iconRefresh: 'md-refresh',
iconUp: 'md-expand-less'
}
});
});
}
});
}
}
});
Demo: http://plnkr.co/edit/ehbzoFGOqhQedchKv0ls?p=preview

Related

ng-repeat not populating table

I've populated a table with data from a JSON file, and then when the user clicks on an item in the table, a container and it's fields display below and it SHOULD be populated with that specific data, though it isn't.
The function select is invoked through list-patents.htm which contains the table with a list of patents. I have used the $rootScope object so the function is accessible from multiple controllers i.e. patentDetailsCtrl
Why isn't my table in patent-item.htm being populated with the ng-repeat directive?
var app = angular.module('myApp', ['ngRoute', 'angularMoment', 'ui.router', "chart.js"]);
$stateProvider
.state("patents.list.item", {
url: "/patent-item",
templateUrl: "templates/patents/list/patent-item.htm",
params: {
id: null,
appNo: null,
clientRef: null,
costToRenew: null,
renewalDueDate: null,
basketStatus: null,
costBandEnd: null,
nextStage: null
},
controller: "patentDetailsCtrl"
})
app.run(function($rootScope) {
$rootScope.select = function() {
return $rootScope.patentItem = item;
}
});
app.controller('patentDetailsCtrl', ['$scope', '$http', function($scope, $http) {
$scope.selectedPatent = $scope.patentItem;
console.log($scope.selectedPatent);
}]);
list-patents.htm
<tbody>
<tr ng-repeat="x in patents">
<td ng-click="select(x)"><a ui-sref="patents.list.item({id: x.id, appNo: x.applicationNumber, clientRef: x.clientRef, costToRenew: x.costToRenew, renewalDueDate: x.renewalDueDate, basketStatus: x.basketStatus, costBandEnd: x.costBandEnd, nextStage: x.nextStage})">{{x.applicationNumber}}</a></td>
<td ng-bind="x.clientRef"></td>
<td ng-bind="x.costToRenew">$</td>
<td ng-bind="x.renewalDueDate"></td>
<td><button type="button" class="btn btn-danger" ng-click="remove(x.id)">Remove</button></td>
</tr>
</tbody>
patent-item.htm
<table>
<tbody>
<thead>
<tr>
<td>applicationNumber</td>
<td>clientRef</td>
<td>costToRenew</td>
<td>renewalDueDate</td>
<td>basketStatus</td>
<td>costBandEnd</td>
<td>nextStage</td>
</tr>
</thead>
<tbody>
<tr ng-repeat="x in selectedPatent">
<td>{{x.applicationNumber}}</td>
<td>{{x.clientRef}}</td>
<td>{{x.costToRenew}}</td>
<td>{{x.renewalDueDate}}</td>
<td>{{x.basketStatus}}</td>
<td>{{x.costBandEnd}}</td>
<td>{{x.nextStage}}</td>
</tr>
</tbody>
</table>
</tbody>
You need to correct below points:
your $rootScope.select() method doesn't accept data (select(x)) passed from your list-patents.htm, So put item as param.,
Like : $rootScope.select = function(item) { \\ code
Sine you do ng-repeat on $rootScope.patentItem, then I guess you need to make it array, and push the passed data to it. (No need of return statement as well.)
So run block should be like :
app.run(function($rootScope) {
rootScope.patentItem = [];
$rootScope.select = function(item) {
$rootScope.patentItem.push(item);
}
});
See this Example Fiddle
To me it seems like you're trying to access the patentItem selected in your select function. If you're passing it to the $rootScope this way, you will also need to access it this way.
So change the line as follows...
$scope.selectedPatent = $rootScope.patentItem

AngularJS: ng-repeat table doesn't update from UIB Modal

I have looked at a number of other questions related to this such as
AngularJS : ng-repeat list is not updated when a model element is spliced from the model array
ng-repeat not updating on update of array
However I think that the way I built my app is sufficiently different that these aren't helping me.
I think my idea of doing this:
$rootScope.$on('connectionDispositionChanged',function(event, item){
$scope.data.matches[item.index].info.disposition = item.disposition;
});
Isn't really working out the way I had hoped. I can actually see in the console that this updating, but it doesn't update in the table. Adding $scope.$apply() after this causes a digest in-progress error.
show.phtml
<div class="container-fluid" ng-app="analysisApp" ng-controller="analysisController">
<table class="table table-condensed">
<thead>
<tr >
<th ng-repeat="header in baseColumns" class="text-center">{{header.name | tableHeader}}</th>
<th ng-repeat="header in comparisonColumns" class="text-center text-info">{{header.name | tableHeader}}</th>
<th> </th>
</tr>
</thead>
<tbody>
<tr table-row data="data" ng-repeat="item in data.matches | filter:searchMatchText track by $index">
</tbody>
</table>
<row class="col-md-12 text-center"><span class="text-muted">End of Data</span></row>
</div><!-- #matches -->
</div>
tableRowDirective.js
"use strict";
analysisApp.directive("tableRow", function($compile) {
var getTemplate = function(scope, element, attrs){
var base = scope.item.base;
var comp = scope.item.comparison;
var info = scope.item.info;
// other non-relevant code...
returnString += '<td class="text-center"><button class="btn btn-default btn-xs" ng-click="matchesSetDisposition(item, data.settings, $index)" >Set Disposition</button>';
returnString += '</td>';
return returnString;
};
var linker = function(scope, element, attrs){
element.html(getTemplate(scope, element, attrs));
$compile(element.contents())(scope);
};
return {
restrict : "A",
replace : true,
link: linker
};
});
analysisController.js
"use strict";
analysisApp.controller('analysisController', ['$scope','$rootScope','loadData','saveData','$uibModal', function ($scope, $rootScope, loadData, saveData, $uibModal, $log) {
$rootScope.$on('connectionDispositionChanged',function(event, item){
// $scope.data.matches[item.index].info.disposition = item.disposition;
});
$scope.matchesSetDisposition = function(item, scope, index){
var modalInstance = $uibModal.open({
animation: $scope.animationsEnabled,
templateUrl: '/angular/analysis/templates/matches-modal.html',
controller: 'matchesModalController',
size: 'lg',
resolve: {
itemData: function () {
return {
dispositionLabels: $scope.dispositionLabels,
disposition: item.info.disposition,
connectionID: item.info.id,
comparisonID: comparisonID,
baseItemID: item.base.id,
baseTitle: itemTitle(item.base),
comparisonItemID: item.comparison.id,
comparisonTitle: itemTitle(item.comparison),
index: index
}
}
}
});
modalInstance.result.then(function (item) {
$scope.data.matches[item.index].info.disposition = item.disposition;
saveTheData('/analysis/apisaveconnectiondisposition', item);
}, function () {
});
};
}]);
matchesModalController.js
"use strict";
analysisApp.controller('matchesModalController', function ($scope, $rootScope, $uibModalInstance, itemData, saveData) {
$scope.itemData = itemData;
$scope.ok = function (item) {
$uibModalInstance.close(item);
};
$scope.cancel = function () {
$uibModalInstance.dismiss('cancel');
};
$scope.delink = function (item) {
BootstrapDialog.confirm({
title: 'WARNING',
message: '<p>Are you sure that you want to break the link between these items?</p>',
type: BootstrapDialog.TYPE_DANGER,
btnOKLabel: 'Break the link',
btnOKClass: 'btn-danger',
callback: function(result) {
if(result) {
$uibModalInstance.dismiss('delink');
saveTheData('/analysis/apidelink', item);
}else {
// cancel the operation
}
}
});
};
var saveTheData = function(url, item){
saveData
.postData(url, item)
.then(function(dataResponse){
$rootScope.$broadcast('connectionDispositionChanged', item);
})
};
});

Change elements in a directive template based on scope data

I have a directive nested within an ng-repeat. The ng-repeat item is passed to the directive. I am attempting to generate a directive template (for testing) or templateUrl with variable elements based on a key/value in the item passed to the directive. Essentially, if item.number > 50 make the button red else make it blue.
I may be using the wrong tool to solve the problem. The goal is to use something like this to change Bootstrap tags. For instance the logic:
if item.number > 50:
class="btn btn-danger"
else:
class="btn btn-success"
If possible I'm trying to solve this with using templateUrl: as I'd like the button to launch a bootstrap modal and that's a lot to fit into the basic template option. It's much cleaner to pass the template individual scope variables.
Here is a JSFiddle that tries to describe the problem.
html
<div ng-controller="TableCtrl">
<table>
<thead>
<tr>
<th>#</th>
<th>Button</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in buttons">
<td>{{item.id}}</td>
<td new-button item='item'></td>
</tr>
</tbody>
</table>
</div>
app.js
var myApp = angular.module('myApp', []);
function TableCtrl($scope) {
$scope.buttons = {
button1: {
id: 1,
number: '10',
},
button2: {
id: 2,
munber: '85',
}
};
};
myApp.directive('newButton', function() {
return {
restrict: 'A',
replace: true,
scope: {
item: '=',
},
link: function(elem, attrs, scope) {
// This is most likely not the right location for this
/*if (item.number > 50) {
button.color = red
}, else {
button.color = blue
}; */
},
template: '<td><button type="button">{{button.color}}</button></td>'
}
});
Perhaps you can use an ng-class for this:
<button ng-class="{
'btn-danger': item.number > 50,
'btn-success': item.number <= 50
}"></button>
See https://docs.angularjs.org/api/ng/directive/ngClass
If you really need a custom directive you could try using it like this
link: function(scope,elem,attrs) {
var item=scope.item;
if (item.number > 50) {
elem.addClass("btn-danger");
} else {
elem.addClass("btn-success");
}
}
But I think that for what you're trying to achieve it's better to use the ngClass directive as follows:
<button type="button" item="item" class="btn" ng-class="item.number > 50?'btn-danger':'btn-success'"></button>
Looking at your example code, there are a few points to note:
Typo in button 2's 'munber' property.
The link function doesn't use dependency injection, so the order of the arguments does matter. Scope needs to be moved first.
Your commented out bit of code is close to working, but you need to address the variables as properties of scope - item is on scope, and the button object you are creating needs to be created on scope in order to be addressed as 'button.' from your view template.
This works (it would be better, as others have said, to use ng-class, rather than class plus moustache syntax, but I wanted to stay as close to your code sample as possible):
HTML
<div ng-controller="TableCtrl">
<table>
<thead>
<tr>
<th>#</th>
<th>Button</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in buttons">
<td>{{item.id}}</td>
<td new-button item='item'></td>
</tr>
</tbody>
</table>
</div>
JS
var myApp = angular.module('myApp', []);
function TableCtrl($scope) {
$scope.buttons = {
button1: {
id: 1,
number: '10',
},
button2: {
id: 2,
number: '85',
}
};
};
myApp.directive('newButton', function() {
return {
restrict: 'A',
replace: true,
scope: {
item: '=',
},
link: function(scope, elem, attrs) {
scope.button = {};
if (scope.item.number > 50) {
scope.button.class = 'btn btn-danger';
} else {
scope.button.class = 'btn btn-success';
};
},
template: '<td><button type="button" class="{{button.class}}">Press Me?</button></td>'
}
});
CSS
.btn-danger {
background-color: red;
}
.btn-success {
background-color: green;
}
Modified JSFiddle

AngularJS : directive does not update scope after $http response in parent scope

I got this directive:
.directive('studentTable', [function() {
return {
restrict: 'A',
replace: true,
scope: {
students: "=",
collapsedTableRows: "="
},
templateUrl: 'partials/studentTable.html',
link: function(scope, elem, attrs) {
...
}
}
}
Template:
<table class="table">
<thead>
<tr>
<th><b>Name</b></th>
<th><b>Surname</b></th>
<th><b>Group</b></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="student in students track by $index">
<td>{{ student.name }}</td>
<td>{{ student.surname }}</td>
<td>{{ student.group }}</td>
</tr>
</tbody>
</table>
Use directive in my html like this:
<div student-table students="students"
collapsedTableRows="collapsedTableRows"></div>
And the parent controller:
.controller('SchoolController', ['$scope', 'User', function($scope, User){
$scope.students = [];
$scope.collapsedTableRows = [];
$scope.search = function(value) {
if(value) {
var orgId = $state.params.id;
var search = User.searchByOrg(orgId, value);
search.success(function (data) {
$scope.students = data;
$scope.collapsedTableRows = [];
_(data).forEach(function () {
$scope.collapsedTableRows.push(true);
});
});
}
}
}])
Now at the beginnig, the table is empty, because no users in students array. After I click search, and get list of students object, I put them to scope variable, but the directive does not update, neither it find change in model (scope.$watch('students',...). What am I missing?
P.S. If I simulate the data using $httpBackend, directive works as it should.
Please make sure that data object returning array of student because somtimes you have to use data.data that simple demo should helps you:
http://plnkr.co/edit/UMHfzD4oSCv27PnD6Y6v?p=preview
$http.get('studen.json').then(function(students) {
$scope.students = students.data; //<-students.data here
},
function(msg) {
console.log(msg)
})
You should try changing the controller this way
...
$scope.$apply(function() {
$scope.students = data;
})
...
This will start a digest loop, if it's not already in progress.
Another form that will do almost the same thing is this:
...
$scope.students = data;
$scope.$digest()
...
PS:
The first method is just a wrapper that execute a $rootScope.$digest() after evaluating the function, considering that a $digest evaluates the current scope and all it's children calling it on the $rootScope is pretty heavy.
So the second method should be preferred if it works.

Angularjs dynamic directive inside ngrepeat

Look at example:
$scope.fields = [{
name: 'Email',
dir : "abc"
}, {
name: 'List',
dir : "ddd"
}];
app.directive('abc', function () {});
app.directive('ddd', function () {});
<table class="table table-hover">
<tr ng-repeat="p in fields">
<input {{p.dir}} ngmodel="p" />
</tr>
</table>
How can I write code, that p.dir will dynamically convert to a directive ?
My example: hhttp://jsbin.com/vejib/1/edit
Try this directive:
app.directive('dynamicDirective',function($compile){
return {
restrict: 'A',
replace: false,
terminal: true,
priority: 1000,
link:function(scope,element,attrs){
element.attr(scope.$eval(attrs.dynamicDirective),"");//add dynamic directive
element.removeAttr("dynamic-directive"); //remove the attribute to avoid indefinite loop
element.removeAttr("data-dynamic-directive");
$compile(element)(scope);
}
};
});
Use it:
<table class="table table-hover">
<tr ng-repeat="p in people">
<td dynamic-directive="p.dir" blah="p"></td>
</tr>
</table>
DEMO
For more information on how this directive works and why we have to add terminal:true and priority: 1000. Check out Add directives from directive in AngularJS
You could put this:
<input {{p.dir}} ngmodel="p" />
also in a directive. You could construct this HTML string in JavaScript and attach it to the DOM. And you would also need to compile the resulting element using the $compile service, so that the dynamic directives will be compiled.
Some dummy sample code (not tested, but should look something like this):
app.directive('dynamicInput', function($compile){
return {
link: function(scope, element){
var htmlString = '<input ' + scope.field.dir + ' ng-model="p"/>';
element.replaceWith(htmlString);
$compile(angular.element(element))(scope);
}
}
});
More info here.

Categories