Angular indeterminate checkbox feature not working - javascript

abc.component.html:
<input class='alignedItem' type="checkbox" value="0" name={{cardProgram.id}}
[indeterminate] ="cardProgram.indeterminate" [checked] = "cardProgram.selected" [(ngModel)]='cardProgram.selected' [disabled]='(cardProgram.appId && !hasOtherAppsAssigned(cardProgram)) && (cardProgram.appId != getAppId())'
(click)='toggleCardProgram(cardProgram,cardProgram.selected)' />
<label class='fa col-1 showMore expand-indicator' [ngClass]="{'fa-plus': !rowIsExpanded(index),
'fa-minus': rowIsExpanded(index)}"
for='{{cardProgram.id}}'></label>
<input #hiddenAnimationInput class='h-0' type="checkbox" id='{{cardProgram.id}}'
(change)='rowClicked(index)' />
<!-- Hidden unless toggled -->
<div class='positionUp25'>
<ul>
<li *ngFor="let agentItem of cardProgram.agents">
<span class='col-12'>
<label class='offset-1TMP col-5'>
{{cardProgram.sys}}/{{cardProgram.prin}}/{{agentItem.agent}}
</label>
<label *ngIf=agentItem.application class='col-6' [ngClass]="{'text-disabled': agentItem.application && agentApp(agentItem) != getAppId()}">
{{agentItem.application.subAppName}}
</label>
<label *ngIf=!agentItem.application class='col-6'>
</label>
<input class='alignedItem ml-3' type="checkbox" name='{{cardProgram.sys}}{{cardProgram.prin}}{{agentItem.agent}}'
#{{cardProgram.sys}}{{cardProgram.prin}}{{agentItem.agent}}='ngModel'
[(ngModel)]='agentItem.selected' (click)='toggleAgent(agentItem, cardProgram)'
[disabled]='agentItem.application && agentApp(agentItem) != getAppId()' />
</span>
</li>
</ul>
</div>
cardPrograms.d.ts:
interface CardProgram {
.......
indeterminate:boolean;
checked:boolean;
......
}
indeterminate.directive.ts
import { Directive, ElementRef,Input } from '#angular/core';
#Directive({ selector: '[indeterminate]' })
export class IndeterminateDirective {
#Input()
set indeterminate(value)
{
this.elem.nativeElement.indeterminate=value;
}
constructor(private elem: ElementRef) {
}
}
abc.component.ts:
private toggleAgent(agent: Agent, cardProgram: CardProgram) {
debugger;
this.dirty = true;
console.log("selected" + agent.selected)
this.updateChangeValToObj(agent, cardProgram)
//agent.selected = !agent.selected;
if (agent.selected) {
agent.application = this.currentApplication();
} else {
agent.application = null;
// if we deselected, also unassign the app at the program level so it will reflect as 'Multiple Apps' state
cardProgram.application = null;
}
// this.togglecheckbox(cardProgram)
var x = 0;
cardProgram.agents.forEach(agent => {
if (agent.selected == true) {
++x;
console.log("inside agent.selected");
}
})
var length = cardProgram.agents.length;
if (x == 0) {
console.log("x is 0");
cardProgram.selected = false;
cardProgram.indeterminate =false;
}
else if (x == length) {
cardProgram.selected = true;
cardProgram.indeterminate =false;
}
else if (x > 0 && x < length) {
cardProgram.selected = false;
cardProgram.indeterminate =true;
}
else{
cardProgram.indeterminate =false;
cardProgram.selected = false;
}
}
When I select all the child check-boxes(agents), the parent should be checked and this works fine.
When all the agents are unchecked, and then I check one of the agents the indeterminate checkbox comes up which is fine.
Problem scenario: The problem comes when I check the parent checkbox which checks all the agents which is fine but when I uncheck one of the agents, that time indeterminate option should show which is not happening. It shows unchecked for parent.
In the abc.component.ts, I see the control goes into else if (x > 0 && x < length) and sets the cardProgram.indeterminate =true; but the indeterminate is not shown in the UI for the above mentioned problem scenario most of the times but strangely works sometimes.
edit: Ok i have come to know exactly how to recreate the issue: lets say there are three child check boxes (agents) and all are unchecked which makes the parent unchecked. Now when I check one of child (shows indeterminate) and then check the parent checkbox(checks all the agents) and uncheck one the child then indeterminate should come up which it does not

