fix(core): pipes injecting viewProviders when used on a component host node (#36512)
The flag that determines whether something should be able to inject from `viewProviders` is opt-out and the pipes weren't opted out, resulting in them being able to see the viewProviders if they're placed on a component host node. Fixes #36146. PR Close #36512
This commit is contained in:
parent
bb150c2704
commit
81d23b33ef
|
@ -71,7 +71,7 @@ import {stringifyForError} from './util/misc_utils';
|
|||
*/
|
||||
let includeViewProviders = true;
|
||||
|
||||
function setIncludeViewProviders(v: boolean): boolean {
|
||||
export function setIncludeViewProviders(v: boolean): boolean {
|
||||
const oldValue = includeViewProviders;
|
||||
includeViewProviders = v;
|
||||
return oldValue;
|
||||
|
|
|
@ -11,6 +11,7 @@ import {PipeTransform} from '../change_detection/pipe_transform';
|
|||
import {setInjectImplementation} from '../di/injector_compatibility';
|
||||
|
||||
import {getFactoryDef} from './definition';
|
||||
import {setIncludeViewProviders} from './di';
|
||||
import {store, ɵɵdirectiveInject} from './instructions/all';
|
||||
import {PipeDef, PipeDefList} from './interfaces/definition';
|
||||
import {HEADER_OFFSET, LView, TVIEW} from './interfaces/view';
|
||||
|
@ -47,7 +48,12 @@ export function ɵɵpipe(index: number, pipeName: string): any {
|
|||
|
||||
const pipeFactory = pipeDef.factory || (pipeDef.factory = getFactoryDef(pipeDef.type, true));
|
||||
const previousInjectImplementation = setInjectImplementation(ɵɵdirectiveInject);
|
||||
|
||||
// DI for pipes is supposed to behave like directives when placed on a component
|
||||
// host node, which means that we have to disable access to `viewProviders`.
|
||||
const previousIncludeViewProviders = setIncludeViewProviders(false);
|
||||
const pipeInstance = pipeFactory();
|
||||
setIncludeViewProviders(previousIncludeViewProviders);
|
||||
setInjectImplementation(previousInjectImplementation);
|
||||
store(tView, getLView(), index, pipeInstance);
|
||||
return pipeInstance;
|
||||
|
|
|
@ -2170,4 +2170,134 @@ describe('di', () => {
|
|||
fixture.componentInstance.child.base,
|
||||
'should not get dirA from parent, but create new dirB from the useFactory provider');
|
||||
});
|
||||
|
||||
|
||||
describe('provider access on the same node', () => {
|
||||
const token = new InjectionToken<number>('token');
|
||||
|
||||
onlyInIvy('accessing providers on the same node through a pipe was not supported in ViewEngine')
|
||||
.it('pipes should access providers from the component they are on', () => {
|
||||
@Pipe({name: 'token'})
|
||||
class TokenPipe {
|
||||
constructor(@Inject(token) private _token: string) {}
|
||||
|
||||
transform(value: string): string {
|
||||
return value + this._token;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'child-comp',
|
||||
template: '{{value}}',
|
||||
providers: [{provide: token, useValue: 'child'}]
|
||||
})
|
||||
class ChildComp {
|
||||
@Input() value: any;
|
||||
}
|
||||
|
||||
@Component({
|
||||
template: `<child-comp [value]="'' | token"></child-comp>`,
|
||||
providers: [{provide: token, useValue: 'parent'}]
|
||||
})
|
||||
class App {
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [App, ChildComp, TokenPipe]});
|
||||
const fixture = TestBed.createComponent(App);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(fixture.nativeElement.textContent.trim()).toBe('child');
|
||||
});
|
||||
|
||||
it('pipes should not access viewProviders from the component they are on', () => {
|
||||
@Pipe({name: 'token'})
|
||||
class TokenPipe {
|
||||
constructor(@Inject(token) private _token: string) {}
|
||||
|
||||
transform(value: string): string {
|
||||
return value + this._token;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'child-comp',
|
||||
template: '{{value}}',
|
||||
viewProviders: [{provide: token, useValue: 'child'}]
|
||||
})
|
||||
class ChildComp {
|
||||
@Input() value: any;
|
||||
}
|
||||
|
||||
@Component({
|
||||
template: `<child-comp [value]="'' | token"></child-comp>`,
|
||||
viewProviders: [{provide: token, useValue: 'parent'}]
|
||||
})
|
||||
class App {
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [App, ChildComp, TokenPipe]});
|
||||
const fixture = TestBed.createComponent(App);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(fixture.nativeElement.textContent.trim()).toBe('parent');
|
||||
});
|
||||
|
||||
it('directives should access providers from the component they are on', () => {
|
||||
@Directive({selector: '[dir]'})
|
||||
class Dir {
|
||||
constructor(@Inject(token) public token: string) {}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'child-comp',
|
||||
template: '',
|
||||
providers: [{provide: token, useValue: 'child'}],
|
||||
})
|
||||
class ChildComp {
|
||||
}
|
||||
|
||||
@Component({
|
||||
template: '<child-comp dir></child-comp>',
|
||||
providers: [{provide: token, useValue: 'parent'}]
|
||||
})
|
||||
class App {
|
||||
@ViewChild(Dir) dir!: Dir;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [App, ChildComp, Dir]});
|
||||
const fixture = TestBed.createComponent(App);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(fixture.componentInstance.dir.token).toBe('child');
|
||||
});
|
||||
|
||||
it('directives should not access viewProviders from the component they are on', () => {
|
||||
@Directive({selector: '[dir]'})
|
||||
class Dir {
|
||||
constructor(@Inject(token) public token: string) {}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'child-comp',
|
||||
template: '',
|
||||
viewProviders: [{provide: token, useValue: 'child'}]
|
||||
})
|
||||
class ChildComp {
|
||||
}
|
||||
|
||||
@Component({
|
||||
template: '<child-comp dir></child-comp>',
|
||||
viewProviders: [{provide: token, useValue: 'parent'}]
|
||||
})
|
||||
class App {
|
||||
@ViewChild(Dir) dir!: Dir;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [App, ChildComp, Dir]});
|
||||
const fixture = TestBed.createComponent(App);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(fixture.componentInstance.dir.token).toBe('parent');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue