mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
fix(core): signals should be tracked when embeddedViewRef.detectChanges is called (#55719)
This commit fixes an issue where signals in embedded views are not tracked if they are refreshed with `EmbeddedViewRef.detectChanges` directly. We had previously assumed that embedded views were always refreshed along with their hosts. PR Close #55719
This commit is contained in:
parent
65bf5ecdad
commit
625ca3e2b3
14 changed files with 326 additions and 34 deletions
|
|
@ -9,7 +9,9 @@
|
|||
import {
|
||||
consumerAfterComputation,
|
||||
consumerBeforeComputation,
|
||||
consumerDestroy,
|
||||
consumerPollProducersForChange,
|
||||
getActiveConsumer,
|
||||
ReactiveNode,
|
||||
} from '@angular/core/primitives/signals';
|
||||
|
||||
|
|
@ -29,12 +31,13 @@ import {
|
|||
REACTIVE_TEMPLATE_CONSUMER,
|
||||
TVIEW,
|
||||
TView,
|
||||
TViewType,
|
||||
} from '../interfaces/view';
|
||||
import {
|
||||
getOrCreateTemporaryConsumer,
|
||||
getOrBorrowReactiveLViewConsumer,
|
||||
maybeReturnReactiveLViewConsumer,
|
||||
ReactiveLViewConsumer,
|
||||
viewShouldHaveReactiveConsumer,
|
||||
} from '../reactive_lview_consumer';
|
||||
import {
|
||||
CheckNoChangesMode,
|
||||
|
|
@ -207,11 +210,27 @@ export function refreshView<T>(
|
|||
// - We might already be in a reactive context if this is an embedded view of the host.
|
||||
// - We might be descending into a view that needs a consumer.
|
||||
enterView(lView);
|
||||
let returnConsumerToPool = true;
|
||||
let prevConsumer: ReactiveNode | null = null;
|
||||
let currentConsumer: ReactiveLViewConsumer | null = null;
|
||||
if (!isInCheckNoChangesPass && viewShouldHaveReactiveConsumer(tView)) {
|
||||
currentConsumer = getOrBorrowReactiveLViewConsumer(lView);
|
||||
prevConsumer = consumerBeforeComputation(currentConsumer);
|
||||
if (!isInCheckNoChangesPass) {
|
||||
if (viewShouldHaveReactiveConsumer(tView)) {
|
||||
currentConsumer = getOrBorrowReactiveLViewConsumer(lView);
|
||||
prevConsumer = consumerBeforeComputation(currentConsumer);
|
||||
} else if (getActiveConsumer() === null) {
|
||||
// If the current view should not have a reactive consumer but we don't have an active consumer,
|
||||
// we still need to create a temporary consumer to track any signal reads in this template.
|
||||
// This is a rare case that can happen with `viewContainerRef.createEmbeddedView(...).detectChanges()`.
|
||||
// This temporary consumer marks the first parent that _should_ have a consumer for refresh.
|
||||
// Once that refresh happens, the signals will be tracked in the parent consumer and we can destroy
|
||||
// the temporary one.
|
||||
returnConsumerToPool = false;
|
||||
currentConsumer = getOrCreateTemporaryConsumer(lView);
|
||||
prevConsumer = consumerBeforeComputation(currentConsumer);
|
||||
} else if (lView[REACTIVE_TEMPLATE_CONSUMER]) {
|
||||
consumerDestroy(lView[REACTIVE_TEMPLATE_CONSUMER]);
|
||||
lView[REACTIVE_TEMPLATE_CONSUMER] = null;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
|
|
@ -351,30 +370,14 @@ export function refreshView<T>(
|
|||
} finally {
|
||||
if (currentConsumer !== null) {
|
||||
consumerAfterComputation(currentConsumer, prevConsumer);
|
||||
maybeReturnReactiveLViewConsumer(currentConsumer);
|
||||
if (returnConsumerToPool) {
|
||||
maybeReturnReactiveLViewConsumer(currentConsumer);
|
||||
}
|
||||
}
|
||||
leaveView();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the view should get its own reactive consumer node.
|
||||
*
|
||||
* In the current design, all embedded views share a consumer with the component view. This allows
|
||||
* us to refresh at the component level rather than at a per-view level. In addition, root views get
|
||||
* their own reactive node because root component will have a host view that executes the
|
||||
* component's host bindings. This needs to be tracked in a consumer as well.
|
||||
*
|
||||
* To get a more granular change detection than per-component, all we would just need to update the
|
||||
* condition here so that a given view gets a reactive consumer which can become dirty independently
|
||||
* from its parent component. For example embedded views for signal components could be created with
|
||||
* a new type "SignalEmbeddedView" and the condition here wouldn't even need updating in order to
|
||||
* get granular per-view change detection for signal components.
|
||||
*/
|
||||
function viewShouldHaveReactiveConsumer(tView: TView) {
|
||||
return tView.type !== TViewType.Embedded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Goes over embedded views (ones created through ViewContainerRef APIs) and refreshes
|
||||
* them by executing an associated template function.
|
||||
|
|
|
|||
|
|
@ -8,13 +8,20 @@
|
|||
|
||||
import {REACTIVE_NODE, ReactiveNode} from '@angular/core/primitives/signals';
|
||||
|
||||
import {LView, REACTIVE_TEMPLATE_CONSUMER} from './interfaces/view';
|
||||
import {markAncestorsForTraversal} from './util/view_utils';
|
||||
import {
|
||||
LView,
|
||||
PARENT,
|
||||
REACTIVE_TEMPLATE_CONSUMER,
|
||||
TVIEW,
|
||||
TView,
|
||||
TViewType,
|
||||
} from './interfaces/view';
|
||||
import {getLViewParent, markAncestorsForTraversal, markViewForRefresh} from './util/view_utils';
|
||||
import {assertDefined} from '../util/assert';
|
||||
|
||||
let freeConsumers: ReactiveLViewConsumer[] = [];
|
||||
let freeConsumers: ReactiveNode[] = [];
|
||||
export interface ReactiveLViewConsumer extends ReactiveNode {
|
||||
lView: LView | null;
|
||||
slot: typeof REACTIVE_TEMPLATE_CONSUMER;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -27,8 +34,7 @@ export function getOrBorrowReactiveLViewConsumer(lView: LView): ReactiveLViewCon
|
|||
}
|
||||
|
||||
function borrowReactiveLViewConsumer(lView: LView): ReactiveLViewConsumer {
|
||||
const consumer: ReactiveLViewConsumer =
|
||||
freeConsumers.pop() ?? Object.create(REACTIVE_LVIEW_CONSUMER_NODE);
|
||||
const consumer = freeConsumers.pop() ?? Object.create(REACTIVE_LVIEW_CONSUMER_NODE);
|
||||
consumer.lView = lView;
|
||||
return consumer;
|
||||
}
|
||||
|
|
@ -42,7 +48,7 @@ export function maybeReturnReactiveLViewConsumer(consumer: ReactiveLViewConsumer
|
|||
freeConsumers.push(consumer);
|
||||
}
|
||||
|
||||
const REACTIVE_LVIEW_CONSUMER_NODE: Omit<ReactiveLViewConsumer, 'lView' | 'slot'> = {
|
||||
const REACTIVE_LVIEW_CONSUMER_NODE: Omit<ReactiveLViewConsumer, 'lView'> = {
|
||||
...REACTIVE_NODE,
|
||||
consumerIsAlwaysLive: true,
|
||||
consumerMarkedDirty: (node: ReactiveLViewConsumer) => {
|
||||
|
|
@ -52,3 +58,60 @@ const REACTIVE_LVIEW_CONSUMER_NODE: Omit<ReactiveLViewConsumer, 'lView' | 'slot'
|
|||
this.lView![REACTIVE_TEMPLATE_CONSUMER] = this;
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a temporary consumer for use with `LView`s that should not have consumers.
|
||||
* If the LView already has a consumer, returns the existing one instead.
|
||||
*
|
||||
* This is necessary because some APIs may cause change detection directly on an LView
|
||||
* that we do not want to have a consumer (Embedded views today). As a result, there
|
||||
* would be no active consumer from running change detection on its host component
|
||||
* and any signals in the LView template would be untracked. Instead, we create
|
||||
* this temporary consumer that marks the first parent that _should_ have a consumer
|
||||
* for refresh. Once change detection runs as part of that refresh, we throw away
|
||||
* this consumer because its signals will then be tracked by the parent's consumer.
|
||||
*/
|
||||
export function getOrCreateTemporaryConsumer(lView: LView): ReactiveLViewConsumer {
|
||||
const consumer = lView[REACTIVE_TEMPLATE_CONSUMER] ?? Object.create(TEMPORARY_CONSUMER_NODE);
|
||||
consumer.lView = lView;
|
||||
return consumer;
|
||||
}
|
||||
|
||||
const TEMPORARY_CONSUMER_NODE = {
|
||||
...REACTIVE_NODE,
|
||||
consumerIsAlwaysLive: true,
|
||||
consumerMarkedDirty: (node: ReactiveLViewConsumer) => {
|
||||
let parent = getLViewParent(node.lView!);
|
||||
while (parent && !viewShouldHaveReactiveConsumer(parent[TVIEW])) {
|
||||
parent = getLViewParent(parent);
|
||||
}
|
||||
if (!parent) {
|
||||
// If we can't find an appropriate parent that should have a consumer, we
|
||||
// don't have a way of appropriately refreshing this LView as part of application synchronization.
|
||||
return;
|
||||
}
|
||||
|
||||
markViewForRefresh(parent);
|
||||
},
|
||||
consumerOnSignalRead(this: ReactiveLViewConsumer): void {
|
||||
this.lView![REACTIVE_TEMPLATE_CONSUMER] = this;
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Indicates if the view should get its own reactive consumer node.
|
||||
*
|
||||
* In the current design, all embedded views share a consumer with the component view. This allows
|
||||
* us to refresh at the component level rather than at a per-view level. In addition, root views get
|
||||
* their own reactive node because root component will have a host view that executes the
|
||||
* component's host bindings. This needs to be tracked in a consumer as well.
|
||||
*
|
||||
* To get a more granular change detection than per-component, all we would just need to update the
|
||||
* condition here so that a given view gets a reactive consumer which can become dirty independently
|
||||
* from its parent component. For example embedded views for signal components could be created with
|
||||
* a new type "SignalEmbeddedView" and the condition here wouldn't even need updating in order to
|
||||
* get granular per-view change detection for signal components.
|
||||
*/
|
||||
export function viewShouldHaveReactiveConsumer(tView: TView) {
|
||||
return tView.type !== TViewType.Embedded;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,19 +7,16 @@
|
|||
*/
|
||||
|
||||
import {NgFor, NgIf} from '@angular/common';
|
||||
import {PLATFORM_BROWSER_ID} from '@angular/common/src/platform_id';
|
||||
import {
|
||||
afterNextRender,
|
||||
ApplicationRef,
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
computed,
|
||||
Directive,
|
||||
EnvironmentInjector,
|
||||
ElementRef,
|
||||
inject,
|
||||
Input,
|
||||
PLATFORM_ID,
|
||||
signal,
|
||||
TemplateRef,
|
||||
ViewChild,
|
||||
|
|
@ -803,6 +800,73 @@ describe('OnPush components with signals', () => {
|
|||
fixture.detectChanges();
|
||||
expect(trim(fixture.nativeElement.textContent)).toEqual('new');
|
||||
});
|
||||
|
||||
it('tracks signal updates if embedded view is change detected directly', () => {
|
||||
@Component({
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `
|
||||
<ng-template #template>{{value()}}</ng-template>
|
||||
`,
|
||||
standalone: true,
|
||||
})
|
||||
class Test {
|
||||
value = signal('initial');
|
||||
@ViewChild('template', {static: true, read: TemplateRef})
|
||||
template!: TemplateRef<{}>;
|
||||
@ViewChild('template', {static: true, read: ViewContainerRef})
|
||||
vcr!: ViewContainerRef;
|
||||
}
|
||||
|
||||
const fixture = TestBed.createComponent(Test);
|
||||
const appRef = TestBed.inject(ApplicationRef);
|
||||
appRef.attachView(fixture.componentRef.hostView);
|
||||
appRef.tick();
|
||||
|
||||
const viewRef = fixture.componentInstance.vcr.createEmbeddedView(
|
||||
fixture.componentInstance.template,
|
||||
);
|
||||
viewRef.detectChanges();
|
||||
expect(fixture.nativeElement.innerText).toContain('initial');
|
||||
|
||||
fixture.componentInstance.value.set('new');
|
||||
appRef.tick();
|
||||
expect(fixture.nativeElement.innerText).toContain('new');
|
||||
});
|
||||
|
||||
it('tracks signal updates if embedded view is change detected directly before attaching', () => {
|
||||
@Component({
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `
|
||||
<ng-template #template>{{value()}}</ng-template>
|
||||
`,
|
||||
standalone: true,
|
||||
})
|
||||
class Test {
|
||||
value = signal('initial');
|
||||
@ViewChild('template', {static: true, read: TemplateRef})
|
||||
template!: TemplateRef<{}>;
|
||||
@ViewChild('template', {static: true, read: ViewContainerRef})
|
||||
vcr!: ViewContainerRef;
|
||||
element = inject(ElementRef);
|
||||
}
|
||||
|
||||
const fixture = TestBed.createComponent(Test);
|
||||
const appRef = TestBed.inject(ApplicationRef);
|
||||
appRef.attachView(fixture.componentRef.hostView);
|
||||
appRef.tick();
|
||||
|
||||
const viewRef = fixture.componentInstance.template.createEmbeddedView(
|
||||
fixture.componentInstance.template,
|
||||
);
|
||||
fixture.componentInstance.element.nativeElement.appendChild(viewRef.rootNodes[0]);
|
||||
viewRef.detectChanges();
|
||||
expect(fixture.nativeElement.innerText).toContain('initial');
|
||||
fixture.componentInstance.vcr.insert(viewRef);
|
||||
|
||||
fixture.componentInstance.value.set('new');
|
||||
appRef.tick();
|
||||
expect(fixture.nativeElement.innerText).toContain('new');
|
||||
});
|
||||
});
|
||||
|
||||
describe('shielded by non-dirty OnPush', () => {
|
||||
|
|
|
|||
|
|
@ -434,6 +434,9 @@
|
|||
{
|
||||
"name": "REACTIVE_LVIEW_CONSUMER_NODE"
|
||||
},
|
||||
{
|
||||
"name": "REACTIVE_NODE"
|
||||
},
|
||||
{
|
||||
"name": "REMOVE_STYLES_ON_COMPONENT_DESTROY"
|
||||
},
|
||||
|
|
@ -491,6 +494,9 @@
|
|||
{
|
||||
"name": "Subscription"
|
||||
},
|
||||
{
|
||||
"name": "TEMPORARY_CONSUMER_NODE"
|
||||
},
|
||||
{
|
||||
"name": "TESTABILITY"
|
||||
},
|
||||
|
|
@ -746,6 +752,12 @@
|
|||
{
|
||||
"name": "configureViewWithDirective"
|
||||
},
|
||||
{
|
||||
"name": "consumerBeforeComputation"
|
||||
},
|
||||
{
|
||||
"name": "consumerDestroy"
|
||||
},
|
||||
{
|
||||
"name": "consumerIsLive"
|
||||
},
|
||||
|
|
@ -1454,6 +1466,9 @@
|
|||
{
|
||||
"name": "viewAttachedToChangeDetector"
|
||||
},
|
||||
{
|
||||
"name": "viewShouldHaveReactiveConsumer"
|
||||
},
|
||||
{
|
||||
"name": "visitDslNode"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -473,6 +473,9 @@
|
|||
{
|
||||
"name": "REACTIVE_LVIEW_CONSUMER_NODE"
|
||||
},
|
||||
{
|
||||
"name": "REACTIVE_NODE"
|
||||
},
|
||||
{
|
||||
"name": "REMOVE_STYLES_ON_COMPONENT_DESTROY"
|
||||
},
|
||||
|
|
@ -533,6 +536,9 @@
|
|||
{
|
||||
"name": "Subscription"
|
||||
},
|
||||
{
|
||||
"name": "TEMPORARY_CONSUMER_NODE"
|
||||
},
|
||||
{
|
||||
"name": "TESTABILITY"
|
||||
},
|
||||
|
|
@ -803,6 +809,12 @@
|
|||
{
|
||||
"name": "configureViewWithDirective"
|
||||
},
|
||||
{
|
||||
"name": "consumerBeforeComputation"
|
||||
},
|
||||
{
|
||||
"name": "consumerDestroy"
|
||||
},
|
||||
{
|
||||
"name": "consumerIsLive"
|
||||
},
|
||||
|
|
@ -1529,6 +1541,9 @@
|
|||
{
|
||||
"name": "viewAttachedToChangeDetector"
|
||||
},
|
||||
{
|
||||
"name": "viewShouldHaveReactiveConsumer"
|
||||
},
|
||||
{
|
||||
"name": "visitDslNode"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -359,6 +359,9 @@
|
|||
{
|
||||
"name": "REACTIVE_LVIEW_CONSUMER_NODE"
|
||||
},
|
||||
{
|
||||
"name": "REACTIVE_NODE"
|
||||
},
|
||||
{
|
||||
"name": "REMOVE_STYLES_ON_COMPONENT_DESTROY"
|
||||
},
|
||||
|
|
@ -401,6 +404,9 @@
|
|||
{
|
||||
"name": "Subscription"
|
||||
},
|
||||
{
|
||||
"name": "TEMPORARY_CONSUMER_NODE"
|
||||
},
|
||||
{
|
||||
"name": "TESTABILITY"
|
||||
},
|
||||
|
|
@ -602,6 +608,12 @@
|
|||
{
|
||||
"name": "configureViewWithDirective"
|
||||
},
|
||||
{
|
||||
"name": "consumerBeforeComputation"
|
||||
},
|
||||
{
|
||||
"name": "consumerDestroy"
|
||||
},
|
||||
{
|
||||
"name": "consumerIsLive"
|
||||
},
|
||||
|
|
@ -1229,6 +1241,9 @@
|
|||
{
|
||||
"name": "viewAttachedToChangeDetector"
|
||||
},
|
||||
{
|
||||
"name": "viewShouldHaveReactiveConsumer"
|
||||
},
|
||||
{
|
||||
"name": "walkProviderTree"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -461,6 +461,9 @@
|
|||
{
|
||||
"name": "Subscription"
|
||||
},
|
||||
{
|
||||
"name": "TEMPORARY_CONSUMER_NODE"
|
||||
},
|
||||
{
|
||||
"name": "TESTABILITY"
|
||||
},
|
||||
|
|
@ -680,6 +683,12 @@
|
|||
{
|
||||
"name": "configureViewWithDirective"
|
||||
},
|
||||
{
|
||||
"name": "consumerBeforeComputation"
|
||||
},
|
||||
{
|
||||
"name": "consumerDestroy"
|
||||
},
|
||||
{
|
||||
"name": "consumerIsLive"
|
||||
},
|
||||
|
|
@ -2468,6 +2477,9 @@
|
|||
{
|
||||
"name": "viewAttachedToChangeDetector"
|
||||
},
|
||||
{
|
||||
"name": "viewShouldHaveReactiveConsumer"
|
||||
},
|
||||
{
|
||||
"name": "walkProviderTree"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -491,6 +491,9 @@
|
|||
{
|
||||
"name": "REACTIVE_LVIEW_CONSUMER_NODE"
|
||||
},
|
||||
{
|
||||
"name": "REACTIVE_NODE"
|
||||
},
|
||||
{
|
||||
"name": "REMOVE_STYLES_ON_COMPONENT_DESTROY"
|
||||
},
|
||||
|
|
@ -557,6 +560,9 @@
|
|||
{
|
||||
"name": "Subscription"
|
||||
},
|
||||
{
|
||||
"name": "TEMPORARY_CONSUMER_NODE"
|
||||
},
|
||||
{
|
||||
"name": "TESTABILITY"
|
||||
},
|
||||
|
|
@ -848,6 +854,12 @@
|
|||
{
|
||||
"name": "configureViewWithDirective"
|
||||
},
|
||||
{
|
||||
"name": "consumerBeforeComputation"
|
||||
},
|
||||
{
|
||||
"name": "consumerDestroy"
|
||||
},
|
||||
{
|
||||
"name": "consumerIsLive"
|
||||
},
|
||||
|
|
@ -1793,6 +1805,9 @@
|
|||
{
|
||||
"name": "viewAttachedToChangeDetector"
|
||||
},
|
||||
{
|
||||
"name": "viewShouldHaveReactiveConsumer"
|
||||
},
|
||||
{
|
||||
"name": "walkProviderTree"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -476,6 +476,9 @@
|
|||
{
|
||||
"name": "REACTIVE_LVIEW_CONSUMER_NODE"
|
||||
},
|
||||
{
|
||||
"name": "REACTIVE_NODE"
|
||||
},
|
||||
{
|
||||
"name": "REMOVE_STYLES_ON_COMPONENT_DESTROY"
|
||||
},
|
||||
|
|
@ -539,6 +542,9 @@
|
|||
{
|
||||
"name": "Subscription"
|
||||
},
|
||||
{
|
||||
"name": "TEMPORARY_CONSUMER_NODE"
|
||||
},
|
||||
{
|
||||
"name": "TESTABILITY"
|
||||
},
|
||||
|
|
@ -815,6 +821,12 @@
|
|||
{
|
||||
"name": "configureViewWithDirective"
|
||||
},
|
||||
{
|
||||
"name": "consumerBeforeComputation"
|
||||
},
|
||||
{
|
||||
"name": "consumerDestroy"
|
||||
},
|
||||
{
|
||||
"name": "consumerIsLive"
|
||||
},
|
||||
|
|
@ -1775,6 +1787,9 @@
|
|||
{
|
||||
"name": "viewAttachedToChangeDetector"
|
||||
},
|
||||
{
|
||||
"name": "viewShouldHaveReactiveConsumer"
|
||||
},
|
||||
{
|
||||
"name": "walkProviderTree"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -278,6 +278,9 @@
|
|||
{
|
||||
"name": "REACTIVE_LVIEW_CONSUMER_NODE"
|
||||
},
|
||||
{
|
||||
"name": "REACTIVE_NODE"
|
||||
},
|
||||
{
|
||||
"name": "RendererFactory2"
|
||||
},
|
||||
|
|
@ -308,6 +311,9 @@
|
|||
{
|
||||
"name": "Subscription"
|
||||
},
|
||||
{
|
||||
"name": "TEMPORARY_CONSUMER_NODE"
|
||||
},
|
||||
{
|
||||
"name": "TESTABILITY"
|
||||
},
|
||||
|
|
@ -464,6 +470,12 @@
|
|||
{
|
||||
"name": "configureViewWithDirective"
|
||||
},
|
||||
{
|
||||
"name": "consumerBeforeComputation"
|
||||
},
|
||||
{
|
||||
"name": "consumerDestroy"
|
||||
},
|
||||
{
|
||||
"name": "consumerIsLive"
|
||||
},
|
||||
|
|
@ -980,6 +992,9 @@
|
|||
{
|
||||
"name": "viewAttachedToChangeDetector"
|
||||
},
|
||||
{
|
||||
"name": "viewShouldHaveReactiveConsumer"
|
||||
},
|
||||
{
|
||||
"name": "walkProviderTree"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -392,6 +392,9 @@
|
|||
{
|
||||
"name": "REACTIVE_LVIEW_CONSUMER_NODE"
|
||||
},
|
||||
{
|
||||
"name": "REACTIVE_NODE"
|
||||
},
|
||||
{
|
||||
"name": "REFERENCE_NODE_BODY"
|
||||
},
|
||||
|
|
@ -464,6 +467,9 @@
|
|||
{
|
||||
"name": "Subscription"
|
||||
},
|
||||
{
|
||||
"name": "TEMPORARY_CONSUMER_NODE"
|
||||
},
|
||||
{
|
||||
"name": "TESTABILITY"
|
||||
},
|
||||
|
|
@ -665,6 +671,12 @@
|
|||
{
|
||||
"name": "configureViewWithDirective"
|
||||
},
|
||||
{
|
||||
"name": "consumerBeforeComputation"
|
||||
},
|
||||
{
|
||||
"name": "consumerDestroy"
|
||||
},
|
||||
{
|
||||
"name": "consumerIsLive"
|
||||
},
|
||||
|
|
@ -1367,6 +1379,9 @@
|
|||
{
|
||||
"name": "viewAttachedToChangeDetector"
|
||||
},
|
||||
{
|
||||
"name": "viewShouldHaveReactiveConsumer"
|
||||
},
|
||||
{
|
||||
"name": "walkProviderTree"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -548,6 +548,9 @@
|
|||
{
|
||||
"name": "REACTIVE_LVIEW_CONSUMER_NODE"
|
||||
},
|
||||
{
|
||||
"name": "REACTIVE_NODE"
|
||||
},
|
||||
{
|
||||
"name": "REMOVE_STYLES_ON_COMPONENT_DESTROY"
|
||||
},
|
||||
|
|
@ -677,6 +680,9 @@
|
|||
{
|
||||
"name": "Subscription"
|
||||
},
|
||||
{
|
||||
"name": "TEMPORARY_CONSUMER_NODE"
|
||||
},
|
||||
{
|
||||
"name": "TESTABILITY"
|
||||
},
|
||||
|
|
@ -965,6 +971,12 @@
|
|||
{
|
||||
"name": "configureViewWithDirective"
|
||||
},
|
||||
{
|
||||
"name": "consumerBeforeComputation"
|
||||
},
|
||||
{
|
||||
"name": "consumerDestroy"
|
||||
},
|
||||
{
|
||||
"name": "consumerIsLive"
|
||||
},
|
||||
|
|
@ -2093,6 +2105,9 @@
|
|||
{
|
||||
"name": "viewAttachedToChangeDetector"
|
||||
},
|
||||
{
|
||||
"name": "viewShouldHaveReactiveConsumer"
|
||||
},
|
||||
{
|
||||
"name": "walkProviderTree"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -323,6 +323,9 @@
|
|||
{
|
||||
"name": "REACTIVE_LVIEW_CONSUMER_NODE"
|
||||
},
|
||||
{
|
||||
"name": "REACTIVE_NODE"
|
||||
},
|
||||
{
|
||||
"name": "REMOVE_STYLES_ON_COMPONENT_DESTROY"
|
||||
},
|
||||
|
|
@ -368,6 +371,9 @@
|
|||
{
|
||||
"name": "Subscription"
|
||||
},
|
||||
{
|
||||
"name": "TEMPORARY_CONSUMER_NODE"
|
||||
},
|
||||
{
|
||||
"name": "TESTABILITY"
|
||||
},
|
||||
|
|
@ -542,6 +548,12 @@
|
|||
{
|
||||
"name": "configureViewWithDirective"
|
||||
},
|
||||
{
|
||||
"name": "consumerBeforeComputation"
|
||||
},
|
||||
{
|
||||
"name": "consumerDestroy"
|
||||
},
|
||||
{
|
||||
"name": "consumerIsLive"
|
||||
},
|
||||
|
|
@ -1079,6 +1091,9 @@
|
|||
{
|
||||
"name": "viewAttachedToChangeDetector"
|
||||
},
|
||||
{
|
||||
"name": "viewShouldHaveReactiveConsumer"
|
||||
},
|
||||
{
|
||||
"name": "walkProviderTree"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -386,6 +386,9 @@
|
|||
{
|
||||
"name": "REACTIVE_LVIEW_CONSUMER_NODE"
|
||||
},
|
||||
{
|
||||
"name": "REACTIVE_NODE"
|
||||
},
|
||||
{
|
||||
"name": "REMOVE_STYLES_ON_COMPONENT_DESTROY"
|
||||
},
|
||||
|
|
@ -434,6 +437,9 @@
|
|||
{
|
||||
"name": "Subscription"
|
||||
},
|
||||
{
|
||||
"name": "TEMPORARY_CONSUMER_NODE"
|
||||
},
|
||||
{
|
||||
"name": "TESTABILITY"
|
||||
},
|
||||
|
|
@ -713,6 +719,12 @@
|
|||
{
|
||||
"name": "configureViewWithDirective"
|
||||
},
|
||||
{
|
||||
"name": "consumerBeforeComputation"
|
||||
},
|
||||
{
|
||||
"name": "consumerDestroy"
|
||||
},
|
||||
{
|
||||
"name": "consumerIsLive"
|
||||
},
|
||||
|
|
@ -1481,6 +1493,9 @@
|
|||
{
|
||||
"name": "viewAttachedToChangeDetector"
|
||||
},
|
||||
{
|
||||
"name": "viewShouldHaveReactiveConsumer"
|
||||
},
|
||||
{
|
||||
"name": "walkProviderTree"
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in a new issue