Mohammad
Mohammad

Reputation: 105

Property 'title' does not exist on type '{}'

Angular

In the first few tags I have an input tag input #title = "ngModel" [(ngModel)]="product.title" this code where I am trying to use two way binding to be able to edit my form and values that I get from the firebase database. I created an empty product object in product-form.component.ts but I get the property type error. I am not sure why the empty product object causing error because the tutorial I'm watching has the same approach. Goal is to be able to use that empty product object or an alternate approach to be able to work with two way binding

product-form.component.ts
import { ProductService } from './../../product.service';
import { CategoryService } from './../../category.service';
import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { take } from 'rxjs/operators';

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


  constructor(
    private route: ActivatedRoute,
    private router: Router,
    private categoryService: CategoryService, 
    private productService: ProductService) 
    {
    this.categories$ = categoryService.getCategories();
    let id = this.route.snapshot.paramMap.get('id');
    if (id) this.productService.get(id).pipe(take(1)).subscribe(p => this.product = p);
    }

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

  ngOnInit(): void {
  }

}

product-form.html

<div class="row">
    <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 name ="price" id = "price" type="number" class="form-control" required>
                </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">Price should be 0.</div>
                </div>

            </div>

            <div class="form-group">
                <label for="category">Category</label>
                <select #category="ngModel" ngModel name="category" id = "category" type="text" 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 name= "imageUrl" id = "imageUrl" type="url" class="form-control" required>
                <div class="alert alert-danger" *ngIf = "imageUrl.touched && imageUrl.invalid">
                    <div *ngIf = "imageUrl.errors.required">Image URL is required.</div>
                    <div *ngIf = "imageUrl.errors.url">Invalid URL</div>
                </div>
            </div>

            <button class="btn btn-primary">Save</button>

        </form>
    </div>
    <div class="col-md-6">
        <div class="card" style="width: 18rem;">
            <img [src]="imageUrl.value" class="card-img-top">
            <div class="card-body">
              <h5 class="card-title">{{title.value}}</h5>
              <p class="card-text">{{price.value | currency:'USD':true}}</p>
            </div>
          </div>
    </div>

</div>


product.service.ts

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

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

  constructor(private db: AngularFireDatabase) { }

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

    // const itemsRef = this.db.list('products');
    // return itemsRef.push(product);
  }

  getAll() {
    return this.db.list('/products').valueChanges();

  }

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



Upvotes: 1

Views: 5892

Answers (2)

Haitham Nakib
Haitham Nakib

Reputation: 53

I fixed this error by changing a couple of things in 'tsconfig.json' file.

First remove "strict":true and add instead of it "noImplicitUseStrict":true (Don't forget adding the comma)

Upvotes: 0

Supun De Silva
Supun De Silva

Reputation: 1487

There are 2 ways to do this

Approach 1

Declare an interface

export interface Product {
  title: string
}

Change the Component code section as follows

from

product = {};

To

product:Product;

Approach 2 - This can have side effects

Change HTML

From

<input #title = "ngModel" [(ngModel)]="product.title" name = "title" id = "title" type="text" class="form-control" required>

To

<input #title = "ngModel" [(ngModel)]="product['title']" name = "title" id = "title" type="text" class="form-control" required>

Upvotes: 3

Related Questions