Angular Datatables - Select only filtered rows - html

I am rendering my DataTable the angular way, I added a checkbox per row and a select all checkbox at the top. The problem is that for example, I filter the rows using the search box if I check the select all it checks all the rows. What do I do so that when the select all checkbox is clicked, it only checks the visible rows after the filtering.
Html
<table id="tblAvailable" datatable="ng" dt-options="mainCtrl.dtOptions" dt-instance="mainCtrl.dtInstance" dt-column-defs="mainCtrl.dtColumnDefs" class="table table-responsive table-hover">
<thead>
<tr>
<th>Ref. #</th>
<th>Type</th>
<th>Category</th>
<th>Applied Amount</th>
<th>New Amount</th>
<th><input type="checkbox" ng-model="mainCtrl.selectAll" ng-click="mainCtrl.toggleAll(mainCtrl.selectAll)" ng-change="mainCtrl.update()"></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="Item in mainCtrl.newLineDetails" ng-if="Item.Amount > 0">
<td>{{Item.Id}}</td>
<td>{{Item.Type.Name}}</td>
<td>{{Item.Category.Name}}</td>
<td>{{Item.Amount | number:2}}</td>
<td><input type="number" ng-disabled="Item.isSelected == false" id="Amount" name="Amount" class="form-control ng-pristine ng-untouched ng-valid ng-not-empty" ng-model="Item.Amount" ng-min="1" ng-max="Item.Amount" ng-required="Item.isSelected == true" ng-change="mainCtrl.updateForFreeUps()"/></td>
<td><input type="checkbox" ng-model="Item.isSelected" ng-change="mainCtrl.toggleOne(Item.Id)"></td>
</tr>
</tbody>
</table>
Ctrl
self.toggleAll = function(selectAll) {
angular.forEach(self.newLineDetails, function (value, index) {
self.newLineDetails[index]["isSelected"] = selectAll;
if (selectAll == false) {
self.newLineDetails[index]["Amount"] = null;
}
})
}
self.toggleOne = function (Id) {
for (var i = 0, len = self.newLineDetails.length; i < len; i++) {
if (self.newLineDetails[i]["Id"] == Id) {
self.newLineDetails[i]["Amount"] = null;
self.selectAll = false;
self.update();
return;
}
}
self.selectAll = true;
}

You must go through the DataTables API. DT remove and inject DOM nodes from a "shadow table" so just manipulating DOM will only have apparently (not real) effect until next redraw. Fortunately you have already implemented a dtInstance.
From 1.10.6 the most convenient way to iterate through rows, columns or cells is the every method. See this plunkr -> http://plnkr.co/edit/NOP5u4PUcwVOBFUtBkBi?p=preview
$scope.$watch('settings.selectAll', function(newVal, oldVal) {
if (newVal == oldVal) return
var api = $scope.dtInstance.DataTable;
api.rows({ search:'applied' }).every(function() {
api.cell({ row:this.index(), column:0 })
.nodes()
.to$()
.find('input')
.prop('checked', $scope.settings.selectAll);
})
$scope.dtInstance.DataTable.draw()
})
Here the checkbox is in column 0; the code can be translated into
cycle through all rows
get the first column
convert to jQuery instance
find the <input>
update the checked status
Some notes about your selectAll checkbox :
best to use an object as ng-model, in the example I have used a settings.selectAll literal
ng-click does not work effectively with checkboxes, if you really want to use a directive, use ng-change only
since you are using a ng-model, you can just $watch that value, calling mainCtrl.toggleAll(mainCtrl.selectAll) is "obscure"

There are many ways for solving this problem:
1) You can write change function for select all checkbox. In that function first you should filter all of your data after that check them.
2) You can copy your main data to another variable (we call it x for now). after that show x in table (not your main resource). when you want to filter the rows using the search box filter main data and past it into the x variable when you want to use it for check or add etc use x variable

Related

Posting data of a single table row when table is in one form

