How to prevent $event fo fire twice - javascript

I'm creating a table component in AngularJS but i'm having a bug that i can't solve.
The structure of my table is something like:
<table>
<thead>...</thead>
<tbody>
<tr ng-click="selectRow()" ng-repeat="$value in values track by $index" ng-click="selectRow($index,$value)>
<td><input type="checkbox" ng-model="$value.__checked" ng-click="selectRow($index,$value)"/></td>
</tr>
</tbody>
</table>
When i click on the row, it works fine, but when i click on the checkbox it doesn't work as expected, and fire the event twice. I thought that just using the stopPropagation() on selectRow when it's fired by the checkbox would work, but it doesn't.
The function selectRow can work for multi and single seleciton.
function selectRow(ngRepeatIndex,ngRepeatValue){
var selectedValues = $scope.$parent.selectedValues;
cleanValueAndArrays(vm.checkAll,vm.checkAll);
if($attrs.onClick)vm.onClick({value: ngRepeatValue});
if(vm.config.selection == 'single'){
if(ngRepeatValue.__checked){
ngRepeatValue.__checked = false;
cleanArrays();
} else {
cleanValueAndArrays(vm.selectedIndexes.length > 0)
pushToArrays(ngRepeatValue,ngRepeatIndex);
ngRepeatValue.__checked = true;
}
} else {
ngRepeatValue.__checked = vm.selectedIndexes.filter(function(val){return val == ngRepeatIndex}).length < 1;
if((ngRepeatValue.__checked) || vm.selectedIndexes.length == 0 ){
pushToArrays(ngRepeatValue,ngRepeatIndex);
return 0;
}
var indexOfValueSelected;
selectedValues.forEach(function(val,indx){
if(angular.equals(val,ngRepeatValue)) indexOfValueSelected = indx;
})
$scope.$parent.selectedValues.splice(indexOfValueSelected, 1);
vm.selectedIndexes.splice(vm.selectedIndexes.indexOf(ngRepeatIndex),1);
}
}

Remove the second ng-click, it is useless, the ng-click on the row will be called if you click on your checkbox.

Create a directive to stop event propagation.
moduleName.directive('preventDefault', function () {
return function (scope, element, attrs) {
$(element).click(function (event) {
event.preventDefault();
});
}
});
You can use it as below:
<table>
<thead>...</thead>
<tbody>
<tr ng-repeat="$value in values track by $index" ng-click="selectRow($index,$value) prevent-default>
<td><input type="checkbox" ng-model="$value.__checked"/></td>
</tr>
</tbody>
</table>

try this :
<table>
<thead>...</thead>
<tbody>
<tr ng-click="selectRow()" ng-repeat="$value in values track by $index" ng-click="selectRow($index,$value)>
<td><input type="checkbox" ng-model="$value.__checked" ng-click="selectRow($index,$value); $event.stopPropagation();"/></td>
</tr>
</tbody>
</table>

Related

javascript validation of dynamic checkbox

