Help !
I'm getting the following error from my Angular (material) application: TypeError: ctx.product is undefined
my service:product.service.ts
#Injectable({
providedIn: 'root'
})
export class ProductService {
baseUrl = 'http://localhost:3000/products';
constructor(
private http: HttpClient,
private router: Router,
private snackBar: MatSnackBar) { }
// Headers
private httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
};
showMessage(msg: string): void {
this.snackBar.open(msg, 'x', {
duration: 3000,
horizontalPosition: "right",
verticalPosition: "top",
})
}
create(product: Product): Observable<Product> {
return this.http.post<Product>(this.baseUrl, product);
}
read(): Observable<Product[]> {
return this.http.get<Product[]>(this.baseUrl);
}
readByID(id: string): Observable<Product> {
const url = `${this.baseUrl}/${id}`
return this.http.get<Product>(url);
};
update(product: Product): Observable<Product> {
const url = `${this.baseUrl}/${product.id}`
return this.http.put<Product>(url, product);
};
// Manipulação de erros
handleError(error: HttpErrorResponse) {
let errorMessage = '';
if (error.error instanceof ErrorEvent) {
// Erro ocorreu no lado do client
errorMessage = error.error.message;
} else {
// Erro ocorreu no lado do servidor
errorMessage = `Código do erro: ${error.status}, ` + `menssagem: ${error.message}`;
}
console.log(errorMessage);
return throwError(errorMessage);
};
}
my component: product-update.component.ts
#Component({
selector: 'app-product-update',
templateUrl: './product-update.component.html',
styleUrls: ['./product-update.component.css']
})
export class ProductUpdateComponent implements OnInit {
//product: Product = {} as Product;
product: Product;
constructor(
private productService: ProductService,
private router: Router,
private route: ActivatedRoute) { }
ngOnInit(): void {
const id = this.route.snapshot.paramMap.get('id');
console.log(id);
this.productService.readByID('id').subscribe(product => {
this.product = product;
});
};
updateProduct(): void {
this.productService.update(this.product).subscribe(() => {
this.productService.showMessage('Producto alterado com sucesso !');
this.router.navigate(['/products']);
})
}
cancel(): void {
this.router.navigate(['/products']);
}
}
my form HTML (Angular material):product-update.component.html
<mat-card>
<mat-card-title>Alterar Produto</mat-card-title>
<form>
<mat-form-field>
<input matInput placeholder="Descrição do produto" [(ngModel)]="product.descricao" name="descricao">
</mat-form-field>
<mat-form-field>
<input matInput placeholder="Grupo do produto" [(ngModel)]="product.id_grupo" name="id_grupo">
</mat-form-field>
<mat-form-field>
<input matInput placeholder="Marca do produto" [(ngModel)]="product.id_marca" name="id_marca">
</mat-form-field>
<mat-form-field>
<input matInput placeholder="Locação do produto" [(ngModel)]="product.id_locacao" name="id_locacao">
</mat-form-field>
<mat-form-field>
<input matInput placeholder="Status do produto A - Ativo ou I - Inativo" [(ngModel)]="product.status" name="status">
</mat-form-field>
<mat-form-field>
<input matInput placeholder="Estoque Mínimo" [(ngModel)]="product.estoque_min" name="estoque_min">
</mat-form-field>
<mat-form-field>
<input matInput placeholder="Estoque Máximo" [(ngModel)]="product.estoque_max" name="estoque_max">
</mat-form-field>
</form>
<button mat-raised-button (click)="updateProduct()" color="primary">
Salvar
</button>
<button mat-raised-button (click)="cancel()">
Cancelar
</button>
</mat-card>
When selecting the item in the list, the following edit form is empty, not bringing the item:
enter image description here
The edit form:
enter image description here
postpone form rendering untill you have a product like this
<form *ngIf="product">
...
Your product: Product in "product-update.component.ts" is not initialized, hence it's undefined. So, assuming your Product is an interface, initialize it using product: Product = new class implements Product { field1: data_type; field2: data_type; };
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.
I have a component in which [(ngModel)]=attrProps._value is getting set on user input and also getting value when already present by default.
Now there is a button which leads to a toggle button for value option when the user wants to have custom value.
But when the user clicks on the refresh button the value in the input field should get clear?
#Component({
selector: 'n-customValue',
template: `
<mat-form-field class="inputfield-attr-view">
<input matInput [(ngModel)]="attrProps._value" placeholder="{{bKeeperService.getAttributeType(attrProps).displayAs}}">
<mat-icon matSuffix (click)="revertIcon()" class="editButton">
<img class="attributeview-button" src="../../assets/icons/refresh.svg">
</mat-icon>
</mat-form-field>
`
})
export class customValueComponent implements OnInit {
#Output() clickEvent: EventEmitter<string> = new EventEmitter<string>();
constructor(public myservice: customService) { }
ngOnInit(): void { }
revertIcon() {
this.clickEvent.emit('REVERT_EVENT');
}
}
I tried using element ref but could not solve it.
#Component({
selector: 'n-customvalue',
template: `
<mat-form-field class="inputfield-attr-view">
<input matInput [(ngModel)]="attrProps._value" #searchInput placeholder="{{bKeeperService.getAttributeType(attrProps).displayAs}}">
<mat-icon matSuffix (click)="revertIcon()" class="editButton">
<img class="attributeview-button" src="../../assets/icons/refresh.svg">
</mat-icon>
</mat-form-field>
`
})
export class customvalueComponent implements OnInit {
#Output() clickEvent: EventEmitter<string> = new EventEmitter<string>();
#Input('nAttrProps') nAttrProps;
#ViewChild('searchInput') searchInput: ElementRef;
attrPropsValue;
constructor(public myservice: customService) { }
ngOnInit(): void { }
revertIcon() {
this.clickEvent.emit('REVERT_EVENT');
this.searchInput.nativeElement.value = '';
}
}
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
My task is to create an account information web page which consists of 4 pre-filled fields (given name, family name, username and email) and a common save button at the bottom. User can change any field by the respective field. I want save button to be enabled only if user changes any fields. Any method to track state changes? My code is as follows:
<mat-card-content>
<div class="form-group">
<mat-form-field class="simple-form-field-50">
<input matInput placeholder="Given name" name="givenName" formControlName="givenName">
</mat-form-field>
<mat-form-field class="simple-form-field-50">
<input matInput placeholder="Family name" name="familyName" formControlName="familyName">
</mat-form-field>
<br>
<mat-form-field>
<input matInput placeholder="Email" name="email" formControlName="email">
</mat-form-field>
<br>
<button
[disabled]="waiting"
class="simple-form-button"
color="primary"
mat-raised-button
type="submit"
value="submit">
Save
</button>
</div>
</mat-card-content>
My Code Output:
Since you're using a Reactive Form, you can use valueChanges on the FormGroup.
Since it is of type Observable, you can subscribe to it to set a variable of type boolean that will be used in the template to enable the button.
...
#Component({...})
export class AppComponent {
form: FormGroup;
disableButton = true;
ngOnInit() {
...
this.form.valueChanges.subscribe(changes => this.disableButton = false);
}
}
And in your template:
<form [formGroup]="form">
...
<button [disabled]="disableButton">Submit</button>
</form>
UPDATE:
If you want to disable it when values don't really change, check for the current value of the form with the previous value:
import { Component } from '#angular/core';
import { FormGroup, FormControl } from '#angular/forms';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
form: FormGroup;
disableButton = true;
userValue = {
firstName: 'John',
lastName: 'Doe',
email: 'john.doe#domain.com'
}
ngOnInit() {
this.form = new FormGroup({
firstName: new FormControl(),
lastName: new FormControl(),
email: new FormControl()
});
this.form.patchValue(this.userValue);
this.form.valueChanges.subscribe(changes => this.wasFormChanged(changes));
}
private wasFormChanged(currentValue) {
const fields = ['firstName', 'lastName', 'email'];
for(let i = 0; i < fields.length; i++) {
const fieldName = fields[i];
if(this.userValue[fieldName] !== currentValue[fieldName]) {
console.log('Came inside');
this.disableButton = false;
return;
}
}
this.disableButton = true;
}
}
NOTE: StackBlitz is updated accordingly.
Here's a Working Sample StackBlitz for your ref.
onChange(targetValue : string ){
console.log(targetValue );}
<input matInput placeholder="test" name="test" formControlName="testNM" (input)="onChange($event.target.value)">
This is called Dirty Check.
You may find this SO answer very useful:
https://stackoverflow.com/a/50387044/1331040
Here is the guide for Template-Driven Forms
https://angular.io/guide/forms
Here is the guide for Reactive Forms
https://angular.io/guide/reactive-forms
And here is the difference between two concepts
https://blog.angular-university.io/introduction-to-angular-2-forms-template-driven-vs-model-driven/
Hope these help.
I would do something like this:
form: FormGroup;
disableButton = true;
originalObj: any;
ngOnInit() {
this.form = new FormGroup({
control: new FormControl('Value')
});
this.originalObj = this.form.controls['control'].value; // store the original value in one variable
this.form.valueChanges.subscribe(changes => {
if (this.originalObj == changes.control) // this if added for check the same value
{
this.disableButton = true;
}
else {
this.disableButton = false;
}
}
);
}
WORKING EXAMPLE