I have done something like this before, except it was done using a mat-table and mat-checkbox. But the core implementation remains the same.
I believe the requirement is to display a table or a list with checkboxes to select each row and a master checkbox to check/uncheck all of them, and also to display an indeterminate state when some of the rows are checked.
Something like this could be done using <mat-checkbox> which provides the same functionality as native <input type="checkbox">.
TS file
intialSelection: number[] = [];
allowMultiSelect = true;
selection = new SelectionModel<number>(this.allowMultiSelect, this.intialSelection);
// checks whether the number of selected elements is equal to the total number of elements in the list
isAllSelected() {
const numSelected = this.selection.selected.length;
const numRows = this.sourceData.length;
return numSelected == numRows;
}
// selects all rows if they are not selected; otherwise clears the selection
masterToggle(){
this.isAllSelected() ?
this.selection.clear() :
this.sourceData.forEach(row => this.selection.select(row.Id));
}
HTML File
<!-- The master checkbox -->
<mat-checkbox (change)="$event ? masterToggle() : null" [checked]="selection.hasValue() && isAllSelected()"
[indeterminate]="selection.hasValue() && !isAllSelected()">
<!-- Checkbox corresponding to individual rows -->
<mat-checkbox (click)="$event.stopPropagation()" (change)="$event ? selection.toggle(id) : null"
[checked]="selection.isSelected(id)">
Reference
https://material.angular.io/components/checkbox/overview

<input [indeterminate]="true" class="form-check-input" type="checkbox"/>

Related

Checkbox click function is not working angular 4

I have this data coming from another component
on the basis of active tag when row is clicked I am pushing Id to ngModel of checkbox input field.
Row click is working fine and checkbox is adding/removing data
but now when I click on checkbox itself it doesn't do anything like checkbox click function is not working
How can I solve that?
html component
<ngb-panel [disabled]="true" *ngFor="let testPanel of otherTests; let i = index;" id="{{testPanel.Id}}" [title]="testPanel.Name">
<ng-template ngbPanelTitle>
<div class="action-items">
<label class="custom-control custom-checkbox">
<input
type="checkbox"
class="custom-control-input"
[name]="testPanel.Id + '-' + testPanel.Moniker"
[ngModel]="panelIds.indexOf(testPanel.Id) > -1"
(ngModelChange)="onPanelCheckboxUpdate($event, testPanel)"
[id]="testPanel.Id + '-' + testPanel.Moniker">
<span class="custom-control-indicator"></span>
</label>
</div>
</ng-template>
</ngb-panel>
ts component
getting Id from service and push it on basis of row click
this.testOrderService.refreshRequestsObservable().subscribe(
data => {
this.panelActive = data.active;
let testFilteredArray = lodash.filter(this.otherTests, item => item.Id === data.id);
if (this.panelActive) {
// is checked
this.panelIds.push(data.id);
if(testFilteredArray.length > 0){
this.selectedPanels.push(testFilteredArray[0]);
}
}
else {
//is false
this.panelIds = this.panelIds.filter(obj => obj !== data.id);
this.selectedPanels = this.selectedPanels.filter(obj => obj.Id !== data.id);
}
// this.panelIds = lodash.uniq(this.panelIds);
this.selectedPanels = lodash.uniqBy(this.selectedPanels, "Id");
this.updateSession();
}
)
checkbox function
onPanelCheckboxUpdate($event: boolean, panel: TestOrderPanel) {
let testPanelIds = panel.Tests.map(test => test.Id);
// Wipe any duplicates
this.panelIds = this.panelIds.filter(
panelId => panel.Id !== panelId && testPanelIds.indexOf(panelId) === -1
);
this.selectedPanels = this.selectedPanels.filter(
selectedPanel =>
panel.Id !== selectedPanel.Id &&
testPanelIds.indexOf(selectedPanel.Id) === -1
);
if ($event) {
this.panelIds.push(panel.Id);
this.selectedPanels.push(panel);
}
this.updateSession();
}
This checkbox function is not working and wont let me change the value of checkbox.
And also is there any way of adding click function in ngbPanel tag?
Any help?
Thanks
If you want to achieve two way data binding , use below code.
In foo.html
<input [(ngModel)]="checBxFlag" type="checkbox"/>
In App.module.ts
import { FormsModule } from '#angular/forms';
#NgModule({
imports: [
[...]
FormsModule
],
[...]
})
If you want fire event click of check box ,use (onClick)="somemethod()" in your foo.html file and define method in foo.ts file.

AngularJS Ng-Repeat - Multiple Custom Filters to Filter Items by JSON Object Properties

JSFiddle: http://jsfiddle.net/ADukg/16368/.
I'm trying to filter items in an ng-repeat list by multiple filters if their JSON properties for the object match a selected option.
So when the 'Unread' filter is selected, items will only be displayed where the 'unread' property of each json object is equal to true, and similarly with the high importance filter.
I also want to be able to combine these two filters, so that the list only displays items with both the unread and high importance properties equal to true when the filters are both selected.
I've got a basic setup in the JSFiddle above where I've used models for the filter checkboxes, but have been looking for a while at a method to implement these filters for the list and have been confused as to how I would do what I need for this scenario.
HTML:
<div>
<label for="filterByAllCheckbox">Filter by All </label>
<input ng-model="filters.all" ng-change="filterByAll()" type="checkbox" id="filterByAllCheckbox" ng-disabled="filters.all">
</div>
<div>
<label for="filterByUnreadCheckbox">Filter by Unread </label>
<input ng-model="filters.unread" ng-change="manageFilters()" type="checkbox" id="filterByUnreadCheckbox">
</div>
<div>
<label for="filterByHighImportanceCheckbox">Filter by High Importance </label>
<input ng-model="filters.highImportance" ng-change="manageFilters()" type="checkbox" id="filterByHighImportanceCheckbox">
</div>
<br>
<ul>
<b>NOTIFICATIONS</b>
<li ng-repeat="notification in notifications">
{{notification.title}}
</li>
</ul>
You can implement such a functionality using ng-show in li item checking for the two conditions (unread and highImportance).
<li ng-repeat="notification in notifications"
ng-show="(filters.all)
|| (filters.unread && !notification.read && filters.highImportance && notification.importance == 'High')
|| (!filters.unread && notification.read && filters.highImportance && notification.importance == 'High')
|| (filters.unread && !notification.read && !filters.highImportance && notification.importance != 'High')
|| (!filters.unread && notification.read && !filters.highImportance && notification.importance != 'High')">
{{notification.title}}
</li>
I doubt if this the best way to achieve what you describe though.
Updated. Implementation of custom filter.
var myApp = angular.module('myApp',[]).filter('notificationFilter', function () {
return function (array, all, unread, highImportance) {
var matches = [];
for (var i = 0; i < array.length; i++) {
if (all
|| (!unread && highImportance && array[i].importance == 'High')
|| (unread && !array[i].read && !highImportance)
|| (unread && !array[i].read && highImportance && array[i].importance == 'High')) {
matches.push(array[i]);
}
return matches;
};
});
Then you have to call filter in your controller methods.
$scope.manageFilters = function() {
if ($scope.filters.unread == true || $scope.filters.highImportance == true) {
$scope.filters.all = false;
} else {
$scope.filters.all = true;
}
$scope.shownNotifications = $filter('notificationFilter')($scope.notifications, $scope.filters.all, $scope.filters.unread, $scope.filters.highImportance);
}
And
$scope.filterByAll = function() {
if ($scope.filters.all == true) {
$scope.filters.unread = false;
$scope.filters.highImportance = false;
$scope.shownNotifications = $filter('notificationFilter')($scope.notifications, $scope.filters.all, $scope.filters.unread, $scope.filters.highImportance);
}
}
And of course change html:
<li ng-repeat="notification in shownNotifications">
{{notification.title}}
</li>
Be sure to declare $filter in controller definition and to initialize list shownNotifications.
You can take a look in updated jsfiddle here.
Feel free to optimize - change my sample implementation as you wish.

Angular 2: default radio button selection

I have two radio buttons (they're not dynamically generated):
<input type="radio" name="orderbydescending" [(ngModel)]="orderbydescending" [value]="['+recordStartDate']">Ascending<br>
<input type="radio" name="orderbydescending" [(ngModel)]="orderbydescending" [value]="['-recordStartDate']">Descending
How do I make one of the radio buttons checked by default?
Thank you!
Edit
The button's values are being passed through this pipe (i.e. not a component, per se...not sure this is worth mentioning?). The app is pretty simple, and the radio buttons are just hardcoded into app.component. Is the pipe the correct place to initialize which radio button is checked by default?
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({name: 'orderBy', pure: false})
export class OrderByPipe implements PipeTransform {
static _OrderByPipeComparator(a:any, b:any):number{
if((isNaN(parseFloat(a)) || !isFinite(a)) || (isNaN(parseFloat(b)) || !isFinite(b))){
//Isn't a number so lowercase the string to properly compare
if(a.toLowerCase() < b.toLowerCase()) return -1;
if(a.toLowerCase() > b.toLowerCase()) return 1;
}
else{
//Parse strings as numbers to compare properly
if(parseFloat(a) < parseFloat(b)) return -1;
if(parseFloat(a) > parseFloat(b)) return 1;
}
return 0; //equal each other
}
transform(input:any, [config = '+']): any{
if(!Array.isArray(input)) return input;
if(!Array.isArray(config) || (Array.isArray(config) && config.length == 1)){
var propertyToCheck:string = !Array.isArray(config) ? config : config[0];
var desc = propertyToCheck.substr(0, 1) == '-';
//Basic array
if(!propertyToCheck || propertyToCheck == '-' || propertyToCheck == '+'){
return !desc ? input.sort() : input.sort().reverse();
}
else {
var property:string = propertyToCheck.substr(0, 1) == '+' || propertyToCheck.substr(0, 1) == '-'
? propertyToCheck.substr(1)
: propertyToCheck;
return input.sort(function(a:any,b:any){
return !desc
? OrderByPipe._OrderByPipeComparator(a[property], b[property])
: -OrderByPipe._OrderByPipeComparator(a[property], b[property]);
});
}
}
else {
//Loop over property of the array in order and sort
return input.sort(function(a:any,b:any){
for(var i:number = 0; i < config.length; i++){
var desc = config[i].substr(0, 1) == '-';
var property = config[i].substr(0, 1) == '+' || config[i].substr(0, 1) == '-'
? config[i].substr(1)
: config[i];
var comparison = !desc
? OrderByPipe._OrderByPipeComparator(a[property], b[property])
: -OrderByPipe._OrderByPipeComparator(a[property], b[property]);
//Don't return 0 yet in case of needing to sort by next property
if(comparison != 0) return comparison;
}
return 0; //equal each other
});
}
}
Edit 2
So in component.app.ts I've edited my export class AppComponent{ to the following:
export class AppComponent {
artists = ARTISTS;
currentArtist: Artist;
orderbydescending = ['-recordStartDate'];
showArtist(item) {
this.currentArtist = item;
}
}
This works in terms of preventing the previous errors, but it doesn't actually make the radio button selected. It still appears as though it's unselected - even though it functions as though it is. Does this make sense?
If you're doing this in Angular 2+ with 2 way binding, in the component where this HTML is being used, you could just try initializing the value associated with the input.
// in your component ts file
orderbydescending: boolean = true;
and you could leave the HTML the same. Although, you seem to have 2 radio buttons associated with the same data value, orderbydescending. I don't know if that's what you intend, but it looks like it could cause problems.
Here's some code from my personal side project to give you a better idea.
#Component({
selector: 'gmu-home-page',
templateUrl: './home-page.component.html',
styleUrls: ['./home-page.component.css']
})
export class HomePageComponent implements OnInit {
// here you would put your variables
myFlag: boolean = true;
constructor() { }
ngOnInit() {
}
}
If orderbydescending and recordStartDate are members of your component class:
#Component({
...
})
export class MyComponent {
public recordStartDate: any = ...
public orderbydescending: any = +recordStartDate;
...
}
the appropriate radio button will be checked if you assign the radio button values with [value]:
<input type="radio" name="order" [(ngModel)]="orderbydescending" [value]="+recordStartDate">Ascending<br>
<input type="radio" name="order" [(ngModel)]="orderbydescending" [value]="-recordStartDate">Descending
In the case shown above, the ascending order radio button will be checked by default because the orderbydescending variable is initially set to +recordStartDate.
Note: The variables in my sample code are of type any because I don't know exactly what kind of data you are using. Your data will probably have a more specific data type.
Try this in the component
radioValue = {valueAsc: 'Asc', valueDesc: 'Desc'} ;
orderbydescending = 'Asc';
and in the template put this
<input type="radio" name="radioGroup" [(ngModel)]="orderbydescending" [value]="radioValue.valueAsc">Ascending
<input type="radio" name="radioGroup" [(ngModel)]="orderbydescending" [value]="radioValue.valueDesc">Descending
With this the first radio button is checked, if you don't want any radio button selected assign to the variable orderbydescending null.
orderbydescending = 'null;

