How to fix object key show undefined after Angular/Firebase library upgrade
by bernt & torsten
I’ve been working on an Angular – Ionic Cordova – Firebase mobile application, recently it was time to upgrade Angular and Firebase libraries to a newer version. I upgraded Angular from 4.x to 7.x and Angular Firebase from 3.9 to 5.x – while I was doing this, I run into a problem with the object key, I will explain what the issue was and how I fixed it.
To be clear, I’m not going to explain the steps on how to upgrade from Angular 4 to 7 – there is a guide already for that which you can find here – https://update.angular.io/
First the issue about the object key, I have a feature in my mobile application that allows users to check what pictures they submitted through the mobile application, the images stored in the Firebase Storage and the extra metadata and user information that is collected are stored in the Firebase Realtime Database.
Code before upgrade of Angular/Firebase libraries
In the display image feature in my mobile application, the images are displayed and are clickable. So I need the key value to be able to either click to see detail view for an image or to delete an image. To do this I create a code snipped that access Firebase Realtime Database with a query and get the key value for each image to display.
This is how the application display image feature looks like on a mobile.
Before I updated Angular and Firebase libraries, my code to query firebase and get the result back looked like this:
import {Component} from '@angular/core';
import {IonicPage, NavController, ToastController} from 'ionic-angular';
import {AngularFireDatabase} from 'angularfire2/database';
import {AngularFireAuth} from 'angularfire2/auth';
import { GoogleAnalytics } from '@ionic-native/google-analytics';
@IonicPage()
@Component({
selector: 'page-mypictures',
templateUrl: 'mypictures.html',
})
export class MypicturesPage {
mypictures: any[] = [];
constructor(public navCtrl: NavController,
public toastCtrl: ToastController,
public ga: GoogleAnalytics,
public af: AngularFireAuth,
public db: AngularFireDatabase) {
this.googleanalyticstrack();
if (this.af.auth.currentUser) {
// Find specific records of a user
this.db.list('rubbish', {
query: {
orderByChild: 'userId',
equalTo: this.af.auth.currentUser.uid
}
}).subscribe(
data => {
this.mypictures = data;
}
);
}
}
/**
* --------------------------------------------------------------
* Google Analytics
* --------------------------------------------------------------
*/
googleanalyticstrack() {
this.ga.startTrackerWithId('UA-xxxxxxxx-x')
.then(() => {
console.log('Google analytics is ready now');
this.ga.trackView('My picture');
this.ga.trackEvent('My picture', 'event', 'My Picture', 1);
})
.catch(e => console.log('Error starting GoogleAnalytics', e));
}
isMypictures(): boolean {
if (this.mypictures.length == 0) {
return false;
} else {
return true;
}
}
litterDetails(key) {
this.navCtrl.push("MypicturesDetailPage", {
key: key
})
}
removeMypicture(key) {
if (this.af.auth.currentUser) {
this.db.object('/rubbish/' + key).remove();
}
}
createToaster(message, duration) {
let toast = this.toastCtrl.create({
message: message,
duration: duration
});
toast.present();
}
}
The bold and inverted text shows the code to query the firebase and subscribe to the result. And here is how the .html file looks like:
<ion-header>
<ion-navbar hideBackButton="true">
<button ion-button menuToggle>
<ion-icon name="menu"></ion-icon>
</button>
<ion-title>Litter App</ion-title>
</ion-navbar>
</ion-header>
<ion-content [ngClass]="{bg:!isMypictures()}">
<div *ngIf="!isMypictures()">
<h4>Your Submissions!</h4>
</div>
<ion-card class="category" *ngIf="isMypictures()">
<div *ngFor="let mypicture of mypictures">
<div (click)="litterDetails(mypicture.$key)">
<div class="image">
<img src={{mypicture.imgUrl}}>
<div class="bg-overlay"></div>
</div>
</div>
<ion-card-content>
<ion-row>
<ion-col col-11>
<p>{{mypicture.brand}}</p>
</ion-col>
<ion-col col-1>
<ion-icon (click)="removeMypicture(mypicture.$key)" class="bookmark" name="trash"></ion-icon>
</ion-col>
</ion-row>
</ion-card-content>
</div>
</ion-card>
</ion-content>
The bold and inverted text above shows how the $key is used to make the image clickable with the right object key value.
After Angular/Firebase library update
After the upgrade, I made some changes to my code, as the newer library of Firebase has some major changes that made my original code to not work out after the upgrade.
I could not use .subscribe alone as a method to get the data, I needed to add either .valuChanges() or .snapshotChanges() method, you can read more about them here.
In the following code example, I used .snapshotChanges() as it would give me the data object and object key, using .valueChanges() just gives me the object data in .json format without the object key. I also had to change how the firebase query had worked, as you can see from the code example below:
import {Component} from '@angular/core';
import {IonicPage, NavController, ToastController} from 'ionic-angular';
import {AngularFireDatabase, AngularFireList} from '@angular/fire/database';
import {AngularFireAuth} from '@angular/fire/auth';
import {GoogleAnalytics} from '@ionic-native/google-analytics';
import { map } from 'rxjs/operators/map';
@IonicPage()
@Component({
selector: 'page-mypictures',
templateUrl: 'mypictures.html',
})
export class MypicturesPage {
mypictures: any[] = [];
myPicturesRef: AngularFireList<any>;
constructor(public navCtrl: NavController,
public toastCtrl: ToastController,
public ga: GoogleAnalytics,
public af: AngularFireAuth,
public db: AngularFireDatabase) {
this.googleanalyticstrack();
if (this.af.auth.currentUser) {
// Find specific records of a user
this.myPicturesRef = db.list('/rubbish', ref => ref
.orderByChild('userId')
.equalTo(this.af.auth.currentUser.uid));
this.myPicturesRef
.snapshotChanges().subscribe((res) => {
this.mypictures = res.map(change => ({key: change.payload.key, ...change.payload.val()}));
});
}
}
/**
* --------------------------------------------------------------
* Google Analytics
* --------------------------------------------------------------
*/
googleanalyticstrack() {
this.ga.startTrackerWithId('UA-xxxxxx-x')
.then(() => {
// console.log('Google analytics is ready now');
this.ga.trackView('My picture');
this.ga.trackEvent('My picture', 'event', 'My Picture', 1);
})
.catch(e => console.log('Error starting GoogleAnalytics', e));
}
isMypictures(): boolean {
if (this.mypictures.length == 0) {
return false;
}
else {
return true;
}
}
litterDetails(key) {
console.log("key for the image:" + key);
this.navCtrl.push("MypicturesDetailPage", {
key: key
})
}
removeMypicture(key) {
if (this.af.auth.currentUser) {
this.db.object('/rubbish/' + key).remove();
}
}
createToaster(message, duration) {
let toast = this.toastCtrl.create({
message: message,
duration: duration
});
toast.present();
}
}
The bold and inverted text is what changed. I also had to make a small change to my HTML, instead of using $key I just use key as shown in the code below in the bold and inverted text.
<ion-header>
<ion-navbar hideBackButton="true">
<button ion-button menuToggle>
<ion-icon name="menu"></ion-icon>
</button>
<ion-title>Litter App</ion-title>
</ion-navbar>
</ion-header>
<ion-content [ngClass]="{bg:!isMypictures()}">
<div *ngIf="!isMypictures()">
<h4>Your Submissions!</h4>
</div>
<ion-card class="mypicture" *ngIf="isMypictures()">
<div *ngFor="let mypicture of mypictures">
<div (click)="litterDetails(mypicture.key)">
<div class="image">
<img src={{mypicture.imgUrl}}>
<div class="bg-overlay"></div>
</div>
</div>
<ion-card-content>
<ion-row>
<ion-col col-11>
<p>{{mypicture.brand}}</p>
</ion-col>
<ion-col col-1>
<ion-icon (click)="removeMypicture(mypicture.key)" class="bookmark" name="trash"></ion-icon>
</ion-col>
</ion-row>
</ion-card-content>
</div>
</ion-card>
</ion-content>
Conclusion
Upgrades should be simple, and should not break the code. I’m not for large changes that make you debug your code all over to fix problems, an upgrade should just go smoothly. That said, my code is now working great and I hope that this may help someone with a similar problem.
Why It’s Important to Exercise When You’re Over 60
Many of us find ourselves in a pickle as the years pile up. Once reliable sidekicks, our...
A Poem: The Last Time
You never know when it will be,
The last time you ski down slopes of snow,
A Poem: Time Millionaire
When the morning wakes, still and clear,
No more alarms, no more rush....