// #docplaster
import {fakeAsync, ComponentFixture, TestBed, tick, waitForAsync} from '@angular/core/testing';

import {asyncData, asyncError} from '../../testing';

import {Subject, defer, of, throwError} from 'rxjs';
import {last} from 'rxjs/operators';

import {TwainComponent} from './twain.component';
import {TwainService} from './twain.service';

describe('TwainComponent', () => {
  let component: TwainComponent;
  let fixture: ComponentFixture<TwainComponent>;
  let getQuoteSpy: jasmine.Spy;
  let quoteEl: HTMLElement;
  let testQuote: string;

  // Helper function to get the error message element value
  // An *ngIf keeps it out of the DOM until there is an error
  const errorMessage = () => {
    const el = fixture.nativeElement.querySelector('.error');
    return el ? el.textContent : null;
  };

  // #docregion setup
  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [TwainComponent],
      providers: [TwainService],
    });
    testQuote = 'Test Quote';

    // #docregion spy
    // Create a fake TwainService object with a `getQuote()` spy
    const twainService = TestBed.inject(TwainService);
    // Make the spy return a synchronous Observable with the test data
    getQuoteSpy = spyOn(twainService, 'getQuote').and.returnValue(of(testQuote));
    // #enddocregion spy

    fixture = TestBed.createComponent(TwainComponent);
    fixture.autoDetectChanges();
    component = fixture.componentInstance;
    quoteEl = fixture.nativeElement.querySelector('.twain');
  });
  // #enddocregion setup

  describe('when test with synchronous observable', () => {
    it('should not show quote before OnInit', () => {
      expect(quoteEl.textContent).withContext('nothing displayed').toBe('');
      expect(errorMessage()).withContext('should not show error element').toBeNull();
      expect(getQuoteSpy.calls.any()).withContext('getQuote not yet called').toBe(false);
    });

    // The quote would not be immediately available if the service were truly async.
    // #docregion sync-test
    it('should show quote after component initialized', async () => {
      await fixture.whenStable(); // onInit()

      // sync spy result shows testQuote immediately after init
      expect(quoteEl.textContent).toBe(testQuote);
      expect(getQuoteSpy.calls.any()).withContext('getQuote called').toBe(true);
    });
    // #enddocregion sync-test

    // The error would not be immediately available if the service were truly async.
    // Use `fakeAsync` because the component error calls `setTimeout`
    // #docregion error-test
    it('should display error when TwainService fails', fakeAsync(() => {
      // tell spy to return an error observable after a timeout
      getQuoteSpy.and.returnValue(
        defer(() => {
          return new Promise((resolve, reject) => {
            setTimeout(() => {
              reject('TwainService test failure');
            });
          });
        }),
      );
      fixture.detectChanges(); // onInit()
      // sync spy errors immediately after init

      tick(); // flush the setTimeout()

      fixture.detectChanges(); // update errorMessage within setTimeout()

      expect(errorMessage())
        .withContext('should display error')
        .toMatch(/test failure/);
      expect(quoteEl.textContent).withContext('should show placeholder').toBe('...');
    }));
    // #enddocregion error-test
  });

  describe('when test with asynchronous observable', () => {
    beforeEach(() => {
      // #docregion async-setup
      // Simulate delayed observable values with the `asyncData()` helper
      getQuoteSpy.and.returnValue(asyncData(testQuote));
      // #enddocregion async-setup
    });

    it('should not show quote before OnInit', () => {
      expect(quoteEl.textContent).withContext('nothing displayed').toBe('');
      expect(errorMessage()).withContext('should not show error element').toBeNull();
      expect(getQuoteSpy.calls.any()).withContext('getQuote not yet called').toBe(false);
    });

    it('should still not show quote after component initialized', () => {
      fixture.detectChanges();
      // getQuote service is async => still has not returned with quote
      // so should show the start value, '...'
      expect(quoteEl.textContent).withContext('should show placeholder').toBe('...');
      expect(errorMessage()).withContext('should not show error').toBeNull();
      expect(getQuoteSpy.calls.any()).withContext('getQuote called').toBe(true);
    });

    // #docregion fake-async-test
    it('should show quote after getQuote (fakeAsync)', fakeAsync(() => {
      fixture.detectChanges(); // ngOnInit()
      expect(quoteEl.textContent).withContext('should show placeholder').toBe('...');

      tick(); // flush the observable to get the quote
      fixture.detectChanges(); // update view

      expect(quoteEl.textContent).withContext('should show quote').toBe(testQuote);
      expect(errorMessage()).withContext('should not show error').toBeNull();
    }));
    // #enddocregion fake-async-test

    // #docregion async-test
    it('should show quote after getQuote (async)', async () => {
      fixture.detectChanges(); // ngOnInit()
      expect(quoteEl.textContent).withContext('should show placeholder').toBe('...');

      await fixture.whenStable();
      // wait for async getQuote
      fixture.detectChanges(); // update view with quote
      expect(quoteEl.textContent).toBe(testQuote);
      expect(errorMessage()).withContext('should not show error').toBeNull();
    });
    // #enddocregion async-test

    it('should display error when TwainService fails', fakeAsync(() => {
      // tell spy to return an async error observable
      getQuoteSpy.and.returnValue(asyncError<string>('TwainService test failure'));

      fixture.detectChanges();
      tick(); // component shows error after a setTimeout()
      fixture.detectChanges(); // update error message

      expect(errorMessage())
        .withContext('should display error')
        .toMatch(/test failure/);
      expect(quoteEl.textContent).withContext('should show placeholder').toBe('...');
    }));
  });
});


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

@Injectable({providedIn: 'root'})
export class UserService {
  isLoggedIn = signal(true);
  user = signal({name: 'Sam Spade'});
}


// #docplaster
import {HttpClient, HttpHandler, provideHttpClient} from '@angular/common/http';
import {HttpTestingController, provideHttpClientTesting} from '@angular/common/http/testing';
import {fakeAsync, TestBed, tick} from '@angular/core/testing';
import {provideRouter, Router} from '@angular/router';
import {RouterTestingHarness} from '@angular/router/testing';

import {asyncData, click} from '../../testing';
import {Hero} from '../model/hero';
import {sharedImports} from '../shared/shared';

import {HeroDetailComponent} from './hero-detail.component';
import {HeroDetailService} from './hero-detail.service';
import {HeroListComponent} from './hero-list.component';

////// Testing Vars //////
let component: HeroDetailComponent;
let harness: RouterTestingHarness;
let page: Page;

////// Tests //////
describe('HeroDetailComponent', () => {
  describe('with HeroModule setup', heroModuleSetup);
  describe('when override its provided HeroDetailService', overrideSetup);
  describe('with FormsModule setup', formsModuleSetup);
  describe('with SharedModule setup', sharedModuleSetup);
});

///////////////////

const testHero = getTestHeroes()[0];
function overrideSetup() {
  // #docregion hds-spy
  class HeroDetailServiceSpy {
    testHero: Hero = {...testHero};

    /* emit cloned test hero */
    getHero = jasmine
      .createSpy('getHero')
      .and.callFake(() => asyncData(Object.assign({}, this.testHero)));

    /* emit clone of test hero, with changes merged in */
    saveHero = jasmine
      .createSpy('saveHero')
      .and.callFake((hero: Hero) => asyncData(Object.assign(this.testHero, hero)));
  }

  // #enddocregion hds-spy

  // #docregion setup-override
  beforeEach(async () => {
    await TestBed.configureTestingModule(
      Object.assign({}, appConfig, {
        imports: [HeroDetailComponent, HeroListComponent],
        providers: [
          provideRouter([
            {path: 'heroes', component: HeroListComponent},
            {path: 'heroes/:id', component: HeroDetailComponent},
          ]),
          HttpClient,
          HttpHandler,
          // HeroDetailService at this level is IRRELEVANT!
          {provide: HeroDetailService, useValue: {}},
        ],
      }),
    )
      // #docregion override-component-method
      .overrideComponent(HeroDetailComponent, {
        set: {providers: [{provide: HeroDetailService, useClass: HeroDetailServiceSpy}]},
      })
      // #enddocregion override-component-method
      .compileComponents();
  });
  // #enddocregion setup-override

  // #docregion override-tests
  let hdsSpy: HeroDetailServiceSpy;

  beforeEach(async () => {
    harness = await RouterTestingHarness.create();
    component = await harness.navigateByUrl(`/heroes/${testHero.id}`, HeroDetailComponent);
    page = new Page();
    // get the component's injected HeroDetailServiceSpy
    hdsSpy = harness.routeDebugElement!.injector.get(HeroDetailService) as any;

    harness.detectChanges();
  });

  it('should have called `getHero`', () => {
    expect(hdsSpy.getHero.calls.count())
      .withContext('getHero called once')
      .toBe(1, 'getHero called once');
  });

  it("should display stub hero's name", () => {
    expect(page.nameDisplay.textContent).toBe(hdsSpy.testHero.name);
  });

  it('should save stub hero change', fakeAsync(() => {
    const origName = hdsSpy.testHero.name;
    const newName = 'New Name';

    page.nameInput.value = newName;

    page.nameInput.dispatchEvent(new Event('input')); // tell Angular

    expect(component.hero.name).withContext('component hero has new name').toBe(newName);
    expect(hdsSpy.testHero.name).withContext('service hero unchanged before save').toBe(origName);

    click(page.saveBtn);
    expect(hdsSpy.saveHero.calls.count()).withContext('saveHero called once').toBe(1);

    tick(); // wait for async save to complete
    expect(hdsSpy.testHero.name).withContext('service hero has new name after save').toBe(newName);
    expect(TestBed.inject(Router).url).toEqual('/heroes');
  }));
}
// #enddocregion override-tests

////////////////////
import {getTestHeroes} from '../model/testing/test-hero.service';

const firstHero = getTestHeroes()[0];

function heroModuleSetup() {
  // #docregion setup-hero-module
  beforeEach(async () => {
    await TestBed.configureTestingModule(
      Object.assign({}, appConfig, {
        imports: [HeroDetailComponent, HeroListComponent],
        providers: [
          provideRouter([
            {path: 'heroes/:id', component: HeroDetailComponent},
            {path: 'heroes', component: HeroListComponent},
          ]),
          provideHttpClient(),
          provideHttpClientTesting(),
        ],
      }),
    ).compileComponents();
  });
  // #enddocregion setup-hero-module

  // #docregion route-good-id
  describe('when navigate to existing hero', () => {
    let expectedHero: Hero;

    beforeEach(async () => {
      expectedHero = firstHero;
      await createComponent(expectedHero.id);
    });
    // #docregion selected-tests
    it("should display that hero's name", () => {
      expect(page.nameDisplay.textContent).toBe(expectedHero.name);
    });
    // #enddocregion route-good-id

    it('should navigate when click cancel', () => {
      click(page.cancelBtn);
      expect(TestBed.inject(Router).url).toEqual(`/heroes/${expectedHero.id}`);
    });

    it('should save when click save but not navigate immediately', () => {
      click(page.saveBtn);
      expect(TestBed.inject(HttpTestingController).expectOne({method: 'PUT', url: 'api/heroes'}));
      expect(TestBed.inject(Router).url).toEqual('/heroes/41');
    });

    it('should navigate when click save and save resolves', fakeAsync(() => {
      click(page.saveBtn);
      tick(); // wait for async save to complete
      expect(TestBed.inject(Router).url).toEqual('/heroes/41');
    }));

    // #docregion title-case-pipe
    it('should convert hero name to Title Case', async () => {
      harness.fixture.autoDetectChanges();
      // get the name's input and display elements from the DOM
      const hostElement: HTMLElement = harness.routeNativeElement!;
      const nameInput: HTMLInputElement = hostElement.querySelector('input')!;
      const nameDisplay: HTMLElement = hostElement.querySelector('span')!;

      // simulate user entering a new name into the input box
      nameInput.value = 'quick BROWN  fOx';

      // Dispatch a DOM event so that Angular learns of input value change.
      nameInput.dispatchEvent(new Event('input'));

      // Wait for Angular to update the display binding through the title pipe
      await harness.fixture.whenStable();

      expect(nameDisplay.textContent).toBe('Quick Brown  Fox');
    });
    // #enddocregion selected-tests

    // #enddocregion title-case-pipe
  });

  // #docregion route-bad-id
  describe('when navigate to non-existent hero id', () => {
    beforeEach(async () => {
      await createComponent(999);
    });

    it('should try to navigate back to hero list', () => {
      expect(TestBed.inject(Router).url).toEqual('/heroes');
    });
  });
  // #enddocregion route-bad-id
}

/////////////////////
import {FormsModule} from '@angular/forms';
import {TitleCasePipe} from '../shared/title-case.pipe';
import {appConfig} from '../app.config';

function formsModuleSetup() {
  // #docregion setup-forms-module
  beforeEach(async () => {
    await TestBed.configureTestingModule(
      Object.assign({}, appConfig, {
        imports: [FormsModule, HeroDetailComponent, TitleCasePipe],
        providers: [
          provideHttpClient(),
          provideHttpClientTesting(),
          provideRouter([{path: 'heroes/:id', component: HeroDetailComponent}]),
        ],
      }),
    ).compileComponents();
  });
  // #enddocregion setup-forms-module

  it("should display 1st hero's name", async () => {
    const expectedHero = firstHero;
    await createComponent(expectedHero.id).then(() => {
      expect(page.nameDisplay.textContent).toBe(expectedHero.name);
    });
  });
}

///////////////////////

function sharedModuleSetup() {
  // #docregion setup-shared-module
  beforeEach(async () => {
    await TestBed.configureTestingModule(
      Object.assign({}, appConfig, {
        imports: [HeroDetailComponent, sharedImports],
        providers: [
          provideRouter([{path: 'heroes/:id', component: HeroDetailComponent}]),
          provideHttpClient(),
          provideHttpClientTesting(),
        ],
      }),
    ).compileComponents();
  });
  // #enddocregion setup-shared-module

  it("should display 1st hero's name", async () => {
    const expectedHero = firstHero;
    await createComponent(expectedHero.id).then(() => {
      expect(page.nameDisplay.textContent).toBe(expectedHero.name);
    });
  });
}

/////////// Helpers /////

/** Create the HeroDetailComponent, initialize it, set test variables  */
// #docregion create-component


// #docplaster

import {APP_BASE_HREF} from '@angular/common';
import {CommonEngine} from '@angular/ssr/node';
import express from 'express';
import {fileURLToPath} from 'node:url';
import {dirname, join, resolve} from 'node:path';
import bootstrap from './src/main.server';

// The Express app is exported so that it can be used by serverless Functions.
export function app(): express.Express {
  const server = express();
  const serverDistFolder = dirname(fileURLToPath(import.meta.url));
  const browserDistFolder = resolve(serverDistFolder, '../browser');
  const indexHtml = join(serverDistFolder, 'index.server.html');
  const commonEngine = new CommonEngine();

  server.set('view engine', 'html');
  server.set('views', browserDistFolder);

  // TODO: implement data requests securely
  // Serve data from URLS that begin "/api/"
  server.get('/api/**', (req, res) => {
    res.status(404).send('data requests are not yet supported');
  });
  // Serve static files from /browser
  server.get(
    '*.*',
    express.static(browserDistFolder, {
      maxAge: '1y',
    }),
  );

  // All regular routes use the Angular engine
  server.get('*', (req, res, next) => {
    const {protocol, originalUrl, baseUrl, headers} = req;

    commonEngine
      .render({
        bootstrap,
        documentFilePath: indexHtml,
        url: `${protocol}://${headers.host}${originalUrl}`,
        publicPath: browserDistFolder,
        providers: [{provide: APP_BASE_HREF, useValue: req.baseUrl}],
      })
      .then((html) => res.send(html))
      .catch((err) => next(err));
  });

  return server;
}

function run(): void {
  const port = process.env['PORT'] || 4000;

  // Start up the Node server
  const server = app();
  server.listen(port, () => {
    console.log(`Node Express server listening on http://localhost:${port}`);
  });
}

run();


import {mergeApplicationConfig, ApplicationConfig} from '@angular/core';
import {provideServerRendering} from '@angular/platform-server';
import {appConfig} from './app.config';

const serverConfig: ApplicationConfig = {
  providers: [provideServerRendering()],
};

export const config = mergeApplicationConfig(appConfig, serverConfig);


import {Component} from '@angular/core';
import {RouterLink, RouterOutlet} from '@angular/router';

import {PLATFORM_ID, APP_ID, Inject} from '@angular/core';
import {isPlatformBrowser} from '@angular/common';

import {MessagesComponent} from './messages/messages.component';

@Component({
  standalone: true,
  selector: 'app-root',
  templateUrl: './app.component.html',
  imports: [RouterLink, RouterOutlet, MessagesComponent],
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  title = 'Tour of Heroes';

  constructor(@Inject(PLATFORM_ID) platformId: object, @Inject(APP_ID) appId: string) {
    const platform = isPlatformBrowser(platformId) ? 'in the browser' : 'on the server';
    console.log(`Running ${platform} with appId=${appId}`);
  }
}


import {Component, OnInit} from '@angular/core';
import {NgFor} from '@angular/common';
import {RouterLink} from '@angular/router';

import {Hero} from '../hero';
import {HeroService} from '../hero.service';

@Component({
  standalone: true,
  selector: 'app-heroes',
  templateUrl: './heroes.component.html',
  imports: [NgFor, RouterLink],
  styleUrls: ['./heroes.component.css'],
})
export class HeroesComponent implements OnInit {
  heroes: Hero[] = [];

  constructor(private heroService: HeroService) {}

  ngOnInit() {
    this.getHeroes();
  }

  getHeroes(): void {
    this.heroService.getHeroes().subscribe((heroes) => (this.heroes = heroes));
  }

  add(name: string): void {
    name = name.trim();
    if (!name) {
      return;
    }
    this.heroService.addHero({name} as Hero).subscribe((hero) => {
      this.heroes.push(hero);
    });
  }

  delete(hero: Hero): void {
    this.heroes = this.heroes.filter((h) => h !== hero);
    this.heroService.deleteHero(hero).subscribe();
  }
}


<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <base href="/">
    <title>Elements</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
  </head>
  <body>
    <app-root></app-root>
  </body>
</html>


{{ hero.name }}


// #docregion
import {Component} from '@angular/core';
// #docregion example
/* avoid */

// UsersComponent is in an Admin feature
@Component({
  standalone: true,
  selector: 'users',
  template: '',
})
export class UsersComponent {}
// #enddocregion example


// #docplaster
// #docregion
import {Component} from '@angular/core';

// #docregion example
@Component({
  // #enddocregion example
  template: '
users component
',
  // #docregion example
  standalone: true,
  selector: 'admin-users',
})
export class UsersComponent {}
// #enddocregion example


import {Component} from '@angular/core';
import {HeroesComponent} from './heroes';

@Component({
  standalone: true,
  selector: 'sg-app',
  template: '<toh-heroes></toh-heroes>',
  imports: [HeroesComponent],
})
export class AppComponent {}


My Heroes

    @for (hero of heroes | async; track hero) {

        <button type="button" (click)="selectedHero=hero">
          {{ hero.id }}
          {{ hero.name }}
        </button>

    }

  @if (selectedHero) {

{{ selectedHero.name | uppercase }} is my hero

  }


import {Component} from '@angular/core';
import {Observable} from 'rxjs';

import {Hero, HeroService} from './shared';
import {AsyncPipe, NgFor, NgIf, UpperCasePipe} from '@angular/common';

// #docregion example
/* avoid */

@Component({
  standalone: true,
  selector: 'toh-heroes',
  template: `

My Heroes

        @for (hero of heroes | async; track hero) {

            {{hero.id}} {{hero.name}}

        }

      @if (selectedHero) {

{{selectedHero.name | uppercase}} is my hero

      }

  `,
  styles: [
    `
    .heroes {
      margin: 0 0 2em 0;
      list-style-type: none;
      padding: 0;
      width: 15em;
    }
    .heroes li {
      cursor: pointer;
      position: relative;
      left: 0;
      background-color: #EEE;
      margin: .5em;
      padding: .3em 0;
      height: 1.6em;
      border-radius: 4px;
    }
    .heroes .badge {
      display: inline-block;
      font-size: small;
      color: white;
      padding: 0.8em 0.7em 0 0.7em;
      background-color: #607D8B;
      line-height: 1em;
      position: relative;
      left: -1px;
      top: -4px;
      height: 1.8em;
      margin-right: .8em;
      border-radius: 4px 0 0 4px;
    }
  `,
  ],
  imports: [NgFor, NgIf, AsyncPipe, UpperCasePipe],
})
export class HeroesComponent {
  heroes: Observable<Hero[]>;
  selectedHero!: Hero;

  constructor(private heroService: HeroService) {
    this.heroes = this.heroService.getHeroes();
  }
}
// #enddocregion example


// #docregion
/* avoid */

import {Component, OnInit} from '@angular/core';
import {HttpClient} from '@angular/common/http';

import {Observable} from 'rxjs';
import {catchError, finalize} from 'rxjs/operators';

import {Hero} from '../shared/hero.model';

const heroesUrl = 'http://angular.io';

@Component({
  standalone: true,
  selector: 'toh-hero-list',
  template: `...`,
})
export class HeroListComponent implements OnInit {
  heroes: Hero[];

  constructor(private http: HttpClient) {
    this.heroes = [];
  }

  getHeroes() {
    this.heroes = [];
    this.http
      .get(heroesUrl)
      .pipe(
        catchError(this.catchBadResponse),
        finalize(() => this.hideSpinner()),
      )
      .subscribe((heroes: Hero[]) => (this.heroes = heroes));
  }
  ngOnInit() {
    this.getHeroes();
  }

  private catchBadResponse(err: any, source: Observable<any>) {
    // log and handle the exception
    return new Observable();
  }

  private hideSpinner() {
    // hide the spinner
  }
}


// #docregion example
import {Component, OnInit} from '@angular/core';

import {Hero, HeroService} from '../shared';

@Component({
  standalone: true,
  selector: 'toh-hero-list',
  template: `...`,
})
export class HeroListComponent implements OnInit {
  heroes: Hero[] = [];
  constructor(private heroService: HeroService) {}
  getHeroes() {
    this.heroes = [];
    this.heroService.getHeroes().subscribe((heroes) => (this.heroes = heroes));
  }
  ngOnInit() {
    this.getHeroes();
  }
}
// #enddocregion example


// #docregion
/* avoid */
import {Component, NgModule, OnInit} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';

interface Hero {
  id: number;
  name: string;
}

@Component({
  selector: 'app-root',
  template: `

{{title}}

{{heroes | json}}

    `,
  styleUrls: ['../app.component.css'],
  standalone: false,
})
export class AppComponent implements OnInit {
  title = 'Tour of Heroes';

  heroes: Hero[] = [];

  ngOnInit() {
    getHeroes().then((heroes) => (this.heroes = heroes));
  }
}

@NgModule({
  imports: [BrowserModule],
  declarations: [AppComponent],
  exports: [AppComponent],
  bootstrap: [AppComponent],
})
export class AppModule {}

platformBrowserDynamic().bootstrapModule(AppModule);

const HEROES: Hero[] = [
  {id: 1, name: 'Bombasto'},
  {id: 2, name: 'Tornado'},
  {id: 3, name: 'Magneta'},
];

function getHeroes(): Promise<Hero[]> {
  return Promise.resolve(HEROES); // TODO: get hero data from the server;
}


Structural Directives

Conditional display of hero

{{hero.name}}

List of heroes

{{hero.name}}

NgIf

  Expression is true and ngIf is true.
  This paragraph is in the DOM.

  Expression is false and ngIf is false.
  This paragraph is not in the DOM.

  Expression sets display to "block".
  This paragraph is visible.

  Expression sets display to "none".
  This paragraph is hidden but still in the DOM.

NgIf with template

<ng-template> element

<ng-template [ngIf]="hero">

{{hero.name}}

</ng-template>

<ng-container>

*ngIf with a <ng-container>

<button type="button" (click)="hero = hero ? null : heroes[0]">Toggle hero</button>

  I turned the corner
  <ng-container *ngIf="hero">
    and saw {{hero.name}}. I waved
  </ng-container>
  and continued on my way.

  I turned the corner

    and saw {{hero.name}}. I waved

  and continued on my way.

<select> with <span>

  Pick your favorite hero
  (<input id="show-sad" type="checkbox" checked (change)="showSad = !showSad">show sad)

<select [(ngModel)]="hero">

      <option [ngValue]="h">{{h.name}} ({{h.emotion}})</option>

</select>

<select> with <ng-container>

  Pick your favorite hero
  (<input id="showSad" type="checkbox" checked (change)="showSad = !showSad">show sad)

<select [(ngModel)]="hero">
  <ng-container *ngFor="let h of heroes">
    <ng-container *ngIf="showSad || h.emotion !== 'sad'">
      <option [ngValue]="h">{{h.name}} ({{h.emotion}})</option>
    </ng-container>
  </ng-container>
</select>

NgFor

<div *ngFor="let hero of heroes; let i=index; let odd=odd; trackBy: trackById" [class.odd]="odd">

  ({{i}}) {{hero.name}}

<ng-template ngFor let-hero [ngForOf]="heroes" let-i="index" let-odd="odd" [ngForTrackBy]="trackById"/>

<ng-template ngFor let-hero [ngForOf]="heroes"
  let-i="index" let-odd="odd" [ngForTrackBy]="trackById">

    ({{i}}) {{hero.name}}

</ng-template>

NgSwitch

Pick your favorite hero

    <input id="hero-{{h}}" type="radio" name="heroes" [(ngModel)]="hero" [value]="h">{{h.name}}

  <input id="none-of-the-above" type="radio" name="heroes" (click)="hero = null">None of the above

NgSwitch

  <app-happy-hero    *ngSwitchCase="'happy'"    [hero]="hero!"></app-happy-hero>
  <app-sad-hero      *ngSwitchCase="'sad'"      [hero]="hero!"></app-sad-hero>
  <app-confused-hero *ngSwitchCase="'confused'" [hero]="hero!"></app-confused-hero>
  <app-unknown-hero  *ngSwitchDefault           [hero]="hero!"></app-unknown-hero>

NgSwitch with <ng-template>

  <ng-template ngSwitchCase="happy">
    <app-happy-hero [hero]="hero!"></app-happy-hero>
  </ng-template>
  <ng-template ngSwitchCase="sad">
    <app-sad-hero [hero]="hero!"></app-sad-hero>
  </ng-template>
  <ng-template ngSwitchCase="confused">
    <app-confused-hero [hero]="hero!"></app-confused-hero>
  </ng-template >
  <ng-template ngSwitchDefault>
    <app-unknown-hero [hero]="hero!"></app-unknown-hero>
  </ng-template>

UnlessDirective

  The condition is currently
  {{condition}}.
  <button
    type="button"
    (click)="condition = !condition"
    [ngClass] = "{ 'a': condition, 'b': !condition }" >
    Toggle condition to {{condition ? 'false' : 'true'}}
  </button>

  (A) This paragraph is displayed because the condition is false.

  (B) Although the condition is true,
  this paragraph is displayed because appUnless is set to false.

UnlessDirective with template

Show this sentence unless the condition is true.

  (A) <p *appUnless="condition" class="code unless">

<ng-template [appUnless]="condition">

    (A) <ng-template [appUnless]="condition">

</ng-template>

IfLoadedDirective

<app-hero></app-hero>

TrigonometryDirective

sin(30°): {{ s }}

cos(30°): {{ c }}

tan(30°): {{ t }}


import {Component} from '@angular/core';
import {ChildComponent} from './child.component';

@Component({
  standalone: true,
  selector: 'app-parent',
  imports: [ChildComponent],
  template: '<app-child/>',
})
export class ParentComponent {}


Built-in Directives

Built-in attribute directives

NgModel (two-way) Binding

<fieldset>
NgModel examples

Current item name: {{ currentItem.name }}

    without NgModel:
    <input [value]="currentItem.name" (input)="currentItem.name=getValue($event)" id="without">

    [(ngModel)]:
    <input [(ngModel)]="currentItem.name" id="example-ngModel">

    (ngModelChange)="...name=$event":
    <input [ngModel]="currentItem.name" (ngModelChange)="currentItem.name=$event" id="example-change">

    (ngModelChange)="setUppercaseName($event)"

      <input [ngModel]="currentItem.name" (ngModelChange)="setUppercaseName($event)" id="example-uppercase">

</fieldset>

NgClass Binding

currentClasses is {{ currentClasses | json }}

This div is initially saveable, unchanged, and special.

    saveable
    <input type="checkbox" [(ngModel)]="canSave" id="saveable">

    modified:
    <input type="checkbox" [value]="!isUnchanged" (change)="isUnchanged=!isUnchanged" id="modified">

    special: <input type="checkbox" [(ngModel)]="isSpecial" id="special">

<button type="button" (click)="setCurrentClasses()">Refresh currentClasses</button>

  This div should be {{ canSave ? "": "not"}} saveable,
                  {{ isUnchanged ? "unchanged" : "modified" }} and
                  {{ isSpecial ? "": "not"}} special after clicking "Refresh".

This div is special

Helpful study course

Study course

NgStyle Binding

  This div is x-large or smaller.

[ngStyle] binding to currentStyles - CSS property names

currentStyles is {{ currentStyles | json }}

  This div is initially italic, normal weight, and extra large (24px).

italic: <input id="canSave" type="checkbox" [(ngModel)]="canSave"> |
normal: <input id="isUnchanged" type="checkbox" [(ngModel)]="isUnchanged"> |
xlarge: <input id="isSpecial" type="checkbox" [(ngModel)]="isSpecial">
<button type="button" (click)="setCurrentStyles()">Refresh currentStyles</button>

  This div should be {{ canSave ? "italic": "plain"}},
                  {{ isUnchanged ? "normal weight" : "bold" }} and,
                  {{ isSpecial ? "extra large": "normal size"}} after clicking "Refresh".

Built-in structural directives

NgIf Binding

If isActive is true, app-item-detail will render:

  <app-item-detail *ngIf="isActive" [item]="item"></app-item-detail>

  <button type="button" (click)="isActiveToggle()">Toggle app-item-detail</button>

If currentCustomer isn't null, say hello to Laura:

Hello, {{ currentCustomer.name }}

nullCustomer is null by default. NgIf guards against null. Give it a value to show it:

Hello, {{ nullCustomer }}

<button type="button" (click)="giveNullCustomerValue()">Give nullCustomer a value</button>

NgIf binding with template (no *)

<ng-template [ngIf]="currentItem">Add {{ currentItem.name }} with template</ng-template>

Show/hide vs. NgIf

Show with class

Hide with class

ItemDetail is in the DOM but hidden

<app-item-detail [class.hidden]="isSpecial"></app-item-detail>

Show with style

Hide with style

NgFor Binding

{{ item.name }}

*ngFor with ItemDetailComponent element

  <app-item-detail *ngFor="let item of items" [item]="item"></app-item-detail>

*ngFor with index

with semi-colon separator

{{ i + 1 }} - {{ item.name }}

with comma separator

{{ i + 1 }} - {{ item.name }}

*ngFor trackBy

<button type="button" (click)="resetList()">Reset items</button>
<button type="button" (click)="changeIds()">Change ids</button>
<button type="button" (click)="clearTrackByCounts()">Clear counts</button>

without trackBy

({{ item.id }}) {{ item.name }}

    Item DOM elements change #{{ itemsNoTrackByCount }} without trackBy

with trackBy

({{ item.id }}) {{ item.name }}

    Item DOM elements change #{{ itemsWithTrackByCount }} with trackBy


with trackBy and semi-colon separator

    ({{ item.id }}) {{ item.name }}

with trackBy and comma separator

({{ item.id }}) {{ item.name }}

with trackBy and space separator

({{ item.id }}) {{ item.name }}

with generic trackById function

({{ item.id }}) {{ item.name }}

NgSwitch Binding

Pick your favorite item

<input id="item-{{i}}"type="radio" name="items" [(ngModel)]="currentItem" [value]="i">{{ i.name }}

  <app-stout-item    *ngSwitchCase="'stout'"    [item]="currentItem"></app-stout-item>
  <app-device-item   *ngSwitchCase="'slim'"     [item]="currentItem"></app-device-item>
  <app-lost-item     *ngSwitchCase="'vintage'"  [item]="currentItem"></app-lost-item>
  <app-best-item     *ngSwitchCase="'bright'"   [item]="currentItem"></app-best-item>

Are you as bright as {{ currentItem.name }}?

  <app-unknown-item  *ngSwitchDefault           [item]="currentItem"></app-unknown-item>


// #docregion
import {Component} from '@angular/core';
import {CarComponent} from './car/car.component';
import {HeroesComponent} from './heroes/heroes.component';

@Component({
  standalone: true,
  selector: 'app-root',
  template: `
    <h1>{{title}}</h1>
    <app-car></app-car>
    <app-heroes></app-heroes>
  `,
  imports: [CarComponent, HeroesComponent],
})
export class AppComponent {
  title = 'Dependency Injection';
}


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

import {APP_CONFIG, AppConfig} from './injection.config';
import {UserService} from './user.service';
import {HeroesComponent} from './heroes/heroes.component';
import {HeroesTspComponent} from './heroes/heroes-tsp.component';
import {ProvidersComponent} from './providers.component';
import {CarComponent} from './car/car.component';
import {InjectorComponent} from './injector.component';
import {TestComponent} from './test.component';
import {NgIf} from '@angular/common';

@Component({
  standalone: true,
  selector: 'app-root',
  template: `

{{title}}

    <app-car></app-car>
    <app-injectors></app-injectors>
    <app-tests></app-tests>

User

      {{userInfo}}
      <button type="button" (click)="nextUser()">Next User</button>

    @if (isAuthorized) {
      <app-heroes id="authorized"></app-heroes>
    }
    @if (!isAuthorized) {
      <app-heroes id="unauthorized"></app-heroes>
    }
    @if (isAuthorized) {
      <app-heroes-tsp id="tspAuthorized"></app-heroes-tsp>
    }
    <app-providers></app-providers>
  `,
  imports: [
    HeroesComponent,
    HeroesTspComponent,
    ProvidersComponent,
    CarComponent,
    InjectorComponent,
    TestComponent,
    NgIf,
  ],
})
export class AppComponent {
  title: string;

  constructor(
    @Inject(APP_CONFIG) config: AppConfig,
    private userService: UserService,
  ) {
    this.title = config.title;
  }

  get isAuthorized() {
    return this.user.isAuthorized;
  }
  nextUser() {
    this.userService.getNewUser();
  }
  get user() {
    return this.userService.user;
  }

  get userInfo() {
    return (
      `Current user, ${this.user.name}, is ` + `${this.isAuthorized ? '' : 'not'} authorized. `
    );
  }
}


import {ApplicationConfig} from '@angular/core';
import {Logger} from './logger.service';
import {UserService} from './user.service';
import {APP_CONFIG, HERO_DI_CONFIG} from './injection.config';
import {provideProtractorTestingSupport} from '@angular/platform-browser';

const appConfig: ApplicationConfig = {
  providers: [
    provideProtractorTestingSupport(),
    Logger,
    UserService,
    {provide: APP_CONFIG, useValue: HERO_DI_CONFIG},
  ],
};

export default appConfig;


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

import {APP_CONFIG, AppConfig} from './injection.config';
import {CarComponent} from './car/car.component';
import {HeroesComponent} from './heroes/heroes.component';

@Component({
  standalone: true,
  selector: 'app-root',
  template: `
    <h1>{{title}}</h1>
    <app-car></app-car>
    <app-heroes></app-heroes>
  `,
  imports: [CarComponent, HeroesComponent],
})
export class AppComponent {
  title: string;

  // #docregion ctor
  constructor(@Inject(APP_CONFIG) config: AppConfig) {
    this.title = config.title;
  }
  // #enddocregion ctor
}


// #docregion
import {EnvironmentInjector, inject, Injectable, runInInjectionContext} from '@angular/core';

@Injectable({providedIn: 'root'})
export class SomeService {}

// #docregion run-in-context
@Injectable({
  providedIn: 'root',
})
export class HeroService {
  private environmentInjector = inject(EnvironmentInjector);

  someMethod() {
    runInInjectionContext(this.environmentInjector, () => {
      inject(SomeService); // Do what you need with the injected service
    });
  }
}
// #enddocregion run-in-context


import {NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {RouterModule} from '@angular/router';
import {ServiceModule} from './service-and-module';

// #docregion
@NgModule({
  imports: [BrowserModule, RouterModule.forRoot([]), ServiceModule],
})
export class AppModule {}


import {Injectable} from '@angular/core';

// #docregion
@Injectable({
  providedIn: 'root',
})
export class Service {}


// #docregion
import {Injectable, NgModule} from '@angular/core';

@Injectable()
export class Service {
  doSomething(): void {}
}

@NgModule({
  providers: [Service],
})
export class ServiceModule {}


<!DOCTYPE html>
<html lang="en">
 <head>
    <meta charset="UTF-8">
    <title>Animations</title>
    <base href="/">
    <meta name="viewport" content="width=device-width, initial-scale=1">
  </head>

  <body>
    <app-root></app-root>
  </body>

</html>


import {ApplicationConfig} from '@angular/core';
import {routes} from './app.routes';
import {provideRouter} from '@angular/router';
import {provideProtractorTestingSupport} from '@angular/platform-browser';
import {provideAnimations} from '@angular/platform-browser/animations';

export const appConfig: ApplicationConfig = {
  providers: [
    // needed for supporting e2e tests
    provideProtractorTestingSupport(),
    provideRouter(routes),
    provideAnimations(),
  ],
};


import {NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';

@NgModule({
  imports: [BrowserModule, BrowserAnimationsModule],
  declarations: [],
  bootstrap: [],
})
export class AppModule {}


// #docplaster
import {NgModule} from '@angular/core';
import {Routes, RouterModule} from '@angular/router'; // CLI imports router

// #docregion child-routes
const routes: Routes = [
  {
    path: 'first-component',
    component: FirstComponent, // this is the component with the <router-outlet> in the template
    children: [
      {
        path: 'child-a', // child route path
        component: ChildAComponent, // child route component that the router renders
      },
      {
        path: 'child-b',
        component: ChildBComponent, // another child route component that the router renders
      },
    ],
  },
];
// #enddocregion child-routes

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
})
export class AppRoutingModule {}


// #docplaster
// #docregion
import {inject} from '@angular/core';
import {Router, NavigationExtras} from '@angular/router';
import {AuthService} from './auth.service';

export const authGuard = () => {
  const authService = inject(AuthService);
  const router = inject(Router);

  if (authService.isLoggedIn) {
    return true;
  }

  // Create a dummy session id
  const sessionId = 123456789;

  // Set our navigation extras object
  // that contains our global query params and fragment
  const navigationExtras: NavigationExtras = {
    queryParams: {session_id: sessionId},
    fragment: 'anchor',
  };

  // Redirect to the login page with extras
  return router.createUrlTree(['/login'], navigationExtras);
};


// #docplaster
import {inject} from '@angular/core';
import {Router, NavigationExtras} from '@angular/router';
import {AuthService} from './auth.service';

export const authGuard = () => {
  const router = inject(Router);
  const authService = inject(AuthService);

  if (authService.isLoggedIn) {
    return true;
  }

  // Create a dummy session id
  const sessionId = 123456789;

  // Set our navigation extras object
  // that contains our global query params and fragment
  const navigationExtras: NavigationExtras = {
    queryParams: {session_id: sessionId},
    fragment: 'anchor',
  };

  // Navigate to the login page with extras
  return router.createUrlTree(['/login'], navigationExtras);
};


// #docregion
import {inject} from '@angular/core';
import {Router} from '@angular/router';

import {AuthService} from './auth.service';

export const authGuard = () => {
  const authService = inject(AuthService);
  const router = inject(Router);

  if (authService.isLoggedIn) {
    return true;
  }

  // Redirect to the login page
  return router.parseUrl('/login');
};

// #enddocregion


import {inject} from '@angular/core';
import {Router} from '@angular/router';
import {AuthService} from './auth.service';

export const authGuard = () => {
  const authService = inject(AuthService);
  const router = inject(Router);

  if (authService.isLoggedIn) {
    return true;
  }

  // Redirect to the login page
  return router.parseUrl('/login');
};


import {Component} from '@angular/core';
import {FormsModule} from '@angular/forms';
import {bootstrapApplication} from '@angular/platform-browser';

@Component({
  selector: 'app-root',
  standalone: true,
  template: `
    Name:
    <input type="text" id="name" [(ngModel)]="name" placeholder="Enter a name here" />

Hello {{ name }}!

  `,
  imports: [FormsModule],
})
export class DemoComponent {
  name = '';
}

bootstrapApplication(DemoComponent);


# Create Home component

This tutorial lesson demonstrates how to create a new [component](guide/components) for your Angular app.

<docs-video src="https://www.youtube.com/embed/R0nRX8jD2D0?si=OMVaw71EIa44yIOJ"/>

## What you'll learn

Your app has a new component: `HomeComponent`.

## Conceptual preview of Angular components

Angular apps are built around components, which are Angular's building blocks.
Components contain the code, HTML layout, and CSS style information that provide the function and appearance of an element in the app.
In Angular, components can contain other components. An app's functions and appearance can be divided and partitioned into components.

In Angular, components have metadata that define its properties.
When you create your `HomeComponent`, you use these properties:

* `selector`: to describe how Angular refers to the component in templates.
* `standalone`: to describe whether the component requires a `NgModule`.
* `imports`: to describe the component's dependencies.
* `template`: to describe the component's HTML markup and layout.
* `styleUrls`: to list the URLs of the CSS files that the component uses in an array.

<docs-pill-row>
  <docs-pill href="api/core/Component" title="Learn more about Components"/>
</docs-pill-row>

<docs-workflow>

<docs-step title="Create the `HomeComponent`">
In this step, you create a new component for your app.

In the **Terminal** pane of your IDE:

1. In your project directory, navigate to the `first-app` directory.
1. Run this command to create a new `HomeComponent`

    <docs-code language="shell">
    ng generate component home
    </docs-code>

1. Run this command to build and serve your app.

    Note: This step is only for your local environment!

    <docs-code language="shell">
    ng serve
    </docs-code>

1. Open a browser and navigate to `http://localhost:4200` to find the application.

1. Confirm that the app builds without error.

    HELPFUL: It should render the same as it did in the previous lesson because even though you added a new component, you haven't included it in any of the app's templates, yet.

1. Leave `ng serve` running as you complete the next steps.
</docs-step>

<docs-step title="Add the new component to your app's layout">
In this step, you add the new component, `HomeComponent` to your app's root component, `AppComponent`, so that it displays in your app's layout.

In the **Edit** pane of your IDE:

1. Open `app.component.ts` in the editor.
1. In `app.component.ts`, import `HomeComponent` by adding this line to the file level imports.

    <docs-code header="Import HomeComponent in src/app/app.component.ts" path="adev/src/content/tutorials/first-app/steps/03-HousingLocation/src/app/app.component.ts" visibleLines="[2]"/>

1. In `app.component.ts`, in `@Component`, update the `imports` array property and add `HomeComponent`.

    <docs-code header="Replace in src/app/app.component.ts" path="adev/src/content/tutorials/first-app/steps/03-HousingLocation/src/app/app.component.ts" visibleLines="[7]"/>

1. In `app.component.ts`, in `@Component`, update the `template` property to include the following HTML code.

    <docs-code header="Replace in src/app/app.component.ts" path="adev/src/content/tutorials/first-app/steps/03-HousingLocation/src/app/app.component.ts" visibleLines="[8,17]"/>

1. Save your changes to  `app.component.ts`.
1. If `ng serve` is running, the app should update.
    If `ng serve` is not running, start it again.
    *Hello world* in your app should change to *home works!* from the `HomeComponent`.
1. Check the running app in the browser and confirm that the app has been updated.

</docs-step>

<docs-step title="Add features to `HomeComponent`">

In this step you add features to `HomeComponent`.

In the previous step, you added the default `HomeComponent` to your app's template so its default HTML appeared in the app.
In this step, you add a search filter and button that is used in a later lesson.
For now, that's all that `HomeComponent` has.
Note that, this step just adds the search elements to the layout without any functionality, yet.

In the **Edit** pane of your IDE:

1. In the `first-app` directory, open `home.component.ts` in the editor.
1. In `home.component.ts`, in `@Component`, update the `template` property with this code.

    <docs-code header="Replace in src/app/home/home.component.ts" path="adev/src/content/tutorials/first-app/steps/03-HousingLocation/src/app/home/home.component.ts" visibleLines="[8,15]"/>

1. Next, open `home.component.css` in the editor and update the content with these styles.

    Note: In the browser, these can go in `src/app/home/home.component.ts` in the `styles` array.

    <docs-code header="Replace in src/app/home/home.component.css" path="adev/src/content/tutorials/first-app/steps/03-HousingLocation/src/app/home/home.component.css"/>

1. Confirm that the app builds without error.
    You should find the filter query box and button in your app and they should be styled.
    Correct any errors before you continue to the next step.

    </docs-step>

</docs-workflow>

Summary: In this lesson, you created a new component for your app and gave it a filter edit control and button.

For more information about the topics covered in this lesson, visit:

<docs-pill-row>
  <docs-pill href="cli/generate/component" title="`ng generate component`"/>
  <docs-pill href="api/core/Component" title="`Component` reference"/>
  <docs-pill href="guide/components" title="Angular components overview"/>
</docs-pill-row>


import {Component} from '@angular/core';
import {CommonModule} from '@angular/common';

@Component({
  selector: 'app-home',
  standalone: true,
  imports: [CommonModule],
  template: `
    <section>
      <form>
        <input type="text" placeholder="Filter by city" />
        <button class="primary" type="button">Search</button>
      </form>
    </section>
  `,
  styleUrls: ['./home.component.css'],
})
export class HomeComponent {}


import {Component} from '@angular/core';
import {CommonModule} from '@angular/common';

@Component({
  selector: 'app-housing-location',
  standalone: true,
  imports: [CommonModule],
  template: `

housing-location works!

  `,
  styleUrls: ['./housing-location.component.css'],
})
export class HousingLocationComponent {}


# Add a property binding to a component’s template

This tutorial lesson demonstrates how to add property binding to a template and use it to pass dynamic data to components.

<docs-video src="https://www.youtube.com/embed/eM3zi_n7lNs?si=AsiczpWnMz5HhJqB&start=599"/>

## What you'll learn

* Your app has data bindings in the `HomeComponent` template.
* Your app sends data from the `HomeComponent` to the `HousingLocationComponent`.

## Conceptual preview of Inputs

In this lesson, you'll continue the process of sharing data from the parent component to the child component by binding data to those properties in the template using property binding.

Property binding enables you to connect a variable to an `Input` in an Angular template. The data is then dynamically bound to the `Input`.

For a more in depth explanation, please refer to the [Property binding](guide/templates/property-binding) guide.

<docs-workflow>

<docs-step title="Update the `HomeComponent` template">
This step adds property binding to the `<app-housing-location>` tag.

In the code editor:

1. Navigate to `src/app/home/home.component.ts`
1. In the template property of the `@Component` decorator, update the code to match the code below:
    <docs-code header="Add housingLocation property binding" path="adev/src/content/tutorials/first-app/steps/07-dynamic-template-values/src/app/home/home.component.ts" visibleLines="[17,19]"/>

    When adding a property binding to a component tag, we use the `[attribute] = "value"` syntax to notify Angular that the assigned value should be treated as a property from the component class and not a string value.

    The value on the right-hand side is the name of the property from the `HomeComponent`.
</docs-step>

<docs-step title="Confirm the code still works">
1.  Save your changes and confirm the app does not have any errors.
1.  Correct any errors before you continue to the next step.
</docs-step>

</docs-workflow>

Summary: In this lesson, you added a new property binding and passed in a reference to a class property. Now, the `HousingLocationComponent` has access to data that it can use to customize the component's display.

For more information about the topics covered in this lesson, visit:

<docs-pill-row>
  <docs-pill href="guide/templates/property-binding" title="Property binding"/>
</docs-pill-row>


import {Component} from '@angular/core';
import {HomeComponent} from './home/home.component';
import {RouterLink, RouterOutlet} from '@angular/router';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [HomeComponent, RouterLink, RouterOutlet],
  template: `

        <router-outlet></router-outlet>

  `,
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  title = 'homes';
}


