X Tutup
Skip to content

Commit c2a60f1

Browse files
committed
feat(core): add support for @ContentChild and @ViewChild
Closes #4251
1 parent 2e9de0b commit c2a60f1

File tree

8 files changed

+313
-23
lines changed

8 files changed

+313
-23
lines changed

modules/angular2/src/core/compiler/directive_resolver.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@ import {
1010
HostBindingMetadata,
1111
HostListenerMetadata,
1212
ContentChildrenMetadata,
13-
ViewChildrenMetadata
13+
ViewChildrenMetadata,
14+
ContentChildMetadata,
15+
ViewChildMetadata
1416
} from 'angular2/src/core/metadata';
1517
import {reflector} from 'angular2/src/core/reflection/reflection';
1618

17-
/**
19+
/*
1820
* Resolve a `Type` for {@link DirectiveMetadata}.
1921
*
2022
* This interface can be overridden by the application developer to create custom behavior.
@@ -86,6 +88,14 @@ export class DirectiveResolver {
8688
if (a instanceof ViewChildrenMetadata) {
8789
queries[propName] = a;
8890
}
91+
92+
if (a instanceof ContentChildMetadata) {
93+
queries[propName] = a;
94+
}
95+
96+
if (a instanceof ViewChildMetadata) {
97+
queries[propName] = a;
98+
}
8999
});
90100
});
91101
return this._merge(dm, properties, events, host, queries);

modules/angular2/src/core/compiler/element_injector.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -967,7 +967,11 @@ export class QueryRef {
967967
// TODO delete the check once only field queries are supported
968968
if (isPresent(this.dirIndex)) {
969969
var dir = this.originator.getDirectiveAtIndex(this.dirIndex);
970-
this.setter(dir, this.list);
970+
if (this.query.first) {
971+
this.setter(dir, this.list.length > 0 ? this.list.first : null);
972+
} else {
973+
this.setter(dir, this.list);
974+
}
971975
}
972976
}
973977

modules/angular2/src/core/metadata.dart

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,14 @@ class ContentChildren extends ContentChildrenMetadata {
102102
: super(selector, descendants: descendants);
103103
}
104104

105+
/**
106+
* See: [ContentChildMetadata] for docs.
107+
*/
108+
class ContentChild extends ContentChildMetadata {
109+
const ContentChild(dynamic /*Type | string*/ selector)
110+
: super(selector);
111+
}
112+
105113
/**
106114
* See: [ViewQueryMetadata] for docs.
107115
*/
@@ -118,6 +126,14 @@ class ViewChildren extends ViewChildrenMetadata {
118126
: super(selector);
119127
}
120128

129+
/**
130+
* See: [ViewChildMetadata] for docs.
131+
*/
132+
class ViewChild extends ViewChildMetadata {
133+
const ViewChild(dynamic /*Type | string*/ selector)
134+
: super(selector);
135+
}
136+
121137
/**
122138
* See: [PropertyMetadata] for docs.
123139
*/

modules/angular2/src/core/metadata.ts

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@
66
export {
77
QueryMetadata,
88
ContentChildrenMetadata,
9+
ContentChildMetadata,
910
ViewChildrenMetadata,
1011
ViewQueryMetadata,
11-
AttributeMetadata,
12+
ViewChildMetadata,
13+
AttributeMetadata
1214
} from './metadata/di';
1315

