Skip to content

Hydration error when projecting multiple component nodes into different slots #53246

@crisbeto

Description

@crisbeto

If we have a container component with the following template:

<ng-content select="foo"></ng-content>
<ng-content select="bar"></ng-content>

And we project the following nodes into it where both foo and bar are component host nodes:

<projector-cmp>
  <foo/>
  <bar/>
  <foo/>
</projector-cmp>

Currently the framework throws an error saying that the second <foo/> node can't be hydrated, whereas if the two <foo/> nodes are right after each other, it works fine:

<!-- Works -->
<projector-cmp>
  <foo/>
  <foo/>
  <bar/>
</projector-cmp>

On the other hand, if we change the markup so it's two <bar/> nodes and one <foo/>, the framework throws an error saying that it's "Trying to claim a node, which was claimed already.":

<projector-cmp>
  <foo/>
  <bar/>
  <foo/>
</projector-cmp>

The issue can be reproduced by adding the following test to hydration_spec.ts:

fit('', async () => {
  @Component({
    standalone: true,
    selector: 'projector-cmp',
    template: `
      <ng-content select="foo"></ng-content>
      <ng-content select="bar"></ng-content>
    `,
  })
  class ProjectorCmp {
  }

  @Component({selector: 'foo', standalone: true, template: ''})
  class FooCmp {
  }

  @Component({selector: 'bar', standalone: true, template: ''})
  class BarCmp {
  }

  @Component({
    standalone: true,
    imports: [ProjectorCmp, FooCmp, BarCmp],
    selector: 'app',
    template: `
      <projector-cmp>
        <foo/>
        <bar/>
        <foo/>
      </projector-cmp>
    `,
  })
  class SimpleComponent {
  }

  const html = await ssr(SimpleComponent);
  const ssrContents = getAppContents(html);

  expect(ssrContents).toContain('<app ngh');

  resetTViewsFor(SimpleComponent, ProjectorCmp);

  const appRef = await hydrate(html, SimpleComponent, [withDebugConsole()]);
  const compRef = getComponentRef<SimpleComponent>(appRef);
  appRef.tick();

  verifyHasLog(
      appRef, 'Angular hydrated 5 component(s) and 9 node(s), 0 component(s) were skipped');

  const clientRootNode = compRef.location.nativeElement;
  verifyAllNodesClaimedForHydration(clientRootNode);
  verifyClientAndSSRContentsMatch(ssrContents, clientRootNode);
});

Metadata

Metadata

Assignees

Labels

P2The issue is important to a large percentage of users, with a workaroundarea: coreIssues related to the framework runtimecore: hydrationstate: has PR

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions