refactor(core): remove `ViewResolver` and `ViewResolverMock`

The methods on `ViewResolverMock` have been merged into `DirectiveResolver`.

BREAKING CHANGE:
- ES5 users can no longer use the `View(…)` function to provide `ViewMetadata`.
  This mirrors the removal of the `@View` decorator a while ago.
This commit is contained in:
Tobias Bosch 2016-07-28 06:31:26 -07:00
parent 20b03bad11
commit 0988cc82b0
25 changed files with 308 additions and 535 deletions

View File

@ -143,8 +143,7 @@ export class CodeGenerator {
const resolver = new CompileMetadataResolver( const resolver = new CompileMetadataResolver(
new compiler.NgModuleResolver(staticReflector), new compiler.NgModuleResolver(staticReflector),
new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector), new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector),
new compiler.ViewResolver(staticReflector), config, console, elementSchemaRegistry, config, console, elementSchemaRegistry, staticReflector);
staticReflector);
const offlineCompiler = new compiler.OfflineCompiler( const offlineCompiler = new compiler.OfflineCompiler(
resolver, normalizer, tmplParser, new StyleCompiler(urlResolver), new ViewCompiler(config), resolver, normalizer, tmplParser, new StyleCompiler(urlResolver), new ViewCompiler(config),
new NgModuleCompiler(), new TypeScriptEmitter(reflectorHost)); new NgModuleCompiler(), new TypeScriptEmitter(reflectorHost));

View File

@ -152,8 +152,7 @@ class Extractor {
const resolver = new CompileMetadataResolver( const resolver = new CompileMetadataResolver(
new compiler.NgModuleResolver(staticReflector), new compiler.NgModuleResolver(staticReflector),
new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector), new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector),
new compiler.ViewResolver(staticReflector), config, console, elementSchemaRegistry, config, console, elementSchemaRegistry, staticReflector);
staticReflector);
// TODO(vicb): handle implicit // TODO(vicb): handle implicit
const extractor = new MessageExtractor(htmlParser, expressionParser, [], {}); const extractor = new MessageExtractor(htmlParser, expressionParser, [], {});

View File

@ -11,7 +11,7 @@
* @description * @description
* Starting point to import all compiler APIs. * Starting point to import all compiler APIs.
*/ */
export {COMPILER_PROVIDERS, CompileDiDependencyMetadata, CompileDirectiveMetadata, CompileFactoryMetadata, CompileIdentifierMetadata, CompileMetadataWithIdentifier, CompilePipeMetadata, CompileProviderMetadata, CompileQueryMetadata, CompileTemplateMetadata, CompileTokenMetadata, CompileTypeMetadata, CompilerConfig, DEFAULT_PACKAGE_URL_PROVIDER, DirectiveResolver, NgModuleResolver, OfflineCompiler, PipeResolver, RenderTypes, RuntimeCompiler, SourceModule, TEMPLATE_TRANSFORMS, UrlResolver, ViewResolver, XHR, analyzeAppProvidersForDeprecatedConfiguration, createOfflineCompileUrlResolver, platformCoreDynamic} from './src/compiler'; export {COMPILER_PROVIDERS, CompileDiDependencyMetadata, CompileDirectiveMetadata, CompileFactoryMetadata, CompileIdentifierMetadata, CompileMetadataWithIdentifier, CompilePipeMetadata, CompileProviderMetadata, CompileQueryMetadata, CompileTemplateMetadata, CompileTokenMetadata, CompileTypeMetadata, CompilerConfig, DEFAULT_PACKAGE_URL_PROVIDER, DirectiveResolver, NgModuleResolver, OfflineCompiler, PipeResolver, RenderTypes, RuntimeCompiler, SourceModule, TEMPLATE_TRANSFORMS, UrlResolver, XHR, analyzeAppProvidersForDeprecatedConfiguration, createOfflineCompileUrlResolver, platformCoreDynamic} from './src/compiler';
export {ElementSchemaRegistry} from './src/schema/element_schema_registry'; export {ElementSchemaRegistry} from './src/schema/element_schema_registry';
export * from './src/template_ast'; export * from './src/template_ast';

View File

@ -406,7 +406,8 @@ export class CompileTemplateMetadata {
export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier { export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
static create( static create(
{type, isComponent, selector, exportAs, changeDetection, inputs, outputs, host, {type, isComponent, selector, exportAs, changeDetection, inputs, outputs, host,
lifecycleHooks, providers, viewProviders, queries, viewQueries, entryComponents, template}: { lifecycleHooks, providers, viewProviders, queries, viewQueries, entryComponents,
viewDirectives, viewPipes, template}: {
type?: CompileTypeMetadata, type?: CompileTypeMetadata,
isComponent?: boolean, isComponent?: boolean,
selector?: string, selector?: string,
@ -423,6 +424,8 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
queries?: CompileQueryMetadata[], queries?: CompileQueryMetadata[],
viewQueries?: CompileQueryMetadata[], viewQueries?: CompileQueryMetadata[],
entryComponents?: CompileTypeMetadata[], entryComponents?: CompileTypeMetadata[],
viewDirectives?: CompileTypeMetadata[],
viewPipes?: CompileTypeMetadata[],
template?: CompileTemplateMetadata template?: CompileTemplateMetadata
} = {}): CompileDirectiveMetadata { } = {}): CompileDirectiveMetadata {
var hostListeners: {[key: string]: string} = {}; var hostListeners: {[key: string]: string} = {};
@ -472,6 +475,8 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
queries, queries,
viewQueries, viewQueries,
entryComponents, entryComponents,
viewDirectives,
viewPipes,
template, template,
}); });
} }
@ -492,12 +497,17 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
viewQueries: CompileQueryMetadata[]; viewQueries: CompileQueryMetadata[];
// Note: Need to keep types here to prevent cycles! // Note: Need to keep types here to prevent cycles!
entryComponents: CompileTypeMetadata[]; entryComponents: CompileTypeMetadata[];
// Note: Need to keep types here to prevent cycles!
viewDirectives: CompileTypeMetadata[];
// Note: Need to keep types here to prevent cycles!
viewPipes: CompileTypeMetadata[];
template: CompileTemplateMetadata; template: CompileTemplateMetadata;
constructor( constructor(
{type, isComponent, selector, exportAs, changeDetection, inputs, outputs, hostListeners, {type, isComponent, selector, exportAs, changeDetection, inputs, outputs, hostListeners,
hostProperties, hostAttributes, lifecycleHooks, providers, viewProviders, queries, hostProperties, hostAttributes, lifecycleHooks, providers, viewProviders, queries,
viewQueries, entryComponents, template}: { viewQueries, entryComponents, viewDirectives, viewPipes, template}: {
type?: CompileTypeMetadata, type?: CompileTypeMetadata,
isComponent?: boolean, isComponent?: boolean,
selector?: string, selector?: string,
@ -516,6 +526,8 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
queries?: CompileQueryMetadata[], queries?: CompileQueryMetadata[],
viewQueries?: CompileQueryMetadata[], viewQueries?: CompileQueryMetadata[],
entryComponents?: CompileTypeMetadata[], entryComponents?: CompileTypeMetadata[],
viewDirectives?: CompileTypeMetadata[],
viewPipes?: CompileTypeMetadata[],
template?: CompileTemplateMetadata, template?: CompileTemplateMetadata,
} = {}) { } = {}) {
this.type = type; this.type = type;
@ -534,6 +546,9 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
this.queries = _normalizeArray(queries); this.queries = _normalizeArray(queries);
this.viewQueries = _normalizeArray(viewQueries); this.viewQueries = _normalizeArray(viewQueries);
this.entryComponents = _normalizeArray(entryComponents); this.entryComponents = _normalizeArray(entryComponents);
this.viewDirectives = _normalizeArray(viewDirectives);
this.viewPipes = _normalizeArray(viewPipes);
this.template = template; this.template = template;
} }

View File

