Topic: TypeError: Cannot read properties of undefined (reading 'input')

pzimmer priority asked 1 year ago


Expected behavior

Doesn't throw errors in console

Actual behavior ERROR TypeError: Cannot read properties of undefined (reading 'input')

This may be the same issue discussed here. https://mdbootstrap.com/support/angular/cannot-read-properties-of-undefined-reading-input/

Resources (screenshots, code snippets etc.)

gamer.component.html

    <div class="d-flex align-items-center" *ngIf="isLoading">
  <strong>Loading...</strong>
  <div class="spinner-border text-success" role="status" aria-hidden="true" style="margin-left: 15px;"></div>
</div>
<div class="d-flex">
  <!-- Image -->
  <div class="flex-shrink-0">
    <app-image  [code]="model.gamerCode" 
                [isSmallCard]="false"
                [isClanMemberPhoto]="false"
                folder="gamerphotos"
                (save)="saveFilename($event)">
    </app-image>
  </div>
  <!-- Body -->
  <div class="flex-grow-1 ms-3" style="margin-left:20px;">
    <div class="row">
      <div class="col-md-4"><h4>{{model.name}}</h4></div>
      <div class="col-md-3 offset-md-5" style="text-align:right">
        <button type="button" class="btn btn-primary btn-sm" (click)="save()">
          <span class="spinner-border spinner-border-sm" role="status" *ngIf="isSaving" aria-hidden="true"></span>
          <i class="fas fa-save"  style="margin-left:5px;"></i> Save
        </button>
        <button type="button" 
                class="btn btn-primary btn-sm" 
                style="margin-left: 10px"
                (click)="!onCancelCheck() ? openCancelModal() : cancelUpdate()">
          <i class="fas fa-undo"></i> Cancel
        </button>
      </div>
    </div>
    <div class="clearfix"></div>
    <form>     
      <mdb-form-control class="col-8" style="margin-top: 10px; margin-bottom: 15px;">
        <input mdbInput type="text" id="name" name="name" class="form-control" [(ngModel)]="model.name" />
        <label mdbLabel class="form-label" for="name">Name</label>
      </mdb-form-control>
      <mdb-form-control class="col-8" style="margin-top: 10px; margin-bottom: 15px;">
        <input mdbCheckbox class="form-check-input" type="checkbox" value="" id="active" [(ngModel)]="model.isActive" [ngModelOptions]="{standalone: true}" />
        <label class="form-check-label" for="active"> Active? </label>
      </mdb-form-control>
      <div class="col-8">
        <label for="gamerClanLeaders">Clan Leaders</label><br/>
        <ng-select [items]="(gamerClanLeaders$ | async)!"
                   bindLabel="displayName"
                   [trackByFn]="trackByFn"
                   [minTermLength]="2"
                   [loading]="gamerClanLeadersLoading"
                   typeToSearchText="Please enter 2 or more characters"
                   [typeahead]="gamerClanLeadersInput$"
                   [(ngModel)]="gamerClanLeadersModel"
                   labelForId="gamerClanLeaders"
                   mdbInput
                   name="gamerClanLeaders"                   
                   [multiple]="true"
                   [maxSelectedItems]="2">
        </ng-select>
      </div>
      <div class="col-8">
        <label for="clanOfficers">Clan Officers</label><br/>
        <ng-select [items]="(clanOfficers$ | async)!"
                   bindLabel="displayName"
                   [trackByFn]="trackByFn"
                   [minTermLength]="2"
                   [loading]="clanOfficersLoading"
                   typeToSearchText="Please enter 2 or more characters"
                   [typeahead]="clanOfficersInput$"
                   [(ngModel)]="clanOfficersModel"
                   labelForId="clanOfficers"
                   mdbInput
                   name="clanOfficers"                   
                   [multiple]="true"
                   [maxSelectedItems]="2">
        </ng-select>
      </div>
      <div class="col-8">
        <label for="clanMembers">Clan Members</label><br/>
        <ng-select [items]="(clanMembers$ | async)!"
                   bindLabel="displayName"
                   [trackByFn]="trackByFn"
                   [minTermLength]="2"
                   [loading]="clanMembersLoading"
                   typeToSearchText="Please enter 2 or more characters"
                   [typeahead]="clanMembersInput$"
                   [(ngModel)]="clanMembersModel"
                   labelForId="clanMembers"
                   mdbInput
                   name="clanMembers"                   
                   [multiple]="true">
        </ng-select>
        <br />
        <br />        
      </div>   
    </form>
  </div>
</div>

gamer.component.ts

 import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { Title } from '@angular/platform-browser';
