Angular Reactive form set Multiselect Checkbox values - javascript

The form is having 1 textbox , 1 radio button and 1 multi select Checkbox The HTML template is like below
<form *ngIf="notificationSettings | async; else loading"
[formGroup]="notificationForm" (ngSubmit)="onSubmit()">
<div class="form-group">
<div *ngFor="let option of notifyBackAlertOptions; let i=index">
<input type="checkbox" class="form-check-input" [value]="option.value" formControlName="notifyBackOptions" />
<label> {{option.name}} </label>
</div>
</div>
<div class="form-group">
<label for="notifyBackEmail">Where shall we send the alerts?</label>
<input type="email" class="form-control" formControlName="notifyBackEmail">
</div>
<div class="form-check" *ngFor="let option of discontinuedAlertOptions;">
<label>
<input formControlName="discontinuedOption" class="form-check-input"
type="radio"
name="discontinuedOption"
[value]="option.value" />
{{option.name}}
</label>
</div>
<div class="float-left">
<button class="btn btn-primary mr-1">Update</button>
</div>
</form>
<ng-template #loading>
Loading ---...
</ng-template>
The component is like below
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
export class NotifcationsComponent implements OnInit {
notificationSettings: Observable<NotificationSetting>;
notificationForm: FormGroup;
submitted = false;
notifyBackAlertOptions = [
{ name: 'Option 1', value: '1' },
{ name: 'Option 2', value: '2' },
{ name: 'Option 3', value: '3' },
{ name: 'Option 4', value: '4' }
];
discontinuedAlertOptions = [
{ name: 'Yes for any', value: '1' },
{name: 'Yes for all', value: '2' },
{ name: 'No', value: '3' }
];
constructor(private formBuilder: FormBuilder,private userService: UserService) { }
ngOnInit() {
this.getCurrentSettings();
this.notificationForm = this.formBuilder.group({
notifyBackEmail: [''],
discontinuedOption: [''],
notifyBackOptions: new FormArray([]),
});
}
getCurrentSettings(): void {
this.notificationSettings = this.userService
.getUserNotificationSettings()
.pipe(tap(data => {
console.log("GET")
this.notificationForm = this.formBuilder.group({
notifyBackEmail: new FormControl(data.notifyBackEmail),
discontinuedOption: new FormControl(data.discontinuedOption),
notifyBackOptions: new FormControl(data.notifyBackOptions)
});
console.log(this.notificationForm) //I can see the values are mapping correctly against notificationForm. Checkbox property ie notifyBackOptions value is coming as ["1", "2"] at this stage
}
));
//This code maps / sets the values of textbox and radio buttons correctly at page loading based on response from API. But not setting values for multiselect checkbox correctly.
//I can see all checkbox values are coming as selected. IN this case 4 checkbox values are selected
//How to set only the notifyBackOptions checkbox selected values marked as checked
}
// convenience getter for easy access to form fields in HTML page
get f() { return this.notificationForm.controls; }
onSubmit() {
this.submitted = true;
// stop here if form is invalid
if (this.notificationForm.invalid) {
return;
}
console.log(this.notificationForm.value);
}
}
The HTML is rendering correctly and capturing the values at form submission . At load time of the component i have to read the values from API endpoint and prefill form based on current settings
The JSON response from API endpoint is like below
{
notifyBackEmail: "email#email-domain.in"
notifyBackOptions: ["1","2"]
discontinuedOption: "1"
}
The existing implementation of getCurrentSettings() is setting values of radio and textbox correctly but not checkbox.
At present this is setting all values of checkbox as selected. How can i set the Checkbox values as selected based on response form API with help of model binding

