AngularJs - allow only one item to be selected in Table - angularjs

I am using smart table. I have a logic behind where I only need one item to be selected. My current table code allow multiple lines to be selected.
This is my HTML:
<table st-safe-src="flow3.dataSet" st-table="flow3.displayed" class="table table-striped">
<thead>
<tr>
<th st-sort="method">Method</th>
<th st-sort="sample">Sample</th>
<th st-sort="parameters">Parameters</th>
</tr>
</thead>
<tbody ui-sortable ng-model="flow3.displayed">
<tr ng-repeat="row in flow3.displayed track by $index" style="cursor: move;" ng-click="row.selected = !row.selected; flow3.selectRow($event, row)" ng-class="{success: row.selected}">
<td>{{row.method.name}}</td>
<td>{{row.sample}}</td>
<td>
<span ng-repeat="parameter in row.parameters">{{parameter.methodInputParameter.name}} : {{parameter.value}}<br/></span>
</td>
<td>
<button type="button" ng-click="flow3.removeItem(row)"
class="btn btn-danger btn-sm btn-round pull-right"
ng-disabled="flow3.confirmDisabled">
<i class="glyphicon glyphicon-trash"></i>
</button>
</td>
</tr>
</tbody>
</table>
This is my JS:
flow3.selectRow = function($event, row) {
flow3.selectedItem = row;
}

If you want the only one row with selected property set to true, you should modify your selectRow method to accept the collection of all rows and then de-select all before selecting clicked one:
flow3.selectRow = function($event, row, rows) {
//de-select all
angular.forEach(rows, function(r){ r.selected = false; });
//select clicked
row.selected = true;
flow3.selectedItem = row;
}
ng-click="flow3.selectRow($event, row, flow3.dataSet)"
If you want to apply different css class to the clicked/selected item you probably can leave your selectRow method as is (since we have the selected item as flow3.selectedItem) and change the condition in the ngClass directive to be (if rows have some unique id property for example):
ng-class="{success: row.id === flow3.selectedItem.id}"
Hope this helps.

I'd recommend looking at the smart table github page and use guide here.
In particular the section relevant to your question is 'Select Data Rows'.
Essentially you need to use the st-select-row attribute along with the st-select-mode to configure click within your table row (tr) elements.

Related

uib-pagination not paging correctly when new line is added

I have a table of people showing and if there are more than 3 then the pagination controls should show. I want to allow the user to add/delete people to/from that table.
I have been able to add and delete from the table, and if there are 3 or less people, they display correctly. If I add a fourth person, I would expect the pagination control to go to the 2nd page and the top row. What appears to happen though is the pagination control, will show a '2' for a possible page number, but the 4th person shows on the first page....all others added also go to the first page. It seems as though the page numbers at the bottom do not work - the page doesn't change. I do update the current page value, but it doesn't seem like it is being reflected.
Below is a portion of my View. There are some extra pieces in it that don't really relate to paging. I use them to get to the right line to provide the user an input field to enter an employee name if the Add Lead button was clicked. Once they find who they are looking for, then the values are just displayed.
<table class="table" >
<tbody ng-repeat="boLead in components[selectedRow].boleads " pagination-id="boleads" >
<tr >
<td id="leadId" >{{boLead.boLeadId}}</td>
<td ng-show="(inserted == boLead && newLead) ">
<input type="text" class="form-control" ng-model="boLead.boLead" ng-model-options='{ debounce: 800 }' ng-change="searchEmployee(boLead.boLead)" />
<table class="table table-hover table-bordered" ng-show="employees.length">
<tr>
<th>Employee ID</th>
<th>Employee Name</th>
</tr>
<tr ng-repeat="result in employees" ng-click="pickEmployee(result, boLead)">
<td>{{result.user_id}}</td>
<td>{{result.name}}</td>
</tr>
</table>
</td>
<td ng-show="(inserted != boLead && !editq || (editq && editl != ($index+(3*(pagination.current-1)))) )">{{boLead.boLead}}</td>
<td ng-if="mode == 'edit'">
<select class="form-control input-md" ng-model="boLead.forwardONS" >
<option>Yes</option>
<option>No</option>
</select>
</td>
<td ng-if="mode == 'edit' && (!checkNewLead(boLead) && !editq)">
<button type="button" ng-click="removeLead($index+(3*(pagination.current-1)))" class="btn btn-danger fa fa-trash"> Delete</button>
</td>
<td ng-if="mode == 'view'" ng-bind-html="boLead.forwardONS"></td>
</tr>
</tbody>
</table>
<uib-pagination previous-text="‹" next-text="›" ng-model="pagination.current" class="pagination-sm" ng-if="pagination.totalLeads > 3" items-per-page="3" total-items="pagination.totalLeads" boundary-link-numbers="true"></uib-pagination>
Controller:
This is the portion for adding a person to the list
$scope.addLead = function() {
$scope.inserted = {
prgGuid: $scope.components[$scope.selectedRow].prgGuid,
componentId: $scope.components[$scope.selectedRow].componentId,
boLeadId: '',
boLead : '',
forwardONS: ''
};
if (typeof $scope.components[$scope.selectedRow].boleads == 'undefined') {
$scope.components[$scope.selectedRow].boleads = new Array($scope.inserted);
} else {
$scope.components[$scope.selectedRow].boleads.push($scope.inserted);
}
$scope.newLead = true;
$scope.editq = true;
$scope.editl = $scope.components[$scope.selectedRow].boleads.length-1;
if ($scope.components[$scope.selectedRow].boleads.length > 3) {
var pg = Math.ceil($scope.components[$scope.selectedRow].boleads.length / 3);
$scope.pagination.current = pg;
}
$scope.pagination.totalLeads = $scope.components[$scope.selectedRow].boleads.length;
};
When I step through this, the $scope.pagination.current = pg; does update properly with what the current page should be. For instance, once a 4th person is added, it becomes 2. This is the ng-model in the pagination controls, but it just doesn't seem like the pagination is listening, since the screen doesn't change to show the new line but instead all four show.
I've looked at a lot of posts, but I seem to be doing something different than everyone - I couldn't find an example of someone adding a line and then the screen page when enough entries would require going to a new page.
I seem to have been able to fix my problem after looking at more examples...I added a slice to the table and it seems to work exactly how I wanted...
I changed this line:
<tbody ng-repeat="boLead in components[selectedRow].boleads " pagination-id="boleads" >
to:
<tbody ng-repeat="boLead in components[selectedRow].boleads.slice(((pagination.current-1)*3), ((pagination.current)*3)) " pagination-id="boleads" >

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>

Expand/collapse odd row in AngularJs

I want to expand/collapse a row, I want to hide all odd rows and on clicking '+' button those hidden row should get visible, my current code is allowing me hide all the odd rows but on clicking '+' button all row are getting visible, my use case is to show only the odd row which is next to clicked even row.
<table class="table table-bordered">
<thead>
<tr>
<th></th>
<th>Name</th>
<th>Type</th>
</tr>
</thead>
<tbody ng-repeat="test in ctrl.SearchResults">
<tr ng-if="$even">
<td>
<button ng-click="ctrl.expanded = !ctrl.expanded" expand>
<span ng-bind="ctrl.expanded ? '-' : '+'"></span>
</button></td>
<td>{{test.apName}}</td>
<td>{{test.type}}</td>
</tr>
<tr ng-show="ctrl.expanded" ng-if="$odd">
<td></td>
<td>{{test.apName}}</td>
<td>{{test.type}}</td>
</tr>
</tbody>
</table>
The reason all the rows expand when you hit the plus, is that the variable expanded is on the controller. So all the rows are 'listening' to the same variable.
As atfornes pointed out in his answer, you could use the $index to identify each row. Another solution is to keep track of the current index on the controller and check for per row if he must be showed:
<tr ng-show="ctrl.expanded == $index" ng-if="$odd">
And on the click event of the plus sign you could have
ctrl.expanded = $index
Or you could handle this in the controller. In this case you can create a method on the controller which accepts the index and then set this index value to the controller's variable expanded.
At the button ng-click code, you could store the current $index with a code like:
<button ng-click="ctrl.expanded = !ctrl.expanded; ctrl.index = $index" expand>
then, you can change the ng-show condition to only display the item if ctrl.expanded and $index === ctrl.index + 1:
<tr ng-show="ctrl.expanded && $index === ctrl.index +1" ng-if="$odd">

Angular ng-repeat stay paged after filter

I'm using AngularJS and I have a simple table using ng-repeat and filter. When i insert anything at the search input it filters OK but it keeps paginated as it was before I filter the table. Any ideas why this is happening ?
Here's my code:
//some var initialization
$scope.pagina = {};
$scope.pagina.currentPage = 1,
$scope.pagina.numPerPage = 10,
$scope.pagina.maxSize = 5;
//return from server with data
coresFactory.getCores().then(function(response) {
$scope.tabelaCores = response.data;
$scope.filteredTodos = $scope.tabelaCores.cores.slice(0, 10);
});
//do pagination
$scope.$watch('pagina.currentPage', function() {
var begin = (($scope.pagina.currentPage - 1) * $scope.pagina.numPerPage),
end = begin + $scope.pagina.numPerPage;
$scope.filteredTodos = $scope.tabelaCores.cores.slice(begin, end);
},true);
<input ng-model="pesquisaTabela" type="search" style="width:300px;" class="form-control input-inline" placeholder="" aria-controls="sample_1"></label>
<table class="table" style="margin-bottom:5px;border:1px solid #DDDDDD" id="sample_1">
<thead>
<tr style="background-color:#F9F9F9">
<th style="width:100px; text-align:center;"> Id </th>
<th> Nome </th>
<th> Plural </th>
<th style="width:100px; text-align:center;"> Ativo </th>
</tr>
</thead>
<tbody>
<tr ng-repeat="cor in filteredTodos | filter:pesquisaTabela" ng-click="setSelected(cor.id)" ng-class="{linhaSelected: cor.id === idCorSelecionada}">
<td style="text-align:center;"> {{cor.id}} </td>
<td style="text-transform:capitalize"> {{cor.nome}} </td>
<td style="text-transform:capitalize"> {{cor.plural}} </td>
<td style="text-align:center;">
<span ng-if="cor.status=='sim'" class="label label-sm label-success"> Sim </span>
<span ng-if="cor.status=='nao'" class="label label-sm label-danger"> Não </span>
</td>
</tr>
</tbody>
</table>
<pagination first-text="<<" last-text=">>" next-text=">" previous-text="<" ng-model="pagina.currentPage" total-items="tabelaCores.cores.length" max-size="pagina.maxSize" boundary-links="true"></pagination>
You have quite a disconnect between various parts
Pagination is working off of one array, display from another and filter from yet another because when filter kicks in it returns a new filtered array.
The way you have things structured, your filter won't work properly either.
When you slice the main data array the filter is only going to work on that part that is sliced....not the whole main array
In order for pagination to be synchronized with filtering your simplest start point would likely be do your own filtering and share the same filtered array between the pagination and the table.
There are some other built in filters that you could use also like limitTo that takes 2 arguments limit and begin. That would help you get rid of currrent slice
There are lots of available grid/table directives available.
Using one of those would be my best suggestion
There is one other way you could do this all in the view. There is a syntax for ng-repeat that creates a new filtered array on the scope and therefore gives you access to it's length
<tr ng-repeat="row in filteredData = ( data |filter:pesquisaTabela | limitTo:10:start)">
Using this you could pass filteredData array to the total-items of the pagination directive.
It also now lets you do things like:
<div> Filtered length is: {{filteredData.length}}</div>

ngRepeat with Table: Change a cell's display text

I have a list of mails which I want to show in a grid (<table>). Some of these mails have attachments. For their corresponding rows, I would like to show an attachment icon in the attachment column. For rest, it should be empty.
JsFiddle: http://jsfiddle.net/6s4Z5/
My template is as follows:
<table id="resultTable">
<thead>
<tr>
<th ng-repeat="columnId in schema.columnOrder">
<img src="icon_attach.png" ng-if="columnId == 'hasAttachments'">
{{schema.columns[columnId].displayText}}
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in results.mails">
<td ng-repeat="columnId in schema.columnOrder">
<img src="icon_attach.png" ng-if="columnId == 'hasAttachments' && row.hasAttachments">
<span ng-if="columnId != 'hasAttachments'" >{{ row[columnId] }}</span>
</td>
</tr>
</tbody>
</table>
where schema.columnOrder is an array of columnIds to be shown in the table.
This template is working but is this the best way to implement this? Moreover, I have to add an extra <span> for ng-if statement. Can that also be removed?
You pretty much can change it to something like:
<span ng-if='your check'><img src=''/>{{row[columnId}}</span>
You could set (in the controller) the value of the column hasAttachments to be the image you want to display there.

Resources