Repository navigation
Closed
Labels
breaking changeseffort2: daysrefactoringIssue that involves refactoring or code-cleanupIssue that involves refactoring or code-cleanup
Milestone
Description
Overview
The current shadow dom emulation also contains content reprojection, but has the following limiations:
- it is not very performant, as it tries to stay relatively close to the ShadowDOM spec (see e.g. Performance regression for tables that use shadow dom emulation #2298).
- it does not allow moving the projected content programmatically somewhere else than the shadow dom of the component that contains the
<content>tag (see Renderer: Is it possible to get a reference to transcluded content? #2320).
The main idea is:
- attach component views already during compilation
- reproject content also already during compilation
With this we have
- performance gain as everything happens during compilation, which might be executed in a build step
- reprojected nodes can be moved via directives out of the Shadow DOM to any place, as the nodes will be copied literally to the place of the
<content>tag during compilation. I.e. a directive can use aProtoViewRef+ anElementRefto also move projected content (see Support for manual content reprojection #1989).
Example for implementing TabGroup
<tab-group>
<!-- Tab label can be set via attribute with the content directly inside of <tab> -->
<tab label="Vegetables">Carrots, Beets </tab>
<!-- Tab label can also be a full element -->
<tab>
<tab-label> Fruits </tab-label>
<tab-body>Apples, Cherries</tab-body>
</tab>
</tab-group>@View({
template: `
<div class="header">
<button *ng-for="#tab of tabs" (click)="selected = tab">
<template ng-outlet="tab.labelTemplate"/>
</button>
</div>
<div class="content">
<template ng-outlet="selected.bodyTemplate"/>
<div>
`
})
@Component()
class TabGroup {
selected:Tab = null;
constructor(@Query(Tab) tabs) {}
}
@View({
template: `
<template ng-template-ref>
{{labelAttribute}}
<ng-content select="tab-label"/>
</template>
<template ng-template-ref>
<ng-content select="tab-body"/>
<ng-content select="*"/>
</span>
`
})
@Component()
class Tab {
constructor(
public @Attribute('label') labelAttribute:string,
private @ViewQuery(TemplateRef) templates
) {}
onInit() {
this.labelTemplate = this.templates.get(0);
this.bodyTemplate = this.templates.get(1);
}
}See #1931
Rules
ng-contentcan only support projecting nodes directly under a host element.
Locating elements further down does not work e.g. ifNgForis placed on a parent.<ng-content select="*">is to be treated as "everything that has not been matched by otherng-contenttags".- If an
<ng-content>element is destroyed, its nodes will not overflow to a<ng-content select="*">, as the reprojection happens during compile time. - light dom (projected content) has to be instantiated exactly once, regardeless of whether projection actually happens via
ng-content(e.g.ng-contentis used viaNgIf), as the usage of the content in the light dom could have side effects - trying to instantiate a
ProtoViewthat contains a<ng-content>element multiple times needs to fail, as the light dom might have side effects and we cannot allow these to happen multiple times. - trying to instantiate a
ProtoViewwith anElementRefthat does not match the place where theProtoViewshould not be allowed, as this would be very confusing to users.
Combination with native ShadowDOM
The only thing that Angular needs to do to support native ShadowDOM is to place the view content into a shadow root instead of the element children. A component can declare this like this:
@View({
useShadowDom: true,
template: '...'
})
...
Angular will then still already attach component views and project content during compilation. However, when component views are instantiated that have the flag useShadowDom set, Angular will move the child nodes of Elements into the shadow root of the Element.
Notes
- cycles: ProtoViews can contain cycles. Attaching component views can stop when they encounter a nested
<template>(or*...ortemplateattribute), as instantiation also stops at these places. - for now append templates of nested components only on render side, as we get the most benefits there by cloning a single time. Also keep the
Viewabstraction (instantiating a component would automatically also instantiate child Views and cache them).
Metadata
Metadata
Assignees
Labels
breaking changeseffort2: daysrefactoringIssue that involves refactoring or code-cleanupIssue that involves refactoring or code-cleanup
Activity
tbosch commentedon Jun 13, 2015
@mhevery: I deviated from the initial plan to have a completely different implementation when shadow dom i used, as I don't think we need it (see section above). Essentially, we can't do a short path, but need to implement the whole thing so that users can use it.
tbosch commentedon Jun 13, 2015
/cc @matanlurey , @jelbourn
jelbourn commentedon Jun 15, 2015
I think this is good. Definitely something that needs very clear documentation in the end, but seems pretty learnable to me.
tbosch commentedon Jun 16, 2015
Side goals:
attachViewInContainer, and replace withattachViewNextToElement/attachViewNextToView, i.e. the renderer should no more need to track the content ofViewContainers.15 remaining items
feat(benchmark): add static_tree benchmark
rolandjitsu commentedon Jul 23, 2015
@tbosch so how do I use native shadow DOM now? Do I add the
useShadowDomproperty on the@View()annotation? Or has it been named to something else? Because I can only finduseNativeShadowDomand there is not much docs on this.angular-automatic-lock-bot commentedon Sep 6, 2019
This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.
Read more about our automatic conversation locking policy.
This action has been performed automatically by a bot.