From f7dfd2325da2a08cac25e9130362a977182d4b30 Mon Sep 17 00:00:00 2001 From: Rado Kirov Date: Tue, 14 Jul 2015 16:06:33 -0700 Subject: [PATCH] chore(query): refactor QueryList and BaseQueryList. Closes #3035, #3016 --- modules/angular2/core.ts | 2 +- .../angular2/src/core/annotations_impl/di.ts | 2 - .../src/core/compiler/base_query_list.ts | 43 ------- .../src/core/compiler/interface_query.ts | 78 ++++++++++++ .../{base_query_list.dart => query_list.dart} | 21 ++-- .../angular2/src/core/compiler/query_list.ts | 114 ++++++------------ 6 files changed, 127 insertions(+), 133 deletions(-) delete mode 100644 modules/angular2/src/core/compiler/base_query_list.ts create mode 100644 modules/angular2/src/core/compiler/interface_query.ts rename modules/angular2/src/core/compiler/{base_query_list.dart => query_list.dart} (69%) diff --git a/modules/angular2/core.ts b/modules/angular2/core.ts index 01fd94aabf..db45babf23 100644 --- a/modules/angular2/core.ts +++ b/modules/angular2/core.ts @@ -15,8 +15,8 @@ export {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver'; export {Compiler} from 'angular2/src/core/compiler/compiler'; export {AppViewManager} from 'angular2/src/core/compiler/view_manager'; +export {IQueryList} from 'angular2/src/core/compiler/interface_query'; export {QueryList} from 'angular2/src/core/compiler/query_list'; -export {BaseQueryList} from 'angular2/src/core/compiler/base_query_list'; export {ElementRef} from 'angular2/src/core/compiler/element_ref'; export {RenderElementRef} from 'angular2/src/render/api'; export {ViewRef, ProtoViewRef} from 'angular2/src/core/compiler/view_ref'; diff --git a/modules/angular2/src/core/annotations_impl/di.ts b/modules/angular2/src/core/annotations_impl/di.ts index 942d3c5186..1221be9ae4 100644 --- a/modules/angular2/src/core/annotations_impl/di.ts +++ b/modules/angular2/src/core/annotations_impl/di.ts @@ -72,8 +72,6 @@ export class Query extends DependencyMetadata { * Specifies that a {@link QueryList} should be injected. * * See {@link QueryList} for usage and example. - * - * @exportedAs angular2/annotations */ @CONST() export class ViewQuery extends Query { diff --git a/modules/angular2/src/core/compiler/base_query_list.ts b/modules/angular2/src/core/compiler/base_query_list.ts deleted file mode 100644 index 3ed5aedf69..0000000000 --- a/modules/angular2/src/core/compiler/base_query_list.ts +++ /dev/null @@ -1,43 +0,0 @@ -import {List, ListWrapper, MapWrapper} from 'angular2/src/facade/collection'; - -/** - * Injectable Objects that contains a live list of child directives in the light Dom of a directive. - * The directives are kept in depth-first pre-order traversal of the DOM. - * - * In the future this class will implement an Observable interface. - * For now it uses a plain list of observable callbacks. - */ -export class BaseQueryList { - protected _results: List = []; - protected _callbacks = []; - protected _dirty = false; - - [Symbol.iterator](): any { return this._results[Symbol.iterator](); } - - reset(newList) { - this._results = newList; - this._dirty = true; - } - - add(obj) { - this._results.push(obj); - this._dirty = true; - } - - fireCallbacks() { - if (this._dirty) { - ListWrapper.forEach(this._callbacks, (c) => c()); - this._dirty = false; - } - } - - onChange(callback): void { this._callbacks.push(callback); } - - removeCallback(callback): void { ListWrapper.remove(this._callbacks, callback); } - - get length() { return this._results.length; } - get first() { return ListWrapper.first(this._results); } - get last() { return ListWrapper.last(this._results); } - - map(fn: (T) => U): U[] { return this._results.map(fn); } -} diff --git a/modules/angular2/src/core/compiler/interface_query.ts b/modules/angular2/src/core/compiler/interface_query.ts new file mode 100644 index 0000000000..d84aae266c --- /dev/null +++ b/modules/angular2/src/core/compiler/interface_query.ts @@ -0,0 +1,78 @@ +/** + * An iterable live list of components in the Light DOM. + * + * Injectable Objects that contains a live list of child directives in the light DOM of a directive. + * The directives are kept in depth-first pre-order traversal of the DOM. + * + * The `QueryList` is iterable, therefore it can be used in both javascript code with `for..of` loop + * as well as in + * template with `*ng-for="of"` directive. + * + * NOTE: In the future this class will implement an `Observable` interface. For now it uses a plain + * list of observable + * callbacks. + * + * # Example: + * + * Assume that `` component would like to get a list its children which are `` + * components as shown in this + * example: + * + * ```html + * + * ... + * {{o.text}} + * + * ``` + * + * In the above example the list of `` elements needs to get a list of `` elements so + * that it could render + * tabs with the correct titles and in the correct order. + * + * A possible solution would be for a `` to inject `` component and then register itself + * with `` + * component's on `hydrate` and deregister on `dehydrate` event. While a reasonable approach, this + * would only work + * partialy since `*ng-for` could rearrange the list of `` components which would not be + * reported to `` + * component and thus the list of `` components would be out of sync with respect to the list + * of `` elements. + * + * A preferred solution is to inject a `QueryList` which is a live list of directives in the + * component`s light DOM. + * + * ```javascript + * @Component({ + * selector: 'tabs' + * }) + * @View({ + * template: ` + *
    + *
  • {{pane.title}}
  • + *
+ * + * ` + * }) + * class Tabs { + * panes: QueryList + * + * constructor(@Query(Pane) panes:QueryList) { + * this.panes = panes; + * } + * } + * + * @Component({ + * selector: 'pane', + * properties: ['title'] + * }) + * @View(...) + * class Pane { + * title:string; + * } + * ``` + */ +// So far this interface is only used for purposes of having unified documentation. +// There are limitations for exposing the interface as the main entry point. +// - Typescript does not support getters/setters in interfaces +// - Dart does not support generic methods (needed for map). +export interface IQueryList {} diff --git a/modules/angular2/src/core/compiler/base_query_list.dart b/modules/angular2/src/core/compiler/query_list.dart similarity index 69% rename from modules/angular2/src/core/compiler/base_query_list.dart rename to modules/angular2/src/core/compiler/query_list.dart index 5bd8fcfb04..8bb4b89d86 100644 --- a/modules/angular2/src/core/compiler/base_query_list.dart +++ b/modules/angular2/src/core/compiler/query_list.dart @@ -1,6 +1,7 @@ -library angular2.src.core.compiler.base_query_list; +library angular2.src.core.compiler.query_list; import 'dart:collection'; +import './interface_query.dart'; /** * Injectable Objects that contains a live list of child directives in the light Dom of a directive. @@ -9,42 +10,42 @@ import 'dart:collection'; * In the future this class will implement an Observable interface. * For now it uses a plain list of observable callbacks. */ -class BaseQueryList extends Object with IterableMixin { +class QueryList extends Object with IterableMixin implements IQueryList { List _results = []; List _callbacks = []; bool _dirty = false; Iterator get iterator => _results.iterator; - reset(newList) { + void reset(List newList) { _results = newList; _dirty = true; } - add(obj) { + void add(T obj) { _results.add(obj); _dirty = true; } // TODO(rado): hook up with change detection after #995. - fireCallbacks() { + void fireCallbacks() { if (_dirty) { _callbacks.forEach((c) => c()); _dirty = false; } } - onChange(callback) { + void onChange(callback) { _callbacks.add(callback); } - removeCallback(callback) { + void removeCallback(callback) { _callbacks.remove(callback); } - get length => _results.length; - get first => _results.first; - get last => _results.last; + int get length => _results.length; + T get first => _results.first; + T get last => _results.last; List map(fn(T)) { // Note: we need to return a list instead of iterable to match JS. diff --git a/modules/angular2/src/core/compiler/query_list.ts b/modules/angular2/src/core/compiler/query_list.ts index ca15fa63c5..229c70132c 100644 --- a/modules/angular2/src/core/compiler/query_list.ts +++ b/modules/angular2/src/core/compiler/query_list.ts @@ -1,84 +1,44 @@ -import {BaseQueryList} from './base_query_list'; +import {List, ListWrapper, MapWrapper} from 'angular2/src/facade/collection'; +import {IQueryList} from './interface_query'; /** - * An iterable live list of components in the Light DOM. - * - * Injectable Objects that contains a live list of child directives in the light DOM of a directive. + * Injectable Objects that contains a live list of child directives in the light Dom of a directive. * The directives are kept in depth-first pre-order traversal of the DOM. * - * The `QueryList` is iterable, therefore it can be used in both javascript code with `for..of` loop - * as well as in - * template with `*ng-for="of"` directive. - * - * NOTE: In the future this class will implement an `Observable` interface. For now it uses a plain - * list of observable - * callbacks. - * - * # Example: - * - * Assume that `` component would like to get a list its children which are `` - * components as shown in this - * example: - * - * ```html - * - * ... - * {{o.text}} - * - * ``` - * - * In the above example the list of `` elements needs to get a list of `` elements so - * that it could render - * tabs with the correct titles and in the correct order. - * - * A possible solution would be for a `` to inject `` component and then register itself - * with `` - * component's on `hydrate` and deregister on `dehydrate` event. While a reasonable approach, this - * would only work - * partialy since `*ng-for` could rearrange the list of `` components which would not be - * reported to `` - * component and thus the list of `` components would be out of sync with respect to the list - * of `` elements. - * - * A preferred solution is to inject a `QueryList` which is a live list of directives in the - * component`s light DOM. - * - * ```javascript - * @Component({ - * selector: 'tabs' - * }) - * @View({ - * template: ` - *
    - *
  • {{pane.title}}
  • - *
- * - * ` - * }) - * class Tabs { - * panes: QueryList - * - * constructor(@Query(Pane) panes:QueryList) { - * this.panes = panes; - * } - * } - * - * @Component({ - * selector: 'pane', - * properties: ['title'] - * }) - * @View(...) - * class Pane { - * title:string; - * } - * ``` + * In the future this class will implement an Observable interface. + * For now it uses a plain list of observable callbacks. */ -export class QueryList extends BaseQueryList { - /** - */ - onChange(callback) { super.onChange(callback); } +export class QueryList implements IQueryList { + protected _results: List < T >= []; + protected _callbacks: List < () => void >= []; + protected _dirty: boolean = false; - /** - */ - removeCallback(callback) { super.removeCallback(callback); } + reset(newList: List): void { + this._results = newList; + this._dirty = true; + } + + add(obj: T): void { + this._results.push(obj); + this._dirty = true; + } + + fireCallbacks(): void { + if (this._dirty) { + ListWrapper.forEach(this._callbacks, (c) => c()); + this._dirty = false; + } + } + + onChange(callback: () => void): void { this._callbacks.push(callback); } + + removeCallback(callback: () => void): void { ListWrapper.remove(this._callbacks, callback); } + + get length(): number { return this._results.length; } + get first(): T { return ListWrapper.first(this._results); } + get last(): T { return ListWrapper.last(this._results); } + + map(fn: (T) => U): U[] { return this._results.map(fn); } + + [Symbol.iterator](): any { return this._results[Symbol.iterator](); } }