Repository: angular
Issue: Components created via ComponentRef do not report errors to ErrorHandler in some cases
Labels: area: core,core: dynamic view creation,P3

### Which @angular/* package(s) are the source of the bug?

core

### Is this a regression?

No

### Description

Currently, the `RootViewRef` class is used to keep track of the underlying view for components created via `ComponentRef`, [see here](https://github.com/angular/angular/blob/9a623c03b27e35f23d4834188880a9d669020e30/packages/core/src/render3/component_ref.ts#L279). When the view is change-detected, the following function is used:
https://github.com/angular/angular/blob/9a623c03b27e35f23d4834188880a9d669020e30/packages/core/src/render3/instructions/shared.ts#L483-L505
which is similar to the `detectChangesInternal` used elsewhere (for non-root views):
https://github.com/angular/angular/blob/9a623c03b27e35f23d4834188880a9d669020e30/packages/core/src/render3/instructions/shared.ts#L1806-L1817
The mentioned functions are very similar, but they have one big difference: the function for non-root views reports errors to the ErrorHandler, when a root view one does not.

This is not an issue in most cases, since there are other points at which the error is intercepted and reports. For example, this function is used during the bootstrap: https://github.com/angular/angular/blob/9a623c03b27e35f23d4834188880a9d669020e30/packages/core/src/application_ref.ts#L594.
and also when an error happens in a listener:
https://github.com/angular/angular/blob/9a623c03b27e35f23d4834188880a9d669020e30/packages/core/src/render3/instructions/listener.ts#L220

However when an APIs like `createComponent` (and I suspect `ViewContainerRef.createComponent`) are used after the bootstrap and outside of listeners, errors would not be reported to the `ErrorHandler`. Here is a TestBed test to illustrate this:
```
   fit('should report errors to the ErrorHandler', () => {
      @Component({
        standalone: true,
        template: '...',
      })
      class StandaloneComponent {
        ngOnInit() {
          throw new Error('!');
        }
      }

      let errorHandlerInvoked = false;
      const hostElement = document.createElement('div');
      const parentInjector = TestBed.inject(EnvironmentInjector);
      const errorHandler = {
        handleError: () => {
          errorHandlerInvoked = true;
        }
      };
      const providers = [
        {provide: ErrorHandler, useValue: errorHandler},
      ];
      const environmentInjector = createEnvironmentInjector(providers, parentInjector);
      const componentRef = createComponent(StandaloneComponent, {hostElement, environmentInjector});
      expect(() => {
        componentRef.changeDetectorRef.detectChanges();
      }).toThrow();

      // This assert fails (ErrorHandler is never invoked):
      expect(errorHandlerInvoked).toBe(true);
    });
```
It's easy to add a logic to report errors:
```
--- a/packages/core/src/render3/instructions/shared.ts
+++ b/packages/core/src/render3/instructions/shared.ts
@@ -497,6 +497,9 @@ export function renderComponentOrTemplate<T>(
       renderView(tView, lView, context);
     }
     refreshView(tView, lView, templateFn, context);
+  } catch (error) {
+    handleError(lView, error);
+    throw error;
   } finally {
     if (!checkNoChangesMode && !creationModeIsActive && rendererFactory.end) {
       rendererFactory.end();
```
However this would have consequences for the mentioned cases (bootstrapping, event listeners invocation) and would result in reporting an error to the ErrorHandler twice. As a possible solution, we can probably mark (via monkey-patching) errors that we've reported and skip reporting it the second time.

Another API that would likely be affected: `TestBed.createComponent`.