Since you are handling with array of values, you need to create array of formControl instead of single formControl. Try something like this:
Try this:
notificationForm: FormGroup;
getCurrentSettings(): void {
this.notificationSettings = this.userService
.getUserNotificationSettings()
.pipe(
tap(data => {
const notifyBackOptions = data.notifyBackOptions;
const notificationControls = this.notifyBackAlertOptions.map(
item => new FormControl(notifyBackOptions.includes(item.value))
);
this.notificationForm = this.formBuilder.group({
notifyBackEmail: [''],
discontinuedOption: [''],
notifyBackOptions: this.formBuilder.array(notificationControls)
});
})
);
}
Then in HTML You need to add formArrayName directive to sync with FormGroup
<form [formGroup]="notificationForm" (ngSubmit)="onSubmit()">
<div class="form-group" formArrayName="notifyBackOptions">
<div *ngFor="let option of notifyBackOptionsArr.controls; let i=index">
<input [formControlName]="i" type="checkbox" class="form-check-input" />
<label> {{notifyBackAlertOptions[i].name}} </label>
</div>
</div>
<div class="form-group">
<label for="notifyBackEmail">Where shall we send the alerts?</label>
<input
type="email"
class="form-control"
formControlName="notifyBackEmail"
/>
</div>
<div class="form-check" *ngFor="let option of discontinuedAlertOptions;">
<label>
<input
formControlName="discontinuedOption"
class="form-check-input"
type="radio"
name="discontinuedOption"
[value]="option.value"
/>
{{option.name}}
</label>
</div>
<div class="float-left">
<button class="btn btn-primary mr-1">Update</button>
</div>
</form>
Working Example

Related

How to check all checkboxes on selecting one in Vuejs?

isCheckAll: false,
methods: {
checkAll: function(){
this.isCheckAll = !this.isCheckAll;
},
updateCheckall: function(){
this.isCheckAll = true;
},
<div class="checkbox-alignment-form-filter">
<input type="checkbox" id="three" class="vh-product" #click='checkAll()' v-model='isCheckAll' />
<label class="productlist-specific" for="three"
> : 2011</label
>
</div>
<div class="checkbox-alignment-form-filter2">
<input type="checkbox" id="four" class="vh-product" #change='updateCheckall()'/>
<label class="productlist-specific" for="four">E0A</label>
</div>
<div class="checkbox-alignment-form-filter3">
<input type="checkbox" id="five" class="vh-product" #change='updateCheckall()'/>
<label class="productlist-specific" for="five">BR</label>
</div>
<div class="checkbox-alignment-form-filter4">
<input type="checkbox" id="six" class="vh-product" #change='updateCheckall()'/>
<label class="productlist-specific" for="six">E11350A</label>
</div>
<div class="checkbox-alignment-form-filter5">
<input type="checkbox" id="seven" class="vh-product" #change='updateCheckall()'>
<label class="productlist-specific" for="seven">E0BR</label>
</div>
How to select check and uncheck all checkboxes, on selecting one checkbox.
I don't know what stopping me to proceed, For every checkbox taken one #click and sub checkboxes also taken one #click. and then in js i am toggling that condition to check and uncheck.
I think you had some problems with your logic, so let me explain this for you:
You need to have a v-model on all of them, to be able to control state updates (whenever a user clicks something it needs to be updated).
You don't need to have two methods for all of this, you can have just one to enable all of the checkboxes. The rest can be handled by v-model.
The following code will enable/disable all of the checkboxes by clicking the first one, and if you click the other ones, it'll just enable/disable that particular checkbox.
<div id="app">
<input type="checkbox" v-model="checkboxes[0]" v-on:click="checkAll()">
<input type="checkbox" v-model="checkboxes[1]">
<input type="checkbox" v-model="checkboxes[2]">
<input type="checkbox" v-model="checkboxes[3]">
</div>
const app = new Vue({
el: "#app",
data: {
// control the state of checkboxes
checkboxes: [false, false, false, false]
},
methods: {
checkAll: function(){
for(let i = 1; i < this.checkboxes.length; i++){
// update all of the checkboxes to the value of the first one.
this.checkboxes[i] = !this.checkboxes[0];
}
}
}
})
your problem was your data handling.
i made you a playable example in CodeSandbox.
with this you are able to control the main checkbox bidirectional.
if all sub checkboxes are true the main checkbox will be set to true as well.
<template>
<div id="app">
<div class="checkbox-group">
<div class="main-checkbox">
<input
type="checkbox"
:name="checkboxes.main.label"
v-model="checkboxes.main.value"
#input="toggleAllCheckboxes()"
/>
<label :for="checkboxes.main.label">{{ checkboxes.main.label }}</label>
</div>
<div
class="sub-checkbox"
v-for="(checkbox, index) in checkboxes.sub"
:key="index"
>
<input
type="checkbox"
:name="checkbox.label"
v-model="checkbox.value"
/>
<label :for="checkbox.label">{{ checkbox.label }}</label>
</div>
</div>
</div>
</template>
<script>
export default {
name: "App",
data: () => ({
checkboxes: {
main: {
value: false,
label: ":2011",
},
sub: [
{
value: false,
label: "E0A",
},
{
value: false,
label: "BR",
},
{
value: false,
label: "E11350A",
},
{
value: false,
label: "E0BR",
},
],
},
}),
computed: {
mainIsChecked: function () {
return this.checkboxes.main.value;
},
allSubsAreChecked: function () {
return this.checkboxes.sub.every((checkbox) => checkbox.value);
},
},
watch: {
allSubsAreChecked: function () {
this.checkboxes.main.value = this.allSubsAreChecked;
},
},
methods: {
toggleAllCheckboxes() {
for (const checkbox of this.checkboxes.sub) {
checkbox.value = !this.mainIsChecked;
}
},
},
};
</script>
If you are working with multiple checkboxes, you should bind them to the same Array using v-model according to the Vue docs. https://v2.vuejs.org/v2/guide/forms.html#Checkbox
In your case, this means that you should have one array managing all the checkboxes except the one which controls selecting them all, that should be handled differently.
Each of those checkboxes should have a value binding as well, telling Vue which value they represent in the array.
You could also store all the possible checkbox values in an array which would help you reducing code duplication in your template.
You can render all 4 checkboxes which can be separately checked using a v-for loop, and manage them with the same v-model.
The checkbox controlling the "all checked" state should be managed with a different v-model. When that checkboxes value changes, we should check/uncheck all the other checkboxes, which can be done easily using a watcher.
Here's an example using this approach https://codesandbox.io/s/inspiring-dijkstra-fzkmn?file=/src/App.vue
export default {
name: "App",
data: () => ({
selectedValues: [],
allChecked: false,
}),
computed: {
isAllChecked() {
return false;
},
allCheckboxOptions() {
return [
{
label: "E0A",
value: "four",
},
{
label: "BR",
value: "five",
},
{
label: "E11350A",
value: "six",
},
{
label: "E0BR",
value: "seven",
},
];
},
},
methods: {
checkAll() {
this.selectedValues = this.allCheckboxOptions.map(
(option) => option.value
);
},
checkNone() {
this.selectedValues = [];
},
},
watch: {
allChecked(isAllChecked) {
if (isAllChecked) {
this.checkAll();
} else {
this.checkNone();
}
},
},
};
<template>
<div id="app">
<div class="checkbox-alignment-form-filter">
<input
type="checkbox"
id="three"
class="vh-product"
v-model="allChecked"
/>
<label class="productlist-specific" for="three"> : 2011</label>
</div>
<template v-for="(checkboxOption, index) in allCheckboxOptions">
<div
:key="checkboxOption.value"
:class="`checkbox-alignment-form-filter${index + 2}`"
>
<input
type="checkbox"
:id="checkboxOption.value"
:value="checkboxOption.value"
v-model="selectedValues"
class="vh-product"
/>
<label class="productlist-specific" for="four">
{{ checkboxOption.label }}
</label>
</div>
</template>
checked: {{ selectedValues }}
</div>
</template>

Angular 7 dynamic form validation using reactive forms

I am currently developing a dynamic form builder it has an admin side and a user side, on the admin side you can set the name of the input and type eg: first name and textbox you can add or take away from fields and there are a set amount of types you can choose from, when you are done the admin will then save the form to the Database. On the user frontend the from is pulled from the Database and using patchValue the from is set the problem I am having is with the user's input to the questions on the form. I am trying to figure out how I can first set the type and placeholder of the input that the user uses to answer the questions.
As well as dynamically apply validation rules to each input, so, for instance, a user wants to fill out a form with their first name, last name, age, and CV for the first and last name I want to have required and a regex expression to only allow certain characters, for the age I want to have a min and max number and cv required. and when the user submits the form I only want to send the name and value of an input and not the whole form.
But keep in mind that these forms are completely dynamic so I need to validate the user's input based on the input type that is set
/*Typescript Code*/
import { Component, OnInit } from '#angular/core';
import { FormArray, FormBuilder, FormGroup, Validators } from '#angular/forms';
import { FormService} from '../form.service';
import { ActivatedRoute } from '#angular/router';
#Component({
selector: 'app-reactive',
templateUrl: './reactive.component.html',
styleUrls: ['./reactive.component.scss']
})
export class ReactiveComponent implements OnInit {
public form: FormGroup;
public fieldList: any;
types: Array<any>;
formData: any;
Param: string;
get contactFormGroup() {
return this.form.get('inputs') as FormArray;
}
constructor(
private route: ActivatedRoute,
private fb: FormBuilder,
private api: FormService) { }
ngOnInit() {
/*First I initlize the form*/
this.form = this.fb.group({
name: [null, Validators.compose([Validators.required])],
organization: [null, Validators.compose([Validators.required])],
inputs: this.fb.array([this.createForm()])
});
this.route.paramMap.subscribe(params => {
this.Param = params.get('id');
this.getForm(this.Param);
});
// set fieldslist to this field
this.fieldList = this.form.get('inputs') as FormArray;
}
// formgroup
/*The I initilize the form array of inputs*/
createForm(): FormGroup {
return this.fb.group({
type: [null, Validators.compose([Validators.required])],/*These come from the DB*/
name: [null, Validators.compose([Validators.required])],/*These come from the DB*/
value: [null, Validators.compose([Validators.required])] /*This gets filled in by the user*/
});
}
/*The I use my API service to call the DB using the form Id*/
getForm(id) {
this.api.getForm(id).subscribe(
(data: any) => this.setForm(data)
);
}
getFieldsFormGroup(index): FormGroup {
const formGroup = this.fieldList.controls[index] as FormGroup;
return formGroup;
}
/*Here I set my form data*/
setForm(data) {
const d = data.results;
this.form.patchValue({
name: [d[0].form_name],
organization: [d[0].org],
});
this.form.setControl('inputs', this.setExistingFields(d[0].fields));
}
/*Here I set my inputs array data*/
setExistingFields(fields: any): FormArray {
const formArray = new FormArray([]);
this.fieldList = formArray;
fields.forEach(f => {
formArray.push(this.fb.group({
name: f.name,
type: f.type,
value: f.value
}));
});
return formArray;
}
submit() {
/* Here when I send my form back to the server I only want to send the inputs name and value nothing else */
console.log(this.form.value.inputs);
}
}
/*HTML CODE*/
<div class="container p-1">
<div class="row">
<div class="col-12">
<form class="md-form" [formGroup]="form" #submittion (submit)="submit()">
<div class="card">
<div class="card-header">Form Name</div>
<div class="card-body">
<div class="row">
<div class="form-group col-6">
<i class="fab fa-wpforms prefix"></i>
<input class="form-control" placeholder="Form Name" formControlName="name" type="text" readonly>
</div>
<div class="form-group col-6">
<i class="far fa-building prefix"></i>
<input class="form-control" placeholder="Organization" formControlName="organization" type="text"
readonly>
</div>
</div>
</div>
<div class="card-header col-12">Please fill in all fields. </div>
<div class="card-body" formArrayName="inputs">
<div class="row">
<div class="col-6" *ngFor="let contact of contactFormGroup.controls; let i = index;">
<div [formGroupName]="i" class="row z-depth-1 m-1 p-1">
<div class="form-group col-6">
<input class="form-control" formControlName="name" type="text" readonly>
</div>
<div class="form-group col-6">
<1-- This is the input that the user will fill in and I am not sure how to dynamically set the type or placeholder this is what I am trying to achieve -->
<input class="form-control" type="" placeholder=""
formControlName="value" />
</div>
</div>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
<div class="icon-bar round">
<button class="btn btn-success round m-1" type="submit" (click)="submit()"><i class="fas fa-save"> Save Form</i></button>
</div>
Any help is very much appricated on this I have been trying to figure it out for a while now, thank you

