Support ContentPlaceHolder inside CompositeControl templates (deferred master page composition)#2017
Support ContentPlaceHolder inside CompositeControl templates (deferred master page composition)#2017Copilot wants to merge 18 commits into
Conversation
|
I fixed some build errors. |
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. |
|
@copilot We want to ensure that the |
Done in commit 981956c. Here's what was implemented:
Early validation in Duplicate-composition guard in Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
|
@copilot Add tests for the scenarios we didn't cover:
|
Done in commit 4dfa92c. Here's what was added: Repeater scenarios (
AuthenticatedView scenario ( Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
4dfa92c to
a0f8205
Compare
b4dabda to
e8516d7
Compare
…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>
Agent-Logs-Url: https://github.com/riganti/dotvvm/sessions/7069fa2e-7910-464f-85d5-bac0a16e9d74 Co-authored-by: tomasherceg <5599524+tomasherceg@users.noreply.github.com>
…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>
Agent-Logs-Url: https://github.com/riganti/dotvvm/sessions/c846671f-e8f5-4f1d-b690-76b7f1ef8655 Co-authored-by: tomasherceg <5599524+tomasherceg@users.noreply.github.com>
Agent-Logs-Url: https://github.com/riganti/dotvvm/sessions/2b48d2fd-fe36-480d-bdc3-1a9c5b7c5012 Co-authored-by: tomasherceg <5599524+tomasherceg@users.noreply.github.com>
Co-authored-by: Standa Lukeš <github@exyi.cz>
69b6e64 to
2041b13
Compare
Co-authored-by: Standa Lukeš <github@exyi.cz>
…tps://github.com/riganti/dotvvm into copilot/support-contentplaceholders-in-templates
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. Before deferring, the master page'sControlBuilderDescriptor.ContentPlaceHolderIdsis checked — if the ID is not declared anywhere in the master page (including inside templates), an exception is thrown immediately at build time with a clear error message.ContentPlaceHolder.OnInit: AddedResolvePendingComposition()— when aContentPlaceHolderis added to the tree (including as a missed lifecycle event duringLoadorPreRender), 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). Throws a descriptive exception if the same ID is resolved a second time (e.g.ContentPlaceHolderplaced inside aRepeaterwith more than one item).DotvvmPresenter:ValidateMasterPageComposition()is called after both theLoadphase (for postback/command requests, whereRepeatercreates children inOnLoad) and after thePreRenderphase (for GET requests, whereRepeatercreates children inOnPreRender). If any deferredContentcontrols remain unmatched at that point (e.g. aRepeaterwith zero items), a descriptive exception is thrown.ControlBuilderDescriptor: AddedContentPlaceHolderIds(ImmutableArray<string>) — populated at compile time by traversing the full resolved tree, including controls nested insideCompositeControltemplate properties. Used by the view builder to validate deferred compositions before they are queued.Internal: AddedPendingMasterPageCompositionsProperty,PendingMasterPageComposition(holds theContent, itsDataContextStack, and master page file for error messages), andResolvedMasterPageCompositionIdsProperty(HashSet<string>) which tracks which placeholder IDs have already been resolved to detect duplicate instantiation.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 early build-time error is thrown.RepeaterMaster.dotmaster+ three content pages — testsContentPlaceHolderinside a server-renderedRepeater: exactly 1 item works, 0 items throws (not found), and more than 1 item throws (duplicate composition).AuthViewMaster.dotmaster+AuthViewContent.dothtml— demonstrates and tests that the sameContentPlaceHolderIDin bothAuthenticatedTemplateandNotAuthenticatedTemplateofAuthenticatedViewworks correctly because only one template is instantiated per request.LateContentPlaceHoldersTests.