Fiddle Example
I've a table in which each row has checkbox and another checkbox in to check-all rows (checkboxes) and send ID of selected/all row(s) as JSON object.
I've an object array from (GET) response (server-side pagination is enabled) and stored it in itemsList $scope variable.
Following is my code.
View
<table class="table">
<thead>
<tr>
<th><input type="checkbox" ng-model="allItemsSelected ng-change="selectAll()"></th>
<th>Date</th>
<th>ID</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in itemsList track by $index" ng-class="{selected: item.isChecked}">
<td>
<input type="checkbox" ng-model="item.isChecked" ng-change="selectItem(item)">
</td>
<td>{{item.date | date:'dd-MM-yyyy'}}</td>
<td>{{item.id}}</td>
</tr>
</tbody>
</table>
Controller
$scope.itemsList = [
{
id : 1,
date : '2019-04-04T07:50:56'
},
{
id : 2,
date : '2019-04-04T07:50:56'
},
{
id : 3,
date : '2019-04-04T07:50:56'
}
];
$scope.allItemsSelected = false;
$scope.selectedItems = [];
// This executes when entity in table is checked
$scope.selectItem = function (item) {
// If any entity is not checked, then uncheck the "allItemsSelected" checkbox
for (var i = 0; i < $scope.itemsList.length; i++) {
if (!this.isChecked) {
$scope.allItemsSelected = false;
// $scope.selectedItems.push($scope.itemsList[i].id);
$scope.selectedItems.push(item.id);
return
}
}
//If not the check the "allItemsSelected" checkbox
$scope.allItemsSelected = true;
};
// This executes when checkbox in table header is checked
$scope.selectAll = function() {
// Loop through all the entities and set their isChecked property
for (var i = 0; i < $scope.itemsList.length; i++) {
$scope.itemsList[i].isChecked = $scope.allItemsSelected;
$scope.selectedItems.push($scope.itemsList[i].id);
}
};
Below are the issues I'm facing...
If you check fiddle example than you can see that on checkAll() the array is updated with all available list. But if click again on checkAll() instead of remove list from array it again add another row on same object array.
Also i want to do same (add/remove from array) if click on any row's checkbox
If i manually check all checkboxes than the thead checkbox should also be checked.
I think that you are on the right path. I don't think is a good idea to have an array only for the selected items, instead you could use the isSelected property of the items. Here is a working fiddle: http://jsfiddle.net/MSclavi/95zvm8yc/2/.
If you have to send the selected items to the backend, you can filter the items if they are checked with
var selectedItems = $scope.itemsList.filter(function (item) {
return !item.isChecked;
});
Hope it helps
This will help you for one of the two doubts:
$scope.selectAll = function() {
if($scope.allItemsSelected){
for (var i = 0; i < $scope.itemsList.length; i++) {
$scope.itemsList[i].isChecked = $scope.allItemsSelected;
$scope.selectedItems.push($scope.itemsList[i].id);
}
}else{
for (var i = 0; i < $scope.itemsList.length; i++) {
scope.itemsList[i].isChecked = $scope.allItemsSelected;
}
$scope.selectedItems = [];
}
};
I'm looking for something to achieve solution to point 2.
ng-checked can be used but it is not good to use ng-checked with ng-model.
Related
I have the following vue template
<div v-for="(v, k, i) in subs" :key="index">
<table>
<thead>
<tr>
<th>Barang</th>
<th>Satuan</th>
<th>Jumlah</th>
</tr>
</thead>
<tbody>
<tr v-for="j in ranges[k]">
<td><Select2 :options="goods" #change="fill_units(i)" /></td>
<td><Select2 ref="units" :options="[]" /> </td>
<td></td>
</tr>
</tbody>
</table>
<button #click="ranges[k]++">Add row</button>
</div>
and the data
data(){
return {
ranges: {
makanan: 1,
minuman: 1,
dll: 1
}
subs: {
makanan: 'Makanan',
minuman: 'Minuman',
dll: 'Dan lain-lain'
}
}
}
what I want is, when first Select2 element change its value, this will trigger the next Select2 element to fill its options based on it's ref. the problem is, i is not the correct index for the next Select2, since there might be more row added by the user. I think temporary variable that can hold the total rows on each table can solve the problem, but I don't know how to make that variable inside the template.
equivalent in vanilaJS loop, look like this
let total_rows = 0;
for(let i = 0; i < 3; i++){
for(let j = 0; j < 5; j++){
total_rows++; // i need this variable
fill_units(total_rows);
}
}
I am so sorry if my question isn't clear enough
edit: add select2 code
fill_units(value, i){
const types = this.goods.filter(type => type.id == value);
if(types.length > 0){
const units = types[0].units;
// delete all previous selections
this.$refs.units[i].select2.empty();
// append new selections
this.$refs.units[i].select2.append(`<option value="">Pilih Unit</option>`);
units.forEach(unit => {
this.$refs.units[i].select2.append(`<option value="${unit.id}">${unit.name}</option>`);
})
// this.$refs.units[i].select2.trigger('change');
}
}
I'm setting up a form for user. I gather questions and options from server and wantto collect andswers in array then post it to controller and array should look like this :
[{"selection": "A", "question_id": "13"}, {"selection": "A", "question_id": "14"}]
I get values from controller and can alert selected radio buttons without array.
Here is my view :
#foreach (var item in Model)
{
<tr>
<td>#item.Order</td>
<td>#Html.Raw(item.Body)</td>
#{var opt = item.QuizesOption.Where(m => m.QuestionId==item.Id).ToList();}
#for (int i = 0; i < opt.Count(); i++)
{
<td width="200">
<input type="radio" class="mybox" value="selection:#opt[i].Order,question_id:#item.Id"><label>#opt[i].Body</label>
</td>
}
</tr>
}
Here is my simple Js
var selected = new Array();
$('#save_value').click(function() {
$('.mybox:checked').each(function() {
alert($(this).val());
});
});
So I can get value seperatly but I need to put it in array as shown format
Just use push in your current code:
var selected = new Array();
$("#save_value").click(function() {
$(".mybox:checked").each(function() {
selected.push($(this).val());
});
});
I have an html table in my view that I want to filter with multiple filters. In this case, I have 3 filters, but I can have much more.
Here is a little part of the code, to show the problem
$(document).ready(function () {
$('#datefilterfrom').on("change", filterRows);
$('#datefilterto').on("change", filterRows);
$('#projectfilter').on("change", filterProject);
$('#servicefilter').on("change", filterService);
});
function filterRows() {
var from = $('#datefilterfrom').val();
var to = $('#datefilterto').val();
if (!from && !to) { // no value for from and to
return;
}
from = from || '1970-01-01'; // default from to a old date if it is not set
to = to || '2999-12-31';
var dateFrom = moment(from);
var dateTo = moment(to);
$('#testTable tr').each(function (i, tr) {
var val = $(tr).find("td:nth-child(2)").text();
var dateVal = moment(val, "DD/MM/YYYY");
var visible = (dateVal.isBetween(dateFrom, dateTo, null, [])) ? "" : "none"; // [] for inclusive
$(tr).css('display', visible);
});
}
function filterProject() {
let dumb = this.options.selectedIndex;
dumb = this.options[dumb].innerHTML;
var filter, table, tr, td, i;
filter = dumb.toUpperCase();
table = document.getElementById("testTable");
tr = table.getElementsByTagName("tr");
for (i = 0; i < tr.length; i++) {
td = tr[i].getElementsByTagName("td")[2];
if (td) {
if (td.innerHTML.toUpperCase().indexOf(filter) > -1) {
tr[i].style.display = "table-row";
} else {
tr[i].style.display = "none";
}
}
}
}
function filterService() {
let dumb = this.options.selectedIndex;
dumb = this.options[dumb].innerHTML;
var filter, table, tr, td, i;
filter = dumb.toUpperCase();
table = document.getElementById("testTable");
tr = table.getElementsByTagName("tr");
for (i = 0; i < tr.length; i++) {
td = tr[i].getElementsByTagName("td")[3];
if (td) {
if (td.innerHTML.toUpperCase().indexOf(filter) > -1) {
tr[i].style.display = "table-row";
} else {
tr[i].style.display = "none";
}
}
}
}
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://rawgit.com/moment/moment/2.2.1/min/moment.min.js"></script>
<div class="row">
<div class="col-md-3">
<h4>Date from</h4>
<input type="date" class="form-control" id="datefilterfrom" data-date-split-input="true">
</div>
<div class="col-md-3">
<h4>Date to</h4>
<input type="date" class="form-control" id="datefilterto" data-date-split-input="true">
</div>
<div class="col-md-2">
<h4>Project</h4>
<select id="projectfilter" name="projectfilter" class="form-control"><option value="1">Test project</option><option value="2">Test2</option></select>
</div>
<div class="col-md-2">
<h4>Service</h4>
<select id="servicefilter" name="servicefilter" class="form-control"><option value="1">Test service</option><option value="2">Test2 service</option></select>
</div>
</div>
<table id="testTable" class="table">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Date</th>
<th scope="col">Project</th>
<th scope="col">Service</th>
</tr>
</thead>
<tbody id="report">
<tr>
<td class="proposalId">9</td><td> 17/07/2018</td> <td> Test project</td><td> Test service</td>
</tr>
<tr><td class="proposalId">8</td><td> 18/07/2018</td><td> Test project</td><td> Test2 service</td></tr>
<tr><td class="proposalId">7</td><td> 17/07/2018</td><td> Test2</td><td> Test2 service</td></tr>
<tr style=""><td class="proposalId">3</td><td> 19/07/2018</td><td> Test2</td><td> Test service</td></tr>
</tbody>
</table>
If you set filters like this
You will have this
This is not right. Because I need to have only test 2 project, so one row.
Where is my problem and How I can solve it?
Here is codepen for code
https://codepen.io/suhomlineugene/pen/pZqyEN
Right now you have a separate function for each of your filters, each of which ignores the settings from the other filters and overwrites their results.
Instead you'll need to combine those into a single function which takes all the filters into account.
Rather than literally combining all the code into one complex function, which would be difficult to maintain, one approach would be to have a single master function that makes all the rows visible, then calls each of the other filter functions in turn; those functions would only hide the rows they're filtering out. What's left visible at the end would be the rows that match all the filter selections.
$(document).ready(function () {
$('#datefilterfrom, #datefilterto, #projectfilter, #servicefilter').on("change", filterAll);
});
function filterAll() {
$('#testTable tr').show();
filterRows();
filterProject();
filterService();
// ...etc
}
function filterRows() { // repeat for filterProject(), filterService(), etc
// same as your original code, except only hide non-matching rows, do not
// show matching rows (because filterAll() already took care of that, and
// you don't want to undo what other filters may have hidden.)
}
(Alternatively, instead of showing everything and then having each individual filter hide rows incrementally, you could have filterAll() build an array of all the rows, pass it to the individual filter functions which will remove items from it, then use the end result to show/hide the appropriate rows in one go.)
Not going to rewrite this all for you but will give you a basic outline for the text only searches:
Create array of the filter data from the top inputs. By adding a data-col to each of those filter controls you can easily determine which column in table to match to
So the filters array would look something like:
[
{col:3, value:'test project'}
]
Then use jQuery filter() on the rows and use Array#every() on the filterValues array and look for the matching cell text using the column index from each filter object
var $rows = $('tbody#report tr')
// add a class `table-filter` to all the top filtering elements
var $filters = $('.table-filter').change(function() {
// create array of filter objects
var filterArr = $filters.filter(function() {
return this.value
}).map(function() {
var $el = $(this);
var value = $el.is('select') ? $el.find(':selected').text() : $el.val()
return {
col: $el.data('col'),
value: value.toLowerCase()
}
}).get();
if (!filterArr.length) {
// no filters show all rows
$rows.show()
} else {
// hide all then filter out the matching rows
$rows.hide().filter(function() {
var $row = $(this);
// match every filter to whole row
return filterArr.every(function(filterObj, i) {
var cellText = $row.find('td').eq(filterObj.col).text().trim().toLowerCase();
return cellText.includes(filterObj.value);
})
})
// show the matches
.show()
}
});
Working demo for the two text search fields
I am newbie to Protractor testing tool for AngularJS. I have following code that I want to test it using Protractor
HTML Part
<table>
<tr ng-repeat="row in rowcolumnsetup" id="{{$index}}">
<td ng-repeat="column in rowcolumnsetup" id="{{$index}}" ng-
style="getClass($parent.$index,$index)"></td>
</tr>
</table>
Javascript
var targetByRow = [[]];
//Setup Target Row
for(var row = 0 ; row < 7 ; row++) {
targetByRow[row] = [7];
for(var column = 0 ; column < 7 ; column++) {
targetByRow[row][column] = {
class : "",
Status : ""
};
}
}
$scope.rowcolumnsetup = targetByRow;
Now in spec.js in Protractor, I want to check whether, rowcolumnsetup has any value in class or status field. So in order to access rowcolumnsetup, I am able to get row but unable to figure out about how to retrieve column
element.all(by.repeater('row in rowcolumnsetup')).then(function(rows) {
rows.forEach(function(row) {
// Work in progress to retrieve column
});
});
var tableData = element.all(by.repeater('row in rowcolumnsetup')).map(function(row) {
var tds = row.all(by.css('td'));
return {
// give ng-style value of first td cell to class
class: tds.get(0).getAttribute('ng-style'),
// assume give the text of first td cell to status
status: tds.get(0).getText()
};
});
tableData.then(function(datas){
//datas[0].class ng-style value of first cell in first row
})
I'm new to knockout.js and I have some trouble with two-way binding of a checkbox.
I have a table that's bound to a list of "companies".
<table>
<thead>
<tr>
<th>...</th>
<th>...</th>
<th>...</th>
</tr>
</thead>
<tbody data-bind="foreach: companies">
<tr>
<td>...</td>
<td><input type="checkbox" data-bind="checked:isCompanyForCommunication, click:$root.checkedNewCompanyForCommunication" /></td>
<td>...</td>
</tr>
</tbody>
</table>
But there should only be 1 company with isCompanyForCommunication = true in the table. So when another checkbox is checked, I have to set all other companies to isCompanyForCommunication = false. Therefore I created a method in the viewModel to uncheck all other companies.
// Definition of Company
var Company = function (crmAccountObject, contactId, companyForCommunicationId) {
this.isCompanyForCommunication = ko.observable(false);
}
// Definition of the ViewModel
var ViewModel = function () {
var self = this;
// ...
// Lists
this.companies = null; // This observableArray is set somewhere else
// Events
this.checkedNewCompanyForCommunication = function (company, event) {
// Set all companies to False
for (var i = 0; i < self.companies().length; i++) {
self.companies()[i].isCompanyForCommunication = ko.observable(false);
}
// Set the "checked" company to TRUE
company.isCompanyForCommunication = ko.observable(true);
return true;
}
}
However, the changes are not reflected in the HTML page. All other checkboxes remain checked.
I created a simplyfied example in jsfiddle to show what exactly I want to achieve with the checkbox https://jsfiddle.net/htZfX/59/
Someone has any idea what I'm doing wrong? Thanks in advance!
You are not setting the value of the isCompanyForCommunication but overriding it with a new observable.
Observables are functions and to update them you need to call them with the new value as the argument:
this.checkedNewCompanyForCommunication = function (company, event) {
// Set all companies to False
for (var i = 0; i < self.companies().length; i++) {
self.companies()[i].isCompanyForCommunication(false);
}
// Set the "checked" company to TRUE
company.isCompanyForCommunication(true);
return true;
}
I've also updated your sample code: JSFiddle.