fix(core): clear the `RefreshTransplantedView` when detached (#38768)
The `RefreshTransplantedView` flag is used to indicate that the view or one of its children is transplanted and dirty, so it should still be refreshed as part of change detection. This flag is set on the transplanted view itself as well setting a counter on as its parents. When a transplanted view is detached and still has this flag, it means it got detached before it was refreshed. This can happen for "backwards references" or transplanted views that are inserted at a location that was already checked. In this case, we should decrement the parent counters _and_ clear the flag on the detached view so it's not seen as "transplanted" anymore (it is detached and has no parent counters to adjust). fixes #38619 PR Close #38768
This commit is contained in:
parent
db21c4fb44
commit
d1415162cb
|
@ -300,6 +300,7 @@ function detachMovedView(declarationContainer: LContainer, lView: LView) {
|
|||
// would be cleared and the counter decremented), we need to decrement the view counter here
|
||||
// instead.
|
||||
if (lView[FLAGS] & LViewFlags.RefreshTransplantedView) {
|
||||
lView[FLAGS] &= ~LViewFlags.RefreshTransplantedView;
|
||||
updateTransplantedViewCount(insertionLContainer, -1);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*/
|
||||
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, DoCheck, Input, TemplateRef, Type, ViewChild} from '@angular/core';
|
||||
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, DoCheck, Input, TemplateRef, Type, ViewChild, ViewContainerRef} from '@angular/core';
|
||||
import {AfterViewChecked} from '@angular/core/src/core';
|
||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||
|
@ -594,6 +594,40 @@ describe('change detection for transplanted views', () => {
|
|||
'SheldonSheldonSheldon',
|
||||
'Expected transplanted view to be refreshed even when insertion is not dirty');
|
||||
});
|
||||
|
||||
it('should not fail when change detecting detached transplanted view', () => {
|
||||
@Component({template: '<ng-template>{{incrementChecks()}}</ng-template>'})
|
||||
class AppComponent {
|
||||
@ViewChild(TemplateRef) templateRef!: TemplateRef<{}>;
|
||||
|
||||
constructor(readonly rootVref: ViewContainerRef, readonly cdr: ChangeDetectorRef) {}
|
||||
|
||||
checks = 0;
|
||||
incrementChecks() {
|
||||
this.checks++;
|
||||
}
|
||||
}
|
||||
|
||||
const fixture = TestBed.configureTestingModule({declarations: [AppComponent]})
|
||||
.createComponent(AppComponent);
|
||||
const component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
|
||||
const viewRef = component.templateRef.createEmbeddedView({});
|
||||
// This `ViewContainerRef` is for the root view
|
||||
component.rootVref.insert(viewRef);
|
||||
// `detectChanges` on this `ChangeDetectorRef` will refresh this view and children, not the root
|
||||
// view that has the transplanted `viewRef` inserted.
|
||||
component.cdr.detectChanges();
|
||||
// The template should not have been refreshed because it was inserted "above" the component so
|
||||
// `detectChanges` will not refresh it.
|
||||
expect(component.checks).toEqual(0);
|
||||
|
||||
// Detach view, manually call `detectChanges`, and verify the template was refreshed
|
||||
component.rootVref.detach();
|
||||
viewRef.detectChanges();
|
||||
expect(component.checks).toEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
function trim(text: string|null): string {
|
||||
|
|
Loading…
Reference in New Issue