@ -17,7 +17,6 @@ export {RuntimeCompiler} from './runtime_compiler';
export * from './url_resolver'; export * from './url_resolver';
export * from './xhr'; export * from './xhr';
export {ViewResolver} from './view_resolver';
export {DirectiveResolver} from './directive_resolver'; export {DirectiveResolver} from './directive_resolver';
export {PipeResolver} from './pipe_resolver'; export {PipeResolver} from './pipe_resolver';
export {NgModuleResolver} from './ng_module_resolver'; export {NgModuleResolver} from './ng_module_resolver';
@ -38,7 +37,6 @@ import {DomElementSchemaRegistry} from './schema/dom_element_schema_registry';
import {UrlResolver, DEFAULT_PACKAGE_URL_PROVIDER} from './url_resolver'; import {UrlResolver, DEFAULT_PACKAGE_URL_PROVIDER} from './url_resolver';
import {Parser} from './expression_parser/parser'; import {Parser} from './expression_parser/parser';
import {Lexer} from './expression_parser/lexer'; import {Lexer} from './expression_parser/lexer';
import {ViewResolver} from './view_resolver';
import {DirectiveResolver} from './directive_resolver'; import {DirectiveResolver} from './directive_resolver';
import {PipeResolver} from './pipe_resolver'; import {PipeResolver} from './pipe_resolver';
import {NgModuleResolver} from './ng_module_resolver'; import {NgModuleResolver} from './ng_module_resolver';
@ -76,7 +74,6 @@ export const COMPILER_PROVIDERS: Array<any|Type|{[k: string]: any}|any[]> =
DomElementSchemaRegistry, DomElementSchemaRegistry,
/*@ts2dart_Provider*/ {provide: ElementSchemaRegistry, useExisting: DomElementSchemaRegistry}, /*@ts2dart_Provider*/ {provide: ElementSchemaRegistry, useExisting: DomElementSchemaRegistry},
UrlResolver, UrlResolver,
ViewResolver,
DirectiveResolver, DirectiveResolver,
PipeResolver, PipeResolver,
NgModuleResolver NgModuleResolver

View File

