Angular Radio Buttons form with unknown number of elements - html

I am new to Angular and I have an issue with the radio buttons.
I have an array of strings which I want to use to create a form with radio buttons. I do not know the length or the content of the array - the values are taken from an external service.
How can I do this using a form builder in my Angular Component and in html file? Can it be something like this?
question-dto.ts
export class QuestionDto {
questionText: string;
questionOptions: string[];
}
quiz.component.ts
question: QuestionDto = new QuestionDto();
questionOptionsForm: any;
constructor(private quizService: QuizService,
private formBuilder: FormBuilder) { }
ngOnInit() {
this.initForm();
}
initForm() {
this.questionOptionsForm = this.formBuilder.group({
//INIT RADIO BUTTONS HERE
})
}
quiz.component.html
<div>
<p>
Answers
</p>
<form *ngIf="questionOptionsForm">
<div *ngFor="let option of question.questionOptions">
<label>
<input type="radio" class="form-control">
{{option}}
</label>
</div>
</form>
</div>

You should try FormArray https://alligator.io/angular/reactive-forms-formarray-dynamic-fields/
code would look like:
initForm() {
this.questionOptionsForm = this.formBuilder.group({
options: this.formBuilder.array([this.createOption()])
});
}
createOption():FormGroup {
return this.formBuilder.group({
name: '',
description: ''
});
}

Related

Validation not working for checkbox in angular

Validation not working for checkbox.
When checkbox is not selected, form is getting submitted. Also not showing error.
HTML Code
<form [formGroup]="feedbackFormWithArray" (ngSubmit)="submitData()">
<label class="form-control-label"><span class="text-danger">*</span>Recommendation:</label>
<div class="recommendation-div">
<div class="rec-div" *ngFor="let recommendation of recommendations">
<label class="container">{{recommendation.listTypeValueName}}
<input formControlName="recommendation" type="checkbox" value="{{recommendation.listTypeValueId}}"
[checked]="recommendation.selected" (change)="isAllSelected(recommendation)" />
<span class="mark"></span>
</label>
<div *ngIf="(submitted && recommendation.invalid)">
<small *ngIf="recommendation.errors?.required" class="text-danger">Please select recommendation</small>
</div>
</div>
</div>
</form>
TS Code
import { Validators } from '#angular/forms';
submitted : boolean = false;
this.feedbackFormWithArray= this.fb.group({
recommendation: ['', [Validators.required]]
});
submitData() {
this.submitted = true;
}
get recommendation()
{
return this.feedbackFormWithArray.get('recommendation');
}
How to solve this?
Thank you!
I think replace feedbackForm with feedbackFormWithArray in 3 rd line of ts code
import { Validators } from '#angular/forms';
submitted : boolean = false;
//in reactive form you should take care the form controller's name.
this.feedbackFormWithArray= this.fb.group({
recommendation: ['', [Validators.required]]
});
submitData() {
this.submitted = true;
}
get recommendation()
{
return this.feedbackFormWithArray.get('recommendation');
}
There is a static property in class Validators called "requiredTrue".
this.feedbackFormWithArray= this.fb.group({
recommendation: ['', [Validators.requiredTrue]]
});
#description
Validator that requires the control's value be true. This validator is commonly used for required checkboxes.
You can check more about it here https://angular.io/api/forms/Validators

Create common input field in Angular

I have created common input field which can be usable across app and it will work with or without reactive form.
For e.g.: I have created common input angular component but it works with reactive form only as it has formGroup and formControlName.
input.html:
<div class="form-control" *ngIf="isRequired" [formGroup]="parentForm">
<label class="label">
{{labelText}}
</label>
<input [type]="inputType" class="input" [control]="control" [formControlName]="formControlNameText" [placeholder]="placeholderText">
</div>
input.ts:
import { Component, OnInit, Input } from '#angular/core';
#Component({
selector: 'app-input',
templateUrl: './input.component.html',
styleUrls: ['./input.component.scss'],
})
export class InputComponent implements OnInit {
#Input() parentForm: any;
#Input() labelText: string;
#Input() inputType: string = "text";
#Input() formControlNameText: string;
#Input() placeholderText: string;
#Input() isRequired: boolean = false;
#Input() requiredMsg: string;
#Input() maxlengthMsg: string;
#Input() control: any;
#Input() type: string;
constructor() { }
ngOnInit() { }
}
Call from one of my form Page:
<app-input parentForm="surveyResponseForm" labelText="test" inputType="text" placeholderText="placeholder"
formControlNameText="authorName" control="" isRequired=true id="authorName">
</app-input>
How do I use this common input if I want to use this without form?
I mean how do I use this selector: app-input in a component which doesn't have any form.
You could add an #Input() property to your component, for example useInForm: boolean, and check its value in your template. If useInForm is true, you would use the [formGroup] and formControlName properties, otherwise you would use a regular element without those properties example in the withoutFormTemplate.
<div class="form-control" *ngIf="isRequired && useInForm; else withoutFormTemplate" [formGroup]="parentForm">
<label class="label">
{{labelText}}
</label>
<input [type]="inputType" class="input" [control]="control" [formControlName]="formControlNameText" [placeholder]="placeholderText">
</div>
<ng-template #withoutFormTemplate>
<input [(ngModel)]="control" [type]="inputType" class="input" [placeholder]="placeholderText">
<ng-template>
I solve more or less same problem by creating a FormGroup. It's not easiest solution, but that working on multiple scenarios.
If not, maybe that give you some clues how to solve your problem...
Form component ts
// Reactive Forms
form: FormGroup;
constructor(private formBuilder: FormBuilder) { }
ngOnInit() {
// Form structure and validators
this.form = this.formBuilder.group({
'user' : this.formBuilder.group({
'username' : ['', Validators.required],
'email' : ['', [Validators.required, Validators.email]]
}),
'identity' : this.formBuilder.group({
'firstname' : ['', Validators.required],
'lastname' : ['', Validators.required],
'address' : this.formBuilder.group({
'street' : ['', Validators.required],
'city' : ['', Validators.required],
})
})
});
}
onSubmit() {
// Get object with same structure as form but only with values
console.log(this.form.value);
alert('Form is ' + (this.form.invalid ? 'invalid' : 'valid'));
}
Form component html
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<form-text [formGroupParent]="form.get(['user'])"
[formGroupControlName]="'username'">
</form-text>
<form-text [formGroupParent]="form.get(['user'])"
[formGroupControlName]="'email'">
</form-text>
<hr>
<form-text [formGroupParent]="form.get(['identity'])"
[formGroupControlName]="'firstname'">
</form-text>
<form-text [formGroupParent]="form.get(['identity'])"
[formGroupControlName]="'lastname'">
</form-text>
<hr>
<form-text [formGroupParent]="form.get(['identity','address'])"
[formGroupControlName]="'street'">
</form-text>
<form-text [formGroupParent]="form.get(['identity','address'])"
[formGroupControlName]="'city'">
</form-text>
<button type="submit">Submit</button>
</form>
Custom input component ts (form-text)
// Needed to bind formControlName
#Input() formGroupParent: FormGroup;
#Input() formGroupControlName: string;
// FormControl store validators
control: FormControl;
ngOnInit() {
// Fetch Form control (validator) from FormGroup parent
this.control = <FormControl>this.formGroupParent.get(this.formGroupControlName);
}
Custom input component html (form-text)
<ng-container [formGroup]="formGroupParent">
<label>{{formGroupControlName}}</label>
<input type="text" formControlName="{{formGroupControlName}}">
</ng-container

Reactive form with dynamic data change from component

I am setting up a reactive form in angular 6, where I have 2 input boxes(one input is an optional entry) and a submit button. If I enter a value to one input box and press submit, I need to fill the other input box by setting corresponding value from component side. If I enter values to both input boxes, then another function is called. If so how is two-way data bindin possible in form controls? I tried using ngModel, which is not working as expected and from stackoverflow answers, came to know that using ngmodel with form controls is soon to be deprecated. How Can I achieve the same if so? Below is the code snippet I am using:
Component.ts:
export class myComponent implements OnInit {
converterForm: FormGroup;
model: myModel = new MyModel();
constructor(private formBuilder: FormBuilder, ) {
this.myForm = this.formBuilder.group({
vOne: [this.model.vOne],
vTwo: [this.model.vTwo],
});
}
onSubmit(searchInputs) {
this.model.vTwo= "new"; //I need to edit the form value and reflect it in html.. two-waybinding
this.converterForm.value.vOne = "edited";
console.log("Submit called");
}
}
html file:
<div>
<div>
<form [formGroup]="myForm" (ngSubmit)="onSubmit(myForm.value)">
<div>
<div>
<mat-form-field>
<input id="vOne" matInput formControlName="vOne" [(ngModel)]="model.vOne">
</mat-form-field>
</div>
<div>
<mat-form-field>
<input id="vTwo" matInput formControlName="vTwo" [(ngModel)]="model.vTwo">
</mat-form-field>
</div>
</div>
<div>
<button mat-raised-button color="primary" type="submit" (click)="search()">
<mat-icon aria-label="Search icon">search </mat-icon>
Search
</button>
</div>
</form>
</div>
thanks in advance.
Using valueChanges for access to live changes, and using setValue func for setting value per input.
in ts file try:
export class myComponent implements OnInit {
myForm: FormGroup;
constructor(private formBuilder: FormBuilder) {
this.myForm = this.formBuilder.group({
vOne: [null],
vTwo: [null],
});
searchHandler();
}
searchHandler() {
const searchInputs = {
vOne: '',
vTwo: '',
};
for (const propertyName in searchInputs) {
const property = this.form.get(propertyName);
property.valueChanges
.subscribe((searchText) => {
// this.form.controls.vOne.setValue('whatever');
// this.form.controls.vTwo.setValue('whatever');
// searchText is what keypress in input tag
});
}
}
onSubmit() {
// this.form.controls.vOne.setValue('whatever');
// this.form.controls.vTwo.setValue('whatever');
}
}
in html file try:
<form [formGroup]="myForm" (ngSubmit)="onSubmit()">
<div>
<mat-form-field>
<input matInput formControlName="vOne">
</mat-form-field>
</div>
<div>
<mat-form-field>
<input matInput formControlName="vTwo">
</mat-form-field>
</div>
</form>