import { HttpClient } from '@angular/common/http';
import { GamerService } from '../../services/gamer.service';
import { IGamer } from '../../models/gamer.model';
import { PhotoService } from "../../services/photo.service";
import { IPhoto } from "../../models/photo.model";
import { ClanMemberService } from "../../services/clanMember.service";
import { IClanMember } from "../../models/clanMember.model";
import { IGamerClanLeader } from "../../models/gamer-clanLeader.model";
import { IClanOfficer } from "../../models/managing-director.model";
import { concat, Observable, of, Subject } from 'rxjs';
import { catchError, distinctUntilChanged, switchMap, tap } from 'rxjs/operators';
import { select, Store } from '@ngrx/store';
import { accessToken, AppState, azureStorageKey } from 'src/app/store/app.state';
import { azureStorageURL } from 'src/app/utils/app-config';
import { setImagefolder, setImageUniqueId, setPhotoName } from 'src/app/store/auth.actions';
import { ToastSuccessComponent } from '../helpers/toast-success/toast-success.component';
import { ToastErrorComponent } from '../helpers/toast-error/toast-error.component';
import { MdbNotificationRef, MdbNotificationService } from 'mdb-angular-ui-kit/notification';
import { ModalCancelComponent } from '../helpers/modal-cancel/modal-cancel.component';
import { MdbModalRef, MdbModalService } from 'mdb-angular-ui-kit/modal';
// import { ToastService } from 'mdb-angular-ui-kit';

@Component({
  selector: 'app-gamer',
  templateUrl: './gamer.component.html',
  styleUrls: ['./gamer.component.scss']
})
export class GamerComponent implements OnInit {

  notificationSuccessRef: MdbNotificationRef<ToastSuccessComponent> | null = null;
  notificationErrorRef: MdbNotificationRef<ToastErrorComponent> | null = null;
  modalRef: MdbModalRef<ModalCancelComponent> | null = null;

  id: string | null = null;
  gamer: IGamer = { isActive: true, gamerClanLeaders: [], clanOfficers: [], clanMembers: [] }
  model: IGamer = { isActive: true, gamerClanLeaders: [], clanOfficers: [], clanMembers: [] }
  gamerClanLeadersModel: IClanMember[] = [];
  clanOfficersModel: IClanMember[] = [];
  clanMembersModel: IClanMember[] = [];
  mode = 'read';  // CRUD
  defaultPhoto: string = '';
  searchText2?= '';
  data: IClanMember[] = [];
  isLoading: boolean = false;
  isLoaded:boolean = false;
  isSaving:boolean = false;

  constructor(private readonly gamerService: GamerService
              , private readonly  store: Store<{user: AppState}>
              , private notificationService: MdbNotificationService
              , private modalService: MdbModalService
              , private readonly clanMemberService: ClanMemberService
              , private readonly activatedRoute: ActivatedRoute
              , private readonly title: Title
              , private readonly router:Router) { }

  ngOnInit(): void {
    const id = this.activatedRoute.snapshot.paramMap.get('id');
    this.store.pipe(select(accessToken)).subscribe(async (accessToken: string | null) => {
      if(accessToken && id !== null)
        {
          this.isLoading = true;
          await this.getGamer(id);
          this.loadClanLeaders();
          this.loadClanOfficers();
          this.loadClanMembers();
        }
    })  
  }

  gamerClanLeaders$: Observable<IClanMember[]> = new Observable<IClanMember[]>();
  gamerClanLeadersLoading = false;
  gamerClanLeadersInput$ = new Subject<string>();

  private loadClanLeaders() {
    this.gamerClanLeaders$ = concat(
      of([]), // default items
      this.gamerClanLeadersInput$.pipe(
        distinctUntilChanged(),
        tap(() => this.gamerClanLeadersLoading = true),
        switchMap(term => this.clanMemberService.getAll({ isActive: true, name: term }).pipe(
          catchError(() => of([])), // empty list on error
          tap(() => this.gamerClanLeadersLoading = false)
        ))
      )
    );
  }

  clanOfficers$: Observable<IClanMember[]> = new Observable<IClanMember[]>();
  clanOfficersLoading = false;
  clanOfficersInput$ = new Subject<string>();

  private loadClanOfficers() {
    this.clanOfficers$ = concat(
      of([]), // default items
      this.clanOfficersInput$.pipe(
        distinctUntilChanged(),
        tap(() => this.clanOfficersLoading = true),
        switchMap(term => this.clanMemberService.getAll({ isActive: true, name: term }).pipe(
          catchError(() => of([])), // empty list on error
          tap(() => this.clanOfficersLoading = false)
        ))
      )
    );
  }

  clanMembers$: Observable<IClanMember[]> = new Observable<IClanMember[]>();
  clanMembersLoading = false;
  clanMembersInput$ = new Subject<string>();

