ng-init miscalculates aliased index after array.splice - arrays

I have encountered a strange behavior related to ng-init, any help would be appreciated.
I have a model object which has a flats property that is an array of flat objects. Each flat object has rooms property which is an array of room objects.
I'm trying to display flat and rooms as follows;
<table ng-repeat="flat in model.flats" ng-init="flatIndex = $index">
<thead>
<tr>
<td>{{flatIndex+1}}. {{flat.name}}</td>
</tr>
</thead>
<tbody>
<tr ng-repeat="room in flat.rooms" ng-init="roomIndex = $index">
<td>{{roomIndex+1}}. {{room.name}}</td>
</tr>
</tbody>
</table>
If i delete a flat or room by using array.splice flatIndex and roomIndex variables doesn't seem to update properly even though $index and ui updates properly.
You can see the problem here in action.
Try to delete 1st, 2nd or 3rd flat or room object by clicking the delete link. Deleting last object from the array doesn't really expose the problem.
Any workarounds would also be appreciated.

This is a known behavior when you use ng-init, the scope property values set by ng-init are not watched and they don't update when you remove items from array to reflect the refreshed index position. So don't use ng-init, instead just use $index (deleteFlat($index)) and flat object reference (to get hold of rooms deleteRoom(flat,$index)).
<table ng-repeat="flat in model.flats track by flat.id">
<thead>
<tr>
<td colspan="2">{{$index+1}}. {{flat.name}}</td>
<td>DELETE FLAT</td>
</tr>
</thead>
<tbody>
<tr ng-repeat="room in flat.rooms track by room.id">
<td> </td>
<td>{{$index+1}}. {{room.name}}</td>
<td>DELETE ROOM</td>
</tr>
</tbody>
</table>
and
$scope.deleteFlat = function(flatIndex){
$scope.model.flats.splice(flatIndex,1);
};
$scope.deleteRoom = function(flat,roomIndex){
flat.rooms.splice(roomIndex,1);
};
Plnkr
Or better off use the ids itself, deleteFlat(flat.id) and deleteRoom(room.id, flat).
<table ng-repeat="flat in model.flats track by flat.id">
<thead>
<tr>
<td colspan="2">{{$index + 1}}. {{flat.name}}</td>
<td>DELETE FLAT</td>
</tr>
</thead>
<tbody>
<tr ng-repeat="room in flat.rooms track by room.id">
<td> </td>
<td>{{$index+1}}. {{room.name}}</td>
<td>DELETE ROOM</td>
</tr>
</tbody>
</table>
and
$scope.deleteFlat = function(flatId){
$scope.model.flats.splice(_getItemIndex(flatId, $scope.model.flats), 1);
};
$scope.deleteRoom = function(roomId, flat){
flat.rooms.splice(_getItemIndex(roomId, flat.rooms), 1);
};
function _getItemIndex(imtId, itms){
var id ;
itms.some(function(itm, idx){
return (itm.id === imtId) && (id = idx)
});
return id;
}
Plnkr2

Related

display data angular 4 (converting obj to array)

I'm trying to display an object of an object in an array, I've been hitting my head against the wall since I cant change the backend.
this is what I got so far, M01, M02, M03... might refer to months since we are November there are 11 however if it was February id be only M01 and M02. since I got a property of an object I cant loop it on a second for and I'm having a lot of trouble with it
this is my view
<div *ngIf="estados" class="table-responsive col-lg-12 tablilla">
<table class="table table-bordered table-striped table-responsive">
<thead>
<tr>
<th></th>
<th *ngFor="let month of listaMonthsNames">{{month}}</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let estado of estados">
<td>{{estado.nom_item}}</td>
<td *ngFor="let month of estado.M[0]">{{month}}</td>
</tr>
</tbody>
</table>
</div>
I think this will help you :
There is no direct way to access Object from the Template side so,
All you need to do is provide Object.keys access to the Template side by this way from Component :
// Component Side :
objectKeys = Object.keys;
// Template Side :
<td *ngFor="let key of objectKeys(estado.M)">
Key: {{key}}, value: {{estado.M[key]}}
</td>
You can read more about the Object.keys HERE

ng repeat dynamic with key objet json AngularJS

