<html lang="en">
<head></head>
<body>

<form id="mainForm" method="post" action="https://stackblitz.com/run" target="_self">
<input type="hidden" name="project[files][README.md]" value="# Angular async debounced callback

This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 21.0.5.

## Development server

To start a local development server, run:

```bash
ng serve
```

Once the server is running, open your browser and navigate to `http://localhost:4200/`. The application will automatically reload whenever you modify any of the source files.

## Code scaffolding

Angular CLI includes powerful code scaffolding tools. To generate a new component, run:

```bash
ng generate component component-name
```

For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run:

```bash
ng generate --help
```

## Building

To build the project run:

```bash
ng build
```

This will compile your project and store the build artifacts in the `dist/` directory. By default, the production build optimizes your application for performance and speed.

## Running unit tests

To execute unit tests with the [Vitest](https://vitest.dev/) test runner, use the following command:

```bash
ng test
```

## Running end-to-end tests

For end-to-end (e2e) testing, run:

```bash
ng e2e
```

Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.

## Additional Resources

For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.
Use `injectAsyncDebouncer` and its `maybeExecute` method to create the event handler. The adapter owns reactive options and lifecycle cleanup.
">
<input type="hidden" name="project[files][angular.json]" value="{
  &quot;$schema&quot;: &quot;./node_modules/@angular/cli/lib/config/schema.json&quot;,
  &quot;version&quot;: 1,
  &quot;cli&quot;: {
    &quot;packageManager&quot;: &quot;npm&quot;,
    &quot;analytics&quot;: false
  },
  &quot;newProjectRoot&quot;: &quot;projects&quot;,
  &quot;projects&quot;: {
    &quot;basic&quot;: {
      &quot;projectType&quot;: &quot;application&quot;,
      &quot;schematics&quot;: {},
      &quot;root&quot;: &quot;&quot;,
      &quot;sourceRoot&quot;: &quot;src&quot;,
      &quot;prefix&quot;: &quot;app&quot;,
      &quot;architect&quot;: {
        &quot;build&quot;: {
          &quot;builder&quot;: &quot;@angular/build:application&quot;,
          &quot;options&quot;: {
            &quot;browser&quot;: &quot;src/main.ts&quot;,
            &quot;tsConfig&quot;: &quot;tsconfig.app.json&quot;,
            &quot;assets&quot;: [
              {
                &quot;glob&quot;: &quot;**/*&quot;,
                &quot;input&quot;: &quot;public&quot;
              }
            ],
            &quot;styles&quot;: [&quot;src/styles.css&quot;]
          },
          &quot;configurations&quot;: {
            &quot;production&quot;: {
              &quot;budgets&quot;: [
                {
                  &quot;type&quot;: &quot;initial&quot;,
                  &quot;maximumWarning&quot;: &quot;500kB&quot;,
                  &quot;maximumError&quot;: &quot;1MB&quot;
                },
                {
                  &quot;type&quot;: &quot;anyComponentStyle&quot;,
                  &quot;maximumWarning&quot;: &quot;4kB&quot;,
                  &quot;maximumError&quot;: &quot;8kB&quot;
                }
              ],
              &quot;outputHashing&quot;: &quot;all&quot;
            },
            &quot;development&quot;: {
              &quot;optimization&quot;: false,
              &quot;extractLicenses&quot;: false,
              &quot;sourceMap&quot;: true
            }
          },
          &quot;defaultConfiguration&quot;: &quot;production&quot;
        },
        &quot;serve&quot;: {
          &quot;builder&quot;: &quot;@angular/build:dev-server&quot;,
          &quot;configurations&quot;: {
            &quot;production&quot;: {
              &quot;buildTarget&quot;: &quot;basic:build:production&quot;
            },
            &quot;development&quot;: {
              &quot;buildTarget&quot;: &quot;basic:build:development&quot;
            }
          },
          &quot;defaultConfiguration&quot;: &quot;development&quot;
        },
        &quot;test&quot;: {
          &quot;builder&quot;: &quot;@angular/build:unit-test&quot;
        }
      }
    }
  }
}
">
<input type="hidden" name="project[files][eslint.config.js]" value="// @ts-check

import rootConfig from &#39;../../../eslint.config.js&#39;

/** @type {import(&#39;eslint&#39;).Linter.Config[]} */
export default [
  ...rootConfig,
  {
    languageOptions: {
      parserOptions: {
        project: &#39;./tsconfig.app.json&#39;,
      },
    },
  },
]
">
<input type="hidden" name="project[files][package.json]" value="{
  &quot;name&quot;: &quot;@tanstack/pacer-example-angular-async-debounced-callback&quot;,
  &quot;scripts&quot;: {
    &quot;ng&quot;: &quot;ng&quot;,
    &quot;start&quot;: &quot;ng serve&quot;,
    &quot;build&quot;: &quot;ng build&quot;,
    &quot;watch&quot;: &quot;ng build --watch --configuration development&quot;,
    &quot;test&quot;: &quot;ng test&quot;,
    &quot;test:e2e&quot;: &quot;PLAYWRIGHT_TEST_DIR=$PWD/tests/e2e playwright test --config ../../../playwright.config.ts&quot;
  },
  &quot;prettier&quot;: {
    &quot;printWidth&quot;: 100,
    &quot;singleQuote&quot;: true,
    &quot;overrides&quot;: [
      {
        &quot;files&quot;: &quot;*.html&quot;,
        &quot;options&quot;: {
          &quot;parser&quot;: &quot;angular&quot;
        }
      }
    ]
  },
  &quot;private&quot;: true,
  &quot;packageManager&quot;: &quot;pnpm@12.6.0&quot;,
  &quot;dependencies&quot;: {
    &quot;@angular/common&quot;: &quot;^22.2.1&quot;,
    &quot;@angular/compiler&quot;: &quot;^22.2.1&quot;,
    &quot;@angular/core&quot;: &quot;^22.2.1&quot;,
    &quot;@angular/forms&quot;: &quot;^22.2.1&quot;,
    &quot;@angular/platform-browser&quot;: &quot;^22.2.1&quot;,
    &quot;@angular/router&quot;: &quot;^22.2.1&quot;,
    &quot;@tanstack/angular-devtools&quot;: &quot;^0.0.12&quot;,
    &quot;@tanstack/angular-pacer&quot;: &quot;https://pkg.pr.new/TanStack/pacer/@tanstack/angular-pacer@8ea4db2c3fd46827c6b7ce64d239d8aaecd0d8ea&quot;,
    &quot;@tanstack/angular-pacer-devtools&quot;: &quot;https://pkg.pr.new/TanStack/pacer/@tanstack/angular-pacer-devtools@8ea4db2c3fd46827c6b7ce64d239d8aaecd0d8ea&quot;,
    &quot;rxjs&quot;: &quot;~7.8.2&quot;,
    &quot;tslib&quot;: &quot;^2.8.1&quot;
  },
  &quot;devDependencies&quot;: {
    &quot;@angular/build&quot;: &quot;^22.2.1&quot;,
    &quot;@angular/cli&quot;: &quot;^22.2.1&quot;,
    &quot;@angular/compiler-cli&quot;: &quot;^22.2.1&quot;,
    &quot;jsdom&quot;: &quot;^30.1.1&quot;,
    &quot;typescript&quot;: &quot;6.0.3&quot;,
    &quot;vitest&quot;: &quot;^5.0.3&quot;
  }
}">
<input type="hidden" name="project[files][tsconfig.app.json]" value="/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
{
  &quot;extends&quot;: &quot;./tsconfig.json&quot;,
  &quot;compilerOptions&quot;: {
    &quot;outDir&quot;: &quot;./out-tsc/app&quot;,
    &quot;types&quot;: []
  },
  &quot;include&quot;: [&quot;./src/**/*.ts&quot;],
  &quot;exclude&quot;: [&quot;./src/**/*.spec.ts&quot;]
}
">
<input type="hidden" name="project[files][tsconfig.json]" value="/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
{
  &quot;compileOnSave&quot;: false,
  &quot;compilerOptions&quot;: {
    &quot;strict&quot;: true,
    &quot;noImplicitOverride&quot;: true,
    &quot;noPropertyAccessFromIndexSignature&quot;: true,
    &quot;noImplicitReturns&quot;: true,
    &quot;noFallthroughCasesInSwitch&quot;: true,
    &quot;skipLibCheck&quot;: true,
    &quot;isolatedModules&quot;: true,
    &quot;experimentalDecorators&quot;: true,
    &quot;importHelpers&quot;: true,
    &quot;target&quot;: &quot;ES2022&quot;,
    &quot;module&quot;: &quot;preserve&quot;
  },
  &quot;angularCompilerOptions&quot;: {
    &quot;enableI18nLegacyMessageIdFormat&quot;: false,
    &quot;strictInjectionParameters&quot;: true,
    &quot;strictInputAccessModifiers&quot;: true,
    &quot;strictTemplates&quot;: true
  },
  &quot;files&quot;: [],
  &quot;references&quot;: [
    {
      &quot;path&quot;: &quot;./tsconfig.app.json&quot;
    },
    {
      &quot;path&quot;: &quot;./tsconfig.spec.json&quot;
    }
  ]
}
">
<input type="hidden" name="project[files][tsconfig.spec.json]" value="/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
{
  &quot;extends&quot;: &quot;./tsconfig.json&quot;,
  &quot;compilerOptions&quot;: {
    &quot;outDir&quot;: &quot;./out-tsc/spec&quot;,
    &quot;types&quot;: [&quot;vitest/globals&quot;]
  },
  &quot;include&quot;: [&quot;src/**/*.d.ts&quot;, &quot;src/**/*.spec.ts&quot;]
}
">
<input type="hidden" name="project[files][public/favicon.ico]" value="https://pkg.pr.new/template/395b92b2-4f14-4deb-a4a0-7daeb98d00f3">
<input type="hidden" name="project[files][src/index.html]" value="&lt;!doctype html&gt;
&lt;html lang=&quot;en&quot;&gt;
  &lt;head&gt;
    &lt;meta charset=&quot;utf-8&quot; /&gt;
    &lt;title&gt;Basic&lt;/title&gt;
    &lt;base href=&quot;/&quot; /&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1&quot; /&gt;
    &lt;link rel=&quot;icon&quot; type=&quot;image/x-icon&quot; href=&quot;favicon.ico&quot; /&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;app-root&gt;&lt;/app-root&gt;
  &lt;/body&gt;
&lt;/html&gt;
">
<input type="hidden" name="project[files][src/main.ts]" value="import { bootstrapApplication } from &#39;@angular/platform-browser&#39;
import { appConfig } from &#39;./app/app.config&#39;
import { App } from &#39;./app/app&#39;

bootstrapApplication(App, appConfig).catch((err) =&gt; console.error(err))
">
<input type="hidden" name="project[files][src/styles.css]" value="/* You can add global styles to this file, and also import other style files */
">
<input type="hidden" name="project[files][src/app/app.config.ts]" value="import { isDevMode } from &#39;@angular/core&#39;
import { provideTanStackDevtools } from &#39;@tanstack/angular-devtools/provider&#39;
import { pacerDevtoolsPlugin } from &#39;@tanstack/angular-pacer-devtools&#39;
import { ApplicationConfig, provideBrowserGlobalErrorListeners } from &#39;@angular/core&#39;
import { provideRouter } from &#39;@angular/router&#39;

import { routes } from &#39;./app.routes&#39;

export const appConfig: ApplicationConfig = {
  providers: [
    ...(isDevMode() ? [provideTanStackDevtools(() =&gt; ({ plugins: [pacerDevtoolsPlugin()] }))] : []),
    provideBrowserGlobalErrorListeners(),
    provideRouter(routes),
  ],
}
">
<input type="hidden" name="project[files][src/app/app.css]" value=":host {
  display: block;
  padding: 2rem;
  max-width: 600px;
  margin: 0 auto;
}

input {
  width: 100%;
  padding: 0.5rem;
  font-size: 1rem;
  border: 1px solid #ccc;
  border-radius: 4px;
}

p {
  margin-top: 1rem;
  color: #666;
}
">
<input type="hidden" name="project[files][src/app/app.html]" value="&lt;h1&gt;TanStack Pacer injectAsyncDebouncer Example 1&lt;/h1&gt;
&lt;h2&gt;Search&lt;/h2&gt;
&lt;input
  type=&quot;search&quot;
  [value]=&quot;search()&quot;
  (input)=&quot;onSearch($any($event.target).value)&quot;
  placeholder=&quot;Type to search, or &#39;error&#39; to test failure...&quot;
/&gt;
@if (isLoading()) {
&lt;p&gt;Loading...&lt;/p&gt;
}@if (error()) {
&lt;p role=&quot;alert&quot;&gt;Error: {{ error() }}&lt;/p&gt;
}
&lt;ul&gt;
  @for (result of results(); track $index) {
  &lt;li&gt;{{ result }}&lt;/li&gt;
  }
&lt;/ul&gt;
&lt;p&gt;500ms wait, 500ms API delay.&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;TanStack Pacer injectAsyncDebouncer Example 2&lt;/h1&gt;
&lt;h2&gt;Async counter&lt;/h2&gt;
&lt;p&gt;Current Count: {{ count() }}, API Calls Made: {{ apiCalls() }}&lt;/p&gt;
&lt;button (click)=&quot;increment()&quot;&gt;Increment (debounced API call)&lt;/button&gt;
&lt;p&gt;
  UI increments immediately; the API returns one additional increment after 300ms. Calls have a
  1000ms wait.
&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;TanStack Pacer injectAsyncDebouncer Example 3&lt;/h1&gt;
&lt;h2&gt;Email validation&lt;/h2&gt;
&lt;label
  &gt;Email Address:
  &lt;input
    type=&quot;email&quot;
    [value]=&quot;email()&quot;
    (input)=&quot;onEmail($any($event.target).value)&quot;
    placeholder=&quot;Enter your email...&quot;
/&gt;&lt;/label&gt;
@if (isValidating()) {
&lt;p&gt;Validating email...&lt;/p&gt;
} @if (validation(); as result) {
&lt;p [style.color]=&quot;result.isValid ? &#39;green&#39; : &#39;red&#39;&quot;&gt;{{ result.message }}&lt;/p&gt;
}
&lt;p&gt;Email validation runs 750ms after typing stops, with a 400ms API delay.&lt;/p&gt;
">
<input type="hidden" name="project[files][src/app/app.routes.ts]" value="import type { Routes } from &#39;@angular/router&#39;

export const routes: Routes = []
">
<input type="hidden" name="project[files][src/app/app.spec.ts]" value="import { vi } from &#39;vitest&#39;
import { TestBed } from &#39;@angular/core/testing&#39;
import { App } from &#39;./app&#39;

describe(&#39;App&#39;, () =&gt; {
  beforeEach(async () =&gt; {
    await TestBed.configureTestingModule({
      imports: [App],
    }).compileComponents()
  })

  it(&#39;should create the app&#39;, () =&gt; {
    const fixture = TestBed.createComponent(App)
    const app = fixture.componentInstance
    expect(app).toBeTruthy()
  })

  it(&#39;should render title&#39;, async () =&gt; {
    const fixture = TestBed.createComponent(App)
    await fixture.whenStable()
    const compiled = fixture.nativeElement as HTMLElement
    expect(compiled.querySelector(&#39;h1&#39;)?.textContent).toContain(
      &#39;TanStack Pacer injectAsyncDebouncer Example&#39;,
    )
  })
})

// Exercise the real adapter and component with a deterministic clock.
describe(&#39;example behavior&#39;, () =&gt; {
  beforeEach(async () =&gt; {
    await TestBed.configureTestingModule({ imports: [App] }).compileComponents()
  })
  afterEach(() =&gt; {
    TestBed.resetTestingModule()
    vi.clearAllTimers()
    vi.useRealTimers()
    vi.restoreAllMocks()
  })
  it(&#39;displays simulated search failures and then recovers&#39;, async () =&gt; {
    vi.useFakeTimers({ toFake: [&#39;setTimeout&#39;, &#39;clearTimeout&#39;, &#39;Date&#39;] })
    const fixture = TestBed.createComponent(App)
    const app = fixture.componentInstance
    fixture.detectChanges()
    TestBed.tick()
    void app.onSearch(&#39;error&#39;)
    await vi.advanceTimersByTimeAsync(1600)
    expect(app.error()).toBe(&#39;Simulated API error&#39;)
    expect(app.results()).toEqual([])
    void app.onSearch(&#39;angular&#39;)
    await vi.advanceTimersByTimeAsync(1600)
    expect(app.results()).toHaveLength(3)
    expect(app.error()).toBe(&#39;&#39;)
  })
  it(&#39;validates email and clears the result when empty&#39;, async () =&gt; {
    vi.useFakeTimers({ toFake: [&#39;setTimeout&#39;, &#39;clearTimeout&#39;, &#39;Date&#39;] })
    const fixture = TestBed.createComponent(App)
    const app = fixture.componentInstance
    fixture.detectChanges()
    TestBed.tick()
    void app.onEmail(&#39;user@example.com&#39;)
    await vi.advanceTimersByTimeAsync(1200)
    expect(app.validation()?.isValid).toBe(true)
    void app.onEmail(&#39;&#39;)
    await vi.advanceTimersByTimeAsync(800)
    expect(app.validation()).toBeNull()
  })
})
">
<input type="hidden" name="project[files][src/app/app.ts]" value="import { Component, signal } from &#39;@angular/core&#39;
import { injectAsyncDebouncer } from &#39;@tanstack/angular-pacer&#39;

@Component({ selector: &#39;app-root&#39;, templateUrl: &#39;./app.html&#39; })
export class App {
  readonly search = signal(&#39;&#39;)
  readonly results = signal&lt;Array&lt;string&gt;&gt;([])
  readonly isLoading = signal(false)
  readonly error = signal(&#39;&#39;)
  readonly count = signal(0)
  readonly apiCalls = signal(0)
  private readonly searchCallback = injectAsyncDebouncer(
    async (query: string) =&gt; {
      if (!query.trim()) {
        this.results.set([])
        return
      }
      this.isLoading.set(true)
      this.error.set(&#39;&#39;)
      try {
        await new Promise((resolve) =&gt; setTimeout(resolve, 500))
        if (query === &#39;error&#39;) throw new Error(&#39;Simulated API error&#39;)
        this.results.set([1, 2, 3].map((id) =&gt; `${query} result ${id}`))
      } finally {
        this.isLoading.set(false)
      }
    },
    {
      wait: 500,
      throwOnError: false,
      onError: (error) =&gt; {
        this.error.set(error.message)
        this.results.set([])
      },
    },
  )
  private readonly incrementCallback = injectAsyncDebouncer(
    async (value: number) =&gt; {
      await new Promise((resolve) =&gt; setTimeout(resolve, 300))
      this.apiCalls.update((count) =&gt; count + 1)
      this.count.set(value + 1)
      return value + 1
    },
    { wait: 1000, leading: false, trailing: true },
  )
  async onSearch(value: string): Promise&lt;void&gt; {
    this.search.set(value)
    await this.searchCallback.maybeExecute(value)
  }
  async increment(): Promise&lt;void&gt; {
    const value = this.count() + 1
    this.count.set(value)
    await this.incrementCallback.maybeExecute(value)
  }
  readonly email = signal(&#39;&#39;)
  readonly validation = signal&lt;{ isValid: boolean; message: string } | null&gt;(null)
  readonly isValidating = signal(false)
  private readonly validate = injectAsyncDebouncer(
    async (email: string) =&gt; {
      if (!email.trim()) {
        this.validation.set(null)
        return
      }
      this.isValidating.set(true)
      try {
        await new Promise((resolve) =&gt; setTimeout(resolve, 400))
        const isValid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)
        this.validation.set({
          isValid,
          message: isValid ? &#39;Email is valid!&#39; : &#39;Please enter a valid email address&#39;,
        })
      } finally {
        this.isValidating.set(false)
      }
    },
    { wait: 750, leading: false },
  )
  async onEmail(value: string): Promise&lt;void&gt; {
    this.email.set(value)
    await this.validate.maybeExecute(value)
  }
}
">
<input type="hidden" name="project[files][tests/e2e/smoke.spec.mts]" value="import { expect, test } from &#39;../../../../../tests/e2e/helpers/fixtures&#39;

test.beforeEach(async ({ page, exampleUrl }) =&gt; {
  await page.clock.install()
  await page.goto(exampleUrl)
  await expect(
    page.getByRole(&#39;heading&#39;, {
      name: &#39;TanStack Pacer injectAsyncDebouncer Example 1&#39;,
      exact: true,
    }),
  ).toBeVisible()
  // Let Angular bootstrap before pausing timers; runFor also advances render frames.
  await page.clock.pauseAt(await page.evaluate(() =&gt; Date.now() + 100))
})

test(&#39;renders all three asynchronous callback demos&#39;, async ({ page }) =&gt; {
  await expect(page.getByRole(&#39;heading&#39;, { level: 1 })).toHaveCount(3)
  await expect(page.getByRole(&#39;searchbox&#39;)).toBeVisible()
  await expect(page.getByRole(&#39;heading&#39;, { name: &#39;Async counter&#39;, exact: true })).toBeVisible()
})

test(&#39;shows successful results and handles a simulated search error&#39;, async ({ page }) =&gt; {
  const input = page.getByRole(&#39;searchbox&#39;)
  await input.fill(&#39;pacer&#39;)
  await page.clock.runFor(1050)
  await expect(page.getByRole(&#39;listitem&#39;)).toHaveText([
    &#39;pacer result 1&#39;,
    &#39;pacer result 2&#39;,
    &#39;pacer result 3&#39;,
  ])
  await page.clock.runFor(1000)
  await input.fill(&#39;error&#39;)
  await page.clock.runFor(1050)
  await expect(page.getByRole(&#39;alert&#39;)).toHaveText(&#39;Error: Simulated API error&#39;)
  await expect(page.getByRole(&#39;listitem&#39;)).toHaveCount(0)
  await expect(page.getByText(&#39;Loading...&#39;, { exact: true })).toHaveCount(0)
})
">
<input type="hidden" name="project[description]" value="generated by https://pkg.pr.new">
<input type="hidden" name="project[template]" value="node">
<input type="hidden" name="project[title]" value="@tanstack/pacer-example-angular-async-debounced-callback">
</form>
<script>document.getElementById("mainForm").submit();</script>

</body></html>