ng-repeat sortBy not reordering after push - angularjs

As the title says, I can´t achieve the desired behavior when using orderBy with ng-repeat.
That´s the list I want ordered:
<div class = 'row' ng-repeat = 'tabla_salarial in master.tablas_salariales | orderBy:-fecha'>
<div class = 'col'><p><label>{{tabla_salarial.fecha | date:dd-MM-yyyy }}</label></p></div>
<div class = 'col'><p><span>{{tabla_salarial.salario_bruto | number : 2 }}€</span></p></div>
<div class = 'col'><p><span>{{tabla_salarial.finiquito_vacaciones | number : 2 }}€</span></p></div>
<div class = 'col'><p><span>{{tabla_salarial.finiquito_indemnizacion | number : 2 }}€</span></p></div>
<div class = 'col'><p><span>{{tabla_salarial.precio_sustitucion | number : 2 }}€</span></p></div>
</div>
And this is the piece of code inside the controller that is supposed to handle the list insertions:
$http.post("controlador/TablaSalarial/insert", $scope.tabla_salarial).then(function(response){
if ( response.data.errno ){
Dialog.toast(response.data.err);
}
else{
RowService.add(response.data.row, "tablas_salariales");
$scope._dialog.hide();
$scope.t = {};
$scope.master.tablas_salariales.push(response.data.row);
}
}, function(response){/*...*/});
The table is correctly ordered at start, but every time a push a new record it is appended at the end of the table, although all the other items keep the order.
I know how to make a different approach, or use a new directive, and that´s not what I want.
Is there any way to make it work as expected? (And by expected I mean, keep the list ordered when pushing a new record)
Thanks in advance.