# Add routes to the application

This tutorial lesson demonstrates how to add routes to your app.

<docs-video src="https://www.youtube.com/embed/r5DEBMuStPw?si=H6Bx6nLJoMLaMxkx" />

IMPORTANT: We recommend using your local environment to learn routing.

## What you'll learn

At the end of this lesson your application will have support for routing.

## Conceptual preview of routing

This tutorial introduces routing in Angular. Routing is the ability to navigate from one component in the application to another. In [Single Page Applications (SPA)](guide/routing), only parts of the page are updated to represent the requested view for the user.

The [Angular Router](guide/routing) enables users to declare routes and specify which component should be displayed on the screen if that route is requested by the application.

In this lesson, you will enable routing in your application to navigate to the details page.

<docs-workflow>

<docs-step title="Create a default details component ">
1. From the terminal, enter the following command to create the `DetailsComponent`:

    <docs-code language="shell">
    ng generate component details
    </docs-code>

    This component will represent the details page that provides more information on a given housing location.
</docs-step>

<docs-step title="Add routing to the application">
1.  In the `src/app` directory, create a file called `routes.ts`. This file is where we will define the routes in the application.

1. In `main.ts`, make the following updates to enable routing in the application:
    1. Import the routes file and the `provideRouter` function:

        <docs-code header="Import routing details in src/main.ts" path="adev/src/content/tutorials/first-app/steps/11-details-page/src/main.ts" visibleLines="[7,8]"/>

    1. Update the call to `bootstrapApplication` to include the routing configuration:

        <docs-code header="Add router configuration in src/main.ts" path="adev/src/content/tutorials/first-app/steps/11-details-page/src/main.ts" visibleLines="[10,17]"/>