I have one form that has a whole table inside it. A for loop iterates through a list of items and adds records to the table. Each record has a submit button. When I click that button, I use AJAX to serialize
and POST the form (because I need to reload a partial), and expect to POST data about that single item in the record to the controller.
In other words, if a table is displaying records from a looped list, I want to be able to POST a single list item to the controller.
What is holding me back is the for and foreach loops when I try to POST a record:
If I use a foreach loop the form always POSTs the very first record in the table, even if I click the button on other records.
If I use a for loop I am required to POST the whole list to my controller because the list is a parameter inside another object. This means I am forced to bind to the model that contains the list.
Possible reason for the issue: I suspect this has something to do with the values of the name attributes in the HTML that asp-for attribute generates. With a for loop the names are not unique, so the binding process assumes the first record with the correct names. With a foreach loop, the names are unique, but the binding process needs to bind to the model that contains the list.
HTML View (simplified). A foreach loop in place of the for loop would look like #foreach (var shipment in lineItem.Shipments) { <tr>... </tr> }
#model OrderTrackingContract.SalesOrder
#foreach (var lineItem in Model.LineItems)
{
<table class="lineItemTables">
//line items table
</table>
#if (lineItem.Shipments.Count > 0)
{
<form method="post">
<table class="table shipmentTable">
<thead>
<tr>
<th>
ShipmentID
</th>
<th>
Qty Shipped
</th>
<th>
Actions
</th>
</tr>
</thead>
<tbody>
#for (int i = 0; i < lineItem.Shipments.Count; i++)
{
<tr>
<td>
<input id="shipmentID" asp-for="#lineItem.Shipments[i].ShipmentID" />
</td>
<td>
<input id="qtyShipped" asp-for="#lineItem.Shipments[i].QtyShipped" value="#lineItem.Shipments[i].QtyShipped" min="1" max="999" />
</td>
<td>
<input class="updateButton" type="submit" value="Update" />
</td>
</tr>
}
</tbody>
</table>
</form>
}
}
AJAX
$('.updateButton').on('click', function () {
var form = $(this).closest('form');
form.submit(function (event) {
event.preventDefault(); /* stop form from submitting normally */
$.ajax({
url: "/OrderTracking/UpdateShipment",
type: "POST",
data: form.serialize(),
success: function () {
//some code here
}
});
});
});
Controller Action
[HttpPost]
public IActionResult UpdateShipment(Shipment shipment)
{
_orderTrackingService.UpdateShipmentByID(shipment.ShipmentID, shipment.QtyShipped);
return NoContent();
}
I have tried to extract the values from the tags using the tag ids shipmentID and qtyShipped closest to the button and pass them to the controller, which worked. However, I want to avoid doing that and instead actually POST the form.
If you just want to pass data for a single row then just get the data for that row and pass to your action... don't serialize every row and post it.
You should do 2 things...
First update the inputs in your loop by giving them a class and not an id. id's should be unique for the page, but you are creating the same id="shipmentID" and id="qtyShipped" for your inputs. Don't do this, the asp-for will create your id and name attributes for you.
<tbody>
#for (int i = 0; i < lineItem.Shipments.Count; i++)
{
<tr>
<td>
<input class="shipmentID" asp-for="#lineItem.Shipments[i].ShipmentID" />
</td>
<td>
<input class="qtyShipped" asp-for="#lineItem.Shipments[i].QtyShipped" value="#lineItem.Shipments[i].QtyShipped" min="1" max="999" />
</td>
<td>
<input class="updateButton" type="submit" value="Update" />
</td>
</tr>
}
</tbody>
Next, update your ajax request to find the row the button is in then get the values of the inputs of that row and make a data object to be passed to your action. Like this:
$('.updateButton').on('click', function (e) {
e.preventDefault(); /* stop form from submitting normally */
var row = $(this).closest('tr'),
shipmentID = $('.shipmentID', row),
qtyShipped = $('.qtyShipped', row);
var data = { ShipmentID: shipmentID.val(), QtyShipped: qtyShipped.val() };
$.ajax({
url: "/OrderTracking/UpdateShipment",
type: "POST",
data: data,
success: function () {
//some code here
}
});
});
You were also attaching a submit event to your form everytime a submit button was clicked. This is bad and would result in 3 submit events being attached if 3 buttons were clicked, so I removed this.

Need to get specific html element row index from *ngFor to activate boolean function

I'm trying to build a dynamic table in Angular with add, edit, delete functionality. Both my edit/delete buttons exist as their own column on every single one of the table rows. The edit button (onclick) is designed to make each of the 3 text data fields (in the same row as the edit button itself) turn into input fields whose user-entered text can then be saved.
<table id="thetable" align="center">
<tr>
<th>Application ID</th>
<th>Description</th>
<th>API Key</th>
<th>EDIT/DELETE</th>
</tr>
<tr id="newtablerow" ng-app="tblRowApp" *ngFor="let prov of providers; let i = index">
<td id="tablevalues" *ngFor="let col of columns">
<span id="columnText" ng-init="getRowIndex(i)" *ngIf="!editing">{{prov[col]}}</span>
<span class="editfield" *ngIf="editing">
<input id="changeText" ng-init="getChangeTextCol(col)" type="text" style="margin-right: 10px" placeholder="{{prov[col]}}">
<button ngOnload="getChangeTextCol(col)" (click)="save(changeText.value); !editing">Save</button>
</span>
</td>
<td id="editdelete">
<button class="edit" name="editButton" (click)="editToggle(i)">/</button>
<button class="delete" (click)="deleteRow(i)">x</button>
</td>
</tr>
public editing: boolean = false;
editToggle(event) {
var table = (<HTMLTableElement>document.getElementById("thetable"));
var getTextFields = table.getElementsByClassName("columnText");
for (var i = 0; i < getTextFields.length; i++) {
if (getTextFields[i].getRowIndex(event) == event) {
getTextFields[i].editing = !getTextFields[i].editing;
}
}
}
getRowIndex(event) {
console.log("row index = " + event);
return event;
}
getChangeTextCol(event) {
return event;
}
deleteRow(event) {
this.providers.splice(event, 1);
}
editToggle() is activated on edit button click and finds the current row index of itself by variable i specified by *ngFor. However, I also need to tell angular the row index of the span element containing the html input field to be shown, but I get an error saying property 'getRowIndex' does not exist on type 'Element'. This works for other functions in HTML elements like deleteRow(i), for instance.

Bind first value and remove all other repeating value from a ng-repeat

I have a scenario to bind a html table using angular js. In my table i need to show an a tag based on another column value(Payment Status). If its fully paid no need to show the a tag, else need to show it for very next element. I am a new one in angular.
<table>
<thead>
<tr>
<th>Month</th>
<th>Installement</th>
<th>PaymentAmount</th>
<th>PaymentDate</th>
<th>Payment Status</th>
<th>Pay</th>
</tr>
</thead>
<tbody>
<tr dir-paginate="row in rowCollection|orderBy:type:reverse|filter:searchKeyword|itemsPerPage:maxsize">
<td>{{row.Month}}</td>
<td>{{row.MonthlyInstallement}}</td>
<td>{{row.PaymentAmount}}</td>
<td>{{row.PaymentDate}}</td>
<td>{{row.PaymentStatus}}</td>
<td ng-if="row.PaymentStatus == 'UNPAID'">
Pay Online
</td>
<td ng-if="row.PaymentStatus == 'FULLY_PAID'">
</td>
</tr>
</tbody>
</table>
function bindFinanceDetails() {
var finUrl = baseurl + 'api/FinancialStatement/GetCarFinanceInfo';
var req = {
method: 'post',
data: {
LoginID: LoginID,
ContractNumber: 11170200669,
CustomerId: 2355898046
},
url: finUrl,
headers: {
RequestedPlatform: "Web",
RequestedLanguage: cookiePreferredLanguage,
Logintoken: $cookieStore.get('LoginToken'),
LoginId: LoginID
}
};
$http(req).then(function(response) {
var getData = response.data.FinanceList;
$scope.rowCollection = getData;
}, function(error) {
toastr.error($filter('translate')('Error Occured'));
});
}
A quite hacky solution will be something like the following (just showing you the needed change in the unpaid td element):
<td ng-if="row.PaymentStatus === 'UNPAID'" ng-show="$index === data.payOnlineIndex"
ng-init="data.payOnlineIndex = (!data.payOnlineIndex || (data.payOnlineIndex > $index)) ? $index : data.payOnlineIndex">
Pay Online
</td>
This way ng-init will run for all unpaid elements, setting the smallest index to the payOnlineIndex variable. ng-show will make sure to only show that one element that has the smallest index.
I encapsulate payOnlineIndex with a data object to keep a stable reference to it. This also requires the following addition to the controller code:
$scope.data = { payOnlineIndex: null };
See a working jsFiddle example here: https://jsfiddle.net/t3vktv0r/
Another option is running your filter and orderBy in the controller, searching for the first occurrence of an "unpaid" row, and marking that element for the "pay online" feature with some flag you can test with ng-if in your view.

ng-repeat highlight all rows but also deselect, select