Name and id of checkboxes are appearing dynamically. And we are not sure of number of checkboxes. A map is iterated to generate checkboxes contained in a table. So, each table will have a checkbox with 2-3 options depending on map entries. Javascript should validate that at least one entry of checkbox in a table should be checked.
HashMap<ServerGroup, ArrayList<String>> serversMap = (HashMap<ServerGroup, ArrayList<String>>) session.getAttribute("userServersMap");
Iterator itr = serversMap.entrySet().iterator();
while (itr.hasNext()) {
Map.Entry<ServerGroup, ArrayList<String>> entry = (Map.Entry<ServerGroup, ArrayList<String>>)itr.next();
<table border='1' align="center">
<tr>
<th>Select Server</th>
<th>Servers</th>
</tr>
<%
for (String server : entry.getValue()) {
%>
<tr>
<td><input type="checkbox" name="<%= entry.getKey().getName().toString() %>" value="<%=server%>"></td>
<td><%=server%></td>
</tr>
<%
}
%>
</table>
Try this with JS:
function checkOneCheckbox(){
var checkboxes=document.getElementsByName("your_checkbox_name"); //all should be same
var is_checked=false;
for(var i=0;i<checkboxes.length;i++)
{
if(checkboxs[i].checked)
{
is_checked=true;
}
}
if(is_checked){
//at least one is checked;
}else {
//...
}
}
More easy with jQuery:
// onready method...binding
var is_checked = false;
$('input[type="checkbox"]').each(function() {
if ($(this).is(":checked")) {
is_checked = true;
}
});
The following solution should hopefully work in this case.
Let's assume that your JSP generated the following html page
(say two html tables, it can generate any number of tables though for your case):
<table border='1' align="center">
<tr>
<th>Select Server</th>
<th>Servers</th>
</tr>
<tr>
<td><input type="checkbox" name="serverX" value="TestServer1"></td>
<td>TestServer1</td>
</tr>
<tr>
<td><input type="checkbox" name="serverY" value="TestServer2"></td>
<td>TestServer2</td>
</tr>
</table>
<table border='1' align="center">
<tr>
<th>Select Server</th>
<th>Servers</th>
</tr>
<tr>
<td><input type="checkbox" name="serverM" value="TestServer3"></td>
<td>TestServer3</td>
</tr>
<tr>
<td><input type="checkbox" name="serverN" value="TestServer4"></td>
<td>TestServer4</td>
</tr>
<tr>
<td><input type="checkbox" name="serverO" value="TestServer5"></td>
<td>TestServer5</td>
</tr>
</table>
Then on form submit you can call a Validate function which will iterate through all the tables on your page and if it finds a table without any checked checkbox it will return false and simply focus on the first checkbox of the table.
The Validate function will be :
function Validate()
{
var table = document.getElementsByTagName('table'); // get all the tables on the page
for(var i=0; i<table.length;i++) //loop through each table
{
var flag = false;
for( var j=1; j<table[i].rows.length;j++) // start checking from 2nd row of the table
{
var currentRow = table[i].rows[j];
var cell = currentRow.cells[0];
var elem = cell.getElementsByTagName("input");
if (elem[0].type == "checkbox")
{
if(elem[0].checked)
{
flag=true;
break; // stop checking this table as one checked found
}
}
}
if(j==table[i].rows.length && flag == false)
{
alert("Please select at least one checkbox!");
table[i].rows[1].cells[0].getElementsByTagName("input")[0].focus();//focus on the first checkbox in the table
return false;
}
}
}
Hope it helps!
Working Demo:
http://jsfiddle.net/6cyf4skd/

Unmark checkbox when there is no more TR and TABLE goes hide

I'm working on this function for remove any marked checkbox in a selector container element. The code works fine but has a small problem: I'm not able to uncheck the first checkbox (the one that toggle all the checkboxes in a selector2 container). Now this is the code for remove the checked checkboxes:
function eliminarMarcados(selector, toggleMsg, msgSelector) {
$(selector + " input[type='checkbox']:checked").closest("tr").not('.tableHead').remove();
if (toggleMsg) {
if ($(selector + " tbody tr").length == 0) {
$(msgSelector).show();
$(selector).hide();
// 1st test didn't work since it's not right
//$(selector + " tr").hasClass('tableHead').$(selector + " input[type='checkbox']").prop('checked', false);
}
}
}
And this is how I call it:
$("#btnEliminarNorma").on('click', function () {
eliminarMarcados("#tablaNorma", true, "#alertSinNorma");
});
This is the code I'm using for toggle all checkboxes checked:
function marcarTodosCheck(selChk, tableBody) {
$(selChk).on('click', function () {
var $toggle = $(this).is(':checked');
$(tableBody).find("input:checkbox").prop("checked", $toggle).trigger("change");
});
$(tableBody).find("input:checkbox").on('click', function () {
if (!$(this).is(':checked')) {
$(selChk).prop("checked", false).trigger("change");
} else if ($(tableBody).find("input:checkbox").length == $(tableBody).find("input:checkbox:checked").length) {
$(selChk).prop("checked", true).trigger("change");
}
});
}
And I call it as follow:
marcarTodosCheck("#toggleCheckNorma", "#tablaNorma");
And this is the HTML code behind this:
<table class="table table-condensed" id="tablaNorma">
<thead>
<tr class="tableHead">
<th><input type="checkbox" id="toggleCheckNorma" name="toggleCheckNorma"></th>
<th>Nro.</th>
<th>Norma COVENIN</th>
<th>Año de Publicación</th>
<th>Comité Técnico</th>
</tr>
</thead>
<tbody id="normaBody">
<tr class="">
<td><input type="checkbox" value="5"></td>
<td>382</td><td>Sit alias sit.</td>
<td>1970</td><td>Velit eum.</td>
</tr>
<tr class="">
<td><input type="checkbox" value="6"></td>
<td>38362</td>
<td>Et voluptatem.</td><td>1976</td>
<td>Et voluptatem.</td>
</tr>
</tbody>
</table>
How I can unmark the first checkbox?
I've just made a Fiddle with an additional <button id="btnEliminarNorma">Uncheck</button> to remove the checkmarks and the adjustment of your first approach in the function eliminarMarcados() :
$(selector).find(" input[type='checkbox']").prop('checked', false);

how can I prevent Nested tables inside ng-repeat from populating in every row?

OK, so I want to create a dynamic nested hierarchy of tables. I get they data no problem, but using ng-repeat at each level causes the parent table to insert child data for a specific row into each row of the parent table. I want to prevent this, I have tried using ng-repeat-start and ng-repeat-end, however, because of the table nesting I cannot add the end tag in an appropriate place to stop the repeat.
UPDATE
Let me clarify a bit here, I have 3 nested tables, the top level table has list of groups, the first child table is a list of all of the items that belong to a specific group in the parent table. When the user clicks the expand button, I populate the child table based on which row in the parent table was clicked, this is OK, however the child table now shows up in each of the parent table rows instead of just the row that was clicked.
Plunker Link
http://plnkr.co/edit/RVOnf9wBF3TzXauzvMfF
HTML:
<table id="sicGroupTable" class="RadGrid_Metro">
<thead>
<tr>
<td>Sic Code Group</td>
</tr>
</thead>
<tbody ng-repeat="group in sicCodeGroup">
<tr class="rgRow">
<td class="rgExpandCol"><img class="rgExpand" ng-click="expandRow(group.GroupId)" ng-model="hideTwoDigitSub" /></td>
<td><input type="checkbox" ng-model="SelectGroup" /></td>
<td>{{group.GroupName}}</td>
</tr>
<tr ng-hide="hideTwoDigitSub">
<td></td>
<td>
<table id="sic2DigitTable" class="RadGrid_Metro">
<thead>
<tr>
<th>2 digit sic Code</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="twoDigitSic in twoDigitSicCodes" class="rgRow">
<td class="rgExpandCol"><img class="rgExpand" ng-click="expandRow(twoDigitSic.SicCode)" /></td>
<td><input type="checkbox" ng-model="Select2DigitSicCode" /></td>
<td>{{twoDigitSic.SICCode2}} - {{twoDigitSic.Title}}</td>
</tr>
<tr>
<td></td>
<td>
<table id="sic4DigitTable" class="RadGrid_Metro">
<thead>
<tr>
<th>4 digit sic code</th>
</tr>
</thead>
<tbody>
<tr class="rgRow">
<td class="rgExpandCol"><img class="rgExpand" ng-click="expandRow(sicCode.SicCode)" /></td>
<td><input type="checkbox" ng-model="Select2DigitSicCode" /></td>
<td>{{sicCode.SicCode}} - {{sicCode.Title}}</td>
</tr>
<tr>
<td></td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
JavaScript:
var app = angular.module("apptSetting", ['ngResource'])
app.factory('dataFactory',
function ($resource) {
return {
getSicGroups: $resource('../Packages/GetJsonSicCodeGroups'),
expandRow: $resource('../Packages/GetExpandedRowData')
}
});
app.controller('aPackageController', ['$scope', 'dataFactory', function ($scope, dataFactory) {
function init() {
$scope.hideTwoDigitSub = true;
$scope.hideFourdigitsub = true;
}
$scope.sicCodeGroup = dataFactory.getSicGroups.query({}, isArray = true);
$scope.twoDigitSicCodes = null;
$scope.expandRow = function (groupId, sicCode) {
if (groupId != undefined)
{
$scope.twoDigitSicCodes = dataFactory.expandRow.query({ GroupId: groupId }, isArray = true);
$scope.hideTwoDigitSub = false;
if (sicCode != null && sicCode != undefined && sicCode != "") {
if (sicCode.length == 2) {
$scope.hideTwoDigitSub = false;
$scope.twoDigitSicCodes = dataFactory.Get2DigitSicCodes.query({ GroupId: groupId }, isArray = true);
}
}
}
}
init();
}])
The issue is that you're using a single boolean hideTwoDigitSub to control all the trs created by your ngRepeat:
<tr ng-hide="hideTwoDigitSub">
So when you set $scope.hideTwoDigitSub = false; every ngHide within your ngRepeat gets that false and thus all the tr elements are shown.
Radio Button Fix
Instead of using a boolean I'd set hideTwoDigitSub to the groupId for the row you want to show (and maybe rename hideTwoDigitSub to showTwoDigitSub since the variable now indicates which row to show).
So inside your expandRow() function I'd set which row to show by changing:
$scope.hideTwoDigitSub = false;
to
$scope.hideTwoDigitSub = groupId;
And change the above tr to:
<tr ng-hide="hideTwoDigitSub != group.GroupId">
So the row will be hidden unless your control variable hideTwoDigitSub is not equal to the current groups GroupId.
Or it might be clearer to use ngShow (note I changed the hideTwoDigitSub to showTwoDigitSub in this example since it's clearer):
<tr ng-show="showTwoDigitSub == group.GroupId">
radio button plunker
Checkbox solution
This approach is easiest done switching hideTwoDigitSub to showTwoDigitSub- so everything below assumes that.
For this approach, inside init() I'd set your control variable to be an array:
$scope.showTwoDigitSub=[];
And then toggle the appropriate control inside expand:
$scope.showTwoDigitSub[groupId] = !$scope.showTwoDigitSub[groupId];
And use the array inside your html:
<tr ng-show="showTwoDigitSub[group.GroupId]">
checkbox plunker

getting the row values with checkbox

hello guys? can you please help with this? i have this tables in HTML.what i want to achieve is that, when i click the row the checkbox will be checked and the row will be highlighted.and is it possible with the checkbox column hidden?
<table border="1" id="estTable">
<thead>
<tr>
<th></th>
<th>Name</th>
<th>Age</th>
</tr>
</thead>
<tbody>
<tr>
<td><input type="checkbox"></td>
<td>Chris</td>
<td>10</td>
</tr>
<tr>
<td><input type="checkbox"></td>
<td>Cass</td>
<td>15</td>
</tr>
<tr>
<td><input type="checkbox"></td>
<td>Aldrin</td>
<td>16</td>
</tr>
</tbody>
</table>
<input type="button" value="Edit" id="editbtn"/>
<div id="out"></div>
and i have this javascript to get the values of the selected row.And i was hoping to print one row at a time.
$('#editbtn').click(function(){
$('#estTable tr').filter(':has(:checkbox:checked)').find('td').each(function() {
$('#out').append("<p>"+$(this).text()+"</p>");
});
});
This gets a little easier when you use classes to add more context to your source:
<tr>
<td class="select hidden">
<input type="checkbox">
</td>
<td class="name">Chris</td>
<td class="age">10</td>
</tr>
Then you can do something like this:
$(document).ready(function () {
'use strict';
$('#estTable tbody tr').click(function (e) {
//when the row is clicked...
var self = $(this), //cache this
checkbox = self.find('.select > input[type=checkbox]'), //get the checkbox
isChecked = checkbox.prop('checked'); //and the current state
if (!isChecked) {
//about to be checked so clear all other selections
$('#estTable .select > input[type=checkbox]').prop('checked', false).parents('tr').removeClass('selected');
}
checkbox.prop('checked', !isChecked).parents('tr').addClass('selected'); //toggle current state
});
$('#editbtn').click(function (e) {
var selectedRow = $('#estTable .select :checked'),
tr = selectedRow.parents('tr'), //get the parent row
name = tr.find('.name').text(), //get the name
age = parseInt(tr.find('.age').text(), 10), //get the age and convert to int
p = $('<p />'); //create a p element
$('#out').append(p.clone().text(name + ': ' + age));
});
});
Live demo: http://jsfiddle.net/Lf9rf/
if i understand the "print one row at a time" correctly, i think you need to empty your "out" selector before executing the new call
$('#editbtn').click(function(){
$('#out').empty();
$('#estTable tr').filter(':has(:checkbox:checked)').find('td').each(function() {
$('#out').append("<p>"+$(this).text()+"</p>");
});
});
jsBin demo
CSS:
.highlight{
background:gold;
}
jQuery:
$('#estTable tr:gt(0)').click(function( e ){
var isChecked = $(this).find(':checkbox').is(':checked');
if(e.target.tagName !== 'INPUT'){
$(this).find(':checkbox').prop('checked', !isChecked);
}
$(this).toggleClass('highlight');
});

enable disable textbox inside a table when checkbox checked

I am using jQuery. How to enable or disable textbox inside a table when checkbox checked.
This is my edit.cshtml.
<table id="scDetails" class="dataTable">
<thead>
<tr>
<th>RItem</th>
<th>IChecked</th>
<th>Notes</th>
</tr>
</thead>
<tbody>
#foreach (var fback in Model.Fbacks)
{
<tr>
<td #Html.DisplayFor(m => fback.RItem)</td>
<td>#Html.CheckBoxFor(m => fback.IChecked)</td>
<td>#Html.TextBoxFor(m => fback.Notes)</td>
</tr>
}
</tbody>
</table>
What i have tried is:
$('td :checkbox').change(function () {
var parentTx = $(this).closest('tr').find(input[type = "text"]);
if (this.checked) {
parentTx.attr("disabled",false);
} else {
parentTx.attr("disabled", true);
}
});
Where am doing wrong? I dont want to use any classes on the controls to achieve this.
You missed the quotes, and you could simply your code with:
$('td input[type="checkbox"]').change(function () {
$(this).closest('tr').find('input[type="text"]').prop('disabled', !this.checked);
}).change();

Categories