feat(query): added support for querying by var bindings
This commit is contained in:
@ -1,4 +1,4 @@
|
||||
import {CONST, Type, stringify, isPresent} from 'angular2/src/facade/lang';
|
||||
import {CONST, Type, stringify, isPresent, StringWrapper, isString} from 'angular2/src/facade/lang';
|
||||
import {DependencyAnnotation} from 'angular2/src/di/annotations_impl';
|
||||
import {resolveForwardRef} from 'angular2/di';
|
||||
|
||||
@ -55,12 +55,17 @@ export class Attribute extends DependencyAnnotation {
|
||||
@CONST()
|
||||
export class Query extends DependencyAnnotation {
|
||||
descendants: boolean;
|
||||
constructor(private _selector:Type, {descendants = false}: {descendants?: boolean} = {}) {
|
||||
constructor(private _selector: Type | string,
|
||||
{descendants = false}: {descendants?: boolean} = {}) {
|
||||
super();
|
||||
this.descendants = descendants;
|
||||
}
|
||||
|
||||
get selector() { return resolveForwardRef(this._selector); }
|
||||
|
||||
get isVarBindingQuery(): boolean { return isString(this.selector); }
|
||||
|
||||
get varBindings(): List<string> { return StringWrapper.split(this.selector, new RegExp(",")); }
|
||||
|
||||
toString() { return `@Query(${stringify(this.selector)})`; }
|
||||
}
|
||||
|
@ -13,7 +13,6 @@ export class ElementBinder {
|
||||
|
||||
constructor(public index: int, public parent: ElementBinder, public distanceToParent: int,
|
||||
public protoElementInjector: eiModule.ProtoElementInjector,
|
||||
public directiveVariableBindings: Map<string, number>,
|
||||
public componentDirective: DirectiveBinding) {
|
||||
if (isBlank(index)) {
|
||||
throw new BaseException('null index not allowed.');
|
||||
|
@ -393,7 +393,8 @@ export class ProtoElementInjector {
|
||||
_strategy: _ProtoElementInjectorStrategy;
|
||||
|
||||
static create(parent: ProtoElementInjector, index: number, bindings: List<ResolvedBinding>,
|
||||
firstBindingIsComponent: boolean, distanceToParent: number) {
|
||||
firstBindingIsComponent: boolean, distanceToParent: number,
|
||||
directiveVariableBindings: Map<string, number>) {
|
||||
var bd = [];
|
||||
|
||||
ProtoElementInjector._createDirectiveBindingData(bindings, bd, firstBindingIsComponent);
|
||||
@ -401,7 +402,8 @@ export class ProtoElementInjector {
|
||||
ProtoElementInjector._createViewInjectorBindingData(bindings, bd);
|
||||
}
|
||||
ProtoElementInjector._createHostInjectorBindingData(bindings, bd, firstBindingIsComponent);
|
||||
return new ProtoElementInjector(parent, index, bd, distanceToParent, firstBindingIsComponent);
|
||||
return new ProtoElementInjector(parent, index, bd, distanceToParent, firstBindingIsComponent,
|
||||
directiveVariableBindings);
|
||||
}
|
||||
|
||||
private static _createDirectiveBindingData(dirBindings: List<ResolvedBinding>,
|
||||
@ -450,7 +452,8 @@ export class ProtoElementInjector {
|
||||
}
|
||||
|
||||
constructor(public parent: ProtoElementInjector, public index: int, bd: List<BindingData>,
|
||||
public distanceToParent: number, public _firstBindingIsComponent: boolean) {
|
||||
public distanceToParent: number, public _firstBindingIsComponent: boolean,
|
||||
public directiveVariableBindings: Map<string, number>) {
|
||||
var length = bd.length;
|
||||
this.eventEmitterAccessors = ListWrapper.createFixedSize(length);
|
||||
this.hostActionAccessors = ListWrapper.createFixedSize(length);
|
||||
@ -693,12 +696,15 @@ export class ElementInjector extends TreeNode<ElementInjector> {
|
||||
}
|
||||
|
||||
onAllChangesDone(): void {
|
||||
if (isPresent(this._query0) && this._query0.originator === this)
|
||||
if (isPresent(this._query0) && this._query0.originator === this) {
|
||||
this._query0.list.fireCallbacks();
|
||||
if (isPresent(this._query1) && this._query1.originator === this)
|
||||
}
|
||||
if (isPresent(this._query1) && this._query1.originator === this) {
|
||||
this._query1.list.fireCallbacks();
|
||||
if (isPresent(this._query2) && this._query2.originator === this)
|
||||
}
|
||||
if (isPresent(this._query2) && this._query2.originator === this) {
|
||||
this._query2.list.fireCallbacks();
|
||||
}
|
||||
}
|
||||
|
||||
hydrate(injector: Injector, host: ElementInjector, preBuiltObjects: PreBuiltObjects): void {
|
||||
@ -716,9 +722,22 @@ export class ElementInjector extends TreeNode<ElementInjector> {
|
||||
this._checkShadowDomAppInjector(this._shadowDomAppInjector);
|
||||
|
||||
this._strategy.hydrate();
|
||||
|
||||
this._addVarBindingsToQueries();
|
||||
|
||||
this.hydrated = true;
|
||||
}
|
||||
|
||||
hasVariableBinding(name: string): boolean {
|
||||
var vb = this._proto.directiveVariableBindings;
|
||||
return isPresent(vb) && MapWrapper.contains(vb, name);
|
||||
}
|
||||
|
||||
getVariableBinding(name: string): any {
|
||||
var index = MapWrapper.get(this._proto.directiveVariableBindings, name);
|
||||
return isPresent(index) ? this.getDirectiveAtIndex(<number>index) : this.getElementRef();
|
||||
}
|
||||
|
||||
private _createShadowDomAppInjector(componentDirective: DirectiveBinding,
|
||||
appInjector: Injector): Injector {
|
||||
if (!ListWrapper.isEmpty(componentDirective.resolvedAppInjectables)) {
|
||||
@ -752,6 +771,10 @@ export class ElementInjector extends TreeNode<ElementInjector> {
|
||||
return this._proto.hostActionAccessors;
|
||||
}
|
||||
|
||||
getDirectiveVariableBindings(): Map<string, number> {
|
||||
return this._proto.directiveVariableBindings;
|
||||
}
|
||||
|
||||
getComponent(): any { return this._strategy.getComponent(); }
|
||||
|
||||
getElementRef(): ElementRef {
|
||||
@ -884,6 +907,23 @@ export class ElementInjector extends TreeNode<ElementInjector> {
|
||||
}
|
||||
}
|
||||
|
||||
private _addVarBindingsToQueries(): void {
|
||||
this._addVarBindingsToQuery(this._query0);
|
||||
this._addVarBindingsToQuery(this._query1);
|
||||
this._addVarBindingsToQuery(this._query2);
|
||||
}
|
||||
|
||||
private _addVarBindingsToQuery(queryRef: QueryRef): void {
|
||||
if (isBlank(queryRef) || !queryRef.query.isVarBindingQuery) return;
|
||||
|
||||
var vb = queryRef.query.varBindings;
|
||||
for (var i = 0; i < vb.length; ++i) {
|
||||
if (this.hasVariableBinding(vb[i])) {
|
||||
queryRef.list.add(this.getVariableBinding(vb[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _createQueryRef(query: Query): void {
|
||||
var queryList = new QueryList<any>();
|
||||
if (isBlank(this._query0)) {
|
||||
@ -1454,13 +1494,32 @@ class QueryRef {
|
||||
|
||||
visit(inj: ElementInjector, aggregator: any[]): void {
|
||||
if (isBlank(inj) || !inj._hasQuery(this)) return;
|
||||
if (inj.hasDirective(this.query.selector)) {
|
||||
aggregator.push(inj.get(this.query.selector));
|
||||
|
||||
if (this.query.isVarBindingQuery) {
|
||||
this._aggregateVariableBindings(inj, aggregator);
|
||||
} else {
|
||||
this._aggregateDirective(inj, aggregator);
|
||||
}
|
||||
|
||||
var child = inj._head;
|
||||
while (isPresent(child)) {
|
||||
this.visit(child, aggregator);
|
||||
child = child._next;
|
||||
}
|
||||
}
|
||||
|
||||
private _aggregateVariableBindings(inj: ElementInjector, aggregator: List<any>): void {
|
||||
var vb = this.query.varBindings;
|
||||
for (var i = 0; i < vb.length; ++i) {
|
||||
if (inj.hasVariableBinding(vb[i])) {
|
||||
aggregator.push(inj.getVariableBinding(vb[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _aggregateDirective(inj: ElementInjector, aggregator: List<any>): void {
|
||||
if (inj.hasDirective(this.query.selector)) {
|
||||
aggregator.push(inj.get(this.query.selector));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -336,9 +336,13 @@ function _createProtoElementInjector(binderIndex, parentPeiWithDistance, renderE
|
||||
// so that, when hydrating, $implicit can be set to the element.
|
||||
var hasVariables = MapWrapper.size(renderElementBinder.variableBindings) > 0;
|
||||
if (directiveBindings.length > 0 || hasVariables) {
|
||||
protoElementInjector = ProtoElementInjector.create(
|
||||
parentPeiWithDistance.protoElementInjector, binderIndex, directiveBindings,
|
||||
isPresent(componentDirectiveBinding), parentPeiWithDistance.distance);
|
||||
var directiveVariableBindings =
|
||||
createDirectiveVariableBindings(renderElementBinder, directiveBindings);
|
||||
|
||||
protoElementInjector =
|
||||
ProtoElementInjector.create(parentPeiWithDistance.protoElementInjector, binderIndex,
|
||||
directiveBindings, isPresent(componentDirectiveBinding),
|
||||
parentPeiWithDistance.distance, directiveVariableBindings);
|
||||
protoElementInjector.attributes = renderElementBinder.readAttributes;
|
||||
}
|
||||
return protoElementInjector;
|
||||
@ -351,12 +355,8 @@ function _createElementBinder(protoView, boundElementIndex, renderElementBinder,
|
||||
if (renderElementBinder.parentIndex !== -1) {
|
||||
parent = protoView.elementBinders[renderElementBinder.parentIndex];
|
||||
}
|
||||
|
||||
var directiveVariableBindings =
|
||||
createDirectiveVariableBindings(renderElementBinder, directiveBindings);
|
||||
var elBinder =
|
||||
protoView.bindElement(parent, renderElementBinder.distanceToParent, protoElementInjector,
|
||||
directiveVariableBindings, componentDirectiveBinding);
|
||||
var elBinder = protoView.bindElement(parent, renderElementBinder.distanceToParent,
|
||||
protoElementInjector, componentDirectiveBinding);
|
||||
protoView.bindEvent(renderElementBinder.eventBindings, boundElementIndex, -1);
|
||||
// variables
|
||||
// The view's locals needs to have a full set of variable names at construction time
|
||||
@ -371,7 +371,7 @@ function _createElementBinder(protoView, boundElementIndex, renderElementBinder,
|
||||
|
||||
export function createDirectiveVariableBindings(
|
||||
renderElementBinder: renderApi.ElementBinder,
|
||||
directiveBindings: List<DirectiveBinding>): Map<String, number> {
|
||||
directiveBindings: List<DirectiveBinding>): Map<string, number> {
|
||||
var directiveVariableBindings = MapWrapper.create();
|
||||
MapWrapper.forEach(renderElementBinder.variableBindings, (templateName, exportAs) => {
|
||||
var dirIndex = _findDirectiveIndexByExportAs(renderElementBinder, directiveBindings, exportAs);
|
||||
|
@ -177,11 +177,9 @@ export class AppProtoView {
|
||||
|
||||
bindElement(parent: ElementBinder, distanceToParent: int,
|
||||
protoElementInjector: ProtoElementInjector,
|
||||
directiveVariableBindings: Map<string, number>,
|
||||
componentDirective: DirectiveBinding = null): ElementBinder {
|
||||
var elBinder =
|
||||
new ElementBinder(this.elementBinders.length, parent, distanceToParent,
|
||||
protoElementInjector, directiveVariableBindings, componentDirective);
|
||||
var elBinder = new ElementBinder(this.elementBinders.length, parent, distanceToParent,
|
||||
protoElementInjector, componentDirective);
|
||||
|
||||
this.elementBinders.push(elBinder);
|
||||
return elBinder;
|
||||
|
@ -149,28 +149,30 @@ export class AppViewManagerUtils {
|
||||
|
||||
var binders = view.proto.elementBinders;
|
||||
for (var i = 0; i < binders.length; ++i) {
|
||||
var binder = binders[i];
|
||||
var elementInjector = view.elementInjectors[i];
|
||||
|
||||
if (isPresent(elementInjector)) {
|
||||
elementInjector.hydrate(appInjector, hostElementInjector, view.preBuiltObjects[i]);
|
||||
this._populateViewLocals(view, elementInjector);
|
||||
this._setUpEventEmitters(view, elementInjector, i);
|
||||
this._setUpHostActions(view, elementInjector, i);
|
||||
|
||||
if (isPresent(binder.directiveVariableBindings)) {
|
||||
MapWrapper.forEach(binder.directiveVariableBindings, (directiveIndex, name) => {
|
||||
if (isBlank(directiveIndex)) {
|
||||
view.locals.set(name, elementInjector.getElementRef().domElement);
|
||||
} else {
|
||||
view.locals.set(name, elementInjector.getDirectiveAtIndex(directiveIndex));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
view.changeDetector.hydrate(view.context, view.locals, view);
|
||||
}
|
||||
|
||||
_populateViewLocals(view: viewModule.AppView, elementInjector: eli.ElementInjector): void {
|
||||
if (isPresent(elementInjector.getDirectiveVariableBindings())) {
|
||||
MapWrapper.forEach(elementInjector.getDirectiveVariableBindings(), (directiveIndex, name) => {
|
||||
if (isBlank(directiveIndex)) {
|
||||
view.locals.set(name, elementInjector.getElementRef().domElement);
|
||||
} else {
|
||||
view.locals.set(name, elementInjector.getDirectiveAtIndex(directiveIndex));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_getOrCreateViewContainer(parentView: viewModule.AppView, boundElementIndex: number) {
|
||||
var viewContainer = parentView.viewContainers[boundElementIndex];
|
||||
if (isBlank(viewContainer)) {
|
||||
|
Reference in New Issue
Block a user