Description
Is this a regression?
- Yes, this behavior used to work in the previous version
The previous version in which this bug was not present was
No response
Description
I want to create a component that does support virtual scrolling but can be turned off.
To do so, I want to use some ng-templates so I do not have to redefine things like headers, item template, etc.
The code for that (minimalistic, without turn on/off virtual scrolling) looks like:
import { Component, Directive, inject, Injector, Input } from '@angular/core';
import { CommonModule } from '@angular/common';
import { bootstrapApplication } from '@angular/platform-browser';
import {
CdkVirtualScrollViewport,
ScrollingModule,
} from '@angular/cdk/scrolling';
@Component({
selector: 'my-test',
template: `I have a viewport? {{ !!cdkVirtualScrollViewport }}`,
})
export class MyTest {
cdkVirtualScrollViewport = inject(CdkVirtualScrollViewport);
}
@Directive({
selector: '[myProvider]',
exportAs: 'myProvider',
})
export class MyProvider {
private readonly myInjector = inject(Injector);
@Input() myProvider!: CdkVirtualScrollViewport;
get injector(): Injector {
return Injector.create({
parent: this.myInjector,
providers: [
{
provide: CdkVirtualScrollViewport,
useValue: this.myProvider,
},
],
});
}
}
@Component({
selector: 'my-app',
template: `
Woop
<cdk-virtual-scroll-viewport itemSize="50" #scrollViewport style="height: 200px">
<ng-container *ngTemplateOutlet="gridTemplate; context: { $implicit: virtualFor, scrollViewport: scrollViewport }" />
</cdk-virtual-scroll-viewport>
<ng-template #gridTemplate let-template let-scrollViewport="scrollViewport">
<div>Imagine a header here</div>
<ng-container [myProvider]="scrollViewport" #myProvider="myProvider">
<ng-container *ngTemplateOutlet="template; injector: myProvider.injector" />
</ng-container>
</ng-template>
<ng-template #virtualFor>
<my-test />
<!-- Comment the ng-container out to see, that my-test has an cdkVirtualScrollViewport -->
<ng-container *cdkVirtualFor="let item of data">
<ng-container *ngTemplateOutlet="itemTemplate; context: { $implicit: item }" />
</ng-container>
</ng-template>
<ng-template #itemTemplate let-item>
<div>{{ item }}</div>
</ng-template>
`,
})
export class AppComponent {
data = ['hello', 'world'];
}
As you can see, I have
- AppComponent: Defines all the templates and the virtual scrolling.
- MyProvider: A directive to create a new injector containing the existing CdkVirtualScrollViewport
- MyTest: a simple test component to see if injection works as it should
Reproduction
StackBlitz link: https://stackblitz.com/edit/stackblitz-starters-sjp7es?file=src%2Fmain.ts
I've also attached a Zip-File with a working sample, because for whatever reason the StackBlitz does not load the DevServer sometimes, maybe they have a hiccup.
Steps to reproduce:
- Either download the example or open the stackblitz link
- You will see an error when starting the app, that there is no provider for CdkVirtualScrollViewport
- If you comment out the
ng-container
containing thecdkVirtualFor
you will see, thatmy-test
components outputs true, because it has an injector containing a CdkVirtualScrollViewport
Expected Behavior
I would expect it to work and CdkVirtualFor actually sees the CdkVirtualScrollViewport, because it is in the Injector, as my-test
component reports.
Actual Behavior
Error pops up with:
[CdkVirtualScrollViewport -> CdkVirtualScrollViewport -> CdkVirtualScrollViewport -> CdkVirtualScrollViewport]:
NullInjectorError: No provider for CdkVirtualScrollViewport!
Environment
Angular CLI: 16.1.1
Node: 18.16.0
Package Manager: npm 9.5.1
OS: darwin arm64
Angular: 16.1.2
... animations, cdk, common, compiler, compiler-cli, core, forms
... platform-browser, platform-browser-dynamic, router
Package Version
---------------------------------------------------------
@angular-devkit/architect 0.1601.1
@angular-devkit/build-angular 16.1.1
@angular-devkit/core 16.1.1
@angular-devkit/schematics 16.1.1
@angular/cli 16.1.1
@schematics/angular 16.1.1
rxjs 7.8.1
typescript 5.1.3