fix(core): Support extending differs from root `NgModule` (#39981)
Differs tries to inject parent differ in order to support extending. This does not work in the 'root' injector as the provider overrides the default injector. The fix is to just assume standard set of providers and extend those instead. PR close #25015 Issue close #11309 `Can't extend IterableDiffers` Issue close #18554 `IterableDiffers.extend is not AOT compatible` (This is fixed because we no longer have an arrow function in the factory but a proper function which can be imported.) PR Close #39981
This commit is contained in:
parent
775394c809
commit
5fc45082ca
|
@ -21,7 +21,7 @@
|
||||||
"master": {
|
"master": {
|
||||||
"uncompressed": {
|
"uncompressed": {
|
||||||
"runtime-es2015": 3153,
|
"runtime-es2015": 3153,
|
||||||
"main-es2015": 431696,
|
"main-es2015": 431137,
|
||||||
"polyfills-es2015": 52493
|
"polyfills-es2015": 52493
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
"master": {
|
"master": {
|
||||||
"uncompressed": {
|
"uncompressed": {
|
||||||
"runtime-es2015": 1485,
|
"runtime-es2015": 1485,
|
||||||
"main-es2015": 140921,
|
"main-es2015": 140333,
|
||||||
"polyfills-es2015": 36964
|
"polyfills-es2015": 36964
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -134,6 +134,10 @@ export interface IterableDifferFactory {
|
||||||
create<V>(trackByFn?: TrackByFunction<V>): IterableDiffer<V>;
|
create<V>(trackByFn?: TrackByFunction<V>): IterableDiffer<V>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function defaultIterableDiffersFactory() {
|
||||||
|
return new IterableDiffers([new DefaultIterableDifferFactory()]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A repository of different iterable diffing strategies used by NgFor, NgClass, and others.
|
* A repository of different iterable diffing strategies used by NgFor, NgClass, and others.
|
||||||
*
|
*
|
||||||
|
@ -141,11 +145,8 @@ export interface IterableDifferFactory {
|
||||||
*/
|
*/
|
||||||
export class IterableDiffers {
|
export class IterableDiffers {
|
||||||
/** @nocollapse */
|
/** @nocollapse */
|
||||||
static ɵprov = ɵɵdefineInjectable({
|
static ɵprov = ɵɵdefineInjectable(
|
||||||
token: IterableDiffers,
|
{token: IterableDiffers, providedIn: 'root', factory: defaultIterableDiffersFactory});
|
||||||
providedIn: 'root',
|
|
||||||
factory: () => new IterableDiffers([new DefaultIterableDifferFactory()])
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated v4.0.0 - Should be private
|
* @deprecated v4.0.0 - Should be private
|
||||||
|
@ -187,14 +188,11 @@ export class IterableDiffers {
|
||||||
static extend(factories: IterableDifferFactory[]): StaticProvider {
|
static extend(factories: IterableDifferFactory[]): StaticProvider {
|
||||||
return {
|
return {
|
||||||
provide: IterableDiffers,
|
provide: IterableDiffers,
|
||||||
useFactory: (parent: IterableDiffers) => {
|
useFactory: (parent: IterableDiffers|null) => {
|
||||||
if (!parent) {
|
// if parent is null, it means that we are in the root injector and we have just overridden
|
||||||
// Typically would occur when calling IterableDiffers.extend inside of dependencies passed
|
// the default injection mechanism for IterableDiffers, in such a case just assume
|
||||||
// to
|
// `defaultIterableDiffersFactory`.
|
||||||
// bootstrap(), which would override default pipes instead of extending them.
|
return IterableDiffers.create(factories, parent || defaultIterableDiffersFactory());
|
||||||
throw new Error('Cannot extend IterableDiffers without a parent injector');
|
|
||||||
}
|
|
||||||
return IterableDiffers.create(factories, parent);
|
|
||||||
},
|
},
|
||||||
// Dependency technically isn't optional, but we can provide a better error message this way.
|
// Dependency technically isn't optional, but we can provide a better error message this way.
|
||||||
deps: [[IterableDiffers, new SkipSelf(), new Optional()]]
|
deps: [[IterableDiffers, new SkipSelf(), new Optional()]]
|
||||||
|
|
|
@ -111,6 +111,10 @@ export interface KeyValueDifferFactory {
|
||||||
create<K, V>(): KeyValueDiffer<K, V>;
|
create<K, V>(): KeyValueDiffer<K, V>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function defaultKeyValueDiffersFactory() {
|
||||||
|
return new KeyValueDiffers([new DefaultKeyValueDifferFactory()]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A repository of different Map diffing strategies used by NgClass, NgStyle, and others.
|
* A repository of different Map diffing strategies used by NgClass, NgStyle, and others.
|
||||||
*
|
*
|
||||||
|
@ -118,11 +122,8 @@ export interface KeyValueDifferFactory {
|
||||||
*/
|
*/
|
||||||
export class KeyValueDiffers {
|
export class KeyValueDiffers {
|
||||||
/** @nocollapse */
|
/** @nocollapse */
|
||||||
static ɵprov = ɵɵdefineInjectable({
|
static ɵprov = ɵɵdefineInjectable(
|
||||||
token: KeyValueDiffers,
|
{token: KeyValueDiffers, providedIn: 'root', factory: defaultKeyValueDiffersFactory});
|
||||||
providedIn: 'root',
|
|
||||||
factory: () => new KeyValueDiffers([new DefaultKeyValueDifferFactory()])
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated v4.0.0 - Should be private.
|
* @deprecated v4.0.0 - Should be private.
|
||||||
|
@ -165,12 +166,10 @@ export class KeyValueDiffers {
|
||||||
return {
|
return {
|
||||||
provide: KeyValueDiffers,
|
provide: KeyValueDiffers,
|
||||||
useFactory: (parent: KeyValueDiffers) => {
|
useFactory: (parent: KeyValueDiffers) => {
|
||||||
if (!parent) {
|
// if parent is null, it means that we are in the root injector and we have just overridden
|
||||||
// Typically would occur when calling KeyValueDiffers.extend inside of dependencies passed
|
// the default injection mechanism for KeyValueDiffers, in such a case just assume
|
||||||
// to bootstrap(), which would override default pipes instead of extending them.
|
// `defaultKeyValueDiffersFactory`.
|
||||||
throw new Error('Cannot extend KeyValueDiffers without a parent injector');
|
return KeyValueDiffers.create(factories, parent || defaultKeyValueDiffersFactory());
|
||||||
}
|
|
||||||
return KeyValueDiffers.create(factories, parent);
|
|
||||||
},
|
},
|
||||||
// Dependency technically isn't optional, but we can provide a better error message this way.
|
// Dependency technically isn't optional, but we can provide a better error message this way.
|
||||||
deps: [[KeyValueDiffers, new SkipSelf(), new Optional()]]
|
deps: [[KeyValueDiffers, new SkipSelf(), new Optional()]]
|
||||||
|
|
|
@ -854,9 +854,15 @@
|
||||||
{
|
{
|
||||||
"name": "defaultIterableDiffers"
|
"name": "defaultIterableDiffers"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "defaultIterableDiffersFactory"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "defaultKeyValueDiffers"
|
"name": "defaultKeyValueDiffers"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "defaultKeyValueDiffersFactory"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "defaultScheduler"
|
"name": "defaultScheduler"
|
||||||
},
|
},
|
||||||
|
|
|
@ -1121,9 +1121,15 @@
|
||||||
{
|
{
|
||||||
"name": "defaultIterableDiffers"
|
"name": "defaultIterableDiffers"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "defaultIterableDiffersFactory"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "defaultKeyValueDiffers"
|
"name": "defaultKeyValueDiffers"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "defaultKeyValueDiffersFactory"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "defaultMalformedUriErrorHandler"
|
"name": "defaultMalformedUriErrorHandler"
|
||||||
},
|
},
|
||||||
|
|
|
@ -281,6 +281,9 @@
|
||||||
{
|
{
|
||||||
"name": "defaultErrorLogger"
|
"name": "defaultErrorLogger"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "defaultIterableDiffersFactory"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "defaultScheduler"
|
"name": "defaultScheduler"
|
||||||
},
|
},
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Injector} from '@angular/core';
|
import {Injector, IterableDiffer, IterableDifferFactory, IterableDiffers, NgModule, TrackByFunction} from '@angular/core';
|
||||||
import {IterableDiffers} from '@angular/core/src/change_detection/differs/iterable_differs';
|
import {TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
import {SpyIterableDifferFactory} from '../../spies';
|
import {SpyIterableDifferFactory} from '../../spies';
|
||||||
|
|
||||||
|
@ -49,13 +49,6 @@ import {SpyIterableDifferFactory} from '../../spies';
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.extend()', () => {
|
describe('.extend()', () => {
|
||||||
it('should throw if calling extend when creating root injector', () => {
|
|
||||||
const injector = Injector.create([IterableDiffers.extend([])]);
|
|
||||||
|
|
||||||
expect(() => injector.get(IterableDiffers))
|
|
||||||
.toThrowError(/Cannot extend IterableDiffers without a parent injector/);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should extend di-inherited differs', () => {
|
it('should extend di-inherited differs', () => {
|
||||||
const parent = new IterableDiffers([factory1]);
|
const parent = new IterableDiffers([factory1]);
|
||||||
const injector = Injector.create([{provide: IterableDiffers, useValue: parent}]);
|
const injector = Injector.create([{provide: IterableDiffers, useValue: parent}]);
|
||||||
|
@ -66,6 +59,32 @@ import {SpyIterableDifferFactory} from '../../spies';
|
||||||
factory2, factory1
|
factory2, factory1
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should support .extend in root NgModule', () => {
|
||||||
|
const DIFFER: IterableDiffer<any> = {} as any;
|
||||||
|
const log: string[] = [];
|
||||||
|
class MyIterableDifferFactory implements IterableDifferFactory {
|
||||||
|
supports(objects: any): boolean {
|
||||||
|
log.push('supports', objects);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
create<V>(trackByFn?: TrackByFunction<V>): IterableDiffer<V> {
|
||||||
|
log.push('create');
|
||||||
|
return DIFFER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@NgModule({providers: [IterableDiffers.extend([new MyIterableDifferFactory()])]})
|
||||||
|
class MyModule {
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({imports: [MyModule]});
|
||||||
|
const differs = TestBed.inject(IterableDiffers);
|
||||||
|
const differ = differs.find('VALUE').create(null!);
|
||||||
|
expect(differ).toEqual(DIFFER);
|
||||||
|
expect(log).toEqual(['supports', 'VALUE', 'create']);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google LLC All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {KeyValueDiffer, KeyValueDifferFactory, KeyValueDiffers, NgModule} from '@angular/core';
|
||||||
|
import {TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
|
|
||||||
|
describe('KeyValueDiffers', function() {
|
||||||
|
it('should support .extend in root NgModule', () => {
|
||||||
|
const DIFFER: KeyValueDiffer<any, any> = {} as any;
|
||||||
|
const log: string[] = [];
|
||||||
|
class MyKeyValueDifferFactory implements KeyValueDifferFactory {
|
||||||
|
supports(objects: any): boolean {
|
||||||
|
log.push('supports', objects);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
create<K, V>(): KeyValueDiffer<K, V> {
|
||||||
|
log.push('create');
|
||||||
|
return DIFFER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@NgModule({providers: [KeyValueDiffers.extend([new MyKeyValueDifferFactory()])]})
|
||||||
|
class MyModule {
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({imports: [MyModule]});
|
||||||
|
const differs = TestBed.inject(KeyValueDiffers);
|
||||||
|
const differ = differs.find('VALUE').create();
|
||||||
|
expect(differ).toEqual(DIFFER);
|
||||||
|
expect(log).toEqual(['supports', 'VALUE', 'create']);
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue