88
99import {
1010 Component ,
11+ ElementRef ,
1112 EnvironmentInjector ,
1213 ErrorHandler ,
1314 Input ,
@@ -16,6 +17,7 @@ import {
1617 ViewContainerRef ,
1718 effect ,
1819 signal ,
20+ viewChild ,
1921} from '@angular/core' ;
2022import { DeferBlockBehavior , DeferBlockState , TestBed } from '@angular/core/testing' ;
2123import { ErrorBoundaryWrappedError , ErrorDetails } from '../../src/error_handler' ;
@@ -312,12 +314,12 @@ describe('Error Boundary Runtime Interception', () => {
312314 @boundary {
313315 <child-cmp-defer></child-cmp-defer>
314316 } @error (let err) {
315- <div id="fallback">Fallback: {{err.message}}</div>
317+ <div id="fallback">Fallback: {{ err.message }}</div>
316318 }
317319 } @loading {
318320 <div id="loading">Loading...</div>
319321 }
320- ` ,
322+ ` ,
321323 imports : [ ChildCmpDefer ] ,
322324 } )
323325 class AppDeferTest {
@@ -517,7 +519,7 @@ describe('Error Boundary Runtime Interception', () => {
517519 @boundary {
518520 <ul>
519521 @for (item of items(); track trackFn(item)) {
520- <li>{{item}}</li>
522+ <li>{{ item }}</li>
521523 }
522524 </ul>
523525 } @error {
@@ -581,7 +583,7 @@ describe('@boundary runtime instructions (JIT)', () => {
581583 <throwing-ctor></throwing-ctor>
582584 Main Content
583585 } @error (let err) {
584- Error: {{err.message}}
586+ Error: {{ err.message }}
585587 }
586588 ` ,
587589 imports : [ ThrowingCtor ] ,
@@ -602,7 +604,7 @@ describe('@boundary runtime instructions (JIT)', () => {
602604 <throwing-hook [shouldThrow]="triggerError()"></throwing-hook>
603605 Main Content
604606 } @error (let err) {
605- Error: {{err.message}}
607+ Error: {{ err.message }}
606608 }
607609 ` ,
608610 imports : [ ThrowingHook ] ,
@@ -629,7 +631,7 @@ describe('@boundary runtime instructions (JIT)', () => {
629631 {{ throwInBinding() }}
630632 Main Content
631633 } @error (let err) {
632- Error: {{err.message}}
634+ Error: {{ err.message }}
633635 }
634636 ` ,
635637 } )
@@ -662,7 +664,7 @@ describe('@boundary runtime instructions (JIT)', () => {
662664 }
663665 Main Content
664666 } @error (let err) {
665- Error: {{err.message}}
667+ Error: {{ err.message }}
666668 }
667669 ` ,
668670 imports : [ ThrowingCtor ] ,
@@ -787,7 +789,7 @@ describe('@boundary runtime instructions (JIT)', () => {
787789 Main Content
788790 }
789791 } @error (let err, r = $reset) {
790- Error: {{err.message}}
792+ Error: {{ err.message }}
791793 <button (click)="r()">Reset</button>
792794 }
793795 ` ,
@@ -822,7 +824,7 @@ describe('@boundary runtime instructions (JIT)', () => {
822824 @boundary {
823825 {{ throwError() }}
824826 } @error (let err) {
825- Error: {{err.message}}
827+ Error: {{ err.message }}
826828 <button (click)="$reset()">Reset</button>
827829 }
828830 ` ,
@@ -866,7 +868,7 @@ describe('@boundary runtime instructions (JIT)', () => {
866868 Main Content
867869 }
868870 } @error {
869- Error: {{$error.message}}
871+ Error: {{ $error.message }}
870872 }
871873 ` ,
872874 } )
@@ -893,7 +895,7 @@ describe('@boundary runtime instructions (JIT)', () => {
893895 Main Content
894896 }
895897 } @error (let err, r = $reset) {
896- Error: {{err.message}}
898+ Error: {{ err.message }}
897899 <button (click)="handleReset(r)">Reset Later</button>
898900 }
899901 ` ,
@@ -944,10 +946,10 @@ describe('@boundary runtime instructions (JIT)', () => {
944946 Main Content
945947 }
946948 } @error (let err; r = $reset; when isChartError(err)) {
947- Chart Error: {{err.message}}
949+ Chart Error: {{ err.message }}
948950 <button id="reset-chart" (click)="r()">Reset</button>
949951 } @error (let error; r = $reset) {
950- Generic Error: {{error.message}}
952+ Generic Error: {{ error.message }}
951953 <button id="reset-generic" (click)="r()">Reset</button>
952954 }
953955 ` ,
@@ -1002,7 +1004,7 @@ describe('@boundary runtime instructions (JIT)', () => {
10021004 {{ throwError() }}
10031005 }
10041006 } @error (let err; when isChartError(err)) {
1005- Chart Error: {{err.message}}
1007+ Chart Error: {{ err.message }}
10061008 }
10071009 ` ,
10081010 } )
@@ -1039,10 +1041,10 @@ describe('@boundary runtime instructions (JIT)', () => {
10391041 @Component ( {
10401042 template : `
10411043 @boundary {
1042- <throwing-effect/>
1044+ <throwing-effect />
10431045 Main Content
10441046 } @error (let err) {
1045- Error: {{err.message}}
1047+ Error: {{ err.message }}
10461048 }
10471049 ` ,
10481050 imports : [ ThrowingEffect ] ,
@@ -1103,4 +1105,54 @@ describe('@boundary runtime instructions (JIT)', () => {
11031105 expect ( capturedDetails . boundary ! . type ) . toBe ( Host ) ;
11041106 expect ( typeof capturedDetails . boundary ! . reset ) . toBe ( 'function' ) ;
11051107 } ) ;
1108+
1109+ it ( 'should allow signal writes in ErrorHandler and view queries when a view inside @boundary throws' , async ( ) => {
1110+ const errors = signal < unknown [ ] > ( [ ] ) ;
1111+
1112+ class SignalWritingErrorHandler implements ErrorHandler {
1113+ handleError ( error : unknown ) {
1114+ errors . update ( ( list ) => [ ...list , error ] ) ;
1115+ }
1116+ }
1117+
1118+ @Component ( {
1119+ selector : 'app-throws' ,
1120+ template : '{{ boom() }}' ,
1121+ } )
1122+ class Throws {
1123+ boom ( ) : string {
1124+ throw new Error ( 'original' ) ;
1125+ }
1126+ }
1127+
1128+ @Component ( {
1129+ template : `
1130+ @boundary {
1131+ @if (true) {
1132+ <app-throws />
1133+ }
1134+ } @error {
1135+ <p>{{ $error.message }}</p>
1136+ }
1137+ <div #anchor></div>
1138+ ` ,
1139+ imports : [ Throws ] ,
1140+ } )
1141+ class App {
1142+ anchor = viewChild < ElementRef > ( 'anchor' ) ;
1143+ }
1144+
1145+ TestBed . configureTestingModule ( {
1146+ providers : [ { provide : ErrorHandler , useClass : SignalWritingErrorHandler } ] ,
1147+ } ) ;
1148+
1149+ const fixture = TestBed . createComponent ( App ) ;
1150+ await fixture . whenStable ( ) ;
1151+
1152+ // The error should have been caught and the @error block should be rendered.
1153+ expect ( fixture . nativeElement . textContent ) . toContain ( 'original' ) ;
1154+ // The signal writing ErrorHandler should have successfully written to the signal
1155+ expect ( errors ( ) . length ) . toBe ( 1 ) ;
1156+ expect ( ( errors ( ) [ 0 ] as Error ) . message ) . toBe ( 'original' ) ;
1157+ } ) ;
11061158} ) ;
0 commit comments