Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions packages/core/src/defer/dom_triggers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
TriggerType,
} from './interfaces';
import {getLDeferBlockDetails} from './utils';
import {shouldTriggerDeferBlock} from './rendering';

/**
* Wrapper for onViewport trigger with angular specific Injector for resolving NgZone instance
Expand Down Expand Up @@ -134,6 +135,10 @@ export function registerDomTrigger<O>(
type: TriggerType,
options?: O,
) {
if (!shouldTriggerDeferBlock(type, initialLView)) {
return;
}

const injector = initialLView[INJECTOR];
const zone = injector.get(NgZone);
let poll: AfterRenderRef;
Expand Down
21 changes: 20 additions & 1 deletion packages/core/src/defer/rendering.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
*/

import {CachedInjectorService} from '../cached_injector_service';
import {NotificationSource} from '../change_detection/scheduling/zoneless_scheduling';
import {EnvironmentInjector, InjectionToken, Injector, Provider} from '../di';
import {
DehydratedContainerView,
Expand All @@ -26,6 +25,7 @@ import {assertDefined} from '../util/assert';

import {
DEFER_BLOCK_STATE,
DeferBlockBehavior,
DeferBlockConfig,
DeferBlockDependencyInterceptor,
DeferBlockInternalState,
Expand All @@ -40,6 +40,7 @@ import {
SSR_BLOCK_STATE,
STATE_IS_FROZEN_UNTIL,
TDeferBlockDetails,
TriggerType,
} from './interfaces';
import {scheduleTimerTrigger} from './timer_scheduler';
import {
Expand Down Expand Up @@ -485,3 +486,21 @@ export function ɵɵdeferEnableTimerScheduling(
applyDeferBlockStateWithSchedulingImpl = applyDeferBlockStateWithScheduling;
}
}

/**
* Determines whether we should proceed with triggering a given defer block.
*/
export function shouldTriggerDeferBlock(triggerType: TriggerType, lView: LView): boolean {
// prevents triggering regular triggers when on the server.
if (triggerType === TriggerType.Regular && typeof ngServerMode !== 'undefined' && ngServerMode) {
return false;
}

// prevents triggering in the case of a test run with manual defer block configuration.
const injector = lView[INJECTOR];
const config = injector.get(DEFER_BLOCK_CONFIG, null, {optional: true});
if (config?.behavior === DeferBlockBehavior.Manual) {
return false;
}
return true;
}
21 changes: 1 addition & 20 deletions packages/core/src/defer/triggering.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ import {onViewportWrapper} from './dom_triggers';
import {onIdle} from './idle_scheduler';
import {
DEFER_BLOCK_STATE,
DeferBlockBehavior,
DeferBlockState,
DeferBlockTrigger,
DeferDependenciesLoadingState,
Expand All @@ -56,11 +55,11 @@ import {
} from './interfaces';
import {DEHYDRATED_BLOCK_REGISTRY, DehydratedBlockRegistry} from './registry';
import {
DEFER_BLOCK_CONFIG,
DEFER_BLOCK_DEPENDENCY_INTERCEPTOR,
renderDeferBlockState,
renderDeferStateAfterResourceLoading,
renderPlaceholder,
shouldTriggerDeferBlock,
} from './rendering';
import {onTimer} from './timer_scheduler';
import {
Expand Down Expand Up @@ -313,24 +312,6 @@ export function triggerResourceLoading(
});
}

/**
* Defines whether we should proceed with triggering a given defer block.
*/
function shouldTriggerDeferBlock(triggerType: TriggerType, lView: LView): boolean {
// prevents triggering regular triggers when on the server.
if (triggerType === TriggerType.Regular && typeof ngServerMode !== 'undefined' && ngServerMode) {
return false;
}

// prevents triggering in the case of a test run with manual defer block configuration.
const injector = lView[INJECTOR];
const config = injector.get(DEFER_BLOCK_CONFIG, null, {optional: true});
if (config?.behavior === DeferBlockBehavior.Manual) {
return false;
}
return true;
}

/**
* Attempts to trigger loading of defer block dependencies.
* If the block is already in a loading, completed or an error state -
Expand Down
41 changes: 39 additions & 2 deletions packages/core/test/acceptance/defer_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,15 @@ import {ChainedInjector} from '../../src/render3/chained_injector';
import {getComponentDef} from '../../src/render3/def_getters';
import {getInjectorResolutionPath} from '../../src/render3/util/injector_discovery_utils';
import {global} from '../../src/util/global';
import {ComponentFixture, DeferBlockBehavior, fakeAsync, flush, TestBed, tick} from '../../testing';
import {
ComponentFixture,
DeferBlockBehavior,
DeferBlockState,
fakeAsync,
flush,
TestBed,
tick,
} from '../../testing';

/**
* Clears all associated directive defs from a given component class.
Expand Down Expand Up @@ -4532,7 +4540,6 @@ describe('@defer', () => {
@placeholder {<button>p{{item}} </button>}
}
`,

changeDetection: ChangeDetectionStrategy.Eager,})
class MyCmp {
items = [1, 2, 3, 4, 5, 6];
Expand Down Expand Up @@ -4652,6 +4659,36 @@ describe('@defer', () => {
expect(activeObservers[2].observedElements.has(button)).toBe(true);
expect(activeObservers[2].options).toEqual({rootMargin: '1vh'});
}));

it('should not attach observer if rendering manually', async () => {
@Component({
template: `
@defer (on viewport(trigger)) {
Main content
} @placeholder {
Placeholder
}

<button #trigger></button>
`,
})
class MyCmp {}

TestBed.configureTestingModule({
deferBlockBehavior: DeferBlockBehavior.Manual,
});
const fixture = TestBed.createComponent(MyCmp);
fixture.detectChanges();

expect(activeObservers.length).toBe(0);
expect(fixture.nativeElement.textContent.trim()).toBe('Placeholder');

const deferBlock = (await fixture.getDeferBlocks())[0];
await deferBlock.render(DeferBlockState.Complete);

expect(activeObservers.length).toBe(0);
expect(fixture.nativeElement.textContent.trim()).toBe('Main content');
});
});

describe('DOM-based events cleanup', () => {
Expand Down
Loading