@ -143,7 +143,16 @@ export class DirectiveResolver {
changeDetection: dm.changeDetection, changeDetection: dm.changeDetection,
providers: dm.providers, providers: dm.providers,
viewProviders: dm.viewProviders, viewProviders: dm.viewProviders,
entryComponents: dm.entryComponents entryComponents: dm.entryComponents,
directives: dm.directives,
pipes: dm.pipes,
template: dm.template,
templateUrl: dm.templateUrl,
styles: dm.styles,
styleUrls: dm.styleUrls,
encapsulation: dm.encapsulation,
animations: dm.animations,
interpolation: dm.interpolation
}); });
} else { } else {

View File

@ -24,7 +24,6 @@ import {PipeResolver} from './pipe_resolver';
import {ElementSchemaRegistry} from './schema/element_schema_registry'; import {ElementSchemaRegistry} from './schema/element_schema_registry';
import {getUrlScheme} from './url_resolver'; import {getUrlScheme} from './url_resolver';
import {MODULE_SUFFIX, ValueTransformer, sanitizeIdentifier, visitValue} from './util'; import {MODULE_SUFFIX, ValueTransformer, sanitizeIdentifier, visitValue} from './util';
import {ViewResolver} from './view_resolver';
@Injectable() @Injectable()
export class CompileMetadataResolver { export class CompileMetadataResolver {
@ -37,10 +36,8 @@ export class CompileMetadataResolver {
constructor( constructor(
private _ngModuleResolver: NgModuleResolver, private _directiveResolver: DirectiveResolver, private _ngModuleResolver: NgModuleResolver, private _directiveResolver: DirectiveResolver,
private _pipeResolver: PipeResolver, private _viewResolver: ViewResolver, private _pipeResolver: PipeResolver, private _config: CompilerConfig,
private _config: CompilerConfig, private _console: Console, private _console: Console, private _schemaRegistry: ElementSchemaRegistry, private _reflector: ReflectorReader = reflector) {}
private _schemaRegistry: ElementSchemaRegistry,
private _reflector: ReflectorReader = reflector) {}
private sanitizeTokenName(token: any): string { private sanitizeTokenName(token: any): string {
let identifier = stringify(token); let identifier = stringify(token);
@ -125,27 +122,28 @@ export class CompileMetadataResolver {
var changeDetectionStrategy: ChangeDetectionStrategy = null; var changeDetectionStrategy: ChangeDetectionStrategy = null;
var viewProviders: Array<cpl.CompileProviderMetadata|cpl.CompileTypeMetadata|any[]> = []; var viewProviders: Array<cpl.CompileProviderMetadata|cpl.CompileTypeMetadata|any[]> = [];
var moduleUrl = staticTypeModuleUrl(directiveType); var moduleUrl = staticTypeModuleUrl(directiveType);
var viewDirectiveTypes: cpl.CompileTypeMetadata[] = [];
var viewPipeTypes: cpl.CompileTypeMetadata[] = [];
var entryComponentTypes: cpl.CompileTypeMetadata[] = []; var entryComponentTypes: cpl.CompileTypeMetadata[] = [];
let selector = dirMeta.selector; let selector = dirMeta.selector;
if (dirMeta instanceof ComponentMetadata) { if (dirMeta instanceof ComponentMetadata) {
var cmpMeta = <ComponentMetadata>dirMeta; var cmpMeta = <ComponentMetadata>dirMeta;
var viewMeta = this._viewResolver.resolve(directiveType); assertArrayOfStrings('styles', cmpMeta.styles);
assertArrayOfStrings('styles', viewMeta.styles); assertInterpolationSymbols('interpolation', cmpMeta.interpolation);
assertInterpolationSymbols('interpolation', viewMeta.interpolation); var animations = isPresent(cmpMeta.animations) ?
var animations = isPresent(viewMeta.animations) ? cmpMeta.animations.map(e => this.getAnimationEntryMetadata(e)) :
viewMeta.animations.map(e => this.getAnimationEntryMetadata(e)) :
null; null;
assertArrayOfStrings('styles', viewMeta.styles); assertArrayOfStrings('styles', cmpMeta.styles);
assertArrayOfStrings('styleUrls', viewMeta.styleUrls); assertArrayOfStrings('styleUrls', cmpMeta.styleUrls);
templateMeta = new cpl.CompileTemplateMetadata({ templateMeta = new cpl.CompileTemplateMetadata({
encapsulation: viewMeta.encapsulation, encapsulation: cmpMeta.encapsulation,
template: viewMeta.template, template: cmpMeta.template,
templateUrl: viewMeta.templateUrl, templateUrl: cmpMeta.templateUrl,
styles: viewMeta.styles, styles: cmpMeta.styles,
styleUrls: viewMeta.styleUrls, styleUrls: cmpMeta.styleUrls,
animations: animations, animations: animations,
interpolation: viewMeta.interpolation interpolation: cmpMeta.interpolation
}); });
changeDetectionStrategy = cmpMeta.changeDetection; changeDetectionStrategy = cmpMeta.changeDetection;
if (isPresent(dirMeta.viewProviders)) { if (isPresent(dirMeta.viewProviders)) {
@ -156,7 +154,26 @@ export class CompileMetadataResolver {
if (cmpMeta.entryComponents) { if (cmpMeta.entryComponents) {
entryComponentTypes = entryComponentTypes =
flattenArray(cmpMeta.entryComponents) flattenArray(cmpMeta.entryComponents)
.map((cmp) => this.getTypeMetadata(cmp, staticTypeModuleUrl(cmp))); .map((type) => this.getTypeMetadata(type, staticTypeModuleUrl(type)));
}
if (cmpMeta.directives) {
viewDirectiveTypes = flattenArray(cmpMeta.directives).map((type) => {
if (!type) {
throw new BaseException(
`Unexpected directive value '${type}' on the View of component '${stringify(directiveType)}'`);
}
return this.getTypeMetadata(type, staticTypeModuleUrl(type));
});
}
if (cmpMeta.pipes) {
viewPipeTypes = flattenArray(cmpMeta.pipes).map((type) => {
if (!type) {
throw new BaseException(
`Unexpected pipe value '${type}' on the View of component '${stringify(directiveType)}'`);
}
return this.getTypeMetadata(type, staticTypeModuleUrl(type));
});
} }
if (!selector) { if (!selector) {
selector = this._schemaRegistry.getDefaultComponentElementName(); selector = this._schemaRegistry.getDefaultComponentElementName();
@ -196,6 +213,8 @@ export class CompileMetadataResolver {
viewProviders: viewProviders, viewProviders: viewProviders,
queries: queries, queries: queries,
viewQueries: viewQueries, viewQueries: viewQueries,
viewDirectives: viewDirectiveTypes,
viewPipes: viewPipeTypes,
entryComponents: entryComponentTypes entryComponents: entryComponentTypes
}); });
this._directiveCache.set(directiveType, meta); this._directiveCache.set(directiveType, meta);
@ -386,20 +405,12 @@ export class CompileMetadataResolver {
return; return;
} }
const addPipe = (pipeType: Type) => { const addPipe = (pipeType: Type) => {
if (!pipeType) {
throw new BaseException(
`Unexpected pipe value '${pipeType}' on the View of component '${stringify(compMeta.type.runtime)}'`);
}
const pipeMeta = this.getPipeMetadata(pipeType); const pipeMeta = this.getPipeMetadata(pipeType);
this._addPipeToModule( this._addPipeToModule(
pipeMeta, moduleMeta.type.runtime, moduleMeta.transitiveModule, moduleMeta.declaredPipes); pipeMeta, moduleMeta.type.runtime, moduleMeta.transitiveModule, moduleMeta.declaredPipes);
}; };
const addDirective = (dirType: Type) => { const addDirective = (dirType: Type) => {
if (!dirType) {
throw new BaseException(
`Unexpected directive value '${dirType}' on the View of component '${stringify(compMeta.type.runtime)}'`);
}
const dirMeta = this.getDirectiveMetadata(dirType); const dirMeta = this.getDirectiveMetadata(dirType);
if (this._addDirectiveToModule( if (this._addDirectiveToModule(
dirMeta, moduleMeta.type.runtime, moduleMeta.transitiveModule, dirMeta, moduleMeta.type.runtime, moduleMeta.transitiveModule,
@ -407,12 +418,11 @@ export class CompileMetadataResolver {
this._getTransitiveViewDirectivesAndPipes(dirMeta, moduleMeta); this._getTransitiveViewDirectivesAndPipes(dirMeta, moduleMeta);
} }
}; };
const view = this._viewResolver.resolve(compMeta.type.runtime); if (compMeta.viewPipes) {
if (view.pipes) { compMeta.viewPipes.forEach((cplType) => addPipe(cplType.runtime));
flattenArray(view.pipes).forEach(addPipe);
} }
if (view.directives) { if (compMeta.viewDirectives) {
flattenArray(view.directives).forEach(addDirective); compMeta.viewDirectives.forEach((cplType) => addDirective(cplType.runtime));
} }
compMeta.entryComponents.forEach((entryComponentType) => { compMeta.entryComponents.forEach((entryComponentType) => {
if (!moduleMeta.transitiveModule.directivesSet.has(entryComponentType.runtime)) { if (!moduleMeta.transitiveModule.directivesSet.has(entryComponentType.runtime)) {

View File

@ -1,55 +0,0 @@
/**
* @license
* Copyright Google Inc. 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 {Injectable, ViewMetadata, ComponentMetadata,} from '@angular/core';
import {ReflectorReader, reflector} from '../core_private';
import {Type, stringify, isBlank, isPresent} from '../src/facade/lang';
import {BaseException} from '../src/facade/exceptions';
function _isComponentMetadata(obj: any): obj is ComponentMetadata {
return obj instanceof ComponentMetadata;
}
/**
* Resolves types to {@link ViewMetadata}.
*/
@Injectable()
export class ViewResolver {
constructor(private _reflector: ReflectorReader = reflector) {}
resolve(component: Type, throwIfNotFound = true): ViewMetadata {
const compMeta: ComponentMetadata =
this._reflector.annotations(component).find(_isComponentMetadata);
if (isPresent(compMeta)) {
if (isBlank(compMeta.template) && isBlank(compMeta.templateUrl)) {
throw new BaseException(
`Component '${stringify(component)}' must have either 'template' or 'templateUrl' set.`);
} else {
return new ViewMetadata({
templateUrl: compMeta.templateUrl,
template: compMeta.template,
directives: compMeta.directives,
pipes: compMeta.pipes,
encapsulation: compMeta.encapsulation,
styles: compMeta.styles,
styleUrls: compMeta.styleUrls,
animations: compMeta.animations,
interpolation: compMeta.interpolation
});
}
} else {
if (throwIfNotFound) {
throw new BaseException(
`Could not compile '${stringify(component)}' because it is not a component.`);
}
return null;
}
}
}

View File

@ -9,34 +9,35 @@
import {beforeEach, ddescribe, describe, expect, iit, it, inject,} from '@angular/core/testing/testing_internal'; import {beforeEach, ddescribe, describe, expect, iit, it, inject,} from '@angular/core/testing/testing_internal';
import {stringify, isBlank} from '../src/facade/lang'; import {stringify, isBlank} from '../src/facade/lang';
import {MockViewResolver} from '../testing'; import {MockDirectiveResolver} from '../testing';
import {Component, ViewMetadata, Injector} from '@angular/core'; import {Component, ViewMetadata, Injector, ComponentMetadata} from '@angular/core';
export function main() { export function main() {
describe('MockViewResolver', () => { describe('MockDirectiveResolver', () => {
var viewResolver: MockViewResolver; var dirResolver: MockDirectiveResolver;
beforeEach(inject( beforeEach(inject([Injector], (injector: Injector) => {
[Injector], (injector: Injector) => { viewResolver = new MockViewResolver(injector); })); dirResolver = new MockDirectiveResolver(injector);
}));
describe('View overriding', () => { describe('View overriding', () => {
it('should fallback to the default ViewResolver when templates are not overridden', () => { it('should fallback to the default ViewResolver when templates are not overridden', () => {
var view = viewResolver.resolve(SomeComponent); var view = <ComponentMetadata>dirResolver.resolve(SomeComponent);
expect(view.template).toEqual('template'); expect(view.template).toEqual('template');
expect(view.directives).toEqual([SomeDirective]); expect(view.directives).toEqual([SomeDirective]);
}); });
it('should allow overriding the @View', () => { it('should allow overriding the @View', () => {
viewResolver.setView(SomeComponent, new ViewMetadata({template: 'overridden template'})); dirResolver.setView(SomeComponent, new ViewMetadata({template: 'overridden template'}));
var view = viewResolver.resolve(SomeComponent); var view = <ComponentMetadata>dirResolver.resolve(SomeComponent);
expect(view.template).toEqual('overridden template'); expect(view.template).toEqual('overridden template');
expect(isBlank(view.directives)).toBe(true); expect(isBlank(view.directives)).toBe(true);
}); });
it('should allow overriding a view after it has been resolved', () => { it('should allow overriding a view after it has been resolved', () => {
viewResolver.resolve(SomeComponent); dirResolver.resolve(SomeComponent);
viewResolver.setView(SomeComponent, new ViewMetadata({template: 'overridden template'})); dirResolver.setView(SomeComponent, new ViewMetadata({template: 'overridden template'}));
var view = viewResolver.resolve(SomeComponent); var view = <ComponentMetadata>dirResolver.resolve(SomeComponent);
expect(view.template).toEqual('overridden template'); expect(view.template).toEqual('overridden template');
expect(isBlank(view.directives)).toBe(true); expect(isBlank(view.directives)).toBe(true);
}); });
@ -44,23 +45,23 @@ export function main() {
describe('inline template definition overriding', () => { describe('inline template definition overriding', () => {
it('should allow overriding the default template', () => { it('should allow overriding the default template', () => {
viewResolver.setInlineTemplate(SomeComponent, 'overridden template'); dirResolver.setInlineTemplate(SomeComponent, 'overridden template');
var view = viewResolver.resolve(SomeComponent); var view = <ComponentMetadata>dirResolver.resolve(SomeComponent);
expect(view.template).toEqual('overridden template'); expect(view.template).toEqual('overridden template');
expect(view.directives).toEqual([SomeDirective]); expect(view.directives).toEqual([SomeDirective]);
}); });
it('should allow overriding an overridden @View', () => { it('should allow overriding an overridden @View', () => {
viewResolver.setView(SomeComponent, new ViewMetadata({template: 'overridden template'})); dirResolver.setView(SomeComponent, new ViewMetadata({template: 'overridden template'}));
viewResolver.setInlineTemplate(SomeComponent, 'overridden template x 2'); dirResolver.setInlineTemplate(SomeComponent, 'overridden template x 2');
var view = viewResolver.resolve(SomeComponent); var view = <ComponentMetadata>dirResolver.resolve(SomeComponent);
expect(view.template).toEqual('overridden template x 2'); expect(view.template).toEqual('overridden template x 2');
}); });
it('should allow overriding a view after it has been resolved', () => { it('should allow overriding a view after it has been resolved', () => {
viewResolver.resolve(SomeComponent); dirResolver.resolve(SomeComponent);
viewResolver.setInlineTemplate(SomeComponent, 'overridden template'); dirResolver.setInlineTemplate(SomeComponent, 'overridden template');
var view = viewResolver.resolve(SomeComponent); var view = <ComponentMetadata>dirResolver.resolve(SomeComponent);
expect(view.template).toEqual('overridden template'); expect(view.template).toEqual('overridden template');
}); });
}); });
@ -68,31 +69,31 @@ export function main() {
describe('Directive overriding', () => { describe('Directive overriding', () => {
it('should allow overriding a directive from the default view', () => { it('should allow overriding a directive from the default view', () => {
viewResolver.overrideViewDirective(SomeComponent, SomeDirective, SomeOtherDirective); dirResolver.overrideViewDirective(SomeComponent, SomeDirective, SomeOtherDirective);
var view = viewResolver.resolve(SomeComponent); var view = <ComponentMetadata>dirResolver.resolve(SomeComponent);
expect(view.directives.length).toEqual(1); expect(view.directives.length).toEqual(1);
expect(view.directives[0]).toBe(SomeOtherDirective); expect(view.directives[0]).toBe(SomeOtherDirective);
}); });
it('should allow overriding a directive from an overridden @View', () => { it('should allow overriding a directive from an overridden @View', () => {
viewResolver.setView(SomeComponent, new ViewMetadata({directives: [SomeOtherDirective]})); dirResolver.setView(SomeComponent, new ViewMetadata({directives: [SomeOtherDirective]}));
viewResolver.overrideViewDirective(SomeComponent, SomeOtherDirective, SomeComponent); dirResolver.overrideViewDirective(SomeComponent, SomeOtherDirective, SomeComponent);
var view = viewResolver.resolve(SomeComponent); var view = <ComponentMetadata>dirResolver.resolve(SomeComponent);
expect(view.directives.length).toEqual(1); expect(view.directives.length).toEqual(1);
expect(view.directives[0]).toBe(SomeComponent); expect(view.directives[0]).toBe(SomeComponent);
}); });
it('should throw when the overridden directive is not present', () => { it('should throw when the overridden directive is not present', () => {
viewResolver.overrideViewDirective(SomeComponent, SomeOtherDirective, SomeDirective); dirResolver.overrideViewDirective(SomeComponent, SomeOtherDirective, SomeDirective);
expect(() => { viewResolver.resolve(SomeComponent); }) expect(() => { dirResolver.resolve(SomeComponent); })
.toThrowError( .toThrowError(
`Overriden directive ${stringify(SomeOtherDirective)} not found in the template of ${stringify(SomeComponent)}`); `Overriden directive ${stringify(SomeOtherDirective)} not found in the template of ${stringify(SomeComponent)}`);
}); });
it('should allow overriding a directive after its view has been resolved', () => { it('should allow overriding a directive after its view has been resolved', () => {
viewResolver.resolve(SomeComponent); dirResolver.resolve(SomeComponent);
viewResolver.overrideViewDirective(SomeComponent, SomeDirective, SomeOtherDirective); dirResolver.overrideViewDirective(SomeComponent, SomeDirective, SomeOtherDirective);
var view = viewResolver.resolve(SomeComponent); var view = <ComponentMetadata>dirResolver.resolve(SomeComponent);
expect(view.directives.length).toEqual(1); expect(view.directives.length).toEqual(1);
expect(view.directives[0]).toBe(SomeOtherDirective); expect(view.directives[0]).toBe(SomeOtherDirective);
}); });

View File

@ -7,7 +7,7 @@
*/ */
import {DirectiveResolver} from '@angular/compiler/src/directive_resolver'; import {DirectiveResolver} from '@angular/compiler/src/directive_resolver';
import {ContentChild, ContentChildren, Directive, DirectiveMetadata, HostBinding, HostListener, Input, Output, ViewChild, ViewChildren} from '@angular/core/src/metadata'; import {Component, ComponentMetadata, ContentChild, ContentChildren, Directive, DirectiveMetadata, HostBinding, HostListener, Input, Output, ViewChild, ViewChildren} from '@angular/core/src/metadata';
@Directive({selector: 'someDirective'}) @Directive({selector: 'someDirective'})
class SomeDirective { class SomeDirective {
@ -104,6 +104,19 @@ class SomeDirectiveWithViewChild {
c: any; c: any;
} }
class SomeDir {}
class SomePipe {}
@Component({
selector: 'sample',
template: 'some template',
directives: [SomeDir],
pipes: [SomePipe],
styles: ['some styles']
})
class ComponentWithTemplate {
}
class SomeDirectiveWithoutMetadata {} class SomeDirectiveWithoutMetadata {}
export function main() { export function main() {
@ -218,5 +231,15 @@ export function main() {
.toEqual({'c': new ViewChild('c'), 'a': new ViewChild('a')}); .toEqual({'c': new ViewChild('c'), 'a': new ViewChild('a')});
}); });
}); });
describe('view', () => {
it('should read out the template related metadata from the Component metadata', () => {
var compMetadata = <ComponentMetadata>resolver.resolve(ComponentWithTemplate);
expect(compMetadata.template).toEqual('some template');
expect(compMetadata.directives).toEqual([SomeDir]);
expect(compMetadata.pipes).toEqual([SomePipe]);
expect(compMetadata.styles).toEqual(['some styles']);
});
});
}); });
} }

View File

@ -35,8 +35,8 @@ export function main() {
beforeEach(() => { resolver = new NgModuleResolver(); }); beforeEach(() => { resolver = new NgModuleResolver(); });
it('should read out the metadata from the class', () => { it('should read out the metadata from the class', () => {
var viewMetadata = resolver.resolve(SomeModule); var moduleMetadata = resolver.resolve(SomeModule);
expect(viewMetadata).toEqual(new NgModuleMetadata({ expect(moduleMetadata).toEqual(new NgModuleMetadata({
declarations: [SomeClass1], declarations: [SomeClass1],
imports: [SomeClass2], imports: [SomeClass2],
exports: [SomeClass3], exports: [SomeClass3],

View File

@ -11,8 +11,8 @@ import {expect} from '@angular/platform-browser/testing/matchers';
import {Injectable, Component, Input, ViewMetadata, Compiler, ComponentFactory, Injector, NgModule, NgModuleFactory} from '@angular/core'; import {Injectable, Component, Input, ViewMetadata, Compiler, ComponentFactory, Injector, NgModule, NgModuleFactory} from '@angular/core';
import {ConcreteType, stringify} from '../src/facade/lang'; import {ConcreteType, stringify} from '../src/facade/lang';
import {fakeAsync, tick, TestComponentBuilder, ComponentFixture, configureCompiler} from '@angular/core/testing'; import {fakeAsync, tick, TestComponentBuilder, ComponentFixture, configureCompiler} from '@angular/core/testing';
import {XHR, ViewResolver} from '@angular/compiler'; import {XHR, DirectiveResolver} from '@angular/compiler';
import {MockViewResolver} from '@angular/compiler/testing'; import {MockDirectiveResolver} from '@angular/compiler/testing';
import {SpyXHR} from './spies'; import {SpyXHR} from './spies';
@ -33,19 +33,19 @@ export function main() {
let compiler: Compiler; let compiler: Compiler;
let xhr: SpyXHR; let xhr: SpyXHR;
let tcb: TestComponentBuilder; let tcb: TestComponentBuilder;
let viewResolver: MockViewResolver; let dirResolver: MockDirectiveResolver;
let injector: Injector; let injector: Injector;
beforeEach(() => { configureCompiler({providers: [{provide: XHR, useClass: SpyXHR}]}); }); beforeEach(() => { configureCompiler({providers: [{provide: XHR, useClass: SpyXHR}]}); });
beforeEach(inject( beforeEach(inject(
[Compiler, TestComponentBuilder, XHR, ViewResolver, Injector], [Compiler, TestComponentBuilder, XHR, DirectiveResolver, Injector],
(_compiler: Compiler, _tcb: TestComponentBuilder, _xhr: SpyXHR, (_compiler: Compiler, _tcb: TestComponentBuilder, _xhr: SpyXHR,
_viewResolver: MockViewResolver, _injector: Injector) => { _dirResolver: MockDirectiveResolver, _injector: Injector) => {
compiler = _compiler; compiler = _compiler;
tcb = _tcb; tcb = _tcb;
xhr = _xhr; xhr = _xhr;
viewResolver = _viewResolver; dirResolver = _dirResolver;
injector = _injector; injector = _injector;
})); }));
@ -74,12 +74,12 @@ export function main() {
}); });
it('should not update existing compilation results', () => { it('should not update existing compilation results', () => {
viewResolver.setView( dirResolver.setView(
SomeComp, SomeComp,
new ViewMetadata({template: '<child-cmp></child-cmp>', directives: [ChildComp]})); new ViewMetadata({template: '<child-cmp></child-cmp>', directives: [ChildComp]}));
viewResolver.setInlineTemplate(ChildComp, 'oldChild'); dirResolver.setInlineTemplate(ChildComp, 'oldChild');
let compFactory = compiler.compileComponentSync(SomeComp); let compFactory = compiler.compileComponentSync(SomeComp);
viewResolver.setInlineTemplate(ChildComp, 'newChild'); dirResolver.setInlineTemplate(ChildComp, 'newChild');
compiler.compileComponentSync(SomeComp); compiler.compileComponentSync(SomeComp);
let compRef = compFactory.create(injector); let compRef = compFactory.create(injector);
expect(compRef.location.nativeElement).toHaveText('oldChild'); expect(compRef.location.nativeElement).toHaveText('oldChild');
@ -151,9 +151,8 @@ export function main() {
} }
xhr.spy('get').andCallFake(() => Promise.resolve('')); xhr.spy('get').andCallFake(() => Promise.resolve(''));
viewResolver.setView( dirResolver.setView(SomeComp, new ViewMetadata({template: '', directives: [ChildComp]}));
SomeComp, new ViewMetadata({template: '', directives: [ChildComp]})); dirResolver.setView(ChildComp, new ViewMetadata({templateUrl: '/someTpl.html'}));
viewResolver.setView(ChildComp, new ViewMetadata({templateUrl: '/someTpl.html'}));
expect(() => compiler.compileModuleSync(SomeModule)) expect(() => compiler.compileModuleSync(SomeModule))
.toThrowError( .toThrowError(
`Can't compile synchronously as ${stringify(ChildComp)} is still being loaded!`); `Can't compile synchronously as ${stringify(ChildComp)} is still being loaded!`);

View File

@ -1,59 +0,0 @@
/**
* @license
* Copyright Google Inc. 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 {ViewResolver} from '@angular/compiler/src/view_resolver';
import {Component, ViewMetadata} from '@angular/core/src/metadata';
class SomeDir {}
class SomePipe {}
@Component({
selector: 'sample',
template: 'some template',
directives: [SomeDir],
pipes: [SomePipe],
styles: ['some styles']
})
class ComponentWithTemplate {
}
@Component({selector: 'sample'})
class ComponentWithoutView {
}
class SimpleClass {}
export function main() {
describe('ViewResolver', () => {
var resolver: ViewResolver;
beforeEach(() => { resolver = new ViewResolver(); });
it('should read out the View metadata from the Component metadata', () => {
var viewMetadata = resolver.resolve(ComponentWithTemplate);
expect(viewMetadata).toEqual(new ViewMetadata({
template: 'some template',
directives: [SomeDir],
pipes: [SomePipe],
styles: ['some styles']
}));
});
it('should throw when Component has neither template nor templateUrl set', () => {
expect(() => resolver.resolve(ComponentWithoutView))
.toThrowError(
/Component 'ComponentWithoutView' must have either 'template' or 'templateUrl' set/);
});
it('should throw when simple class has no component decorator', () => {
expect(() => resolver.resolve(SimpleClass))
.toThrowError('Could not compile \'SimpleClass\' because it is not a component.');
});
});
}

View File

@ -7,14 +7,12 @@
*/ */
export * from './testing/schema_registry_mock'; export * from './testing/schema_registry_mock';
export * from './testing/view_resolver_mock';
export * from './testing/test_component_builder'; export * from './testing/test_component_builder';
export * from './testing/directive_resolver_mock'; export * from './testing/directive_resolver_mock';
export * from './testing/ng_module_resolver_mock'; export * from './testing/ng_module_resolver_mock';
import {createPlatformFactory, CompilerOptions, PlatformRef} from '@angular/core'; import {createPlatformFactory, CompilerOptions, PlatformRef} from '@angular/core';
import {platformCoreDynamic, DirectiveResolver, ViewResolver, NgModuleResolver} from './index'; import {platformCoreDynamic, DirectiveResolver, NgModuleResolver} from './index';
import {MockViewResolver} from './testing/view_resolver_mock';
import {MockDirectiveResolver} from './testing/directive_resolver_mock'; import {MockDirectiveResolver} from './testing/directive_resolver_mock';
import {MockNgModuleResolver} from './testing/ng_module_resolver_mock'; import {MockNgModuleResolver} from './testing/ng_module_resolver_mock';
@ -30,7 +28,6 @@ export const platformCoreDynamicTesting =
useValue: { useValue: {
providers: [ providers: [
{provide: DirectiveResolver, useClass: MockDirectiveResolver}, {provide: DirectiveResolver, useClass: MockDirectiveResolver},
{provide: ViewResolver, useClass: MockViewResolver},
{provide: NgModuleResolver, useClass: MockNgModuleResolver} {provide: NgModuleResolver, useClass: MockNgModuleResolver}
] ]
}, },

View File

@ -6,12 +6,12 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {Compiler, ComponentMetadata, DirectiveMetadata, Injectable, Injector} from '@angular/core'; import {AnimationEntryMetadata, Compiler, ComponentMetadata, DirectiveMetadata, Injectable, Injector, ViewMetadata, resolveForwardRef} from '@angular/core';
import {DirectiveResolver} from '../src/directive_resolver'; import {DirectiveResolver} from '../src/directive_resolver';
import {Map} from '../src/facade/collection'; import {Map} from '../src/facade/collection';
import {Type, isPresent} from '../src/facade/lang'; import {BaseException} from '../src/facade/exceptions';
import {Type, isArray, isPresent, stringify} from '../src/facade/lang';
/** /**
@ -22,33 +22,75 @@ import {Type, isPresent} from '../src/facade/lang';
export class MockDirectiveResolver extends DirectiveResolver { export class MockDirectiveResolver extends DirectiveResolver {
private _providerOverrides = new Map<Type, any[]>(); private _providerOverrides = new Map<Type, any[]>();
private viewProviderOverrides = new Map<Type, any[]>(); private viewProviderOverrides = new Map<Type, any[]>();
private _views = new Map<Type, ViewMetadata>();
private _inlineTemplates = new Map<Type, string>();
private _animations = new Map<Type, AnimationEntryMetadata[]>();
private _directiveOverrides = new Map<Type, Map<Type, Type>>();
constructor(private _injector: Injector) { super(); } constructor(private _injector: Injector) { super(); }
private get _compiler(): Compiler { return this._injector.get(Compiler); } private get _compiler(): Compiler { return this._injector.get(Compiler); }
private _clearCacheFor(component: Type) { this._compiler.clearCacheFor(component); }
resolve(type: Type, throwIfNotFound = true): DirectiveMetadata { resolve(type: Type, throwIfNotFound = true): DirectiveMetadata {
var dm = super.resolve(type, throwIfNotFound); const dm = super.resolve(type, throwIfNotFound);
if (!dm) { if (!dm) {
return null; return null;
} }
var providerOverrides = this._providerOverrides.get(type); const providerOverrides = this._providerOverrides.get(type);
var viewProviderOverrides = this.viewProviderOverrides.get(type); const viewProviderOverrides = this.viewProviderOverrides.get(type);
var providers = dm.providers; let providers = dm.providers;
if (isPresent(providerOverrides)) { if (isPresent(providerOverrides)) {
var originalViewProviders: any[] = isPresent(dm.providers) ? dm.providers : []; const originalViewProviders: any[] = isPresent(dm.providers) ? dm.providers : [];
providers = originalViewProviders.concat(providerOverrides); providers = originalViewProviders.concat(providerOverrides);
} }
if (dm instanceof ComponentMetadata) { if (dm instanceof ComponentMetadata) {
var viewProviders = dm.viewProviders; let viewProviders = dm.viewProviders;
if (isPresent(viewProviderOverrides)) { if (isPresent(viewProviderOverrides)) {
var originalViewProviders: any[] = isPresent(dm.viewProviders) ? dm.viewProviders : []; const originalViewProviders: any[] = isPresent(dm.viewProviders) ? dm.viewProviders : [];
viewProviders = originalViewProviders.concat(viewProviderOverrides); viewProviders = originalViewProviders.concat(viewProviderOverrides);
} }
let view = this._views.get(type);
if (!view) {
view = dm;
}
const directives: any[] = [];
if (isPresent(view.directives)) {
flattenArray(view.directives, directives);
}
let animations = view.animations;
let templateUrl = view.templateUrl;
const directiveOverrides = this._directiveOverrides.get(type);
const inlineAnimations = this._animations.get(type);
if (isPresent(inlineAnimations)) {
animations = inlineAnimations;
}
let inlineTemplate = this._inlineTemplates.get(type);
if (isPresent(inlineTemplate)) {
templateUrl = null;
} else {
inlineTemplate = view.template;
}
if (isPresent(directiveOverrides) && isPresent(view.directives)) {
directiveOverrides.forEach((to, from) => {
var srcIndex = directives.indexOf(from);
if (srcIndex == -1) {
throw new BaseException(
`Overriden directive ${stringify(from)} not found in the template of ${stringify(type)}`);
}
directives[srcIndex] = to;
});
}
return new ComponentMetadata({ return new ComponentMetadata({
selector: dm.selector, selector: dm.selector,
inputs: dm.inputs, inputs: dm.inputs,
@ -60,7 +102,16 @@ export class MockDirectiveResolver extends DirectiveResolver {
changeDetection: dm.changeDetection, changeDetection: dm.changeDetection,
providers: providers, providers: providers,
viewProviders: viewProviders, viewProviders: viewProviders,
entryComponents: dm.entryComponents entryComponents: dm.entryComponents,
template: inlineTemplate,
templateUrl: templateUrl,
directives: directives.length > 0 ? directives : null,
animations: animations,
styles: view.styles,
styleUrls: view.styleUrls,
pipes: view.pipes,
encapsulation: view.encapsulation,
interpolation: view.interpolation
}); });
} }
@ -77,11 +128,58 @@ export class MockDirectiveResolver extends DirectiveResolver {
setProvidersOverride(type: Type, providers: any[]): void { setProvidersOverride(type: Type, providers: any[]): void {
this._providerOverrides.set(type, providers); this._providerOverrides.set(type, providers);
this._compiler.clearCacheFor(type); this._clearCacheFor(type);
} }
setViewProvidersOverride(type: Type, viewProviders: any[]): void { setViewProvidersOverride(type: Type, viewProviders: any[]): void {
this.viewProviderOverrides.set(type, viewProviders); this.viewProviderOverrides.set(type, viewProviders);
this._compiler.clearCacheFor(type); this._clearCacheFor(type);
}
/**
* Overrides the {@link ViewMetadata} for a component.
*/
setView(component: Type, view: ViewMetadata): void {
this._views.set(component, view);
this._clearCacheFor(component);
}
/**
* Overrides the inline template for a component - other configuration remains unchanged.
*/
setInlineTemplate(component: Type, template: string): void {
this._inlineTemplates.set(component, template);
this._clearCacheFor(component);
}
setAnimations(component: Type, animations: AnimationEntryMetadata[]): void {
this._animations.set(component, animations);
this._clearCacheFor(component);
}
/**
* Overrides a directive from the component {@link ViewMetadata}.
*/
overrideViewDirective(component: Type, from: Type, to: Type): void {
var overrides = this._directiveOverrides.get(component);
if (!overrides) {
overrides = new Map<Type, Type>();
this._directiveOverrides.set(component, overrides);
}
overrides.set(from, to);
this._clearCacheFor(component);
}
}
function flattenArray(tree: any[], out: Array<Type|any[]>): void {
if (!isPresent(tree)) return;
for (var i = 0; i < tree.length; i++) {
var item = resolveForwardRef(tree[i]);
if (isArray(item)) {
flattenArray(item, out);
} else {
out.push(item);
}
} }
} }

View File

@ -9,7 +9,7 @@
import {AnimationEntryMetadata, Compiler, ComponentFactory, Inject, Injectable, Injector, NgZone, ViewMetadata} from '@angular/core'; import {AnimationEntryMetadata, Compiler, ComponentFactory, Inject, Injectable, Injector, NgZone, ViewMetadata} from '@angular/core';
import {ComponentFixture, ComponentFixtureNoNgZone, TestBed, TestComponentBuilder} from '@angular/core/testing'; import {ComponentFixture, ComponentFixtureNoNgZone, TestBed, TestComponentBuilder} from '@angular/core/testing';
import {DirectiveResolver, ViewResolver} from '../index'; import {DirectiveResolver} from '../index';
import {MapWrapper} from '../src/facade/collection'; import {MapWrapper} from '../src/facade/collection';
import {ConcreteType, IS_DART, Type, isPresent} from '../src/facade/lang'; import {ConcreteType, IS_DART, Type, isPresent} from '../src/facade/lang';
@ -101,15 +101,14 @@ export class OverridingTestComponentBuilder extends TestComponentBuilder {
private _applyMetadataOverrides() { private _applyMetadataOverrides() {
let mockDirectiveResolver = this._injector.get(DirectiveResolver); let mockDirectiveResolver = this._injector.get(DirectiveResolver);
let mockViewResolver = this._injector.get(ViewResolver); this._viewOverrides.forEach((view, type) => { mockDirectiveResolver.setView(type, view); });
this._viewOverrides.forEach((view, type) => { mockViewResolver.setView(type, view); });
this._templateOverrides.forEach( this._templateOverrides.forEach(
(template, type) => mockViewResolver.setInlineTemplate(type, template)); (template, type) => mockDirectiveResolver.setInlineTemplate(type, template));
this._animationOverrides.forEach( this._animationOverrides.forEach(
(animationsEntry, type) => mockViewResolver.setAnimations(type, animationsEntry)); (animationsEntry, type) => mockDirectiveResolver.setAnimations(type, animationsEntry));
this._directiveOverrides.forEach((overrides, component) => { this._directiveOverrides.forEach((overrides, component) => {
overrides.forEach( overrides.forEach(
(to, from) => { mockViewResolver.overrideViewDirective(component, from, to); }); (to, from) => { mockDirectiveResolver.overrideViewDirective(component, from, to); });
}); });
this._bindingsOverrides.forEach( this._bindingsOverrides.forEach(
(bindings, type) => mockDirectiveResolver.setProvidersOverride(type, bindings)); (bindings, type) => mockDirectiveResolver.setProvidersOverride(type, bindings));

View File

@ -1,141 +0,0 @@
/**
* @license
* Copyright Google Inc. 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 {AnimationEntryMetadata, BaseException, Compiler, Injectable, Injector, Type, ViewMetadata, resolveForwardRef} from '@angular/core';
import {ViewResolver} from '../index';
import {Map} from '../src/facade/collection';
import {isArray, isBlank, isPresent, stringify} from '../src/facade/lang';
@Injectable()
export class MockViewResolver extends ViewResolver {
/** @internal */
_views = new Map<Type, ViewMetadata>();
/** @internal */
_inlineTemplates = new Map<Type, string>();
/** @internal */
_animations = new Map<Type, AnimationEntryMetadata[]>();
/** @internal */
_directiveOverrides = new Map<Type, Map<Type, Type>>();
constructor(private _injector: Injector) { super(); }
private get _compiler(): Compiler { return this._injector.get(Compiler); }
private _clearCacheFor(component: Type) { this._compiler.clearCacheFor(component); }
/**
* Overrides the {@link ViewMetadata} for a component.
*/
setView(component: Type, view: ViewMetadata): void {
this._views.set(component, view);
this._clearCacheFor(component);
}
/**
* Overrides the inline template for a component - other configuration remains unchanged.
*/
setInlineTemplate(component: Type, template: string): void {
this._inlineTemplates.set(component, template);
this._clearCacheFor(component);
}
setAnimations(component: Type, animations: AnimationEntryMetadata[]): void {
this._animations.set(component, animations);
this._clearCacheFor(component);
}
/**
* Overrides a directive from the component {@link ViewMetadata}.
*/
overrideViewDirective(component: Type, from: Type, to: Type): void {
var overrides = this._directiveOverrides.get(component);
if (isBlank(overrides)) {
overrides = new Map<Type, Type>();
this._directiveOverrides.set(component, overrides);
}
overrides.set(from, to);
this._clearCacheFor(component);
}
/**
* Returns the {@link ViewMetadata} for a component:
* - Set the {@link ViewMetadata} to the overridden view when it exists or fallback to the default
* `ViewResolver`,
* see `setView`.
* - Override the directives, see `overrideViewDirective`.
* - Override the @View definition, see `setInlineTemplate`.
*/
resolve(component: Type, throwIfNotFound = true): ViewMetadata {
var view = this._views.get(component);
if (isBlank(view)) {
view = super.resolve(component, throwIfNotFound);
if (!view) {
return null;
}
}
var directives: any[] /** TODO #9100 */ = [];
if (isPresent(view.directives)) {
flattenArray(view.directives, directives);
}
var animations = view.animations;
var templateUrl = view.templateUrl;
var overrides = this._directiveOverrides.get(component);
var inlineAnimations = this._animations.get(component);
if (isPresent(inlineAnimations)) {
animations = inlineAnimations;
}
var inlineTemplate = this._inlineTemplates.get(component);
if (isPresent(inlineTemplate)) {
templateUrl = null;
} else {
inlineTemplate = view.template;
}
if (isPresent(overrides) && isPresent(view.directives)) {
overrides.forEach((to, from) => {
var srcIndex = directives.indexOf(from);
if (srcIndex == -1) {
throw new BaseException(
`Overriden directive ${stringify(from)} not found in the template of ${stringify(component)}`);
}
directives[srcIndex] = to;
});
}
view = new ViewMetadata({
template: inlineTemplate,
templateUrl: templateUrl,
directives: directives.length > 0 ? directives : null,
animations: animations,
styles: view.styles,
styleUrls: view.styleUrls,
pipes: view.pipes,
encapsulation: view.encapsulation,
interpolation: view.interpolation
});
return view;
}
}
function flattenArray(tree: any[], out: Array<Type|any[]>): void {
if (!isPresent(tree)) return;
for (var i = 0; i < tree.length; i++) {
var item = resolveForwardRef(tree[i]);
if (isArray(item)) {
flattenArray(item, out);
} else {
out.push(item);
}
}
}

View File

@ -17,7 +17,7 @@ import {AnimationEntryMetadata} from './animation/metadata';
import {AttributeMetadata, ContentChildMetadata, ContentChildrenMetadata, QueryMetadata, ViewChildMetadata, ViewChildrenMetadata, ViewQueryMetadata} from './metadata/di'; import {AttributeMetadata, ContentChildMetadata, ContentChildrenMetadata, QueryMetadata, ViewChildMetadata, ViewChildrenMetadata, ViewQueryMetadata} from './metadata/di';
import {ComponentMetadata, DirectiveMetadata, HostBindingMetadata, HostListenerMetadata, InputMetadata, OutputMetadata, PipeMetadata} from './metadata/directives'; import {ComponentMetadata, DirectiveMetadata, HostBindingMetadata, HostListenerMetadata, InputMetadata, OutputMetadata, PipeMetadata} from './metadata/directives';
import {ModuleWithProviders, NgModuleMetadata, SchemaMetadata} from './metadata/ng_module'; import {ModuleWithProviders, NgModuleMetadata, SchemaMetadata} from './metadata/ng_module';
import {ViewEncapsulation, ViewMetadata} from './metadata/view'; import {ViewEncapsulation} from './metadata/view';
export {ANALYZE_FOR_ENTRY_COMPONENTS, AttributeMetadata, ContentChildMetadata, ContentChildrenMetadata, QueryMetadata, ViewChildMetadata, ViewChildrenMetadata, ViewQueryMetadata} from './metadata/di'; export {ANALYZE_FOR_ENTRY_COMPONENTS, AttributeMetadata, ContentChildMetadata, ContentChildrenMetadata, QueryMetadata, ViewChildMetadata, ViewChildrenMetadata, ViewQueryMetadata} from './metadata/di';
export {ComponentMetadata, DirectiveMetadata, HostBindingMetadata, HostListenerMetadata, InputMetadata, OutputMetadata, PipeMetadata} from './metadata/directives'; export {ComponentMetadata, DirectiveMetadata, HostBindingMetadata, HostListenerMetadata, InputMetadata, OutputMetadata, PipeMetadata} from './metadata/directives';
@ -44,46 +44,7 @@ export interface DirectiveDecorator extends TypeDecorator {}
* *
* @stable * @stable
*/ */
export interface ComponentDecorator extends TypeDecorator { export interface ComponentDecorator extends TypeDecorator {}
/**
* Chain {@link ViewMetadata} annotation.
*/
View(obj: {
templateUrl?: string,
template?: string,
directives?: Array<Type|any[]>,
pipes?: Array<Type|any[]>,
renderer?: string,
styles?: string[],
styleUrls?: string[],
animations?: AnimationEntryMetadata[],
interpolation?: [string, string]
}): ViewDecorator;
}
/**
* Interface for the {@link ViewMetadata} decorator function.
*
* See {@link ViewMetadataFactory}.
*
* @experimental
*/
export interface ViewDecorator extends TypeDecorator {
/**
* Chain {@link ViewMetadata} annotation.
*/
View(obj: {
templateUrl?: string,
template?: string,
directives?: Array<Type|any[]>,
pipes?: Array<Type|any[]>,
renderer?: string,
styles?: string[],
styleUrls?: string[],
animations?: AnimationEntryMetadata[],
interpolation?: [string, string]
}): ViewDecorator;
}
/** /**
* Interface for the {@link NgModuleMetadata} decorator function. * Interface for the {@link NgModuleMetadata} decorator function.
@ -237,75 +198,6 @@ export interface ComponentMetadataFactory {
}): ComponentMetadata; }): ComponentMetadata;
} }
/**
* {@link ViewMetadata} factory for creating annotations, decorators or DSL.
*
* ### Example as TypeScript Decorator
*
* ```
* import {Component, View} from '@angular/core';
*
* @Component({...})
* class MyComponent {
* constructor() {
* ...
* }
* }
* ```
*
* ### Example as ES5 DSL
*
* ```
* var MyComponent = ng
* .Component({...})
* .View({...})
* .Class({
* constructor: function() {
* ...
* }
* })
* ```
*
* ### Example as ES5 annotation
*
* ```
* var MyComponent = function() {
* ...
* };
*
* MyComponent.annotations = [
* new ng.Component({...}),
* new ng.View({...})
* ]
* ```
*
* @experimental You should most likely use ComponentMetadataFactory instead
*/
export interface ViewMetadataFactory {
(obj: {
templateUrl?: string,
template?: string,
directives?: Array<Type|any[]>,
pipes?: Array<Type|any[]>,
encapsulation?: ViewEncapsulation,
styles?: string[],
styleUrls?: string[],
animations?: AnimationEntryMetadata[],
interpolation?: [string, string]
}): ViewDecorator;
new (obj: {
templateUrl?: string,
template?: string,
directives?: Array<Type|any[]>,
pipes?: Array<Type|any[]>,
encapsulation?: ViewEncapsulation,
styles?: string[],
styleUrls?: string[],
animations?: AnimationEntryMetadata[],
interpolation?: [string, string]
}): ViewMetadata;
}
/** /**
* {@link AttributeMetadata} factory for creating annotations, decorators or DSL. * {@link AttributeMetadata} factory for creating annotations, decorators or DSL.
* *
@ -541,7 +433,7 @@ export interface NgModuleMetadataFactory {
* @Annotation * @Annotation
*/ */
export var Component: ComponentMetadataFactory = export var Component: ComponentMetadataFactory =
<ComponentMetadataFactory>makeDecorator(ComponentMetadata, (fn: any) => fn.View = View); <ComponentMetadataFactory>makeDecorator(ComponentMetadata);
// TODO(alexeagle): remove the duplication of this doc. It is copied from DirectiveMetadata. // TODO(alexeagle): remove the duplication of this doc. It is copied from DirectiveMetadata.
/** /**
@ -580,7 +472,7 @@ export var Component: ComponentMetadataFactory =
* current `ElementInjector` resolves the constructor dependencies for each directive. * current `ElementInjector` resolves the constructor dependencies for each directive.
* *
* Angular then resolves dependencies as follows, according to the order in which they appear in the * Angular then resolves dependencies as follows, according to the order in which they appear in the
* {@link ViewMetadata}: * {@link ComponentMetadata}:
* *
* 1. Dependencies on the current element * 1. Dependencies on the current element
* 2. Dependencies on element injectors and their parents until it encounters a Shadow DOM boundary * 2. Dependencies on element injectors and their parents until it encounters a Shadow DOM boundary
@ -829,7 +721,8 @@ export var Component: ComponentMetadataFactory =
* location in the current view * location in the current view
* where these actions are performed. * where these actions are performed.
* *
* Views are always created as children of the current {@link ViewMetadata}, and as siblings of the * Views are always created as children of the current {@link ComponentMetadata}, and as siblings of
* the
* `<template>` element. Thus a * `<template>` element. Thus a
* directive in a child view cannot inject the directive that created it. * directive in a child view cannot inject the directive that created it.
* *
@ -928,41 +821,6 @@ export var Component: ComponentMetadataFactory =
export var Directive: DirectiveMetadataFactory = export var Directive: DirectiveMetadataFactory =
<DirectiveMetadataFactory>makeDecorator(DirectiveMetadata); <DirectiveMetadataFactory>makeDecorator(DirectiveMetadata);
// TODO(alexeagle): remove the duplication of this doc. It is copied from ViewMetadata.
/**
* Metadata properties available for configuring Views.
*
* Each Angular component requires a single `@Component` and at least one `@View` annotation. The
* `@View` annotation specifies the HTML template to use, and lists the directives that are active
* within the template.
*
* When a component is instantiated, the template is loaded into the component's shadow root, and
* the expressions and statements in the template are evaluated against the component.
*
* For details on the `@Component` annotation, see {@link ComponentMetadata}.
*
* ### Example
*
* ```
* @Component({
* selector: 'greet',
* template: 'Hello {{name}}!',
* directives: [GreetUser, Bold]
* })
* class Greet {
* name: string;
*
* constructor() {
* this.name = 'World';
* }
* }
* ```
* @deprecated
* @Annotation
*/
var View: ViewMetadataFactory =
<ViewMetadataFactory>makeDecorator(ViewMetadata, (fn: any) => fn.View = View);
/** /**
* Specifies that a constant attribute value should be injected. * Specifies that a constant attribute value should be injected.
* *

View File

@ -50,7 +50,7 @@ import {ViewEncapsulation} from './view';
* current `ElementInjector` resolves the constructor dependencies for each directive. * current `ElementInjector` resolves the constructor dependencies for each directive.
* *
* Angular then resolves dependencies as follows, according to the order in which they appear in the * Angular then resolves dependencies as follows, according to the order in which they appear in the
* {@link ViewMetadata}: * {@link ComponentMetadata}:
* *
* 1. Dependencies on the current element * 1. Dependencies on the current element
* 2. Dependencies on element injectors and their parents until it encounters a Shadow DOM boundary * 2. Dependencies on element injectors and their parents until it encounters a Shadow DOM boundary
@ -299,7 +299,8 @@ import {ViewEncapsulation} from './view';
* location in the current view * location in the current view
* where these actions are performed. * where these actions are performed.
* *
* Views are always created as children of the current {@link ViewMetadata}, and as siblings of the * Views are always created as children of the current {@link ComponentMetadata}, and as siblings of
* the
* `<template>` element. Thus a * `<template>` element. Thus a
* directive in a child view cannot inject the directive that created it. * directive in a child view cannot inject the directive that created it.
* *
@ -787,8 +788,6 @@ export class DirectiveMetadata extends InjectableMetadata {
* *
* All template expressions and statements are then evaluated against the component instance. * All template expressions and statements are then evaluated against the component instance.
* *
* For details on the `@View` annotation, see {@link ViewMetadata}.
*
* ## Lifecycle hooks * ## Lifecycle hooks
* *
* When the component class implements some {@linkDocs guide/lifecycle-hooks} the * When the component class implements some {@linkDocs guide/lifecycle-hooks} the
@ -877,12 +876,32 @@ export class ComponentMetadata extends DirectiveMetadata {
*/ */
moduleId: string; moduleId: string;
/**
* Specifies a template URL for an Angular component.
*
* NOTE: Only one of `templateUrl` or `template` can be defined per View.
*
* <!-- TODO: what's the url relative to? -->
*/
templateUrl: string; templateUrl: string;
/**
* Specifies an inline template for an Angular component.
*
* NOTE: Only one of `templateUrl` or `template` can be defined per View.
*/
template: string; template: string;
/**
* Specifies stylesheet URLs for an Angular component.
*
* <!-- TODO: what's the url relative to? -->
*/
styleUrls: string[]; styleUrls: string[];
/**
* Specifies an inline stylesheet for an Angular component.
*/
styles: string[]; styles: string[];
/** /**
@ -962,6 +981,12 @@ export class ComponentMetadata extends DirectiveMetadata {
pipes: Array<Type|any[]>; pipes: Array<Type|any[]>;
/**
* Specify how the template and the styles should be encapsulated.
* The default is {@link ViewEncapsulation#Emulated `ViewEncapsulation.Emulated`} if the view
* has styles,
* otherwise {@link ViewEncapsulation#None `ViewEncapsulation.None`}.
*/
encapsulation: ViewEncapsulation; encapsulation: ViewEncapsulation;
interpolation: [string, string]; interpolation: [string, string];

View File

@ -73,7 +73,7 @@ export var VIEW_ENCAPSULATION_VALUES =
* ``` * ```
* @ts2dart_const * @ts2dart_const
* *
* @experimental You should most likely be using ComponentMetadata instead. * @deprecated Use ComponentMetadata instead.
*/ */
export class ViewMetadata { export class ViewMetadata {
/** /**

View File

@ -1525,7 +1525,8 @@ function declareTests({useJit}: {useJit: boolean}) {
tcb.createAsync(ComponentWithoutView); tcb.createAsync(ComponentWithoutView);
expect(true).toBe(false); expect(true).toBe(false);
} catch (e) { } catch (e) {
expect(e.message).toContain(`must have either 'template' or 'templateUrl' set.`); expect(e.message).toContain(
`No template specified for component ${stringify(ComponentWithoutView)}`);
} }
})); }));

View File

@ -7,8 +7,8 @@
*/ */
import {LowerCasePipe, NgIf} from '@angular/common'; import {LowerCasePipe, NgIf} from '@angular/common';
import {CompilerConfig, NgModuleResolver, ViewResolver} from '@angular/compiler'; import {CompilerConfig, NgModuleResolver} from '@angular/compiler';
import {MockNgModuleResolver, MockViewResolver} from '@angular/compiler/testing'; import {MockNgModuleResolver} from '@angular/compiler/testing';
import {ANALYZE_FOR_ENTRY_COMPONENTS, CUSTOM_ELEMENTS_SCHEMA, Compiler, Component, ComponentFactoryResolver, ComponentRef, ComponentResolver, DebugElement, Directive, Host, HostBinding, Inject, Injectable, Injector, Input, ModuleWithProviders, NgModule, NgModuleMetadata, NgModuleRef, OpaqueToken, Optional, Pipe, Provider, ReflectiveInjector, SelfMetadata, SkipSelf, SkipSelfMetadata, ViewMetadata, forwardRef, getDebugNode, provide} from '@angular/core'; import {ANALYZE_FOR_ENTRY_COMPONENTS, CUSTOM_ELEMENTS_SCHEMA, Compiler, Component, ComponentFactoryResolver, ComponentRef, ComponentResolver, DebugElement, Directive, Host, HostBinding, Inject, Injectable, Injector, Input, ModuleWithProviders, NgModule, NgModuleMetadata, NgModuleRef, OpaqueToken, Optional, Pipe, Provider, ReflectiveInjector, SelfMetadata, SkipSelf, SkipSelfMetadata, ViewMetadata, forwardRef, getDebugNode, provide} from '@angular/core';
import {Console} from '@angular/core/src/console'; import {Console} from '@angular/core/src/console';
import {ComponentFixture, configureCompiler} from '@angular/core/testing'; import {ComponentFixture, configureCompiler} from '@angular/core/testing';
@ -125,11 +125,10 @@ function declareTests({useJit}: {useJit: boolean}) {
configureCompiler({useJit: useJit, providers: [{provide: Console, useValue: console}]}); configureCompiler({useJit: useJit, providers: [{provide: Console, useValue: console}]});
}); });
beforeEach( beforeEach(inject([Compiler, Injector], (_compiler: Compiler, _injector: Injector) => {
inject([Compiler, Injector, ViewResolver], (_compiler: Compiler, _injector: Injector) => { compiler = _compiler;
compiler = _compiler; injector = _injector;
injector = _injector; }));
}));
function createModule<T>( function createModule<T>(
moduleType: ConcreteType<T>, parentInjector: Injector = null): NgModuleRef<T> { moduleType: ConcreteType<T>, parentInjector: Injector = null): NgModuleRef<T> {

View File

@ -19,15 +19,14 @@ export function main() {
}); });
it('should declare Component class', () => { it('should declare Component class', () => {
var MyComponent = var MyComponent = Component({}).Class({constructor: function() { this.works = true; }});
Component({}).View({}).View({}).Class({constructor: function() { this.works = true; }});
expect(new MyComponent().works).toEqual(true); expect(new MyComponent().works).toEqual(true);
}); });
it('should create type in ES5', () => { it('should create type in ES5', () => {
function MyComponent(){}; function MyComponent(){};
var as: any /** TODO #9100 */; var as: any /** TODO #9100 */;
(<any>MyComponent).annotations = as = Component({}).View({}); (<any>MyComponent).annotations = as = Component({});
expect(reflector.annotations(MyComponent)).toEqual(as.annotations); expect(reflector.annotations(MyComponent)).toEqual(as.annotations);
}); });
}); });

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {CompilerConfig, DirectiveResolver, NgModuleResolver, ViewResolver, analyzeAppProvidersForDeprecatedConfiguration} from '@angular/compiler'; import {CompilerConfig, DirectiveResolver, NgModuleResolver, analyzeAppProvidersForDeprecatedConfiguration} from '@angular/compiler';
import {OverridingTestComponentBuilder, platformCoreDynamicTesting} from '@angular/compiler/testing'; import {OverridingTestComponentBuilder, platformCoreDynamicTesting} from '@angular/compiler/testing';
import {Compiler, CompilerFactory, CompilerOptions, NgModule, PlatformRef, Provider, ReflectiveInjector, Type, createPlatform, createPlatformFactory} from '@angular/core'; import {Compiler, CompilerFactory, CompilerOptions, NgModule, PlatformRef, Provider, ReflectiveInjector, Type, createPlatform, createPlatformFactory} from '@angular/core';
import {TestComponentBuilder, TestComponentRenderer, initTestEnvironment} from '@angular/core/testing'; import {TestComponentBuilder, TestComponentRenderer, initTestEnvironment} from '@angular/core/testing';

View File

@ -13,7 +13,7 @@ import {
ViewMetadata ViewMetadata
} from '@angular/core'; } from '@angular/core';
import {CompilerConfig, ViewResolver} from '@angular/compiler'; import {CompilerConfig, DirectiveResolver} from '@angular/compiler';
import {getIntParameter, bindAction} from '@angular/testing/src/benchmark_util'; import {getIntParameter, bindAction} from '@angular/testing/src/benchmark_util';
@ -21,8 +21,8 @@ function _createBindings(): any[] {
var multiplyTemplatesBy = getIntParameter('elements'); var multiplyTemplatesBy = getIntParameter('elements');
return [ return [
{ {
provide: ViewResolver, provide: DirectiveResolver,
useFactory: () => new MultiplyViewResolver( useFactory: () => new MultiplyDirectiveResolver(
multiplyTemplatesBy, multiplyTemplatesBy,
[BenchmarkComponentNoBindings, BenchmarkComponentWithBindings]), [BenchmarkComponentNoBindings, BenchmarkComponentWithBindings]),
deps: [] deps: []
@ -59,7 +59,7 @@ function measureWrapper(func, desc) {
} }
class MultiplyViewResolver extends ViewResolver { class MultiplyDirectiveResolver extends DirectiveResolver {
_multiplyBy: number; _multiplyBy: number;
_cache = new Map<Type, ViewMetadata>(); _cache = new Map<Type, ViewMetadata>();