I had condition in my reactive form where one checkbox selected, submit button will be enabled, and if there are none checkbox selected, it will remain disabled, the problem is I had selectAll function, which if I clicked, it will selected all the checkbox and enabled submit button, then if I unselect individual checkbox after select all function, the submit button should be enabled until all the checkbox is unselect, this is what I had tried:
ts file
selectAll() {
this.formReceivedSummons.controls.map(value => value.get('isChecked').setValue(true));
return this.disabledButton = false;
}
changeCheck(event){
this.disabledButton = !event.target.checked;
}
html file
<div *ngIf="isShowResponse">
<p>Inquiry Response</p>
<form [formGroup]="form" (ngSubmit)="submitSelectedCheckboxes()">
<ng-container formArrayName="receivedSummons" *ngFor="let
summon of formReceivedSummons.controls; let i = index">
<ng-container [formGroupName]="i">
<input type="checkbox" formControlName="isChecked"
(change)="changeCheck($event)">
{{ summon.get('isChecked').value ? 'selected' : 'select' }}
</ng-container>
</ng-container>
<button [disabled]="disabledButton">Submit</button>
</form>
<button (click)="selectAll()">Select All</button>
</div>
supposed to be after select all function, submit button will enabled until all checkbox is unselected individually then it will disabled, this is my stackblitz demo, I could use any suggestion to solve this problem,
the "clasic solution" is make a custom error validator
Imagine that you has some like
options=["option 1","option 2","option 2"]
form=new FormGroup({
prop1:new FormControl(),
receivedSummons:new FormArray(this.options.map(x=>new FormControl()),this.selectAtLeastOne())
})
selectAtLeastOne()
{
return (formArray:FormArray)=>{
return formArray.value.some(x=>x)?null:{error:"At least one"}
}
}
You only need
<button [disabled]="form.invalid">submit</button>
The completed form is like
<div [formGroup]="form">
<input formControlName="prop1">
<div formArrayName="receivedSummons">
<div *ngFor="let control of form.get('receivedSummons').controls;let i=index" >
<input type="checkbox" [formControlName]="i">{{options[i]}}
</div>
</div>
<button [disabled]="form.invalid">submit</button>
</div>
NOTE: I choose use a formArray of FormControls, not a FormArray of formGroups, if you want to use a formArray of FormGroups, the code becomes like
form2=new FormGroup({
prop1:new FormControl(),
receivedSummons:new FormArray(
this.options.map(x=>new FormGroup({
isChecked:new FormControl()
})),this.selectAtLeastOne2())
})
selectAtLeastOne2()
{
return (formArray:FormArray)=>{
return formArray.value.some(x=>x.isChecked)?null:{error:"At least one"}
}
}
And the .html
<div [formGroup]="form2">
<input formControlName="prop1">
<div formArrayName="receivedSummons">
<div *ngFor="let control of form.get('receivedSummons').controls;let i=index" [formGroupName]="i" >
<input type="checkbox" formControlName="isChecked">{{options[i]}}
</div>
</div>
<button [disabled]="form2.invalid">submit</button>
</div>
You can see the two forms in stackblitz
Updated I add two funtions to check/uncheck all
selectAll(select: boolean) {
this.form.get("receivedSummons").setValue(this.options.map(x => select));
}
selectAll2(select: boolean) {
this.form2.get("receivedSummons").setValue(
this.options.map(x => {
return { isChecked: select };
})
);
}
The check to check/uncheck all like (see as I use a refernce variable to pass if is checkd or not the checkBox
<input #check1 type="checkbox" (change)="selectAll(check1.checked)">Check All
In html, define a reference in input "#checkboxes"
<input type="checkbox" formControlName="isChecked" #checkboxes (click)="changeCheck()">
In ts,
We can get all checkbox values using #ViewChildren
#ViewChildren("checkboxes") checkboxes: QueryList<ElementRef>;
ViewChildren, QueryList, ElementRef to be imported from 'angular/core'
changeCheck(){
let flag= true;
this.checkboxes.forEach((element) => { // loop all checkboxes to find checked boxes
if(element.nativeElement.checked){
flag=false // if atleast one checkbox checked, put the flag flase
}
});
this.disabledButton = flag;
}
Working code at Stackblitz
Try the below code:
changeCheck(event){
let flag = true
for (let summon of this.formReceivedSummons.controls){
if(summon.get('isChecked').value) {
flag = false
}
}
this.disabledButton = flag
}
Related
I am working on angular 4 validation. I have a reactive form that have two radio button and two form groups. If user select's first radio button, it will remove validation from second form group and add validation on first form group and when select's second radio button it will add validation on second form group and remove from first form group.
Below is my form group example
this.mainForm = this.formBuilder.group({
cardOrBank: new FormControl(''),
cardDetails: new FormGroup({
cardNo: new FormControl(''),
cvv: new FormControl('')
}),
bankDetails: new FormGroup({
accNo: new FormControl(''),
ifsc: new FormControl('')
})
});
HTML
<form [formGroup]="mainForm" (ngSubmit)="onFormSubmit()">
<div>
Select: <input type="radio" formControlName="cardOrBank"> card
<input type="radio" formControlName="cardOrBank"> bank
</div>
<div formGroupName="cardDetails">
<div>
Card No: <input formControlName="cardNo">
</div>
<div>
CVV: <input formControlName="cvv">
</div>
</div>
<div formGroupName="bankDetails">
<div>
ACC No: <input formControlName="accNo">
</div>
<div>
IFSC: <input formControlName="ifsc">
</div>
</div>
<div>
<button type="submit">Submit</button>
</div>
</form>
If select card from radio button, it will add validation on cardDetails form and remove validation from bankDetails and vice versa.
P.S: Form fields may be more according to the requirement.
Thanks.
After doing a lot of work finally i was able to achieve this.
Below are the changes you need to make in your code :
// in component.ts file :
// Write two genric methods which sets and clears the validator
setRequireValidator(form:any){
for (const field in form.controls) { // 'field' is a string
let con = form.get(field); // 'control' is a FormControl
con.setValidators([Validators.required]);
con.updateValueAndValidity();
}
}
removeValidator(form:any){
console.log('form contro',form);
for (const field in form.controls) { // 'field' is a string
let con = form.get(field); // 'control' is a FormControl
con.clearValidators();
con.updateValueAndValidity();
}
// while initiallizing the form ragister the event for value changed on `cardOrBank`
// check which value user has selected and accordingly toggle between them
this.mainForm.get('cardOrBank').valueChanges.subscribe((val) => {
const cardControl = this.mainForm.get('cardDetails');
const bankControl = this.mainForm.get('bankDetails');
if(val === 'card') {
alert('card sletecd')
this.removeValidator(bankControl);
this.setRequireValidator(cardControl);
} else{
alert('bank sletecd')
this.removeValidator(cardControl);
this.setRequireValidator(bankControl);
}
});
<!-- In component.html file-->
<form [formGroup]="mainForm" (ngSubmit)="onFormSubmit()">
<div>
<label>
<!-- You missed value attribute -->
<input type="radio" value="card" formControlName="cardOrBank">
<span>Card</span>
</label>
<label>
<input type="radio" value="bank" formControlName="cardOrBank">
<span>Bank</span>
</label>
</div>
<div formGroupName="cardDetails">
<div>
Card No: <input formControlName="cardNo">
</div>
<div>
CVV: <input formControlName="cvv">
</div>
</div>
<div formGroupName="bankDetails">
<div>
ACC No: <input formControlName="accNo">
</div>
<div>
IFSC: <input formControlName="ifsc">
</div>
</div>
<div>
<button type="submit" [disabled]="!mainForm.valid">Submit</button>
</div>
</form>
Here is the Working Example of your requirement :
Working demo
you can do like this
addValidation() {
this.form.get('title').setValidators([Validators.required, Validators.minLength(3)]);
this.form.get('title').updateValueAndValidity();
}
removeValidation() {
this.form.get('title').clearValidators();
this.form.get('title').updateValueAndValidity();
}
You can do two things, either you can put explicit method or you can subscribe to the changes happens to formControlName to perform this validator switch.
if you want to subscribe then subscribe on ngOnInit() lifecycle hook:
ngOnInit() {
this.mainForm.get('cardDetails').valueChanges.subscribe((val) => {
if(val === 'card') {
this.mainForm.get('cardDetails').setValidators(Validators.required);
} else {
this.mainForm.get('bankDetails').removeValidators(Validators.required);
}
this.mainForm.get('bankDetails').updateValueAndValidity();
});
}
**add Value attribute in the html.**
**2nd Option :**
Select: <input type="radio" formControlName="cardOrBank (change)="changedPayment('card')"> card
<input type="radio" formControlName="cardOrBank (change)="changedPayment('bank')> bank
changedPayment(val) {
if(val === 'card') {
this.mainForm.get('cardDetails').setValidators(Validators.required);
} else {
this.mainForm.get('bankDetails').removeValidators(Validators.required);
}
this.mainForm.get('bankDetails').updateValueAndValidity();
}
I assume that cardOrBank form control has two values 'card' and 'bank'. In your ngOnInit, you should subscribe the valueChanges of your radio button form control.
ngOnInit() {
this.mainForm.get('cardDetails').valueChanges.subscribe((cardOrBank) => {
if(cardOrBank === 'card') {
this.mainForm.get('cardDetails').setValidators(Validators.required);
this.mainForm.get('bankDetails').removeValidators(Validators.required);
} else {
this.mainForm.get('bankDetails').setValidators(Validators.required);
this.mainForm.get('cardDetails').removeValidators(Validators.required);
}
this.mainForm.get('bankDetails')updateValueAndValidity();
this.mainForm.get('cardDetails')updateValueAndValidity();
});
}
Your radio button should have a value attribute
<div>
Select: <input type="radio" value="card" formControlName="cardOrBank"> card
<input type="radio" value="bank" formControlName="cardOrBank"> bank
</div>
I am looping through an array of key values to create a list of checkboxes each with a sibling disabled input. On check of each checkbox, the sibling input text field becomes enabled and is required. In this view there is a 'previous' and 'next' button and the 'next' button should be disabled if a user selects a checkbox and then does not enter anything in it's required sibling input. I almost have this working, however the 'next' button should become disabled as soon as a user checks the box as this would mean they have not entered anything in the required text input. Right now the 'next' button only becomes disabled if a user checks the checkbox, focuses on the sibling input and then leaves without entering.
My HTML...
<div *ngFor="let promotion of promotionOptions; let i = index">
<div class="col-md-6 input-container radio-label">
<mat-checkbox [checked]="!promotion.key" (change)="promotion.key = !promotion.key">
{{ promotion.name }}
</mat-checkbox>
</div>
<mat-input-container>
<input matInput [disabled]="promotion.key" placeholder="Cost" name="promotionCost{{i}}" #promotionCost="ngModel" [ngModel]="" (keyup)="promotionCostInput($event.target.value)"
[required]="!promotion.key" type="number">
<div *ngIf="promotionCost.errors && (promotionCost.dirty || promotionCost.touched)" class="alert alert-danger cost-alert">
<div [hidden]="!promotionCost.errors.required">Please enter the checked promotion's cost</div>
</div>
</mat-input-container>
</div>
<div class="clearfix"></div>
<div class="button-container">
<button class="main-btn dark icon-left" (click)="updateStep(1)"><i class="fa fa-angle-left"></i>Previous</button>
<button class="main-btn icon-right" (click)="updateStep(3)" [disabled]="!promotionCostValid">Next<i class="fa fa-angle-right"></i></button>
</div>
And the method I'm using in my .ts file for disabling the 'next' button:
promotionCostInput(value) {
if (!value) {
this.promotionCostValid = false;
} else {
this.promotionCostValid = true;
}
}
How can I validate the sibling input when a user checks the checkbox?
Your problem is that the state of your next button is only updated when the keyup event is fired on any of your inputs. Besides, it is updated with only the value of one input but according to what you say, youwant to check that every inputs of your ngFor is filled.
I suggest you to store the value of your inputs in your model and to check that promotion cost is valid for all promotions any time the input change or a checkbox is checked.
<div *ngFor="let promotion of promotionOptions; let i = index">
<div class="col-md-6 input-container radio-label">
<mat-checkbox [checked]="!promotion.key" (change)="promotion.key = !promotion.key; checkPromotionCost();">
{{ promotion.name }}
</mat-checkbox>
</div>
<mat-input-container>
<input
matInput
[disabled]="promotion.key"
placeholder="Cost"
name="promotionCost{{i}}"
(keyup)="promotion.cost = $event.target.value; checkPromotionCost();"
[required]="!promotion.key" type="number"
>
<div *ngIf="promotionCost.errors && (promotionCost.dirty || promotionCost.touched)" class="alert alert-danger cost-alert">
<div [hidden]="!promotionCost.errors.required">
Please enter the checked promotion's cost</div>
</div>
</mat-input-container>
</div>
<div class="clearfix"></div>
<div class="button-container">
<button class="main-btn dark icon-left" (click)="updateStep(1)"><i class="fa fa-angle-left"></i>Previous</button>
<button class="main-btn icon-right" (click)="updateStep(3)" [disabled]="!promotionCostValid">Next<i class="fa fa-angle-right"></i></button>
</div>
And in the controller:
checkPromotionCost() {
this.promotionCostValid = true;
this.promotionOptions.forEach(promotion => {
if (promotion.key && promotion.cost === '') {
this.promotionCostValid = false;
}
});
}
I am trying to get the checked radio button and add the value to an Array. Currently, i cannot remove the previously checked radio buttons, so basically it keeps adding to the array every time i select a radio button.
item.component.ts
displaySelectedConditions(event) {
if(event.target.checked) {
this.selectedConditionsArr.push(event.target.value);
}
}
item.component.html
<ul class="dropdown-menu">
<li *ngFor="let item of filteredItems | funder "> //generates 4 items
<a><input type="radio" (change) = "displaySelectedConditions($event);"
name="funder" id="{{item}}" value="{{item}}">
<label for="{{item}}" >{{item}}</label></a>
</li>
</ul><!-- Dropdown Menu -->
I would suggest if you want to have the values neatly stored somewhere, then make use of a form. Simple template driven form works well here, then you would have all your values stored in an object, here's a sample:
<form #radioGroup="ngForm">
<div *ngFor="let str of strings">
<input type="radio" [value]="str" name="str" ngModel />{{str}}
</div>
<hr>
<div *ngFor="let num of numbers">
<input type="radio" [value]="num" name="num" ngModel />{{num}}
</div>
</form>
This would create an object like:
{
str: "value here",
num: "value here"
}
And if you declare the form like the following, you can easily access the object values:
#ViewChild('radioGroup') radioGroup: NgForm;
Somewhere in component:
console.log(this.radioGroup.value);
Plunker
I'm having a list of check boxes and a main check box which if selected checks all the check boxes .I can also check the individual check boxes.
HTML:
<button type="button" ng-disabled="!selectedAll">Click</button>
<div>
<input type="checkbox" ng-model="selectedAll" class="checkbox" ng- click="toggle=!toggle">
</div>
<div ng- repeat="item in items" >
<input type="checkbox" ng- checked="toggle" ng-model="selectedCheckBox[item.id]"> <i></i>
</div>
Controller:
$scope.toggle = false;
$scope.selectedIntervention = {};
I'm able to disable/enable the button by selecting the main check box(outside ng-repeat) by enabling ng-disabled="!selectedAll" .But I'm not able to diable the button if I select any check box that is inside ng-repeat.I tried by giving ng-model="!selectedCheckBox" but didn't worked that way.Any possible solution is highly appreciated.Thanks
$scope.toggle = false;
$scope.selectedCheckBox = {};
Do following
Add
ng-click="checked(toggle)"
In
<input type="checkbox" ng-click="checked(toggle)" ng-model="selectedCheckBox[item.id]">
And change button like this
<button type="button" ng-disabled="!toggle">Click</button>
And in controller
$scope.checked = function () {
$scope.toggle = !$scope.toggle;
}
I have a form with a text input and a radio button pair used to select yes/no. For purposes of keeping this simple, the radio button click event checks the value and if yes, it shows the input text field. If no, it hides the input field. I also check the initial state on document ready and show/hide the input text field.
I find that clicking No results in the input hiding using a jQuery .hide() method. But when I select Yes the resulting .show() method call does not show the input. If I set the radio to Yes and then refresh the page then the input shows up just fine.
Firebug show no input tag. It's like clicking No radio deleted the input from the DOM.
Here's the JS code sample:
$(document).ready(function() {
if ($('#cost_sharing_yes').attr('checked') == 'checked') {
$('input#Institutional_CS_TP').show();
} else {
$('input#Institutional_CS_TP').hide();
}
$('#cost_sharing_yes').click(function() {
$('input[id="Institutional_CS_TP"]').show();
});
$('#cost_sharing_no').click(function() {
$('input#Institutional_CS_TP').fadeOut("fast");
});
}
You are missing ) for closing ready function:
$(document).ready(function() {
} // <--
For getting the checked property of the inputs perperly you should use prop method instead of attr.
$(document).ready(function() {
var isChecked = $('#cost_sharing_yes').prop('checked');
$('#Institutional_CS_TP').toggle(isChecked);
// ..
})
I figured out my problem. It was a self-inflicted coding problem.
To keep the example simple I had removed another function call in the mix that I didn't think had any bearing on the problem. I was wrong. In that function I had
$('td#Institutional_CS_TP).text('$0');
$('input[name="Institutional_CS_TP"]').val('0.00');
This resulted in only the td value showing, not the input inside that same td.
Both my td and the input tags inside the td had the same ID values...not a good idea.
html code
<div id="myRadioGroup">
Value Based<input type="radio" name="cars" value="2" />
Percent Based<input type="radio" name="cars" value="3" />
<br>
<div id="Cars2" class="desc" style="display: none;">
<br>
<label for="txtPassportNumber">Commission Value</label>
<input type="text" id="txtPassportNumber" class="form-control" name="commision_value" />
</div>
<div id="Cars3" class="desc" style="display: none;">
<br>
<label for="txtPassportNumber">Commission Percent</label>
<input type="text" id="txtPassportNumber" class="form-control" name="commision_percent" />
</div>
</div>
Jquery code
function myFunction() {
var x = document.getElementById("myInput");
if (x.type === "password") {
x.type = "text";
} else {
x.type = "password";
}
}
function myFunction1() {
var y = document.getElementById("myInput1");
if (y.type === "password") {
y.type = "text";
} else {
y.type = "password";
}
}