fix(compiler): fix cross view references and providers with `useValue`.
Before, we would create all fields in the generated views with visibility `private`. This does not work if an embedded view references a directive / element in a parent view. In Dart, this was no problem so far as it does not have a `private` modifier. Before, `useValue` in a provider did not work when doing offline compile, as so far the `MetadataResolver` was only used for jit mode. Now, `useValue` supports any kind of value that the static reflector can return. E.g. primitives, arrays, string maps, … Closes #8366
This commit is contained in:
parent
163d80adb7
commit
f114d6c560
|
@ -24,7 +24,7 @@ import {LifecycleHooks, LIFECYCLE_HOOKS_VALUES} from 'angular2/src/core/metadata
|
||||||
import {reflector} from 'angular2/src/core/reflection/reflection';
|
import {reflector} from 'angular2/src/core/reflection/reflection';
|
||||||
import {Injectable, Inject, Optional} from 'angular2/src/core/di';
|
import {Injectable, Inject, Optional} from 'angular2/src/core/di';
|
||||||
import {PLATFORM_DIRECTIVES, PLATFORM_PIPES} from 'angular2/src/core/platform_directives_and_pipes';
|
import {PLATFORM_DIRECTIVES, PLATFORM_PIPES} from 'angular2/src/core/platform_directives_and_pipes';
|
||||||
import {MODULE_SUFFIX, sanitizeIdentifier} from './util';
|
import {MODULE_SUFFIX, sanitizeIdentifier, ValueTransformer, visitValue} from './util';
|
||||||
import {assertArrayOfStrings} from './assertions';
|
import {assertArrayOfStrings} from './assertions';
|
||||||
import {getUrlScheme} from 'angular2/src/compiler/url_resolver';
|
import {getUrlScheme} from 'angular2/src/compiler/url_resolver';
|
||||||
import {Provider} from 'angular2/src/core/di/provider';
|
import {Provider} from 'angular2/src/core/di/provider';
|
||||||
|
@ -314,9 +314,7 @@ export class CompileMetadataResolver {
|
||||||
isPresent(provider.useClass) ?
|
isPresent(provider.useClass) ?
|
||||||
this.getTypeMetadata(provider.useClass, staticTypeModuleUrl(provider.useClass)) :
|
this.getTypeMetadata(provider.useClass, staticTypeModuleUrl(provider.useClass)) :
|
||||||
null,
|
null,
|
||||||
useValue: isPresent(provider.useValue) ?
|
useValue: convertToCompileValue(provider.useValue),
|
||||||
new cpl.CompileIdentifierMetadata({runtime: provider.useValue}) :
|
|
||||||
null,
|
|
||||||
useFactory: isPresent(provider.useFactory) ?
|
useFactory: isPresent(provider.useFactory) ?
|
||||||
this.getFactoryMetadata(provider.useFactory,
|
this.getFactoryMetadata(provider.useFactory,
|
||||||
staticTypeModuleUrl(provider.useFactory)) :
|
staticTypeModuleUrl(provider.useFactory)) :
|
||||||
|
@ -417,3 +415,19 @@ function calcTemplateBaseUrl(reflector: ReflectorReader, type: any,
|
||||||
|
|
||||||
return reflector.importUri(type);
|
return reflector.importUri(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Only fill CompileIdentifierMetadata.runtime if needed...
|
||||||
|
function convertToCompileValue(value: any): any {
|
||||||
|
return visitValue(value, new _CompileValueConverter(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CompileValueConverter extends ValueTransformer {
|
||||||
|
visitOther(value: any, context: any): any {
|
||||||
|
if (isStaticType(value)) {
|
||||||
|
return new cpl.CompileIdentifierMetadata(
|
||||||
|
{name: value['name'], moduleUrl: staticTypeModuleUrl(value)});
|
||||||
|
} else {
|
||||||
|
return new cpl.CompileIdentifierMetadata({runtime: value});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -344,6 +344,9 @@ class _DartEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisito
|
||||||
|
|
||||||
private _visitIdentifier(value: CompileIdentifierMetadata, typeParams: o.Type[],
|
private _visitIdentifier(value: CompileIdentifierMetadata, typeParams: o.Type[],
|
||||||
ctx: EmitterVisitorContext): void {
|
ctx: EmitterVisitorContext): void {
|
||||||
|
if (isBlank(value.name)) {
|
||||||
|
throw new BaseException(`Internal error: unknown identifier ${value}`);
|
||||||
|
}
|
||||||
if (isPresent(value.moduleUrl) && value.moduleUrl != this._moduleUrl) {
|
if (isPresent(value.moduleUrl) && value.moduleUrl != this._moduleUrl) {
|
||||||
var prefix = this.importsWithPrefixes.get(value.moduleUrl);
|
var prefix = this.importsWithPrefixes.get(value.moduleUrl);
|
||||||
if (isBlank(prefix)) {
|
if (isBlank(prefix)) {
|
||||||
|
|
|
@ -7,6 +7,7 @@ import {
|
||||||
RegExpWrapper,
|
RegExpWrapper,
|
||||||
StringWrapper
|
StringWrapper
|
||||||
} from 'angular2/src/facade/lang';
|
} from 'angular2/src/facade/lang';
|
||||||
|
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||||
import {OutputEmitter, EmitterVisitorContext} from './abstract_emitter';
|
import {OutputEmitter, EmitterVisitorContext} from './abstract_emitter';
|
||||||
import {AbstractJsEmitterVisitor} from './abstract_js_emitter';
|
import {AbstractJsEmitterVisitor} from './abstract_js_emitter';
|
||||||
import {getImportModulePath, ImportEnv} from './path_util';
|
import {getImportModulePath, ImportEnv} from './path_util';
|
||||||
|
@ -34,6 +35,9 @@ class JsEmitterVisitor extends AbstractJsEmitterVisitor {
|
||||||
constructor(private _moduleUrl: string) { super(); }
|
constructor(private _moduleUrl: string) { super(); }
|
||||||
|
|
||||||
visitExternalExpr(ast: o.ExternalExpr, ctx: EmitterVisitorContext): any {
|
visitExternalExpr(ast: o.ExternalExpr, ctx: EmitterVisitorContext): any {
|
||||||
|
if (isBlank(ast.value.name)) {
|
||||||
|
throw new BaseException(`Internal error: unknown identifier ${ast.value}`);
|
||||||
|
}
|
||||||
if (isPresent(ast.value.moduleUrl) && ast.value.moduleUrl != this._moduleUrl) {
|
if (isPresent(ast.value.moduleUrl) && ast.value.moduleUrl != this._moduleUrl) {
|
||||||
var prefix = this.importsWithPrefixes.get(ast.value.moduleUrl);
|
var prefix = this.importsWithPrefixes.get(ast.value.moduleUrl);
|
||||||
if (isBlank(prefix)) {
|
if (isBlank(prefix)) {
|
||||||
|
|
|
@ -311,6 +311,9 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
|
||||||
|
|
||||||
private _visitIdentifier(value: CompileIdentifierMetadata, typeParams: o.Type[],
|
private _visitIdentifier(value: CompileIdentifierMetadata, typeParams: o.Type[],
|
||||||
ctx: EmitterVisitorContext): void {
|
ctx: EmitterVisitorContext): void {
|
||||||
|
if (isBlank(value.name)) {
|
||||||
|
throw new BaseException(`Internal error: unknown identifier ${value}`);
|
||||||
|
}
|
||||||
if (isPresent(value.moduleUrl) && value.moduleUrl != this._moduleUrl) {
|
if (isPresent(value.moduleUrl) && value.moduleUrl != this._moduleUrl) {
|
||||||
var prefix = this.importsWithPrefixes.get(value.moduleUrl);
|
var prefix = this.importsWithPrefixes.get(value.moduleUrl);
|
||||||
if (isBlank(prefix)) {
|
if (isBlank(prefix)) {
|
||||||
|
|
|
@ -1,4 +1,13 @@
|
||||||
import {IS_DART, StringWrapper, Math, isBlank} from 'angular2/src/facade/lang';
|
import {
|
||||||
|
IS_DART,
|
||||||
|
StringWrapper,
|
||||||
|
Math,
|
||||||
|
isBlank,
|
||||||
|
isArray,
|
||||||
|
isStrictStringMap,
|
||||||
|
isPrimitive
|
||||||
|
} from 'angular2/src/facade/lang';
|
||||||
|
import {StringMapWrapper} from 'angular2/src/facade/collection';
|
||||||
|
|
||||||
export var MODULE_SUFFIX = IS_DART ? '.dart' : '';
|
export var MODULE_SUFFIX = IS_DART ? '.dart' : '';
|
||||||
|
|
||||||
|
@ -27,3 +36,36 @@ export function splitAtColon(input: string, defaultValues: string[]): string[] {
|
||||||
export function sanitizeIdentifier(name: string): string {
|
export function sanitizeIdentifier(name: string): string {
|
||||||
return StringWrapper.replaceAll(name, /\W/g, '_');
|
return StringWrapper.replaceAll(name, /\W/g, '_');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function visitValue(value: any, visitor: ValueVisitor, context: any): any {
|
||||||
|
if (isArray(value)) {
|
||||||
|
return visitor.visitArray(<any[]>value, context);
|
||||||
|
} else if (isStrictStringMap(value)) {
|
||||||
|
return visitor.visitStringMap(<{[key: string]: any}>value, context);
|
||||||
|
} else if (isBlank(value) || isPrimitive(value)) {
|
||||||
|
return visitor.visitPrimitive(value, context);
|
||||||
|
} else {
|
||||||
|
return visitor.visitOther(value, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ValueVisitor {
|
||||||
|
visitArray(arr: any[], context: any): any;
|
||||||
|
visitStringMap(map: {[key: string]: any}, context: any): any;
|
||||||
|
visitPrimitive(value: any, context: any): any;
|
||||||
|
visitOther(value: any, context: any): any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ValueTransformer implements ValueVisitor {
|
||||||
|
visitArray(arr: any[], context: any): any {
|
||||||
|
return arr.map(value => visitValue(value, this, context));
|
||||||
|
}
|
||||||
|
visitStringMap(map: {[key: string]: any}, context: any): any {
|
||||||
|
var result = {};
|
||||||
|
StringMapWrapper.forEach(map,
|
||||||
|
(value, key) => { result[key] = visitValue(value, this, context); });
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
visitPrimitive(value: any, context: any): any { return value; }
|
||||||
|
visitOther(value: any, context: any): any { return value; }
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||||
import * as o from '../output/output_ast';
|
import * as o from '../output/output_ast';
|
||||||
import {Identifiers, identifierToken} from '../identifiers';
|
import {Identifiers, identifierToken} from '../identifiers';
|
||||||
import {InjectMethodVars} from './constants';
|
import {InjectMethodVars} from './constants';
|
||||||
|
@ -18,6 +19,7 @@ import {
|
||||||
import {getPropertyInView, createDiTokenExpression, injectFromViewParentInjector} from './util';
|
import {getPropertyInView, createDiTokenExpression, injectFromViewParentInjector} from './util';
|
||||||
import {CompileQuery, createQueryList, addQueryToTokenMap} from './compile_query';
|
import {CompileQuery, createQueryList, addQueryToTokenMap} from './compile_query';
|
||||||
import {CompileMethod} from './compile_method';
|
import {CompileMethod} from './compile_method';
|
||||||
|
import {ValueTransformer, visitValue} from '../util';
|
||||||
|
|
||||||
export class CompileNode {
|
export class CompileNode {
|
||||||
constructor(public parent: CompileElement, public view: CompileView, public nodeIndex: number,
|
constructor(public parent: CompileElement, public view: CompileView, public nodeIndex: number,
|
||||||
|
@ -72,6 +74,7 @@ export class CompileElement extends CompileNode {
|
||||||
private _createAppElement() {
|
private _createAppElement() {
|
||||||
var fieldName = `_appEl_${this.nodeIndex}`;
|
var fieldName = `_appEl_${this.nodeIndex}`;
|
||||||
var parentNodeIndex = this.isRootElement() ? null : this.parent.nodeIndex;
|
var parentNodeIndex = this.isRootElement() ? null : this.parent.nodeIndex;
|
||||||
|
// private is fine here as no child view will reference an AppElement
|
||||||
this.view.fields.push(new o.ClassField(fieldName, o.importType(Identifiers.AppElement),
|
this.view.fields.push(new o.ClassField(fieldName, o.importType(Identifiers.AppElement),
|
||||||
[o.StmtModifier.Private]));
|
[o.StmtModifier.Private]));
|
||||||
var statement = o.THIS_EXPR.prop(fieldName)
|
var statement = o.THIS_EXPR.prop(fieldName)
|
||||||
|
@ -140,13 +143,7 @@ export class CompileElement extends CompileNode {
|
||||||
return o.importExpr(provider.useClass)
|
return o.importExpr(provider.useClass)
|
||||||
.instantiate(depsExpr, o.importType(provider.useClass));
|
.instantiate(depsExpr, o.importType(provider.useClass));
|
||||||
} else {
|
} else {
|
||||||
if (provider.useValue instanceof CompileIdentifierMetadata) {
|
return _convertValueToOutputAst(provider.useValue);
|
||||||
return o.importExpr(provider.useValue);
|
|
||||||
} else if (provider.useValue instanceof o.Expression) {
|
|
||||||
return provider.useValue;
|
|
||||||
} else {
|
|
||||||
return o.literal(provider.useValue);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
var propName = `_${resolvedProvider.token.name}_${this.nodeIndex}_${this._instances.size}`;
|
var propName = `_${resolvedProvider.token.name}_${this.nodeIndex}_${this._instances.size}`;
|
||||||
|
@ -379,11 +376,11 @@ function createProviderProperty(propName: string, provider: ProviderAst,
|
||||||
type = o.DYNAMIC_TYPE;
|
type = o.DYNAMIC_TYPE;
|
||||||
}
|
}
|
||||||
if (isEager) {
|
if (isEager) {
|
||||||
view.fields.push(new o.ClassField(propName, type, [o.StmtModifier.Private]));
|
view.fields.push(new o.ClassField(propName, type));
|
||||||
view.createMethod.addStmt(o.THIS_EXPR.prop(propName).set(resolvedProviderValueExpr).toStmt());
|
view.createMethod.addStmt(o.THIS_EXPR.prop(propName).set(resolvedProviderValueExpr).toStmt());
|
||||||
} else {
|
} else {
|
||||||
var internalField = `_${propName}`;
|
var internalField = `_${propName}`;
|
||||||
view.fields.push(new o.ClassField(internalField, type, [o.StmtModifier.Private]));
|
view.fields.push(new o.ClassField(internalField, type));
|
||||||
var getter = new CompileMethod(view);
|
var getter = new CompileMethod(view);
|
||||||
getter.resetDebugInfo(compileElement.nodeIndex, compileElement.sourceAst);
|
getter.resetDebugInfo(compileElement.nodeIndex, compileElement.sourceAst);
|
||||||
// Note: Equals is important for JS so that it also checks the undefined case!
|
// Note: Equals is important for JS so that it also checks the undefined case!
|
||||||
|
@ -402,3 +399,29 @@ class _QueryWithRead {
|
||||||
this.read = isPresent(query.meta.read) ? query.meta.read : match;
|
this.read = isPresent(query.meta.read) ? query.meta.read : match;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _convertValueToOutputAst(value: any): o.Expression {
|
||||||
|
return visitValue(value, new _ValueOutputAstTransformer(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ValueOutputAstTransformer extends ValueTransformer {
|
||||||
|
visitArray(arr: any[], context: any): o.Expression {
|
||||||
|
return o.literalArr(arr.map(value => visitValue(value, this, context)));
|
||||||
|
}
|
||||||
|
visitStringMap(map: {[key: string]: any}, context: any): o.Expression {
|
||||||
|
var entries = [];
|
||||||
|
StringMapWrapper.forEach(
|
||||||
|
map, (value, key) => { entries.push([key, visitValue(value, this, context)]); });
|
||||||
|
return o.literalMap(entries);
|
||||||
|
}
|
||||||
|
visitPrimitive(value: any, context: any): o.Expression { return o.literal(value); }
|
||||||
|
visitOther(value: any, context: any): o.Expression {
|
||||||
|
if (value instanceof CompileIdentifierMetadata) {
|
||||||
|
return o.importExpr(value);
|
||||||
|
} else if (value instanceof o.Expression) {
|
||||||
|
return value;
|
||||||
|
} else {
|
||||||
|
throw new BaseException(`Illegal state: Don't now how to compile value ${value}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,8 +29,7 @@ export class CompilePipe {
|
||||||
}
|
}
|
||||||
return injectFromViewParentInjector(diDep.token, false);
|
return injectFromViewParentInjector(diDep.token, false);
|
||||||
});
|
});
|
||||||
this.view.fields.push(new o.ClassField(this.instance.name, o.importType(this.meta.type),
|
this.view.fields.push(new o.ClassField(this.instance.name, o.importType(this.meta.type)));
|
||||||
[o.StmtModifier.Private]));
|
|
||||||
this.view.createMethod.resetDebugInfo(null, null);
|
this.view.createMethod.resetDebugInfo(null, null);
|
||||||
this.view.createMethod.addStmt(o.THIS_EXPR.prop(this.instance.name)
|
this.view.createMethod.addStmt(o.THIS_EXPR.prop(this.instance.name)
|
||||||
.set(o.importExpr(this.meta.type).instantiate(deps))
|
.set(o.importExpr(this.meta.type).instantiate(deps))
|
||||||
|
|
|
@ -97,8 +97,7 @@ function mapNestedViews(declarationAppElement: o.Expression, view: CompileView,
|
||||||
|
|
||||||
export function createQueryList(query: CompileQueryMetadata, directiveInstance: o.Expression,
|
export function createQueryList(query: CompileQueryMetadata, directiveInstance: o.Expression,
|
||||||
propertyName: string, compileView: CompileView): o.Expression {
|
propertyName: string, compileView: CompileView): o.Expression {
|
||||||
compileView.fields.push(new o.ClassField(propertyName, o.importType(Identifiers.QueryList),
|
compileView.fields.push(new o.ClassField(propertyName, o.importType(Identifiers.QueryList)));
|
||||||
[o.StmtModifier.Private]));
|
|
||||||
var expr = o.THIS_EXPR.prop(propertyName);
|
var expr = o.THIS_EXPR.prop(propertyName);
|
||||||
compileView.createMethod.addStmt(o.THIS_EXPR.prop(propertyName)
|
compileView.createMethod.addStmt(o.THIS_EXPR.prop(propertyName)
|
||||||
.set(o.importExpr(Identifiers.QueryList).instantiate([]))
|
.set(o.importExpr(Identifiers.QueryList).instantiate([]))
|
||||||
|
|
|
@ -77,6 +77,7 @@ export class CompileEventListener {
|
||||||
(<o.Statement[]>[markPathToRootStart.callMethod('markPathToRootAsCheckOnce', []).toStmt()])
|
(<o.Statement[]>[markPathToRootStart.callMethod('markPathToRootAsCheckOnce', []).toStmt()])
|
||||||
.concat(this._method.finish())
|
.concat(this._method.finish())
|
||||||
.concat([new o.ReturnStatement(resultExpr)]);
|
.concat([new o.ReturnStatement(resultExpr)]);
|
||||||
|
// private is fine here as no child view will reference the event handler...
|
||||||
this.compileElement.view.eventHandlerMethods.push(new o.ClassMethod(
|
this.compileElement.view.eventHandlerMethods.push(new o.ClassMethod(
|
||||||
this._methodName, [this._eventParam], stmts, o.BOOL_TYPE, [o.StmtModifier.Private]));
|
this._methodName, [this._eventParam], stmts, o.BOOL_TYPE, [o.StmtModifier.Private]));
|
||||||
}
|
}
|
||||||
|
@ -95,6 +96,7 @@ export class CompileEventListener {
|
||||||
}
|
}
|
||||||
var disposable = o.variable(`disposable_${this.compileElement.view.disposables.length}`);
|
var disposable = o.variable(`disposable_${this.compileElement.view.disposables.length}`);
|
||||||
this.compileElement.view.disposables.push(disposable);
|
this.compileElement.view.disposables.push(disposable);
|
||||||
|
// private is fine here as no child view will reference the event handler...
|
||||||
this.compileElement.view.createMethod.addStmt(
|
this.compileElement.view.createMethod.addStmt(
|
||||||
disposable.set(listenExpr).toDeclStmt(o.FUNCTION_TYPE, [o.StmtModifier.Private]));
|
disposable.set(listenExpr).toDeclStmt(o.FUNCTION_TYPE, [o.StmtModifier.Private]));
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,7 @@ function bind(view: CompileView, currValExpr: o.ReadVarExpr, fieldExpr: o.ReadPr
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// private is fine here as no child view will reference the cached value...
|
||||||
view.fields.push(new o.ClassField(fieldExpr.name, null, [o.StmtModifier.Private]));
|
view.fields.push(new o.ClassField(fieldExpr.name, null, [o.StmtModifier.Private]));
|
||||||
view.createMethod.addStmt(
|
view.createMethod.addStmt(
|
||||||
o.THIS_EXPR.prop(fieldExpr.name).set(o.importExpr(Identifiers.uninitialized)).toStmt());
|
o.THIS_EXPR.prop(fieldExpr.name).set(o.importExpr(Identifiers.uninitialized)).toStmt());
|
||||||
|
|
|
@ -88,7 +88,7 @@ export function createFlatArray(expressions: o.Expression[]): o.Expression {
|
||||||
|
|
||||||
export function createPureProxy(fn: o.Expression, argCount: number, pureProxyProp: o.ReadPropExpr,
|
export function createPureProxy(fn: o.Expression, argCount: number, pureProxyProp: o.ReadPropExpr,
|
||||||
view: CompileView) {
|
view: CompileView) {
|
||||||
view.fields.push(new o.ClassField(pureProxyProp.name, null, [o.StmtModifier.Private]));
|
view.fields.push(new o.ClassField(pureProxyProp.name, null));
|
||||||
var pureProxyId =
|
var pureProxyId =
|
||||||
argCount < Identifiers.pureProxies.length ? Identifiers.pureProxies[argCount] : null;
|
argCount < Identifiers.pureProxies.length ? Identifiers.pureProxies[argCount] : null;
|
||||||
if (isBlank(pureProxyId)) {
|
if (isBlank(pureProxyId)) {
|
||||||
|
|
|
@ -128,9 +128,8 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
|
||||||
private _visitText(ast: TemplateAst, value: string, ngContentIndex: number,
|
private _visitText(ast: TemplateAst, value: string, ngContentIndex: number,
|
||||||
parent: CompileElement): o.Expression {
|
parent: CompileElement): o.Expression {
|
||||||
var fieldName = `_text_${this.view.nodes.length}`;
|
var fieldName = `_text_${this.view.nodes.length}`;
|
||||||
this.view.fields.push(new o.ClassField(fieldName,
|
this.view.fields.push(
|
||||||
o.importType(this.view.genConfig.renderTypes.renderText),
|
new o.ClassField(fieldName, o.importType(this.view.genConfig.renderTypes.renderText)));
|
||||||
[o.StmtModifier.Private]));
|
|
||||||
var renderNode = o.THIS_EXPR.prop(fieldName);
|
var renderNode = o.THIS_EXPR.prop(fieldName);
|
||||||
var compileNode = new CompileNode(parent, this.view, this.view.nodes.length, renderNode, ast);
|
var compileNode = new CompileNode(parent, this.view, this.view.nodes.length, renderNode, ast);
|
||||||
var createRenderNode =
|
var createRenderNode =
|
||||||
|
@ -194,8 +193,7 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
|
||||||
}
|
}
|
||||||
var fieldName = `_el_${nodeIndex}`;
|
var fieldName = `_el_${nodeIndex}`;
|
||||||
this.view.fields.push(
|
this.view.fields.push(
|
||||||
new o.ClassField(fieldName, o.importType(this.view.genConfig.renderTypes.renderElement),
|
new o.ClassField(fieldName, o.importType(this.view.genConfig.renderTypes.renderElement)));
|
||||||
[o.StmtModifier.Private]));
|
|
||||||
this.view.createMethod.addStmt(o.THIS_EXPR.prop(fieldName).set(createRenderNodeExpr).toStmt());
|
this.view.createMethod.addStmt(o.THIS_EXPR.prop(fieldName).set(createRenderNodeExpr).toStmt());
|
||||||
|
|
||||||
var renderNode = o.THIS_EXPR.prop(fieldName);
|
var renderNode = o.THIS_EXPR.prop(fieldName);
|
||||||
|
@ -257,8 +255,7 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
|
||||||
var nodeIndex = this.view.nodes.length;
|
var nodeIndex = this.view.nodes.length;
|
||||||
var fieldName = `_anchor_${nodeIndex}`;
|
var fieldName = `_anchor_${nodeIndex}`;
|
||||||
this.view.fields.push(
|
this.view.fields.push(
|
||||||
new o.ClassField(fieldName, o.importType(this.view.genConfig.renderTypes.renderComment),
|
new o.ClassField(fieldName, o.importType(this.view.genConfig.renderTypes.renderComment)));
|
||||||
[o.StmtModifier.Private]));
|
|
||||||
this.view.createMethod.addStmt(
|
this.view.createMethod.addStmt(
|
||||||
o.THIS_EXPR.prop(fieldName)
|
o.THIS_EXPR.prop(fieldName)
|
||||||
.set(ViewProperties.renderer.callMethod(
|
.set(ViewProperties.renderer.callMethod(
|
||||||
|
|
|
@ -26,6 +26,7 @@ bool isString(Object obj) => obj is String;
|
||||||
bool isFunction(Object obj) => obj is Function;
|
bool isFunction(Object obj) => obj is Function;
|
||||||
bool isType(Object obj) => obj is Type;
|
bool isType(Object obj) => obj is Type;
|
||||||
bool isStringMap(Object obj) => obj is Map;
|
bool isStringMap(Object obj) => obj is Map;
|
||||||
|
bool isStrictStringMap(Object obj) => obj is Map;
|
||||||
bool isArray(Object obj) => obj is List;
|
bool isArray(Object obj) => obj is List;
|
||||||
bool isPromise(Object obj) => obj is Future;
|
bool isPromise(Object obj) => obj is Future;
|
||||||
bool isNumber(Object obj) => obj is num;
|
bool isNumber(Object obj) => obj is num;
|
||||||
|
|
|
@ -138,6 +138,11 @@ export function isStringMap(obj: any): boolean {
|
||||||
return typeof obj === 'object' && obj !== null;
|
return typeof obj === 'object' && obj !== null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const STRING_MAP_PROTO = Object.getPrototypeOf({});
|
||||||
|
export function isStrictStringMap(obj: any): boolean {
|
||||||
|
return isStringMap(obj) && Object.getPrototypeOf(obj) === STRING_MAP_PROTO;
|
||||||
|
}
|
||||||
|
|
||||||
export function isPromise(obj: any): boolean {
|
export function isPromise(obj: any): boolean {
|
||||||
return obj instanceof (<any>_global).Promise;
|
return obj instanceof (<any>_global).Promise;
|
||||||
}
|
}
|
||||||
|
|
|
@ -314,6 +314,27 @@ export function main() {
|
||||||
expect(d.dependency).toBeAnInstanceOf(SimpleDirective);
|
expect(d.dependency).toBeAnInstanceOf(SimpleDirective);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it('should support useValue with different values', fakeAsync(() => {
|
||||||
|
var el = createComp('', tcb.overrideProviders(TestComp, [
|
||||||
|
provide('numLiteral', {useValue: 0}),
|
||||||
|
provide('boolLiteral', {useValue: true}),
|
||||||
|
provide('strLiteral', {useValue: 'a'}),
|
||||||
|
provide('null', {useValue: null}),
|
||||||
|
provide('array', {useValue: [1]}),
|
||||||
|
provide('map', {useValue: {'a': 1}}),
|
||||||
|
provide('instance', {useValue: new TestValue('a')}),
|
||||||
|
provide('nested', {useValue: [{'a': [1]}, new TestValue('b')]}),
|
||||||
|
]));
|
||||||
|
expect(el.inject('numLiteral')).toBe(0);
|
||||||
|
expect(el.inject('boolLiteral')).toBe(true);
|
||||||
|
expect(el.inject('strLiteral')).toBe('a');
|
||||||
|
expect(el.inject('null')).toBe(null);
|
||||||
|
expect(el.inject('array')).toEqual([1]);
|
||||||
|
expect(el.inject('map')).toEqual({'a': 1});
|
||||||
|
expect(el.inject('instance')).toEqual(new TestValue('a'));
|
||||||
|
expect(el.inject('nested')).toEqual([{'a': [1]}, new TestValue('b')]);
|
||||||
|
}));
|
||||||
|
|
||||||
it("should instantiate providers that have dependencies with SkipSelf", fakeAsync(() => {
|
it("should instantiate providers that have dependencies with SkipSelf", fakeAsync(() => {
|
||||||
var el = createComp('<div simpleDirective><span someOtherDirective></span></div>',
|
var el = createComp('<div simpleDirective><span someOtherDirective></span></div>',
|
||||||
tcb.overrideProviders(
|
tcb.overrideProviders(
|
||||||
|
@ -679,3 +700,7 @@ export function main() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class TestValue {
|
||||||
|
constructor(public value: string) {}
|
||||||
|
}
|
|
@ -1,14 +1,9 @@
|
||||||
import {Component, Injectable} from 'angular2/core';
|
import {Component, Inject} from 'angular2/core';
|
||||||
import {FORM_DIRECTIVES} from 'angular2/common';
|
import {FORM_DIRECTIVES} from 'angular2/common';
|
||||||
import {MyComp} from './a/multiple_components';
|
import {MyComp} from './a/multiple_components';
|
||||||
|
|
||||||
@Component({
|
@Component({selector: 'basic', templateUrl: './basic.html', directives: [MyComp, FORM_DIRECTIVES]})
|
||||||
selector: 'basic',
|
|
||||||
templateUrl: './basic.html',
|
|
||||||
directives: [MyComp, FORM_DIRECTIVES],
|
|
||||||
})
|
|
||||||
@Injectable()
|
|
||||||
export class Basic {
|
export class Basic {
|
||||||
ctxProp: string;
|
ctxProp: string;
|
||||||
constructor() { this.ctxProp = 'initial value'; }
|
constructor() { this.ctxProp = 'initiaValue'; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
import {Component, Inject, OpaqueToken} from 'angular2/core';
|
||||||
|
import {NgIf} from 'angular2/common';
|
||||||
|
|
||||||
|
export const SOME_OPAQUE_TOKEN = new OpaqueToken('opaqueToken');
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'comp-providers',
|
||||||
|
template: '',
|
||||||
|
providers: [
|
||||||
|
{provide: 'strToken', useValue: 'strValue'},
|
||||||
|
{provide: SOME_OPAQUE_TOKEN, useValue: 10},
|
||||||
|
{provide: 'reference', useValue: NgIf},
|
||||||
|
{provide: 'complexToken', useValue: {a: 1, b: ['test', SOME_OPAQUE_TOKEN]}},
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class CompWithProviders {
|
||||||
|
constructor(@Inject('strToken') public ctxProp) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'cmp-reference',
|
||||||
|
template: `
|
||||||
|
<input #a>{{a.value}}
|
||||||
|
<div *ngIf="true">{{a.value}}</div>
|
||||||
|
`,
|
||||||
|
directives: [NgIf]
|
||||||
|
})
|
||||||
|
export class CompWithReferences {
|
||||||
|
}
|
Loading…
Reference in New Issue