  private loadClanMembers() {
    this.clanMembers$ = concat(
      of([]), // default items
      this.clanMembersInput$.pipe(
        distinctUntilChanged(),
        tap(() => this.clanMembersLoading = true),
        switchMap(term => this.clanMemberService.getAll({ isActive: true, name: term }).pipe(
          catchError(() => of([])), // empty list on error
          tap(() => this.clanMembersLoading = false)
        ))
      )
    );
  }

  trackByFn(item: IClanMember) {
    return item.clanMemberId;
  }

  getGamer(id: string) {
    //console.log('getGamer');
    this.gamerService.get(id, { includeClanMembers: true, includeClanLeaders: true, includeClanOfficers: true }).subscribe(gamer => {
      this.gamer = gamer;
      this.model = { ...gamer };
      this.setTitle();

      // Put the PSC ClanMembers into an array.
      this.clanOfficersModel = (gamer.clanOfficers) ? gamer.clanOfficers.filter(pe => pe.clanMember).map(pe => pe.clanMember!) : [];
      this.gamerClanLeadersModel = (gamer.gamerClanLeaders) ? gamer.gamerClanLeaders.filter(pl => pl.clanMember).map(pl => pl.clanMember!) : [];
      this.clanMembersModel = (gamer.clanMembers) ? [...gamer.clanMembers] : [];

      this.store.dispatch(setPhotoName({photoName: this.model.photoName ?  this.model.photoName : 'No_Image_Available.jpg'}));
      this.store.dispatch(setImagefolder({folderLocation: 'gamerphotos'}));
      this.store.dispatch(setImageUniqueId({imageUniqueId: new Date().getTime()}));

      this.isLoaded = true;
      this.isLoading = false;
    },
      err => {
        console.error(err);
      });
  }

  setTitle() {
    this.title.setTitle(`Gamer - ${this.gamer.name}`);
  }

  updateGamer() {
    this.isSaving = true;
    //console.log('updateGamer');
    //console.log(this.model);
    this.gamerService.update(this.model, this.model.gamerId, true, {includeClanLeaders: true, includeClanOfficers: true, includeClanMembers: true}).subscribe(() => {
      //console.log('Gamer updated.');
      this.gamer = { ...this.model };
      this.setTitle();
      this.mode = 'read';
      this.isSaving = false;
      this.showSuccess();
    },
      err => {
        this.isSaving = false;
        console.error(err);
        this.showError();
      });
  }

  edit() {
    this.mode = 'update';
  }

  save() {
    //console.log('save');

    if (this.model.clanMembers) {
      // Remove any that are no longer selected.
      const clanMembers = this.model.clanMembers.filter(pe => this.clanMembersModel.find(pem => pem.clanMemberId === pe.clanMemberId));

      // Add any new ones.
      const newClanMembers = this.clanMembersModel.filter(pe => !(this.model.clanMembers!.find(pem => pem.clanMemberId === pe.clanMemberId)));

      this.model.clanMembers = [...clanMembers, ...newClanMembers];
    }

    if (this.model.gamerClanLeaders) {
      // Remove any that are no longer selected.
      const gamerClanLeaders = this.model.gamerClanLeaders.filter(pe => this.gamerClanLeadersModel.find(pem => pem.clanMemberId === pe.clanMemberId));

      // Add any new ones.
      const newGamerClanLeaderModels = this.gamerClanLeadersModel.filter(pe => !(this.model.gamerClanLeaders!.find(pem => pem.clanMemberId === pe.clanMemberId)));
      const newGamerClanLeaders = newGamerClanLeaderModels.map<IGamerClanLeader>(pe => ({clanMemberId: pe.clanMemberId!, gamerId: this.model.gamerId!}));

      this.model.gamerClanLeaders = [...gamerClanLeaders, ...newGamerClanLeaders];
    }

    if (this.model.clanOfficers) {
      // Remove any that are no longer selected.
      const clanOfficers = this.model.clanOfficers.filter(pe => this.clanOfficersModel.find(pem => pem.clanMemberId === pe.clanMemberId));

      // Add any new ones.
      const newClanOfficerModels = this.clanOfficersModel.filter(pe => !(this.model.clanOfficers!.find(pem => pem.clanMemberId === pe.clanMemberId)));
      const newClanOfficers = newClanOfficerModels.map<IClanOfficer>(pe => ({clanMemberId: pe.clanMemberId!, gamerId: this.model.gamerId!}));

      this.model.clanOfficers = [...clanOfficers, ...newClanOfficers];
    }

    this.updateGamer();
  }

  onCancelCheck(){
    return JSON.stringify(this.gamer) === JSON.stringify(this.model);
  }