How can I apply CSS to a link if at least one input is not original, and undo that change if all inputs are original?

I have a bunch of checkboxes, radio buttons, and text fields on my page. They all have '_boom' appended to the end of the id. I want to detect if any one of these inputs is not its original value, and if so, apply CSS to a button called 'save' on the page. Then, if the user reverts any changes they made and all inputs have their original values, I want to undo the CSS.
I've gotten close with the code below. But let's say I check 3 checkboxes. Upon checking the 1st box, the CSS changes. Good! I check the 2nd and 3rd boxes. The CSS stays the same. Good! But then I uncheck ONE of the boxes, and the CSS reverts. Bad! The CSS should only revert if I undo every change.
$('[id*="_boom"]').change(function() {
var sType = $(this).prop('type'); //get the type of attribute we're dealing with
if( sType === "checkbox" || sType === "radio" ){ //checkbox or radio type
var originalCheckedState = $(this).prop("defaultChecked");
var currentCheckedState = $(this).prop("checked");
if(currentCheckedState !== originalCheckedState){
$("a#save").css("color","#CCCCCC");
}
else {
$("a#save").css("color","black");
}
}
if( sType === "text" ){ //text type
var originalValue = $(this).prop("defaultValue");
var currentValue = $(this).val();
if(currentValue !== originalValue){
$("a#save").css("color","#CCCCCC");
}
else {
$("a#save").css("color","black");
}
}
});
#save {
color: black;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<input type="checkbox" id="check_boom" />
<input type="checkbox" id="check1_boom" />
<input type="checkbox" id="check2_boom" />
<input type="radio" id="radio_boom" />
<input type="text" defaultValue="test" id="text_boom" />
<input type="text" defaultValue="test" id="text2_boom" />
Save
There are many possible improvements in your code to make it cleaner and standardized. Things like instead of relying on id you should consider class attribute and all... but I will not revamp your code. Here's the solution to your existing code.
The idea is loop through all the form elements and if atleast one of the elements is different than its default value then set the flag and come out of the loop.
At the end, check for that flag and set the css accordingly.
For this, I have enclosed your elements into a form form1.
$("#form1 :input").change(function() {
var changed = false;
formElems = $("#form1 :input");
for(i=0;i<formElems.length; i++){
var sType = $(formElems[i]).prop("type");
if(sType === "checkbox" || sType === "radio"){
if($(formElems[i]).prop("defaultChecked") !== $(formElems[i]).prop("checked")){
changed = true;
break;
}
}else if(sType === "text"){
if($(formElems[i]).prop("defaultValue") !== $(formElems[i]).val()){
changed = true;
break;
}
}
}
if(changed){
$("a#save").css("color","#CCCCCC");
}else{
$("a#save").css("color","black");
}
});
And here is your form
<form id="form1">
<input type="checkbox" id="check_boom" />
<input type="checkbox" id="check1_boom" />
<input type="checkbox" id="check2_boom" />
<input type="radio" id="radio_boom" />
<input type="text" defaultValue="test" id="text_boom" />
<input type="text" defaultValue="test" id="text2_boom" />
Save
</form>
The problem is, when one of them change to its original value, it doesn't mean there is no change.
So, in your else code block, you should check all the inputs, if all of them are the original values, remove the 'save' class from the button, otherwise, keep it.
var isChanged = function ($element) {
var sType = $element.prop('type');
if (sType === "checkbox" || sType === "radio") {
var originalCheckedState = $element.prop("defaultChecked");
var currentCheckedState = $element.prop("checked");
if (currentCheckedState !== originalCheckedState) {
return true;
} else {
return false;
}
} else if( sType === "text" ) {
var originalValue = $element.prop("defaultValue");
var currentValue = $element.val();
if (currentValue !== originalValue) {
return true;
} else {
return false;
}
}
};
var $inputs = $('[id*="_boom"]');
var isAnyChanged = function () {
$inputs.each(function () {
if (isChanged($(this))) {
return true;
}
});
return false;
};
$inputs.change(function () {
if (isChanged($(this))) {
$("a#save").css("color","#CCCCCC");
} else if (!isAnyChanged()) {
$("a#save").css("color","black");
}
});

jQuery help with checking parent of checked child checkbox

I have a main row and some other rows underneath that main row like this:
[checkbox] Main heading row
[checkbox] first child row
[checkbox] second child row
When I click on the child row, it should check the parent (main) row automatically. Problem is that it doesn't check it first time I click it. I have to check the first child row first, then uncheck the first child row and then check first child row again to get the parent (main) row get checked. I want the parent row get checked as soon as any of the child rows get checked.
I am using the following code
function checkbox_click(){
var n = event.srcElement;
if(n.parentElement.id == "row"){
n = n.parentElement;
}
if(n.id == "row"){
alert("ID: 1");
n.rs = n.parentElement;
if(this.multiSelect == 0){ // single select
alert("ID: 2");
n.all[0].checked = 1;
this.selectedRows = [ n ];
if(this.lastClicked && this.lastClicked != n){
this.lastClicked.all[0].checked = 0;
this.lastClicked.style.color = "000099";
this.lastClicked.style.backgroundColor = "";
}
} else {
alert("ID: 3");
n.all[0].click();
}
if(this.parentElement == pg.procs) {
alert("ID: 4");
var terminate = false;
var counter = 0;
if(n.className == "proc") {
alert("ID: 5");
z = n.nextSibling;
while(z.id == "row" && z.className != "proc" && !terminate) {
alert("ID: 6");
z.all[0].checked = 0;
z.style.backgroundColor = z.className == "w" ? "ffffff" : "ffffcc";
counter++;
if(counter > 1000) terminate = true;
z = z.nextSibling;
}
} else {
$(".row input").change(function() {
alert("ID: 7");
var $row= $(this).closest(".row");
var $main_row = $row.prev('.proc').length ? $row.prev('.proc') : $row.prevUntil(".proc").prev();
$main_row.find(":checkbox").attr("checked", function(i,attr) {
return $main_row.nextUntil('.proc').filter(':has(input:checked)').length ? "checked" : false;
});
});
$(".proc input").change(function() {
alert("ID: 8");
$(this).closest(".proc").nextUntil('.proc').children(':checkbox').attr('checked', this.checked);
});
}
If you want to check the parent checkbox when one of the child checkboxes is checked, I would suggest using a common class for the child checkboxes, and a unique id attribute for the parent checkbox (or store it as a variable).
Let's assume you have a structured HTML document that contains something like the following:
<div>
<input type="checkbox" name="ckparent" id="ckparent" />
<label for="ckparent">Parent</label>
<div>
<input type="checkbox" name="ckchild1" id="ckchild1" class="ckchild" />
<label for="ckchild1">Child 1</label>
</div>
<div>
<input type="checkbox" name="ckchild2" id="ckchild2" class="ckchild" />
<label for="ckchild2">Child 2</label>
</div>
</div>
You could then write the following jQuery code to check the parent checkbox when either of the children are checked:
$('input:checkbox.ckchild').click(function(event) {
var checked = $(this).is(':checked');
if (checked) {
$('#ckparent').attr('checked', true);
}
});
EDIT: The order in which the changed and clicked events are fired with regards to when the checked attribute is actually changed is dependent on the browser you are using -- which browsers are you targeting?

Categories