Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
 into jdanyow-unwrap
  • Loading branch information
EisenbergEffect committed Nov 10, 2015
2 parents 60b8067 + 7c689a0 commit 3444123
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 19 deletions.
94 changes: 86 additions & 8 deletions src/repeat.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,20 @@
/*eslint no-loop-func:0, no-unused-vars:0*/
import {inject} from 'aurelia-dependency-injection';
import {ObserverLocator, calcSplices, getChangeRecords} from 'aurelia-binding';
import {BoundViewFactory, ViewSlot, customAttribute, bindable, templateController} from 'aurelia-templating';
import {
ObserverLocator,
getChangeRecords,
BindingBehavior,
ValueConverter
} from 'aurelia-binding';
import {
BoundViewFactory,
TargetInstruction,
ViewSlot,
ViewResources,
customAttribute,
bindable,
templateController
} from 'aurelia-templating';
import {CollectionStrategyLocator} from './collection-strategy-locator';

/**
Expand All @@ -10,12 +23,14 @@ import {CollectionStrategyLocator} from './collection-strategy-locator';
* @class Repeat
* @constructor
* @param {BoundViewFactory} viewFactory The factory generating the view
* @param {TargetInstruction} instruction The view instruction
* @param {ViewSlot} viewSlot The slot the view is injected in to
* @param {ViewResources} viewResources The view resources
* @param {ObserverLocator} observerLocator The observer locator instance
*/
@customAttribute('repeat')
@templateController
@inject(BoundViewFactory, ViewSlot, ObserverLocator, CollectionStrategyLocator)
@inject(BoundViewFactory, TargetInstruction, ViewSlot, ViewResources, ObserverLocator, CollectionStrategyLocator)
export class Repeat {
/**
* List of items to bind the repeater to
Expand All @@ -27,9 +42,11 @@ export class Repeat {
@bindable local
@bindable key
@bindable value
constructor(viewFactory, viewSlot, observerLocator, collectionStrategyLocator) {
constructor(viewFactory, instruction, viewSlot, viewResources, observerLocator, collectionStrategyLocator) {
this.viewFactory = viewFactory;
this.instruction = instruction;
this.viewSlot = viewSlot;
this.lookupFunctions = viewResources.lookupFunctions;
this.observerLocator = observerLocator;
this.local = 'item';
this.key = 'key';
Expand All @@ -47,12 +64,16 @@ export class Repeat {
return;
}

this.sourceExpression = getSourceExpression(this.instruction, 'repeat.for');
this.scope = { bindingContext, overrideContext };
this.collectionStrategy = this.collectionStrategyLocator.getStrategy(this.items);
this.collectionStrategy.initialize(this, bindingContext, overrideContext);
this.processItems();
}

unbind() {
this.sourceExpression = null;
this.scope = null;
this.collectionStrategy.dispose();
this.items = null;
this.collectionStrategy = null;
Expand Down Expand Up @@ -94,17 +115,74 @@ export class Repeat {
}
}

processItemsByStrategy() {
getInnerCollection() {
let expression = unwrapExpression(this.sourceExpression);
if (!expression) {
return null;
}
return expression.evaluate(this.scope, null);
}

observeInnerCollection() {
let items = this.getInnerCollection();
if (items instanceof Array) {
this.collectionObserver = this.observerLocator.getArrayObserver(items);
} else if (items instanceof Map) {
this.collectionObserver = this.observerLocator.getMapObserver(items);
} else {
return false;
}
this.callContext = 'handleInnerCollectionChanges';
this.collectionObserver.subscribe(this.callContext, this);
return true;
}

observeCollection() {
let items = this.items;
this.collectionObserver = this.collectionStrategy.getCollectionObserver(items);
this.collectionStrategy.processItems(items);
if (this.collectionObserver) {
this.callContext = 'handleChanges';
this.callContext = 'handleCollectionChanges';
this.collectionObserver.subscribe(this.callContext, this);
}
}

handleChanges(collection, changes) {
processItemsByStrategy() {
if (!this.observeInnerCollection()) {
this.observeCollection();
}
this.collectionStrategy.processItems(this.items);
}

handleCollectionChanges(collection, changes) {
this.collectionStrategy.handleChanges(collection, changes);
}

handleInnerCollectionChanges(collection, changes) {
let newItems = this.sourceExpression.evaluate(this.scope, this.lookupFunctions);
if (newItems === this.items) {
return;
}
this.items = newItems;
this.itemsChanged();
}
}

function getSourceExpression(instruction, attrName) {
return instruction.behaviorInstructions
.filter(bi => bi.originalAttrName === attrName)[0]
.attributes
.items
.sourceExpression;
}

function unwrapExpression(expression) {
let unwrapped = false;
while (expression instanceof BindingBehavior) {
expression = expression.expression;
}
while (expression instanceof ValueConverter) {
expression = expression.expression;
unwrapped = true;
}
return unwrapped ? expression : null;
}
14 changes: 8 additions & 6 deletions test/array-collection-strategy.spec.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import {ObserverLocator} from 'aurelia-binding';
import {BoundViewFactory, TemplatingEngine, ViewSlot, ViewFactory, ModuleAnalyzer} from 'aurelia-templating';
import {BoundViewFactory, TemplatingEngine, ViewSlot, ViewFactory, ModuleAnalyzer, TargetInstruction, ViewResources} from 'aurelia-templating';
import {Container} from 'aurelia-dependency-injection';
import {initialize} from 'aurelia-pal-browser';
import {Repeat} from '../src/repeat';
import {CollectionStrategyLocator} from '../src/collection-strategy-locator';
import {ArrayCollectionStrategy} from '../src/array-collection-strategy';
import {ViewSlotMock, BoundViewFactoryMock, CollectionStrategyMock, ViewMock, ArrayObserverMock, ViewFactoryMock} from './mocks';
import {ViewSlotMock, BoundViewFactoryMock, CollectionStrategyMock, ViewMock, ArrayObserverMock, ViewFactoryMock, instructionMock, viewResourcesMock} from './mocks';

describe('ArrayCollectionStrategy', () => {
let repeat, strategy, viewSlot, viewFactory, observerLocator, collectionStrategyLocator, collectionStrategyMock;
Expand All @@ -22,6 +22,8 @@ describe('ArrayCollectionStrategy', () => {
collectionStrategyLocator = new CollectionStrategyLocator();
collectionStrategyMock = new CollectionStrategyMock();
strategy = new ArrayCollectionStrategy();
container.registerInstance(TargetInstruction, instructionMock);
container.registerInstance(ViewResources, viewResourcesMock);
container.registerInstance(ViewSlot, viewSlot);
container.registerInstance(BoundViewFactory, viewFactory);
container.registerInstance(ObserverLocator, observerLocator);
Expand Down Expand Up @@ -95,7 +97,7 @@ describe('ArrayCollectionStrategy', () => {
});

it('should correctly handle adding item (i.e Array.prototype.push())', () => {
repeat = new Repeat(new ViewFactoryMock(), viewSlot, new ObserverLocator());
repeat = new Repeat(new ViewFactoryMock(), instructionMock, viewSlot, viewResourcesMock, new ObserverLocator());
strategy.initialize(repeat, {});
splices = [{
addedCount: 1,
Expand All @@ -118,7 +120,7 @@ describe('ArrayCollectionStrategy', () => {
view4.overrideContext = { item: 'norf' };
let viewSlotMock = new ViewSlotMock();
viewSlotMock.children = [view1, view2, view3, view4];
repeat = new Repeat(new ViewFactoryMock(), viewSlotMock, new ObserverLocator());
repeat = new Repeat(new ViewFactoryMock(), instructionMock, viewSlotMock, viewResourcesMock, new ObserverLocator());
strategy.initialize(repeat, {});
splices = [{
addedCount: 1,
Expand All @@ -138,7 +140,7 @@ describe('ArrayCollectionStrategy', () => {
view4.overrideContext = { item: 'norf' };
let viewSlotMock = new ViewSlotMock();
viewSlotMock.children = [view1, view2, view3, view4];
repeat = new Repeat(new ViewFactoryMock(), viewSlotMock, new ObserverLocator());
repeat = new Repeat(new ViewFactoryMock(), instructionMock, viewSlotMock, viewResourcesMock, new ObserverLocator());
strategy.initialize(repeat, {});

splices = [{
Expand Down Expand Up @@ -168,7 +170,7 @@ describe('ArrayCollectionStrategy', () => {
it('moving animated item', done => {
let viewSlotMock = new ViewSlotMock();
viewSlotMock.children = [view1, view2, view3];
repeat = new Repeat(new ViewFactoryMock(), viewSlotMock, new ObserverLocator());
repeat = new Repeat(new ViewFactoryMock(), instructionMock, viewSlotMock, viewResourcesMock, new ObserverLocator());
strategy.initialize(repeat, {});
let removeAction = () => {
viewSlot.children.splice(2, 1);
Expand Down
22 changes: 22 additions & 0 deletions test/mocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,25 @@ export class CollectionStrategyMock {
getCollectionObserver() {}
dispose() {}
}

export const instructionMock = {
behaviorInstructions: [
{
originalAttrName: 'repeat.for',
attributes: {
items: {
sourceExpression: {
evaluate: (scope, lookupFunctions) => null
}
}
}
}
]
};

export const viewResourcesMock = {
lookupFunctions: {
valueConverters: name => null,
bindingBehaviors: name => null
}
};
8 changes: 5 additions & 3 deletions test/number-strategy.spec.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import {ObserverLocator} from 'aurelia-binding';
import {BoundViewFactory, TemplatingEngine, ViewSlot, ViewFactory, ModuleAnalyzer} from 'aurelia-templating';
import {BoundViewFactory, TemplatingEngine, ViewSlot, ViewFactory, ModuleAnalyzer, TargetInstruction, ViewResources} from 'aurelia-templating';
import {Container} from 'aurelia-dependency-injection';
import {initialize} from 'aurelia-pal-browser';
import {Repeat} from '../src/repeat';
import {CollectionStrategyLocator} from '../src/collection-strategy-locator';
import {NumberStrategy} from '../src/number-strategy';
import {ViewSlotMock, BoundViewFactoryMock, CollectionStrategyMock, ViewMock, ArrayObserverMock, ViewFactoryMock} from './mocks';
import {ViewSlotMock, BoundViewFactoryMock, CollectionStrategyMock, ViewMock, ArrayObserverMock, ViewFactoryMock, instructionMock, viewResourcesMock} from './mocks';

describe('NumberStrategy', () => {
let repeat, strategy, viewSlot, viewFactory, observerLocator, collectionStrategyLocator, collectionStrategyMock;
Expand All @@ -22,6 +22,8 @@ describe('NumberStrategy', () => {
collectionStrategyLocator = new CollectionStrategyLocator();
collectionStrategyMock = new CollectionStrategyMock();
strategy = new NumberStrategy();
container.registerInstance(TargetInstruction, instructionMock);
container.registerInstance(ViewResources, viewResourcesMock);
container.registerInstance(ViewSlot, viewSlot);
container.registerInstance(BoundViewFactory, viewFactory);
container.registerInstance(ObserverLocator, observerLocator);
Expand All @@ -32,7 +34,7 @@ describe('NumberStrategy', () => {

describe('processItems', () => {
beforeEach(() => {
repeat = new Repeat(new ViewFactoryMock(), viewSlot, new ObserverLocator());
repeat = new Repeat(new ViewFactoryMock(), instructionMock, viewSlot, viewResourcesMock, new ObserverLocator());
strategy.initialize(repeat, {});
viewSlot.children = [];
});
Expand Down
6 changes: 4 additions & 2 deletions test/repeat.spec.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import {ObserverLocator} from 'aurelia-binding';
import {BoundViewFactory, TemplatingEngine, ViewSlot, ViewFactory, ModuleAnalyzer} from 'aurelia-templating';
import {BoundViewFactory, TemplatingEngine, ViewSlot, ViewFactory, ModuleAnalyzer, TargetInstruction, ViewResources} from 'aurelia-templating';
import {Container} from 'aurelia-dependency-injection';
import {initialize} from 'aurelia-pal-browser';
import {Repeat} from '../src/repeat';
import {CollectionStrategyLocator} from '../src/collection-strategy-locator';
import {ViewSlotMock, BoundViewFactoryMock, CollectionStrategyMock, ViewMock, ArrayObserverMock} from './mocks';
import {ViewSlotMock, BoundViewFactoryMock, CollectionStrategyMock, ViewMock, ArrayObserverMock, instructionMock, viewResourcesMock} from './mocks';

describe('repeat', () => {
let repeat, viewSlot, viewFactory, observerLocator, collectionStrategyLocator, collectionStrategyMock;
Expand All @@ -20,6 +20,8 @@ describe('repeat', () => {
observerLocator = new ObserverLocator();
collectionStrategyLocator = new CollectionStrategyLocator();
collectionStrategyMock = new CollectionStrategyMock();
container.registerInstance(TargetInstruction, instructionMock);
container.registerInstance(ViewResources, viewResourcesMock);
container.registerInstance(ViewSlot, viewSlot);
container.registerInstance(BoundViewFactory, viewFactory);
container.registerInstance(ObserverLocator, observerLocator);
Expand Down

0 comments on commit 3444123

Please sign in to comment.