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 Jul 3, 2019
1 parent 750f519 commit 4078883
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
Expand Up @@ -3,7 +3,22 @@ import { DOM } from 'aurelia-pal';
import { TaskQueue } from 'aurelia-task-queue'; import { TaskQueue } from 'aurelia-task-queue';
import { bindable, CompositionContext, CompositionEngine, customElement, noView, View, ViewResources, ViewSlot } from 'aurelia-templating'; 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. * 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; @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. * 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; const changes = composer.changes;
composer.changes = Object.create(null); 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 // just try to activate the current view model
composer.pendingTask = tryActivateViewModel(composer.currentViewModel, changes.model); composer.pendingTask = tryActivateViewModel(composer.currentViewModel, changes.model);
if (!composer.pendingTask) { return; } if (!composer.pendingTask) { return; }
Expand Down
13 changes: 12 additions & 1 deletion test/compose.spec.ts
@@ -1,6 +1,6 @@
import './setup'; import './setup';
import { TaskQueue } from 'aurelia-task-queue'; import { TaskQueue } from 'aurelia-task-queue';
import { Compose } from '../src/compose'; import { Compose, ActivationStrategy } from '../src/compose';
import * as LogManager from 'aurelia-logging'; import * as LogManager from 'aurelia-logging';
import { View } from 'aurelia-framework'; import { View } from 'aurelia-framework';


Expand Down Expand Up @@ -156,6 +156,17 @@ describe('Compose', () => {
done(); 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', () => { describe('does not trigger composition', () => {
Expand Down

0 comments on commit 4078883

Please sign in to comment.