angulartypescriptfirebaseangularfire2

Unable to retrieve object from firebase


I have integrated an edit button in my list of products, but clicking on edit button opens the form page that is unpopulated. When I console.log(p) in my product.service.ts file, it displayed null. And I think the main issue lies there.

[I am completely new in Angular, therefore any help will be appreciated].

Here is my product form template-

<div class ="row">
    <div class="col-md-6">
        <div class="card" style="width: 30rem;">
            <img [src]= "product.imageUrl" class="card-img-top" *ngIf= "product.imageUrl">
            <div class="card-body">
              <h5 class="card-title"> {{ product.title }}</h5>
              <p class="card-text"> {{ product.description }} </p>
            </div>
          </div>
    </div>

    <div class="col-md-6">
        <form #f="ngForm" (ngSubmit)="save(f.value)">
            <div class="form-group">
                <label for="title">Title</label>
                <input #title= "ngModel" [(ngModel)]= "product.title"  name = "title" id ="title" type="text" class="form-control" required>
                <div class="alert alert-danger" *ngIf="title.touched && title.invalid">
                    Title is required. 
                </div>
            </div>
            <div class="form-group">
                <label for="price">Price</label>
                <div class="input-group">
                    <span class="input-group-text"> ₹</span>
                    <input #price="ngModel" [(ngModel)] = "product.price" name ="price" id = "price" type="number" class="form-control " required min="0">
                </div>
                <div class="alert alert-danger" *ngIf = "price.touched && price.invalid"> 
                    <div *ngIf="price.errors.required">Price is required.</div> 
                    <div *ngIf="price.errors.min"> Minimum Prie should be zero. </div> 
                </div>
                <div class="alert alert-danger" *ngIf = ""> Price is required. </div>
            </div>
            <div class="form-group">
                <label for="category">Category</label>
                <select #category ="ngModel" [(ngModel)] ="product.category" name = "category" id="category"  class="form-control" required>
                    <option value = ""></option>
                    <option *ngFor = "let c of categories$ | async " [value]="c.name">
                        {{c.name}}
                    </option> 
                </select>
                <div class="alert alert-danger" *ngIf= "category.touched && category.invalid">
                    Category is required.
                </div>
            </div>
            <div class="form-group">
                <label for="imageUrl">Image Url</label>
                <input #imageUrl="ngModel" [(ngModel)]= "product.imageUrl" name ="imageUrl" id ="imageUrl" type="text" class="form-control" required pattern="https://.*" >
                <div class="alert alert-danger" *ngIf="imageUrl.touched && imageUrl.invalid">
                    <div *ngIf="imageUrl.errors.required">Image Url is required.</div>
                    <div *ngIf="imageUrl.errors.pattern">Enter a valid Url.</div>
                </div>
            </div>
            <div class="form-group">
                <label for="description">Description</label>
                <input #description= "ngModel" [(ngModel)] = "product.description" name = "description" id ="description" type="text" class="form-control" required>
                <div class="alert alert-danger" *ngIf="description.touched && description.invalid">
                    description is required. 
                </div>
            </div>
            <button class="btn btn-primary">Save</button>
        </form>
    </div>
</div>


Product-form.component.ts

import { Component } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { CategoryService } from 'src/app/category.service';
import { ProductService } from 'src/app/product.service';
import { take } from 'rxjs/operators'

@Component({
  selector: 'app-product-form',
  templateUrl: './product-form.component.html',
  styleUrls: ['./product-form.component.css']
})
export class ProductFormComponent {

  categories$;
  product:any = {};
  constructor(
    private categoryService: CategoryService, 
    private productService: ProductService,
    private router: Router,
    private route: ActivatedRoute) {
   
      this.categories$ = categoryService.getCategories();

      let id = this.route.snapshot.paramMap.get('id');
      if (id) this.productService.get(id).snapshotChanges().pipe(take(1)).subscribe(p => this.product = p);

  }

  save(product){
    this.productService.create(product);
    this.router.navigate(['/admin/products'])
  }

}

Lastly My product service is given as (product.service.ts) -

import { Injectable } from '@angular/core';
import { AngularFireDatabase } from '@angular/fire/compat/database';
import { map } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class ProductService {

  constructor(private db: AngularFireDatabase) { }

  create(product) {
    this.db.list('/products ').push(product); 
  }

  getAll() {
    return this.db.list('/products ').snapshotChanges()
    .pipe(map( action => {
      return action.map(product => {
        const key = product.payload.key;
        const data = product.payload.val();
        return  data;
      })
     } )); 
  }

  get(productId: string){
    return this.db.object('/products/' + productId);
  }
}

The exported firebase data.json file-

{
  "categories": {
    "action": {
      "name": "Action"
    },
    "adventure": {
      "name": "Adventure"
    },
    "fighting": {
      "name": "Fighting"
    },
    "puzzle": {
      "name": "Puzzle"
    },
    "strategy": {
      "name": "Strategy"
    }
  },
  "products ": {
    "-NdjCd4DNpztAOwiR1Hk": {
      "category": "Action",
      "imageUrl": "https://cdn.cloudflare.steamstatic.com/steam/apps/1888160/ss_549f55589c10866bc31243d277324e31ad155b29.600x338.jpg?t=1694077391",
      "price": 3599,
      "title": "ARMORED CORE™ VI FIRES OF RUBICON™"
    },
    "-Ndu1DegkAnM2FWqx1tu": {
      "category": "Action",
      "description": "In space, no one can hear you scream … except your friends! Rally your crew, man your spaceship, and prepare for an epic adventure. Battle fearsome foes, salvage valuable loot, die repeatedly - while reclaiming humanity's lost territory together.",
      "imageUrl": "https://cdn.cloudflare.steamstatic.com/steam/apps/1063420/header.jpg?t=1694177016",
      "price": 2999,
      "title": "Void Crew"
    }
  }

Page view before clicking edit (https://i.sstatic.net/hbdlG.png)

Page view after clicking edit (also do note the url) - (https://i.sstatic.net/ZCbKV.png)


Solution

  • I found out the issue, actually in my firebase products node, there is an extra space at the end which was causing the issue.

    Thus, correcting this in my product.service.ts file, corrected the issue.

    get(productId: string){
        return this.db.object('/products/' + productId);
    }