chore(query): refactor QueryList and BaseQueryList.
Closes #3035, #3016
This commit is contained in:
parent
b03560b670
commit
f7dfd2325d
|
@ -15,8 +15,8 @@ export {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
|
||||||
export {Compiler} from 'angular2/src/core/compiler/compiler';
|
export {Compiler} from 'angular2/src/core/compiler/compiler';
|
||||||
|
|
||||||
export {AppViewManager} from 'angular2/src/core/compiler/view_manager';
|
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 {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 {ElementRef} from 'angular2/src/core/compiler/element_ref';
|
||||||
export {RenderElementRef} from 'angular2/src/render/api';
|
export {RenderElementRef} from 'angular2/src/render/api';
|
||||||
export {ViewRef, ProtoViewRef} from 'angular2/src/core/compiler/view_ref';
|
export {ViewRef, ProtoViewRef} from 'angular2/src/core/compiler/view_ref';
|
||||||
|
|
|
@ -72,8 +72,6 @@ export class Query extends DependencyMetadata {
|
||||||
* Specifies that a {@link QueryList} should be injected.
|
* Specifies that a {@link QueryList} should be injected.
|
||||||
*
|
*
|
||||||
* See {@link QueryList} for usage and example.
|
* See {@link QueryList} for usage and example.
|
||||||
*
|
|
||||||
* @exportedAs angular2/annotations
|
|
||||||
*/
|
*/
|
||||||
@CONST()
|
@CONST()
|
||||||
export class ViewQuery extends Query {
|
export class ViewQuery extends Query {
|
||||||
|
|
|
@ -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<T> {
|
|
||||||
protected _results: List<T> = [];
|
|
||||||
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<U>(fn: (T) => U): U[] { return this._results.map(fn); }
|
|
||||||
}
|
|
|
@ -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 `<tabs>` component would like to get a list its children which are `<pane>`
|
||||||
|
* components as shown in this
|
||||||
|
* example:
|
||||||
|
*
|
||||||
|
* ```html
|
||||||
|
* <tabs>
|
||||||
|
* <pane title="Overview">...</pane>
|
||||||
|
* <pane *ng-for="#o of objects" [title]="o.title">{{o.text}}</pane>
|
||||||
|
* </tabs>
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* In the above example the list of `<tabs>` elements needs to get a list of `<pane>` elements so
|
||||||
|
* that it could render
|
||||||
|
* tabs with the correct titles and in the correct order.
|
||||||
|
*
|
||||||
|
* A possible solution would be for a `<pane>` to inject `<tabs>` component and then register itself
|
||||||
|
* with `<tabs>`
|
||||||
|
* 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 `<pane>` components which would not be
|
||||||
|
* reported to `<tabs>`
|
||||||
|
* component and thus the list of `<pane>` components would be out of sync with respect to the list
|
||||||
|
* of `<pane>` 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: `
|
||||||
|
* <ul>
|
||||||
|
* <li *ng-for="#pane of panes">{{pane.title}}</li>
|
||||||
|
* </ul>
|
||||||
|
* <content></content>
|
||||||
|
* `
|
||||||
|
* })
|
||||||
|
* class Tabs {
|
||||||
|
* panes: QueryList<Pane>
|
||||||
|
*
|
||||||
|
* constructor(@Query(Pane) panes:QueryList<Pane>) {
|
||||||
|
* 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<T> {}
|
|
@ -1,6 +1,7 @@
|
||||||
library angular2.src.core.compiler.base_query_list;
|
library angular2.src.core.compiler.query_list;
|
||||||
|
|
||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
|
import './interface_query.dart';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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.
|
||||||
|
@ -9,42 +10,42 @@ import 'dart:collection';
|
||||||
* In the future this class will implement an Observable interface.
|
* In the future this class will implement an Observable interface.
|
||||||
* For now it uses a plain list of observable callbacks.
|
* For now it uses a plain list of observable callbacks.
|
||||||
*/
|
*/
|
||||||
class BaseQueryList<T> extends Object with IterableMixin<T> {
|
class QueryList<T> extends Object with IterableMixin<T> implements IQueryList<T> {
|
||||||
List<T> _results = [];
|
List<T> _results = [];
|
||||||
List _callbacks = [];
|
List _callbacks = [];
|
||||||
bool _dirty = false;
|
bool _dirty = false;
|
||||||
|
|
||||||
Iterator<T> get iterator => _results.iterator;
|
Iterator<T> get iterator => _results.iterator;
|
||||||
|
|
||||||
reset(newList) {
|
void reset(List<T> newList) {
|
||||||
_results = newList;
|
_results = newList;
|
||||||
_dirty = true;
|
_dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
add(obj) {
|
void add(T obj) {
|
||||||
_results.add(obj);
|
_results.add(obj);
|
||||||
_dirty = true;
|
_dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(rado): hook up with change detection after #995.
|
// TODO(rado): hook up with change detection after #995.
|
||||||
fireCallbacks() {
|
void fireCallbacks() {
|
||||||
if (_dirty) {
|
if (_dirty) {
|
||||||
_callbacks.forEach((c) => c());
|
_callbacks.forEach((c) => c());
|
||||||
_dirty = false;
|
_dirty = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onChange(callback) {
|
void onChange(callback) {
|
||||||
_callbacks.add(callback);
|
_callbacks.add(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
removeCallback(callback) {
|
void removeCallback(callback) {
|
||||||
_callbacks.remove(callback);
|
_callbacks.remove(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
get length => _results.length;
|
int get length => _results.length;
|
||||||
get first => _results.first;
|
T get first => _results.first;
|
||||||
get last => _results.last;
|
T get last => _results.last;
|
||||||
|
|
||||||
List map(fn(T)) {
|
List map(fn(T)) {
|
||||||
// Note: we need to return a list instead of iterable to match JS.
|
// Note: we need to return a list instead of iterable to match JS.
|
|
@ -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 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
|
* In the future this class will implement an Observable interface.
|
||||||
* as well as in
|
* For now it uses a plain list of observable callbacks.
|
||||||
* 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 `<tabs>` component would like to get a list its children which are `<pane>`
|
|
||||||
* components as shown in this
|
|
||||||
* example:
|
|
||||||
*
|
|
||||||
* ```html
|
|
||||||
* <tabs>
|
|
||||||
* <pane title="Overview">...</pane>
|
|
||||||
* <pane *ng-for="#o of objects" [title]="o.title">{{o.text}}</pane>
|
|
||||||
* </tabs>
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* In the above example the list of `<tabs>` elements needs to get a list of `<pane>` elements so
|
|
||||||
* that it could render
|
|
||||||
* tabs with the correct titles and in the correct order.
|
|
||||||
*
|
|
||||||
* A possible solution would be for a `<pane>` to inject `<tabs>` component and then register itself
|
|
||||||
* with `<tabs>`
|
|
||||||
* 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 `<pane>` components which would not be
|
|
||||||
* reported to `<tabs>`
|
|
||||||
* component and thus the list of `<pane>` components would be out of sync with respect to the list
|
|
||||||
* of `<pane>` 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: `
|
|
||||||
* <ul>
|
|
||||||
* <li *ng-for="#pane of panes">{{pane.title}}</li>
|
|
||||||
* </ul>
|
|
||||||
* <content></content>
|
|
||||||
* `
|
|
||||||
* })
|
|
||||||
* class Tabs {
|
|
||||||
* panes: QueryList<Pane>
|
|
||||||
*
|
|
||||||
* constructor(@Query(Pane) panes:QueryList<Pane>) {
|
|
||||||
* this.panes = panes;
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* @Component({
|
|
||||||
* selector: 'pane',
|
|
||||||
* properties: ['title']
|
|
||||||
* })
|
|
||||||
* @View(...)
|
|
||||||
* class Pane {
|
|
||||||
* title:string;
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*/
|
*/
|
||||||
export class QueryList<T> extends BaseQueryList<T> {
|
export class QueryList<T> implements IQueryList<T> {
|
||||||
/**
|
protected _results: List < T >= [];
|
||||||
*/
|
protected _callbacks: List < () => void >= [];
|
||||||
onChange(callback) { super.onChange(callback); }
|
protected _dirty: boolean = false;
|
||||||
|
|
||||||
/**
|
reset(newList: List<T>): void {
|
||||||
*/
|
this._results = newList;
|
||||||
removeCallback(callback) { super.removeCallback(callback); }
|
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<U>(fn: (T) => U): U[] { return this._results.map(fn); }
|
||||||
|
|
||||||
|
[Symbol.iterator](): any { return this._results[Symbol.iterator](); }
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue