You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular5双向绑定报错:TypeError: Cannot read property 'title' of null

Fixing "Cannot read property 'title' of null" in Angular 5 + Firebase Form Binding

Hey there, let's walk through the issues causing your two-way binding to fail and that frustrating error message. I can see a couple of key problems in your code—let's tackle them one by one:

1. Broken Firebase Path in ProductService

First up, your get method in ProductService has a critical typo in the database path. Right now you're concatenating /products with the product ID directly, which creates an invalid path like /productsabc123 (missing the slash between the collection and document ID). Firebase can't find the product with this wrong path, so it returns null, which triggers the error.

Fix the ProductService get method:

import { Injectable } from '@angular/core';
import { AngularFireDatabase } from 'angularfire2/database';

@Injectable()
export class ProductService {
  constructor(private db: AngularFireDatabase) { }

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

  getAll() {
    return this.db.list('/products').snapshotChanges().map(categories => {
      return categories.map(c => ({ key: c.payload.key, ...c.payload.val() }));
    });
  }

  get(productId) {
    // Add the missing slash between /products and the product ID
    return this.db.object(`/products/${productId}`).valueChanges();
  }

  // Optional: Add an update method to handle editing existing products
  update(productId, product) {
    return this.db.object(`/products/${productId}`).update(product);
  }
}

2. Handling Asynchronous Data & Null Values

Even with the fixed path, Firebase returns data asynchronously. When your template first loads, product might still be null (if the subscription hasn't completed yet) or could be null if the product ID doesn't exist in the database. This causes the template to throw an error when it tries to access product.title before the data is ready.

Fix 1: Use the Safe Navigation Operator in the Template

Add ?. to your ngModel binding to safely access the property only when product exists:

<form #f="ngForm" (ngSubmit)="save(f.value)">
  <div class="form-group">
    <label for="title">Title</label>
    <!-- Add ?. to safely access product.title -->
    <input #title="ngModel" [(ngModel)]="product?.title" name="title" id="title" type="text" class="form-control" required>
    <div class="alert alert-danger mt-1" *ngIf="title.touched && title.invalid">
      Title is required.
    </div>
  </div>
  <!-- other input fields here -->
</form>

Fix 2: Default to an Empty Object When Product is Null

In your component's subscription, make sure you never set product to null—fall back to an empty object instead:

import { Product } from './../../models/product';
import { CategoryService } from './../../category.service';
import { Component, OnInit } from '@angular/core';
import { ProductService } from '../../product.service';
import { Router, ActivatedRoute } from '@angular/router';
import 'rxjs/add/operator/take';

@Component({
  selector: 'app-product-form',
  templateUrl: './product-form.component.html',
  styleUrls: ['./product-form.component.css']
})
export class ProductFormComponent implements OnInit {
  categories$;
  product: any = {}; // Initialize with empty object to avoid null state

  constructor(
    private router: Router,
    private route: ActivatedRoute,
    private categoryService: CategoryService,
    private productService: ProductService) {

    this.categories$ = categoryService.getCategories();
    const id = this.route.snapshot.paramMap.get('id');

    if (id) {
      this.productService.get(id)
        .take(1)
        .subscribe(p => {
          // If p is null (product not found), default to empty object
          this.product = p || {};
        });
    }
  }

  ngOnInit() { }

  // Update save method to handle both create and edit scenarios
  save(product) {
    const id = this.route.snapshot.paramMap.get('id');
    if (id) {
      this.productService.update(id, product);
    } else {
      this.productService.create(product);
    }
    this.router.navigate(['/admin/products']);
  }
}

Fix 3 (Optional): Hide Form Until Data Loads

If you prefer not to use the safe navigation operator, wrap your entire form in an *ngIf to only render it when product exists:

<div *ngIf="product">
  <form #f="ngForm" (ngSubmit)="save(f.value)">
    <!-- form content here -->
  </form>
</div>

3. Bonus: Fixing the Console Log Issue

You might have noticed your console.log(this.product) in the constructor prints an empty object—that's because the Firebase subscription is asynchronous. The log runs before the data is returned from Firebase. Move the log inside the subscription if you want to see the loaded product:

if (id) {
  this.productService.get(id)
    .take(1)
    .subscribe(p => {
      this.product = p || {};
      console.log(this.product); // Now this will log the actual product data
    });
}

These changes should resolve the Cannot read property 'title' of null error and get your two-way binding working correctly. Let me know if you run into any other snags!

内容的提问来源于stack exchange,提问作者Joginder Pawan Kumar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 09:24:32