I have a tr :
<tr ng-hide="func(item.status)" ng-repeat="item in itemArray" >
<td></td>
.
.
</tr>
In the Func() the item.status is compared with the value of a dropdown D which can be changed by the user at any time.
I have a div which i need to show if the current number of visible tr == 0.
I am storing the number of visible tr as
$scope.qVisible = $("#tableid tbody tr:visible").length;
How can i have the qVisible be updated only once all the ng-hide statements have been executed to get the correct result?
I am going to assume that you have a dropdown somewhere that looks something like..
<select ng-model="selectedStatus" ng-options="status for status in statuses"></select>
So your ng-hide can do something like
<tr ng-hide="item.status !== selectedStatus" ng-repeat="item in itemArray" >
<td></td>
.
.
</tr>
In your controller, you need to setup a watch on the selectedStatus..
$scope.qVisible = false;
$scope.$watch("selectedStatus",function(newVal){
//for loop over items.. counting the number of items that match selectedStatus
// if count is 0, set a scope variable
$scope.qVisible = true;
});
In your html..
<div ng-show="qVisible">...</div>
This is the way to do it without touching the DOM.
You can have $watch that listens to the same as data-ng-repeat runs the same function as hide:
function update(newValue) {
var i;
$scope.numberOfShownItems = 0;
for (i = 0; i < newValue.length; i += 1) {
if ($scope.func(newValue[i])) {
$scope.numberOfShownItems += 1;
}
}
$scope.qVisible = !!$scope.numberOfShownItems;
}
$scope.items = [1,2,3,4,5,6];
$scope.func = function (item) {
if (item % 2 === 0) {
return true;
}
return false;
}
$scope.$watch('items', update);
Example:
http://jsfiddle.net/EgZak/
Related
I use the following form and script to let users filter a td table on the input they give in. It filters the rows of the table and only shows the rows corresponding to their given value. They can update the rows that they are seeing, after they do this the page refreshes/reloads to refresh the table. After the page is refreshed/reloaded the search filter shows all rows again. I am searching for a way to keep the rows that they had before the update event happend based on their filter input. In other words, as if the refresh never happend.
Search form;
...
<p align='left' style="display:inline">
<table class="userprof" align='left'>
<tr>
<td class="footer">Filter:
<input type="text" id="myInput" name="filter" style="color:black !important;" placeholder="Filter table" onkeyup='saveValue(this);' />
</td>
</tr>
</table>
</p>
...
I use the folowing script to save their input as localstorage.
...
document.getElementById("myInput").value = getSavedValue("myInput"); // set the value to this input
/* Here you can add more inputs to set value. if it's saved */
//Save the value function - save it to localStorage as (ID, VALUE)
function saveValue(e) {
var id = e.id; // get the sender's id to save it .
var val = e.value; // get the value.
localStorage.setItem(id, val); // Every time user writing something, the localStorage's value will override .
}
//get the saved value function - return the value of "v" from localStorage.
function getSavedValue(v) {
if (!localStorage.getItem(v)) {
return ""; // You can change this to your defualt value.
}
return localStorage.getItem(v);
}
...
I use the following script to filter the table rows
...
function filterTable(event) {
var filter = event.target.value.toUpperCase();
var rows = document.querySelector("#myTable tbody").rows;
for (var i = 0; i < rows.length; i++) {
var nameCol = rows[i].cells[1].textContent.toUpperCase();
var rankCol = rows[i].cells[2].textContent.toUpperCase();
var rankerCol = rows[i].cells[5].textContent.toUpperCase();
var typeCol = rows[i].cells[6].textContent.toUpperCase();
var emailCol = rows[i].cells[3].textContent.toUpperCase();
if (nameCol.indexOf(filter) > -1 || rankCol.indexOf(filter) > -1 || rankerCol.indexOf(filter) > -1 || typeCol.indexOf(filter) > -1 || emailCol.indexOf(filter) > -1) {
rows[i].style.display = "";
} else {
rows[i].style.display = "none";
}
}
}
document.querySelector('#myInput').addEventListener('keyup', filterTable, false);
...
You are almost there and only need minor modifications to make this happen.
I'd suggest that you change your flow up a bit.
First remove the onkeyup inline listener from your HTML. You are currently listening for that event 3 times on 1 element which seems overkill.
...
<p align='left' style="display:inline">
<table class="userprof" align='left'>
<tr>
<td class="footer">Filter:
<input type="text" id="myInput" name="filter" style="color:black !important;" placeholder="Filter table" />
</td>
</tr>
</table>
</p>
...
Then modify the filterTable to accept just a value, not an event object. This way you can call filterTable at any time and inject a value into it. And it allows you to call it immediately with the stored value when the page loads so that your initial filter will be set (or not if there is nothing stored).
Now listen for the keyup event with only a single listener which will both pass the value of the event to filterTable and the event itself to saveValue so that are both filtering and saving.
// Store the input in a variable for reference.
var myInput = document.getElementById("myInput");
var savedValue = getSavedValue("myInput");
// Immediately filter the table and set the input value.
filterTable(savedValue);
myInput.value = savedValue;
//Save the value function - save it to localStorage as (ID, VALUE)
function saveValue(e) {
var id = e.id; // get the sender's id to save it .
var val = e.value; // get the value.
localStorage.setItem(id, val); // Every time user writing something, the localStorage's value will override .
}
//get the saved value function - return the value of "v" from localStorage.
function getSavedValue(v) {
if (!localStorage.getItem(v)) {
return ""; // You can change this to your default value.
}
return localStorage.getItem(v);
}
function filterTable(value) {
console.log(value);
var filter = value.toUpperCase();
var rows = document.querySelector("#myTable tbody").rows;
for (var i = 0; i < rows.length; i++) {
var nameCol = rows[i].cells[1].textContent.toUpperCase();
var rankCol = rows[i].cells[2].textContent.toUpperCase();
var rankerCol = rows[i].cells[5].textContent.toUpperCase();
var typeCol = rows[i].cells[6].textContent.toUpperCase();
var emailCol = rows[i].cells[3].textContent.toUpperCase();
if (nameCol.indexOf(filter) > -1 || rankCol.indexOf(filter) > -1 || rankerCol.indexOf(filter) > -1 || typeCol.indexOf(filter) > -1 || emailCol.indexOf(filter) > -1) {
rows[i].style.display = "";
} else {
rows[i].style.display = "none";
}
}
}
myInput.addEventListener('keyup', function(event) {
var value = event.target.value;
saveValue(event);
filterTable(value);
});
I got a table column with selects and text value cells like this:
<tr>
<td data-key="data1">some text data</td>
</tr>
<tr>
<td data-key="data2">
<select>
<option>1_option</option>
<option>2_option</option>
</select>
</td>
</tr>
I need to grab the data depending on the type of data in the cell. I do it like this:
var obj = $('#myTable tbody tr').map(function() {
var $row = $(this);
var localobj = {};
var cell = $row.find(':nth-child(1)');
dataattr = cell[0].getAttribute('data-key');
var selectObject = cell.find("select");
console.log(selectObject);
if(selectObject){ // always true here, but I need false if there is no select in the cell
localobj[dataattr] = selectObject.val();
}else{
localobj[dataattr] = cell.text();
}
return localobj;
}).get();
It grabs selected values correctly but cannot get the text ones because it always returns true in my if evaluation. Any ideas how to fix it?
Thank you
jQuery wraps everything in it's own object container and therefore selectObject will always evaluate to true as it is an object that is not undefined or null.
You can simply check to make sure the object has at least 1 element via
if (selectObject.length > 0) { ... }
try like this
var tbl = $('#tblHours');
tbl.find('tr').each(function(){
$(this).find('td').each(function(){
alert($(this).find('select :selected').val());
});
});
As
As explained by #Arvind Audacious, jQuery always returns a container. You cannot assume the result of the query is NULL. Instead, you need to check its length in order to verify if it has actually retrieved any elements. See code below for example:
$('#myTable tbody tr td').each(function(){
var selectObject = $(this).find('select');
if(selectObject.length == 0) {
console.log($(this).text())
} else {
console.log(selectObject);
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Checking jQuery selector object won't work, as it will be always true. Checking the length of the selector return is the best approach for this. Please check the fiddle - https://jsfiddle.net/nsjithin/r43dqqdy/1/
var obj = $('#myTable tbody tr').map(function() {
var $row = $(this);
var localobj = {};
var td = $row.find('td').first();
var dataattr = td.attr('data-key');
var select = td.find('select');
if(select.length > 0){
console.log(select);
if(select.find('option:selected').length > 0){
localobj[dataattr] = select.val();
}
else{
// If not selected. What to do here??
}
}
else{
localobj[dataattr] = td.text();
}
return localobj;
}).get();
console.log(obj);
In my form I have a table with checkbox in all of these columns. I have 3 <tr> and each <tr> has its ng-repeate calling the webservice to display the clones (Json data).
When I click on a checkbox I generate a js array which records id using this code :
checkoptions (array, model) {
angular.forEach(array, (value, key) => {
if (array[key].checked) {
model.push(array[key].id)
}
})
And in HTML :
<tr ng-repeat="developer in $ctrl.developers">
<td>{{developer.label}}</td>
<td>
<input type="checkbox" id="{{developer.id}}"
ng-change="$ctrl.checkoptions($ctrl.developers,$ctrl.employees.developers)"
ng-model="developer.checked">
<label for="{{developer.id}}"></label>
</td>
It works, but the problem is that when I uncheck a checkbox it is not removed from the js array
I included an else part to remove from the array:
http://jsfiddle.net/x9m1nqvp/1/
$scope.checkoptions = function (array, model) {
angular.forEach(array, (value, key) => {
if (array[key].checked) {
var index = model.indexOf(array[key].id);
if(index == -1)
model.push(array[key].id)
}
else {
var index = model.indexOf(array[key].id);
if(index >=0)
model.splice(index, 1);
}
})
While Everton's answer gets the job done, it is a bit redundant checking every item in the array, every time a single checkbox changes state. You don't really have to update for every single item in the array.
Here is an example, where only the checkbox that's actually toggled, is added or removed from the employees.developers array (Note: no need for the redundant angular.forEach):
$scope.checkoption = function (developer){
if (developer.checked) {
var index = $scope.employees.developers.indexOf(developer.id);
if(index == -1)
$scope.employees.developers.push(developer.id)
} else {
var index = $scope.employees.developers.indexOf(developer.id);
if(index >=0)
$scope.employees.developers.splice(index, 1);
}
}
and how this is used in the html:
<tr ng-repeat="developer in developers">
<td>{{developer.label}}</td>
<td>
<input type="checkbox" id="{{developer.id}}"
ng-change="checkoption(developer)"
ng-model="developer.checked">
<label for="{{developer.id}}"></label>
</td>
</tr>
I have a MVC web app in which I show a table.
Some of my rows can have a similar id, on which I need to show only one checkbox for all those rows, and individual checkboxes for the rows which don't have a matching id. Something like below:
row1 and row2 have the same id, hence the checkbox is in between them (denoted by red checkbox).
row3, row4 have different ids, hence they need to have their individual checkboxes (denoted by green).
I know I need to play on the rowspan property, but I am unable to visualize how to get on it.
Below is the sample code:
[Route("Search")]
[HttpGet]
public async Task<IActionResult> Search()
{
//Some API call
return View("Search", model);
}
View Code:
<table id="tblsearch">
#if (Model.HasRecords)
{
var counter = 0;
<tbody>
#foreach (var item in Model.SearchResults)
{
<tr>
<td>
<input type="checkbox" id="Dummy_#counter" name="chkSearch" data-id="#item.Id"/>
<label for="Dummy_#counter"></label>
</td>
<td>#item.FullAddress</td>
<td>#item.Price</td>
<td>#item.OfficeName</td>
}
else
{
<tr><td>Data Not Found</td></tr>
}
</table>
I am trying to first hide all the checkboxes, then trying to match the id's in each row, and then if the ids of 2 rows are same, I am trying to increase the rowspan by 2.
js code:
function pageLoad()
{
var rowCount = $('#tblSearch >tbody >tr').length;
for(var i=0;i<rowCount-1;i++)
{
$('#Dummy_' + i).hide();
}
var searchArray= [];
for (var i = 0; i < rowCount - 1; i++) {
searchArray[i]= $('#tblSearch >tbody >tr')[i].attr('data-id');
}
}
Please guide how to proceed.
You should control the layout of the page in this instance from your View, please forgive my syntax as I primarily work in vbhtml these days.
Important things are to order your search results (in case they aren't already)
Remember and update the last processed Id.
<table id="tblsearch">
#if (Model.HasRecords)
{
var counter = 0;
var lastId = -1;
<tbody>
#foreach (var item in Model.SearchResults.OrderBy(x=>x.Id))
{
<tr>
#if(lastId!= item.Id){
<td rowspan="#(Model.SearchResults.Count(x=>x.Id == item.Id) > 0 ? Model.SearchResults.Count(x=>x.Id == item.Id) : 1 )">
<input type="checkbox" id="Dummy_#counter" name="chkSearch" data-id="#item.Id"/>
<label for="Dummy_#counter"></label>
</td>
}
<td>#item.FullAddress</td>
<td>#item.Price</td>
<td>#item.OfficeName</td>
#lastId = item.Id;
//I assume there was code snipped here...
}
else
{
<tr><td>Data Not Found</td></tr>
}
</table>
There is no need for any javascript. You can simply group your items by the Id property and conditionally render the checkbox column with a rowspan attribute if its the first item in the group.
<tbody>
#foreach (var group in Model.SearchResults.GroupBy(x => x.Id))
{
bool isFirstRow = true;
foreach (var item in group)
{
<tr>
#if (isFirstRow)
{
<td rowspan="#group.Count()">
#Html.CheckBox("chkSearch")
</td>
isFirstRow = false;
}
<td>#item.FullAddress</td>
<td>#item.Price</td>
<td>#item.OfficeName</td>
</tr>
}
}
</tbody>
How can I pass arguments to a function that is assigned to variable.
For example:
var updateDiv = function() {
var row = this;
var values = "";
$('input[type=text],textarea,input[type=radio]:checked,input[type=checkbox]:checked', this).each(function() {
if ($(this).val()!="" && $(this).val()!=null) {
if (values!="") values = values + ","+ $(this).val();
else values += $(this).val();
}
});
if (values!="") {
if(values.substring(0,1)==",") values = values.substring(1) +"<br>";
else values = values +"<br>";
}
$('.jist', row).append(values);
}
$('tr:has(input)').each(updateDiv);
$('tr:has(textarea)').each(updateDiv);
HTML:
<tr>
<td>ai</td><td> <input type="text" name="ai" id="ai"></td>
<td><input type="checkbox" name="ana" id="ana" value="N/A"></td>
<td><div class="jist"></div></td>
</tr>
I want to pass arguments to updateDiv -> updateDiv("mystring");
and I want to use "mystring" in the function this way - > $('.'+mystring, row).append(values);
Simple and Clean
Not sure how I missed the obvious here.
jQuery
var updateDiv = function(divClass) {
...
$(divClass, row).append(values);
}
$('tr:has(input)').each(function(){ updateDiv('.hist'); });
$('tr:has(textarea)').each(function(){ updateDiv('.something-else'); });
.
Global Variable Method
You could assign global variables with the class name. By defining the variable before each .each() the updateDiv function uses a different class name.
jQuery
var updateDiv = function() {
...
$(window.divClass, row).append(values);
}
window.divClass = '.hist';
$('tr:has(input)').each(updateDiv);
window.divClass = '.something-else';
$('tr:has(textarea)').each(updateDiv);
.
HTML5 Data Method
You could assign values as data objects to the elements which are being called. I also cleaned up some of your selectors and jQuery redundancies.
Fiddle: http://jsfiddle.net/iambriansreed/KWCdn/
HTML
<table>
<tr data-update=".hist">
<td>AutoI</td>
<td> <input type="text" name="autoIH_complaint" id="autoIH_complaint"></td>
<td><input class="NA" type="checkbox" name="autoINA" id="autoINA" value="N/A"></td>
<td><div class="hist"></div></td>
</tr>
</table>
jQuery
var updateDiv = function() {
var row = this, values = "";
$('input:text,textarea,:radio:checked,:checkbox:checked', this).each(function() {
if (this.value != "" && this.value != null) {
if (values != "") values = values + "," + this.value;
else values += this.value;
}
});
if (values != "") {
if (values.substring(0, 1) == ",") values = values.substring(1) + "<br>";
else values = values + "<br>";
}
$(row.data('update'), row).append(values);
}
$('tr:has(input)').each(updateDiv);
$('tr:has(textarea)').each(updateDiv);
We can pass the index into the function, much like jQuery's .each() method.
<div class="test">1</div>
<div class="test">2</div>
This will alert "0" and then "1" for the indexes.
var updateDiv = function( index )
{
alert( index );
}
$('.test').each(updateDiv);
If you pass in strings as parameters, .each(updateDiv("string1")) it is evaluates the function first.