Angular Uncaught ReferenceError: categories is not defined

Viewed 60

I am new to angular so following Deborah Kurata's angular getting started and reactive forms courses. In the course code there is a product-list component which in the template has a table and in the model calls a products service to retrieve some data in the ngOnInit.

I am using this as an example for my own application where I fetch categories from the backend and display them in a table in virtually the same way.

The problem is that while the example code works and loads the data into the table, my code (done in exactly the same way!) is returning the 'Uncaught ReferenceError: categories is not defined' error, saying that the categories variable, used in the template *ngIf to conditionally display the table is not defined, when it clearly is defined in the corresponding model.

To clarify, this error is only visible on watching the template 'categories' from the ngIf while debugging. On running there are no errors in the console, the table simply doesn't show. Suggesting the ngIf is working as it should, but why is the data in model 'categories' not transferring over to the 'categories' in the template?

I've checked my getCategories method and it successfully gets 5 items from the back end. Why is this not visible to the template? Angular version used in the course code is 12.2.0 whereas my project uses 14.0.0. What am I missing here?

Course template code

    <div class="table-responsive">
      <table class="table mb-0"
             *ngIf="products && products.length">
        <thead>
          <tr>
            <th>
              <button class="btn btn-outline-primary btn-sm"
                      (click)="toggleImage()">
                {{showImage ? "Hide" : "Show"}} Image
              </button>
            </th>
            <th>Product</th>
            <th>Code</th>
            <th>Available</th>
            <th>Price</th>
            <th>5 Star Rating</th>
            <th></th>
          </tr>
        </thead>
        <tbody>
          <tr *ngFor="let product of filteredProducts">
            <td>
              <img *ngIf="showImage && product.imageUrl"
                   [src]="product.imageUrl"
                   [title]="product.productName"
                   [style.width.px]="imageWidth"
                   [style.margin.px]="imageMargin">
            </td>
            <td>
              <a [routerLink]="['/products', product.id]">
                {{ product.productName }}
              </a>
            </td>
            <td>{{ product.productCode }}</td>
            <td>{{ product.releaseDate }}</td>
            <td>{{ product.price | currency:"USD":"symbol":"1.2-2" }}</td>
            <td>
              <pm-star [rating]="product.starRating">
              </pm-star>
            </td>
            <td>
              <button class="btn btn-outline-primary btn-sm"
                      [routerLink]="['/products', product.id, 'edit']">
                Edit
              </button>
            </td>
          </tr>
        </tbody>
      </table>
    </div>

Course model code

    export class ProductListComponent implements OnInit {
          pageTitle = 'Product List';
          imageWidth = 50;
          imageMargin = 2;
          showImage = false;
          errorMessage = '';
        
          _listFilter = '';
          get listFilter(): string {
            return this._listFilter;
          }
          set listFilter(value: string) {
            this._listFilter = value;
            this.filteredProducts = this.listFilter ? this.performFilter(this.listFilter) : this.products;
          }
        
          filteredProducts: Product[] = [];
          products: Product[] = [];
        
          constructor(private productService: ProductService) { }
        
          performFilter(filterBy: string): Product[] {
            filterBy = filterBy.toLocaleLowerCase();
            return this.products.filter((product: Product) =>
              product.productName.toLocaleLowerCase().indexOf(filterBy) !== -1);
          }
        
          ngOnInit(): void {
            this.productService.getProducts().subscribe({
              next: products => {
                this.products = products;
                this.filteredProducts = this.products;
              },
              error: err => this.errorMessage = err
            });
          }
        }

My template code

    <div class='table-responsive'>
          <table class='table mb-0'
           *ngIf='categories && categories.length'>
            <thead>
              <tr>
                <th>ID</th>
                <th>Name</th>
                <th>Description</th>
                <th>Notes</th>
                <th>Actions</th>
              </tr>
            </thead>
            <tbody>
              <tr *ngFor="let category of filteredCategories">
                <td>
                  <a [routerLink]="['/category-list', category.id]">
                    {{ category.id }}
                  </a>
                </td>
                <td>{{ category.name }}</td>
                <td>{{ category.description }}</td>
                <td>{{ category.notes }}</td>
                <td>
                  <button class="btn btn-outline-primary btn-sm"
                    [routerLink]="['/categories', category.id, 'edit']">
                    Edit
                  </button>
                </td>
              </tr>
            </tbody>
          </table>
        </div>

My model code

    export class CategoryListComponent implements OnInit {
      pageTitle = 'Categories';
      errorMessage = '';
    
      sub!: Subscription;
    
      filteredCategories: Category[] = [];
      categories: Category[] = [];
    
      _listFilter = '';
      get listFilter(): string {
        return this._listFilter;
      }
      set listFilter(value: string) {
        this._listFilter = value;
        this.filteredCategories = this.listFilter ? this.performFilter(this.listFilter) : this.categories;
      }
    
      constructor(private categoryService: CategoryService) { }
    
      performFilter(filterBy: string): Category[] {
        filterBy = filterBy.toLocaleLowerCase();
        return this.categories.filter((category: Category) => 
        category.name.toLocaleLowerCase().indexOf(filterBy) !== -1);
      }
    
      ngOnInit(): void {
        this.sub = this.categoryService.getCategories().subscribe({
          next: c => {
            this.categories = c;
            this.filteredCategories = this.categories;
          },
          error: err => this.errorMessage = err
        });
      }
    }

Edit: another difference is that the course uses the 'angular-in-memory-web-api' while I'm using an actual api. When debugging with breakpoints in the model ngOnInit categories can be seen in watch list populated with data, but by the time it gets into the template breakpoint on ngIf 'categories' in watchlist is 'undefined'

0 Answers
Related