0

I have a situation in my Angular 2 app where I am iterating over an array of objects, and if the "completed" property is set to false for any of those objects, I want to print to the view 'Yes', because there is (at least) one object with "completed" equal to false -- meaning it's still an active flag.

However, right now, the template code I have is printing 'Yes' to the view for EACH time one of these objects with the "completed" property set to false returns true. How can I adjust this code so that I'm only getting "Yes" printed once if ANY ONE (or more) of the objects in the array being iterated over has that "completed" property set to true?

Here's my code:

<td *ngFor="let flag of service.flags">
     <ng-template *ngIf="flag?.completed === false"> 
        <span class="standard-flag">Yes</span>
     </ng-template> 
</td> 

I also tried using the ternary operator, but it gives me the same result:

<td *ngFor="let flag of service.flags">
     <ng-template *ngIf="flag?.completed === false ? true : false""> 
        <span class="standard-flag">Yes</span>
     </ng-template> 
</td> 

While I would normally handle the logic in the component, I'm thinking there must be a way, perhaps with indexing, that I could this in the view on this occasion.

Rey
  • 1,393
  • 1
  • 15
  • 24
  • I would do the iteration within the component.ts file and then set a variable (`this.completed`) to true when any of them returns true. Then, within the template, you can assign an attribute to show "yes": `[hidden] = "completed"` on a DOM element: `

    Yes

    `.
    – Reticulated Spline Jul 27 '17 at 22:14
  • I agree, and normally I would do this in the component and print to the view the result. However in this particular case (for reasons I don't want to go into) I'd rather handle it in the view directly. I'm thinking there must be a way to do this with indexing of some sort. – Rey Jul 27 '17 at 22:15

1 Answers1

0

You don't need an ngFor to make this work. ngFor repeats for every element in your array, that's why you see a bunch Yes's

Use Array.some() which returns true immediately if at least one of the elements in the array returns true, so it is the right tool for the job here

<td>
    <span *ngIf="service.flags.some(flag => flag.completed === false) === true">
        Yes
    </span>
</td> 
Trash Can
  • 6,608
  • 5
  • 24
  • 38
  • Sounds promising. Will give it a try. I didn't realize I could essentially iterate over all of the objects in the array without using *ngFor. – Rey Jul 27 '17 at 22:23
  • Getting an error: zone.js:522 Unhandled Promise rejection: Template parse errors: Parser Error: Bindings cannot contain assignments at column 42 in [service.flags.some(flag => flag.completed === false) === true] – Rey Jul 27 '17 at 22:26
  • Sounds strange, try refactoring this `service.flags.some(flag => flag.completed === false) === true` into a function and call the function inside `ngIf` instead – Trash Can Jul 27 '17 at 22:32
  • Yup, that would probably work. But in this case I'm trying to handle this logic in the view specifically. – Rey Jul 27 '17 at 22:34
  • Then try removing `=== true` and see if it rids of the error – Trash Can Jul 27 '17 at 22:35
  • I had the same thought. Still produces the error. Seems like there should be a way to get this to work though. – Rey Jul 27 '17 at 22:36
  • I wouldn't want to insert long logic inside the view if I were you – Trash Can Jul 27 '17 at 22:36
  • We've written large Angular apps, and all of our bindings are really simple, the logic for the bindings such as `ngIf` is inside the controller class always – Trash Can Jul 27 '17 at 22:38
  • It occurs to me that what I could do here is handle this logic in a custom pipe, and use that in the template view. Will give that a try. Thanks for the Array.some() tip. Good to know. – Rey Jul 27 '17 at 22:43
  • Please no, pipes are used for formatting data for presentation(e.g capitalizing, formatting datetime, etc), doesn't make sense to use pipe here – Trash Can Jul 27 '17 at 22:45
  • Rather than getting into why it's not workable to handle the logic in the component for me in this particular instance (when normally I would), a pipe is the next best thing, considering the binding syntax in Angular 2 prevents me from doing this in the view itself. I found other examples of the same problem: https://stackoverflow.com/questions/35986017/can-you-use-array-filter-in-an-angular-binding – Rey Jul 27 '17 at 22:47
  • 1
    Yeah, just do as you please, it is your code, but I think adding a function to your component class is much easier and less an overkill for what you are trying to achieve. Just an opinion – Trash Can Jul 27 '17 at 22:55