Skip to content

Commit

Permalink
feat(compose): @bindable activation-strategy
Browse files Browse the repository at this point in the history
This commit introduces @bindable activation-strategy for compose.
The default strategy is 'invoke-lifecycle', which is also the current
strategy. The strategy 'replace' forces re-creation of bound view and
view-model on model change. This is helpful, where the re-instantiation
of view and view-model is needed on model change. For example, when
validation is used in a custom element; on model change, old validation
rules still gets triggered if the view-model is not reinstantiated.

Fixes aurelia#381
  • Loading branch information
Sayan751 committed Jun 26, 2019
1 parent 0cd7d50 commit 2986c64
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 3 deletions.
28 changes: 26 additions & 2 deletions src/compose.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,22 @@ import { DOM } from 'aurelia-pal';
import { TaskQueue } from 'aurelia-task-queue';
import { bindable, CompositionContext, CompositionEngine, customElement, noView, View, ViewResources, ViewSlot } from 'aurelia-templating';


/**
* Available activation strategies for the view and view-model bound to compose
*
* @export
* @enum {string}
*/
export enum ActivationStrategy {
/**
* Default activation strategy; the 'activate' lifecycle hook will be invoked when the model changes.
*/
InvokeLifecycle = 'invoke-lifecycle',
/**
* The view/view-model will be recreated, when the "model" changes.
*/
Replace = 'replace'
}

/**
* Used to compose a new view / view-model template or bind to an existing instance.
Expand Down Expand Up @@ -39,6 +54,15 @@ export class Compose {
*/
@bindable viewModel: any;

/**
* Strategy to activate the view-model. Default is "invoke-lifecycle".
* Bind "replace" to recreate the view/view-model when the model changes.
*
* @property activationStrategy
* @type {ActivationStrategy}
*/
@bindable activationStrategy: ActivationStrategy = ActivationStrategy.InvokeLifecycle;

/**
* SwapOrder to control the swapping order of the custom element's view.
*
Expand Down Expand Up @@ -226,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)) {
if (!('view' in changes) && !('viewModel' in changes) && ('model' in changes) && composer.activationStrategy !== ActivationStrategy.Replace) {
// just try to activate the current view model
composer.pendingTask = tryActivateViewModel(composer.currentViewModel, changes.model);
if (!composer.pendingTask) { return; }
Expand Down
13 changes: 12 additions & 1 deletion test/compose.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import './setup';
import { TaskQueue } from 'aurelia-task-queue';
import { Compose } from '../src/compose';
import { Compose, ActivationStrategy } from '../src/compose';
import * as LogManager from 'aurelia-logging';
import { View } from 'aurelia-framework';

Expand Down Expand Up @@ -156,6 +156,17 @@ describe('Compose', () => {
done();
});
});

it('when "model" changes and the activation-strategy is set to "replace"', done => {
const model = {};
sut.activationStrategy = 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', () => {
Expand Down

0 comments on commit 2986c64

Please sign in to comment.