Lifecycle Hooks Examples

This page demonstrates AngularDart lifecycle hooks with practical examples.

Example 1: Data Loading with OnInit

Load data when the component initializes:

import 'package:angulardart/angular.dart';

@Component(
  selector: 'user-list',
  template: '<div *ngIf="isLoading">Loading users...</div>'
      '<ul *ngIf="!isLoading">'
      '<li *ngFor="let user of users">{{ user.name }}</li>'
      '</ul>',
)
class UserListComponent implements OnInit {
  List<User> users = [];
  bool isLoading = true;
  final UserService _userService;

  UserListComponent(this._userService);

  @override
  void ngOnInit() {
    _userService.getUsers().then((data) {
      users = data;
      isLoading = false;
    });
  }
}

Example 2: Cleanup with OnDestroy

Clean up resources when the component is destroyed:

@Component(
  selector: 'live-clock',
  template: '<p>Current time: {{ currentTime }}</p>',
)
class LiveClockComponent implements OnDestroy {
  String currentTime = '';
  late final Timer _timer;

  LiveClockComponent() {
    _updateTime();
    _timer = Timer.periodic(Duration(seconds: 1), (_) => _updateTime());
  }

  void _updateTime() {
    currentTime = DateTime.now().toIso8601String();
  }

  @override
  void ngOnDestroy() {
    _timer.cancel();
  }
}

Example 3: Reactive Changes with OnChanges

React to input changes:

@Component(
  selector: 'search-results',
  template: '<div *ngIf="hasSearchChanged">'
      '<p>Searching for: {{ query }}</p>'
      '<div *ngIf="isLoading">Searching...</div>'
      '<ul *ngIf="!isLoading">'
      '<li *ngFor="let result of results">{{ result.title }}</li>'
      '</ul>'
      '</div>',
)
class SearchResultsComponent implements OnChanges {
  @Input()
  String query = '';

  List<SearchResult> results = [];
  bool isLoading = false;
  bool hasSearchChanged = false;

  final SearchService _searchService;

  SearchResultsComponent(this._searchService);

  @override
  void ngOnChanges(SimpleChanges changes) {
    if (changes.containsKey('query') && !changes['query']!.isFirstChange()) {
      hasSearchChanged = true;
      isLoading = true;
      _searchService.search(query).then((data) {
        results = data;
        isLoading = false;
      });
    }
  }
}

Example 4: View Access with AfterViewInit

Access child elements after the view is initialized:

@Component(
  selector: 'chart-container',
  template: '<div #chartContainer class="chart"></div>'
      '<button (click)="resetChart()">Reset</button>',
)
class ChartContainerComponent implements AfterViewInit {
  @ViewChild('chartContainer')
  HtmlElement? chartContainer;

  Chart? _chart;

  @override
  void ngAfterViewInit() {
    _initializeChart();
  }

  void _initializeChart() {
    if (chartContainer != null) {
      _chart = Chart(chartContainer!, {
        'type': 'bar',
        'data': {/* ... */},
      });
    }
  }

  void resetChart() {
    _chart?.reset();
  }
}

Example 5: Content Projection with AfterContentInit

Work with projected content:

@Component(
  selector: 'tab-container',
  template: '<div class="tabs">'
      '<ng-content select="[tab-label]"></ng-content>'
      '</div>'
      '<div class="tab-content">'
      '<ng-content select="[tab-panel]"></ng-content>'
      '</div>',
)
class TabContainerComponent implements AfterContentInit {
  @ContentChildren(TabLabelDirective)
  List<TabLabelDirective>? labels;

  @override
  void ngAfterContentInit() {
    // Initialize tabs after content is projected
    labels?.first.isActive = true;
  }
}

Example 6: Custom Change Detection with DoCheck

Implement custom change detection:

@Component(
  selector: 'item-counter',
  template: '<p>Items: {{ items.length }}</p>'
      '<p *ngIf="hasChanged">List was modified!</p>',
)
class ItemCounterComponent implements DoCheck {
  @Input()
  List<String> items = [];

  int _previousLength = 0;
  bool hasChanged = false;

  @override
  void ngDoCheck() {
    if (items.length != _previousLength) {
      hasChanged = true;
      _previousLength = items.length;
      // Reset after a delay
      Future.delayed(Duration(seconds: 2), () => hasChanged = false);
    }
  }
}

Example 7: Complete Lifecycle Demo

@Component(
  selector: 'lifecycle-demo',
  template: '<p>Check console for lifecycle output</p>',
)
class LifecycleDemoComponent
    implements OnInit, OnChanges, DoCheck, OnDestroy,
               AfterContentInit, AfterContentChecked,
               AfterViewInit, AfterViewChecked {
  @Input()
  String? name;

  int _doCheckCount = 0;
  int _viewCheckCount = 0;
  int _contentCheckCount = 0;

  LifecycleDemoComponent() {
    print('Constructor called');
  }

  @override
  void ngOnChanges(SimpleChanges changes) {
    print('ngOnChanges: $changes');
  }

  @override
  void ngOnInit() {
    print('ngOnInit');
  }

  @override
  void ngDoCheck() {
    _doCheckCount++;
    if (_doCheckCount <= 3) {
      print('ngDoCheck (#$_doCheckCount)');
    }
  }

  @override
  void ngAfterContentInit() {
    print('ngAfterContentInit');
  }

  @override
  void ngAfterContentChecked() {
    _contentCheckCount++;
    if (_contentCheckCount <= 3) {
      print('ngAfterContentChecked (#$_contentCheckCount)');
    }
  }

  @override
  void ngAfterViewInit() {
    print('ngAfterViewInit');
  }

  @override
  void ngAfterViewChecked() {
    _viewCheckCount++;
    if (_viewCheckCount <= 3) {
      print('ngAfterViewChecked (#$_viewCheckCount)');
    }
  }

  @override
  void ngOnDestroy() {
    print('ngOnDestroy');
  }
}