Angular Native
DocsLearnExamplesGitHubSponsor
Build a habit tracker

3. A list of habits

One card is a design. The tracker needs one per habit, drawn from data. The data is a signal and the cards come from @for, as in any Angular app. The part worth knowing is what that turns into on a phone.

Step 1 of 3

Draw a card for every habit

Keep the habits in a signal, and draw the card for each:

import { Component, signal } from '@angular/core';

interface Habit {
  readonly id: string;
  readonly name: string;
  readonly done: boolean;
}

export class App {
  protected readonly habits = signal<readonly Habit[]>([
    { id: 'water', name: 'Drink water', done: false },
    { id: 'read', name: 'Read ten pages', done: false },
    { id: 'walk', name: 'Walk', done: true },
  ]);
}
@for (habit of habits(); track habit.id) {
  <view class="habit">
    <text>{{ habit.name }}</text>
    <text class="status">To do</text>
  </view>
}

Every card is a set of native views, created when its habit appears and destroyed when it goes. A stable, unique id lets Angular keep the views of a row it already has when the list changes, and move them when the list is reordered, rather than tear them down and build them again, which on a phone is the expensive part. Names make poor keys, because they can change and they can repeat: two tracked rows with the same key leave Angular unable to tell which is which.