Return all object values within selected selected property for Angular reactive form

I have a simple dynamic radio button, in my example 3 are created from returning data.
When a radio value is selected it populates my reactive form with that value, "yes", "no" or "maybe". In theses objects there are other properties that I would like to be sent back as part of the form.
As below I return section, but also want to return sectionCode of the matched section.
Before radio selection is changed my form shows all properties correctly, but obviously I can only pass back one value, so as soon as I do the other object items are lost.
https://stackblitz.com/edit/angular-r4g6uv?file=src%2Fapp%2Fpersonal%2Fpersonal.component.ts
section:[
{
section: "yes",
sectionCode: "1"
},
{
section: "no",
sectionCode: "2"
},
{
section: "maybe",
sectionCode: "3"
}
]
component.html
<div formGroupName="radioButtons" class="form-group col-6 pl-0 pt-3">
<h2 class="mb-2">radioButtons</h2>
<label for="intel-form-submitter" appRequiredLabel>select</label><br />
<div class="form-check-inline" *ngFor="let item of personal.radioButtons.section">
<label for="{{item.section}}" class="col-12 customradio"
><span>{{item.section}}</span>
<input value="{{item.section}}" id="{{item.section}}" type="radio" formControlName="section"/>
<span class="checkmark"></span>
</label>
</div>
</div>
component.ts
createEntry(formBuilder: FormBuilder) {
return this.formBuilder.group({
title: this.personal.title,
creationDate: this.personal.creationDate,
radioButtons: this.formBuilder.group({
section: this.personal.radioButtons.section,
})
});
}
hope this helps you.
Bind the object instead of one value in the object. In Component.html
[value]="item"
Component.html
<form [formGroup]="form" novalidate>
<div formGroupName="radioButtons" novalidate>
<div class="form-check-inline" *ngFor="let item of section">
<label for="{{item.section}}" class="col-12 customradio"
><span>{{item.section}}</span>
<input **[value]="item"** id="{{item.section}}" type="radio" formControlName="section"/>
<span class="checkmark"></span>
</label>
</div>
</div>
<button type="button" (click)="save()" >Save</button>
</form>
{{form.value|json}}
Component.ts
xport class AppComponent implements OnInit {
section = [
{
section: "yes",
sectionCode: "1"
},
{
section: "no",
sectionCode: "2"
},
{
section: "maybe",
sectionCode: "3"
}
];
selectedSection: any;
form: FormGroup;
constructor(public formBuilder: FormBuilder) {}
ngOnInit() {
this.form = this.createEntry();
}
createEntry() {
return this.formBuilder.group({
radioButtons: this.formBuilder.group({
section: this.section[0]
})
});
}
save(){
console.log(this.form)
}
}
Find project in - https://stackblitz.com/edit/k-react-form-radio-test-5bixbv
You can bind the object itself instead of just section name.
<input [value]="item" id="{{item.section}}" type="radio"/>
Demo : https://stackblitz.com/edit/angular-h1mwhe

