From 682f5bfeedf7d909dde3cb221433b0fcd3c1fe4e Mon Sep 17 00:00:00 2001
From: Tim Deschryver <28659384+timdeschryver@users.noreply.github.com>
Date: Mon, 8 Apr 2024 17:36:40 +0200
Subject: [PATCH] docs: add example for output and model

---
 .../22-signal-inputs.component.spec.ts        | 58 +++++++++++++++++++
 .../examples/22-signal-inputs.component.ts    | 17 +++++-
 2 files changed, 72 insertions(+), 3 deletions(-)

diff --git a/apps/example-app/src/app/examples/22-signal-inputs.component.spec.ts b/apps/example-app/src/app/examples/22-signal-inputs.component.spec.ts
index f2500a2..6a55e61 100644
--- a/apps/example-app/src/app/examples/22-signal-inputs.component.spec.ts
+++ b/apps/example-app/src/app/examples/22-signal-inputs.component.spec.ts
@@ -1,15 +1,73 @@
 import { render, screen } from '@testing-library/angular';
 import { SignalInputComponent } from './22-signal-inputs.component';
+import userEvent from '@testing-library/user-event';
 
 test('works with signal inputs', async () => {
   await render(SignalInputComponent, {
     componentInputs: {
+      greeting: 'Hello',
       name: 'world',
+    },
+  });
+
+  expect(screen.getByText(/hello world/i)).toBeInTheDocument();
+});
+
+test('can update signal inputs', async () => {
+  const { fixture } = await render(SignalInputComponent, {
+    componentInputs: {
       greeting: 'Hello',
+      name: 'world',
     },
   });
 
   expect(screen.getByText(/hello world/i)).toBeInTheDocument();
+
+  fixture.componentInstance.name.set('updated');
+  // set doesn't trigger change detection within the test, findBy is needed to update the template
+  expect(await screen.findByText(/hello updated/i)).toBeInTheDocument();
+  // it's not recommended to access the model directly, but it's possible
+  expect(fixture.componentInstance.name()).toBe('updated');
+});
+
+test('output emits a value', async () => {
+  const submitFn = jest.fn();
+  await render(SignalInputComponent, {
+    componentInputs: {
+      greeting: 'Hello',
+      name: 'world',
+    },
+    componentOutputs: {
+      submit: { emit: submitFn } as any,
+    },
+  });
+
+  await userEvent.click(screen.getByRole('button'));
+
+  expect(submitFn).toHaveBeenCalledWith('world');
+});
+
+test('model update also updates the template', async () => {
+  const { fixture } = await render(SignalInputComponent, {
+    componentInputs: {
+      greeting: 'Hello',
+      name: 'initial',
+    },
+  });
+
+  expect(screen.getByText(/hello initial/i)).toBeInTheDocument();
+
+  await userEvent.clear(screen.getByRole('textbox'));
+  await userEvent.type(screen.getByRole('textbox'), 'updated');
+
+  expect(screen.getByText(/hello updated/i)).toBeInTheDocument();
+  expect(fixture.componentInstance.name()).toBe('updated');
+
+  fixture.componentInstance.name.set('new value');
+  // set doesn't trigger change detection within the test, findBy is needed to update the template
+  expect(await screen.findByText(/hello new value/i)).toBeInTheDocument();
+  // it's not recommended to access the model directly, but it's possible
+  expect(fixture.componentInstance.name()).toBe('new value');
 });
 
 test('works with signal inputs and rerenders', async () => {
diff --git a/apps/example-app/src/app/examples/22-signal-inputs.component.ts b/apps/example-app/src/app/examples/22-signal-inputs.component.ts
index 2ba1619..ae1e779 100644
--- a/apps/example-app/src/app/examples/22-signal-inputs.component.ts
+++ b/apps/example-app/src/app/examples/22-signal-inputs.component.ts
@@ -1,13 +1,24 @@
-import { Component, input } from '@angular/core';
+import { Component, input, model, output } from '@angular/core';
+import { FormsModule } from '@angular/forms';
 
 @Component({
   selector: 'app-signal-input',
-  template: ` {{ greetings() }} {{ name() }} `,
+  template: `
+    <div>{{ greetings() }} {{ name() }}</div>
+    <button (click)="submitName()">Submit</button>
+    <input type="text" [(ngModel)]="name" />
+  `,
   standalone: true,
+  imports: [FormsModule],
 })
 export class SignalInputComponent {
   greetings = input<string>('', {
     alias: 'greeting',
   });
-  name = input.required<string>();
+  name = model.required<string>();
+  submit = output<string>();
+
+  submitName() {
+    this.submit.emit(this.name());
+  }
 }