Well, there are some mistakes in your code:
You should use single quotes in your orderBy filter:
<div class="row" ng-repeat="tabla_salarial in master.tablas_salariales | orderBy: '-fecha'">
The date filter should also have single quotes:
<div class="col"><p><label>{{tabla_salarial.fecha | date:'dd-MM-yyyy' }}</label></p></div>
Working demo:
angular.module('app', [])
.controller('mainCtrl', function($scope) {
$scope.products = [];
var start = new Date(2012, 0, 1);
$scope.add = function(times) {
times = times || 1;
for (var i = 1; i <= times; i++) {
$scope.products.push({
"id": i,
"expiresOn": new Date(start.getTime() + Math.random() * (new Date().getTime() - start.getTime())),
"price": parseFloat(Math.min(100 + (Math.random() * (999 - 100)),999))
});
}
}
$scope.add(5);
});
<html ng-app="app">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.7/angular.min.js"></script>
</head>
<body ng-controller="mainCtrl">
<table>
<caption>Products table</caption>
<thead>
<tr>
<th>Id</th>
<th>Expires On</th>
<th>Price</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="product in products | orderBy: '-date'">
<td ng-bind="product.id"></td>
<td ng-bind="product.expiresOn | date: 'dd-MM-yyyy'"></td>
<td ng-bind="product.price | currency: '€': 2"></td>
</tr>
</tbody>
</table>
<hr>
<button type="button" value="add" ng-click="add()">Add new product</button>
</body>
</html>
Note: Instead of use the number filter and inserting the symbol by hand, as you're doing here for example:
<div class = 'col'><p><span>{{tabla_salarial.salario_bruto | number : 2 }}€</span></p></div>
You can use currency filter as below:
<div class = 'col'><p><span>{{tabla_salarial.salario_bruto | currency: '€': 2" }}</span></p></div>
I hope it helps!

Related

How to get ng-repeat checkbox values by row and column from a table

I have a table comprised mainly of checkboxes. This is a simplistic version of how it looks.
Name | A | B | C | D | E |
--------|----|----|----|----|----|
James | [] | [X]| [] | [] | [] |
--------|----|----|----|----|----|
Micheal | [x]| [] | [] | [] | [] |
--------|----|----|----|----|----|
Peter | [] | [] | [] | [x]| [] |
--------|----|----|----|----|----|
Eddy | [] | [] | [] | [] | [] |
--------|----|----|----|----|----|
From the table above, there's a names column and A to E columns with checkboxes.
My question is how I can get the the values of the checked checkboxes with the associated name_id of that row. Each column has a distinct id as well. So for instance an object of the above sample could be:
{
table_id:1
checked_boxes: [
{name_id:10, col_id:2},
{name_id:20, col_id:1},
{name_id:30, col_id:4}
]
}
This is my tbody with the ng-repeat:
<tbody>
<tr ng-repeat="item in names track by $index">
<td id="{{item.name_id}}">{{item.name}}</td>
<td ng-repeat="col in cols">
<input type="checkbox" value="" class="form-control" ng-model="selected[col.col_id]" />
</td>
</tr>
</tbody>
Do I need to make any adjustments in the tbody? And how can I get the desired object shown above specifically the checked_boxes? I've tried various means of pushing the values into a checked_boxes empty array to no success.
Your checkboxes array is hard to work with. So it's just takes a bit of rearrangement. Start with a 2D array of checkboxes and populate it all with false values. Then run through known checkboxes and find their index for rows and columns, then simply show it with ng-model="checkboxes[$parent.$index][$index]".
Here is my version as a demo:
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
var n = $scope.names = [
{name_id:10, name:"Micheal"},
{name_id:20, name:"James"},
{name_id:30, name:"Peter"},
{name_id:40, name:"Eddy"}
];
var c = $scope.cols = [
{col_id:1},
{col_id:2},
{col_id:3},
{col_id:4},
{col_id:5}
];
var checked_boxes = [
{name_id:10, col_id:2},
{name_id:20, col_id:1},
{name_id:30, col_id:4}
];
$scope.checkboxes = n.map( function(x) {
return c.map( function(y) {
return false;
});
})
for(var i=0; i<checked_boxes.length; i++){
var ni = n.map(function(_) { return _.name_id; }).indexOf(checked_boxes[i].name_id); // match the index of `name_id`
var ci = checked_boxes[i].col_id-1; // or like above -> c.map(...).indexOf(...) // but for `col_id`
$scope.checkboxes[ni][ci] = true;
}
});
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>
<body>
<div ng-app="myApp" ng-controller="myCtrl">
<table>
<tr ng-repeat="item in names track by $index">
<td>{{item.name}}</td>
<td ng-repeat="col in cols">
<input type="checkbox" value="" class="form-control" ng-model="checkboxes[$parent.$index][$index]" />
</td>
</tr>
</table>
</div>
</body>
</html>
I've made a little example for you so you can have this simpler. My suggestion is as follows:
Instead of use ng-model on your checkboxes you can change to ng-click to set the value on your data array, making it easier if you want to gather this information later.
<div ng-app="questions">
<div ng-controller="QuestionController as ctrl">
<table>
<thead>
<tr>
<th>Name</th>
<th>A</th>
<th>B</th>
<th>C</th>
<th>D</th>
<th>E</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in ctrl.questions track by $index">
<td id="{{item.name_id}}">{{item.name}}</td>
<td ng-repeat="col in ctrl.cols">
<input type="checkbox" class="form-control" ng-click="ctrl.selectOption(item, col)"/>
</td>
</tr>
</tbody>
</table>
<button ng-click="ctrl.load()">
Load button data
</button>
</div>
</div>
And in your javascript you will have the following:
angular.module('questions', []);
angular.module('questions').controller('QuestionController', QuestionController);
function QuestionController () {
var ctrl = this;
ctrl.questions = [{
name: 'Name 1',
name_id: 'Name_1',
selectedOption: ''
},{
name: 'Name 2',
name_id: 'Name_2',
selectedOption: ''
},{
name: 'Name 3',
name_id: 'Name_3',
selectedOption: ''
}];
ctrl.cols = ['A', 'B', 'C', 'D', 'E'];
ctrl.selectOption = function (entry, selectedOption) {
entry.selectedOption = selectedOption;
};
ctrl.load = function () {
angular.forEach(ctrl.questions, function (question) {
console.log('Name: ' + question.name + ' - Answer: ' + question.selectedOption);
});
}
}

Date from Json list is not getting displayed properly in angular js

