feat(query): adds initial implementation of the query api.
Queries allow a directive to inject a live list of directives of a given type from its LightDom. The injected list is Iterable (in JS and Dart). It will be Observable when Observables are support in JS, for now it maintains a simple list of onChange callbacks API. To support queries, element injectors now maintain a list of child injectors in the correct DOM order (dynamically updated by viewports). For performance reasons we allow only 3 active queries in an injector subtree. The feature adds no overhead to the application when not used. Queries walk the injector tree only during dynamic view addition/removal as triggered by viewport directives. Syncs changes between viewContainer on the render and logic sides. Closes #792
This commit is contained in:
+12
@@ -54,3 +54,15 @@ export class Attribute extends DependencyAnnotation {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The directive can inject an query that would reflect a list of ancestor directives
|
||||
*/
|
||||
export class Query extends DependencyAnnotation {
|
||||
directive;
|
||||
@CONST()
|
||||
constructor(directive) {
|
||||
super();
|
||||
this.directive = directive;
|
||||
}
|
||||
}
|
||||
|
||||
+281
-12
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,47 @@
|
||||
library angular2.src.core.compiler.query_list;
|
||||
|
||||
import 'package:angular2/src/core/annotations/annotations.dart';
|
||||
import 'dart: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.
|
||||
*/
|
||||
class QueryList extends Object with IterableMixin<Directive> {
|
||||
List<Directive> _results;
|
||||
List _callbacks;
|
||||
bool _dirty;
|
||||
|
||||
QueryList(): _results = [], _callbacks = [], _dirty = false;
|
||||
|
||||
Iterator<Directive> get iterator => _results.iterator;
|
||||
|
||||
reset(newList) {
|
||||
_results = newList;
|
||||
_dirty = true;
|
||||
}
|
||||
|
||||
add(obj) {
|
||||
_results.add(obj);
|
||||
_dirty = true;
|
||||
}
|
||||
|
||||
// TODO(rado): hook up with change detection after #995.
|
||||
fireCallbacks() {
|
||||
if (_dirty) {
|
||||
_callbacks.forEach((c) => c());
|
||||
_dirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
onChange(callback) {
|
||||
this._callbacks.add(callback);
|
||||
}
|
||||
|
||||
removeCallback(callback) {
|
||||
this._callbacks.remove(callback);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import {List, MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {Directive} from 'angular2/src/core/annotations/annotations';
|
||||
|
||||
/**
|
||||
* 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 QueryList {
|
||||
_results: List<Directive>;
|
||||
_callbacks;
|
||||
_dirty;
|
||||
|
||||
constructor() {
|
||||
this._results = [];
|
||||
this._callbacks = [];
|
||||
this._dirty = false;
|
||||
}
|
||||
|
||||
[Symbol.iterator]() {
|
||||
return this._results[Symbol.iterator]();
|
||||
}
|
||||
|
||||
reset(newList) {
|
||||
this._results = newList;
|
||||
this._dirty = true;
|
||||
}
|
||||
|
||||
add(obj) {
|
||||
ListWrapper.push(this._results, obj);
|
||||
this._dirty = true;
|
||||
}
|
||||
|
||||
// TODO(rado): hook up with change detection after #995.
|
||||
fireCallbacks() {
|
||||
if (this._dirty) {
|
||||
ListWrapper.forEach(this._callbacks, (c) => c());
|
||||
this._dirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
onChange(callback) {
|
||||
ListWrapper.push(this._callbacks, callback);
|
||||
}
|
||||
|
||||
removeCallback(callback) {
|
||||
ListWrapper.remove(this._callbacks, callback);
|
||||
}
|
||||
}
|
||||
+14
-5
@@ -75,6 +75,11 @@ export class ViewContainer {
|
||||
return this._views.length;
|
||||
}
|
||||
|
||||
_siblingInjectorToLinkAfter(index: number) {
|
||||
if (index == 0) return null;
|
||||
return ListWrapper.last(this._views[index - 1].rootElementInjectors)
|
||||
}
|
||||
|
||||
hydrated() {
|
||||
return isPresent(this.appInjector);
|
||||
}
|
||||
@@ -106,7 +111,7 @@ export class ViewContainer {
|
||||
if (atIndex == -1) atIndex = this._views.length;
|
||||
ListWrapper.insert(this._views, atIndex, view);
|
||||
this.parentView.changeDetector.addChild(view.changeDetector);
|
||||
this._linkElementInjectors(view);
|
||||
this._linkElementInjectors(this._siblingInjectorToLinkAfter(atIndex), view);
|
||||
|
||||
return view;
|
||||
}
|
||||
@@ -133,15 +138,19 @@ export class ViewContainer {
|
||||
return detachedView;
|
||||
}
|
||||
|
||||
_linkElementInjectors(view) {
|
||||
for (var i = 0; i < view.rootElementInjectors.length; ++i) {
|
||||
view.rootElementInjectors[i].parent = this.elementInjector;
|
||||
contentTagContainers() {
|
||||
return this._views;
|
||||
}
|
||||
|
||||
_linkElementInjectors(sibling, view) {
|
||||
for (var i = view.rootElementInjectors.length - 1; i >= 0; i--) {
|
||||
view.rootElementInjectors[i].linkAfter(this.elementInjector, sibling);
|
||||
}
|
||||
}
|
||||
|
||||
_unlinkElementInjectors(view) {
|
||||
for (var i = 0; i < view.rootElementInjectors.length; ++i) {
|
||||
view.rootElementInjectors[i].parent = null;
|
||||
view.rootElementInjectors[i].unlink();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,6 +122,7 @@ export class ViewContainer {
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
static moveViewNodesAfterSibling(sibling, view) {
|
||||
for (var i = view.rootNodes.length - 1; i >= 0; --i) {
|
||||
DOM.insertAfter(sibling, view.rootNodes[i]);
|
||||
|
||||
Reference in New Issue
Block a user