1. In `src/app/app.component.ts`, update the component to use routing:
    1. Add a file level import for `RoutingModule`:

        <docs-code header="Import RouterModule in src/app/app.component.ts" path="adev/src/content/tutorials/first-app/steps/11-details-page/src/app/app.component.ts" visibleLines="[3]"/>

    1. Add `RouterModule` to the `@Component` metadata imports

        <docs-code header="Import RouterModule in src/app/app.component.ts" path="adev/src/content/tutorials/first-app/steps/11-details-page/src/app/app.component.ts" visibleLines="[7]"/>

    1. In the `template` property, replace the `<app-home></app-home>` tag with the `<router-outlet>` directive and add a link back to the home page. Your code should match this code:

        <docs-code header="Add router-outlet in src/app/app.component.ts" path="adev/src/content/tutorials/first-app/steps/11-details-page/src/app/app.component.ts" visibleLines="[8,18]"/>

</docs-step>

<docs-step title="Add route to new component">
In the previous step you removed the reference to the `<app-home>` component in the template. In this step, you will add a new route to that component.

1. In `routes.ts`, perform the following updates to create a route.
    1. Add a file level imports for the `HomeComponent`, `DetailsComponent` and the `Routes` type that you'll use in the route definitions.

        <docs-code header="Import components and Routes" path="adev/src/content/tutorials/first-app/steps/11-details-page/src/app/routes.ts" visibleLines="[1,3]"/>

    1. Define a variable called `routeConfig` of type `Routes` and define two  routes for the app:
        <docs-code header="Add routes to the app" path="adev/src/content/tutorials/first-app/steps/11-details-page/src/app/routes.ts" visibleLines="[5,18]"/>

        The entries in the `routeConfig` array represent the routes in the application. The first entry navigates to the `HomeComponent` whenever the url matches `''`. The second entry uses some special formatting that will be revisited in a future lesson.

1. Save all changes and confirm that the application works in the browser. The application should still display the list of housing locations.
</docs-step>

