diff --git a/src/repeat.ts b/src/repeat.ts
index 97c5423..6ca555b 100644
--- a/src/repeat.ts
+++ b/src/repeat.ts
@@ -29,6 +29,9 @@ import {AbstractRepeater} from './abstract-repeater';
@templateController
@inject(BoundViewFactory, TargetInstruction, ViewSlot, ViewResources, ObserverLocator, RepeatStrategyLocator)
export class Repeat extends AbstractRepeater {
+
+ static useInnerMatcher = true;
+
/**
* List of items to bind the repeater to.
*
@@ -269,7 +272,9 @@ export class Repeat extends AbstractRepeater {
if (viewFactory) {
const template = viewFactory.template;
const instructions = viewFactory.instructions;
- const instructionIds = Object.keys(instructions);
+ if (Repeat.useInnerMatcher) {
+ return extractMatcherBindingExpression(instructions);
+ }
// if the template has more than 1 immediate child element
// it's a repeat put on a element
// not valid for matcher binding
@@ -284,24 +289,7 @@ export class Repeat extends AbstractRepeater {
return undefined;
}
const repeatedElementTargetId = repeatedElement.getAttribute('au-target-id');
- for (let i = 0; i < instructionIds.length; i++) {
- const instructionId = instructionIds[i];
- // matcher binding can only works when root element is not a
- // checking first el child
- if (instructionId !== repeatedElementTargetId) {
- continue;
- }
- const expressions = instructions[instructionId].expressions as BindingExpression[];
- if (expressions) {
- for (let ii = 0; ii < expressions.length; ii++) {
- if (expressions[ii].targetProperty === 'matcher') {
- const matcherBinding = expressions[ii];
- expressions.splice(ii, 1);
- return matcherBinding;
- }
- }
- }
- }
+ return extractMatcherBindingExpression(instructions, repeatedElementTargetId);
}
return undefined;
@@ -362,3 +350,28 @@ export class Repeat extends AbstractRepeater {
}
}
}
+
+/**
+ * Iterate a record of TargetInstruction and their expressions to find first binding expression that targets property named "matcher"
+ */
+const extractMatcherBindingExpression = (instructions: Record, targetedElementId?: string): BindingExpression | undefined => {
+ const instructionIds = Object.keys(instructions);
+ for (let i = 0; i < instructionIds.length; i++) {
+ const instructionId = instructionIds[i];
+ // matcher binding can only works when root element is not a
+ // checking first el child
+ if (targetedElementId !== undefined && instructionId !== targetedElementId) {
+ continue;
+ }
+ const expressions = instructions[instructionId].expressions as BindingExpression[];
+ if (expressions) {
+ for (let ii = 0; ii < expressions.length; ii++) {
+ if (expressions[ii].targetProperty === 'matcher') {
+ const matcherBindingExpression = expressions[ii];
+ expressions.splice(ii, 1);
+ return matcherBindingExpression;
+ }
+ }
+ }
+ }
+};
diff --git a/test/repeat.issue-378.spec.ts b/test/repeat.issue-378.spec.ts
index 0210c77..2d0a91e 100644
--- a/test/repeat.issue-378.spec.ts
+++ b/test/repeat.issue-378.spec.ts
@@ -2,10 +2,12 @@ import './setup';
import { StageComponent } from 'aurelia-testing';
import { bootstrap } from 'aurelia-bootstrapper';
import { waitForFrames } from './test-utilities';
+import { Repeat } from '../src/repeat';
// https://github.com/aurelia/templating-resources/issues/378
fdescribe('repeat.issue-378.spec.ts', () => {
it('works with -->> <... matcher />', async () => {
+ Repeat.useInnerMatcher = false;
const model = new class {
products = [
{ id: 0, name: 'Motherboard' },
@@ -45,9 +47,11 @@ fdescribe('repeat.issue-378.spec.ts', () => {
expect(model.selectedProduct).toBe(model.products[2]);
component.dispose();
+ Repeat.useInnerMatcher = true;
});
it('works with -->> <... matcher />', async () => {
+ Repeat.useInnerMatcher = false;
const model = new class {
products = new Map([
[0, 'Motherboard'],
@@ -87,17 +91,19 @@ fdescribe('repeat.issue-378.spec.ts', () => {
expect(model.selectedProduct).toEqual(Array.from(model.products)[2]);
component.dispose();
+ Repeat.useInnerMatcher = true;
});
describe('QA', () => {
it('works with matcher', async () => {
const model = new class App {
+
components: any[];
componentsAlternative: any;
objComponents: any[];
objComponentsAlternative: any[];
amatcher: (a: any, b: any) => boolean;
- matcherCalls: number;
+ matcherCallCount: number;
constructor() {
this.components = [];
@@ -113,9 +119,9 @@ fdescribe('repeat.issue-378.spec.ts', () => {
this.objComponentsAlternative.push({ id: i, text: i - 1, cls: 'obj-comp-alt' });
}
- this.matcherCalls = 0;
+ this.matcherCallCount = 0;
this.amatcher = (a, b) => {
- this.matcherCalls++;
+ this.matcherCallCount++;
return a.id === b.id;
};
}
@@ -130,18 +136,33 @@ fdescribe('repeat.issue-378.spec.ts', () => {
this.objComponents = this.objComponents.slice(0).reverse();
}
};
- const view = `
-
+ const view =
+ `
-
- [\${$index}]
- `;
+ class="a-matcher"
+ obj.bind="obj"
+ index.bind="$index">
+ `;
const component = StageComponent
- .withResources()
+ .withResources([
+ class Dummy {
+ static $view = `[\${index}]`;
+ static $resource = {
+ name: 'dummy',
+ bindables: ['obj', 'index']
+ };
+
+ counter = 0;
+
+ attached() {
+ this.counter++;
+ }
+ }
+ ] as any)
.inView(view)
.boundTo(model);
@@ -152,10 +173,10 @@ fdescribe('repeat.issue-378.spec.ts', () => {
expect(document.querySelectorAll('.obj-comp').length).toBe(10);
model.swapArrays();
- await waitForFrames(2);
+ await waitForFrames(1);
expect(document.querySelectorAll('.obj-comp-alt').length).toBe(10);
- expect(model.matcherCalls).toBeGreaterThan(40);
+ expect(model.matcherCallCount).toBeGreaterThan(40);
component.dispose();
});