Is there a way to save the data from dropdownlist-input field?

So I have this HTML field where I can Input some text. I can send it to API and it has no problem with it. However is it possible to have the input field as a dropdown list and get the same outcome?
I have tried one way, but I cant still bind 2 properties into my NgModel to get the data going into my API. So I had these hardcoded lists in my .ts file to test if this method works. I get the value into my input field, but I cannot send it to API.
HTML
<div fxLayout="row" fxLayoutAlign="center start">
<div fxLayout="column" class="term-detail">
<ejs-dropdownlist [dataSource]='data' [(value)]='value' placeholder='Select a Term'></ejs-dropdownlist>
<div style='margin-top: 50px'>
<input matInput type="text" [(ngModel)]="value" />
</div>
</div>
</div>
<button (click)="onSave()">Tallenna</button>
TYPESCRIPT FILE
export class SelectTermComponent implements OnInit {
terms: Term;
constructor(private service: TermService, private httpService: TermHttpService, private router: Router ) {
this.terms = new Term();
}
public data: string[] = ['Test 1', 'Test 2', 'Test 3', 'Test 4'];
public value: 'Badminton';
ngOnInit() {
}
onSave() {
console.log('data sent to DB!');
this.httpService.create(this.terms).subscribe(result => {
console.log(result);
});
this.router.navigate(['termselected']);
console.log('navigated to termSelected');
}
This is how it works without the dropdownlist select:
<mat-form-field floatPlaceholder="auto">
<input matInput [(ngModel)]="terms.source" required
placeholder="Lähtökäsite">
</mat-form-field>
Term.ts
export class Term {
termId: number;
termValue: string;
termArrow: string;
source: number;
target: number;
constructor(termId?: number, termValue?: string, termArrow?: string, source?: number, target?: number) {
this.termId = termId;
this.termValue = termValue;
this.termArrow = termArrow;
this.source = source;
this.target = target;
}
}
I expect the data to go into the API but since ngModel has the 'value' binding instead of 'term.x' I think it is not the way it should be. Any ideas?

Angular 2 dialog popup with text field input

I have a simple popup that shows a message with OK button to release it, what I want is to add to this popup a text field that the user will need to type something in it and click OK to submit his answer.
currently it looks like this:
#Component({
selector: 'dialog-component',
template: `<h2>{{title}}</h2>
<p>{{message}}</p>
<button md-button (click)="dialog.close()">OK</button>`
})
export class DialogComponent {
public title: string;
public message: string;
constructor( public dialog: MdDialogRef<DialogComponent>) { }
}
and im using it like:
public showDialog(message: MessageBoxMessage) {
if (typeof message !== 'undefined') {
let dialogRef: MdDialogRef<DialogComponent> = this._dialog.open(DialogComponent);
dialogRef.componentInstance.title = message.title;
dialogRef.componentInstance.message = message.content;
}
}
how can I change it to have a popup with text-field and ok button tht pass me the value of the text-field?
You can create EventEmitter in your dialog:
#Component({
selector: 'dialog-component',
template: `<h2>{{title}}</h2>
<p>{{message}}</p>
<mat-form-field class="example-full-width">
<input matInput placeholder="Favorite food" #input>
</mat-form-field>
<button mat-button (click)="onOk.emit(input.value); dialog.close()">OK</button>`
})
export class DialogComponent {
public title: string;
public message: string;
onOk = new EventEmitter();
constructor( public dialog: MatDialogRef<ErrorDialogComponent>) { }
}
then subscribe to it in parent component
dialogRef.componentInstance.onOk.subscribe(result => {
this.resultFromDialog = result;
})
Plunker Example
Another way is passing value to MatDialog.close method
(click)="dialog.close(input.value)"
....
dialogRef.afterClosed().subscribe(result => {
this.resultFromDialog = result;
});
Plunker Example
You can bind the answer to a model like this :
#Component({
selector: 'dialog-component',
template: `<h2>{{title}}</h2>
<p>{{message}}</p>
<input type="text" name="data" [(ngModel)]="data">
<button md-button (click)="dialog.close()">OK</button>`
})
export class DialogComponent {
public title: string;
public message: string;
data: string;
constructor( public dialog: MdDialogRef<ErrorDialogComponent>) { }
}
And then bind the (click) to a function that sends your data.