feat(query): make QueryList notify on changes via an observable
BREAKING CHANGE: Before: query.onChange(() => ...); After: query.changes.subscribe((iterable) => {}); Closes #4395
This commit is contained in:
parent
9b7378d132
commit
3aa204791b
|
@ -383,7 +383,7 @@ export class ElementInjector extends TreeNode<ElementInjector> implements Depend
|
||||||
this._preBuiltObjects = null;
|
this._preBuiltObjects = null;
|
||||||
this._strategy.callOnDestroy();
|
this._strategy.callOnDestroy();
|
||||||
this._strategy.dehydrate();
|
this._strategy.dehydrate();
|
||||||
this._queryStrategy.clearQueryLists();
|
this._queryStrategy.dehydrate();
|
||||||
}
|
}
|
||||||
|
|
||||||
hydrate(imperativelyCreatedInjector: Injector, host: ElementInjector,
|
hydrate(imperativelyCreatedInjector: Injector, host: ElementInjector,
|
||||||
|
@ -392,6 +392,7 @@ export class ElementInjector extends TreeNode<ElementInjector> implements Depend
|
||||||
this._preBuiltObjects = preBuiltObjects;
|
this._preBuiltObjects = preBuiltObjects;
|
||||||
|
|
||||||
this._reattachInjectors(imperativelyCreatedInjector);
|
this._reattachInjectors(imperativelyCreatedInjector);
|
||||||
|
this._queryStrategy.hydrate();
|
||||||
this._strategy.hydrate();
|
this._strategy.hydrate();
|
||||||
|
|
||||||
this.hydrated = true;
|
this.hydrated = true;
|
||||||
|
@ -604,7 +605,8 @@ export class ElementInjector extends TreeNode<ElementInjector> implements Depend
|
||||||
interface _QueryStrategy {
|
interface _QueryStrategy {
|
||||||
setContentQueriesAsDirty(): void;
|
setContentQueriesAsDirty(): void;
|
||||||
setViewQueriesAsDirty(): void;
|
setViewQueriesAsDirty(): void;
|
||||||
clearQueryLists(): void;
|
hydrate(): void;
|
||||||
|
dehydrate(): void;
|
||||||
updateContentQueries(): void;
|
updateContentQueries(): void;
|
||||||
updateViewQueries(): void;
|
updateViewQueries(): void;
|
||||||
findQuery(query: QueryMetadata): QueryRef;
|
findQuery(query: QueryMetadata): QueryRef;
|
||||||
|
@ -613,7 +615,8 @@ interface _QueryStrategy {
|
||||||
class _EmptyQueryStrategy implements _QueryStrategy {
|
class _EmptyQueryStrategy implements _QueryStrategy {
|
||||||
setContentQueriesAsDirty(): void {}
|
setContentQueriesAsDirty(): void {}
|
||||||
setViewQueriesAsDirty(): void {}
|
setViewQueriesAsDirty(): void {}
|
||||||
clearQueryLists(): void {}
|
hydrate(): void {}
|
||||||
|
dehydrate(): void {}
|
||||||
updateContentQueries(): void {}
|
updateContentQueries(): void {}
|
||||||
updateViewQueries(): void {}
|
updateViewQueries(): void {}
|
||||||
findQuery(query: QueryMetadata): QueryRef {
|
findQuery(query: QueryMetadata): QueryRef {
|
||||||
|
@ -632,9 +635,9 @@ class InlineQueryStrategy implements _QueryStrategy {
|
||||||
|
|
||||||
constructor(ei: ElementInjector) {
|
constructor(ei: ElementInjector) {
|
||||||
var protoRefs = ei._proto.protoQueryRefs;
|
var protoRefs = ei._proto.protoQueryRefs;
|
||||||
if (protoRefs.length > 0) this.query0 = new QueryRef(protoRefs[0], new QueryList<any>(), ei);
|
if (protoRefs.length > 0) this.query0 = new QueryRef(protoRefs[0], ei);
|
||||||
if (protoRefs.length > 1) this.query1 = new QueryRef(protoRefs[1], new QueryList<any>(), ei);
|
if (protoRefs.length > 1) this.query1 = new QueryRef(protoRefs[1], ei);
|
||||||
if (protoRefs.length > 2) this.query2 = new QueryRef(protoRefs[2], new QueryList<any>(), ei);
|
if (protoRefs.length > 2) this.query2 = new QueryRef(protoRefs[2], ei);
|
||||||
}
|
}
|
||||||
|
|
||||||
setContentQueriesAsDirty(): void {
|
setContentQueriesAsDirty(): void {
|
||||||
|
@ -649,39 +652,39 @@ class InlineQueryStrategy implements _QueryStrategy {
|
||||||
if (isPresent(this.query2) && this.query2.isViewQuery) this.query2.dirty = true;
|
if (isPresent(this.query2) && this.query2.isViewQuery) this.query2.dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
clearQueryLists(): void {
|
hydrate(): void {
|
||||||
if (isPresent(this.query0)) this.query0.reset();
|
if (isPresent(this.query0)) this.query0.hydrate();
|
||||||
if (isPresent(this.query1)) this.query1.reset();
|
if (isPresent(this.query1)) this.query1.hydrate();
|
||||||
if (isPresent(this.query2)) this.query2.reset();
|
if (isPresent(this.query2)) this.query2.hydrate();
|
||||||
|
}
|
||||||
|
|
||||||
|
dehydrate(): void {
|
||||||
|
if (isPresent(this.query0)) this.query0.dehydrate();
|
||||||
|
if (isPresent(this.query1)) this.query1.dehydrate();
|
||||||
|
if (isPresent(this.query2)) this.query2.dehydrate();
|
||||||
}
|
}
|
||||||
|
|
||||||
updateContentQueries() {
|
updateContentQueries() {
|
||||||
if (isPresent(this.query0) && !this.query0.isViewQuery) {
|
if (isPresent(this.query0) && !this.query0.isViewQuery) {
|
||||||
this.query0.update();
|
this.query0.update();
|
||||||
this.query0.list.fireCallbacks();
|
|
||||||
}
|
}
|
||||||
if (isPresent(this.query1) && !this.query1.isViewQuery) {
|
if (isPresent(this.query1) && !this.query1.isViewQuery) {
|
||||||
this.query1.update();
|
this.query1.update();
|
||||||
this.query1.list.fireCallbacks();
|
|
||||||
}
|
}
|
||||||
if (isPresent(this.query2) && !this.query2.isViewQuery) {
|
if (isPresent(this.query2) && !this.query2.isViewQuery) {
|
||||||
this.query2.update();
|
this.query2.update();
|
||||||
this.query2.list.fireCallbacks();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateViewQueries() {
|
updateViewQueries() {
|
||||||
if (isPresent(this.query0) && this.query0.isViewQuery) {
|
if (isPresent(this.query0) && this.query0.isViewQuery) {
|
||||||
this.query0.update();
|
this.query0.update();
|
||||||
this.query0.list.fireCallbacks();
|
|
||||||
}
|
}
|
||||||
if (isPresent(this.query1) && this.query1.isViewQuery) {
|
if (isPresent(this.query1) && this.query1.isViewQuery) {
|
||||||
this.query1.update();
|
this.query1.update();
|
||||||
this.query1.list.fireCallbacks();
|
|
||||||
}
|
}
|
||||||
if (isPresent(this.query2) && this.query2.isViewQuery) {
|
if (isPresent(this.query2) && this.query2.isViewQuery) {
|
||||||
this.query2.update();
|
this.query2.update();
|
||||||
this.query2.list.fireCallbacks();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -703,7 +706,7 @@ class DynamicQueryStrategy implements _QueryStrategy {
|
||||||
queries: QueryRef[];
|
queries: QueryRef[];
|
||||||
|
|
||||||
constructor(ei: ElementInjector) {
|
constructor(ei: ElementInjector) {
|
||||||
this.queries = ei._proto.protoQueryRefs.map(p => new QueryRef(p, new QueryList<any>(), ei));
|
this.queries = ei._proto.protoQueryRefs.map(p => new QueryRef(p, ei));
|
||||||
}
|
}
|
||||||
|
|
||||||
setContentQueriesAsDirty(): void {
|
setContentQueriesAsDirty(): void {
|
||||||
|
@ -720,10 +723,17 @@ class DynamicQueryStrategy implements _QueryStrategy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
clearQueryLists(): void {
|
hydrate(): void {
|
||||||
for (var i = 0; i < this.queries.length; ++i) {
|
for (var i = 0; i < this.queries.length; ++i) {
|
||||||
var q = this.queries[i];
|
var q = this.queries[i];
|
||||||
q.reset();
|
q.hydrate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dehydrate(): void {
|
||||||
|
for (var i = 0; i < this.queries.length; ++i) {
|
||||||
|
var q = this.queries[i];
|
||||||
|
q.dehydrate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -732,7 +742,6 @@ class DynamicQueryStrategy implements _QueryStrategy {
|
||||||
var q = this.queries[i];
|
var q = this.queries[i];
|
||||||
if (!q.isViewQuery) {
|
if (!q.isViewQuery) {
|
||||||
q.update();
|
q.update();
|
||||||
q.list.fireCallbacks();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -742,7 +751,6 @@ class DynamicQueryStrategy implements _QueryStrategy {
|
||||||
var q = this.queries[i];
|
var q = this.queries[i];
|
||||||
if (q.isViewQuery) {
|
if (q.isViewQuery) {
|
||||||
q.update();
|
q.update();
|
||||||
q.list.fireCallbacks();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -972,8 +980,10 @@ export class ProtoQueryRef {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class QueryRef {
|
export class QueryRef {
|
||||||
constructor(public protoQueryRef: ProtoQueryRef, public list: QueryList<any>,
|
public list: QueryList<any>;
|
||||||
private originator: ElementInjector, public dirty: boolean = true) {}
|
public dirty: boolean;
|
||||||
|
|
||||||
|
constructor(public protoQueryRef: ProtoQueryRef, private originator: ElementInjector) {}
|
||||||
|
|
||||||
get isViewQuery(): boolean { return this.protoQueryRef.query.isViewQuery; }
|
get isViewQuery(): boolean { return this.protoQueryRef.query.isViewQuery; }
|
||||||
|
|
||||||
|
@ -991,6 +1001,8 @@ export class QueryRef {
|
||||||
this.protoQueryRef.setter(dir, this.list);
|
this.protoQueryRef.setter(dir, this.list);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.list.notifyOnChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _update(): void {
|
private _update(): void {
|
||||||
|
@ -1073,9 +1085,10 @@ export class QueryRef {
|
||||||
inj.addDirectivesMatchingQuery(this.protoQueryRef.query, aggregator);
|
inj.addDirectivesMatchingQuery(this.protoQueryRef.query, aggregator);
|
||||||
}
|
}
|
||||||
|
|
||||||
reset(): void {
|
dehydrate(): void { this.list = null; }
|
||||||
this.list.reset([]);
|
|
||||||
this.list.removeAllCallbacks();
|
hydrate(): void {
|
||||||
|
this.list = new QueryList<any>();
|
||||||
this.dirty = true;
|
this.dirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
library angular2.src.core.compiler.query_list;
|
library angular2.src.core.compiler.query_list;
|
||||||
|
|
||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
|
import 'package:angular2/src/core/facade/async.dart';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See query_list.ts
|
* See query_list.ts
|
||||||
|
@ -8,33 +9,11 @@ import 'dart:collection';
|
||||||
class QueryList<T> extends Object
|
class QueryList<T> extends Object
|
||||||
with IterableMixin<T> {
|
with IterableMixin<T> {
|
||||||
List<T> _results = [];
|
List<T> _results = [];
|
||||||
List _callbacks = [];
|
EventEmitter _emitter = new EventEmitter();
|
||||||
bool _dirty = false;
|
|
||||||
|
|
||||||
Iterator<T> get iterator => _results.iterator;
|
Iterator<T> get iterator => _results.iterator;
|
||||||
|
|
||||||
/** @private */
|
Stream<Iterable<T>> get changes => _emitter;
|
||||||
void reset(List<T> newList) {
|
|
||||||
_results = newList;
|
|
||||||
_dirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void add(T obj) {
|
|
||||||
_results.add(obj);
|
|
||||||
_dirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void onChange(callback) {
|
|
||||||
_callbacks.add(callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
void removeCallback(callback) {
|
|
||||||
_callbacks.remove(callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
void removeAllCallbacks() {
|
|
||||||
this._callbacks = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
int get length => _results.length;
|
int get length => _results.length;
|
||||||
T get first => _results.first;
|
T get first => _results.first;
|
||||||
|
@ -49,10 +28,12 @@ class QueryList<T> extends Object
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @private */
|
/** @private */
|
||||||
void fireCallbacks() {
|
void reset(List<T> newList) {
|
||||||
if (_dirty) {
|
_results = newList;
|
||||||
_callbacks.forEach((c) => c());
|
}
|
||||||
_dirty = false;
|
|
||||||
}
|
/** @private */
|
||||||
|
void notifyOnChanges() {
|
||||||
|
_emitter.add(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import {ListWrapper, MapWrapper} from 'angular2/src/core/facade/collection';
|
import {ListWrapper, MapWrapper} from 'angular2/src/core/facade/collection';
|
||||||
import {getSymbolIterator} from 'angular2/src/core/facade/lang';
|
import {getSymbolIterator} from 'angular2/src/core/facade/lang';
|
||||||
|
import {Observable, EventEmitter} from 'angular2/src/core/facade/async';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -12,7 +13,7 @@ import {getSymbolIterator} from 'angular2/src/core/facade/lang';
|
||||||
* javascript `for (var i of items)` loops as well as in Angular templates with
|
* javascript `for (var i of items)` loops as well as in Angular templates with
|
||||||
* `*ng-for="#i of myList"`.
|
* `*ng-for="#i of myList"`.
|
||||||
*
|
*
|
||||||
* Changes can be observed by attaching callbacks.
|
* Changes can be observed by subscribing to the changes `Observable`.
|
||||||
*
|
*
|
||||||
* NOTE: In the future this class will implement an `Observable` interface.
|
* NOTE: In the future this class will implement an `Observable` interface.
|
||||||
*
|
*
|
||||||
|
@ -21,45 +22,16 @@ import {getSymbolIterator} from 'angular2/src/core/facade/lang';
|
||||||
* @Component({...})
|
* @Component({...})
|
||||||
* class Container {
|
* class Container {
|
||||||
* constructor(@Query(Item) items: QueryList<Item>) {
|
* constructor(@Query(Item) items: QueryList<Item>) {
|
||||||
* items.onChange(() => console.log(items.length));
|
* items.changes.subscribe(_ => console.log(items.length));
|
||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
export class QueryList<T> {
|
export class QueryList<T> {
|
||||||
protected _results: Array < T >= [];
|
private _results: Array<T> = [];
|
||||||
protected _callbacks: Array < () => void >= [];
|
private _emitter = new EventEmitter();
|
||||||
protected _dirty: boolean = false;
|
|
||||||
|
|
||||||
/** @private */
|
|
||||||
reset(newList: T[]): void {
|
|
||||||
this._results = newList;
|
|
||||||
this._dirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @private */
|
|
||||||
add(obj: T): void {
|
|
||||||
this._results.push(obj);
|
|
||||||
this._dirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* registers a callback that is called upon each change.
|
|
||||||
*/
|
|
||||||
onChange(callback: () => void): void { this._callbacks.push(callback); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* removes a given callback.
|
|
||||||
*/
|
|
||||||
removeCallback(callback: () => void): void { ListWrapper.remove(this._callbacks, callback); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* removes all callback that have been attached.
|
|
||||||
*/
|
|
||||||
removeAllCallbacks(): void { this._callbacks = []; }
|
|
||||||
|
|
||||||
toString(): string { return this._results.toString(); }
|
|
||||||
|
|
||||||
|
get changes(): Observable { return this._emitter; }
|
||||||
get length(): number { return this._results.length; }
|
get length(): number { return this._results.length; }
|
||||||
get first(): T { return ListWrapper.first(this._results); }
|
get first(): T { return ListWrapper.first(this._results); }
|
||||||
get last(): T { return ListWrapper.last(this._results); }
|
get last(): T { return ListWrapper.last(this._results); }
|
||||||
|
@ -71,11 +43,13 @@ export class QueryList<T> {
|
||||||
|
|
||||||
[getSymbolIterator()](): any { return this._results[getSymbolIterator()](); }
|
[getSymbolIterator()](): any { return this._results[getSymbolIterator()](); }
|
||||||
|
|
||||||
|
toString(): string { return this._results.toString(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
reset(res: T[]): void { this._results = res; }
|
||||||
|
|
||||||
/** @private */
|
/** @private */
|
||||||
fireCallbacks(): void {
|
notifyOnChanges(): void { this._emitter.next(this); }
|
||||||
if (this._dirty) {
|
|
||||||
ListWrapper.forEach(this._callbacks, (c) => c());
|
|
||||||
this._dirty = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import {Query, Directive} from 'angular2/src/core/metadata';
|
||||||
import {NgControl} from './ng_control';
|
import {NgControl} from './ng_control';
|
||||||
import {ControlValueAccessor} from './control_value_accessor';
|
import {ControlValueAccessor} from './control_value_accessor';
|
||||||
import {isPresent} from 'angular2/src/core/facade/lang';
|
import {isPresent} from 'angular2/src/core/facade/lang';
|
||||||
|
import {ObservableWrapper} from 'angular2/src/core/facade/async';
|
||||||
import {setProperty} from './shared';
|
import {setProperty} from './shared';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -81,6 +82,6 @@ export class SelectControlValueAccessor implements ControlValueAccessor {
|
||||||
registerOnTouched(fn: () => any): void { this.onTouched = fn; }
|
registerOnTouched(fn: () => any): void { this.onTouched = fn; }
|
||||||
|
|
||||||
private _updateValueWhenListOfOptionsChanges(query: QueryList<NgSelectOption>) {
|
private _updateValueWhenListOfOptionsChanges(query: QueryList<NgSelectOption>) {
|
||||||
query.onChange(() => this.writeValue(this.value));
|
ObservableWrapper.subscribe(query.changes, (_) => this.writeValue(this.value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import {
|
||||||
} from 'angular2/test_lib';
|
} from 'angular2/test_lib';
|
||||||
|
|
||||||
import {isPresent} from 'angular2/src/core/facade/lang';
|
import {isPresent} from 'angular2/src/core/facade/lang';
|
||||||
|
import {ObservableWrapper} from 'angular2/src/core/facade/async';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Component,
|
Component,
|
||||||
|
@ -263,7 +264,7 @@ export function main() {
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("onChange", () => {
|
describe("changes", () => {
|
||||||
it('should notify query on change',
|
it('should notify query on change',
|
||||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||||
var template = '<needs-query #q>' +
|
var template = '<needs-query #q>' +
|
||||||
|
@ -277,7 +278,7 @@ export function main() {
|
||||||
var q = view.debugElement.componentViewChildren[0].getLocal("q");
|
var q = view.debugElement.componentViewChildren[0].getLocal("q");
|
||||||
view.detectChanges();
|
view.detectChanges();
|
||||||
|
|
||||||
q.query.onChange(() => {
|
ObservableWrapper.subscribe(q.query.changes, (_) => {
|
||||||
expect(q.query.first.text).toEqual("1");
|
expect(q.query.first.text).toEqual("1");
|
||||||
expect(q.query.last.text).toEqual("2");
|
expect(q.query.last.text).toEqual("2");
|
||||||
async.done();
|
async.done();
|
||||||
|
@ -304,8 +305,8 @@ export function main() {
|
||||||
|
|
||||||
var firedQ2 = false;
|
var firedQ2 = false;
|
||||||
|
|
||||||
q2.query.onChange(() => { firedQ2 = true; });
|
ObservableWrapper.subscribe(q2.query.changes, (_) => { firedQ2 = true; });
|
||||||
q1.query.onChange(() => {
|
ObservableWrapper.subscribe(q1.query.changes, (_) => {
|
||||||
expect(firedQ2).toBe(true);
|
expect(firedQ2).toBe(true);
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,8 +1,21 @@
|
||||||
import {describe, it, expect, beforeEach, ddescribe, iit, xit, el} from 'angular2/test_lib';
|
import {
|
||||||
|
describe,
|
||||||
|
it,
|
||||||
|
expect,
|
||||||
|
beforeEach,
|
||||||
|
ddescribe,
|
||||||
|
iit,
|
||||||
|
xit,
|
||||||
|
el,
|
||||||
|
fakeAsync,
|
||||||
|
tick
|
||||||
|
} from 'angular2/test_lib';
|
||||||
|
|
||||||
import {MapWrapper, ListWrapper, iterateListLike} from 'angular2/src/core/facade/collection';
|
import {MapWrapper, ListWrapper, iterateListLike} from 'angular2/src/core/facade/collection';
|
||||||
import {StringWrapper} from 'angular2/src/core/facade/lang';
|
import {StringWrapper} from 'angular2/src/core/facade/lang';
|
||||||
|
import {ObservableWrapper} from 'angular2/src/core/facade/async';
|
||||||
import {QueryList} from 'angular2/src/core/compiler/query_list';
|
import {QueryList} from 'angular2/src/core/compiler/query_list';
|
||||||
|
import {DOM} from 'angular2/src/core/dom/dom_adapter';
|
||||||
|
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
|
@ -16,95 +29,64 @@ export function main() {
|
||||||
|
|
||||||
function logAppend(item) { log += (log.length == 0 ? '' : ', ') + item; }
|
function logAppend(item) { log += (log.length == 0 ? '' : ', ') + item; }
|
||||||
|
|
||||||
it('should support adding objects and iterating over them', () => {
|
|
||||||
queryList.add('one');
|
|
||||||
queryList.add('two');
|
|
||||||
iterateListLike(queryList, logAppend);
|
|
||||||
expect(log).toEqual('one, two');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should support resetting and iterating over the new objects', () => {
|
it('should support resetting and iterating over the new objects', () => {
|
||||||
queryList.add('one');
|
queryList.reset(['one']);
|
||||||
queryList.add('two');
|
queryList.reset(['two']);
|
||||||
queryList.reset(['one again']);
|
|
||||||
queryList.add('two again');
|
|
||||||
iterateListLike(queryList, logAppend);
|
iterateListLike(queryList, logAppend);
|
||||||
expect(log).toEqual('one again, two again');
|
expect(log).toEqual('two');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support length', () => {
|
it('should support length', () => {
|
||||||
queryList.add('one');
|
queryList.reset(['one', 'two']);
|
||||||
queryList.add('two');
|
|
||||||
expect(queryList.length).toEqual(2);
|
expect(queryList.length).toEqual(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support map', () => {
|
it('should support map', () => {
|
||||||
queryList.add('one');
|
queryList.reset(['one', 'two']);
|
||||||
queryList.add('two');
|
|
||||||
expect(queryList.map((x) => x)).toEqual(['one', 'two']);
|
expect(queryList.map((x) => x)).toEqual(['one', 'two']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support toString', () => {
|
it('should support toString', () => {
|
||||||
queryList.add('one');
|
queryList.reset(['one', 'two']);
|
||||||
queryList.add('two');
|
|
||||||
var listString = queryList.toString();
|
var listString = queryList.toString();
|
||||||
expect(StringWrapper.contains(listString, 'one')).toBeTruthy();
|
expect(StringWrapper.contains(listString, 'one')).toBeTruthy();
|
||||||
expect(StringWrapper.contains(listString, 'two')).toBeTruthy();
|
expect(StringWrapper.contains(listString, 'two')).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support first and last', () => {
|
it('should support first and last', () => {
|
||||||
queryList.add('one');
|
queryList.reset(['one', 'two', 'three']);
|
||||||
queryList.add('two');
|
|
||||||
queryList.add('three');
|
|
||||||
expect(queryList.first).toEqual('one');
|
expect(queryList.first).toEqual('one');
|
||||||
expect(queryList.last).toEqual('three');
|
expect(queryList.last).toEqual('three');
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('simple observable interface', () => {
|
if (DOM.supportsDOMEvents()) {
|
||||||
it('should fire callbacks on change', () => {
|
describe('simple observable interface', () => {
|
||||||
var fires = 0;
|
it('should fire callbacks on change', fakeAsync(() => {
|
||||||
queryList.onChange(() => { fires += 1; });
|
var fires = 0;
|
||||||
|
ObservableWrapper.subscribe(queryList.changes, (_) => { fires += 1; });
|
||||||
|
|
||||||
queryList.fireCallbacks();
|
queryList.notifyOnChanges();
|
||||||
expect(fires).toEqual(0);
|
tick();
|
||||||
|
|
||||||
queryList.add('one');
|
expect(fires).toEqual(1);
|
||||||
|
|
||||||
queryList.fireCallbacks();
|
queryList.notifyOnChanges();
|
||||||
expect(fires).toEqual(1);
|
tick();
|
||||||
|
|
||||||
queryList.fireCallbacks();
|
expect(fires).toEqual(2);
|
||||||
expect(fires).toEqual(1);
|
}));
|
||||||
|
|
||||||
|
it('should provides query list as an argument', fakeAsync(() => {
|
||||||
|
var recorded;
|
||||||
|
ObservableWrapper.subscribe(queryList.changes, (v: any) => { recorded = v; });
|
||||||
|
|
||||||
|
queryList.reset(["one"]);
|
||||||
|
queryList.notifyOnChanges();
|
||||||
|
tick();
|
||||||
|
|
||||||
|
expect(recorded).toBe(queryList);
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
|
}
|
||||||
it('should support removing callbacks', () => {
|
|
||||||
var fires = 0;
|
|
||||||
var callback = () => fires += 1;
|
|
||||||
queryList.onChange(callback);
|
|
||||||
|
|
||||||
queryList.add('one');
|
|
||||||
queryList.fireCallbacks();
|
|
||||||
expect(fires).toEqual(1);
|
|
||||||
|
|
||||||
queryList.removeCallback(callback);
|
|
||||||
|
|
||||||
queryList.add('two');
|
|
||||||
queryList.fireCallbacks();
|
|
||||||
expect(fires).toEqual(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should support removing all callbacks', () => {
|
|
||||||
var fires = 0;
|
|
||||||
var callback = () => fires += 1;
|
|
||||||
queryList.onChange(callback);
|
|
||||||
|
|
||||||
queryList.add('one');
|
|
||||||
queryList.removeAllCallbacks();
|
|
||||||
|
|
||||||
queryList.fireCallbacks();
|
|
||||||
|
|
||||||
expect(fires).toEqual(0);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -301,24 +301,26 @@ export function main() {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it("should support <select> with a dynamic list of options",
|
it("should support <select> with a dynamic list of options",
|
||||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
inject([TestComponentBuilder], fakeAsync((tcb: TestComponentBuilder) => {
|
||||||
var t = `<div [ng-form-model]="form">
|
var t = `<div [ng-form-model]="form">
|
||||||
<select ng-control="city">
|
<select ng-control="city">
|
||||||
<option *ng-for="#c of data" [value]="c"></option>
|
<option *ng-for="#c of data" [value]="c"></option>
|
||||||
</select>
|
</select>
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|
||||||
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then((rootTC) => {
|
var rootTC;
|
||||||
rootTC.debugElement.componentInstance.form =
|
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then((rtc) => rootTC = rtc);
|
||||||
new ControlGroup({"city": new Control("NYC")});
|
tick();
|
||||||
rootTC.debugElement.componentInstance.data = ['SF', 'NYC'];
|
|
||||||
rootTC.detectChanges();
|
|
||||||
|
|
||||||
var select = rootTC.debugElement.query(By.css('select'));
|
rootTC.debugElement.componentInstance.form =
|
||||||
expect(select.nativeElement.value).toEqual('NYC');
|
new ControlGroup({"city": new Control("NYC")});
|
||||||
async.done();
|
rootTC.debugElement.componentInstance.data = ['SF', 'NYC'];
|
||||||
});
|
rootTC.detectChanges();
|
||||||
}));
|
tick();
|
||||||
|
|
||||||
|
var select = rootTC.debugElement.query(By.css('select'));
|
||||||
|
expect(select.nativeElement.value).toEqual('NYC');
|
||||||
|
})));
|
||||||
|
|
||||||
it("should support custom value accessors",
|
it("should support custom value accessors",
|
||||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||||
|
|
|
@ -809,13 +809,12 @@ var NG_API = [
|
||||||
'Query.token',
|
'Query.token',
|
||||||
'Query.varBindings',
|
'Query.varBindings',
|
||||||
'QueryList',
|
'QueryList',
|
||||||
'QueryList.add()',
|
|
||||||
'QueryList.any():dart',
|
'QueryList.any():dart',
|
||||||
'QueryList.contains():dart',
|
'QueryList.contains():dart',
|
||||||
'QueryList.elementAt():dart',
|
'QueryList.elementAt():dart',
|
||||||
'QueryList.every():dart',
|
'QueryList.every():dart',
|
||||||
'QueryList.expand():dart',
|
'QueryList.expand():dart',
|
||||||
'QueryList.fireCallbacks():',
|
'QueryList.notifyOnChanges():',
|
||||||
'QueryList.first',
|
'QueryList.first',
|
||||||
'QueryList.firstWhere():dart',
|
'QueryList.firstWhere():dart',
|
||||||
'QueryList.fold():dart',
|
'QueryList.fold():dart',
|
||||||
|
@ -828,10 +827,8 @@ var NG_API = [
|
||||||
'QueryList.lastWhere():dart',
|
'QueryList.lastWhere():dart',
|
||||||
'QueryList.length',
|
'QueryList.length',
|
||||||
'QueryList.map()',
|
'QueryList.map()',
|
||||||
'QueryList.onChange()',
|
'QueryList.changes',
|
||||||
'QueryList.reduce():dart',
|
'QueryList.reduce():dart',
|
||||||
'QueryList.removeAllCallbacks()',
|
|
||||||
'QueryList.removeCallback()',
|
|
||||||
'QueryList.reset()',
|
'QueryList.reset()',
|
||||||
'QueryList.single',
|
'QueryList.single',
|
||||||
'QueryList.singleWhere():dart',
|
'QueryList.singleWhere():dart',
|
||||||
|
|
Loading…
Reference in New Issue