Issue
I'm trying to filter through a list, but keep getting "TypeError: Cannot read property 'Name' of null". I can't seem to figure out why locations.Name would be null. I purposely use this.locations = initializeLocations() to prevent it. The data is there and my list gets generated well (with ngFor).
Typescript
//Imports
@Component({
selector: 'app-locaties',
templateUrl: './locaties.component.html',
styleUrls: ['./locaties.component.css'],
})
export class LocatiesComponent implements OnInit {
// the full list
masterLocations: any = [];
locations: any = [];
user;
constructor(
private toolbarTitle: ToolbarTitleService,
public popoverController: PopoverController,
private syncService: SyncServiceService,
private userService: UserService
) {}
async ngOnInit() {
this.toolbarTitle.setToolbarTitle('Locaties');
this.user = await this.userService.getUser();
// Haalt alle shops van de gebruiker op en zet ze in locations
await this.initializeLocations();
}
async initializeLocations() {
this.masterLocations = await this.syncService.getShops(this.user);
if (this.locations.length === 0) {
this.locations = JSON.parse(JSON.stringify(this.masterLocations));
}
}
// Popover
async presentPopover(ev: any, Contact: any) {
const popover = await this.popoverController.create({
component: PopoverComponent,
componentProps: {
phones: Contact.Phones[0].Number,
email: Contact.Email,
street: Contact.Addresses[0].Street1,
city: Contact.Addresses[0].City,
},
event: ev,
translucent: true,
});
return await popover.present();
}
filterList(ev: any) {
const val = ev.target.value;
if (val && val.trim() != '') {
const clone = JSON.parse(JSON.stringify(this.masterLocations));
this.locations = clone.filter((item) => {
return item.Name.toLowerCase().indexOf(val.toLowerCase()) > -1;
});
} else {
this.locations = this.masterLocations;
}
}
selectVal(val) {
alert('you have selected = ' + val);
}
}
HTML
<ion-content fullscreen>
<!-- Searchbar with a placeholder -->
<!-- (ionChange)="ionChange($event)" -->
<ion-searchbar
debounce="1000"
(ionInput)="filterList($event)"
placeholder="Zoek een locatie"
></ion-searchbar>
<ion-grid>
<ion-row>
<!-- locatie cards -->
<ion-col class="row1" size="11">
<ion-list lines="none">
<ion-item
(click)="selectVal(location.Name)"
*ngFor="let location of locations"
>
<ion-card class="locatieCard">
<ion-item>
<img
class="locatieImg"
src="assets/spar_img.jpg"
slot="start"
/>
<ion-grid>
<ion-row>
<ion-card-subtitle>{{ location.Name }}</ion-card-subtitle>
</ion-row>
<ion-row>
<ion-button
size="small"
fill="clear"
(click)="presentPopover($event, location.Contact)"
>
Meer info
</ion-button>
</ion-row>
</ion-grid>
</ion-item>
</ion-card>
</ion-item>
</ion-list>
</ion-col>
<ion-col class="row2" size="1"> ion col 2 </ion-col>
</ion-row>
</ion-grid>
</ion-content>
This is what getShops() in initializeLocations() returns:

this is the code:
getShops(user: any) {
const selector = {
_id: { $in: user.Shops },
};
return this.dbService.localDB
.find({
selector,
})
.then((result: any) => {
console.log('SHOPS: ', result.docs);
return result.docs;
});
}
Solution
Well, my assumption is, that you mix asynchronity with synchronity. The first thing you do in your filterList() method is to call this.initializeLocations();. Which triggers
this.locations = await this.syncService.getShops(this.user);
Although you work with await here, it is still an async operation from the view of filterList(). It won't wait for this operation to finish.
So while this call is being processed you work a second time with this.locations while you try to filter it. And I reckon that the async process from above cleared the array already and waits for the new value to put it in.
Remove
this.initializeLocations();
from your filter-method. This will do the trick.
Now you have to care for the list to be used as database and not be overwritten. So what we do now is, to bring in a list with masterLocations.
export class LocatiesComponent implements OnInit {
// the full list
masterLocations: any = [];
// the filtered list
locations: any = [];
// the user
user;
Get the master list here
async initializeLocations() {
this.masterLocations = await this.syncService.getShops(this.user);
}
And the filtered list here. With JSON.pare(JSON.stringify()) we generate a clone of the entire list.
filterList(ev: any) {
const val = ev.target.value;
if (val && val.trim() != '') {
const clone = JSON.parse(JSON.stringify(this.masterLocations));
this.locations = clone.filter((item) => {
return item.Name.toLowerCase().indexOf(val.toLowerCase()) > -1;
});
}
else {
this.locations = JSON.parse(JSON.stringify(this.masterLocations));
}
}
Answered By - Lynx 242

0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.