1

I have a class UserList with a private internal variable that is designed to only be available to accessor functions. How can I access that internal list in a performant manner within an ngFor binding?

class UserList {
  private users:user[] = []
  public addUser():void{..} 
  public removeUser():void{..}
  public listUsers():user[]{
    return this.users; //this is the only way to access the private users list
  } 
}

and then in some component..

@component ({...})
class AppComponent {
  users:UserList = new UserList();
}

and in that component's template, the getter function is called..

<span *ngFor="let user in users.listUsers()">
  {{ user.username }}
</span>

So clearly this doesn't perform well, because angular can't possibly know when this list changes and will have to call users.listUsers() constantly to keep the view updated. Can I somehow limit this behavior and inform Angular that the list has changed with some event? I don't want to make the list public, and I don't want to access the private property directly in the template.

References: Angular2 - should private variables be accessible in the template?

Update with solution:

Amit's suggestion to use OnPush change detection worked out. I built a callback into the users service to inform any components using it of a change to the private user list. The app component could then call markForChange() to update the view. This let me refresh the view without breaking either the dependency injection pattern or the encapsulation I was attempting to enforce.

I still feel unsatisfied with this approach, because the change detection scheme affects the entire component and not the element. To me it seems strange that Angular wouldn't be able to efficiently bind the result of a function to an element without creating a performance sinkhole or breaking design patterns, or re-configuring the entire component to observe that behavior.

Austin
  • 117
  • 2
  • 12
  • Perhaps create getters/setters for the relevant variables? – P. Moloney Jan 08 '18 at 12:16
  • That's what I've done. I would prefer in the private list was only accessable by the getters. But that limits what I can bind in the template. I have to bind the getter function, which means angular has to constantly call that function to see whether data has changed. I'm now looking into OnPush change detection – Austin Jan 08 '18 at 17:30

2 Answers2

1

You could use OnPush change detection strategy, and mark the component to detect changes as soon as you know the list has changed.

Amit
  • 4,274
  • 21
  • 25
  • I need to do some research on how to use OnPush change detection, then I'll comment again... – Austin Jan 08 '18 at 12:16
  • 1
    You basically set your component to that change strategy, inject `ChanageDetectorRef`, and call `markForCheck()` on it whenever you think the view needs to be reevaluated. – Amit Jan 08 '18 at 12:40
  • This worked fairly well. I built a callback into usersService that would inform the app component of the change. The app component would then call markForCheck() and it drastically reduced the frequency of unnecessary polling to the getter function. I'm still not convinced that there can't be a better way to bind the return value of a function to the template - see my question update. – Austin Jan 08 '18 at 21:42
  • I feel you. I guess if some functions could be marked as pure, they could only call them once the input changes (which they can already know how to detect properly) – Amit Jan 09 '18 at 07:42
1

This should not. I am requesting you to don't do it. It will break DI.
Please call your private data from your service and make a public variable for template

Imran
  • 3,031
  • 4
  • 25
  • 41
  • I could treat userList as a service (and just call it usersService) and keep a recent output from usersService.listUsers() in the app component? That way the template could bind to that recent copy of the data. This is what I almost went with, but I would need some kind of onChange event that the app component controller could subscribe to in order to prevent the data going stale. I wasn't sure if this was the standard approach or if most people would somehow bind the accessor getList() within the template. – Austin Jan 08 '18 at 12:07
  • @AustinMcCool its possible to change your data after subscribe . Can you edit your question and show what you tried before – Imran Jan 08 '18 at 15:10