diff --git a/modules/angular2/src/change_detection/parser/parser.ts b/modules/angular2/src/change_detection/parser/parser.ts index edc89251fb..296e4f35f0 100644 --- a/modules/angular2/src/change_detection/parser/parser.ts +++ b/modules/angular2/src/change_detection/parser/parser.ts @@ -490,14 +490,11 @@ export class _ParseAST { new MethodCall(receiver, id, fn, args); } else { - let getter = this.reflector.getter(id); - let setter = this.reflector.setter(id); - if (isSafe) { if (this.optionalOperator("=")) { this.error("The '?.' operator cannot be used in the assignment"); } else { - return new SafePropertyRead(receiver, id, getter); + return new SafePropertyRead(receiver, id, this.reflector.getter(id)); } } else { if (this.optionalOperator("=")) { @@ -506,9 +503,9 @@ export class _ParseAST { } let value = this.parseConditional(); - return new PropertyWrite(receiver, id, setter, value); + return new PropertyWrite(receiver, id, this.reflector.setter(id), value); } else { - return new PropertyRead(receiver, id, getter); + return new PropertyRead(receiver, id, this.reflector.getter(id)); } } } diff --git a/modules/angular2/src/core/compiler/element_injector.ts b/modules/angular2/src/core/compiler/element_injector.ts index bbb184365e..488644f197 100644 --- a/modules/angular2/src/core/compiler/element_injector.ts +++ b/modules/angular2/src/core/compiler/element_injector.ts @@ -44,6 +44,7 @@ import {ChangeDetector, ChangeDetectorRef} from 'angular2/src/change_detection/c import {QueryList} from './query_list'; import {reflector} from 'angular2/src/reflection/reflection'; import {RenderDirectiveMetadata} from 'angular2/src/render/api'; +import {EventConfig} from 'angular2/src/render/dom/util'; import {PipeBinding} from '../pipes/pipe_binding'; var _staticKeys; @@ -303,18 +304,8 @@ function _createEventEmitterAccessors(bwv: BindingWithVisibility): EventEmitterA if (!(binding instanceof DirectiveBinding)) return []; var db = binding; return ListWrapper.map(db.eventEmitters, eventConfig => { - let fieldName; - let eventName; - var colonIdx = eventConfig.indexOf(':'); - if (colonIdx > -1) { - // long format: 'fieldName: eventName' - fieldName = StringWrapper.substring(eventConfig, 0, colonIdx).trim(); - eventName = StringWrapper.substring(eventConfig, colonIdx + 1).trim(); - } else { - // short format: 'name' when fieldName and eventName are the same - fieldName = eventName = eventConfig; - } - return new EventEmitterAccessor(eventName, reflector.getter(fieldName)); + var parsedEvent = EventConfig.parse(eventConfig); + return new EventEmitterAccessor(parsedEvent.eventName, reflector.getter(parsedEvent.fieldName)); }); } diff --git a/modules/angular2/src/render/dom/compiler/directive_parser.ts b/modules/angular2/src/render/dom/compiler/directive_parser.ts index 906a49e66f..87f162a553 100644 --- a/modules/angular2/src/render/dom/compiler/directive_parser.ts +++ b/modules/angular2/src/render/dom/compiler/directive_parser.ts @@ -10,7 +10,7 @@ import {CompileElement} from './compile_element'; import {CompileControl} from './compile_control'; import {RenderDirectiveMetadata} from '../../api'; -import {dashCaseToCamelCase, camelCaseToDashCase, EVENT_TARGET_SEPARATOR} from '../util'; +import {EventConfig, dashCaseToCamelCase, camelCaseToDashCase} from '../util'; import {DirectiveBuilder, ElementBinderBuilder} from '../view/proto_view_builder'; /** @@ -146,12 +146,9 @@ export class DirectiveParser implements CompileStep { _bindDirectiveEvent(eventName, action, compileElement, directiveBinderBuilder) { var ast = this._parser.parseAction(action, compileElement.elementDescription); - if (StringWrapper.contains(eventName, EVENT_TARGET_SEPARATOR)) { - var parts = eventName.split(EVENT_TARGET_SEPARATOR); - directiveBinderBuilder.bindEvent(parts[1], ast, parts[0]); - } else { - directiveBinderBuilder.bindEvent(eventName, ast); - } + var parsedEvent = EventConfig.parse(eventName); + var targetName = parsedEvent.isLongForm ? parsedEvent.fieldName : null; + directiveBinderBuilder.bindEvent(parsedEvent.eventName, ast, targetName); } _bindHostProperty(hostPropertyName, expression, compileElement, directiveBinderBuilder) { diff --git a/modules/angular2/src/render/dom/util.ts b/modules/angular2/src/render/dom/util.ts index 643dd63847..74198fc41b 100644 --- a/modules/angular2/src/render/dom/util.ts +++ b/modules/angular2/src/render/dom/util.ts @@ -29,6 +29,27 @@ export function dashCaseToCamelCase(input: string): string { (m) => { return m[1].toUpperCase(); }); } +export class EventConfig { + constructor(public fieldName: string, public eventName: string, public isLongForm: boolean) {} + + static parse(eventConfig: string): EventConfig { + var fieldName = eventConfig, eventName = eventConfig, isLongForm = false; + var separatorIdx = eventConfig.indexOf(EVENT_TARGET_SEPARATOR); + if (separatorIdx > -1) { + // long format: 'fieldName: eventName' + fieldName = StringWrapper.substring(eventConfig, 0, separatorIdx).trim(); + eventName = StringWrapper.substring(eventConfig, separatorIdx + 1).trim(); + isLongForm = true; + } + return new EventConfig(fieldName, eventName, isLongForm); + } + + getFullName(): string { + return this.isLongForm ? `${this.fieldName}${EVENT_TARGET_SEPARATOR}${this.eventName}` : + this.eventName; + } +} + // Attention: This is on the hot path, so don't use closures or default values! export function queryBoundElements(templateContent: Node, isSingleElementChild: boolean): Element[] { diff --git a/modules/angular2/src/render/dom/view/proto_view_builder.ts b/modules/angular2/src/render/dom/view/proto_view_builder.ts index 1e4766ef2b..256f66d4f5 100644 --- a/modules/angular2/src/render/dom/view/proto_view_builder.ts +++ b/modules/angular2/src/render/dom/view/proto_view_builder.ts @@ -262,7 +262,7 @@ export class DirectiveBuilder { } } -export class EventBuilder extends AstTransformer { +class EventBuilder extends AstTransformer { locals: List = []; localEvents: List = []; globalEvents: List = []; diff --git a/modules/angular2/src/transform/bind_generator/generator.dart b/modules/angular2/src/transform/bind_generator/generator.dart index 4268389585..f90670571d 100644 --- a/modules/angular2/src/transform/bind_generator/generator.dart +++ b/modules/angular2/src/transform/bind_generator/generator.dart @@ -31,7 +31,7 @@ Future createNgSettersAndGetters( return '$out'; } -// TODO(kegluneq): De-dupe from template_compiler/generator.dart. +// TODO(kegluneq): De-dupe from template_compiler/generator.dart, #3589. /// Consumes the map generated by {@link _createPropertiesMap} to codegen /// setters. diff --git a/modules/angular2/src/transform/common/property_utils.dart b/modules/angular2/src/transform/common/property_utils.dart index ce5f44998a..285ec69049 100644 --- a/modules/angular2/src/transform/common/property_utils.dart +++ b/modules/angular2/src/transform/common/property_utils.dart @@ -3,7 +3,10 @@ library angular2.transform.common.property_utils; import 'package:analyzer/src/generated/scanner.dart' show Keyword; /// Whether `name` is a valid property name. -bool isValid(String name) => !Keyword.keywords.containsKey(name); +bool isValid(String name) { + var keyword = Keyword.keywords[name]; + return keyword == null || keyword.isPseudoKeyword; +} /// Prepares `name` to be emitted inside a string. String sanitize(String name) => name.replaceAll('\$', '\\\$'); diff --git a/modules/angular2/src/transform/template_compiler/generator.dart b/modules/angular2/src/transform/template_compiler/generator.dart index b7a060a6bc..10d7791ea3 100644 --- a/modules/angular2/src/transform/template_compiler/generator.dart +++ b/modules/angular2/src/transform/template_compiler/generator.dart @@ -23,8 +23,9 @@ import 'package:barback/barback.dart'; import 'change_detector_codegen.dart' as change; import 'compile_step_factory.dart'; -import 'reflection_capabilities.dart'; -import 'reflector_register_codegen.dart' as reg; +import 'reflection/codegen.dart' as reg; +import 'reflection/processor.dart' as reg; +import 'reflection/reflection_capabilities.dart'; import 'view_definition_creator.dart'; /// Reads the `.ng_deps.dart` file represented by `entryPoint` and parses any @@ -42,23 +43,22 @@ Future processTemplates(AssetReader reader, AssetId entryPoint, var extractor = new _TemplateExtractor(new DomElementSchemaRegistry(), new TemplateCloner(-1), new XhrImpl(reader, entryPoint)); - var registrations = new reg.Codegen(); + final processor = new reg.Processor(); + var changeDetectorClasses = new change.Codegen(); for (var rType in viewDefResults.viewDefinitions.keys) { var viewDefEntry = viewDefResults.viewDefinitions[rType]; - var result = await extractor.extractTemplates(viewDefEntry.viewDef); - if (result == null) continue; + var protoView = await extractor.extractTemplates(viewDefEntry.viewDef); + if (protoView == null) continue; if (generateRegistrations) { - // TODO(kegluneq): Generate these getters & setters based on the - // `ProtoViewDto` rather than querying the `ReflectionCapabilities`. - registrations.generate(result.recording); + processor.process(viewDefEntry, protoView); } - if (result.protoView != null && generateChangeDetectors) { + if (generateChangeDetectors) { var saved = reflector.reflectionCapabilities; reflector.reflectionCapabilities = const NullReflectionCapabilities(); var defs = getChangeDetectorDefinitions(viewDefEntry.hostMetadata, - result.protoView, viewDefEntry.viewDef.directives); + protoView, viewDefEntry.viewDef.directives); for (var i = 0; i < defs.length; ++i) { changeDetectorClasses.generate('${rType.typeName}', '_${rType.typeName}_ChangeDetector$i', defs[i]); @@ -67,6 +67,10 @@ Future processTemplates(AssetReader reader, AssetId entryPoint, } } + // TODO(kegluneq): Do not hard-code `false` here once i/3436 is fixed. + final registrations = new reg.Codegen(generateChangeDetectors: false); + registrations.generate(processor); + var code = viewDefResults.ngDeps.code; if (registrations.isEmpty && changeDetectorClasses.isEmpty) return code; var importInjectIdx = @@ -93,19 +97,16 @@ class _TemplateExtractor { ElementSchemaRegistry _schemaRegistry; TemplateCloner _templateCloner; - _TemplateExtractor(ElementSchemaRegistry schemaRegistry, - TemplateCloner templateCloner, XHR xhr) + _TemplateExtractor(this._schemaRegistry, this._templateCloner, XHR xhr) : _factory = new CompileStepFactory(new ng.Parser(new ng.Lexer())) { var urlResolver = new UrlResolver(); var styleUrlResolver = new StyleUrlResolver(urlResolver); var styleInliner = new StyleInliner(xhr, styleUrlResolver, urlResolver); _loader = new ViewLoader(xhr, styleInliner, styleUrlResolver); - _schemaRegistry = schemaRegistry; - _templateCloner = templateCloner; } - Future<_ExtractResult> extractTemplates(ViewDefinition viewDef) async { + Future extractTemplates(ViewDefinition viewDef) async { // Check for "imperative views". if (viewDef.template == null && viewDef.templateAbsUrl == null) return null; @@ -115,8 +116,7 @@ class _TemplateExtractor { // operations between saving and restoring it, otherwise we can get into // a bad state. See issue #2359 for additional context. var savedReflectionCapabilities = reflector.reflectionCapabilities; - var recordingCapabilities = new RecordingReflectionCapabilities(); - reflector.reflectionCapabilities = recordingCapabilities; + reflector.reflectionCapabilities = const NullReflectionCapabilities(); var pipeline = new CompilePipeline(_factory.createSteps(viewDef)); @@ -130,13 +130,6 @@ class _TemplateExtractor { reflector.reflectionCapabilities = savedReflectionCapabilities; - return new _ExtractResult(recordingCapabilities, protoViewDto); + return protoViewDto; } } - -class _ExtractResult { - final RecordingReflectionCapabilities recording; - final ProtoViewDto protoView; - - _ExtractResult(this.recording, this.protoView); -} diff --git a/modules/angular2/src/transform/template_compiler/reflector_register_codegen.dart b/modules/angular2/src/transform/template_compiler/reflection/codegen.dart similarity index 68% rename from modules/angular2/src/transform/template_compiler/reflector_register_codegen.dart rename to modules/angular2/src/transform/template_compiler/reflection/codegen.dart index 90225bfebd..ba13b8f3a5 100644 --- a/modules/angular2/src/transform/template_compiler/reflector_register_codegen.dart +++ b/modules/angular2/src/transform/template_compiler/reflection/codegen.dart @@ -1,25 +1,32 @@ -library angular2.transform.template_compiler.reflector_register_codegen; +library angular2.transform.template_compiler.reflection.codegen; import 'package:angular2/src/transform/common/names.dart'; import 'package:angular2/src/transform/common/property_utils.dart' as prop; -import 'reflection_capabilities.dart'; + +import 'model.dart'; class Codegen { final StringBuffer _buf = new StringBuffer(); - void generate(RecordingReflectionCapabilities recording) { - if (recording != null) { - var calls = _generateGetters(recording.getterNames); + /// Whether we are pre-generating change detectors. + /// If we have pre-generated change detectors, we need + final bool generateChangeDetectors; + + Codegen({this.generateChangeDetectors}); + + void generate(CodegenModel model) { + if (model != null) { + var calls = _generateGetters(_extractNames(model.getterNames)); if (calls.isNotEmpty) { _buf.write('..${REGISTER_GETTERS_METHOD_NAME}' '({${calls.join(', ')}})'); } - calls = _generateSetters(recording.setterNames); + calls = _generateSetters(_extractNames(model.setterNames)); if (calls.isNotEmpty) { _buf.write('..${REGISTER_SETTERS_METHOD_NAME}' '({${calls.join(', ')}})'); } - calls = _generateMethods(recording.methodNames); + calls = _generateMethods(_extractNames(model.methodNames)); if (calls.isNotEmpty) { _buf.write('..${REGISTER_METHODS_METHOD_NAME}' '({${calls.join(', ')}})'); @@ -27,6 +34,15 @@ class Codegen { } } + Iterable _extractNames(Iterable accessors) { + var names = accessors.where((accessor) { + return accessor.isStaticallyNecessary || !generateChangeDetectors; + }).map((accessor) => accessor.sanitizedName); + var nameList = names.toList(); + nameList.sort(); + return nameList; + } + bool get isEmpty => _buf.isEmpty; @override diff --git a/modules/angular2/src/transform/template_compiler/reflection/model.dart b/modules/angular2/src/transform/template_compiler/reflection/model.dart new file mode 100644 index 0000000000..30297d4b85 --- /dev/null +++ b/modules/angular2/src/transform/template_compiler/reflection/model.dart @@ -0,0 +1,49 @@ +library angular2.transform.template_compiler.reflection.model; + +import 'package:angular2/src/render/dom/util.dart'; + +/// Defines the names of getters, setters, and methods which need to be +/// available to Angular 2 via the `reflector` at runtime. +/// See [angular2.src.reflection.reflector] for details. +abstract class CodegenModel { + Iterable get getterNames; + Iterable get methodNames; + Iterable get setterNames; +} + +/// Wraps a getter, setter, or method that we may need to access reflectively in +/// an Angular2 app. +/// This is essentially a wrapper for `sanitizedName`, which is the name of the +/// actual getter, setter, or method that will be registered. Note that +/// `operator==` and `hashCode` basically forward to `sanitizedName`. +class ReflectiveAccessor { + /// The value in the Ast determining that we need this accessor. This is the + /// value that is actually present in the template, which may not directly + /// correspond to the model on the `Component`. + final String astValue; + + /// The sanitized name of this accessor. This is the name of the getter, + /// setter, or method on the `Component`. + final String sanitizedName; + + /// Whether this getter, setter, or method is still necessary when we have + /// pre-generated change detectors. + final bool isStaticallyNecessary; + + ReflectiveAccessor(String astValue, {this.isStaticallyNecessary}) + : this.astValue = astValue, + this.sanitizedName = sanitizePropertyName(astValue); + + @override + bool operator ==(other) { + if (other is! ReflectiveAccessor) return false; + return sanitizedName == other.sanitizedName; + } + + @override + int get hashCode => sanitizedName.hashCode; +} + +String sanitizePropertyName(String name) { + return dashCaseToCamelCase(EventConfig.parse(name).fieldName); +} diff --git a/modules/angular2/src/transform/template_compiler/reflection/processor.dart b/modules/angular2/src/transform/template_compiler/reflection/processor.dart new file mode 100644 index 0000000000..0bd87d3ab0 --- /dev/null +++ b/modules/angular2/src/transform/template_compiler/reflection/processor.dart @@ -0,0 +1,124 @@ +library angular2.transform.template_compiler.reflection.processor; + +import 'package:angular2/src/change_detection/parser/ast.dart'; +import 'package:angular2/src/render/api.dart'; +import 'package:angular2/src/transform/template_compiler/view_definition_creator.dart'; + +import 'model.dart'; + +class Processor implements CodegenModel { + /// The names of all requested `getter`s. + final Set getterNames = new Set(); + + /// The names of all requested `setter`s. + final Set setterNames = new Set(); + + /// The names of all requested `method`s. + final Set methodNames = new Set(); + + _NgAstVisitor _visitor; + + Processor() { + _visitor = new _NgAstVisitor(this); + } + + void process(ViewDefinitionEntry viewDefEntry, ProtoViewDto protoViewDto) { + _processViewDefinition(viewDefEntry); + _processProtoViewDto(protoViewDto); + } + + /// Extracts the names of necessary getters from the events in host and + /// dependent [DirectiveMetadata]. + void _processViewDefinition(ViewDefinitionEntry viewDefEntry) { + // These are necessary even with generated change detectors. + if (viewDefEntry.hostMetadata != null && + viewDefEntry.hostMetadata.events != null) { + viewDefEntry.hostMetadata.events.forEach((eventName) { + getterNames.add( + new ReflectiveAccessor(eventName, isStaticallyNecessary: true)); + }); + } + } + + void _processProtoViewDto(ProtoViewDto protoViewDto) { + _visitor.isStaticallyNecessary = false; + + protoViewDto.textBindings.forEach((ast) => ast.visit(_visitor)); + protoViewDto.elementBinders.forEach((binder) { + binder.propertyBindings.forEach((binding) { + binding.astWithSource.visit(_visitor); + setterNames.add(new ReflectiveAccessor(binding.property, + isStaticallyNecessary: false)); + }); + + binder.directives.forEach((directiveBinding) { + directiveBinding.propertyBindings.values + .forEach((propBinding) => propBinding.visit(_visitor)); + directiveBinding.propertyBindings.keys.forEach((bindingName) { + setterNames.add(new ReflectiveAccessor(bindingName, + isStaticallyNecessary: false)); + }); + + directiveBinding.hostPropertyBindings.forEach((elementBinding) { + elementBinding.astWithSource.visit(_visitor); + setterNames.add(new ReflectiveAccessor(elementBinding.property, + isStaticallyNecessary: false)); + }); + }); + + binder.eventBindings + .forEach((eventBinding) => eventBinding.source.visit(_visitor)); + + binder.directives.forEach((directiveBinding) { + directiveBinding.eventBindings + .forEach((eventBinding) => eventBinding.source.visit(_visitor)); + }); + + if (binder.nestedProtoView != null) { + _processProtoViewDto(binder.nestedProtoView); + } + }); + } +} + +class _NgAstVisitor extends RecursiveAstVisitor { + final Processor _result; + + /// Whether any getters or setters recorded are necessary when running + /// statically. A getter or setter that is necessary only for change detection + /// is not necessary when running statically because all accesses are handled + /// by the dedicated change detector classes. + bool isStaticallyNecessary = false; + + _NgAstVisitor(this._result); + + visitMethodCall(MethodCall ast) { + _result.methodNames + .add(new ReflectiveAccessor(ast.name, isStaticallyNecessary: true)); + super.visitMethodCall(ast); + } + + visitPropertyRead(PropertyRead ast) { + _result.getterNames.add(new ReflectiveAccessor(ast.name, + isStaticallyNecessary: isStaticallyNecessary)); + super.visitPropertyRead(ast); + } + + visitPropertyWrite(PropertyWrite ast) { + _result.setterNames.add(new ReflectiveAccessor(ast.name, + isStaticallyNecessary: isStaticallyNecessary)); + super.visitPropertyWrite(ast); + } + + visitSafeMethodCall(SafeMethodCall ast) { + _result.methodNames + .add(new ReflectiveAccessor(ast.name, isStaticallyNecessary: true)); + super.visitSafeMethodCall(ast); + } + + visitSafePropertyRead(SafePropertyRead ast) { + _result.getterNames.add(new ReflectiveAccessor(ast.name, + isStaticallyNecessary: isStaticallyNecessary)); + super.visitSafePropertyRead(ast); + } +} diff --git a/modules/angular2/src/transform/template_compiler/reflection_capabilities.dart b/modules/angular2/src/transform/template_compiler/reflection/reflection_capabilities.dart similarity index 52% rename from modules/angular2/src/transform/template_compiler/reflection_capabilities.dart rename to modules/angular2/src/transform/template_compiler/reflection/reflection_capabilities.dart index 07246fb47f..65734853ca 100644 --- a/modules/angular2/src/transform/template_compiler/reflection_capabilities.dart +++ b/modules/angular2/src/transform/template_compiler/reflection/reflection_capabilities.dart @@ -1,4 +1,4 @@ -library angular2.transform.template_compiler.reflection_capabilities; +library angular2.transform.template_compiler.reflection.reflection_capabilities; import 'package:angular2/src/reflection/reflection_capabilities.dart'; import 'package:angular2/src/reflection/types.dart'; @@ -32,32 +32,3 @@ class NullReflectionCapabilities implements ReflectionCapabilities { _nullGetter(Object p) => null; _nullSetter(Object p, v) => null; _nullMethod(Object p, List a) => null; - -/// ReflectionCapabilities object that records requests for `getter`s, -/// `setter`s, and `method`s so these can be code generated rather than -/// reflectively accessed at runtime. -class RecordingReflectionCapabilities extends NullReflectionCapabilities { - /// The names of all requested `getter`s. - final Set getterNames = new Set(); - - /// The names of all requested `setter`s. - final Set setterNames = new Set(); - - /// The names of all requested `method`s. - final Set methodNames = new Set(); - - GetterFn getter(String name) { - getterNames.add(name); - return super.getter(name); - } - - SetterFn setter(String name) { - setterNames.add(name); - return super.setter(name); - } - - MethodFn method(String name) { - methodNames.add(name); - return super.method(name); - } -} diff --git a/modules/angular2/test/render/dom/util_spec.ts b/modules/angular2/test/render/dom/util_spec.ts new file mode 100644 index 0000000000..c303bb52c6 --- /dev/null +++ b/modules/angular2/test/render/dom/util_spec.ts @@ -0,0 +1,31 @@ +import {EventConfig} from 'angular2/src/render/dom/util'; +import {ddescribe, describe, expect, it} from 'angular2/test_lib'; + +export function main() { + describe('EventConfig', () => { + describe('parse', () => { + it('should handle short form events', () => { + var eventConfig = EventConfig.parse('shortForm'); + expect(eventConfig.fieldName).toEqual('shortForm'); + expect(eventConfig.eventName).toEqual('shortForm'); + expect(eventConfig.isLongForm).toEqual(false); + }); + it('should handle long form events', () => { + var eventConfig = EventConfig.parse('fieldName: eventName'); + expect(eventConfig.fieldName).toEqual('fieldName'); + expect(eventConfig.eventName).toEqual('eventName'); + expect(eventConfig.isLongForm).toEqual(true); + }); + }); + describe('getFullName', () => { + it('should handle short form events', () => { + var eventConfig = new EventConfig('shortForm', 'shortForm', false); + expect(eventConfig.getFullName()).toEqual('shortForm'); + }); + it('should handle long form events', () => { + var eventConfig = new EventConfig('fieldName', 'eventName', true); + expect(eventConfig.getFullName()).toEqual('fieldName:eventName'); + }); + }); + }); +} diff --git a/modules/angular2/test/transform/integration/two_annotations_files/expected/bar.ng_deps.dart b/modules/angular2/test/transform/integration/two_annotations_files/expected/bar.ng_deps.dart index bf8b4bd191..09dff325f1 100644 --- a/modules/angular2/test/transform/integration/two_annotations_files/expected/bar.ng_deps.dart +++ b/modules/angular2/test/transform/integration/two_annotations_files/expected/bar.ng_deps.dart @@ -19,8 +19,7 @@ void initReflector() { const Component(selector: '[soup]'), const View(template: 'Salad: {{myNum}} is awesome') ], const [], () => new MyComponent())) - ..registerGetters({'myNum': (o) => o.myNum}) - ..registerSetters({'myNum': (o, v) => o.myNum = v}); + ..registerGetters({'myNum': (o) => o.myNum}); _gen.preGeneratedProtoDetectors['MyComponent_comp_0'] = _MyComponent_ChangeDetector0.newProtoChangeDetector; } diff --git a/modules/angular2/test/transform/template_compiler/all_tests.dart b/modules/angular2/test/transform/template_compiler/all_tests.dart index cbf74cb54a..4345133dc9 100644 --- a/modules/angular2/test/transform/template_compiler/all_tests.dart +++ b/modules/angular2/test/transform/template_compiler/all_tests.dart @@ -7,6 +7,7 @@ import 'package:angular2/src/transform/common/asset_reader.dart'; import 'package:angular2/src/transform/common/logging.dart'; import 'package:angular2/src/transform/template_compiler/generator.dart'; import 'package:dart_style/dart_style.dart'; +import 'package:path/path.dart' as path; import 'package:guinness/guinness.dart'; import '../common/read_file.dart'; @@ -31,7 +32,7 @@ void changeDetectorTests() { // TODO(tbosch): This is just a temporary test that makes sure that the dart server and // dart browser is in sync. Change this to "not contains notifyBinding" // when https://github.com/angular/angular/issues/3019 is solved. - it('shouldn always notifyDispatcher for template variables', () async { + it('should not always notifyDispatcher for template variables', () async { var inputPath = 'template_compiler/ng_for_files/hello.ng_deps.dart'; var output = await (process(new AssetId('a', inputPath))); expect(output).toContain('notifyDispatcher'); @@ -90,7 +91,7 @@ void noChangeDetectorTests() { _formatThenExpectEquals(output, expected); }); - it('should not generated duplicate getters/setters', () async { + it('should not generate duplicate getters/setters', () async { var inputPath = 'template_compiler/duplicate_files/hello.ng_deps.dart'; var expected = readFile( 'template_compiler/duplicate_files/expected/hello.ng_deps.dart'); @@ -144,6 +145,15 @@ void noChangeDetectorTests() { output = await process(new AssetId('a', inputPath)); _formatThenExpectEquals(output, expected); }); + + it('should generate all expected getters, setters, & methods.', () async { + var base = 'template_compiler/registrations_files'; + var inputPath = path.join(base, 'registrations.ng_deps.dart'); + var expected = + readFile(path.join(base, 'expected/registrations.ng_deps.dart')); + var output = await process(new AssetId('a', inputPath)); + _formatThenExpectEquals(output, expected); + }); } void _formatThenExpectEquals(String actual, String expected) { diff --git a/modules/angular2/test/transform/template_compiler/duplicate_files/expected/hello.ng_deps.dart b/modules/angular2/test/transform/template_compiler/duplicate_files/expected/hello.ng_deps.dart index 1a75201ce0..aef59bea09 100644 --- a/modules/angular2/test/transform/template_compiler/duplicate_files/expected/hello.ng_deps.dart +++ b/modules/angular2/test/transform/template_compiler/duplicate_files/expected/hello.ng_deps.dart @@ -17,6 +17,5 @@ void initReflector(reflector) { ], const [ const [] ], () => new HelloCmp())) - ..registerGetters({'greeting': (o) => o.greeting}) - ..registerSetters({'greeting': (o, v) => o.greeting = v}); + ..registerGetters({'greeting': (o) => o.greeting}); } diff --git a/modules/angular2/test/transform/template_compiler/inline_expression_files/expected/hello.ng_deps.dart b/modules/angular2/test/transform/template_compiler/inline_expression_files/expected/hello.ng_deps.dart index ef96f7070c..252420270a 100644 --- a/modules/angular2/test/transform/template_compiler/inline_expression_files/expected/hello.ng_deps.dart +++ b/modules/angular2/test/transform/template_compiler/inline_expression_files/expected/hello.ng_deps.dart @@ -18,6 +18,5 @@ void initReflector(reflector) { const [] ], () => new HelloCmp())) ..registerGetters({'b': (o) => o.b, 'greeting': (o) => o.greeting}) - ..registerSetters( - {'b': (o, v) => o.b = v, 'greeting': (o, v) => o.greeting = v}); + ..registerSetters({'a': (o, v) => o.a = v}); } diff --git a/modules/angular2/test/transform/template_compiler/inline_method_files/expected/hello.ng_deps.dart b/modules/angular2/test/transform/template_compiler/inline_method_files/expected/hello.ng_deps.dart index ff3896471f..71934092dc 100644 --- a/modules/angular2/test/transform/template_compiler/inline_method_files/expected/hello.ng_deps.dart +++ b/modules/angular2/test/transform/template_compiler/inline_method_files/expected/hello.ng_deps.dart @@ -13,7 +13,7 @@ void initReflector(reflector) { HelloCmp, new ReflectionInfo(const [ const Component(selector: 'hello-app'), - const View(template: '') + const View(template: '') ], const [ const [] ], () => new HelloCmp())) diff --git a/modules/angular2/test/transform/template_compiler/inline_method_files/hello.ng_deps.dart b/modules/angular2/test/transform/template_compiler/inline_method_files/hello.ng_deps.dart index 0b78ebc1ed..94242be518 100644 --- a/modules/angular2/test/transform/template_compiler/inline_method_files/hello.ng_deps.dart +++ b/modules/angular2/test/transform/template_compiler/inline_method_files/hello.ng_deps.dart @@ -13,7 +13,7 @@ void initReflector(reflector) { HelloCmp, new ReflectionInfo(const [ const Component(selector: 'hello-app'), - const View(template: '') + const View(template: '') ], const [ const [] ], () => new HelloCmp())); diff --git a/modules/angular2/test/transform/template_compiler/registrations_files/dependency.ng_meta.json b/modules/angular2/test/transform/template_compiler/registrations_files/dependency.ng_meta.json new file mode 100644 index 0000000000..9c7d08e21f --- /dev/null +++ b/modules/angular2/test/transform/template_compiler/registrations_files/dependency.ng_meta.json @@ -0,0 +1,98 @@ +{ + "DependencyCmp": { + "kind": "type", + "value": { + "id": "DependencyCmp", + "selector": "dependency", + "compileChildren": true, + "hostProperties": {}, + "hostListeners": {}, + "hostActions": {}, + "hostAttributes": {}, + "properties": null, + "readAttributes": null, + "type": null, + "exportAs": null, + "callOnDestroy": null, + "callOnCheck": null, + "callOnInit": null, + "callOnChange": null, + "callOnAllChangesDone": null, + "events": ["dependencyEventName"], + "changeDetection": null, + "version": 1 + } + }, + "DirectiveProps": { + "kind": "type", + "value": { + "id": "DirectiveProps", + "selector": "[dir-props]", + "compileChildren": true, + "hostProperties": {"hprop": "hprop"}, + "hostListeners": {}, + "hostActions": {}, + "hostAttributes": {}, + "properties": ["prop"], + "readAttributes": null, + "type": null, + "exportAs": null, + "callOnDestroy": null, + "callOnCheck": null, + "callOnInit": null, + "callOnChange": null, + "callOnAllChangesDone": null, + "events": null, + "changeDetection": null, + "version": 1 + } + }, + "DirectiveEvents": { + "kind": "type", + "value": { + "id": "DirectiveEvents", + "selector": "[dir-events]", + "compileChildren": true, + "hostProperties": {}, + "hostListeners": {"subevent": "doAThing()"}, + "hostActions": {}, + "hostAttributes": {}, + "properties": [], + "readAttributes": null, + "type": null, + "exportAs": null, + "callOnDestroy": null, + "callOnCheck": null, + "callOnInit": null, + "callOnChange": null, + "callOnAllChangesDone": null, + "events": null, + "changeDetection": null, + "version": 1 + } + }, + "NgFor": { + "kind": "type", + "value": { + "id": "NgFor", + "selector": "[ng-for][ng-for-of]", + "compileChildren": true, + "hostProperties": {}, + "hostListeners": {}, + "hostActions": {}, + "hostAttributes": {}, + "properties": ["ngForOf"], + "readAttributes": null, + "type": null, + "exportAs": null, + "callOnDestroy": null, + "callOnCheck": true, + "callOnInit": null, + "callOnChange": null, + "callOnAllChangesDone": null, + "events": null, + "changeDetection": null, + "version": 1 + } + } +} \ No newline at end of file diff --git a/modules/angular2/test/transform/template_compiler/registrations_files/expected/registrations.ng_deps.dart b/modules/angular2/test/transform/template_compiler/registrations_files/expected/registrations.ng_deps.dart new file mode 100644 index 0000000000..b22dbca399 --- /dev/null +++ b/modules/angular2/test/transform/template_compiler/registrations_files/expected/registrations.ng_deps.dart @@ -0,0 +1,108 @@ +library examples.hello_world.index_common_dart.ng_deps.dart; + +import 'dependency.dart'; +import 'dependency.ng_deps.dart' as i0; +import 'hello.dart'; +import 'package:angular2/angular2.dart' + show Component, Directive, View, NgElement; + +var _visited = false; +void initReflector(reflector) { + if (_visited) return; + _visited = true; + reflector + ..registerType( + TextBindingsCmp, + new ReflectionInfo(const [ + const Component(selector: 'text'), + const View(template: '{{textBindings}}') + ], const [ + const [] + ], () => new TextBindingsCmp())) + ..registerType( + PropertyBindingsCmp, + new ReflectionInfo(const [ + const Component(selector: 'props'), + const View(template: '
') + ], const [ + const [] + ], () => new PropertyBindingsCmp())) + ..registerType( + EventsCmp, + new ReflectionInfo(const [ + const Component(selector: 'events', events: const ['eventName']), + const View(template: 'Hi') + ], const [ + const [] + ], () => new EventsCmp())) + ..registerType( + SubEventsCmp, + new ReflectionInfo(const [ + const Component(selector: 'sub-events'), + const View( + template: '', + directives: const [DependencyCmp]) + ], const [ + const [] + ], () => new SubEventsCmp())) + ..registerType( + TemplateEventsCmp, + new ReflectionInfo(const [ + const Component(selector: 'template-events'), + const View(template: '
') + ], const [ + const [] + ], () => new TemplateEventsCmp())) + ..registerType( + DirectivePropsCmp, + new ReflectionInfo(const [ + const Component(selector: 'directive-props-cmp'), + const View( + template: '
', + directives: const [DirectiveProps]) + ], const [ + const [] + ], () => new DirectivePropsCmp())) + ..registerType( + DirectiveEventsCmp, + new ReflectionInfo(const [ + const Component(selector: 'directive-events-cmp'), + const View( + template: '
', + directives: const [DirectiveEvents]) + ], const [ + const [] + ], () => new DirectiveEventsCmp())) + ..registerType( + RecursiveCmp, + new ReflectionInfo(const [ + const Component(selector: 'recursive-cmp'), + const View( + template: + '
  • test
  • ', + directives: const [NgFor]) + ], const [ + const [] + ], () => new RecursiveCmp())) + ..registerGetters({ + 'eventName': (o) => o.eventName, + 'hprop': (o) => o.hprop, + 'propValue': (o) => o.propValue, + 'textBindings': (o) => o.textBindings, + 'thing': (o) => o.thing, + 'things': (o) => o.things + }) + ..registerSetters({ + 'field': (o, v) => o.field = v, + 'hprop': (o, v) => o.hprop = v, + 'ngForOf': (o, v) => o.ngForOf = v, + 'prop': (o, v) => o.prop = v, + 'propName': (o, v) => o.propName = v, + 'recursiveProp': (o, v) => o.recursiveProp = v + }) + ..registerMethods({ + 'doAThing': (o, List args) => Function.apply(o.doAThing, args), + 'onMouseOver': (o, List args) => Function.apply(o.onMouseOver, args) + }); + i0.initReflector(); +} diff --git a/modules/angular2/test/transform/template_compiler/registrations_files/registrations.ng_deps.dart b/modules/angular2/test/transform/template_compiler/registrations_files/registrations.ng_deps.dart new file mode 100644 index 0000000000..1c60100437 --- /dev/null +++ b/modules/angular2/test/transform/template_compiler/registrations_files/registrations.ng_deps.dart @@ -0,0 +1,88 @@ +library examples.hello_world.index_common_dart.ng_deps.dart; + +import 'dependency.dart'; +import 'dependency.ng_deps.dart' as i0; +import 'hello.dart'; +import 'package:angular2/angular2.dart' + show Component, Directive, View, NgElement; + +var _visited = false; +void initReflector(reflector) { + if (_visited) return; + _visited = true; + reflector + ..registerType( + TextBindingsCmp, + new ReflectionInfo(const [ + const Component(selector: 'text'), + const View(template: '{{textBindings}}') + ], const [ + const [] + ], () => new TextBindingsCmp())) + ..registerType( + PropertyBindingsCmp, + new ReflectionInfo(const [ + const Component(selector: 'props'), + const View(template: '
    ') + ], const [ + const [] + ], () => new PropertyBindingsCmp())) + ..registerType( + EventsCmp, + new ReflectionInfo(const [ + const Component(selector: 'events', events: const ['eventName']), + const View(template: 'Hi') + ], const [ + const [] + ], () => new EventsCmp())) + ..registerType( + SubEventsCmp, + new ReflectionInfo(const [ + const Component(selector: 'sub-events'), + const View( + template: '', + directives: const [DependencyCmp]) + ], const [ + const [] + ], () => new SubEventsCmp())) + ..registerType( + TemplateEventsCmp, + new ReflectionInfo(const [ + const Component(selector: 'template-events'), + const View(template: '
    ') + ], const [ + const [] + ], () => new TemplateEventsCmp())) + ..registerType( + DirectivePropsCmp, + new ReflectionInfo(const [ + const Component(selector: 'directive-props-cmp'), + const View( + template: '
    ', + directives: const [DirectiveProps]) + ], const [ + const [] + ], () => new DirectivePropsCmp())) + ..registerType( + DirectiveEventsCmp, + new ReflectionInfo(const [ + const Component(selector: 'directive-events-cmp'), + const View( + template: '
    ', + directives: const [DirectiveEvents]) + ], const [ + const [] + ], () => new DirectiveEventsCmp())) + ..registerType( + RecursiveCmp, + new ReflectionInfo(const [ + const Component(selector: 'recursive-cmp'), + const View( + template: + '
  • test
  • ', + directives: const [NgFor]) + ], const [ + const [] + ], () => new RecursiveCmp())); + i0.initReflector(); +} diff --git a/modules/angular2/test/transform/template_compiler/registrations_files/registrations.ng_meta.json b/modules/angular2/test/transform/template_compiler/registrations_files/registrations.ng_meta.json new file mode 100644 index 0000000000..f6a72482d2 --- /dev/null +++ b/modules/angular2/test/transform/template_compiler/registrations_files/registrations.ng_meta.json @@ -0,0 +1,194 @@ +{ + "TextBindingsCmp": { + "kind": "type", + "value": { + "id": "TextBindingsCmp", + "selector": "text", + "compileChildren": true, + "hostProperties": {}, + "hostListeners": {}, + "hostActions": {}, + "hostAttributes": {}, + "properties": null, + "readAttributes": null, + "type": null, + "exportAs": null, + "callOnDestroy": null, + "callOnCheck": null, + "callOnInit": null, + "callOnChange": null, + "callOnAllChangesDone": null, + "events": null, + "changeDetection": null, + "version": 1 + } + }, + "PropertyBindingsCmp": { + "kind": "type", + "value": { + "id": "PropertyBindingsCmp", + "selector": "props", + "compileChildren": true, + "hostProperties": {}, + "hostListeners": {}, + "hostActions": {}, + "hostAttributes": {}, + "properties": [], + "readAttributes": [], + "type": 0, + "exportAs": null, + "callOnDestroy": false, + "callOnCheck": false, + "callOnInit": false, + "callOnChange": false, + "callOnAllChangesDone": false, + "events": [], + "changeDetection": null, + "version": 1 + } + }, + "EventsCmp": { + "kind": "type", + "value": { + "id": "EventsCmp", + "selector": "events", + "compileChildren": true, + "hostProperties": {}, + "hostListeners": {}, + "hostActions": {}, + "hostAttributes": {}, + "properties": [], + "readAttributes": [], + "type": 1, + "exportAs": null, + "callOnDestroy": false, + "callOnCheck": false, + "callOnInit": false, + "callOnChange": false, + "callOnAllChangesDone": false, + "events": ["eventName"], + "changeDetection": null, + "version": 1 + } + }, + "SubEventsCmp": { + "kind": "type", + "value": { + "id": "SubEventsCmp", + "selector": "events", + "compileChildren": true, + "hostProperties": {}, + "hostListeners": {}, + "hostActions": {}, + "hostAttributes": {}, + "properties": [], + "readAttributes": [], + "type": 1, + "exportAs": null, + "callOnDestroy": false, + "callOnCheck": false, + "callOnInit": false, + "callOnChange": false, + "callOnAllChangesDone": false, + "events": [], + "changeDetection": null, + "version": 1 + } + }, + "TemplateEventsCmp": { + "kind": "type", + "value": { + "id": "TemplateEventsCmp", + "selector": "template-events", + "compileChildren": true, + "hostProperties": {}, + "hostListeners": {}, + "hostActions": {}, + "hostAttributes": {}, + "properties": [], + "readAttributes": [], + "type": 1, + "exportAs": null, + "callOnDestroy": false, + "callOnCheck": false, + "callOnInit": false, + "callOnChange": false, + "callOnAllChangesDone": false, + "events": [], + "changeDetection": null, + "version": 1 + } + }, + "DirectivePropsCmp": { + "kind": "type", + "value": { + "id": "DirectivePropsCmp", + "selector": "directive-props-cmp", + "compileChildren": true, + "hostProperties": {}, + "hostListeners": {}, + "hostActions": {}, + "hostAttributes": {}, + "properties": [], + "readAttributes": [], + "type": 1, + "exportAs": null, + "callOnDestroy": false, + "callOnCheck": false, + "callOnInit": false, + "callOnChange": false, + "callOnAllChangesDone": false, + "events": [], + "changeDetection": null, + "version": 1 + } + }, + "DirectiveEventsCmp": { + "kind": "type", + "value": { + "id": "DirectiveEventsCmp", + "selector": "directive-events-cmp", + "compileChildren": true, + "hostProperties": {}, + "hostListeners": {}, + "hostActions": {}, + "hostAttributes": {}, + "properties": [], + "readAttributes": [], + "type": 1, + "exportAs": null, + "callOnDestroy": false, + "callOnCheck": false, + "callOnInit": false, + "callOnChange": false, + "callOnAllChangesDone": false, + "events": [], + "changeDetection": null, + "version": 1 + } + }, + "RecursiveCmp": { + "kind": "type", + "value": { + "id": "RecursiveCmp", + "selector": "recursive-cmp", + "compileChildren": true, + "hostProperties": {}, + "hostListeners": {}, + "hostActions": {}, + "hostAttributes": {}, + "properties": [], + "readAttributes": [], + "type": 1, + "exportAs": null, + "callOnDestroy": false, + "callOnCheck": false, + "callOnInit": false, + "callOnChange": false, + "callOnAllChangesDone": false, + "events": [], + "changeDetection": null, + "version": 1 + } + } +} \ No newline at end of file diff --git a/modules/angular2/test/transform/template_compiler/url_expression_files/expected/hello.ng_deps.dart b/modules/angular2/test/transform/template_compiler/url_expression_files/expected/hello.ng_deps.dart index 99f5d1b6bd..d7cf6ffdd6 100644 --- a/modules/angular2/test/transform/template_compiler/url_expression_files/expected/hello.ng_deps.dart +++ b/modules/angular2/test/transform/template_compiler/url_expression_files/expected/hello.ng_deps.dart @@ -17,6 +17,5 @@ void initReflector(reflector) { ], const [ const [] ], () => new HelloCmp())) - ..registerGetters({'greeting': (o) => o.greeting}) - ..registerSetters({'greeting': (o, v) => o.greeting = v}); + ..registerGetters({'greeting': (o) => o.greeting}); } diff --git a/modules/angular2/test/transform/template_compiler/with_prefix_files/expected/goodbye.ng_deps.dart b/modules/angular2/test/transform/template_compiler/with_prefix_files/expected/goodbye.ng_deps.dart index be86e23445..092b8c8241 100644 --- a/modules/angular2/test/transform/template_compiler/with_prefix_files/expected/goodbye.ng_deps.dart +++ b/modules/angular2/test/transform/template_compiler/with_prefix_files/expected/goodbye.ng_deps.dart @@ -17,6 +17,5 @@ void initReflector(reflector) { ], const [ const [] ], () => new GoodbyeCmp())) - ..registerGetters({'name': (o) => o.name}) - ..registerSetters({'name': (o, v) => o.name = v}); + ..registerGetters({'name': (o) => o.name}); } diff --git a/modules/angular2/test/transform/template_compiler/with_prefix_files/expected/ng2_prefix.ng_deps.dart b/modules/angular2/test/transform/template_compiler/with_prefix_files/expected/ng2_prefix.ng_deps.dart index 29c3fb4fe9..9b4eadc31d 100644 --- a/modules/angular2/test/transform/template_compiler/with_prefix_files/expected/ng2_prefix.ng_deps.dart +++ b/modules/angular2/test/transform/template_compiler/with_prefix_files/expected/ng2_prefix.ng_deps.dart @@ -17,6 +17,5 @@ void initReflector(reflector) { ], const [ const [] ], () => new MyApp())) - ..registerGetters({'name': (o) => o.name}) - ..registerSetters({'name': (o, v) => o.name = v}); + ..registerGetters({'name': (o) => o.name}); }