</docs-workflow>

Summary: In this lesson, you enabled routing in your app as well as defined new routes. Now your app can support navigation between views. In the next lesson, you will learn to navigate to the "details" page for a given housing location.

You are making great progress with your app, well done.

For more information about the topics covered in this lesson, visit:

<docs-pill-row>
  <docs-pill href="guide/routing" title="Routing in Angular Overview"/>
  <docs-pill href="guide/routing/common-router-tasks" title="Common Routing Tasks"/>
</docs-pill-row>


# Use *ngFor to list objects in component

This tutorial lesson demonstrates how to use `ngFor` directive in Angular templates in order to display dynamically repeated data in a template.

<docs-video src="https://www.youtube.com/embed/eM3zi_n7lNs?si=MIl5NcRxvcLjYt5f&start=477"/>

## What you'll learn

* You will have added a data set to the app
* Your app will display a list of elements from the new data set using `ngFor`

## Conceptual preview of ngFor

In Angular, `ngFor` is a specific type of [directive](guide/directives) used to dynamically repeat data in a template. In plain JavaScript you would use a for loop - ngFor provides similar functionality for Angular templates.

You can utilize `ngFor` to iterate over arrays and even asynchronous values. In this lesson, you'll add a new array of data to iterate over.

For a more in depth explanation, please refer to the [Built-in directives](guide/directives#ngFor) guide.

<docs-workflow>

<docs-step title="Add housing data to the `HomeComponent`">

In the `HomeComponent` there is only a single housing location. In this step, you will add an array of `HousingLocation` entries.

1. In `src/app/home/home.component.ts`, remove the `housingLocation` property from the `HomeComponent` class.
1. Update the `HomeComponent` class to have a property called `housingLocationList`. Update your code to match the following code:
    <docs-code header="Add housingLocationList property" path="adev/src/content/tutorials/first-app/steps/09-services/src/app/home/home.component.ts" visibleLines="26-131"/>

    IMPORTANT: Do not remove the `@Component` decorator, you will update that code in an upcoming step.

</docs-step>

<docs-step title="Update the `HomeComponent` template to use `ngFor`">
Now the app has a dataset that you can use to display the entries in the browser using the `ngFor` directive.

1. Update the `<app-housing-location>` tag in the template code to this:
    <docs-code header="Add ngFor to HomeComponent template" path="adev/src/content/tutorials/first-app/steps/09-services/src/app/home/home.component.ts" visibleLines="[17,22]"/>

    Note, in the code `[housingLocation] = "housingLocation"` the `housingLocation` value now refers to the variable used in the `ngFor` directive. Before this change, it referred to the property on the `HomeComponent` class.

1. Save all changes.

1. Refresh the browser and confirm that the app now renders a grid of housing locations.

</docs-step>

</docs-workflow>

Summary: In this lesson, you used the `ngFor` directive to repeat data dynamically in Angular templates. You also added a new array of data to be used in the Angular app. The application now dynamically renders a list of housing locations in the browser.

The app is taking shape, great job.

For more information about the topics covered in this lesson, visit:

<docs-pill-row>
  <docs-pill href="guide/directives/structural-directives" title="Structural Directives"/>
  <docs-pill href="guide/directives#ngFor" title="ngFor guide"/>
  <docs-pill href="api/common/NgFor" title="ngFor"/>
</docs-pill-row>


# Add HTTP communication to your app

This tutorial demonstrates how to integrate HTTP and an API into your app.

Up until this point your app has read data from a static array in an Angular service. The next step is to use a JSON server that your app will communicate with over HTTP. The HTTP request will simulate the experience of working with data from a server.

<docs-video src="https://www.youtube.com/embed/5K10oYJ5Y-E?si=TiuNKx_teR9baO7k"/>

IMPORTANT: We recommend using your local environment for this step of the tutorial.


## What you'll learn

Your app will use data from a JSON server

<docs-workflow>

<docs-step title="Configure the JSON server">
JSON Server is an open source tool used to create mock REST APIs. You'll use it to serve the housing location data that is currently stored in the housing service.

1. Install `json-server` from npm by using the following command.
    <docs-code language="bash">
        npm install -g json-server
    </docs-code>

1. In the root directory of your project, create a file called `db.json`. This is where you will store the data for the `json-server`.

1. Open `db.json` and copy the following code into the file
    <docs-code language="json">
        {
            "locations": [
                {
                    "id": 0,
                    "name": "Acme Fresh Start Housing",
                    "city": "Chicago",
                    "state": "IL",
                    "photo": "https://angular.dev/assets/images/tutorials/common/bernard-hermant-CLKGGwIBTaY-unsplash.jpg",
                    "availableUnits": 4,
                    "wifi": true,
                    "laundry": true
                },
                {
                    "id": 1,
                    "name": "A113 Transitional Housing",
                    "city": "Santa Monica",
                    "state": "CA",
                    "photo": "https://angular.dev/assets/images/tutorials/common/brandon-griggs-wR11KBaB86U-unsplash.jpg",
                    "availableUnits": 0,
                    "wifi": false,
                    "laundry": true
                },
                {
                    "id": 2,
                    "name": "Warm Beds Housing Support",
                    "city": "Juneau",
                    "state": "AK",
                    "photo": "https://angular.dev/assets/images/tutorials/common/i-do-nothing-but-love-lAyXdl1-Wmc-unsplash.jpg",
                    "availableUnits": 1,
                    "wifi": false,
                    "laundry": false
                },
                {
                    "id": 3,
                    "name": "Homesteady Housing",
                    "city": "Chicago",
                    "state": "IL",
                    "photo": "https://angular.dev/assets/images/tutorials/common/ian-macdonald-W8z6aiwfi1E-unsplash.jpg",
                    "availableUnits": 1,
                    "wifi": true,
                    "laundry": false
                },
                {
                    "id": 4,
                    "name": "Happy Homes Group",
                    "city": "Gary",
                    "state": "IN",
                    "photo": "https://angular.dev/assets/images/tutorials/common/krzysztof-hepner-978RAXoXnH4-unsplash.jpg",
                    "availableUnits": 1,
                    "wifi": true,
                    "laundry": false
                },
                {
                    "id": 5,
                    "name": "Hopeful Apartment Group",
                    "city": "Oakland",
                    "state": "CA",
                    "photo": "https://angular.dev/assets/images/tutorials/common/r-architecture-JvQ0Q5IkeMM-unsplash.jpg",
                    "availableUnits": 2,
                    "wifi": true,
                    "laundry": true
                },
                {
                    "id": 6,
                    "name": "Seriously Safe Towns",
                    "city": "Oakland",
                    "state": "CA",
                    "photo": "https://angular.dev/assets/images/tutorials/common/phil-hearing-IYfp2Ixe9nM-unsplash.jpg",
                    "availableUnits": 5,
                    "wifi": true,
                    "laundry": true
                },
                {
                    "id": 7,
                    "name": "Hopeful Housing Solutions",
                    "city": "Oakland",
                    "state": "CA",
                    "photo": "https://angular.dev/assets/images/tutorials/common/r-architecture-GGupkreKwxA-unsplash.jpg",
                    "availableUnits": 2,
                    "wifi": true,
                    "laundry": true
                },
                {
                    "id": 8,
                    "name": "Seriously Safe Towns",
                    "city": "Oakland",
                    "state": "CA",
                    "photo": "https://angular.dev/assets/images/tutorials/common/saru-robert-9rP3mxf8qWI-unsplash.jpg",
                    "availableUnits": 10,
                    "wifi": false,
                    "laundry": false
                },
                {
                    "id": 9,
                    "name": "Capital Safe Towns",
                    "city": "Portland",
                    "state": "OR",
                    "photo": "https://angular.dev/assets/images/tutorials/common/webaliser-_TPTXZd9mOo-unsplash.jpg",
                    "availableUnits": 6,
                    "wifi": true,
                    "laundry": true
                }
            ]
        }
    </docs-code>

1. Save this file.

1. Time to test your configuration. From the command line, at the root of your project run the following commands.

    <docs-code language="bash">
        json-server --watch db.json
    </docs-code>

1. In your web browser, navigate to the `http://localhost:3000/locations` and confirm that the response includes the data stored in `db.json`.

If you have any trouble with your configuration, you can find more details in the [official documentation](https://www.npmjs.com/package/json-server).
</docs-step>

<docs-step title="Update service to use web server instead of local array">
The data source has been configured, the next step is to update your web app to connect to it use the data.

1. In `src/app/housing.service.ts`, make the following changes:

    1. Update the code to remove `housingLocationList` property and the array containing the data.

    1. Add a string property called `url` and set its value to `'http://localhost:3000/locations'`

        <docs-code language="javascript">
        url = 'http://localhost:3000/locations';
        </docs-code>

        This code will result in errors in the rest of the file because it depends on the `housingLocationList` property. We're going to update the service methods next.

    1. Update the `getAllHousingLocations` function to make a call to the web server you configured.

        <docs-code header="" path="adev/src/content/tutorials/first-app/steps/14-http/src-final/app/housing.service.ts" visibleLines="[10,13]"/>

        The code now uses asynchronous code to make a **GET** request over HTTP.

        HELPFUL: For this example, the code uses `fetch`. For more advanced use cases consider using `HttpClient` provided by Angular.

    1. Update the `getHousingLocationsById` function to make a call to the web server you configured.

        <docs-code header="" path="adev/src/content/tutorials/first-app/steps/14-http/src-final/app/housing.service.ts" visibleLines="[15,18]"/>

    1. Once all the updates are complete, your updated service should match the following code.

        <docs-code header="Final version of housing.service.ts" path="adev/src/content/tutorials/first-app/steps/14-http/src-final/app/housing.service.ts" visibleLines="[1,24]" />

</docs-step>

<docs-step title="Update the components to use asynchronous calls to the housing service">
The server is now reading data from the HTTP request but the components that rely on the service now have errors because they were programmed to use the synchronous version of the service.

1. In `src/app/home/home.component.ts`, update the `constructor` to use the new asynchronous version of the `getAllHousingLocations` method.

    <docs-code header="" path="adev/src/content/tutorials/first-app/steps/14-http/src-final/app/home/home.component.ts" visibleLines="[32,37]"/>

1. In `src/app/details/details.component.ts`, update the `constructor` to use the new asynchronous version of the `getHousingLocationById` method.

    <docs-code header="" path="adev/src/content/tutorials/first-app/steps/14-http/src-final/app/details/details.component.ts" visibleLines="[61,66]"/>

1. Save your code.

1. Open the application in the browser and confirm that it runs without any errors.
</docs-step>

</docs-workflow>

Note: This lesson relies on the `fetch` browser API. For the support of interceptors, please refer to the [Http Client documentation](/guide/http)

Summary: In this lesson, you updated your app to use a local web server (`json-server`), and use asynchronous service methods to retrieve data.

Congratulations! You've successfully completed this tutorial and are ready to continue your journey with building even more complex Angular Apps.

If you would like to learn more, please consider completing some of Angular's other developer [tutorials](tutorials) and [guides](overview).


/*
 *  Protractor support is deprecated in Angular.
 *  Protractor is used in this example for compatibility with Angular documentation tools.
 */
import {bootstrapApplication, provideProtractorTestingSupport} from '@angular/platform-browser';
import {AppComponent} from './app/app.component';
import {provideRouter} from '@angular/router';
import routeConfig from './app/routes';

bootstrapApplication(AppComponent, {
  providers: [provideProtractorTestingSupport(), provideRouter(routeConfig)],
}).catch((err) => console.error(err));


# Control Flow in Components - `@if`

Deciding what to display on the screen for a user is a common task in application development. Many times, the decision is made programmatically using conditions.

To express conditional displays in templates, Angular uses the `@if` template syntax.

In this activity, you'll learn how to use conditionals in templates.

<hr/>

The syntax that enables the conditional display of elements in a template is `@if`.

Here's an example of how to use the `@if` syntax in a component:

```angular-ts
@Component({
  ...
  template: `
    @if (isLoggedIn) {
      <p>Welcome back, Friend!</p>
    }
  `,
})
class AppComponent {
  isLoggedIn = true;
}
```

Two things to take note of:

- There is an `@` prefix for the `if` because it is a special type of syntax called [Angular template syntax](guide/templates)
- For applications using v16 and older please refer to the [Angular documentation for NgIf](guide/directives/structural-directives) for more information.

<docs-workflow>

<docs-step title="Create a property called `isServerRunning`">
In the `AppComponent` class, add a `boolean` property called `isServerRunning`, set the initial value to `true`.
</docs-step>

<docs-step title="Use `@if` in the template">
Update the template to display the message `Yes, the server is running` if the value of `isServerRunning` is `true`.

</docs-step>

<docs-step title="Use `@else` in the template">
Now Angular supports native template syntax for defining the else case with the `@else` syntax. Update the template to display the message `No, the server is not running` as the else case.

Here's an example:

```angular-ts
template: `
  @if (isServerRunning) { ... }
  @else { ... }
`;
```

Add your code to fill in the missing markup.

</docs-step>

</docs-workflow>

This type of functionality is called conditional control flow. Next you'll learn how to repeat items in a template.


import {Component} from '@angular/core';
import {RouterOutlet, RouterLink} from '@angular/router';

@Component({
  selector: 'app-root',
  template: `

      Home
      |
      User

    <router-outlet />
  `,
  standalone: true,
  imports: [RouterOutlet, RouterLink],
})
export class AppComponent {}


# Routing Overview

For most apps, there comes a point where the app requires more than a single page. When that time inevitably comes, routing becomes a big part of the performance story for users.

In this activity, you'll learn how to setup and configure your app to use Angular Router.

<docs-workflow>

<docs-step title="Create an app.routes.ts file">

Inside `app.routes.ts`, make the following changes:

1. Import `Routes` from the `@angular/router` package.
1. Export a constant called `routes` of type `Routes`, assign it `[]` as the value.

```ts
import {Routes} from '@angular/router';

export const routes: Routes = [];
```

</docs-step>

<docs-step title="Add routing to provider">

In `app.config.ts`, configure the app to Angular Router with the following steps:

1. Import the `provideRouter` function from `@angular/router`.
1. Import `routes` from the `./app.routes.ts`.
1. Call the `provideRouter` function with `routes` passed in as an argument in the `providers` array.

<docs-code language="ts" highlight="[2,3,6]">
import {ApplicationConfig} from '@angular/core';
import {provideRouter} from '@angular/router';
import {routes} from './app.routes';

export const appConfig: ApplicationConfig = {
  providers: [provideRouter(routes)],
};
</docs-code>

</docs-step>

<docs-step title="Import `RouterOutlet` in the component">

Finally, to make sure your app is ready to use the Angular Router, you need to tell the app where you expect the router to display the desired content. Accomplish that by using the `RouterOutlet` directive from `@angular/router`.

Update the template for `AppComponent` by adding `<router-outlet />`

<docs-code language="angular-ts" highlight="[11]">
import {RouterOutlet} from '@angular/router';

@Component({
  ...
  template: `

      Home
      |
      User

    <router-outlet />
  `,
  standalone: true,
  imports: [RouterOutlet],
})
export class AppComponent {}
</docs-code>

</docs-step>

</docs-workflow>

Your app is now setup to use Angular Router. Nice work! 🙌

Keep the momentum going to learn the next step of defining the routes for our app.


import {Routes} from '@angular/router';

export const routes: Routes = [];


# Inject-based dependency injection

Creating an injectable service is the first part of the dependency injection (DI) system in Angular. How do you inject a service into a component? Angular has a convenient function called `inject()` that can be used in the proper context.

Note: Injection contexts are beyond the scope of this tutorial, but you can find more information in the [Angular Docs](guide/di/dependency-injection-context) if you would like to learn more.

In this activity, you'll learn how to inject a service and use it in a component.

<hr>

It is often helpful to initialize class properties with values provided by the DI system. Here's an example:

<docs-code language="ts" highlight="[3]">
@Component({...})
class PetCareDashboardComponent {
    petRosterService = inject(PetRosterService);
}
</docs-code>

<docs-workflow>

<docs-step title="Inject the `CarService`">

In `app.component.ts`, using the `inject()` function inject the `CarService` and assign it to a property called `carService`

Note: Notice the difference between the property `carService` and the class `CarService`.

</docs-step>

<docs-step title="Use the `carService` instance">

Calling `inject(CarService)` gave you an instance of the `CarService` that you can use in your application, stored in the `carService` property.

In the `constructor` function of the `AppComponent`, add the following implementation:

```ts
constructor() {
    this.display = this.carService.getCars().join(' ⭐ ');
}
```

</docs-step>

<docs-step title="Update the `AppComponent` template">

Update the component template in `app.component.ts` with the following code:

```ts
template: `<p>Car Listing: {{ display }}</p>`,
```

</docs-step>

</docs-workflow>

You've just injected your first service into a component - fantastic effort. Before you finish this section on DI, you'll learn an alternative syntax to inject resources into your components.


# Reactive Forms

When you want to manage your forms programmatically instead of relying purely on the template, reactive forms are the answer.

In this activity, you'll learn how to setup reactive forms.

<hr>

<docs-workflow>

<docs-step title="Import `ReactiveForms` module">

In `app.component.ts`, import `ReactiveFormsModule` from `@angular/forms` and add it to the `imports` array of the component.

```angular-ts
import { ReactiveFormsModule } from '@angular/forms';

@Component({
  selector: 'app-root',
  standalone: true,
  template: `
    <form>
      <label>Name
        <input type="text" />
      </label>
      <label>Email
        <input type="email" />
      </label>
      <button type="submit">Submit</button>
    </form>
  `,
  imports: [ReactiveFormsModule],
})
```

</docs-step>

<docs-step title="Create the `FormGroup` object with FormControls">

Reactive forms use the `FormControl` class to represent the form controls (e.g., inputs). Angular provides the `FormGroup` class to serve as a grouping of form controls into a helpful object that makes handling large forms more convenient for developers.

Add `FormControl` and `FormGroup` to the import from `@angular/forms` so that you can create a FormGroup for each form, with the properties `name` and `email` as FormControls.

```ts
import {ReactiveFormsModule, FormControl, FormGroup } from '@angular/forms';
...
export class AppComponent {
  profileForm = new FormGroup({
    name: new FormControl(''),
    email: new FormControl(''),
  });
}
```

</docs-step>

<docs-step title="Link the FormGroup and FormControls to the form">

Each `FormGroup` should be attached to a form using the `[formGroup]` directive.

In addition, each `FormControl` can be attached with the `formControlName` directive and assigned to the corresponding property. Update the template with the following form code:

```angular-html
<form [formGroup]="profileForm">
  <label>
    Name
    <input type="text" formControlName="name" />
  </label>
  <label>
    Email
    <input type="email" formControlName="email" />
  </label>
  <button type="submit">Submit</button>
</form>
```

</docs-step>

<docs-step title="Handle update to the form">

When you want to access data from the `FormGroup`, it can be done by accessing the value of the `FormGroup`. Update the `template` to display the form values:

```angular-html
...
<h2>Profile Form</h2>
<p>Name: {{ profileForm.value.name }}</p>
<p>Email: {{ profileForm.value.email }}</p>
```

</docs-step>

<docs-step title="Access FormGroup values">
Add a new method to the component class called `handleSubmit` that you'll later use to handle the form submission.
This method will display values from the form, you can access the values from the FormGroup.

In the component class, add the `handleSubmit()` method to handle the form submission.

<docs-code language="ts">
handleSubmit() {
  alert(
    this.profileForm.value.name + ' | ' + this.profileForm.value.email
  );
}
</docs-code>
</docs-step>

<docs-step title="Add `ngSubmit` to the form">
You have access to the form values, now it is time to handle the submission event and use the `handleSubmit` method.
Angular has an event handler for this specific purpose called `ngSubmit`. Update the form element to call the `handleSubmit` method when the form is submitted.

<docs-code language="angular-html" highlight="[3]">
<form
  [formGroup]="profileForm"
  (ngSubmit)="handleSubmit()">
</docs-code>

</docs-step>

</docs-workflow>

And just like that, you know how to work with reactive forms in Angular.

Fantastic job with this activity. Keep going to learn about form validation.


# Control Flow in Components - `@for`

Often when building web applications, you need to repeat some code a specific number of times - for example, given an array of names, you may want to display each name in a `<p>` tag.

In this activity, you'll learn how to use `@for` to repeat elements in a template.
<hr/>

The syntax that enables repeating elements in a template is `@for`.

Here's an example of how to use the `@for` syntax in a component:

```angular-ts
@Component({
  ...
  template: `
    @for (os of operatingSystems; track os.id) {
      {{ os.name }}
    }
  `,
})
export class AppComponent {
  operatingSystems = [{id: 'win', name: 'Windows'}, {id: 'osx', name: 'MacOS'}, {id: 'linux', name: 'Linux'}];
}
```

Two things to take note of:

* There is an `@` prefix for the `for` because it is a special syntax called [Angular template syntax](guide/templates)
* For applications using v16 and older please refer to the [Angular documentation for NgFor](guide/directives/structural-directives)

<docs-workflow>

<docs-step title="Add the `users` property">
In the `AppComponent` class, add a property called `users` that contains users and their names.

```ts
[{id: 0, name: 'Sarah'}, {id: 1, name: 'Amy'}, {id: 2, name: 'Rachel'}, {id: 3, name: 'Jessica'}, {id: 4, name: 'Poornima'}]
```

</docs-step>

<docs-step title="Update the template">
Update the template to display each user name in a `p` element using the `@for` template syntax.

```angular-html
@for (user of users; track user.id) {
  <p>{{ user.name }}</p>
}
```

Note: the use of `track` is required, you may use the `id` or some other unique identifier.

</docs-step>

</docs-workflow>

This type of functionality is called control flow. Next, you'll learn to customize and communicate with components - by the way, you're doing a great job so far.


# Property Binding in Angular

Property binding in Angular enables you to set values for properties of HTML elements, Angular components and more.

Use property binding to dynamically set values for properties and attributes. You can do things such as toggle button features, set image paths programmatically, and share values between components.

In this activity, you'll learn how to use property binding in templates.

To bind to an element's attribute, wrap the attribute name in square brackets. Here's an example:

```angular-html
<img alt="photo" [src]="imageURL">
```

In this example, the value of the `src` attribute will be bound to the class property `imageURL`. Whatever value `imageURL` has will be set as the `src` attribute of the `img` tag.

<docs-workflow>

<docs-step title="Add a property called `isEditable`" header="app.component.ts" language="ts">
Update the code in `app.component.ts` by adding a property to the `AppComponent` class called `isEditable` with the initial value set to `true`.

<docs-code highlight="[2]">
export class AppComponent {
    isEditable = true;
}
</docs-code>
</docs-step>

<docs-step title="Bind to `contentEditable`" header="app.component.ts" language="ts">
Next, bind the `contentEditable` attribute of the `div` to the `isEditable` property by using the [] syntax.

<docs-code highlight="[3]" language="angular-ts">
@Component({
    ...
    template: `<div [contentEditable]="isEditable"></div>`,
})
</docs-code>
</docs-step>

</docs-workflow>

The div is now editable. Nice work 👍

Property binding is one of Angular's many powerful features. If you'd like to learn more checkout [the Angular documentation](guide/templates/property-binding).


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

@Component({
  selector: 'app-user',
  template: `
    Username: {{ username }}
  `,
  standalone: true,
})
export class UserComponent {
  username = 'youngTech';
}

@Component({
  selector: 'app-root',
  template: `
    <section>
      <app-user />
    </section>
  `,
  standalone: true,
  imports: [UserComponent],
})
export class AppComponent {}


import {Component} from '@angular/core';
import {FormsModule} from '@angular/forms';

@Component({
  selector: 'app-user',
  template: `

Username: {{ username }}

Framework: {{ favoriteFramework }}

      Favorite Framework:
      <input id="framework" type="text" [(ngModel)]="favoriteFramework" />

    <button (click)="showFramework()">Show Framework</button>
  `,
  standalone: true,
  imports: [FormsModule],
})
export class UserComponent {
  favoriteFramework = '';
  username = 'youngTech';

  showFramework() {
    alert(this.favoriteFramework);
  }
}


import {Component} from '@angular/core';
import {FormsModule} from '@angular/forms';

@Component({
  selector: 'app-user',
  template: `

Username: {{ username }}

Framework:

      Favorite Framework:
      <input id="framework" type="text" [(ngModel)]="favoriteFramework" />

    <button (click)="showFramework()">Show Framework</button>
  `,
  standalone: true,
  imports: [FormsModule],
})
export class UserComponent {
  favoriteFramework = '';
  username = 'youngTech';

  showFramework() {}
}


# Forms Overview

Forms are a big part of many apps because they enable your app to accept user input. Let's learn about how forms are handled in Angular.

In Angular, there are two types of forms: template-driven and reactive. You'll learn about both over the next few activities.

In this activity, you'll learn how to setup a form using a template-driven approach.

<hr>

<docs-workflow>

<docs-step title="Create an input field">

In `user.component.ts`, update the template by adding a text input with the `id` set to `framework`, type set to `text`.

```angular-html
<label for="framework">
  Favorite Framework:
  <input id="framework" type="text" />
</label>
```

</docs-step>

<docs-step title="Import `FormsModule`">

For this form to use Angular features that enable data binding to forms, you'll need to import the `FormsModule`.

Import the `FormsModule` from `@angular/forms` and add it to the `imports` array of the `UserComponent`.

<docs-code language="ts" highlight="[2, 7]">
import {Component} from '@angular/core';
import {FormsModule} from '@angular/forms';

@Component({
  ...
  standalone: true,
  imports: [FormsModule],
})
export class UserComponent {}
</docs-code>

</docs-step>

<docs-step title="Add binding to the value of the input">

The `FormsModule` has a directive called `ngModel` that binds the value of the input to a property in your class.

Update the input to use the `ngModel` directive, specifically with the following syntax `[(ngModel)]="favoriteFramework"` to bind to the `favoriteFramework` property.

<docs-code language="html" highlight="[3]">
<label for="framework">
  Favorite Framework:
  <input id="framework" type="text" [(ngModel)]="favoriteFramework" />
</label>
</docs-code>

After you've made changes, try entering a value in the input field. Notice how it updates on the screen (yes, very cool).

Note: The syntax `[()]` is known as "banana in a box" but it represents two-way binding: property binding and event binding. Learn more in the [Angular docs about two-way data binding](guide/templates/two-way-binding).

</docs-step>

</docs-workflow>

You've now taken an important first step towards building forms with Angular.

Nice work. Let's keep the momentum going!


import {Component} from '@angular/core';
import {FormsModule} from '@angular/forms';

@Component({
  selector: 'app-user',
  template: `

Username: {{ username }}

{{ username }}'s favorite framework: {{ favoriteFramework }}

      Favorite Framework:
      <input id="framework" type="text" [(ngModel)]="favoriteFramework" />

  `,
  standalone: true,
  imports: [FormsModule],
})
export class UserComponent {
  favoriteFramework = '';
  username = 'youngTech';
}


# Constructor-based dependency injection

In previous activities you used the `inject()` function to make resources available, "providing" them to your components. The `inject()` function is one pattern and it is useful to know that there is another pattern for injecting resources called constructor-based dependency injection.

You specify the resources as parameters to the `constructor` function of a component. Angular will make those resources available to your component.

In this activity, you will learn how to use constructor-based dependency injection.

To inject a service or some other injectable resource into your component use the following syntax:

<docs-code language="ts" highlight="[3]">
@Component({...})
class PetCarDashboardComponent {
    constructor(private petCareService: PetCareService) {
        ...
    }
}
</docs-code>

There are a few things to notice here:

- Use the `private` keyword
- The `petCareService` becomes a property you can use in your class
- The `PetCareService` class is the injected class

Alright, now you give this a try:

<docs-workflow>

<docs-step title="Update the code to use constructor-based DI">

In `app.component.ts`, update the constructor code to match the code below:

Tip: Remember, if you get stuck refer to the example on this activity page.

```ts
constructor(private carService: CarService) {
    this.display = this.carService.getCars().join(' ⭐ ');
}
```

</docs-step>

</docs-workflow>

Congratulations on completing this activity. The example code works the same as with using the `inject` function. While these two approaches are largely the same, there are some small differences that are beyond the scope of this tutorial.

You can find out more information about dependency injection in the [Angular Documentation](guide/di).


# Defer triggers

While the default options for `@defer` offer great options for lazy loading parts of your components it may still be desirable to further customize the deferred loading experience.

By default, deferred content loads when the browser is idle. You can, however, customize when this loading occurs by specifying a **trigger**. This lets you pick the loading behavior best suited to your component.

Deferrable views offer two types of loading trigger:

      on

        A trigger condition using a trigger from the list of built-in triggers.

        For example: @defer (on viewport)

      when

        A condition as an expression which is evaluated for truthiness. When the expression is truthy, the placeholder is swapped with the lazily loaded content.

        For example: @defer (when customizedCondition)

If the `when` condition evaluates to `false`, the `defer` block is not reverted back to the placeholder. The swap is a one-time operation.

You can define multiple event triggers at once, these triggers will be evaluated as OR conditions.

* Ex: `@defer (on viewport; on timer(2s))`
* Ex: `@defer (on viewport; when customizedCondition)`

In this activity, you'll learn how to use triggers to specify the condition to load the deferrable views.

<docs-workflow>

<docs-step title="Add `on hover` trigger">
In your `app.component.ts`,  add an `on hover` trigger to the `@defer` block.

<docs-code language="angular-html" hightlight="[1]">
@defer (on hover) {

} @placeholder (minimum 1s) {

Placeholder for comments

} @loading (minimum 1s; after 500ms) {

Loading comments...

} @error {

Failed to load comments

}
</docs-code>

Now, the page will not render the comments section until you hover its placeholder.
</docs-step>

<docs-step title="Add a 'Show all comments' button">
Next, update the template to include a button with the label "Show all comments". Include a template variable called `#showComments` with the button.

<docs-code language="angular-html" hightlight="[1]">
<button type="button" #showComments>Show all comments</button>

@defer (on hover) {

} @placeholder (minimum 1s) {

Placeholder for comments

} @loading (minimum 1s; after 500ms) {

Loading comments...

} @error {

Failed to load comments

}
</docs-code>

Note: for more information on [template variables check the documentation](https://angular.dev/guide/templates/reference-variables#).

</docs-step>

<docs-step title="Add `on interaction` trigger">
Update the `@defer` block in the template to use the `on interaction` trigger. Provide the `showComments` template variable as the parameter to `interaction`.

<docs-code language="angular-html" hightlight="[3]">
<button type="button" #showComments>Show all comments</button>

@defer (on hover; on interaction(showComments)) {

} @placeholder (minimum 1s) {

Placeholder for comments

} @loading (minimum 1s; after 500ms) {

Loading comments...

} @error {

Failed to load comments

}
</docs-code>

With these changes, the page will wait for one of the following conditions before rendering the comments section:
* User hovers the comments section’s placeholder
* User clicks on the “Show all comments" button

You can reload the page to try out different triggers to render the comments section.
</docs-step>
</docs-workflow>

If you would like to learn more, check out the documentation for [Deferrable View](https://angular.dev/guide/defer).
Keep learning to unlock more of Angular's great features.