diff --git a/modules/angular2/src/core/compiler/directive_resolver.ts b/modules/angular2/src/core/compiler/directive_resolver.ts
index 49819bfd08..53a70fec3e 100644
--- a/modules/angular2/src/core/compiler/directive_resolver.ts
+++ b/modules/angular2/src/core/compiler/directive_resolver.ts
@@ -10,11 +10,13 @@ import {
HostBindingMetadata,
HostListenerMetadata,
ContentChildrenMetadata,
- ViewChildrenMetadata
+ ViewChildrenMetadata,
+ ContentChildMetadata,
+ ViewChildMetadata
} from 'angular2/src/core/metadata';
import {reflector} from 'angular2/src/core/reflection/reflection';
-/**
+/*
* Resolve a `Type` for {@link DirectiveMetadata}.
*
* This interface can be overridden by the application developer to create custom behavior.
@@ -86,6 +88,14 @@ export class DirectiveResolver {
if (a instanceof ViewChildrenMetadata) {
queries[propName] = a;
}
+
+ if (a instanceof ContentChildMetadata) {
+ queries[propName] = a;
+ }
+
+ if (a instanceof ViewChildMetadata) {
+ queries[propName] = a;
+ }
});
});
return this._merge(dm, properties, events, host, queries);
diff --git a/modules/angular2/src/core/compiler/element_injector.ts b/modules/angular2/src/core/compiler/element_injector.ts
index d3e8eb6d66..50051c1bd6 100644
--- a/modules/angular2/src/core/compiler/element_injector.ts
+++ b/modules/angular2/src/core/compiler/element_injector.ts
@@ -967,7 +967,11 @@ export class QueryRef {
// TODO delete the check once only field queries are supported
if (isPresent(this.dirIndex)) {
var dir = this.originator.getDirectiveAtIndex(this.dirIndex);
- this.setter(dir, this.list);
+ if (this.query.first) {
+ this.setter(dir, this.list.length > 0 ? this.list.first : null);
+ } else {
+ this.setter(dir, this.list);
+ }
}
}
diff --git a/modules/angular2/src/core/metadata.dart b/modules/angular2/src/core/metadata.dart
index 06df4f3fae..1301849f91 100644
--- a/modules/angular2/src/core/metadata.dart
+++ b/modules/angular2/src/core/metadata.dart
@@ -102,6 +102,14 @@ class ContentChildren extends ContentChildrenMetadata {
: super(selector, descendants: descendants);
}
+/**
+ * See: [ContentChildMetadata] for docs.
+ */
+class ContentChild extends ContentChildMetadata {
+ const ContentChild(dynamic /*Type | string*/ selector)
+ : super(selector);
+}
+
/**
* See: [ViewQueryMetadata] for docs.
*/
@@ -118,6 +126,14 @@ class ViewChildren extends ViewChildrenMetadata {
: super(selector);
}
+/**
+ * See: [ViewChildMetadata] for docs.
+ */
+class ViewChild extends ViewChildMetadata {
+ const ViewChild(dynamic /*Type | string*/ selector)
+ : super(selector);
+}
+
/**
* See: [PropertyMetadata] for docs.
*/
diff --git a/modules/angular2/src/core/metadata.ts b/modules/angular2/src/core/metadata.ts
index dd0070807c..75c596a459 100644
--- a/modules/angular2/src/core/metadata.ts
+++ b/modules/angular2/src/core/metadata.ts
@@ -6,9 +6,11 @@
export {
QueryMetadata,
ContentChildrenMetadata,
+ ContentChildMetadata,
ViewChildrenMetadata,
ViewQueryMetadata,
- AttributeMetadata,
+ ViewChildMetadata,
+ AttributeMetadata
} from './metadata/di';
export {
@@ -26,9 +28,11 @@ export {ViewMetadata, ViewEncapsulation} from './metadata/view';
import {
QueryMetadata,
ContentChildrenMetadata,
+ ContentChildMetadata,
ViewChildrenMetadata,
+ ViewChildMetadata,
ViewQueryMetadata,
- AttributeMetadata,
+ AttributeMetadata
} from './metadata/di';
import {
@@ -408,11 +412,22 @@ export interface ContentChildrenFactory {
new (selector: Type | string, {descendants}?: {descendants?: boolean}): ContentChildrenMetadata;
}
+export interface ContentChildFactory {
+ (selector: Type | string): any;
+ new (selector: Type | string): ContentChildFactory;
+}
+
export interface ViewChildrenFactory {
(selector: Type | string): any;
new (selector: Type | string): ViewChildrenMetadata;
}
+export interface ViewChildFactory {
+ (selector: Type | string): any;
+ new (selector: Type | string): ViewChildFactory;
+}
+
+
/**
* {@link PipeMetadata} factory for creating decorators.
*
@@ -546,13 +561,23 @@ export var Query: QueryFactory = makeParamDecorator(QueryMetadata);
*/
export var ContentChildren: ContentChildrenFactory = makePropDecorator(ContentChildrenMetadata);
+/**
+ * {@link ContentChildMetadata} factory function.
+ */
+export var ContentChild: ContentChildFactory = makePropDecorator(ContentChildMetadata);
+
/**
* {@link ViewChildrenMetadata} factory function.
*/
export var ViewChildren: ViewChildrenFactory = makePropDecorator(ViewChildrenMetadata);
/**
- * {@link ViewQueryMetadata} factory function.
+ * {@link ViewChildMetadata} factory function.
+ */
+export var ViewChild: ViewChildFactory = makePropDecorator(ViewChildMetadata);
+
+/**
+ * {@link di/ViewQueryMetadata} factory function.
*/
export var ViewQuery: QueryFactory = makeParamDecorator(ViewQueryMetadata);
diff --git a/modules/angular2/src/core/metadata/di.ts b/modules/angular2/src/core/metadata/di.ts
index 85539047ef..13fdd8a258 100644
--- a/modules/angular2/src/core/metadata/di.ts
+++ b/modules/angular2/src/core/metadata/di.ts
@@ -170,11 +170,13 @@ export class QueryMetadata extends DependencyMetadata {
* children (true).
*/
descendants: boolean;
+ first: boolean;
constructor(private _selector: Type | string,
- {descendants = false}: {descendants?: boolean} = {}) {
+ {descendants = false, first = false}: {descendants?: boolean, first?: boolean} = {}) {
super();
this.descendants = descendants;
+ this.first = first;
}
/**
@@ -229,6 +231,32 @@ export class ContentChildrenMetadata extends QueryMetadata {
}
}
+// TODO: add an example after ContentChild and ViewChild are in master
+/**
+ * Configures a content query.
+ *
+ * Content queries are set before the `afterContentInit` callback is called.
+ *
+ * ### Example
+ *
+ * ```
+ * @Directive({
+ * selector: 'someDir'
+ * })
+ * class SomeDir {
+ * @ContentChild(ChildDirective) contentChild;
+ *
+ * afterContentInit() {
+ * // contentChild is set
+ * }
+ * }
+ * ```
+ */
+@CONST()
+export class ContentChildMetadata extends QueryMetadata {
+ constructor(_selector: Type | string) { super(_selector, {descendants: true, first: true}); }
+}
+
/**
* Similar to {@link QueryMetadata}, but querying the component view, instead of
* the content children.
@@ -266,8 +294,9 @@ export class ContentChildrenMetadata extends QueryMetadata {
*/
@CONST()
export class ViewQueryMetadata extends QueryMetadata {
- constructor(_selector: Type | string, {descendants = false}: {descendants?: boolean} = {}) {
- super(_selector, {descendants: descendants});
+ constructor(_selector: Type | string,
+ {descendants = false, first = false}: {descendants?: boolean, first?: boolean} = {}) {
+ super(_selector, {descendants: descendants, first: first});
}
/**
@@ -302,3 +331,29 @@ export class ViewQueryMetadata extends QueryMetadata {
export class ViewChildrenMetadata extends ViewQueryMetadata {
constructor(_selector: Type | string) { super(_selector, {descendants: true}); }
}
+
+/**
+ * Configures a view query.
+ *
+ * View queries are set before the `afterViewInit` callback is called.
+ *
+ * ### Example
+ *
+ * ```
+ * @Component({
+ * selector: 'someDir'
+ * })
+ * @View({templateUrl: 'someTemplate', directives: [ItemDirective]})
+ * class SomeDir {
+ * @ViewChild(ItemDirective) viewChild:ItemDirective;
+ *
+ * afterViewInit() {
+ * // viewChild is set
+ * }
+ * }
+ * ```
+ */
+@CONST()
+export class ViewChildMetadata extends ViewQueryMetadata {
+ constructor(_selector: Type | string) { super(_selector, {descendants: true, first: true}); }
+}
\ No newline at end of file
diff --git a/modules/angular2/test/core/compiler/directive_resolver_spec.ts b/modules/angular2/test/core/compiler/directive_resolver_spec.ts
index f2550ba023..ef80e60a61 100644
--- a/modules/angular2/test/core/compiler/directive_resolver_spec.ts
+++ b/modules/angular2/test/core/compiler/directive_resolver_spec.ts
@@ -10,7 +10,11 @@ import {
ContentChildren,
ContentChildrenMetadata,
ViewChildren,
- ViewChildrenMetadata
+ ViewChildrenMetadata,
+ ContentChild,
+ ContentChildMetadata,
+ ViewChild,
+ ViewChildMetadata
} from 'angular2/src/core/metadata';
@Directive({selector: 'someDirective'})
@@ -80,6 +84,18 @@ class SomeDirectiveWithViewChildren {
c;
}
+@Directive({selector: 'someDirective', queries: {"c": new ContentChild("c")}})
+class SomeDirectiveWithContentChild {
+ @ContentChild("a") a: any;
+ c;
+}
+
+@Directive({selector: 'someDirective', queries: {"c": new ViewChild("c")}})
+class SomeDirectiveWithViewChild {
+ @ViewChild("a") a: any;
+ c;
+}
+
class SomeDirectiveWithoutMetadata {}
export function main() {
@@ -156,6 +172,18 @@ export function main() {
expect(directiveMetadata.queries)
.toEqual({"cs": new ViewChildren("c"), "as": new ViewChildren("a")});
});
+
+ it('should append ContentChild', () => {
+ var directiveMetadata = resolver.resolve(SomeDirectiveWithContentChild);
+ expect(directiveMetadata.queries)
+ .toEqual({"c": new ContentChild("c"), "a": new ContentChild("a")});
+ });
+
+ it('should append ViewChild', () => {
+ var directiveMetadata = resolver.resolve(SomeDirectiveWithViewChild);
+ expect(directiveMetadata.queries)
+ .toEqual({"c": new ViewChild("c"), "a": new ViewChild("a")});
+ });
});
});
}
diff --git a/modules/angular2/test/core/compiler/query_integration_spec.ts b/modules/angular2/test/core/compiler/query_integration_spec.ts
index 00e22f6c2e..f2a45e11ed 100644
--- a/modules/angular2/test/core/compiler/query_integration_spec.ts
+++ b/modules/angular2/test/core/compiler/query_integration_spec.ts
@@ -12,6 +12,7 @@ import {
TestComponentBuilder,
} from 'angular2/test_lib';
+import {isPresent} from 'angular2/src/core/facade/lang';
import {
Component,
@@ -27,8 +28,12 @@ import {
ViewQuery,
ContentChildren,
ViewChildren,
+ ContentChild,
+ ViewChild,
AfterContentInit,
- AfterViewInit
+ AfterViewInit,
+ AfterContentChecked,
+ AfterViewChecked
} from 'angular2/core';
import {asNativeElements} from 'angular2/src/core/debug';
@@ -81,6 +86,63 @@ export function main() {
});
}));
+ it('should contain the first content child',
+ inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
+ var template =
+ '