9

In Angular 1 we avoided calling functions from within template expressions in excess, e.g. ng-repeat="item in vm.getFilteredItems()" because property changes unrelated to the result of getFilteredItems would cause the function to recompute repeatedly and unnecessarily on digests, which commonly causes performance problems at scale. Instead we bound to objects, and compute based on events (e.g. ng-repeat="item in vm.filteredItems).

In Angular 2, the dirty checking process has been optimized but functions called in component templates will still be called when any properties at the component level change, regardless of whether the function is dependent on those properties. I expect this could lead to the same performance issues if used improperly.

Here is a simplified example of the differing approaches in Angular 2:

// function binding in template
@Component({
  selector: 'func',
  template: `
    <input [(ngModel)]="searchTerm" placeholder="searchTerm" />
    <div *ngFor="let name of filteredNames(searchTerm)">{{name}}</div>
  `
})
export class FuncComponent {
  @Input() names:string[];

  filteredNames(searchTerm) {

    if (!searchTerm) return this.names;

    let filteredNames = [];

    return this.names.filter((name) => {
      return name.toLowerCase().indexOf(searchTerm.toLowerCase()) > -1;
    });
  }
}

-

// no function binding
@Component({
  selector: 'nofunc',
  template: `
    <input [(ngModel)]="searchTerm" (ngModelChange)="search($event)" placeholder="searchTerm" />
    <div *ngFor="let name of filteredNames">{{name}}</div>
  `
})
export class NoFuncComponent implements OnInit {
  @Input() names:string[];

  searchTerm: string;

  ngOnInit() {
    this.search(this.searchTerm);
  }

  search() {
    if (!this.searchTerm) {
      this.filteredNames = this.names;
      return;
    }

    this.filteredNames = this.names.filter((name) => {
      return name.toLowerCase().indexOf(this.searchTerm.toLowerCase()) > -1;
    });
  }
}

http://plnkr.co/edit/AAFknlJgso3D8F1w3QC1?p=preview

Is this still a concern in Angular 2? Which approach is preferred, and why? Thanks!

1 Answer 1

6

You could create a pipe. Pipes (if pure) are only called when depending values change.

@Pipe({
  name: 'search',
//  pure: 'false'
})
class SearchPipe {
  transform(names, searchTerm) {
   if (!this.searchTerm) {
      this.filteredNames = names;
      return;
    }

    return names.filter((name) => {
      return name.toLowerCase().indexOf(this.searchTerm.toLowerCase()) > -1;
    });
  }
}

@Component({
  selector: 'func',
  pipes: [SearchPipe],
  template: `
    <input [(ngModel)]="searchTerm" placeholder="searchTerm" />
    <div *ngFor="let name of names | search:searchTerm">{{name}}</div>
  `
})
export class FuncComponent {
  @Input() names:string[];
}

If the pipe needs to recognize changes in names then pure needs to be disabled.

If names is replaced by a different instance instead of just members being added or removed, then pure works fine and the pipe is only executed when names or searchTerm changes.

Hint

In devMode (default) change detection runs twice and the pipe will be called twice as much as expected.

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

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.