refactor(core): remove deprecated @Component.directives and @Component.pipes
BREAKING CHANGE: previously deprecated @Component.directives and @Component.pipes support was removed. All the components and pipes now must be declarated via an NgModule. NgModule is the basic compilation block passed into the Angular compiler via Compiler#compileModuleSync or #compileModuleAsync. Because of this change, the Compiler#compileComponentAsync and #compileComponentSync were removed as well - any code doing compilation should compile module instead using the apis mentioned above. Lastly, since modules are the basic compilation unit, the ngUpgrade module was modified to always require an NgModule to be passed into the UpgradeAdapter's constructor - previously this was optional.
This commit is contained in:
@ -304,11 +304,23 @@ export class PlatformRef_ extends PlatformRef {
|
||||
}
|
||||
|
||||
private _bootstrapModuleWithZone<M>(
|
||||
moduleType: Type<M>, compilerOptions: CompilerOptions|CompilerOptions[] = [],
|
||||
ngZone: NgZone): Promise<NgModuleRef<M>> {
|
||||
moduleType: Type<M>, compilerOptions: CompilerOptions|CompilerOptions[] = [], ngZone: NgZone,
|
||||
componentFactoryCallback?: any): Promise<NgModuleRef<M>> {
|
||||
const compilerFactory: CompilerFactory = this.injector.get(CompilerFactory);
|
||||
const compiler = compilerFactory.createCompiler(
|
||||
compilerOptions instanceof Array ? compilerOptions : [compilerOptions]);
|
||||
|
||||
// ugly internal api hack: generate host component factories for all declared components and
|
||||
// pass the factories into the callback - this is used by UpdateAdapter to get hold of all
|
||||
// factories.
|
||||
if (componentFactoryCallback) {
|
||||
return compiler.compileModuleAndAllComponentsAsync(moduleType)
|
||||
.then(({ngModuleFactory, componentFactories}) => {
|
||||
componentFactoryCallback(componentFactories);
|
||||
return this._bootstrapModuleFactoryWithZone(ngModuleFactory, ngZone);
|
||||
});
|
||||
}
|
||||
|
||||
return compiler.compileModuleAsync(moduleType)
|
||||
.then((moduleFactory) => this._bootstrapModuleFactoryWithZone(moduleFactory, ngZone));
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ function _throwError() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Low-level service for running the angular compiler duirng runtime
|
||||
* Low-level service for running the angular compiler during runtime
|
||||
* to create {@link ComponentFactory}s, which
|
||||
* can later be used to create and render a Component instance.
|
||||
*
|
||||
@ -55,20 +55,6 @@ function _throwError() {
|
||||
* @stable
|
||||
*/
|
||||
export class Compiler {
|
||||
/**
|
||||
* Loads the template and styles of a component and returns the associated `ComponentFactory`.
|
||||
*/
|
||||
compileComponentAsync<T>(component: Type<T>, ngModule: Type<any> = null):
|
||||
Promise<ComponentFactory<T>> {
|
||||
throw _throwError();
|
||||
}
|
||||
/**
|
||||
* Compiles the given component. All templates have to be either inline or compiled via
|
||||
* `compileComponentAsync` before. Otherwise throws a {@link ComponentStillLoadingError}.
|
||||
*/
|
||||
compileComponentSync<T>(component: Type<T>, ngModule: Type<any> = null): ComponentFactory<T> {
|
||||
throw _throwError();
|
||||
}
|
||||
/**
|
||||
* Compiles the given NgModule and all of its components. All templates of the components listed
|
||||
* in `entryComponents`
|
||||
|
@ -481,8 +481,7 @@ export class DirectiveMetadata extends InjectableMetadata implements DirectiveMe
|
||||
* selector: 'app',
|
||||
* template: `
|
||||
* <bank-account bank-name="RBC" account-id="4747"></bank-account>
|
||||
* `,
|
||||
* directives: [BankAccount]
|
||||
* `
|
||||
* })
|
||||
* class App {}
|
||||
* ```
|
||||
@ -525,8 +524,7 @@ export class DirectiveMetadata extends InjectableMetadata implements DirectiveMe
|
||||
* template: `
|
||||
* <interval-dir (everySecond)="everySecond()" (everyFiveSeconds)="everyFiveSeconds()">
|
||||
* </interval-dir>
|
||||
* `,
|
||||
* directives: [IntervalDir]
|
||||
* `
|
||||
* })
|
||||
* class App {
|
||||
* everySecond() { console.log('second'); }
|
||||
@ -578,8 +576,7 @@ export class DirectiveMetadata extends InjectableMetadata implements DirectiveMe
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'app',
|
||||
* template: `<button counting>Increment</button>`,
|
||||
* directives: [CountClicks]
|
||||
* template: `<button counting>Increment</button>`
|
||||
* })
|
||||
* class App {}
|
||||
* ```
|
||||
@ -612,8 +609,7 @@ export class DirectiveMetadata extends InjectableMetadata implements DirectiveMe
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'app',
|
||||
* template: `<input [(ngModel)]="prop">`,
|
||||
* directives: [FORM_DIRECTIVES, NgModelStatus]
|
||||
* template: `<input [(ngModel)]="prop">`
|
||||
* })
|
||||
* class App {
|
||||
* prop;
|
||||
@ -690,8 +686,7 @@ export class DirectiveMetadata extends InjectableMetadata implements DirectiveMe
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'main',
|
||||
* template: `<child-dir #c="child"></child-dir>`,
|
||||
* directives: [ChildDir]
|
||||
* template: `<child-dir #c="child"></child-dir>`
|
||||
* })
|
||||
* class MainComponent {
|
||||
* }
|
||||
@ -716,8 +711,7 @@ export class DirectiveMetadata extends InjectableMetadata implements DirectiveMe
|
||||
* contentChildren: new ContentChildren(ChildDirective),
|
||||
* viewChildren: new ViewChildren(ChildDirective)
|
||||
* },
|
||||
* template: '<child-directive></child-directive>',
|
||||
* directives: [ChildDirective]
|
||||
* template: '<child-directive></child-directive>'
|
||||
* })
|
||||
* class SomeDir {
|
||||
* contentChildren: QueryList<ChildDirective>,
|
||||
@ -761,8 +755,6 @@ export interface ComponentMetadataType extends DirectiveMetadataType {
|
||||
styleUrls?: string[];
|
||||
styles?: string[];
|
||||
animations?: AnimationEntryMetadata[];
|
||||
directives?: Array<Type<any>|any[]>;
|
||||
pipes?: Array<Type<any>|any[]>;
|
||||
encapsulation?: ViewEncapsulation;
|
||||
interpolation?: [string, string];
|
||||
entryComponents?: Array<Type<any>|any[]>;
|
||||
@ -836,8 +828,7 @@ export class ComponentMetadata extends DirectiveMetadata implements ComponentMet
|
||||
* viewProviders: [
|
||||
* Greeter
|
||||
* ],
|
||||
* template: `<needs-greeter></needs-greeter>`,
|
||||
* directives: [NeedsGreeter]
|
||||
* template: `<needs-greeter></needs-greeter>`
|
||||
* })
|
||||
* class HelloWorld {
|
||||
* }
|
||||
@ -969,10 +960,6 @@ export class ComponentMetadata extends DirectiveMetadata implements ComponentMet
|
||||
*/
|
||||
animations: AnimationEntryMetadata[];
|
||||
|
||||
directives: Array<Type<any>|any[]>;
|
||||
|
||||
pipes: Array<Type<any>|any[]>;
|
||||
|
||||
/**
|
||||
* Specify how the template and the styles should be encapsulated.
|
||||
* The default is {@link ViewEncapsulation#Emulated `ViewEncapsulation.Emulated`} if the view
|
||||
@ -991,13 +978,11 @@ export class ComponentMetadata extends DirectiveMetadata implements ComponentMet
|
||||
*/
|
||||
entryComponents: Array<Type<any>|any[]>;
|
||||
|
||||
constructor({selector, inputs, outputs,
|
||||
host, exportAs, moduleId,
|
||||
providers, viewProviders, changeDetection = ChangeDetectionStrategy.Default,
|
||||
queries, templateUrl, template,
|
||||
styleUrls, styles, animations,
|
||||
directives, pipes, encapsulation,
|
||||
interpolation, entryComponents}: ComponentMetadataType = {}) {
|
||||
constructor(
|
||||
{selector, inputs, outputs, host, exportAs, moduleId, providers, viewProviders,
|
||||
changeDetection = ChangeDetectionStrategy.Default, queries, templateUrl, template, styleUrls,
|
||||
styles, animations, encapsulation, interpolation,
|
||||
entryComponents}: ComponentMetadataType = {}) {
|
||||
super({
|
||||
selector: selector,
|
||||
inputs: inputs,
|
||||
@ -1014,8 +999,6 @@ export class ComponentMetadata extends DirectiveMetadata implements ComponentMet
|
||||
this.template = template;
|
||||
this.styleUrls = styleUrls;
|
||||
this.styles = styles;
|
||||
this.directives = directives;
|
||||
this.pipes = pipes;
|
||||
this.encapsulation = encapsulation;
|
||||
this.moduleId = moduleId;
|
||||
this.animations = animations;
|
||||
@ -1092,9 +1075,9 @@ export class PipeMetadata extends InjectableMetadata implements PipeMetadataType
|
||||
* selector: 'app',
|
||||
* template: `
|
||||
* <bank-account bank-name="RBC" account-id="4747"></bank-account>
|
||||
* `,
|
||||
* directives: [BankAccount]
|
||||
* `
|
||||
* })
|
||||
*
|
||||
* class App {}
|
||||
* ```
|
||||
* @stable
|
||||
@ -1138,8 +1121,7 @@ export class InputMetadata {
|
||||
* template: `
|
||||
* <interval-dir (everySecond)="everySecond()" (everyFiveSeconds)="everyFiveSeconds()">
|
||||
* </interval-dir>
|
||||
* `,
|
||||
* directives: [IntervalDir]
|
||||
* `
|
||||
* })
|
||||
* class App {
|
||||
* everySecond() { console.log('second'); }
|
||||
@ -1177,8 +1159,7 @@ export class OutputMetadata {
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'app',
|
||||
* template: `<input [(ngModel)]="prop">`,
|
||||
* directives: [FORM_DIRECTIVES, NgModelStatus]
|
||||
* template: `<input [(ngModel)]="prop">`
|
||||
* })
|
||||
* class App {
|
||||
* prop;
|
||||
@ -1216,8 +1197,7 @@ export class HostBindingMetadata {
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'app',
|
||||
* template: `<button counting>Increment</button>`,
|
||||
* directives: [CountClicks]
|
||||
* template: `<button counting>Increment</button>`
|
||||
* })
|
||||
* class App {}
|
||||
* ```
|
||||
|
@ -103,30 +103,6 @@ export class ViewMetadata {
|
||||
*/
|
||||
styles: string[];
|
||||
|
||||
/**
|
||||
* Specifies a list of directives that can be used within a template.
|
||||
*
|
||||
* Directives must be listed explicitly to provide proper component encapsulation.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```javascript
|
||||
* @Component({
|
||||
* selector: 'my-component',
|
||||
* directives: [NgFor]
|
||||
* template: '
|
||||
* <ul>
|
||||
* <li *ngFor="let item of items">{{item}}</li>
|
||||
* </ul>'
|
||||
* })
|
||||
* class MyComponent {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
directives: Array<Type<any>|any[]>;
|
||||
|
||||
pipes: Array<Type<any>|any[]>;
|
||||
|
||||
/**
|
||||
* Specify how the template and the styles should be encapsulated.
|
||||
* The default is {@link ViewEncapsulation#Emulated `ViewEncapsulation.Emulated`} if the view
|
||||
@ -140,12 +116,9 @@ export class ViewMetadata {
|
||||
interpolation: [string, string];
|
||||
|
||||
constructor(
|
||||
{templateUrl, template, directives, pipes, encapsulation, styles, styleUrls, animations,
|
||||
interpolation}: {
|
||||
{templateUrl, template, encapsulation, styles, styleUrls, animations, interpolation}: {
|
||||
templateUrl?: string,
|
||||
template?: string,
|
||||
directives?: Array<Type<any>|any[]>,
|
||||
pipes?: Array<Type<any>|any[]>,
|
||||
encapsulation?: ViewEncapsulation,
|
||||
styles?: string[],
|
||||
styleUrls?: string[],
|
||||
@ -156,8 +129,6 @@ export class ViewMetadata {
|
||||
this.template = template;
|
||||
this.styleUrls = styleUrls;
|
||||
this.styles = styles;
|
||||
this.directives = directives;
|
||||
this.pipes = pipes;
|
||||
this.encapsulation = encapsulation;
|
||||
this.animations = animations;
|
||||
this.interpolation = interpolation;
|
||||
|
@ -8,8 +8,7 @@
|
||||
|
||||
import {ANALYZE_FOR_ENTRY_COMPONENTS, CUSTOM_ELEMENTS_SCHEMA, Compiler, Component, ComponentFactoryResolver, Directive, HostBinding, Inject, Injectable, Injector, Input, NgModule, NgModuleRef, Optional, Pipe, SelfMetadata, Type, forwardRef} from '@angular/core';
|
||||
import {Console} from '@angular/core/src/console';
|
||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
import {AsyncTestCompleter, beforeEach, beforeEachProviders, ddescribe, describe, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal';
|
||||
import {ComponentFixture, TestBed, inject} from '@angular/core/testing';
|
||||
import {expect} from '@angular/platform-browser/testing/matchers';
|
||||
|
||||
import {BaseException} from '../../src/facade/exceptions';
|
||||
@ -263,20 +262,35 @@ function declareTests({useJit}: {useJit: boolean}) {
|
||||
.toBe(SomeComp);
|
||||
});
|
||||
|
||||
it('should throw when using an entryComponent that was neither declared nor imported', () => {
|
||||
@Component({template: '', entryComponents: [SomeComp]})
|
||||
it('should throw if we cannot find a module associated with a module-level entryComponent', () => {
|
||||
@Component({template: ''})
|
||||
class SomeCompWithEntryComponents {
|
||||
}
|
||||
|
||||
@NgModule({entryComponents: [SomeCompWithEntryComponents]})
|
||||
@NgModule({declarations: [], entryComponents: [SomeCompWithEntryComponents]})
|
||||
class SomeModule {
|
||||
}
|
||||
|
||||
expect(() => createModule(SomeModule))
|
||||
.toThrowError(
|
||||
`NgModule ${stringify(SomeModule)} uses ${stringify(SomeCompWithEntryComponents)} via "entryComponents" but it was neither declared nor imported! If ${stringify(SomeCompWithEntryComponents)} is declared in an imported module, make sure it is exported.`);
|
||||
'Component SomeCompWithEntryComponents is not part of any NgModule or the module has not been imported into your module.');
|
||||
});
|
||||
|
||||
it('should throw if we cannot find a module associated with a component-level entryComponent',
|
||||
() => {
|
||||
@Component({template: '', entryComponents: [SomeComp]})
|
||||
class SomeCompWithEntryComponents {
|
||||
}
|
||||
|
||||
@NgModule({declarations: [SomeCompWithEntryComponents]})
|
||||
class SomeModule {
|
||||
}
|
||||
|
||||
expect(() => createModule(SomeModule))
|
||||
.toThrowError(
|
||||
'Component SomeComp is not part of any NgModule or the module has not been imported into your module.');
|
||||
});
|
||||
|
||||
it('should create ComponentFactories via ANALYZE_FOR_ENTRY_COMPONENTS', () => {
|
||||
@NgModule({
|
||||
declarations: [SomeComp],
|
||||
@ -417,61 +431,6 @@ function declareTests({useJit}: {useJit: boolean}) {
|
||||
expect(compFixture.debugElement.children[0].children[0].properties['title'])
|
||||
.toBe('transformed someValue');
|
||||
});
|
||||
|
||||
it('should hoist @Component.directives/pipes into the module', () => {
|
||||
@Component({
|
||||
selector: 'parent',
|
||||
template: '<comp></comp>',
|
||||
directives: [CompUsingModuleDirectiveAndPipe, SomeDirective],
|
||||
pipes: [SomePipe]
|
||||
})
|
||||
class ParentCompUsingModuleDirectiveAndPipe {
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [ParentCompUsingModuleDirectiveAndPipe],
|
||||
entryComponents: [ParentCompUsingModuleDirectiveAndPipe]
|
||||
})
|
||||
class SomeModule {
|
||||
}
|
||||
|
||||
const compFixture = createComp(ParentCompUsingModuleDirectiveAndPipe, SomeModule);
|
||||
compFixture.detectChanges();
|
||||
expect(compFixture.debugElement.children[0].children[0].properties['title'])
|
||||
.toBe('transformed someValue');
|
||||
});
|
||||
|
||||
it('should allow to use directives/pipes via @Component.directives/pipes that were already imported from another module',
|
||||
() => {
|
||||
@Component({
|
||||
selector: 'parent',
|
||||
template: '<comp></comp>',
|
||||
directives: [CompUsingModuleDirectiveAndPipe, SomeDirective],
|
||||
pipes: [SomePipe]
|
||||
})
|
||||
class ParentCompUsingModuleDirectiveAndPipe {
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [SomeDirective, SomePipe, CompUsingModuleDirectiveAndPipe],
|
||||
exports: [SomeDirective, SomePipe, CompUsingModuleDirectiveAndPipe]
|
||||
})
|
||||
class SomeImportedModule {
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [ParentCompUsingModuleDirectiveAndPipe],
|
||||
imports: [SomeImportedModule],
|
||||
entryComponents: [ParentCompUsingModuleDirectiveAndPipe]
|
||||
})
|
||||
class SomeModule {
|
||||
}
|
||||
|
||||
const compFixture = createComp(ParentCompUsingModuleDirectiveAndPipe, SomeModule);
|
||||
compFixture.detectChanges();
|
||||
expect(compFixture.debugElement.children[0].children[0].properties['title'])
|
||||
.toBe('transformed someValue');
|
||||
});
|
||||
});
|
||||
|
||||
describe('import/export', () => {
|
||||
@ -603,24 +562,6 @@ function declareTests({useJit}: {useJit: boolean}) {
|
||||
});
|
||||
});
|
||||
|
||||
describe('bound compiler', () => {
|
||||
it('should provide a Compiler instance that uses the directives/pipes of the module', () => {
|
||||
@NgModule({declarations: [SomeDirective, SomePipe]})
|
||||
class SomeModule {
|
||||
}
|
||||
|
||||
const ngModule = createModule(SomeModule);
|
||||
const boundCompiler: Compiler = ngModule.injector.get(Compiler);
|
||||
const cf = boundCompiler.compileComponentSync(CompUsingModuleDirectiveAndPipe);
|
||||
const compFixture = new ComponentFixture(cf.create(injector), null, false);
|
||||
compFixture.detectChanges();
|
||||
expect(compFixture.debugElement.children[0].properties['title'])
|
||||
.toBe('transformed someValue');
|
||||
|
||||
// compile again should produce the same result
|
||||
expect(boundCompiler.compileComponentSync(CompUsingModuleDirectiveAndPipe)).toBe(cf);
|
||||
});
|
||||
});
|
||||
|
||||
describe('providers', function() {
|
||||
let moduleType: any = null;
|
||||
|
Reference in New Issue
Block a user