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.

Tristan
  • 1,608
  • 1
  • 20
  • 34

1 Answers1

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

Arielle Nguyen
  • 2,932
  • 1
  • 20
  • 15
  • 1
    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. – Tristan Apr 19 '18 at 19:34
  • Each time change detection runs. Sorry I've just updated my answer. – Arielle Nguyen Apr 19 '18 at 21:13
  • I tried adding `
    ` and `this.lockClass2 = this.checkLevel(2) ? "locked" : "";` but I get an error: Property 'lockClass2' does not exist on type 'LevelsPage'.
    – Tristan Apr 20 '18 at 03:27
  • You need to declare the prop lockClass2 in the component. My apologies. I've updated the answer to include codesandbox example. – Arielle Nguyen Apr 20 '18 at 04:49
  • 1
    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. – Tristan Apr 20 '18 at 15:49
  • Thanks for checking, you can also deploy/upload to codesandbox https://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. – Arielle Nguyen Apr 20 '18 at 17:57
  • I have updated my question with some context that may be able to help. – Tristan Apr 20 '18 at 18:11