Dear Mr. Jason & everyone
I am getting this error
the add new-product page is not working
I appreciate any help, I am stuck here for two weeks…
Dear Mr. Jason & everyone
I am getting this error
the add new-product page is not working
I appreciate any help, I am stuck here for two weeks…
As usual going to need to see the code for the relevant HTML and Typescript files to see what might be going wrong.
by all means
// product-form.component.ts
import { Component, OnDestroy, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { CategoryService } from 'src/app/category.service';
import { Product } from 'src/app/models/product';
import { ProductService } from 'src/app/product.service';
import { tap } from 'rxjs/operators';
import { Observable, Subscription } from 'rxjs';
import { AngularFireList } from 'angularfire2/database';
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, OnDestroy {
categories$!: { title: string; }[] | any;
categories!: { title: string; }[] | any;
subscription!: Subscription;
subscription$!: Subscription;
product: any;
id;
//product = {};
constructor(
private router: Router,
private route: ActivatedRoute,
private categoryService: CategoryService,
private productService: ProductService) {
/*
this.subscription = this.categoryService.getCategories().subscribe( categories => {
this.categories = categories;
}); */
this.categories$ = categoryService.getCategories();
//this.categories$ = this.categoryService.getCategoriesValueChanges();
/*this.subscription$ = this.categoryService.getCategoriesValueChanges().subscribe( categories => {
this.categories$ = categories;
}); */
//this.categories$ = this.categoryService.getCategoriesValueChanges();
this.id = this.route.snapshot.paramMap.get('id');
if(this.id) this.productService.get(this.id).pipe(take(1)).subscribe(p => this.product = p );
} // no need to add the private access modifier as we are not going to reference this anywhere outsde the constructor
save(product: Product){ // I added HTMLInputElement from my own
console.log(product);
if(this.id) this.productService.update(this.id, product);
else this.productService.create(product);
this.router.navigate(['/admin/products']);
}
ngOnInit(): void {
this.categories = this.categoryService.getCategories()
.pipe(tap(c => console.log("category =", c)));
}
ngOnDestroy() {
//this.subscription.unsubscribe();
//this.subscription$.unsubscribe();
}
}
// product-form.component.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-prepend">
<span class="input-group-text">$</span>
<input #price="ngModel" [(ngModel)]="product.price" name="price" id="price" type="number" class="form-control" [min]="0" 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">Minimun Price should be 0 or higher</div>
</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.key">{{c.data.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</label>
<input #imageUrl="ngModel" [(ngModel)]="product.imageUrl" name="imageUrl" id="imageUrl" type="text" class="form-control" url 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">Please enter a valid 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]="product.imageUrl" class="card-img-top">
<div class="card-body">
<h5 class="card-title">{{product.title}}</h5>
<p class="card-text">{{product.price | currency:'USD':true }}</p>
</div>
</div>
</div>
</div>
// product.service
import { Injectable } from '@angular/core';
import { AngularFireDatabase, AngularFireList } from 'angularfire2/database';
import { map } from 'rxjs/operators';
import { Product } from './models/product';
@Injectable({
providedIn: 'root'
})
export class ProductService {
product!: Product;
constructor(private db: AngularFireDatabase) { }
create(product: Product){
this.db.list('/products').push(product);
}
getAll() {
return this.db.list('/products').snapshotChanges().pipe(map(actions => {
return actions.map(a => {
const key = a.payload.key;
const data = a.payload.val();
return {data, key};
})
}));
}
get(productId: string) {
return this.db.object('/products/' + productId).valueChanges();
}
update(productId: string, product: Product){
return this.db.object('/products/' + productId).update(product);
}
}
So I think you are either seeing what happens when this if condition is false or a race condition
It could also be a simple race where product has not been assigned yet.
Either way, I think you need an ngIf to check if product is assigned a value before you try to use it in your template.
Hi,
i have the same issue. can you help me
hi,
I think this has to do with TS compiler. In “your product-form.component.ts” file the type of the property ‘product’ is any. You should try giving it the ’ Product’ type like this:
product: Product;
this way the compiler will know that title is a property of the product object.