Skip to content

Commit 3aa3020

Browse files
committed
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
1 parent 4078883 commit 3aa3020

File tree

4 files changed

+140
-15
lines changed

4 files changed

+140
-15
lines changed

package-lock.json

Lines changed: 31 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/compose.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ function processChanges(composer: Compose) {
250250
const changes = composer.changes;
251251
composer.changes = Object.create(null);
252252

253-
if (!('view' in changes) && !('viewModel' in changes) && ('model' in changes) && composer.activationStrategy !== ActivationStrategy.Replace) {
253+
if (!('view' in changes) && !('viewModel' in changes) && ('model' in changes) && determineActivationStrategy(composer) !== ActivationStrategy.Replace) {
254254
// just try to activate the current view model
255255
composer.pendingTask = tryActivateViewModel(composer.currentViewModel, changes.model);
256256
if (!composer.pendingTask) { return; }
@@ -298,3 +298,12 @@ function requestUpdate(composer: Compose) {
298298
processChanges(composer);
299299
});
300300
}
301+
302+
function determineActivationStrategy(composer: Compose) {
303+
let activationStrategy = composer.activationStrategy;
304+
const vm = composer.currentViewModel;
305+
if (vm && typeof vm.determineActivationStrategy === 'function') {
306+
activationStrategy = vm.determineActivationStrategy();
307+
}
308+
return activationStrategy;
309+
}

test/compose.integration.spec.ts

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import './setup';
22
import { StageComponent, ComponentTester } from 'aurelia-testing';
33
import { bootstrap } from 'aurelia-bootstrapper';
4-
import { Compose } from '../src/compose';
5-
import { InlineViewStrategy, useView } from 'aurelia-framework';
4+
import { Compose, ActivationStrategy } from '../src/compose';
5+
import { InlineViewStrategy, useView, TaskQueue } from 'aurelia-framework';
66

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

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

84+
it('works with determineActivationStrategy() - replace', async () => {
85+
const { component, compose } = await bootstrapCompose(
86+
`<compose view-model.bind="viewModel"></compose>`,
87+
{
88+
viewModel: class {
89+
// w/o the get view strategy, the initial composition fails, which results to undefined currentViewModel
90+
getViewStrategy() {
91+
return new InlineViewStrategy('<template></template>');
92+
}
93+
94+
determineActivationStrategy() {
95+
return ActivationStrategy.Replace;
96+
}
97+
}
98+
}
99+
);
100+
101+
const taskQueue = new TaskQueue();
102+
spyOn(compose.compositionEngine, 'compose').and.callThrough();
103+
const oldModel = compose.model;
104+
compose.modelChanged({ a: 1 }, oldModel);
105+
106+
taskQueue.queueMicroTask(() => {
107+
expect(compose.compositionEngine.compose).toHaveBeenCalledTimes(1);
108+
component.dispose();
109+
});
110+
111+
});
112+
113+
it('works with determineActivationStrategy() - invoke-lifecycle', async () => {
114+
let activationCount = 0;
115+
const { component, compose } = await bootstrapCompose(
116+
`<compose view-model.bind="viewModel"></compose>`,
117+
{
118+
viewModel: class {
119+
activate() {
120+
activationCount++;
121+
}
122+
123+
// w/o the get view strategy, the initial composition fails, which results to undefined currentViewModel
124+
getViewStrategy() {
125+
return new InlineViewStrategy('<template></template>');
126+
}
127+
128+
determineActivationStrategy() {
129+
return ActivationStrategy.InvokeLifecycle;
130+
}
131+
}
132+
}
133+
);
134+
135+
const taskQueue = new TaskQueue();
136+
137+
const oldModel = compose.model;
138+
compose.modelChanged({}, oldModel);
139+
140+
taskQueue.queueMicroTask(() => {
141+
expect(activationCount).toBe(2, 'activation count === 2');
142+
component.dispose();
143+
});
144+
});
145+
84146
describe('scope traversing', () => {
85147
it('traverses scope by default', async () => {
86148
const { component } = await bootstrapCompose(

test/compose.spec.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,19 @@ describe('Compose', () => {
167167
done();
168168
});
169169
});
170+
171+
it('when "model" changes and the "determineActivationStrategy" hook in view-model returns "replace"', done => {
172+
const model = {};
173+
sut.currentViewModel = {
174+
determineActivationStrategy() { return ActivationStrategy.Replace; }
175+
};
176+
updateBindable('model', model);
177+
taskQueue.queueMicroTask(() => {
178+
expect(compositionEngineMock.compose).toHaveBeenCalledTimes(1);
179+
expect(compositionEngineMock.compose).toHaveBeenCalledWith(jasmine.objectContaining({ model }));
180+
done();
181+
});
182+
});
170183
});
171184

172185
describe('does not trigger composition', () => {
@@ -178,6 +191,28 @@ describe('Compose', () => {
178191
done();
179192
});
180193
});
194+
195+
it('when "model" changes and the "determineActivationStrategy" hook in view-model returns "invoke-lifecycle"', done => {
196+
const model = {};
197+
sut.currentViewModel = {
198+
determineActivationStrategy() { return ActivationStrategy.InvokeLifecycle; }
199+
};
200+
updateBindable('model', model);
201+
taskQueue.queueMicroTask(() => {
202+
expect(compositionEngineMock.compose).not.toHaveBeenCalled();
203+
done();
204+
});
205+
});
206+
207+
it('when "model" changes and the "determineActivationStrategy" hook is not implemented in view-model', done => {
208+
const model = {};
209+
sut.currentViewModel = {};
210+
updateBindable('model', model);
211+
taskQueue.queueMicroTask(() => {
212+
expect(compositionEngineMock.compose).not.toHaveBeenCalled();
213+
done();
214+
});
215+
});
181216
});
182217

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

0 commit comments

Comments
 (0)