  cancelUpdate() {
    //console.log('cancelUpdate');

    // TODO: Prompt if there were changes before reverting.

    // Revert
    this.model = { ...this.gamer };
    this.clanOfficersModel = (this.gamer.clanOfficers) ? this.gamer.clanOfficers.filter(pe => pe.clanMember).map(pe => pe.clanMember!) : [];
    this.gamerClanLeadersModel = (this.gamer.gamerClanLeaders) ? this.gamer.gamerClanLeaders.filter(pl => pl.clanMember).map(pl => pl.clanMember!) : [];
    this.clanMembersModel = (this.gamer.clanMembers) ? [...this.gamer.clanMembers] : [];
    this.router.navigate(['/gamers']);
  }

  openCancelModal() {
    this.modalRef = this.modalService.open(ModalCancelComponent, {
      data: { title: 'Gamer'},
    });    
  }

  saveFilename(filename?: string) {
    this.gamer.photoName = filename;
    this.model.photoName = filename;
    this.gamerService.update(this.gamer, this.gamer.gamerId, false).subscribe(() => {
      //console.log("Updated photo filename.");
    },
    err => {
      console.error(err);
    });
  }

  showSuccess() {    
    this.notificationSuccessRef = this.notificationService.open(ToastSuccessComponent
                                                                , { data: { text: 'Gamer updated!'}
                                                                    , position: 'bottom-center'
                                                                    , autohide: true });
  }

  showError() {
    this.notificationSuccessRef = this.notificationService.open(ToastErrorComponent
                                                                  , { data: { text: 'Something went wrong. Gamer could not be updated!'}
                                                                      , position: 'bottom-center'
                                                                      , autohide: true });
  }

}

package.json

    {
  "name": "gamer",
  "version": "0.0.0",
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e"
  },
  "private": true,
  "dependencies": {
    "@angular/animations": "^14.2.8",
    "@angular/cdk": "^14.2.6",
    "@angular/common": "^14.2.8",
    "@angular/compiler": "^14.2.8",
    "@angular/core": "^14.2.8",
    "@angular/forms": "^14.2.8",
    "@angular/platform-browser": "^14.2.8",
    "@angular/platform-browser-dynamic": "^14.2.8",
    "@angular/router": "^14.2.8",
    "@azure/msal-angular": "^2.0.0-beta.1",
    "@azure/msal-browser": "^2.12.1",
    "@fortawesome/fontawesome-free": "^5.15.2",
    "@ng-select/ng-select": "^9.0.2",
    "@ngrx/data": "^14.3.2",
    "@ngrx/effects": "^14.3.2",
    "@ngrx/entity": "^14.3.2",
    "@ngrx/router-store": "^14.3.2",
    "@ngrx/store": "^14.3.2",
    "@ngrx/store-devtools": "^14.3.2",
    "@types/chart.js": "^2.9.30",
    "animate.css": "^4.1.1",
    "bn-ng-idle": "^1.0.1",
    "chart.js": "^2.5.0",
    "easy-pie-chart": "^2.1.7",
    "hammerjs": "^2.0.8",
    "mdb-angular-file-upload": "mdbootstrap.com/mdb/angular/mdb5/plugins/prd/file-upload",
    "mdb-angular-ui-kit": "mdbootstrap.com/mdb/angular/mdb5/prd/mdb5-angular-ui-kit-pro-essential",
    "mdb-angular-wysiwyg": "mdbootstrap.com/mdb/angular/mdb5/plugins/prd/wysiwyg",
    "rxjs": "~6.6.0",
    "screenfull": "^3.3.0",
    "tslib": "^2.0.0",
    "zone.js": "~0.11.4"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "^14.2.7",
    "@angular/cli": "^14.2.7",
    "@angular/compiler-cli": "^14.2.8",
    "@types/jasmine": "~3.6.0",
    "@types/jasminewd2": "~2.0.3",
    "@types/node": "^12.11.1",
    "codelyzer": "^6.0.0",
    "jasmine-core": "~3.6.0",
    "jasmine-spec-reporter": "~5.0.0",
    "karma": "~6.3.4",
    "karma-chrome-launcher": "~3.1.0",
    "karma-coverage-istanbul-reporter": "~3.0.2",
    "karma-jasmine": "~4.0.0",
    "karma-jasmine-html-reporter": "^1.5.0",
    "protractor": "~7.0.0",
    "ts-node": "~8.3.0",
    "tslint": "~6.1.0",
    "typescript": "^4.6.4"
  }
}

Grzegorz Bujański staff answered 1 year ago


The error showed up because you used a checkbox inside the mdb-form-control component. The form control component is for other components that have a floating label, eg input type="text" input type="number" and select



Please insert min. 20 characters.

FREE CONSULTATION

Hire our experts to build a dedicated project. We'll analyze your business requirements, for free.

Status

Answered

Specification of the issue

  • ForumUser: Priority
  • Premium support: Yes
  • Technology: MDB Angular
  • MDB Version: MDB5 3.0.0
  • Device: Desktop
  • Browser: Chrome
  • OS: Windows
  • Provided sample code: No
  • Provided link: Yes