1
- import { Component , input , output , inputBinding , outputBinding } from '@angular/core' ;
1
+ import { Component , input , output , inputBinding , outputBinding , twoWayBinding , signal , model } from '@angular/core' ;
2
2
import { render , screen , aliasedInput } from '../src/public_api' ;
3
3
4
4
describe ( 'ATL Bindings API Support' , ( ) => {
@@ -17,6 +17,23 @@ describe('ATL Bindings API Support', () => {
17
17
clicked = output < string > ( ) ;
18
18
}
19
19
20
+ @Component ( {
21
+ selector : 'atl-two-way-test' ,
22
+ template : `
23
+ <div data-testid="name-display">{{ name() }}</div>
24
+ <input data-testid="name-input" [value]="name()" (input)="name.set($any($event.target).value)" />
25
+ <button data-testid="update-button" (click)="updateName()">Update</button>
26
+ ` ,
27
+ standalone : true ,
28
+ } )
29
+ class TwoWayBindingTestComponent {
30
+ name = model < string > ( 'default' ) ;
31
+
32
+ updateName ( ) {
33
+ this . name . set ( 'updated from component' ) ;
34
+ }
35
+ }
36
+
20
37
it ( 'should support inputBinding for regular inputs' , async ( ) => {
21
38
await render ( BindingsTestComponent , {
22
39
bindings : [ inputBinding ( 'value' , ( ) => 'test-value' ) , inputBinding ( 'greet' , ( ) => 'hi there' ) ] ,
@@ -39,6 +56,49 @@ describe('ATL Bindings API Support', () => {
39
56
expect ( clickHandler ) . toHaveBeenCalledWith ( 'clicked: bound-value' ) ;
40
57
} ) ;
41
58
59
+ it ( 'should support inputBinding with writable signal for re-rendering scenario' , async ( ) => {
60
+ const valueSignal = signal ( 'initial-value' ) ;
61
+
62
+ await render ( BindingsTestComponent , {
63
+ bindings : [ inputBinding ( 'value' , valueSignal ) , inputBinding ( 'greet' , ( ) => 'hi there' ) ] ,
64
+ } ) ;
65
+
66
+ expect ( screen . getByTestId ( 'value' ) ) . toHaveTextContent ( 'initial-value' ) ;
67
+ expect ( screen . getByTestId ( 'greeting' ) ) . toHaveTextContent ( 'hi there' ) ;
68
+
69
+ // Update the signal and verify it reflects in the component
70
+ valueSignal . set ( 'updated-value' ) ;
71
+
72
+ // The binding should automatically update the component
73
+ expect ( await screen . findByText ( 'updated-value' ) ) . toBeInTheDocument ( ) ;
74
+ } ) ;
75
+
76
+ it ( 'should support twoWayBinding for model signals' , async ( ) => {
77
+ const nameSignal = signal ( 'initial name' ) ;
78
+
79
+ await render ( TwoWayBindingTestComponent , {
80
+ bindings : [ twoWayBinding ( 'name' , nameSignal ) ] ,
81
+ } ) ;
82
+
83
+ // Verify initial value
84
+ expect ( screen . getByTestId ( 'name-display' ) ) . toHaveTextContent ( 'initial name' ) ;
85
+ expect ( screen . getByTestId ( 'name-input' ) ) . toHaveValue ( 'initial name' ) ;
86
+
87
+ // Update from outside (signal change)
88
+ nameSignal . set ( 'updated from signal' ) ;
89
+ expect ( await screen . findByDisplayValue ( 'updated from signal' ) ) . toBeInTheDocument ( ) ;
90
+ expect ( screen . getByTestId ( 'name-display' ) ) . toHaveTextContent ( 'updated from signal' ) ;
91
+
92
+ // Update from component - let's trigger change detection after the click
93
+ const updateButton = screen . getByTestId ( 'update-button' ) ;
94
+ updateButton . click ( ) ;
95
+
96
+ // Give Angular a chance to process the update and check both the signal and display
97
+ // The twoWayBinding should update the external signal
98
+ expect ( await screen . findByText ( 'updated from component' ) ) . toBeInTheDocument ( ) ;
99
+ expect ( nameSignal ( ) ) . toBe ( 'updated from component' ) ;
100
+ } ) ;
101
+
42
102
it ( 'should warn when mixing bindings with traditional inputs but still work' , async ( ) => {
43
103
const consoleSpy = jest . spyOn ( console , 'warn' ) . mockImplementation ( ) ;
44
104
const clickHandler = jest . fn ( ) ;
0 commit comments