Confused about how to correctly utilize template variables within an *ngIf condition

My goal is to create a feature where users can adjust the number of rows in the catalog view on spiritwoodart.com. However, I encountered an issue at the initial stage of implementation and believe it stems from a basic misunderstanding. I am struggling to find assistance with my search query being unhelpful ("why can't I insert template variables in ngIf statement"). Please reach out if more information is required. Appreciate any insights, even constructive criticism for my novice skills.

When I make this adjustment:

            <div *ngIf="(i+1) % n === 0"
                class="w-100"></div>

And change it to:

            <div #n='3' *ngIf="(i+1) % n === 0"
                class="w-100"></div>

Or even try this:

            <div *ngIf="(i+1) % {{3}} === 0"
                class="w-100"></div>

I receive an error message like this:

compiler.js:485 Uncaught Error: Template parse errors:
There is no directive with "exportAs" set to "3" ("
                        [cart]="cart"></product-card>
                </div>
                <div [ERROR ->]#n='3' *ngIf="(i+1) % n === 0"
                    class="w-100"></div>
            </ng-container>
"): ng:///AppModule/ProductsComponent.html@12:21

Context-template:

<div class="row">
    <div class="col-3">
        <product-filter [category]="category"></product-filter>
    </div>
    <div class="col">
        <div class="row"
            *ngIf="cart$ | async as cart">
            <ng-container *ngFor="let p of filteredProducts; let i = index">
                <div class="col">
                    <product-card [product]="p"
                        [cart]="cart"></product-card>
                </div>
                <div *ngIf="(i+1) % 3 === 0"
                    class="w-100"></div>
            </ng-container>
        </div>
    </div>
</div>

Context-component:

import { Cart } from './../models/cart';
import { CartService } from './../cart.service';
import { Product } from './../models/product';
import { ProductService } from './../product.service';
import { Component, OnInit, OnDestroy } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import 'rxjs/add/operator/switchMap';
import { Subscription } from 'rxjs/Subscription';
import { Observable } from 'rxjs/Observable';
@Component({
  selector: 'app-products',
  templateUrl: './products.component.html',
  styleUrls: ['./products.component.css']
})
export class ProductsComponent implements OnInit {
  products: Product[] = [];
  filteredProducts: Product[] = [];
  category: string; // keep this field in this class in order to initialize it... then delegate it out
  cart$: Observable<Cart>;

  constructor(
    private route: ActivatedRoute,
    private productService: ProductService,
    private cartService: CartService
  ) {}

  async ngOnInit() {
    this.cart$ = await this.cartService.getCart();
    this.populateProducts();
  }

  private populateProducts() {
    this.productService
      .getAll()
      .switchMap(products => {
        this.products = products;
        return this.route.queryParamMap;
      })
      .subscribe(params => {
        this.category = params.get('category'); // initializing category field
        this.applyFilter();
      });
  }

  private applyFilter() {
    // setting the filtered products array
    this.filteredProducts = this.category
      ? this.products.filter(p => p.category === this.category)
      : this.products;
  }
}

Answer №1

# refers to a template reference variable that points to a DOM element, but it cannot be used directly in that way.

The ng-init directive was present in AngularJS for a similar purpose, but it is not included in Angular due to the potential for misuse. One can replicate its functionality, as demonstrated in this response.

An alternative solution involves using a structural directive. A common approach for handling truthy values is utilizing the ngIf structural directive like so:

<ng-container *ngIf="3; let n">
  <div *ngIf="(i+1) % n === 0">
    ...

This method may fail with falsy values because the nested component will not compile. For better support of falsy values, a custom ngVar structural directive can be implemented, further explained here.

Answer №2

Just wanted to share my experience as a newbie and how JB's advice helped me out. Thank you, JB! Hopefully, this will be beneficial to others, too.

Here are the minor tweaks I made:

template:

<div class="row">
        <input #myInput (change)="changeColumnNumber(myInput.value)" type="number">
</div>

component:

  n = 4;

Full context:

template:

    <div class="row">
        <input #myInput (change)="changeColumnNumber(myInput.value)" type="number">
</div>

<div class="row">
    <div class="col-3">
        <product-filter [category]="category"></product-filter>
    </div>

    <div class="col">
        <div class="row"
            *ngIf="cart$ | async as cart">
            <ng-container *ngFor="let p of filteredProducts; let i = index">
                <div class="col">
                    <product-card [product]="p"
                        [cart]="cart"></product-card>
                </div>
                <div *ngIf="(i+1) % n === 0"
                    class="w-100"></div>
            </ng-container>
        </div>
    </div>
</div>

component:

import { Cart } from './../models/cart';
import { CartService } from './../cart.service';
import { Product } from './../models/product';
import { ProductService } from './../product.service';
import { Component, OnInit, OnDestroy } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import 'rxjs/add/operator/switchMap';
import { Subscription } from 'rxjs/Subscription';
import { Observable } from 'rxjs/Observable';
@Component({
  selector: 'app-products',
  templateUrl: './products.component.html',
  styleUrls: ['./products.component.css']
})
export class ProductsComponent implements OnInit {
  products: Product[] = [];
  filteredProducts: Product[] = [];
  category: string; // keep this field in this class in order to initialize it... then delegate it out
  cart$: Observable<Cart>;
  n = 4;


  constructor(
    private route: ActivatedRoute,
    private productService: ProductService,
    private cartService: CartService,

  ) {}

  async ngOnInit() {
    this.cart$ = await this.cartService.getCart();
    this.populateProducts();
  }

  private populateProducts() {
    this.productService
      .getAll()
      .switchMap(products => {
        this.products = products;
        return this.route.queryParamMap;
      })
      .subscribe(params => {
        this.category = params.get('category'); // initializing category field
        this.applyFilter();
      });
  }

  private applyFilter() {
    // setting the filtered products array
    this.filteredProducts = this.category
      ? this.products.filter(p => p.category === this.category)
      : this.products;
  }

  changeColumnNumber(value) {
    console.log(value);
    this.n = value;
    console.log(this.n);
  }
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Can Angular Flex support multiple sticky columns at once?

I am trying to make the information columns in my angular material table stay sticky on the left side. I have attempted to use the "sticky" tag on each column, but it did not work as expected. <table mat-table [dataSource]="dataSource" matSort class= ...

Exploring the functionality of LazyLoading and configuring Angular's NgModule

I am currently working on a module called 'DevicePreview' that contains various routes and can be accessed both from the root and from another module called 'Content'. When accessed from the root, DevicePreview displays routes /a, /b, ...

Retrieve the :id parameter from the URL as a numerical value in Node.js using Typescript

Is there a way to directly get the :id param from the URL as a number instead of a string in order to easily pass it on to TypeORM for fetching data based on a specific ID? Currently, I am using the following approach where I have to create an additional ...

The function cb() was never invoked, resulting in an error during the npm install process

I'm having trouble installing node modules in my angular project. Running npm install gives me this error: npm ERR! cb() never called! npm ERR! This is an error with npm itself. Please report this error at: npm ERR! <https://npm.community> ...

Transitioning an AngularJS factory to TypeScript

I'm currently in the process of transitioning an AngularJS application to Angular and one of the challenges I've encountered is converting my JavaScript code to TypeScript. While I've been successful with components and services, factories h ...

Attempting to add a character sequence

I'm struggling with a seemingly basic issue of appending a char string (\r\n) to another. Despite trying methods like arrays (strcpy) and string objects, I haven't made any progress. To effectively send a string to a Java applet, I need ...

Issue with the Angular source maps are causing the sourceMappingUrl to break

After upgrading from Angular 7 to Angular 8, I've encountered an issue in Chrome and Firefox where there is an error in the dev console In Firefox: Error: JSON.parse: unexpected character at line 1 column 1 of the JSON data URL for Source Map: [url ...

How can RxJS be used to handle only the first value returned when calling multiple URLs?

I am faced with the challenge of having multiple URLs containing crucial information. My goal is to find a specific ID within these URLs, but I do not know which URL holds the necessary details. The approach I'm taking involves calling each URL and us ...

ngx-timeago encounters errors during deployment of Angular application

When I build my project locally, everything goes smoothly... However, when using aws code build with a docker image and running npx ng build, the following error occurs: #25 41.40 ./node_modules/ngx-timeago/__ivy_ngcc__/fesm2015/ngx-timeago.js:439:76-112 ...

What is the process for attaching an analytics tag to data messages using the Firebase Admin SDK with Javascript or TypeScript?

Adding a label to my message is something I'm trying to do. I checked out the official guidelines here and found a similar question answered on Stack Overflow here. I've been attempting to implement this in JavaScript, but I'm stuck. Here& ...

Setting a Fixed Default Value in an Angular Dropdown Menu

Within my code, there is a specific column where users have the ability to insert an element and choose its priority type while doing so. I am currently attempting to assign a default value to the dropdown selection (row.PriorityType.Id ==1). Although I at ...

Discover how to access all of the response headers from an HTTP request in Angular

Currently, I am utilizing HttpClient to make a request for a `json` file. My intention is to have the file cached using `ETag`, however, this feature does not seem to be functioning as expected. Upon investigation, it appears that the absence of sending of ...

Error in Angular Typescript: Utilize the return value of "filter" function to fix the issue

Encountering a sonar error: The return value of "filter" should be utilized Despite using the filter, the error persists. What might be the issue here? array.filter(item => { item.value.split(' ').forEach( i => { if ( doSomething(i) ...

Place a hook following the storage of a variable in the device's memory

Within a component, I am facing the following situation: const [home, setHome]=useState(false) if(home){ return(<Redirect push={true} to="/" />); } setItem("isRegistered", resquest[0].user) setHome(true) The issue here is that ...

How can I use JavaScript to update the content inside HTML tags with a specific string?

I have a situation where I need to replace a string in two different ways Input: Parameters-->string, AnnotationName, input Case 1: And I should input <i>Annotaion</i> as <b>input</b> Output : { displayData: `And I should inp ...

Exploring the functionality of window.matchmedia in React while incorporating Typescript

Recently, I have been working on implementing a dark mode toggle switch in React Typescript. In the past, I successfully built one using plain JavaScript along with useState and window.matchmedia('(prefers-color-scheme dark)').matches. However, w ...

Evaluating file selection and uploading functionality within Spectron

Currently, I am faced with the challenge of writing a test for an electron GUI that includes a choose file dialog. Unfortunately, I do not have access to the inner workings of the GUI. Here is the code snippet I have written: await app.client.chooseFile( ...

Error message thrown while using Angular2 Routing

I've been working on setting up a module to route the application, and although I believe I've set all the variables correctly, I seem to be missing something as I keep encountering this error: "Error: Uncaught (in promise): Error: Cannot find pr ...

Tips for updating a template dynamically within an Angular 2 and Ionic 2 component

I am working on a component that fetches its template from a remote URL. I am seeking a way to trigger a function upon an event, which will fetch the component's template again and update the already displayed template. @Component({ selector: &ap ...

Reducing the Angular version from 11 to 9

{ "name": "project-learn-web", "version": "1.0.0", "scripts": { "ng": "ng", "start": "ng serve", "build": "ng build", " ...