Can't get value from radio button ionic 3

I'm trying to make multiple choice quiz app with ionic but i confuse how to get score, i think i can get the selected value in radio button to making a score. but i dont understand how to get value from selected radio button in ionic with ngFor loop from database.
this is my html file
<ion-header>
<ion-navbar>
<ion-title>Quiz <ion-icon name="more" (click)="showMore($event)" style="float:right; cursor:pointer; width:20px;"></ion-icon></ion-title>
</ion-navbar>
</ion-header>
<ion-content padding>
<div *ngFor="let item of items">
<p class="pertanyaan">{{ item.pertanyaan }} </p><br>
<form>
<div>
<input type="radio" name="jwb1" id="jwb1" value="A" [(ngModel)]="jawaban"/>
<label for="jwb1">{{ item.jawaban1 }}</label>
</div>
<div>
<input type="radio" name="jwb1" id="jwb2" value="B" [(ngModel)]="jawaban"/>
<label for="jwb2">{{ item.jawaban2 }}</label>
</div>
<div>
<input type="radio" name="jwb1" id="jwb3" value="C" [(ngModel)]="jawaban"/>
<label for="jwb3">{{ item.jawaban3 }}</label>
</div>
<div>
<input type="radio" name="jwb1" id="jwb4" value="D" [(ngModel)]="jawaban"/>
<label for="jwb4">{{ item.jawaban4 }}</label>
</div>
<br>
<br>
<br>
<br>
<br>
</form>
</div>
<button ion-button name="submit" ng-model="submit" (click)="konfirmasiKirim()">Submit</button>
</ion-content>
and this my ts file
import { Component } from '#angular/core';
import { IonicPage, NavController } from 'ionic-angular';
import { Http } from '#angular/http';
import 'rxjs/add/operator/map';
import { NavParams } from 'ionic-angular/navigation/nav-params';
import { AlertController } from 'ionic-angular/components/alert/alert-controller';
#IonicPage()
#Component({
selector: 'page-quiz',
templateUrl: 'quiz.html',
})
export class QuizPage {
public username:any;
public jawaban:any;
public items : any = [];
constructor(public navCtrl: NavController,
public http : Http,
public navParams: NavParams,
public alertCtrl: AlertController)
{
}
ionViewDidLoad() {
this.username = this.navParams.get('username');
}
ionViewWillEnter()
{
this.load();
}
// Retrieve the JSON encoded data from the remote server
// Using Angular's Http class and an Observable - then
// assign this to the items array for rendering to the HTML template
load()
{
this.http.get('https://apifisika.000webhostapp.com')
.map(res => res.json())
.subscribe(data =>
{
this.items = data;
});
}
konfirmasiKirim()
{
let confirm = this.alertCtrl.create({
title: 'Kirim Jawaban?',
message: 'Hai '+this.username+' sudah yakin untuk kirim jawaban?',
buttons: [
{
text: 'Cek kembali',
handler: () => {
console.log('mengko');
}
},
{
text: 'Kirim Jawaban',
handler: () => {
// this.navCtrl.push(QuizPage);
alert(this.jawaban);
}
}
]
});
confirm.present();
}
}
In my database there are 10 question, So i need to get value from each question, for example number 1 i choose a, number 2 b, ... number 10 i choose d. And then i get all the value above to make a scoring.
Can someone help me to get selected value in radio button with *ngFor loop? pls
tweak the code of this.items = data; to be
this.items.forEach(item => {
item.isSelected = false;
});
and in your HTML do ngModel on this value like so:
<form>
<div *ngFor="let item of items">
<p class="pertanyaan">{{ item.pertanyaan }} </p>
<div>
<input type="radio" name="jwb{{ item.jawaban1 }}" id="jwb{{ item.jawaban1 }}" [(ngModel)]="item.isSelected"/>
<label for="jwb1">{{ item.jawaban1 }}</label>
</div>
</div>
</form>
and to display the results as a score of total:
<h3>{{ totalSelected() }}</h3>
and in your ts within your class add the following function:
totalSelected(){
return this.items.filter(item => return item.isSelected).length
}
That's it, I'm ready to tweak it for you if you want to calculate each selected answer passed on some certain condition and/or return a specific weight for the correct answer

