Why is ngClass calling function indefinitely?
Asked Answered
S

1

6

I am attempting to add a class based off of whether or not a value is present in storage, or set to a specific value. I am attempting to use [ngClass] for this. For some reason, my function to check the storage is getting called infinitely. What is causing this behavior and how can I stop it?

One example of the usage in my html file:

<div id="level2" class="cell middle" [ngClass]="{'locked': checkLevel(2)}">

checkLevel function:

checkLevel(level): any{
        console.log("checkLevel(" + level + ")");
        var result = this.storage.getLevel(level.toString());
        console.log(result);
        if (result == null || result['status'] == 'locked'){
            return true;
        } else {
            return false;
        }
    }

getLevel function:

getLevel(level:string):any{
        console.log("Inside getLevel.");
        this.storage.get('games' + level).then((val) => {
            console.log(val);
            return val;
        });
    }

Output to the console:

checkLevel(10)
storage.ts:20 Inside getLevel.
levels.ts:28 undefined
levels.ts:26 checkLevel(11)
storage.ts:20 Inside getLevel.
levels.ts:28 undefined
levels.ts:26 checkLevel(12)
storage.ts:20 Inside getLevel.
levels.ts:28 undefined
levels.ts:26 checkLevel(13)
storage.ts:20 Inside getLevel.
levels.ts:28 undefined
levels.ts:26 checkLevel(14)
storage.ts:20 Inside getLevel.
levels.ts:28 undefined

And it just keeps doing that forever. Cycling through all of the places that I use the [ngClass]. The actual levels page is never displayed.

--CONTEXT UPDATE--

I am using this on a page that contains a slider with 3 slides. Each slide contains 9 divs or 'cells' that represent a single level. There are 27 levels and that will never change, so the levels are static elements on the page (not loaded dynamically). Each level (except the first) is initially locked. Completing a level will unlock the next one. Or, levels can be unlocked by using the in game currency. So I need to check the local storage and set the locked class if there is no data for the level, or if the level has a status of locked. If it is unlocked, do not apply the locked class.

I just need some way to dynamically change classes based off of the level data that I am returned.

Sain answered 19/4, 2018 at 1:28 Comment(1)
looks like its angular change detectionUn
A
4

If you have a method in your binding, it will be called every time change detection runs.

If you need to update parent button by clicking on another parent button, try this:

Template:

<button (click)="updateLockClass()">Click to update</button>
<button type="button" [ngClass]="lockClass">Button with "lockClass</button>

App component:

export class AppComponent {
  lockClass = "";

  checkClass(num) {
    return num == 2;
  }

  updateLockClass() {
    this.lockClass = this.checkClass(2) ? "lockClass2" : "lockClass";
  }
}

If you need to update child button by updating parent value that gets parsed into child component, try this:

parent component:

<app-child [childMessage]="parentMessage"></app-child>
    <input [(ngModel)]="parentMessage">

child.component.html

<button type="button" [ngClass]="childClass">Child Button with "childClass"</button>

child.component.ts

import { Component, Input, OnChanges, SimpleChanges } from "@angular/core";

@Component({
  selector: "app-child",
  templateUrl: "./child.component.html",
  styleUrls: ["./app.component.css"]
})
export class ChildComponent implements OnChanges {
  childClass = "";
  @Input() childMessage: string;

  checkClass(num) {
    return num == 2;
  }

  updateChildClass() {
    this.childClass = this.checkClass(2) ? "lockClass3" : "lockClass";
  }

  constructor() {
  }

  ngOnChanges(changes: SimpleChanges) {
    console.log('Change detected:', changes.childMessage);
    if (changes.childMessage.currentValue != "Change parent value!") {
      this.updateChildClass();
    }
  }
}

Link to codesandbox: https://codesandbox.io/s/1y4wnz423

Abundant answered 19/4, 2018 at 7:4 Comment(7)
What do you mean by 'each time'? I want it to be called for each level. But it is currently calling it an infinite amount of times per level.Sain
Each time change detection runs. Sorry I've just updated my answer.Abundant
I tried adding <div id="level2" class="cell middle" [ngClass]="lockClass2"> and this.lockClass2 = this.checkLevel(2) ? "locked" : ""; but I get an error: Property 'lockClass2' does not exist on type 'LevelsPage'.Sain
You need to declare the prop lockClass2 in the component. My apologies. I've updated the answer to include codesandbox example.Abundant
I will accept this as the correct answer, but there has to be an easier way to conditionally add a class to an element. I need to do this on 27 elements in the page where each check is independent.Sain
Thanks for checking, you can also deploy/upload to codesandbox codesandbox.io/s so we can take a deeper look. We didn't know that you have to do that on 27 elements in the page.Abundant
I have updated my question with some context that may be able to help.Sain

© 2022 - 2024 — McMap. All rights reserved.