I have a small problem I would like to set up a ng-repeat dynamic with the values I receive from my JSON object,
First, i made my th from my table with the keys
Secondly, i would like to do my td in dynamic (without putting the name of the key ex: obj.NOM, obj.TYPE ...)
I managed to do something with an Object.keys but logic is not good so I need some help
this is my JSON object ( i show you just the little piece of code that I have a problem )
"HEADER":[
{"NOM":"API-APP","TYPE":"string","DESCRIPTION":"Code application"},
{"NOM":"API-SIGNATURE","TYPE":"string","DESCRIPTION":"Signature de la requete API"},
{"NOM":"API-TIMESTAMP","TYPE":"integer","DESCRIPTION":"Timestamp en microseconde"}]
and this is my ng repeat
<span><b>HEADER</b></span>
<br>
<br>
<table class="table">
<th ng-repeat ="(key, itemHeader) in itemHead.HEADER[0] track by $index">{{key}}</th>
<tr ng-repeat ="(key, itemHeader) in itemHead.HEADER track by $index" >
<td>{{getFirstPropertyValue(itemHeader,$index)}}</td>
</tr>
</table>
I explain i made first a ng-repeat for th with the keys and I would like put my data (3 data per row) in td without put ( .NOM .TYPE .DESCRIPTION)
So I took the function object.key which works very well but that makes me just one element per row but I need 3 elements per row
this is my scope for the function object.key
$scope.getFirstPropertyValue = function(obj,index){
return obj[Object.keys(obj)[index]];
}
and this my result
thanks in advance for your help
<table class="table">
<thead>
<tr>
<th ng-repeat="(key, itemHeader) in itemHead.HEADER[0]">
{{key}}
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in itemHead.HEADER">
<td ng-repeat="column in row">
{{column}}
</td>
</tr>
</tbody>
</table>

ng-repeat shows terrible performance with large lists

I'm experiencing performance issues when using ng-repeat for long lists (thousands of records) that need to be shown within a table.
Here is the HTML I'm using:
<table class="table table-hover">
<thead>
<tr>
<th ng-repeat="One_Header in Data_List.Header track by $index">{{One_Header}}</th>
</tr>
</thead>
<tbody>
<tr ng-model="Data_List" ng-repeat="One_Source in Data_List.Body track by $index" style="cursor:pointer">
<td ng-repeat="One_Data_Field in One_Source track by $index">{{One_Data_Field}}</td>
</tr>
</tbody>
</table>
The received JSON (takes about 1 second to get it from the server) contains the data as follows:
"Data":{
"Header":["Title_1","Title_2","Title_3"],
"Body":[
["Rec_1_Data_1","Rec_1_Data_2","Rec_1_Data_3"],
["Rec_2_Data_1","Rec_2_Data_2","Rec_2_Data_3"],
["Rec_3_Data_1","Rec_3_Data_2","Rec_3_Data_3"],
:
["Rec_n_Data_1","Rec_n_Data_2","Rec_n_Data_3"]
]
}
I also tried one-directional binding (i.e. {{::One_Data_Field}}) without any noticeable improvement.
As stated above, response arrives within a second and then it takes up to 10 seconds for the table to be built.
in order to improve the performance, you can use pagination directives created using angular js.
Angular-Paging
pagination
If you are using Angular material design then I would highly recommend using virtual repeat
virtual repeat
you can try this sample pagination implementation:
var pageSize = 15;
$scope.paginationLimit = function(pagesShown) {
return pageSize * pagesShown.count;
};
$scope.hasMoreItemsToShow = function(list, pagesShown) {
return pagesShown.count < (list.length / pageSize);
};
$scope.showMoreItems = function(pagesShown) {
pagesShown.count = pagesShown.count + 1;
};
<tr ng-model="Data_List" ng-repeat="One_Source in Data_List.Body track by $index " style="cursor:pointer">
<td ng-repeat="One_Data_Field in One_Source track by $index | limitTo: paginationLimit(pagesShown)">{{One_Data_Field}}</td>
</tr>
//sample row with show more button
<tr class="showMore">
<td></td>
<td class="showBtn">
<button type="button" class="btn btn-primary btn-lg" ng-init="pagesShown={'count': 1}" ng-click="showMoreItems(pagesShown)" ng-show="hasMoreItemsToShow(One_Source track, pagesShown)">Show More</button>
</td>
<td></td>
</tr>

Angularjs smart-table not sorting $index table cells

I have a problem withe the Smart Table AngularJS Sorting, I implemented this on my table as:
The initialized app:
angular.module('myproyApp', ['smart-table'])
The controller side:
$scope.dataList = []; //any json collection with: id, name and description
The view side with st-table directive:
<table class="table table-bordered table-striped" st-table="dataRows" st-safe-src="dataList">
<thead>
<tr>
<th><span class="glyphicon"></span>Q</th>
<th st-sort="name">Name</th>
<th st-sort="descripcion">Description</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in dataRows">
<td class="col-md-1">{{$index + 1}}</td>
<td class="col-md-4">{{row.name}}</td>
<td class="col-md-4">{{row.description}}</td>
<td>Change</td>
</tr>
</tbody>
</table>
On click the sorting header's cells the table is sorting, but the sorting isn't working for the $index cells. Please if you have any think to include the $index cells to sort. I want not to use the indexes on dataList $scope values, I need that this index will be include only on table view.
Track by is used to link your data with the DOM generation made by ng-repeat. When you add track by you tell angular to generate a single DOM element per data object in the given collection. Because $index has to do with the DOM there is no way to have it relate to a particular data entry. Here's a more detailed explanation.
If you really want to do it without touching your dataList, you could call indexOf in your table:
<tbody>
<tr ng-repeat="row in dataList | orderBy:sortField">
<td class="col-md-1">{{dataRows.indexOf(row)}}</td>
<td class="col-md-4">{{row.name}}</td>
<td class="col-md-4">{{row.description}}</td>
<td>Change</td>
</tr>
</tbody>
Where there is a scoped variable called sortField which is a string that is the name of the field you wish to sort by. I implemented a similar thing in this plunker, using the smart-tables module. http://plnkr.co/edit/AF90dQ
I would advise against this because it quickly becomes expensive for large arrays, and runs into problems if your entries aren't unique.

angularJS - Repeating tr's in a table, but dynamically adding more rows while looping

Since examples always tell more then just words here is what I would like to do in another language
<tr>
<c:forEach items="${items}" var="item">
<td>...</td>
<td>...</td>
<td>...</td>
<c:if test="${item.showWarning}">
</tr><tr><td colspan="3">${item.warning}</td>
</c:if>
</tr>
So this will loop over a set of items and show some properties of these items. If there is a warning, a new row will be added underneath the current row in which the warning will be shown. However, how can I do this in angularJs? If I put a ng-repeat on the tr, it will stop at the first end tag of tr. I have read on some other threads that this is not very easily done, but how can it be done? And yes, I really do want to use a table. Here is my contrived example with angularjs which is obviously not working as I would like it to. Any pointers how this can be done?
JSBin example with tr-ng-repeat
Currently (1.0.7/1.1.5) you can't output data outside the ng-repeat, but version 1.2(see youtube video AngularJS 1.2 and Beyond at 17:30) will bring the following syntax(adapted to your example):
<tr ng-repeat-start="item in items">
<td>...</td>
<td>...</td>
<td>...</td>
</tr>
<tr ng-repeat-end ng-show="item.showWarning">
<td colspan="3">{{item.warning}}</td>
</tr>
The idea is that whatever is between -start and -end will be repeated including the -end element.
One solution that I can think of is having multiple tbody tags within the same table. Here is a discussion on the use of multiple tbody tags within the same table.
So, for your issue, you could have the following setup:
<table ng-controller="ItemController">
<thead>
<th>Name</th>
<th>Description</th>
<th>warning?</th>
</thead>
<tbody ng-repeat="item in items">
<tr>
<td>{{item.name}}</td>
<td>{{item.description}}</td>
<td>{{item.warning}}</td>
</tr>
<tr ng-show="item.warning">
<td colspan="3" style="text-align: center">Warning !!!</td>
</tr>
</tbody>
</table>
Repeat the table body as many times as there are entries for the table and within it have two rows - one to actually display the row entry and one to be displayed conditionally.
You can repeat over the tbody
<tbody ng-repeat="item in items">
<tr>
<td>{{item.name}}</td>
<td>{{item.description}}</td>
<td>{{item.warning}}</td>
</tr>
<tr ng-show="item.warning">
<td colspan="3">Warning !!!</td>
</tr>
</tbody>
Also you do not need to wrap the ng-show expression in {{}}, you can just use item.warning

Resources