Topic: TypeError: Cannot read properties of undefined (reading 'input')
Expected behavior
Doesn't throw errors in console
Actual behavior
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 2 years 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
FREE CONSULTATION
Hire our experts to build a dedicated project. We'll analyze your business requirements, for free.
Answered
- User: Pro
- 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