The date in angular js is getting displayed like /Date(1494288000000)/ which is from asp.net list of objects.
<tr class="unread" data-ng-repeat="notification in Notifications | filter:q | startFrom:currentPage*pageSize | limitTo:pageSize">
<td class="inbox-small-cells">
<input data-ng-model="arr[notification.NotificationId]" type="checkbox" value="{{notification.NotificationId}}" ng-checked="" ng-click="" class="mail-checkbox">
</td>
<td class="view-message dont-show">{{notification.Title}}</td>
<td class="view-message ">{{notification.Message}}</td>
<td class="view-message text-right">{{notification.Date | date}}</td>
</tr>
Use new Date.. Angular date filter have included new Date() conversion. We don't need to write extra code.
angular.module('exApp',[])
.controller('mainController', function ($scope) {
$scope.current = new Date();
var d = 1494288000000; // number
console.log(typeof d);
$scope.ex ="1494288000000"; // string
console.log(typeof $scope.ex);
$scope.me = Date($scope.ex);
$scope.newDate = new Date(d);
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<div ng-app="exApp">
<div ng-controller="mainController">
{{ current | date: 'd-MM-y'}} <br>
Converted : {{newDate|date}} <br>Without Convert : {{ex|date}}<br>Extra convert: {{me|date}}
</div>
</div>
var dateToDisplay = new Date(parseInt(jsonDate.substr(6)));
return dateToDisplay;
This is to be included in the custom filter function.
{{dateToDisplay | customFilterName | date}}
This is to display date in view page. First filter through custom filter and then again filter through date filter. Source How do I format a Microsoft JSON date?

How to show only one row in angular datatable?

I am new to angularjs, i have 25 rows to show, but for first time loading i am trying to show only one row, there will be one expand button to show remaining rows, then on click of expand i want to show all the rows.
Here is the code.
<table>
<tbody>
<tr ng-repeat="x in names">
<td>{{x}}</td>
</tr>
</tbody>
</table>
You can use:
<div ng-repeat="x in names | limitTo: limit">
<p>{{x}}</p>
</div>
$scope.limit = 1;
and on ng-click you can set your limit like: ng-click='limit = names.length'
This is what you can try.
<div ng-init="limit= 1">
<button ng-click="limit=names.length">View</button>
<table>
<tbody>
<tr ng-repeat="x in names | limitTo: limit">
<td>{{x}}</td>
</tr>
</tbody>
</table>
</div>
https://jsfiddle.net/alpeshprajapati/7MhLd/2252/
Try limitTo filter :
The limitTo filter returns an array or a string containing only a specified number of elements.
Syntax :
{{ object | limitTo : limit }}
As per the requirement :
Js :
var app = angular.module('myApp', []);
app.controller('MyCtrl',function($scope) {
$scope.elements = ["1", "2", "3", "4", "5"];
$scope.limit = 1;
});
Html :
<button ng-click="limit=elements.length">Expand More</button>
<table>
<tr ng-repeat="item in elements | limitTo: limit">
<td>{{item}}</td>
</tr>
</table>
Working fiddle : https://jsfiddle.net/rohitjindal/vcxvvecr/2/
// Angular `slice` filter for arrays
var app = angular.module('myApp', []);
app.filter('slice', function() {
return function(arr, start, end) {
return arr.slice(start, end);
};
});
app.controller('MainController', function($scope) {
$scope.offset = 1;
$scope.items = [1,2,3,4,5,6,7,8,9,10,11,12,13,14];
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller='MainController' ng-init="start = 0;">
<ul>
<li ng-repeat="item in items | slice:start:offset">{{item}}</li>
</ul>
<button ng-click="offset = items.length">Expand</button>
</div>
</div>
I use slice for limit set
You can also try with:
limitTo: (limit) : (begin)
you can say ng-repeat="item in list | limitTo:50:0"
Limit the rows by set a scope variable in the controller and filter it in the ng-repeat.
Script:
var app = angular.module('myApp', []);
app.controller('limitCtrl',function($scope) {
$scope.limitNumber = 1;
});
Html:
<table>
<tbody>
<tr ng-repeat="x in names | limitTo: limitNumber">
<td>{{x}}</td>
</tr>
</tbody>
</table>

angular filter on an object property in ngRepeat doesn't seem to work with 'track by'

I have an map of data items I'm displaying in a table that gets refreshed with new values in the controller (hence the track by key). This all works fine.
If I try to throw an angular filter into the mix, it ignores the filter and displays all the data.
(BTW, items in data are being filled in by a callback from data coming from websocket - maybe this is causing the problem)
What am I missing here?
<label>Filter by Id <input ng-model="search.id"></label><br>
<table class="table table-striped">
<tr>
<th>Id</th>
<th>type</th>
<th>value</th>
<th>time</th>
</tr>
<tr ng-repeat="item in data | filter:search track by item.key">
<td>{{item.id}}</td>
<td>{{item.type}}</td>
<td>{{item.value}}</td>
<td>{{item.timestamp}}</td>
</tr>
</table>
controller:
DataService.addListener(listener);
$scope.data = {};
//incoming data from a websocket
function listener(data) {
var key = data.stuff.id1 + ':' + data.stuff.id2;
var lineItem = {
'id' : data.stuff.id1,
'type' : data.stuff.id2,
'value' : data.data.value,
'timestamp' : new Date(data.stuff.ts).toTimeString(),
'key' :key
};
$scope.$apply(function() {
$scope.data[key] = lineItem;
});
}
note that item.key is a property that uniquely identifies the item in the map of data.
You are filtering by search and not search.id since your input's model is set as search.id. Try this:
<tr ng-repeat="item in data | filter:search.id track by item.key">
Problem was filter doesn't work on maps. Derp.
Stole a map filter from this answer here, now it all works.
plunker
http://plnkr.co/edit/u5D3tGGKDOY4P4u6T1GZ?p=preview
<!DOCTYPE html>
<html ng-app="app">
<head>
<script data-require="angular.js#1.4.1" data-semver="1.4.1" src="https://code.angularjs.org/1.4.1/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-controller="TestCtrl">
<label>Filter by Id <input ng-model="search.id"></label><br>
<table border="1">
<tr>
<th>Id</th>
<th>value</th>
</tr>
<tr ng-repeat="item in data | mapFilter:search track by item.key">
<td>{{item.id}}</td>
<td>{{item.value}}</td>
</tr>
</table>
</body>
</html>
script:
angular.module('app', []).controller('TestCtrl', function($scope) {
$scope.data = {};
for (var i = 0; i < 20; i++) {
var item = {
id: randomWord() + " " + randomWord(),
key: 'key ' + i,
value: Math.floor(Math.random() * 300),
};
$scope.data[item.key] = item;
}
})
.filter('mapFilter', function($filter) {
var filter = $filter('filter');
return function(map, expression, comparator) {
if (! expression) return map;
var result = {};
angular.forEach(map, function(data, index) {
if (filter([data], expression, comparator).length)
result[index] = data;
});
return result;
}
});
var words = [
'lunville',
'pandybat',
'demurrer',
'slighter',
'reguline',
'exploder',
'krakatoa',
'wirespun',
];
function randomWord() {
return words[ Math.floor(Math.random() * 8)];
}

How to default a table search results to hidden with AngularJS filters?

In the following Angularjs snippet, an entire table is shown by default and gets filtered down as you start typing.
What would be best practice to change it to show no results by default and only start showing results after, say, at least 3 results match the search query?
Bonus question, how would you go about only displaying results if a minimum of 2 characters have been entered?
Html:
<div ng-app="myApp">
<div ng-controller="PeopleCtrl">
<input type="text" ng-model="search.$">
<table>
<tr ng-repeat="person in population.sample | filter:search">
<td>{{person.name}}</td>
<td>{{person.job}}</td>
</tr>
</table>
</div>
</div>
Main.js:
var myApp = angular.module('myApp', []);
myApp.factory('Population', function () {
var Population = {};
Population.sample = [
{
name: "Bob",
job: "Truck driver"
}
// etc.
];
return Population;
});
function PeopleCtrl($scope, Population) {
$scope.people = Population;
}
You can do all of that in your markup, actually... here's a plunk to demonstrate
And here's the change in your markup:
<input type="text" ng-model="search">
<table ng-show="(filteredData = (population.sample | filter:search)) && filteredData.length >= 3 && search && search.length >= 2">
<tr ng-repeat="person in filteredData">
<td>{{person.name}}</td>
<td>{{person.job}}</td>
</tr>
</table>
EDIT: changed my answer to reflect your requests.

Resources