feat(query): initial implementation of view query.

ViewQuery is a new API that allows a component to query its view.

Closes #1935
This commit is contained in:
Rado Kirov
2015-07-10 10:30:31 -07:00
parent 1eab4f5f07
commit 7ee6963f5d
9 changed files with 223 additions and 10 deletions

View File

@ -1,6 +1,6 @@
import {ComponentAnnotation, DirectiveAnnotation, LifecycleEvent} from './annotations';
import {ViewAnnotation} from './view';
import {AttributeAnnotation, QueryAnnotation} from './di';
import {AttributeAnnotation, QueryAnnotation, ViewQueryAnnotation} from './di';
import {
makeDecorator,
makeParamDecorator,
@ -368,3 +368,9 @@ export var Attribute: AttributeFactory = makeParamDecorator(AttributeAnnotation)
* {@link Query} factory function.
*/
export var Query: QueryFactory = makeParamDecorator(QueryAnnotation);
/**
* {@link ViewQuery} factory function.
*/
export var ViewQuery: QueryFactory = makeParamDecorator(ViewQueryAnnotation);

View File

@ -1,4 +1,5 @@
export {
Query as QueryAnnotation,
ViewQuery as ViewQueryAnnotation,
Attribute as AttributeAnnotation,
} from '../annotations_impl/di';

View File

@ -57,6 +57,8 @@ export class Query extends DependencyMetadata {
this.descendants = descendants;
}
get isViewQuery() { return false; }
get selector() { return resolveForwardRef(this._selector); }
get isVarBindingQuery(): boolean { return isString(this.selector); }
@ -65,3 +67,20 @@ export class Query extends DependencyMetadata {
toString(): string { return `@Query(${stringify(this.selector)})`; }
}
/**
* Specifies that a {@link QueryList} should be injected.
*
* See {@link QueryList} for usage and example.
*
* @exportedAs angular2/annotations
*/
@CONST()
export class ViewQuery extends Query {
constructor(_selector: Type | string, {descendants = false}: {descendants?: boolean} = {}) {
super(_selector, {descendants: descendants});
}
get isViewQuery() { return true; }
toString(): string { return `@ViewQuery(${stringify(this.selector)})`; }
}

View File

@ -45,4 +45,9 @@ class BaseQueryList<T> extends Object with IterableMixin<T> {
get length => _results.length;
get first => _results.first;
get last => _results.last;
List map(fn(T)) {
// Note: we need to return a list instead of iterable to match JS.
return this._results.map(fn).toList();
}
}

View File

@ -38,4 +38,6 @@ export class BaseQueryList<T> {
get length() { return this._results.length; }
get first() { return ListWrapper.first(this._results); }
get last() { return ListWrapper.last(this._results); }
map<U>(fn: (T) => U): U[] { return this._results.map(fn); }
}

View File

@ -434,7 +434,7 @@ export class ElementInjector extends TreeNode<ElementInjector> implements Depend
private _preBuiltObjects = null;
// Queries are added during construction or linking with a new parent.
// They are never removed.
// They are removed only through unlinking.
private _query0: QueryRef;
private _query1: QueryRef;
private _query2: QueryRef;
@ -487,6 +487,10 @@ export class ElementInjector extends TreeNode<ElementInjector> implements Depend
this._hydrateInjector(imperativelyCreatedInjector, host);
if (isPresent(host)) {
this._addViewQueries(host);
}
this._addDirectivesToQueries();
this._addVarBindingsToQueries();
@ -650,6 +654,22 @@ export class ElementInjector extends TreeNode<ElementInjector> implements Depend
}
}
private _addViewQueries(host: ElementInjector): void {
if (isPresent(host._query0) && host._query0.originator == host)
this._addViewQuery(host._query0);
if (isPresent(host._query1) && host._query1.originator == host)
this._addViewQuery(host._query1);
if (isPresent(host._query2) && host._query2.originator == host)
this._addViewQuery(host._query2);
}
private _addViewQuery(queryRef: QueryRef): void {
// TODO(rado): Replace this.parent check with distanceToParent = 1 when
// https://github.com/angular/angular/issues/2707 is fixed.
if (!queryRef.query.descendants && isPresent(this.parent)) return;
this._assignQueryRef(queryRef);
}
private _addVarBindingsToQueries(): void {
this._addVarBindingsToQuery(this._query0);
this._addVarBindingsToQuery(this._query1);
@ -733,15 +753,15 @@ export class ElementInjector extends TreeNode<ElementInjector> implements Depend
private _addParentQueries(): void {
if (isBlank(this.parent)) return;
if (isPresent(this.parent._query0)) {
if (isPresent(this.parent._query0) && !this.parent._query0.query.isViewQuery) {
this._addQueryToTree(this.parent._query0);
if (this.hydrated) this.parent._query0.update();
}
if (isPresent(this.parent._query1)) {
if (isPresent(this.parent._query1) && !this.parent._query1.query.isViewQuery) {
this._addQueryToTree(this.parent._query1);
if (this.hydrated) this.parent._query1.update();
}
if (isPresent(this.parent._query2)) {
if (isPresent(this.parent._query2) && !this.parent._query2.query.isViewQuery) {
this._addQueryToTree(this.parent._query2);
if (this.hydrated) this.parent._query2.update();
}