1416
export {
@@ -26,9 +28,11 @@ export {ViewMetadata, ViewEncapsulation} from './metadata/view';
2628
import {
2729
QueryMetadata,
2830
ContentChildrenMetadata,
31+
ContentChildMetadata,
2932
ViewChildrenMetadata,
33+
ViewChildMetadata,
3034
ViewQueryMetadata,
31-
AttributeMetadata,
35+
AttributeMetadata
3236
} from './metadata/di';
3337

3438
import {
@@ -408,11 +412,22 @@ export interface ContentChildrenFactory {
408412
new (selector: Type | string, {descendants}?: {descendants?: boolean}): ContentChildrenMetadata;
409413
}
410414

415+
export interface ContentChildFactory {
416+
(selector: Type | string): any;
417+
new (selector: Type | string): ContentChildFactory;
418+
}
419+
411420
export interface ViewChildrenFactory {
412421
(selector: Type | string): any;
413422
new (selector: Type | string): ViewChildrenMetadata;
414423
}
415424

425+
export interface ViewChildFactory {
426+
(selector: Type | string): any;
427+
new (selector: Type | string): ViewChildFactory;
428+
}
429+
430+
416431
/**
417432
* {@link PipeMetadata} factory for creating decorators.
418433
*
@@ -546,13 +561,23 @@ export var Query: QueryFactory = makeParamDecorator(QueryMetadata);
546561
*/
547562
export var ContentChildren: ContentChildrenFactory = makePropDecorator(ContentChildrenMetadata);
548563

564+
/**
565+
* {@link ContentChildMetadata} factory function.
566+
*/
567+
export var ContentChild: ContentChildFactory = makePropDecorator(ContentChildMetadata);
568+
549569
/**
550570
* {@link ViewChildrenMetadata} factory function.
551571
*/
552572
export var ViewChildren: ViewChildrenFactory = makePropDecorator(ViewChildrenMetadata);
553573

554574
/**
555-
* {@link ViewQueryMetadata} factory function.
575+
* {@link ViewChildMetadata} factory function.
576+
*/
577+
export var ViewChild: ViewChildFactory = makePropDecorator(ViewChildMetadata);
578+
579+
/**
580+
* {@link di/ViewQueryMetadata} factory function.
556581
*/
557582
export var ViewQuery: QueryFactory = makeParamDecorator(ViewQueryMetadata);
558583

modules/angular2/src/core/metadata/di.ts

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -170,11 +170,13 @@ export class QueryMetadata extends DependencyMetadata {
170170
* children (true).
171171
*/
172172
descendants: boolean;
173+
first: boolean;
173174

174175
constructor(private _selector: Type | string,
175-
{descendants = false}: {descendants?: boolean} = {}) {
176+
{descendants = false, first = false}: {descendants?: boolean, first?: boolean} = {}) {
176177
super();
177178
this.descendants = descendants;
179+
this.first = first;
178180
}
179181

180182
/**
@@ -229,6 +231,32 @@ export class ContentChildrenMetadata extends QueryMetadata {
229231
}
230232
}
231233

234+
// TODO: add an example after ContentChild and ViewChild are in master
235+
/**
236+
* Configures a content query.
237+
*
238+
* Content queries are set before the `afterContentInit` callback is called.
239+
*
240+
* ### Example
241+
*
242+
* ```
243+
* @Directive({
244+
* selector: 'someDir'
245+
* })
246+
* class SomeDir {
247+
* @ContentChild(ChildDirective) contentChild;
248+
*
249+
* afterContentInit() {
250+
* // contentChild is set
251+
* }
252+
* }
253+
* ```
254+
*/
255+
@CONST()
256+
export class ContentChildMetadata extends QueryMetadata {
257+
constructor(_selector: Type | string) { super(_selector, {descendants: true, first: true}); }
258+
}
259+
232260
/**
233261
* Similar to {@link QueryMetadata}, but querying the component view, instead of
234262
* the content children.
@@ -266,8 +294,9 @@ export class ContentChildrenMetadata extends QueryMetadata {
266294
*/
267295
@CONST()
268296
export class ViewQueryMetadata extends QueryMetadata {
269-
constructor(_selector: Type | string, {descendants = false}: {descendants?: boolean} = {}) {
270-
super(_selector, {descendants: descendants});
297+
constructor(_selector: Type | string,
298+
{descendants = false, first = false}: {descendants?: boolean, first?: boolean} = {}) {
299+
super(_selector, {descendants: descendants, first: first});
271300
}
272301

273302
/**
@@ -302,3 +331,29 @@ export class ViewQueryMetadata extends QueryMetadata {
302331
export class ViewChildrenMetadata extends ViewQueryMetadata {
303332
constructor(_selector: Type | string) { super(_selector, {descendants: true}); }
304333
}
334+
335+
/**
336+
* Configures a view query.
337+
*
338+
* View queries are set before the `afterViewInit` callback is called.
339+
*
340+
* ### Example
341+
*
342+
* ```
343+
* @Component({
344+
* selector: 'someDir'
345+
* })
346+
* @View({templateUrl: 'someTemplate', directives: [ItemDirective]})
347+
* class SomeDir {
348+
* @ViewChild(ItemDirective) viewChild:ItemDirective;
349+
*
350+
* afterViewInit() {
351+
* // viewChild is set
352+
* }
353+
* }
354+
* ```
355+
*/
356+
@CONST()
357+
export class ViewChildMetadata extends ViewQueryMetadata {
358+
constructor(_selector: Type | string) { super(_selector, {descendants: true, first: true}); }
359+
}

modules/angular2/test/core/compiler/directive_resolver_spec.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@ import {
1010
ContentChildren,
1111
ContentChildrenMetadata,
1212
ViewChildren,
13-
ViewChildrenMetadata
13+
ViewChildrenMetadata,
14+
ContentChild,
15+
ContentChildMetadata,
16+
ViewChild,
17+
ViewChildMetadata
1418
} from 'angular2/src/core/metadata';
1519

1620
@Directive({selector: 'someDirective'})
@@ -80,6 +84,18 @@ class SomeDirectiveWithViewChildren {
8084
c;
8185
}
8286

87+
@Directive({selector: 'someDirective', queries: {"c": new ContentChild("c")}})
88+
class SomeDirectiveWithContentChild {
89+
@ContentChild("a") a: any;
90+
c;
91+
}
92+
93+
@Directive({selector: 'someDirective', queries: {"c": new ViewChild("c")}})
94+
class SomeDirectiveWithViewChild {
95+
@ViewChild("a") a: any;
96+
c;
97+
}
98+
8399
class SomeDirectiveWithoutMetadata {}
84100

85101
export function main() {
@@ -156,6 +172,18 @@ export function main() {
156172
expect(directiveMetadata.queries)
157173
.toEqual({"cs": new ViewChildren("c"), "as": new ViewChildren("a")});
158174
});
175+
176+
it('should append ContentChild', () => {
177+
var directiveMetadata = resolver.resolve(SomeDirectiveWithContentChild);
178+
expect(directiveMetadata.queries)
179+
.toEqual({"c": new ContentChild("c"), "a": new ContentChild("a")});
180+
});
181+
182+
it('should append ViewChild', () => {
183+
var directiveMetadata = resolver.resolve(SomeDirectiveWithViewChild);
184+
expect(directiveMetadata.queries)
185+
.toEqual({"c": new ViewChild("c"), "a": new ViewChild("a")});
186+
});
159187
});
160188
});
161189
}

0 commit comments

Comments
 (0)
X Tutup