Accessing row in ngTable after it was sorted - angularjs

In this plunk I have an ngTable with three rows. What I need to know is the row number after it was sorted. For example, sort the ngTable by name ascending. You should see in the name column 'aaa', 'bbb' and 'ccc'. Then click on the button, I'm expecting to see the first row updated, however the last row is set with the new values. This happens because the $scope.data array itself is not sorted, but ngTable sorts a copy internally. I need to update the first "visible" row, how can I achieve that?
HTML
<table ng-table="tableParams" class="table table-bordered">
<tbody>
<tr ng-repeat="u in $data">
<td title="'User ID'" sortable="'uid'" >
{{ u.uid }}
</td>
<td title="'Name'" sortable="'nm'" >
{{ u.nm }}
</td>
</tbody>
</table>
Javascript
var app = angular.module('app', ['ngTable']);
app.controller('myCtl', function($scope, NgTableParams) {
$scope.data = [{
uid: 1,
nm: 'ccc'
}, {
uid: 2,
nm: 'bbb'
}, {
uid: 3,
nm: 'aaa'
}];
$scope.tableParams = new NgTableParams({
count: 5
}, {
data: $scope.data
});
$scope.updateRow = function(row){
$scope.data[row].uid = 999;
$scope.data[row].nm = 'XXXXX';
$scope.tableParams.reload();
};
});

You have access to the data in tableParams.data. So to do what you model above, you would just need to do something like this:
$scope.updateRow = function(){
$scope.tableParams.data[0].uid = 999;
$scope.tableParams.data[0].nm = 'XXXXX';
$scope.data = $scope.tableParams.data;
};

Related

How to use select filter with static values on async (server-side) ngTable

I'm using ngTable 0.3.3 and am doing a async call to populate the table (pagination also done on server side). Here's my code:
var data = [];
$scope.tableParams = new ngTableParams({
page: 1,
count: 10
}, {
getData: function($defer, params) {
// getData gets called when you click on a different page in the pagination links.
// get the page number and size from params.$params
var pageNumber = params.$params.page;
var pageSize = params.$params.count;
// set up the query parameters as expected by your server
var mm = params.filter();
var queryParams = {"page_size":pageSize, "page_number":pageNumber, "sorting": params.sorting(), role: mm.role, id: mm.id, email: mm.email};
$log.log(mm.role);
// $log.log(mm.role, mm.email);
User.getCount().then(function (total) {
User.query(queryParams).then(function (result) {
params.total(total);
$defer.resolve(result);
});
});
}
});
with this html:
<table ng-table="tableParams" show-filter="true" class="table table-custom">
<tr ng-repeat="user in $data">
<td data-title="'Name'">
{{user.first_name}} {{user.last_name}}
</td>
<td data-title="'Role'" sortable="'role'" filter="{ 'role': 'text' }">
{{user._role}}
</td>
<td data-title="'Email'" sortable="'email'" filter="{ 'email': 'text' }">
{{user.email}}
</td>
<td data-title="'Created date'">
{{user.created_at | date: 'dd MMM yyyy'}}
</td>
<td data-title="'Edit'">
<a ui-sref="app.users.edit({userId: user.id})">Edit</a>
</td>
<td data-title="'Edit'">
<a ng-click="deleteUser(user.id)">Delete</a>
</td>
</tr>
</table>
And both filtering and pagination works as expected. You can see I have a $log call which tells me when the filter is being triggered while I type in the text field. Now I'm trying to get the role as a select rather than a text field, so I changed my code like so:
$scope.roles = function (column) {
var def = $q.defer();
var docType = [{role: 'admin'}, {role: 'customer'}, {role: 'singer'}, {role: 'voiceArtist'}];
def.resolve(docType);
return def;
};
var data = [];
$scope.tableParams = new ngTableParams({
page: 1,
count: 10
}, {
getData: function($defer, params) {
// getData gets called when you click on a different page in the pagination links.
// get the page number and size from params.$params
var pageNumber = params.$params.page;
var pageSize = params.$params.count;
// set up the query parameters as expected by your server
var mm = params.filter();
var queryParams = {"page_size":pageSize, "page_number":pageNumber, "sorting": params.sorting(), role: mm.role, id: mm.id, email: mm.email};
$log.log(mm.role);
// $log.log(mm.role, mm.email);
User.getCount().then(function (total) {
User.query(queryParams).then(function (result) {
params.total(total);
$defer.resolve(result);
});
});
}
});
And this html:
<table ng-table="tableParams" show-filter="true" class="table table-custom">
<tr ng-repeat="user in $data">
<td data-title="'Name'">
{{user.first_name}} {{user.last_name}}
</td>
<td data-title="'Role'" sortable="'role'" filter="{ 'role': 'text' }">
{{user._role}}
</td>
<td data-title="'Role1'" filter="{role: 'select'}" filter-data="roles($column)">{{user._role}}</td>
<td data-title="'Email'" sortable="'email'" filter="{ 'email': 'text' }">
{{user.email}}
</td>
<td data-title="'Created date'">
{{user.created_at | date: 'dd MMM yyyy'}}
</td>
<td data-title="'Edit'">
<a ui-sref="app.users.edit({userId: user.id})">Edit</a>
</td>
<td data-title="'Edit'">
<a ng-click="deleteUser(user.id)">Delete</a>
</td>
</tr>
</table>
The roles (that I want to populate the select from) is a static array with five elements in it. I can see the select on the Role column, but is empty (doesn't show admin, customer, etc) and it doesn't trigger the filter. When I select an item, nothing happens anywhere. Wha am I missing here?
PS: I've seen similar questions and have even tried their plunker when was available, but the questions I've found they either use async values for the select (which I don't want to) or the whole data is static, which again, I don't want to. Any help would be much appreciated.
EDIT
How I got it to work:
My js code:
$scope.roles = function (column) {
var def = $q.defer();
var docType = [{id: 'admin', title: 'Admin'}, {id: 'customer', title: 'Customer'}, {id: 'singer', title: 'Singer'}, {id: 'voiceArtist', title: 'Voice Artist'}];
def.resolve(docType);
return def;
};
var data = [];
$scope.deleteUser = function (id) {
User.get(id).then(function (result) {
$scope.tmpUser = result;
$scope.tmpUser.remove().then(function (result) {
if (result) {
$scope.showAlert('User deleted successfully.', 'success');
var n = true;
angular.forEach(data, function (v, k) {
if ($scope.tmpUser.id === v.id) {
if (n) {
data.splice(k, 1);
$scope.tableParams.reload();
n = false;
}
}
});
}
});
});
};
$scope.tableParams = new ngTableParams({
page: 1,
count: 10
}, {
getData: function($defer, params) {
// getData gets called when you click on a different page in the pagination links.
// get the page number and size from params.$params
var pageNumber = params.$params.page;
var pageSize = params.$params.count;
// set up the query parameters as expected by your server
var mm = params.filter();
var queryParams = {"page_size":pageSize, "page_number":pageNumber, "sorting": params.sorting(), role: mm.role, id: mm.id, email: mm.email};
User.getCount().then(function (total) {
User.query(queryParams).then(function (result) {
params.total(total);
$defer.resolve(result);
});
});
}
});
Its important to note that the array you pass must in the format of [{id:'someId', title: 'SomeTitle'}, {...}]
Your select is not populated because you're not waiting for the promise to resolve.
Here's a plunker that I stumbled upon when I faced this issue a few months ago:
plnkr.co/edit/XJo9rp?p=preview
Try something like that:
function getData() {
return $q.when([{role: 'admin'},...]);
}
var promise = getData();
Which is the short version for:
function getData() {
var def = $q.defer();
var docType = [{role: 'admin'},...];
def.resolve(docType);
return def;
}
var promise = getData();
and your scope function:
$scope.roles = function(column) {
var select = promise.then(function(results) {
return results;
})
return select;
};

angular smart table not working

I am using smart table http://lorenzofox3.github.io/smart-table-website/ and I am trying to follow pipe/ajax plugin example. But its not showing any data. this is what I have in my html
<div ng-controller="AboutCtrl">
<table class="table" st-pipe="mc.callServer" st-table="mc.displayed">
<thead>
<tr>
<th st-sort="id">id</th>
<th st-sort="name">name</th>
<th st-sort="age">age</th>
<th st-sort="saved">saved people</th>
</tr>
<tr>
<th><input st-search="id"/></th>
<th><input st-search="name"/></th>
<th><input st-search="age"/></th>
<th><input st-search="saved"/></th>
</tr>
</thead>
<tbody ng-show="!mc.isLoading">
<tr ng-repeat="row in mc.displayed">
<td>{{row.id}}</td>
<td>{{row.name}}</td>
<td>{{row.age}}</td>
<td>{{row.saved}}</td>
</tr>
</tbody>
<tbody ng-show="mc.isLoading">
<tr>
<td colspan="4" class="text-center">Loading ... </td>
</tr>
</tbody>
<tfoot>
<tr>
<td class="text-center" st-pagination="" st-items-by-page="10" colspan="4">
</td>
</tr>
</tfoot>
</table>
</div>
and here is my js
angular.module('eventsApp')
.controller('AboutCtrl', ['Resource', function (service) {
var ctrl = this;
this.displayed = [];
this.callServer = function callServer(tableState) {
ctrl.isLoading = true;
var pagination = tableState.pagination;
var start = pagination.start || 0; // This is NOT the page number, but the index of item in the list that you want to use to display the table.
var number = pagination.number || 10; // Number of entries showed per page.
service.getPage(start, number, tableState).then(function (result) {
ctrl.displayed = result.data;
tableState.pagination.numberOfPages = result.numberOfPages;//set the number of pages so the pagination can update
ctrl.isLoading = false;
});
};
}]).factory('Resource', ['$q', '$filter', '$timeout', function ($q, $filter, $timeout) {
//this would be the service to call your server, a standard bridge between your model an $http
// the database (normally on your server)
var randomsItems = [];
function createRandomItem(id) {
var heroes = ['Batman', 'Superman', 'Robin', 'Thor', 'Hulk', 'Niki Larson', 'Stark', 'Bob Leponge'];
return {
id: id,
name: heroes[Math.floor(Math.random() * 7)],
age: Math.floor(Math.random() * 1000),
saved: Math.floor(Math.random() * 10000)
};
}
for (var i = 0; i < 1000; i++) {
randomsItems.push(createRandomItem(i));
}
//fake call to the server, normally this service would serialize table state to send it to the server (with query parameters for example) and parse the response
//in our case, it actually performs the logic which would happened in the server
function getPage(start, number, params) {
var deferred = $q.defer();
var filtered = params.search.predicateObject ? $filter('filter')(randomsItems, params.search.predicateObject) : randomsItems;
if (params.sort.predicate) {
filtered = $filter('orderBy')(filtered, params.sort.predicate, params.sort.reverse);
}
var result = filtered.slice(start, start + number);
$timeout(function () {
//note, the server passes the information about the data set size
deferred.resolve({
data: result,
numberOfPages: Math.ceil(filtered.length / number)
});
}, 1500);
return deferred.promise;
}
return {
getPage: getPage
};
}]);
and this is what my browser show
Try change this
<div ng-controller="AboutCtrl">
to
<div ng-controller="AboutCtrl as mc">
</script>-->
</script>
</script>
</script>-->
<script>
angular.module('eventsApp', ['smart-table', 'ngSanitize', 'ngRoute'])
.controller('AboutCtrl', ["$scope", "Resource", function ($scope, Resource) {
var ctrl = this;
this.displayed = Resource.GetData();
this.callServer = function callServer(tableState) {
ctrl.isLoading = true;
var pagination = tableState.pagination;
var start = pagination.start || 0; // This is NOT the page number, but the index of item in the list that you want to use to display the table.
var number = pagination.number || 10; // Number of entries showed per page.
Resource.getPage(start, number, tableState).then(function (result) {
ctrl.displayed = result.data;
tableState.pagination.numberOfPages = result.numberOfPages; //set the number of pages so the pagination can update
ctrl.isLoading = false;
});
};
} ]).factory('Resource', ['$q', '$filter', '$timeout', function ($q, $filter, $timeout) {
//this would be the service to call your server, a standard bridge between your model an $http
// the database (normally on your server)
var randomsItems = [];
function GetData() {
function createRandomItem(id) {
var heroes = ['Batman', 'Superman', 'Robin', 'Thor', 'Hulk', 'Niki Larson', 'Stark', 'Bob Leponge'];
return {
id: id,
name: heroes[Math.floor(Math.random() * 7)],
age: Math.floor(Math.random() * 1000),
saved: Math.floor(Math.random() * 10000)
};
}
for (var i = 0; i < 1000; i++) {
randomsItems.push(createRandomItem(i));
}
return randomsItems;
}
//fake call to the server, normally this service would serialize table state to send it to the server (with query parameters for example) and parse the response
//in our case, it actually performs the logic which would happened in the server
function getPage(start, number, params) {
var deferred = $q.defer();
var filtered = params.search.predicateObject ? $filter('filter')(randomsItems, params.search.predicateObject) : randomsItems;
if (params.sort.predicate) {
filtered = $filter('orderBy')(filtered, params.sort.predicate, params.sort.reverse);
}
var result = filtered.slice(start, start + number);
$timeout(function () {
//note, the server passes the information about the data set size
deferred.resolve({
data: result,
numberOfPages: Math.ceil(filtered.length / number)
});
}, 1500);
return deferred.promise;
}
return {
getPage: getPage,
GetData: GetData
};
} ]);
</script>
</head>
<body ng-app="eventsApp">
<div ng-controller="AboutCtrl as mc">
<table class="table" st-pipe="mc.callServer" st-table="mc.displayed">
<thead>
<tr>
<th st-sort="id">id</th>
<th st-sort="name">name</th>
<th st-sort="age">age</th>
<th st-sort="saved">saved people</th>
</tr>
<tr>
<th><input st-search="id"/></th>
<th><input st-search="name"/></th>
<th><input st-search="age"/></th>
<th><input st-search="saved"/></th>
</tr>
</thead>
<tbody ng-show="!isLoading">
<tr ng-repeat="row in mc.displayed">
<td>{{row.id}}</td>
<td>{{row.name}}</td>
<td>{{row.age}}</td>
<td>{{row.saved}}</td>
</tr>
</tbody>
<tbody ng-show="mc.isLoading">
<tr>
<td colspan="4" class="text-center">Loading ... </td>
</tr>
</tbody>
<tfoot>
<tr>
<td class="text-center" st-pagination="" st-items-by-page="10" colspan="4">
</td>
</tr>
</tfoot>
</table>
</div>
</body>
</html>

Attach a jQuery Extension containing Angular

I have a grid element that generates the following HTML:
<div id="grid">
<table class="grid test" ng-controller="gridController" width="900">
<thead>
<tr><td class="th-break" colspan="8"> </td></tr>
<tr>
<th>Id</th>
<th>Title</th>
<th>Potential Customer</th>
<th>Est. Close Date</th>
<th>Est. Revenue</th>
<th>Probability</th>
<th>Rating</th>
<th></th>
</tr>
<tr><td class="th-break" colspan="8"> </td></tr>
</thead>
<tbody ui-sortable="sortableOptions" ng-model="opps">
<tr ng-repeat="obj in opps|orderBy:orderByField:reverseSort" style="cursor:move;">
<td>{{obj.id}}</td>
<td>{{obj.title}}</td>
<td>{{obj.customer}}</td>
<td>{{obj.closeDate}}</td>
<td>{{obj.revenue}}</td>
<td>{{obj.probability}}</td>
<td>{{obj.rating}}</td>
<td><i class="fa fa-times" ng-click="delete(obj)"></i></td>
</tr>
</tbody>
</table>
</div>
I created angular code that bound data to the grid and it worked perfectly. However, it was all manual binding and I'm tidying up my code. I want to create a jQuery extension so that I can pass in a ton of options and bind the grid using:
$('div#grid').bindGrid();
Here's my jQuery:
(function ($) {
'use strict';
$.fn.bindGrid = function (options) {
var settings = $.extend({
}, options);
var grid = this;
var gridMod = angular.module('grid', ['ui.sortable']);
gridMod.controller('gridController', function ($scope) {
$scope.orderBy = 'title';
$scope.reverseSort = false;
var list = [
{ 'id': 1, 'title': 'Interested in Product Designer', 'customer': '', 'closeDate': '4/2/2013', 'revenue': '$349,383.00', 'probability': '70', 'rating': 'Hot' },
{ 'id': 2, 'title': 'Interested in Product Designer', 'customer': 'Bold Sales Accessories', 'closeDate': '6/11/2013', 'revenue': '$234,382.00', 'probability': '20', 'rating': 'Cold' },
{ 'id': 3, 'title': 'Interested in Product Designer', 'customer': 'Coho Winery', 'closeDate': '6/18/2013', 'revenue': '$182,796.00', 'probability': '50', 'rating': 'Warm' },
{ 'id': 4, 'title': 'Interested in Plotters', 'customer': 'Daring Farm', 'closeDate': '7/28/2013', 'revenue': '$685,780.00', 'probability': '50', 'rating': 'Warm' }
];
$scope.opps = list;
$scope.delete = function (item) {
var index = $scope.opps.indexOf(item);
$scope.opps.splice(index, 1);
}
$scope.sortableOptions = {
stop: function (e, ui) {
var newOrder = list.map(function (i) {
return i.id;
}).join(', ');
console.log(newOrder);
}
}
});
angular.element(document).ready(function () {
angular.bootstrap(grid, ['grid']);
});
};
}(jQuery));
When I run this, I get "'gridController' not a function, got undefined." I'm sure it has something to do with scope, but not sure what's going on. Any suggestions?
And I don't want to use a directive as I need this to be highly configurable via options passed in through jQuery.
Thanks.
Nevermind, I had a typo. grrhhh, too many late nights. I've edited the code above.

filter an item within ng-repeat angularjs

I want to create a filter to retrieve only the last digits from a URL, for example team._links.team.href gives back a URL such as http://api.football-data.org/alpha/teams/61 but I want only want it to give back 61. How could I do this?
HTML:
<tr ng-repeat="team in teamsList">
<td>{{$index + 1}}</td>
<td><a href="#/teams/{{team._links.team.href }}">
{{team.teamName}}
</a></td>
<td>{{team.playedGames}}</td>
<td>{{team.points}}</td>
<td>{{team.goals}}</td>
<td>{{team.goalsAgainst}}</td>
<td>{{team.goalDifference}}</td>
</tr>
CONTROLLER.js
angular.module('PremierLeagueApp.controllers', []).
controller('teamsController', function($scope, footballdataAPIservice) {
$scope.teamsList = [];
footballdataAPIservice.getTeams().success(function (response) {
//Dig into the response to get the relevant data
$scope.teamsList = response.standing;
});
});
You can add a simple function on the controller like this:
$scope.teamID = function(url) {
return url.substr(url.lastIndexOf('/') + 1);
};
and then use it in your repeat:
<a href="#/teams/{{teamID(team._links.team.href)}}">
Here's a fiddle.
One way to go is to define your own filter for this. For example like this:
.filter('idOnly', function() {
return function(input) {
return input.substring(input.lastIndexOf('/')+1)
};
})
which then can be used like this:
<td>
<a href="#/teams/{{team._links.team.href | idOnly }}">
{{team.teamName}}
</a>
</td>
A full code snippet is added below.
angular.module('PremierLeagueApp', [])
.filter('idOnly', function() {
return function(input) {
return input.substring(input.lastIndexOf('/')+1)
};
})
.controller('teamsController', function($scope) {
$scope.teamsList = [ { teamName : 'team1',
playedGames : 1,
points: 0,
goals: 1,
goalsAgainst: 4,
goalsDifference: 3,
_links : {
team : { href : "http://api.football-data.org/alpha/teams/61"}
}
},
{ teamName : 'team2',
playedGames : 1,
points: 3,
goals: 4,
goalsAgainst: 1,
goalsDifference: 3,
_links : {
team : {href : "http://api.football-data.org/alpha/teams/62"}
}
}
];
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="PremierLeagueApp">
<div ng-controller="teamsController">{{aap}}
<table>
<tr ng-repeat="team in teamsList">
<td>{{$index + 1}}</td>
<td><a href="#/teams/{{team._links.team.href | idOnly }}">
{{team.teamName}}
</a></td>
<td>{{team.playedGames}}</td>
<td>{{team.points}}</td>
<td>{{team.goals}}</td>
<td>{{team.goalsAgainst}}</td>
<td>{{team.goalDifference}}</td>
</tr>
</table>
</div>
</body>

What should I use in angularJS to output a resource's name when I have its ID?

I have a controller with $scope.users = [{id: 1, name: "Test"}, {id: 2, name: "Dude"}];
I would like to do something like {{id | getName}} and it would take the id I have, crawl $scope.users to look for the corresponding ID and output "Test" or "Dude" depending on the value of id.
What should I use in angularJS to achieve this ?
<table class="table table-striped">
<thead>
<tr>
<th>Winner</th>
<th>Loser</th>
<th>Date</th>
</tr>
</thead>
<tbody ng-repeat="match in matchs">
<tr>
<!-- What I want is instead of winner_id / loser_id go and get from $scope.users the id and output user name instead -->
<td>{{match.winner_id}} - {{match.winner_score}}</td>
<td>{{match.loser_id}} - {{match.loser_score}}</td>
<td>{{match.created_at}}</td>
</tr>
</tbody>
</table>
http://jsfiddle.net/wHVG6/
Without writing your own, use the built-in filter:
{{(users | filter:{id: 1})[0].name}}
Your own filter could look something like:
app.filter('getName', ['filterFilter', function (filterFilter) {
return function (input, id) {
var r = filterFilter(input, {id: id})[0];
return r && r.name;
};
}]);
with:
{{users | getName:2}}
Though, mind that the input is users, not the id. This is needed as a filter has no direct scope access, so you'll have to provide every data needed.
Alternatively, you could wrap it in a simple getter function:
$scope.getName = function getName(id) {
return getNameFilter($scope.users, id);
};
with:
{{getName(3)}}
demo: http://jsbin.com/exezik/4/
full code:
(function (app, ng) {
app.controller('TestCtrl', ['$scope', 'getNameFilter', function ($scope, getNameFilter) {
$scope.users = [{id: 1, name: "Test"}, {id: 2, name: "Dude"}, {id: 3, name: "Foobar"}];
$scope.getName = function getName(id) {
return getNameFilter($scope.users, id);
};
}]);
app.filter('getName', ['filterFilter', function (filterFilter) {
return function (input, id) {
var r = filterFilter(input, { id: id})[0];
return r && r.name;
};
}]);
}(angular.module('Test', []), angular));

Resources