All notes

PR 10849 review-grok-4-7

A public marketing page loads the layouts it shows. The header stays in the template so the welcome video can still project into it.

Reviewed 25 September 20263 files385 added, 345 removed

PR 10849 · Base 33fcd8d508 · Head 3645b6d5

Should I merge it now? Yes.

The public template and the new loader agree.

I compared every ngSwitchCase in the old preview with MARKETING_PAGE_LAYOUT_LOADERS and getMarketingPageLayoutInputs. The header is the one layout that stays a real template. The other 53 cases from that switch are loaders, and each extra input is the same expression as before. The add-section picker still reads the layout constants. This commit does not touch it.

I would merge 3645b6d5. The size numbers in the pull request description are the author's. I did not rebuild.

The inputs match the old switch

getMarketingPageLayoutInputs

  • Every lazy layout still gets data.
  • The 14 editing layouts still get isEditingMode from cmsPreviewMode.
  • Pinned horizontal scroll still gets disablePinnedScroll on the mobile preview viewport.
  • Vertical card carousel, comparison, and the comparison full table still get forceMobileView on that same viewport.
  • Vertical card snap scroll still gets isEditingMode from the mobile viewport, which is what the old template did.
  • Footer still gets syncWhitelabelLogo and previewBranding.

I checked those input names against each component class. Every one is an @Input on the class the loader returns. All 53 loader files export the class they name.

The header bindings are copied onto app-header-layout, including #headerLayout, the user menu template, and the welcome video modal. That is why the header is in STATIC_MARKETING_PAGE_LAYOUT_IDS and is not a dynamic import. ngComponentOutlet cannot project those two pieces of content.

LAYOUT_IDS has 55 keys. PRO_CONTACT_FORM_LAYOUT was already missing from the old switch, so a page that uses it still renders an empty slot. This pull request does not change that.

How a page loads layouts

loadVisibleLayoutComponents

Visible section ids are deduped. The header is skipped. Ids that already have a class are skipped. The rest import together. PendingTasks stays open until that batch finishes, including when the import throws, so server rendering does not wait forever. After a successful batch the classes are stored and markForCheck runs. The preview is OnPush.

A newer resolve, or ngOnDestroy, bumps layoutLoadGeneration. A stale batch does not write classes. Viewport, branding, and preview-mode changes rebuild layoutInputs without importing again. NgComponentOutlet applies those inputs on the existing component.

Angular's ViewContainerRef.createComponent looks up a server-rendered view by component id. The pending task is what keeps the app unstable until the class is assigned and change detection is scheduled, which is the window that lookup needs. I did not watch a browser for a hydration error.

One sharp edge. The batch is Promise.all. If one chunk rejects, none of that batch is stored, and the void call leaves an unhandled rejection. A missing chunk should fail one section. I would not hold the merge for it.

What I did not run

I did not build TopFans, compare SSR titles or app-*-layout counts, or open /, /athletes, /music-artists, /topfan-vs-patreon, or /creators-home. The test plan on the pull request is still unchecked. GitHub reports the pull request open and mergeable, with no status checks.

The useful check after merge is the one in that plan. Compare the server HTML for those routes, confirm unused layouts are absent, and confirm the header menu, welcome video, and editor picker still behave, with no hydration error in the console.

Review references