4

I have the following in Angular:

<div *ngIf="myarrayContainsEating('Chocolate')">Chocolate Is Good</div>

In my component, I have:

myarray = [{'name':'Charles', 'age':25, 'eating':'Vanilla'}, {'name':'Joseph', 'age':18, 'eating':'Banana'}]

myArrayContainsEating(food) {

  let chocolateExists = false;
  for(var i = 0; myarray.length; i++) {
    if(myarray[i].eating == 'Chocolate') {
       chocolateExists = true;
    }
  }
  return chocolateExists;
}

The problem is, when I have a button where I hit it and make it so that Charles is eating 'Chocolate', it does not show the 'Chocolate Is Good' text I expect. Is there a way to make this work?

8
  • What does myarrayContainsEating do? Commented Nov 24, 2019 at 17:46
  • @NicholasK Updated original post, just returns a boolean whether if found a value of 'Chocolate'. Commented Nov 24, 2019 at 17:49
  • Your if condition is incorrect. Change it to if (myarray[i].eating === "Chocolate") { ... }. Also, your current myarray doesn't have any object with the eating key whose value is chocolate. Commented Nov 24, 2019 at 18:00
  • @NicholasK I intentionally did not have any object with the eating key whose value is chocolate. I want to make it so that if somebody hit a button that later set say 'Charles' to be eating 'chocolate' or a new user is added that is eating 'chocolate', that the template would auto update to show the text. Commented Nov 24, 2019 at 18:10
  • 1
    It’s unfortunate that nobody actually mentions that calling a method from your template is quite bad practice... Commented Nov 24, 2019 at 18:30

3 Answers 3

5

Fixing your solution

Some problems with your code were already mentioned in other answers. Another thing to remind you of is the way you modify the eating property: You have to make sure to replace the array with a new array, each time an entry changes. Else the Change Detector will not pick up your changes.

You could do it like this:

Template

<div *ngIf="myarrayContainsEating('Chocolate')">Chocolate Is Good</div>

<button (click)="charlesEatChoc()">Make Charles eat Chocolate</button>

TS

myarray = [{'name':'Charles', 'age':25, 'eating':'Vanilla'}, {'name':'Joseph', 'age':18, 'eating':'Banana'}]

myarrayContainsEating(food) {
   return this.myarray.some(entry => entry.eating === food); // Check if any "eating" property is equal to food
}

charlesEatChoc() {
  this.myarray = this.myarray.map(entry => // This returns a new array (!!!IMPORTANT!!!)
     entry.name === 'Charles' // Map the entry if it matches your condition
        ? {...entry, eating: 'Chocolate'} // If it does, create a new one with the altered eating property
        : entry // If not, return the entry itself without modification
  );
}

Here is a working Stackblitz.

Cleaner solution

Calling a function inside an expression binding is not a good practice in Angular, since the Change Detector will have to rerun those methods each time there might have been a change. A cleaner way is to either hold a variable and change it accordingly (reactive apporach) or use a pipe.

Component template

<div *ngIf="myarray | containsFood:'Chocolate'">Chocolate Is Good</div>

<button (click)="charlesEatChoc()">Make Charles eat Chocolate</button>

Component TS

myarray = [{'name':'Charles', 'age':25, 'eating':'Vanilla'}, {'name':'Joseph', 'age':18, 'eating':'Banana'}]

charlesEatChoc() {
  this.myarray = this.myarray.map(entry => // This returns a new array (!!!IMPORTANT!!!)
     entry.name === 'Charles' // Map the entry if it matches your condition
        ? {...entry, eating: 'Chocolate'} // If it does, create a new one with the altered eating property
        : entry // If not, return the entry itself without modification
  );
}

Pipe TS

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'containsFood'
})
export class ContainsFoodPipe implements PipeTransform {

  transform(value: Array<any>, food:string): boolean {
     return value.some(entry => entry.eating === food); // Check if any "eating" property is equal to food
  }

}

Again, another working Stackblitz.

Sign up to request clarification or add additional context in comments.

1 Comment

Very nice solution with the Pipe, thank you! Note to future readers be sure to add the pipe to the declarations array in your module (e.g. app.module.ts)
2

Your final method should look like:

myArrayContainsEating(food) {
   let chocolateExists = false;
   for (let i = 0; i < this.myarray.length; i++) {
      if (this.myarray[i].eating == food) {
         chocolateExists = true;
      }
   }
   return chocolateExists;
}

The following are the issues with your code:

  1. Your if condition is incorrect. Change it to:

    if (myarray[i].eating === food) { 
       ... 
    }
    

    You need to access the eating property of each object. Nevertheless, the myArrayContainsEating(...) will still return false as there is no object in myArray array that has an object whose eating key contains the value chocolate. Thereby, the <div> will not be displayed.

  2. Also, you are missing a terminal condition in your loop:

    for(let i = 0; i < myarray.length; i++) {
       ...
    }
    
  3. Next, you need to use the parameter passed in the method i.e food to evaluate in the if expression.


Alternatively, you could easily just do:

myArrayContainsEating(food) {
   return this.myarray.find(e => e.eating === food);
}

Comments

0

Your for function is bad. You should traverse myarray[i].eating and not myarray.eating

myArrayContainsEating(food) {
  let chocolateExists = false;
  for(var i = 0; myarray.length; i++) {
    if(myarray[i].eating == 'Chocolate') {
       chocolateExists = true;
    }
  }
  return chocolateExists;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.