Support ContentPlaceHolder inside CompositeControl templates (deferred master page composition)#2017
Support ContentPlaceHolder inside CompositeControl templates (deferred master page composition)#2017
Conversation
…ster page composition) Agent-Logs-Url: https://github.com/riganti/dotvvm/sessions/ca479579-7532-44bd-96e4-d8638c2d5130 Co-authored-by: tomasherceg <5599524+tomasherceg@users.noreply.github.com>
Agent-Logs-Url: https://github.com/riganti/dotvvm/sessions/ca479579-7532-44bd-96e4-d8638c2d5130 Co-authored-by: tomasherceg <5599524+tomasherceg@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
|
I fixed some build errors. |
Agent-Logs-Url: https://github.com/riganti/dotvvm/sessions/7069fa2e-7910-464f-85d5-bac0a16e9d74 Co-authored-by: tomasherceg <5599524+tomasherceg@users.noreply.github.com>
Done in commit c2ec3b6. Here's what was added: Same ContentPlaceHolderID in root and nested master ( This also required a bug fix: when the same ID appears at multiple levels, the pending list contains multiple entries with the same ID. The original Default content ( Selenium tests added for both cases. |
| }); | ||
| } | ||
|
|
||
| [Fact] |
There was a problem hiding this comment.
@copilot Move these tests to a separate class (LateContentPlaceHoldersTests) to keep the current convention.
There was a problem hiding this comment.
Done in commit 84eb337. All four LateContentPlaceHolders tests have been moved to a new LateContentPlaceHoldersTests class in LateContentPlaceHoldersTests.cs, and MasterPageTests now contains only the original nested master pages test.
…rsTests class Agent-Logs-Url: https://github.com/riganti/dotvvm/sessions/d304bd6d-2bdb-4b99-97c5-89777d4241b4 Co-authored-by: tomasherceg <5599524+tomasherceg@users.noreply.github.com>
ContentPlaceHoldercouldn't be used insideCompositeControltemplates becauseGetContentsruns in theLoadphase, after master page composition (which runs duringBuildView, beforeInit).Changes
Framework
DefaultDotvvmViewBuilder: Instead of throwing immediately when aContentPlaceHolderID is not found duringPerformMasterPageComposition, unmatchedContentcontrols are deferred into a sharedList<PendingMasterPageComposition>stored on the rootDotvvmView. Works correctly across nested master page hierarchies.ContentPlaceHolder.OnInit: AddedResolvePendingComposition()— when aContentPlaceHolderis added to the tree (including as a missed lifecycle event duringLoad), it traverses ancestors to find the pending list and performs the deferred composition. UsesFindLastIndexso that when the sameContentPlaceHolderIDis reused at multiple master page levels, each placeholder correctly matches its corresponding content (outermost placeholder ↔ outermost content).DotvvmPresenter: AddedValidateMasterPageComposition()called after theLoadphase on both GET and postback paths. If anyContentcontrols remain unmatched at that point, a descriptive exception is thrown.Internal: AddedPendingMasterPageCompositionsPropertyandPendingMasterPageComposition(holds theContent, itsDataContextStack, and master page file for error messages).Usage Example
Where
TemplateContainerControlis aCompositeControlthat instantiates the template inGetContents(Load phase).Samples & Tests
LateContentPlaceHoldersfeature sample with a 3-level master page hierarchy (Root.dotmaster→Nested.dotmaster→Content.dothtml) where eachContentPlaceHolderlives inside aCompositeControltemplate.NestedSameId.dotmaster+SharedIdContent.dothtml— demonstrates and tests the case where the sameContentPlaceHolderID("RootContent") is reused in both the root and the middle master page.ContentWithDefault.dothtml— tests that when noContentis provided for aContentPlaceHolder, its own default children are rendered.MismatchedContent.dothtml— a content page with aContentwhoseContentPlaceHolderIDhas no match anywhere, verifying the post-Load error is thrown.MasterPageTests.