i want to disabled selected angular material between tow date . for example user can not selected any date between 2021-03-02 and 2011-03-02 .
i write this code :
<input matInput [max]="maxDate" [min]="minDate" [matDatepicker]="picker6"/>
maxDate = new Date();
minDate =new Date().setFullYear(this.maxDate.getFullYear() - 10)
but it not worked .
Demo
whats the problem ? how can i sovle this problem ???
Please replace your html file with below
your.component.html
<mat-form-field>
<mat-label>Choose Start date</mat-label>
<input matInput [max]="unavailabilityForm.controls.startDate.value" [matDatepicker]="picker1" />
<mat-datepicker-toggle matSuffix [for]="picker1"></mat-datepicker-toggle>
<mat-datepicker [dateClass]="dateClass" #picker1></mat-datepicker>
</mat-form-field>
<mat-form-field class="example-full-width" appearance="fill">
<mat-label>Choose End date</mat-label>
<input matInput [min]="unavailabilityForm.controls.endDate.value" [matDatepicker]="picker" />
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
<mat-datepicker [dateClass]="dateClass" #picker></mat-datepicker>
</mat-form-field>
your.component.ts
import { Component, OnInit, ViewEncapsulation } from "#angular/core";
import { FormBuilder } from "#angular/forms";
/** #title Datepicker with custom date classes */
#Component({
selector: "datepicker-date-class-example",
templateUrl: "datepicker-date-class-example.html",
styleUrls: ["datepicker-date-class-example.css"],
encapsulation: ViewEncapsulation.None
})
export class DatepickerDateClassExample implements OnInit {
unavailabilityForm: any;
maxDate = new Date();
constructor(private formBuilder: FormBuilder) {}
ngOnInit() {
let startDateTimeStamp = new Date().setFullYear(new Date().getFullYear() - 10);
this.unavailabilityForm = this.formBuilder.group({
startDate: [new Date(startDateTimeStamp)],
endDate: [new Date()]
});
}
}
It will resolve your concern.
Related
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 have created a custom directive for checking manual entries in angular material date picker (with help of here), and it is working.
Q: Is there any way to add that directive to all instances that are already present in the project without adding the directive one by one?
My directive:
import { Directive, ElementRef, HostBinding, OnDestroy, OnInit } from '#angular/core';
import * as textMask from '../../../util/vanillaTextMask.js';
#Directive({
selector: `fed-mask, [fed-mask], [fedMask]`
})
export class FedMaskDirective implements OnInit, OnDestroy {
#HostBinding('class.fed-mask') compClass = true;
fedMask = {
mask: [
new RegExp('\\d'),
new RegExp('\\d'),
'.',
new RegExp('\\d'),
new RegExp('\\d'),
'.',
new RegExp('\\d'),
new RegExp('\\d'),
new RegExp('\\d'),
new RegExp('\\d')
],
showMask: false,
guide: false,
placeholderChar: '_'
};
maskedInputController;
constructor(private element: ElementRef) { }
ngOnInit(): void {
this.maskedInputController = textMask.maskInput({
inputElement: this.element.nativeElement,
...this.fedMask
});
}
ngOnDestroy() {
this.maskedInputController.destroy();
}
}
how I'm currently using it:
<mat-form-field>
<input
(dateChange)="DateChange($event)"
formControlName="Date"
[matDatepicker]="Datepicker"
matInput
placeholder="Date"
(click)="Datepicker.open()"
fedMask
>
<mat-datepicker-toggle matSuffix [for]="Datepicker"></mat-datepicker-toggle>
<mat-datepicker #Datepicker></mat-datepicker>
<mat-error *ngIf="form.get('Date').hasError('required')">
Error
</mat-error>
</mat-form-field>
Expectation:
is there any way to add the fedMask directive to every instance of material date picker input without manually adding it for every time it has been used in the whole project?
you can use any existing attribute as a selector, for example
#Directive({
selector: "matDatepicker",
})
will target every element that has the attribute matDatepicker present on it.
Another example:
#Directive({selector: 'input:([matDatepicker])' })
will target every input with the matDatepicker attribute.
The same goes for tags:
#Directive({
selector: "mat-datepicker",
})
I'm trying to create a seat reservation application with datepicker. initially all the messages are seen on an alert, but I decided to move them to the dialogs.
as you know better than me to create the dialog you have to create a new component. the problem is that, I can't pass the date that is captured in the first component inside a method that formats the date.
how can I do?
I am attaching the relevant codes
appcomponent.html:
<mat-form-field class="example-full-width" appearance="fill">
<mat-label>Prenota</mat-label>
<input matInput [matDatepicker]="picker" [(ngModel)]="this.datari.myFormattedDates" (dateChange)="openDialog($event)">
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
<mat-datepicker [dateClass]="dateClass()" #picker> </mat-datepicker>
</mat-form-field>
appcomponent.ts:
export class AppComponent implements OnInit {
dateOfBirth = Date();
pipe = new DatePipe('it-IT');
currentDate = moment().format('DD/MM/YYYY');
datari: MyConst = new MyConst();
openDialog(event) {
//catch date
this.dateOfBirth = event.value;
//I format the date
this.datari.myFormattedDates = this.pipe.transform(this.dateOfBirth, 'dd/MM/yyyy');
// open dialog
this.dialog.open(DialogComponent );
}
dialogcomponent.ts:
export class DialogComponent implements OnInit {
dialogTitle = 'Parking';
dialogMessage = 'do you want to book for' + '?' ;
constructor(public dialog: MatDialog, public dialogRef: MatDialogRef<DialogComponent>,
#Inject(MAT_DIALOG_DATA) public data: any) { }
ngOnInit() {
}
}
dialogcomponent.html:
<h3 mat-dialog-title>{{dialogTitle}}</h3>
<div mat-dialog-content>
{{dialogMessage}}
</div>
<div style="float:right;margin:20px;">
<input style="margin:0px 10px;" type="button" value="confirm" [mat-dialog-close]="true">
<input type="button" value="cancel" [mat-dialog-close]="false">
</div>
thanks a lot to everyone :)
You will be able to pass data through the open method like this :
this.dialog.open(DialogComponent, {
data: { date: this.datari.myFormattedDates },
// Others options
// width: ...
// height: ...
// panelClass: ...
});
And in your dialog component :
constructor(public dialog: MatDialog, public dialogRef: MatDialogRef<DialogComponent>, #Inject(MAT_DIALOG_DATA) public data: { data: any }) { }
ngOnInit() {
console.log(this.data.date);
}
Take a look at https://material.angular.io/components/dialog/overview
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 have the form for date filter and I trying set the default value for the start and end date for date inputs.
<form [formGroup]="filter" (ngSubmit)="applyFilter()">
<mat-form-field>
<input matInput [matDatepicker]="start" formControlName="start" placeholder="Начальная дата">
<mat-datepicker-toggle matSuffix [for]="start"></mat-datepicker-toggle>
<mat-datepicker #start></mat-datepicker>
</mat-form-field>
<mat-form-field>
<input matInput [matDatepicker]="end" formControlName="end" placeholder="Конечная дата">
<mat-datepicker-toggle matSuffix [for]="end"></mat-datepicker-toggle>
<mat-datepicker #end></mat-datepicker>
</mat-form-field>
And TS part
refreshFilter() {
const now = new Date();
const monthAgo = new Date().setMonth(now.getMonth() - 1).toString();
console.log(monthAgo)
console.log(now)
this.filter = new FormGroup({
start: new FormControl(monthAgo, []),
end: new FormControl(now, [])
});
}
My console.log() for the month ago is 1533908066234 but for new Date is Mon Sep 10 2018 16:34:26 GMT+0300 and with timestamp form input doesn't work. How to get correct format date of month ago for success setting into FormControl?
If you Want to format a date in Angular you can use the DatePipe
Try to use the pipe to format.
This allows you to format a date value according to locale rules.
If you need more info about this you also can check here:
https://angular.io/api/common/DatePipe
Also do this monthAgo.toLocaleDateString()
I hope this helps!
I followed the following steps and it worked as expected:
created a new Angular 6 app using CLI and added Angular Materials to project
imported FormsModule and ReactiveFormsModule in app.module.ts
copied exactly the same html markup as you provided into app.component.html
added the below code in app.component.ts
import { Component, OnInit } from '#angular/core';
import {FormControl, FormGroup} from '#angular/forms';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
filter: FormGroup
ngOnInit() {
let now = new Date();
let monthAgo = new Date();
monthAgo.setMonth(now.getMonth() - 1);
this.filter = new FormGroup({
start: new FormControl(monthAgo, []),
end: new FormControl(now, [])
});
}
}
Hope this helps.
I found the solution:
const now = new Date();
const monthAgo = new Date(now.getFullYear(), now.getMonth() - 1, now.getDate(), now.getHours());
this.filter = new FormGroup({
start: new FormControl(monthAgo , []),
end: new FormControl(now, [])
});
I converting the date from timestamp to string format. That's all.
If somebody knows how to rewriting this more elegantly, I would be grateful.