I have a basic html table where i need to have all the rows initially highlighted when the table is created. Also, if the user clicks the row it un highlights and clicked again highlights.
I have the click on a row, and it highlights. If you click again it un highlights.
I just need to initially highlight all rows possibly by ng-repeat. It also needs to release the highlighting when the row is clicked again and then highlight back. userData is just a line of text for each row
HTML
<table class="superusertable" cellpadding="5" cellspacing="0">
<tbody class="table-font">
<tr ng-init="" ng-repeat="source in userData"
ng-model="source.fromSourceID"
ng-class="{'sourcesSelected': source.sourcesSelected}"
ng-click="select(source)">
<td width="290px">
<div class="action-checkbox"; width="290px">{{source.fromSourceID}}
</div>
</td>
</tr>
</tbody>
</table>
angular
$scope.select = function(item) {
item.sourcesSelected ? item.sourcesSelected = false : item.sourcesSelected = true;
};
You can just add a function to the ng-init attribute on your tr. Just pass in your item and set it to true. Then like Aluan said in a comment, you can just make your ng-click function simpler by doing item.sourcesSelected = !item.sourcesSelected.
html
<table class="superusertable" cellpadding="5" cellspacing="0">
<tbody class="table-font">
<tr ng-init="init(source)"
ng-repeat="source in userData"
ng-model="source.fromSourceID"
ng-class="{'sourcesSelected': source.sourcesSelected}"
ng-click="select(source)">
<td width="290px">
<div class="action-checkbox"; width="290px">{{source.fromSourceID}}</div>
</td>
</tr>
</tbody>
</table>
angular
$scope.select = function(item) {
item.sourcesSelected = !item.sourcesSelected;
};
$scope.init = function(item) {
item.sourcesSelected = true;
}
On a side note, you can completely eliminate the ng-init and init function by setting item.sourcesSelected = true when you are retrieving your data.
There are too many errors i can observe.
ng-init="" not required
ternary operator is wrong you should do something following:
item.sourcesSelected = item.sourcesSelected ? false : true;

checking a single checkbox per html row using angular js

i have a table that loads multiple checkboxes and selectboxes.when i click on one checkbox or select box it automatically selects every other box on the table .i want to have the option to choose either one checkbox or select box per row on it own.
<tr id="TableBody" ng-repeat="code in Register.RegisterDetails">
<td>{{$index+1}}</td>
<td ng-bind="code .CodeID"><input type="text" ng-model="Register.CodeID" /></td>
<td ng-bind="code .name"><input type="text" ng-model="Register.Firstname" /></td>
<td ng-bind="code .Lastname"><input type="text" ng-model="Register.Lastname" /></td>
<td><input type="checkbox" ng-model="Register.Presentstatus" id="PresentCheckbox" name="PresentCheckbox" /></td>
<td><select id="reasons" name="reasons" ng-model="Register.Category" ng-disabled="Register.Presentstatus" ng-clicked="Register.Presentstatus && O" ></td>
</tr>
my module that gets my data
function Register(){ self.RegisterDetails = function () {
var params = { pass params here };
return $http.get
{
url: GetRegisterDetails,
params: params,
success: function (data) {
self.RegisterDetails = data.data;
}
});
}
my controller
ngAppModule.controller('RegisterController',['$scope','$http',function($scope,$http)
{
var self = this;
$scope.Register = new Register($http);
}]);
all the above code works fine. i just dont know how to check a single box per row.sorry im new to this site
The main problem you're running into is you are binding to the wrong this inside your ng-repeat.
Your HTML currently repeats a checkbox for every code, but binds that to the same object property Register.Presentstatus.
<tr id="TableBody" ng-repeat="code in Register.RegisterDetails">
<td>{{$index+1}}</td>
....
<td><input type="checkbox" ng-model="Register.Presentstatus" .../></td>
</tr>
You'll need to bind this to a row-specific (code-specific) property if you want each row to have independent check boxes. Perhaps you are looking for something that binds the checkbox to an element in an array:
<tr id="TableBody" ng-repeat="code in Register.RegisterDetails">
<td>{{$index+1}}</td>
....
<td><input type="checkbox" ng-model="Register.Presentstatus[$index]" .../></td>
</tr>
or actually binds to a property of the code object
<tr id="TableBody" ng-repeat="code in Register.RegisterDetails">
<td>{{$index+1}}</td>
....
<td><input type="checkbox" ng-model="code.Presentstatus" .../></td>
</tr>
Since youur ng-model="Register.Presentstatus" is repeating and is same for all so you have same binding for all rows. you can alter them to have different binding