X Tutup
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 14 additions & 12 deletions modules/playground/src/order_management/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ import {
NgFor,
Component,
Directive,
View,
Host,
forwardRef,
Provider,
EventEmitter,
FORM_DIRECTIVES,
Injectable
Injectable,
Input,
Output
} from 'angular2/core';

import {ListWrapper} from 'angular2/src/core/facade/collection';
Expand Down Expand Up @@ -79,8 +80,8 @@ class DataService {

// ---- components

@Component({selector: 'order-list-cmp'})
@View({
@Component({
selector: 'order-list-cmp',
template: `
<h1>Orders</h1>
<div *ng-for="#order of orders" [class.warning]="order.total > order.limit">
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While you are at it, feel free to add a timy little leading space :)

Expand Down Expand Up @@ -116,8 +117,8 @@ class OrderListComponent {
}


@Component({selector: 'order-item-cmp', inputs: ['item'], outputs: ['delete']})
@View({
@Component({
selector: 'order-item-cmp',
template: `
<div>
<div>
Expand All @@ -143,14 +144,14 @@ class OrderListComponent {
directives: [FORM_DIRECTIVES]
})
class OrderItemComponent {
item: OrderItem;
delete = new EventEmitter();
@Input() item: OrderItem;
@Output() delete = new EventEmitter();

onDelete(): void { this.delete.next(this.item); }
}

@Component({selector: 'order-details-cmp'})
@View({
@Component({
selector: 'order-details-cmp',
template: `
<div *ng-if="order !== null">
<h1>Selected Order</h1>
Expand Down Expand Up @@ -189,8 +190,9 @@ class OrderDetailsComponent {
addItem(): void { this._service.addItemForOrder(this.order); }
}

@Component({selector: 'order-management-app', bindings: [DataService]})
@View({
@Component({
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can also remove View from the imports.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

selector: 'order-management-app',
providers: [DataService],
template: `
<order-list-cmp></order-list-cmp>
<order-details-cmp></order-details-cmp>
Expand Down
16 changes: 7 additions & 9 deletions modules/playground/src/zippy_component/zippy.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
import {Component, View, EventEmitter} from 'angular2/angular2';
import {Component, EventEmitter, Input, Output} from 'angular2/angular2';
import {ObservableWrapper} from 'angular2/src/core/facade/async';

@Component(
{selector: 'zippy', inputs: ['title'], outputs: ['openHandler: open', 'closeHandler: close']})
@View({templateUrl: 'zippy.html'})
@Component({selector: 'zippy', templateUrl: 'zippy.html'})
export class Zippy {
visible: boolean = true;
title: string = '';
openHandler: EventEmitter<any> = new EventEmitter();
closeHandler: EventEmitter<any> = new EventEmitter();
@Input() title: string = '';
@Output() open: EventEmitter<any> = new EventEmitter();
@Output() close: EventEmitter<any> = new EventEmitter();

toggle() {
this.visible = !this.visible;
if (this.visible) {
ObservableWrapper.callNext(this.openHandler, null);
ObservableWrapper.callNext(this.open, null);
} else {
ObservableWrapper.callNext(this.closeHandler, null);
ObservableWrapper.callNext(this.close, null);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,95 @@ class _DirectiveMetadataVisitor extends Object
return null;
}

@override
Object visitFieldDeclaration(FieldDeclaration node) {
for (var variable in node.fields.variables) {
for (var meta in node.metadata) {
if (_isAnnotation(meta, 'Output')) {
final renamed = _getRenamedValue(meta);
if (renamed != null) {
_outputs.add('${variable.name}: ${renamed}');
} else {
_outputs.add('${variable.name}');
}
}

if (_isAnnotation(meta, 'Input')) {
final renamed = _getRenamedValue(meta);
if (renamed != null) {
_inputs.add('${variable.name}: ${renamed}');
} else {
_inputs.add('${variable.name}');
}
}

if (_isAnnotation(meta, 'HostBinding')) {
final renamed = _getRenamedValue(meta);
if (renamed != null) {
_host['[${renamed}]'] = '${variable.name}';
} else {
_host['[${variable.name}]'] = '${variable.name}';
}
}
}
}
return null;
}

@override
Object visitMethodDeclaration(MethodDeclaration node) {
for (var meta in node.metadata) {
if (_isAnnotation(meta, 'HostListener')) {
if (meta.arguments.arguments.length == 0 || meta.arguments.arguments.length > 2) {
throw new ArgumentError(
'Incorrect value passed to HostListener. Expected 1 or 2.');
}

final eventName = _getHostListenerEventName(meta);
final params = _getHostListenerParams(meta);
_host['(${eventName})'] = '${node.name}($params)';
}
}
return null;
}

//TODO Use AnnotationMatcher instead of string matching
bool _isAnnotation(Annotation node, String annotationName) {
var id = node.name;
final name = id is PrefixedIdentifier ? '${id.identifier}' : '$id';
return name == annotationName;
}

String _getRenamedValue(Annotation node) {
if (node.arguments.arguments.length == 1) {
final renamed = naiveEval(node.arguments.arguments.single);
if (renamed is! String) {
throw new ArgumentError(
'Incorrect value. Expected a String, but got "${renamed}".');
}
return renamed;
} else {
return null;
}
}

String _getHostListenerEventName(Annotation node) {
final name = naiveEval(node.arguments.arguments.first);
if (name is! String) {
throw new ArgumentError(
'Incorrect event name. Expected a String, but got "${name}".');
}
return name;
}

String _getHostListenerParams(Annotation node) {
if (node.arguments.arguments.length == 2) {
return naiveEval(node.arguments.arguments[1]).join(',');
} else {
return "";
}
}

@override
Object visitClassDeclaration(ClassDeclaration node) {
node.metadata.accept(this);
Expand All @@ -237,6 +326,8 @@ class _DirectiveMetadataVisitor extends Object
_lifecycleHooks = node.implementsClause != null
? node.implementsClause.accept(_lifecycleVisitor)
: const [];

node.members.accept(this);
}
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,34 @@ void allTests() {
..prefix = 'dep2');
});

it('should merge `outputs` from the annotation and fields.',
() async {
var model = await _testCreateModel('directives_files/components.dart');
expect(model.types['ComponentWithOutputs'].outputs).
toEqual({'a': 'a', 'b': 'b', 'c': 'renamed'});
});

it('should merge `inputs` from the annotation and fields.',
() async {
var model = await _testCreateModel('directives_files/components.dart');
expect(model.types['ComponentWithInputs'].inputs).
toEqual({'a': 'a', 'b': 'b', 'c': 'renamed'});
});

it('should merge host bindings from the annotation and fields.',
() async {
var model = await _testCreateModel('directives_files/components.dart');
expect(model.types['ComponentWithHostBindings'].hostProperties).
toEqual({'a': 'a', 'b': 'b', 'renamed': 'c'});
});

it('should merge host listeners from the annotation and fields.',
() async {
var model = await _testCreateModel('directives_files/components.dart');
expect(model.types['ComponentWithHostListeners'].hostListeners).
toEqual({'a': 'onA()', 'b': 'onB()', 'c': 'onC(\$event.target,\$event.target.value)'});
});

it('should warn if @Component has a `template` and @View is present.',
() async {
final logger = new RecordingLogger();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
library angular2.test.transform.directive_processor.directive_files.components;

import 'package:angular2/angular2.dart'
show Component, Directive, View, NgElement;
show Component, Directive, View, NgElement, Output, Input;
import 'dep1.dart';
import 'dep2.dart' as dep2;

Expand All @@ -18,3 +18,47 @@ class ViewFirst {}
template: '<dep1></dep1><dep2></dep2>',
directives: [Dep, dep2.Dep])
class ComponentOnly {}

@Component(
selector: 'component-with-outputs',
template: '<dep1></dep1><dep2></dep2>',
outputs: ['a']
)
class ComponentWithOutputs {
@Output() Object b;
@Output('renamed') Object c;
}

@Component(
selector: 'component-with-inputs',
template: '<dep1></dep1><dep2></dep2>',
inputs: ['a']
)
class ComponentWithInputs {
@Input() Object b;
@Input('renamed') Object c;
}

@Component(
selector: 'component-with-inputs',
template: '<dep1></dep1><dep2></dep2>',
host: {
'[a]':'a'
}
)
class ComponentWithHostBindings {
@HostBinding() Object b;
@HostBinding('renamed') Object c;
}

@Component(
selector: 'component-with-inputs',
template: '<dep1></dep1><dep2></dep2>',
host: {
'(a)':'onA()'
}
)
class ComponentWithHostListeners {
@HostListener('b') void onB() {}
@HostListener('c', ['\$event.target', '\$event.target.value']) void onC(t,v) {}
}
X Tutup