Skip to content
Permalink
Browse files

feat(compose): activation strategy hook in VM

This commit adds the support to determine the activation strategy of the
bound view-model by invoking the determineActivationStrategy hook in the
view-model, when implemented. The activation strategy from the
view-model always overrides the one in the instance of `compose`.

Addendum for #381
  • Loading branch information...
Sayan751 committed Jul 4, 2019
1 parent 4078883 commit 3aa3020da12c4b5e330f838542edd7b285ccb993
Showing with 140 additions and 15 deletions.
  1. +31 −12 package-lock.json
  2. +10 −1 src/compose.ts
  3. +64 −2 test/compose.integration.spec.ts
  4. +35 −0 test/compose.spec.ts

Some generated files are not rendered by default. Learn more.

@@ -250,7 +250,7 @@ function processChanges(composer: Compose) {
const changes = composer.changes;
composer.changes = Object.create(null);

if (!('view' in changes) && !('viewModel' in changes) && ('model' in changes) && composer.activationStrategy !== ActivationStrategy.Replace) {
if (!('view' in changes) && !('viewModel' in changes) && ('model' in changes) && determineActivationStrategy(composer) !== ActivationStrategy.Replace) {
// just try to activate the current view model
composer.pendingTask = tryActivateViewModel(composer.currentViewModel, changes.model);
if (!composer.pendingTask) { return; }
@@ -298,3 +298,12 @@ function requestUpdate(composer: Compose) {
processChanges(composer);
});
}

function determineActivationStrategy(composer: Compose) {
let activationStrategy = composer.activationStrategy;
const vm = composer.currentViewModel;
if (vm && typeof vm.determineActivationStrategy === 'function') {
activationStrategy = vm.determineActivationStrategy();
}
return activationStrategy;
}
@@ -1,8 +1,8 @@
import './setup';
import { StageComponent, ComponentTester } from 'aurelia-testing';
import { bootstrap } from 'aurelia-bootstrapper';
import { Compose } from '../src/compose';
import { InlineViewStrategy, useView } from 'aurelia-framework';
import { Compose, ActivationStrategy } from '../src/compose';
import { InlineViewStrategy, useView, TaskQueue } from 'aurelia-framework';

describe('compose.integration.spec.ts', () => {

@@ -81,6 +81,68 @@ describe('compose.integration.spec.ts', () => {
component.dispose();
});

it('works with determineActivationStrategy() - replace', async () => {
const { component, compose } = await bootstrapCompose(
`<compose view-model.bind="viewModel"></compose>`,
{
viewModel: class {
// w/o the get view strategy, the initial composition fails, which results to undefined currentViewModel
getViewStrategy() {
return new InlineViewStrategy('<template></template>');
}

determineActivationStrategy() {
return ActivationStrategy.Replace;
}
}
}
);

const taskQueue = new TaskQueue();
spyOn(compose.compositionEngine, 'compose').and.callThrough();
const oldModel = compose.model;
compose.modelChanged({ a: 1 }, oldModel);

taskQueue.queueMicroTask(() => {
expect(compose.compositionEngine.compose).toHaveBeenCalledTimes(1);
component.dispose();
});

});

it('works with determineActivationStrategy() - invoke-lifecycle', async () => {
let activationCount = 0;
const { component, compose } = await bootstrapCompose(
`<compose view-model.bind="viewModel"></compose>`,
{
viewModel: class {
activate() {
activationCount++;
}

// w/o the get view strategy, the initial composition fails, which results to undefined currentViewModel
getViewStrategy() {
return new InlineViewStrategy('<template></template>');
}

determineActivationStrategy() {
return ActivationStrategy.InvokeLifecycle;
}
}
}
);

const taskQueue = new TaskQueue();

const oldModel = compose.model;
compose.modelChanged({}, oldModel);

taskQueue.queueMicroTask(() => {
expect(activationCount).toBe(2, 'activation count === 2');
component.dispose();
});
});

describe('scope traversing', () => {
it('traverses scope by default', async () => {
const { component } = await bootstrapCompose(
@@ -167,6 +167,19 @@ describe('Compose', () => {
done();
});
});

it('when "model" changes and the "determineActivationStrategy" hook in view-model returns "replace"', done => {
const model = {};
sut.currentViewModel = {
determineActivationStrategy() { return ActivationStrategy.Replace; }
};
updateBindable('model', model);
taskQueue.queueMicroTask(() => {
expect(compositionEngineMock.compose).toHaveBeenCalledTimes(1);
expect(compositionEngineMock.compose).toHaveBeenCalledWith(jasmine.objectContaining({ model }));
done();
});
});
});

describe('does not trigger composition', () => {
@@ -178,6 +191,28 @@ describe('Compose', () => {
done();
});
});

it('when "model" changes and the "determineActivationStrategy" hook in view-model returns "invoke-lifecycle"', done => {
const model = {};
sut.currentViewModel = {
determineActivationStrategy() { return ActivationStrategy.InvokeLifecycle; }
};
updateBindable('model', model);
taskQueue.queueMicroTask(() => {
expect(compositionEngineMock.compose).not.toHaveBeenCalled();
done();
});
});

it('when "model" changes and the "determineActivationStrategy" hook is not implemented in view-model', done => {
const model = {};
sut.currentViewModel = {};
updateBindable('model', model);
taskQueue.queueMicroTask(() => {
expect(compositionEngineMock.compose).not.toHaveBeenCalled();
done();
});
});
});

it('aggregates changes from single drain of the micro task queue', done => {

0 comments on commit 3aa3020

Please sign in to comment.
You can’t perform that action at this time.