Angular 2 form field validation with data dash attribute

I am trying to figure out form validation in Angular 2. Right now I have an add product form that is adding a product to a particular store's inventory. I want to validate that the product's price is more than the wholesale price of the product, so I added the wholesale price as a data-* attribute. What I can't figure out is how to reference that attribute with Angular 2's NgControl. Here is my code:
...
<div class="four wide field">
<label>Wholesale Price</label>
<div class="ui left icon input">
<input type="text" disabled [(ngModel)]="wholesalePrice" ngControl="wholesale" />
<i class="dollar icon"></i>
</div>
</div>
<div class="four wide field">
<label>Price</label>
<div class="ui left icon input">
<input type="text" [(ngModel)]="retailPrice" [attr.data-wholesale]="wholesalePrice" ngControl="price" required />
<i class="dollar icon"></i>
</div>
</div>
...
...
constructor(private fb: FormBuilder) {
this.form = fb.group({
price: ['price', Validators.compose([
Validators.required,
this.validator_moreThanWholesale
])],
quantity: ['quantity']
});
}
...
I would create a global validator for your form:
this.form = fb.group({
price: ['price', Validators.compose([
Validators.required
])],
quantity: ['quantity']
}, {
validator: this.validator_moreThanWholesale
}));
The validator_moreThanWholesale method will have all the controls as input so you will be able to check:
validator_moreThanWholesale(group: ControlGroup) {
var wholesale = group.controls.wholesale.value;
var price = group.controls.price.value;
return (price < wholesale) ? { moreThanWholesale: true } : null
}
See this question for more details:
Validators same as other value in form

Categories

Resources