Angular NgModel Binding cannot read property of undefined

1.8k views Asked by At

So I'm trying to edit an Item from array of items that I Have but I don't know how to bind NgModel Also It's being edited trough Material Dialog window. This is the TS file I'm working with:

import { Component, OnInit , Inject , Input } from '@angular/core';
import {MatDialog, MatDialogRef, MAT_DIALOG_DATA} from '@angular/material/dialog';
import { StorageItem } from '../../StorageItem';
import { ItemsService } from '../items.service';
import { Items } from '../../MockItems';
import { Observable, Subject } from 'rxjs';

export interface DialogData {
  Unit: string;
  Name: string;
  Amount: number;
}
@Component({
  selector: 'app-dialog-window',
  templateUrl: './dialog-window.component.html',
  styleUrls: ['./dialog-window.component.css']
})
export class DialogWindowComponent implements OnInit {
  Unit: string;
  Name: string;
  Amount: number;
  selected: StorageItem;
  constructor(public dialog: MatDialog , public itemsService: ItemsService) {}
  openDialog(hero: StorageItem): void {
    this.selected = hero;
    console.log(this.selected.Name);
    const dialogRef = this.dialog.open(DialogComponent, {
      width: '250px',
      data: {Unit: this.Unit, Amount: this.Amount, Name: this.Name}
    });
    dialogRef.afterClosed().subscribe(Unit => {
      console.log('The dialog was closed');
      this.Unit = Unit;
    });
  }
  ngOnInit(): void {}
}


@Component({
  selector: 'app-dialog',
  templateUrl: 'DialogEdit.html',
})
export class DialogComponent {
  $Items: StorageItem;
  constructor(
              public dialogRef: MatDialogRef<DialogComponent>,
              @Inject(MAT_DIALOG_DATA) public data: DialogData) {
  }
  onNoClick(): void {
    this.dialogRef.close();
  }


}

The Html With the button that opens the dialog:

   <li>
   <button mat-raised-button (click)="openDialog()">Edit Item</button>
  </li>

The Html for the inside of dialog:

    <div mat-dialog-content>
  <p>Name</p>
  <mat-form-field>
    <mat-label>Name</mat-label>
    <input matInput [(ngModel)]="Items.Name">
  </mat-form-field>
</div>

I don't know if It's supposed to be Items.Name I just put it as an example.

The Array looks something like this:

import { StorageItem } from './StorageItem';

export const Items: StorageItem[] = [
  { Quantity: 11, Name: 'Sand', Unit: 'kg'},
  { Quantity: 6, Name: 'Lamps', Unit:  'pcs'},
  { Quantity: 18, Name: 'Rocks', Unit: 'kg'},
  { Quantity: 2, Name: 'Tables', Unit:  'pcs'},
  { Quantity: 2, Name: 'Water', Unit:  'l'},
  { Quantity: 200, Name: 'Rope', Unit:  'm'},
];

And the StorageItem interface like this:

export interface StorageItem {
  Name: string;
  Quantity: number;
  Unit: string;
}

So what I want to achieve is to open the dialog window and have the name the input so that I can change it and close the dialog. Also it fine if it's only for the session I don't need it to change in the file. After I reload It can go back to default. If I put data.Unit it Will work but that is not the point I want to bind it to The Name of the Item and change that. There is a table genrated let StorageItem of Items plus the button so every object has the button next to it.

I've tried doing the same as in tour of heroes what they did with

selected: StorageItem
funcionOnclickToOpenDialog(storageItem: StorageItem){
this.selected = storageItem;
}

but that didn't work either.

If anything is missing or u want me to clarify I'll be happy to help.

3

There are 3 answers

0
C0mpl3x On BEST ANSWER

The problem was in my html, when using ng-container the forms started to get funky. Now when I generate the my html recursively this soltuion works:

<ng-template #formCategoryTemplate let-formCategory>
   <div [formGroup]="getFormGroup(formCategory)" class="request-form">
     </ng-container>
       <tr class="input" *ngIf="isInput(formEntry)">
         <td>
           <mat-form-field appearance="outline" class="full-width">
             <input [formControl]="getFormControl(toInput(formEntry))" matInput>
           </mat-form-field>
         </td>
       </tr>
     </ng-container>
   </div>
</ng-template>

The toIput will just take the entry and return entry as InputEntry

The getFormGroup will just return myFormGroup

1
Asaf On

You are editing the name and not the unit :)

dialogRef.afterClosed().subscribe(Unit => {
  console.log('The dialog was closed');
  this.Unit = Unit;
});

You should change the above to this:

dialogRef.afterClosed().subscribe(name => {
  console.log('The dialog was closed');
  this.Name = name;
});
2
adhs91 On

Items should be a class variable in order to be used in the template. I see here Items is declared with const. It should be like

Items: StorageItem[] = [
    { Quantity: 11, Name: 'Sand', Unit: 'kg'},
    { Quantity: 6, Name: 'Lamps', Unit:  'pcs'},
    { Quantity: 18, Name: 'Rocks', Unit: 'kg'},
    { Quantity: 2, Name: 'Tables', Unit:  'pcs'},
    { Quantity: 2, Name: 'Water', Unit:  'l'},
    { Quantity: 200, Name: 'Rope', Unit:  'm'},
 ];