Related
I have angular code something like this: and I want to populate checkbox ticked for the option which are default selected
export class InformationComponent implements OnInit{
dataIds: FormControl;
#Input()
requestBody:Request;
requestForm: FormGroup;
constructor(private _formBuilder: FormBuilder){}
ngOnInit(){
this.dataIds = new FormControl(this.requestBody.dataIds);
this.requestForm = this._formBuilder.group({
dataIds: this.dataIds;
})
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
<form [formGroup]="reqForm" #mainForm>
<div>
<app-data-list [dataIds]="dataIds" [disabled]="disabled"> </app-data-list>
</div>
</form>
and below is my app-data-list component
export class EntityListComponent implemments OnInit, OnDestroy{
#Input()
public disabled:boolean;
public entitiesFilter:FormControl = new FormControl();
protected entityList = new RequestEntity[];
#Input()
public dataIds:FormControl;
#ViewChild('multiSelect', {static:true}) multiselect:MatSelect;
protected _onDestroy = new Subject<void>();
public filteredEntityList:ReplaySubject<RequestEntity[]> = new ReplaySubject<RequestEntity[]>
ngOnInit(){
this.myservice.getData().subscribe((resp:RequestEntity[])=>{
entityList = resp;
})
this.filteredEntityList.next(this.entityList.slice());
this.entitiesFilter.valueChanges.pipe(takeUntil(this._onDestroy)).subscribe(() => { this.filterEntitiesList();});
}
ngAfterViewInit(){
this.setInitialValue();
}
setInitialValue(){
this.filteredEntitiesList.pipe(take(1), takeUntil(_onDestroy)).subscribe(() => {
this.multiSelect.compareWith =(a:RequestEntity, b:RequestEntity) => a && b && a.entity == b.entity;
})
}
}
<div [formGroup]="form">
<mat-form-field>
<mat-label> Data ids</mat-label>
<mat-select [formControl]="dataIds" [multiple]="true" #multiSelect [disabled]="disabled">
<ngx-mat-select-search [formControl]="entitiesFilter" placeholderLabel="Search">
</ngx-mat-select-search>
<mat-option *ngFor="let entity of filterdEntitiesList |async" [value]"entity"> {{entity?.entity?.entityName}} </mat-option>
</mat-select>
</mat-form-field>
</div>
but my code is not pre populating the checked option against selected dataIds from back end but I had similar snippet in my code which does exactly same thing
I am creating an HTML form in Angular. I am running into an issue. I want to be able to display a duplicate of an HTML block with a new form control at the click of a button.
Here is what thing look like now:
I would like the user to be able to click the button labeled Click me and have a duplicate of the HTML block display but with a different form control. Do you guys have any suggestions of how I can do that? Here is what I have so far.
import { Component, OnInit, Directive, ViewChild } from '#angular/core';
import { FormControl, FormGroupDirective, NgForm, Validators, FormBuilder, FormGroup } from '#angular/forms';
#Component({
selector: 'app-questions',
templateUrl: './questions.component.html',
styleUrls: ['./questions.component.scss']
})
export class QuestionsComponent implements OnInit {
jobForm: FormGroup = this.fb.group({
companyName: this.fb.control('', [Validators.required ]),
position: this.fb.control('', [Validators.required ]),
description: this.fb.control('', [Validators.required ]),
startDate: this.fb.control('', [Validators.required ]),
endDate: this.fb.control('', [Validators.required ])
});
constructor(private readonly fb: FormBuilder) { }
ngOnInit(): void {
}
displayForm() {
console.log(this.jobForm);
}
}
<h3>Education:</h3>
<form [formGroup]="jobForm">
<mat-form-field >
<mat-label>Company Name: </mat-label>
<input matInput type="text" formControlName="companyName"/>
<mat-error *ngIf="jobForm.controls.companyName.errors">Company name is required</mat-error>
</mat-form-field>
<mat-form-field >
<mat-label>Position: </mat-label>
<input matInput type="text" formControlName="position"/>
<mat-error *ngIf="jobForm.controls.position.errors">Position is required</mat-error>
</mat-form-field>
<mat-form-field >
<mat-label>Select start and end date:</mat-label>
<mat-date-range-input [rangePicker]="picker">
<input matStartDate placeholder="Start date" formControlName="startDate">
<input matEndDate placeholder="End date" formControlName="endDate">
</mat-date-range-input>
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
<mat-date-range-picker #picker></mat-date-range-picker>
</mat-form-field>
<mat-form-field >
<mat-label>Description: </mat-label>
<textarea matInput type="text" formControlName="description"></textarea>
<mat-error *ngIf="jobForm.controls.description.errors">Job description is required</mat-error>
</mat-form-field>
</form>
<button (click)="displayForm()">Click me</button>
After a user hits the click me button id like to generate a duplicate form so they can fill out the details.
Thanks
I believe what you're asking for is a way to dynamically add a form group to the page. If that is the case then the solution below should help.
You can use the *ngFor structural directive to iterate over a FormGroup array. The following adjustments will need to be made:
import { Component, OnInit, Directive, ViewChild } from '#angular/core';
import { FormControl, FormGroupDirective, NgForm, Validators, FormBuilder, FormGroup } from '#angular/forms';
#Component({
selector: 'app-questions',
templateUrl: './questions.component.html',
styleUrls: ['./questions.component.scss']
})
export class QuestionsComponent implements OnInit {
jobForms: FormGroup[] = []; // Declare an empty array
constructor(private readonly fb: FormBuilder) { }
ngOnInit(): void {
this.addFormRow(); // Add an empty form group to the array
}
//displayForm() {
// console.log(this.jobForm);
//}
// Add an additional row to the jobForms array - to be called from the template
addFormRow() {
this.jobForms.push(this.fb.group({
companyName: this.fb.control('', [Validators.required ]),
position: this.fb.control('', [Validators.required ]),
description: this.fb.control('', [Validators.required ]),
startDate: this.fb.control('', [Validators.required ]),
endDate: this.fb.control('', [Validators.required ])
}));
}
}
<h3>Education:</h3>
<form *ngFor="let formGroup of jobForms"
[formGroup]="formGroup">
<mat-form-field >
<mat-label>Company Name: </mat-label>
<input matInput type="text" formControlName="companyName"/>
<mat-error *ngIf="jobForm.controls.companyName.errors">Company name is required</mat-error>
</mat-form-field>
<mat-form-field >
<mat-label>Position: </mat-label>
<input matInput type="text" formControlName="position"/>
<mat-error *ngIf="jobForm.controls.position.errors">Position is required</mat-error>
</mat-form-field>
<mat-form-field >
<mat-label>Select start and end date:</mat-label>
<mat-date-range-input [rangePicker]="picker">
<input matStartDate placeholder="Start date" formControlName="startDate">
<input matEndDate placeholder="End date" formControlName="endDate">
</mat-date-range-input>
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
<mat-date-range-picker #picker></mat-date-range-picker>
</mat-form-field>
<mat-form-field >
<mat-label>Description: </mat-label>
<textarea matInput type="text" formControlName="description"></textarea>
<mat-error *ngIf="jobForm.controls.description.errors">Job description is required</mat-error>
</mat-form-field>
</form>
<!-- Call addFormRow() to add a FormGroup to the array -->
<button (click)="addFormRow()">Click me</button>
If you can use Lodash library it would be easy
//const newjobFormGroup = _.cloneDeep(jobForm) as FormGroup;
CopyJobForm(toCopyForm: FormGroup){
//return _.cloneDeep(jobForm) as FormGroup;
return _.cloneDeep(FormGroup) as FormGroup;
}
Or
this answer for a full deep clone
/**
* Deep clones the given AbstractControl, preserving values, validators, async validators, and disabled status.
* #param control AbstractControl
* #returns AbstractControl
*/
export function cloneAbstractControl<T extends AbstractControl>(control: T): T {
let newControl: T;
if (control instanceof FormGroup) {
const formGroup = new FormGroup({}, control.validator, control.asyncValidator);
const controls = control.controls;
Object.keys(controls).forEach(key => {
formGroup.addControl(key, cloneAbstractControl(controls[key]));
})
newControl = formGroup as any;
}
else if (control instanceof FormArray) {
const formArray = new FormArray([], control.validator, control.asyncValidator);
control.controls.forEach(formControl => formArray.push(cloneAbstractControl(formControl)))
newControl = formArray as any;
}
else if (control instanceof FormControl) {
newControl = new FormControl(control.value, control.validator, control.asyncValidator) as any;
}
else {
throw new Error('Error: unexpected control value');
}
if (control.disabled) newControl.disable({emitEvent: false});
return newControl;
}
I am trying to add a form field with custom telephone number input control. I used the example of the phone from https://material.angular.io/components/form-field/examples.
Here is the code:
<mat-form-field>
<example-tel-input placeholder="Phone number" required></example-tel-input>
<mat-icon matSuffix>phone</mat-icon>
<mat-hint>Include area code</mat-hint>
</mat-form-field>
import {FocusMonitor} from '#angular/cdk/a11y';
import {coerceBooleanProperty} from '#angular/cdk/coercion';
import {Component, ElementRef, Input, OnDestroy} from '#angular/core';
import {FormBuilder, FormGroup} from '#angular/forms';
import {MatFormFieldControl} from '#angular/material';
import {Subject} from 'rxjs';
/** #title Form field with custom telephone number input control. */
#Component({
selector: 'form-field-custom-control-example',
templateUrl: 'form-field-custom-control-example.html',
styleUrls: ['form-field-custom-control-example.css'],
})
export class FormFieldCustomControlExample {}
/** Data structure for holding telephone number. */
export class MyTel {
constructor(public area: string, public exchange: string, public subscriber: string) {}
}
/** Custom `MatFormFieldControl` for telephone number input. */
#Component({
selector: 'example-tel-input',
templateUrl: 'example-tel-input-example.html',
styleUrls: ['example-tel-input-example.css'],
providers: [{provide: MatFormFieldControl, useExisting: MyTelInput}],
host: {
'[class.example-floating]': 'shouldLabelFloat',
'[id]': 'id',
'[attr.aria-describedby]': 'describedBy',
}
})
export class MyTelInput implements MatFormFieldControl<MyTel>, OnDestroy {
static nextId = 0;
parts: FormGroup;
stateChanges = new Subject<void>();
focused = false;
ngControl = null;
errorState = false;
controlType = 'example-tel-input';
id = `example-tel-input-${MyTelInput.nextId++}`;
describedBy = '';
get empty() {
const {value: {area, exchange, subscriber}} = this.parts;
return !area && !exchange && !subscriber;
}
get shouldLabelFloat() { return this.focused || !this.empty; }
#Input()
get placeholder(): string { return this._placeholder; }
set placeholder(value: string) {
this._placeholder = value;
this.stateChanges.next();
}
private _placeholder: string;
#Input()
get required(): boolean { return this._required; }
set required(value: boolean) {
this._required = coerceBooleanProperty(value);
this.stateChanges.next();
}
private _required = false;
#Input()
get disabled(): boolean { return this._disabled; }
set disabled(value: boolean) {
this._disabled = coerceBooleanProperty(value);
this.stateChanges.next();
}
private _disabled = false;
#Input()
get value(): MyTel | null {
const {value: {area, exchange, subscriber}} = this.parts;
if (area.length === 3 && exchange.length === 3 && subscriber.length === 4) {
return new MyTel(area, exchange, subscriber);
}
return null;
}
set value(tel: MyTel | null) {
const {area, exchange, subscriber} = tel || new MyTel('', '', '');
this.parts.setValue({area, exchange, subscriber});
this.stateChanges.next();
}
constructor(fb: FormBuilder, private fm: FocusMonitor, private elRef: ElementRef<HTMLElement>) {
this.parts = fb.group({
area: '',
exchange: '',
subscriber: '',
});
fm.monitor(elRef, true).subscribe(origin => {
this.focused = !!origin;
this.stateChanges.next();
});
}
ngOnDestroy() {
this.stateChanges.complete();
this.fm.stopMonitoring(this.elRef);
}
setDescribedByIds(ids: string[]) {
this.describedBy = ids.join(' ');
}
onContainerClick(event: MouseEvent) {
if ((event.target as Element).tagName.toLowerCase() != 'input') {
this.elRef.nativeElement.querySelector('input')!.focus();
}
}
}
example-tel-input-example.html
<div [formGroup]="parts" class="example-tel-input-container">
<input class="example-tel-input-element" formControlName="area" size="3">
<span class="example-tel-input-spacer">–</span>
<input class="example-tel-input-element" formControlName="exchange" size="3">
<span class="example-tel-input-spacer">–</span>
<input class="example-tel-input-element" formControlName="subscriber" size="4">
</div>
But I get the following error:
ERROR Error: mat-form-field must contain a MatFormFieldControl.
Need to import two module and add those in imports and exports section.
import { MatFormFieldModule } from '#angular/material/form-field';
import { MatInputModule } from '#angular/material/input';
#NgModule ({
imports: [ MatFormFieldModule, MatInputModule ],
exports: [ MatFormFieldModule, MatInputModule ]
})
And the most thing which everybody miss this '/' character. if you see the Angular Material Documentation , they also miss this (Last Checked 16 Jun 2020, don't know they even updated or not). I make an example for clarifications
<!-- Wrong -->
<mat-form-field>
<input matInput>
</mat-form-field>
<!-- Right -->
<mat-form-field>
<input matInput />
</mat-form-field>
Look at the snippet carefully. when <input begin it must close with /> but most people miss the / (backslash) character.
Make sure MatInputModule is imported and <mat-form-field> contains <input> with matInput / matSelect directives.
https://github.com/angular/material2/issues/7898
Import MatInputModule, solved my error
You need to specify your class as a provider for MatFormFieldControl
https://material.angular.io/guide/creating-a-custom-form-field-control#providing-our-component-as-a-matformfieldcontrol
#Component({
selector: 'form-field-custom-control-example',
templateUrl: 'form-field-custom-control-example.html',
styleUrls: ['form-field-custom-control-example.css'],
providers: [
{ provide: MatFormFieldControl, useExisting: FormFieldCustomControlExample }
]
})
Also, don't forget to write the name attribute in the input tag:
name="yourName"
if you are using any 'input' tag in your code along with 'mat-form-field', make sure to include 'matInput' in the input tag
if there is any *ngIf present in child tag of 'mat-form-field', specify the *ngIf condition in 'mat-form-field' tag
Another possible issue closing the input tag. If you copy code from one of the Angular examples (https://material.angular.io/components/datepicker/overview), you may end up with the code:
<mat-form-field appearance="fill">
<mat-label>Choose a date</mat-label>
<input matInput [matDatepicker]="picker">
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
<mat-datepicker #picker></mat-datepicker>
</mat-form-field>
The input should have the close tag (slash):
<input matInput [matDatepicker]="picker" />
This will fix your issue
import {
MatFormFieldModule,
MatInputModule
} from "#angular/material";
Error says that mat-form-field should contain at least one form field from which input is taken.
Ex : matInput mat-select etc.
In your case you may add matInput tag to your input fields within example-tel-input-example.html. And also you could move mat-form-field inside example-tel-input-example component and add it against each matInput field.
<div [formGroup]="parts" class="example-tel-input-container">
<mat-form-field>
<input matInput class="example-tel-input-element" formControlName="area" size="3">
</mat-form-field>
<span class="example-tel-input-spacer">–</span>
<mat-form-field>
<input matInput class="example-tel-input-element" formControlName="exchange" size="3">
</mat-form-field>
<span class="example-tel-input-spacer">–</span>
<mat-form-field>
<input matInput class="example-tel-input-element" formControlName="subscriber" size="4">
</mat-form-field>
</div>
Note : mat-icon or mat-hint cannot be considered as form-fields
I recently learned Angular2. Currently, I have got a material modal after some searching. However,I can't find how I return the data that the user can input.
In the modal I currently have, there is one input field and one checkbox. When close the console logs "de dialog closed" and "true".
This is my modal HTML:
<h2 mat-dialog-title>Add Group</h2>
<mat-dialog-content>
<div>
<mat-form-field>
<input matInput placeholder="Groupname">
</mat-form-field>
<mat-form-field>
<mat-checkbox >Private group?</mat-checkbox>
</mat-form-field>
<mat-form-field>
<button mat-raised-button color="primary">img</button>
</mat-form-field>
</div>
</mat-dialog-content>
<mat-dialog-actions>
<button mat-button mat-dialog-close>cancel</button>
<!-- The mat-dialog-close directive optionally accepts a value as a result for the dialog. -->
<button mat-button [mat-dialog-close]="true" >save</button>
</mat-dialog-actions>
TS code:
import { Component, OnInit, Inject } from '#angular/core';
import { GroupsService} from '../../../services/Groups.service';
import { Groups } from '../../../models/groupModel'
import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '#angular/material';
#Component({
selector: 'app-groups',
templateUrl: './groups.component.html',
styleUrls: ['./groups.component.scss'],
})
export class GroupsComponent implements OnInit {
animal: string;
name: string;
groups: Groups[];
constructor(
private groupsService: GroupsService,
public dialog: MatDialog
){}
ngOnInit() {
this.groupsService.getMyGroups()
.then(group =>{
this.groups = group;
console.log(this.groups)
}).catch(error=>{
console.log(error);
});
}
openDialog(): void {
let dialogRef = this.dialog.open(DialogOverviewExampleDialog, {
height: '300px',
width: '300px',
});
dialogRef.afterClosed().subscribe(result => {
console.log('The dialog was closed');
console.log(result);
this.animal = result;
});
}
testgroup(id){
console.log(id)
}
acceptGroup(){
console.log('accept')
}
declineGroup(){
console.log('decline');
}
createGroup(){
console.log("sample");
}
}
#Component({
selector: 'dialog-overview-example-dialog',
templateUrl: './model.html',
})
export class DialogOverviewExampleDialog {
constructor(
public dialogRef: MatDialogRef<DialogOverviewExampleDialog>,
#Inject(MAT_DIALOG_DATA) public data: any) { }
onNoClick(): void {
this.dialogRef.close();
}
}
This part of your code inside HTML template is currently responsible for what you're passing back from the model: [mat-dialog-close]="true". As you see you're just passing true and nothing else. Simplest way to achieve what you want is to create form inside the dialog and pass its value.
<h2 mat-dialog-title>Add Group</h2>
<mat-dialog-content>
<form #modalForm="ngForm">
<div>
<mat-form-field>
<input matInput name="groupName" placeholder="Groupname" ngModel>
</mat-form-field>
<mat-form-field>
<mat-checkbox name="privateGroup" ngModel>Private group?</mat-checkbox>
</mat-form-field>
<mat-form-field>
<button mat-raised-button color="primary">img</button>
</mat-form-field>
</div>
</form>
</mat-dialog-content>
<mat-dialog-actions>
<button mat-button mat-dialog-close>cancel</button>
<button mat-button [mat-dialog-close]="modalForm.value">save</button>
</mat-dialog-actions>
You can also pass form value on submit (using (ngSubmit)="mySubmitFunction(modalForm.value)" on form tag ) to the mySubmitFunction defined in DialogOverviewExampleDialog. And then, after some kind of validation for example, pass data by using: this.dialogRef.close(someVariableContainingValues).
use this code i think it will be work:
dialogRef.afterClosed().subscribe(result => {
console.log('The dialog was closed');
console.log(result);
//this.animal = result;
}).()=>{
this.animal = result;
};
I would like to pass the parent component's FormGroup to its child for the purpose of displaying an error-message using the child.
Given the following parent:
parent.component.ts
import { Component, OnInit } from '#angular/core'
import {
REACTIVE_FORM_DIRECTIVES, AbstractControl, FormBuilder, FormControl, FormGroup, Validators
} from '#angular/forms'
#Component({
moduleId: module.id,
selector: 'parent-cmp',
templateUrl: 'language.component.html',
styleUrls: ['language.component.css'],
directives: [ErrorMessagesComponent]
})
export class ParentCmp implements OnInit {
form: FormGroup;
first: AbstractControl;
second: AbstractControl;
constructor(private _fb: FormBuilder) {
this.first = new FormControl('');
this.second = new FormControl('')
}
ngOnInit() {
this.form = this._fb.group({
'first': this.first,
'second': this.second
});
}
}
I would now like to pass the form:FormGroup variable above to the child component below:
error-message.component.ts
import { Component, OnInit, Input } from '#angular/core'
import { NgIf } from '#angular/common'
import {REACTIVE_FORM_DIRECTIVES, FormGroup } from '#angular/forms'
#Component({
moduleId: module.id,
selector: 'epimss-error-messages',
template: `<span class="error" *ngIf="errorMessage !== null">{{errorMessage}}</span>`,
styles: [],
directives: [REACTIVE_FORM_DIRECTIVES, NgIf]
})
export class ErrorMessagesComponent implements OnInit {
#Input() ctrlName: string
constructor(private _form: FormGroup) { }
ngOnInit() { }
get errorMessage() {
// Find the control in the Host (Parent) form
let ctrl = this._form.find(this.ctrlName);
console.log('ctrl| ', ctrl);
// for (let propertyName of ctrl.errors) {
// // If control has a error
// if (ctrl.errors.hasOwnProperty(propertyName) && ctrl.touched) {
// // Return the appropriate error message from the Validation Service
// return CustomValidators.getValidatorErrorMessage(propertyName);
// }
// }
return null;
}
The constructor formGroup represents the FormGroup of the parent - in its present form it does not work.
I am trying to follow this obsolete example at http://iterity.io/2016/05/01/angular/angular-2-forms-and-advanced-custom-validation/
In the parent component do this:
<div [formGroup]="form">
<div>Your parent controls here</div>
<your-child-component [formGroup]="form"></your-child-component>
</div>
And then in your child component you can get hold of that reference like so:
export class YourChildComponent implements OnInit {
public form: FormGroup;
// Let Angular inject the control container
constructor(private controlContainer: ControlContainer) { }
ngOnInit() {
// Set our form property to the parent control
// (i.e. FormGroup) that was passed to us, so that our
// view can data bind to it
this.form = <FormGroup>this.controlContainer.control;
}
}
You can even ensure either formGroupName or [formGroup] is specified on your component by changing its selector like so:
selector: '[formGroup] epimss-error-messages,[formGroupName] epimss-error-messages'
This answer should be sufficient for your needs, but if you want to know more I've written a blog entry here:
https://mrpmorris.blogspot.co.uk/2017/08/angular-composite-controls-formgroup-formgroupname-reactiveforms.html
For Angular 11 I tried all the above answers, and in different combinations, but nothing quite worked for me. So I ended up with the following solution which worked for me just as I wanted.
TypeScript
#Component({
selector: 'fancy-input',
templateUrl: './fancy-input.component.html',
styleUrls: ['./fancy-input.component.scss']
})
export class FancyInputComponent implements OnInit {
valueFormGroup?: FormGroup;
valueFormControl?: FormControl;
constructor(
private formGroupDirective: FormGroupDirective,
private formControlNameDirective: FormControlName
) {}
ngOnInit() {
this.valueFormGroup = this.formGroupDirective.form;
this.valueFormControl = this.formGroupDirective.getControl(this.formControlNameDirective);
}
get controlName() {
return this.formControlNameDirective.name;
}
get enabled() {
return this.valueFormControl?.enabled
}
}
HTML
<div *ngIf="valueFormGroup && valueFormControl">
<!-- Edit -->
<div *ngIf="enabled; else notEnabled" [formGroup]="valueFormGroup">
<input class="input" type="text" [formControlName]="controlName">
</div>
<!-- View only -->
<ng-template #notEnabled>
<div>
{{valueFormControl?.value}}
</div>
</ng-template>
</div>
Usage
Note that I had to add ngDefaultControl otherwise it would give no default value accessor error in console (if somebody knows how to get rid of it without error - will be much appreciated).
<form [formGroup]="yourFormGroup" (ngSubmit)="save()">
<fancy-input formControlName="yourFormControlName" ngDefaultControl></fancy-input>
</form>
this is an example of child component used inside parent formGroup :
child component ts:
import { Component, OnInit, Input } from '#angular/core';
import { FormGroup, ControlContainer, FormControl } from '#angular/forms';
#Component({
selector: 'app-date-picker',
template: `
<mat-form-field [formGroup]="form" style="width:100%;">
<input matInput [matDatepicker]="picker" [placeholder]="placeHolder" [formControl]="control" readonly>
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
<mat-datepicker #picker></mat-datepicker>
</mat-form-field>
<mat-icon (click)="clearDate()">replay</mat-icon>`,
styleUrls: ['./date-picker.component.scss']
})
export class DatePickerComponent implements OnInit {
public form: FormGroup;
public control : FormControl;
#Input() controlName : string;
#Input() placeHolder : string;
constructor(private controlContainer: ControlContainer) {
}
clearDate(){
this.control.reset();
}
ngOnInit() {
this.form = <FormGroup>this.controlContainer.control;
this.control = <FormControl>this.form.get(this.controlName);
}
}
css date picker :
mat-icon{
position: absolute;
left: 83%;
top: 31%;
transform: scale(0.9);
cursor: pointer;
}
and used like this :
<app-date-picker class="col-md-4" [formGroup]="feuilleForm" controlName="dateCreation" placeHolder="Date de création"></app-date-picker>
Parent Component :
#Component({
selector: 'app-arent',
templateUrl: `<form [formGroup]="parentFormGroup" #formDir="ngForm">
<app-child [formGroup]="parentFormGroup"></app-child>
</form> `
})
export class ParentComponent implements {
parentFormGroup :formGroup
ngOnChanges() {
console.log(this.parentFormGroup.value['name'])
}
}
Child Component :
#Component({
selector: 'app-Child',
templateUrl: `<form [formGroup]="childFormGroup" #formDir="ngForm">
<input id="nameTxt" formControlName="name">
</form> `
})
export class ChildComponent implements OnInit {
#Input() formGroup: FormGroup
childFormGroup :FormGroup
ngOnInit() {
// Build your child from
this.childFormGroup.addControl('name', new FormControl(''))
/* Bind your child form control to parent form group
changes in 'nameTxt' directly reflect to your parent
component formGroup
*/
this.formGroup.addControl("name", this.childFormGroup.controls.name);
}
}
The ngOnInit was important - this did not work in the constructor.
And I prefer looking for the FormControlDirective - its the first one found in the child component's ancestor hierarchy
constructor(private formGroupDirective: FormGroupDirective) {}
ngOnInit() {
this.formGroupDirective.control.addControl('password', this.newPasswordControl);
this.formGroupDirective.control.addControl('confirmPassword', this.confirmPasswordControl);
this.formGroup = this.formGroupDirective.control;
}
I would do this in this way, i have passed child form data as group to parent so you can have separated form data in submit call.
Parent:
<form [formGroup]="registerStudentForm" (ngSubmit)="onSubmit()">
<app-basic-info [breakpoint]="breakpoint" [formGroup]="registerStudentForm"></app-basic-info>
<button mat-button>Submit</button>
</form>
Child:
<mat-card [formGroup]="basicInfo">
<mat-card-title>Basic Information</mat-card-title>
<mat-card-content>
<mat-grid-list
[gutterSize]="'20px'"
[cols]="breakpoint"
rowHeight="60px"
>
<mat-grid-tile>
<mat-form-field appearance="legacy" class="full-width-field">
<mat-label>Full name</mat-label>
<input matInput formControlName="full_name" />
</mat-form-field>
</mat-grid-tile>
</mat-grid-list>
</mat-card-content>
</mat-card>
Parent.ts:
export class RegisterComponent implements OnInit {
constructor() { }
registerForm = new FormGroup({});
onSubmit() {
console.warn(this.registerForm.value);
}
}
Child.ts
export class BasicInfoComponent implements OnInit {
#Input() breakpoint;
#Input() formGroup: FormGroup;
basicInfo: FormGroup;
constructor() { }
ngOnInit(): void {
this.basicInfo = new FormGroup({
full_name: new FormControl('Riki maru'),
dob: new FormControl(''),
});
this.formGroup.addControl('basicInfo', this.basicInfo);
}
}
Here in your child form components #Input() formGroup: FormGroup; part would be reference of parent component
I would pass the form as an input to the child component;
#Component(
{
moduleId: module.id,
selector: 'epimss-error-messages',
template: `
<span class="error" *ngIf="errorMessage !== null">{{errorMessage}}</span>`,
styles: [],
directives: [REACTIVE_FORM_DIRECTIVES, NgIf]
})
export class ErrorMessagesComponent implements OnInit {
#Input()
ctrlName: string
#Input('form') _form;
ngOnInit() {
this.errorMessage();
}
errorMessage() {
// Find the control in the Host (Parent) form
let ctrl = this._form.find(this.ctrlName);
console.log('ctrl| ', ctrl)
// for (let propertyName of ctrl.errors) {
// // If control has a error
// if (ctrl.errors.hasOwnProperty(propertyName) && ctrl.touched) {
// // Return the appropriate error message from the Validation Service
// return CustomValidators.getValidatorErrorMessage(propertyName);
// }
// }
return null;
}
And of course you'll need o pass the form from the parent component to the child, which you can do it in different ways , but the simplest is :
Somewhere in your parent ;
<epimss-error-messages [form]='form'></epimss-error-messages>
If you want to access the parent from the child component, you can access parent property of the FormControl instance, https://angular.io/api/forms/AbstractControl#parent
To get the parent error:
const parent = control.parent;
const errors = parent.errors;