Reusing an Existing View Model

Hi,

could someone please elaborate on this?

I find “Available return values are replace and invoke-lifecycle . Remember, “lifecycle” refers to the navigation lifecycle.” in the cheatsheet rather distracting.

What will actually tell the router to re-use a view-model?

Will create a PR for the docs when clarified.

Thx

1 Like

This only affects routes using exactly same view-model, like the example in doc:

{ route: 'product/a',    moduleId: 'product',     nav: true },
{ route: 'product/b',    moduleId: 'product',     nav: true },

When you navigate the app from page product/a to product/b, for efficiency, aurelia will reuse the instance (new Product(...)) created by the old page to render the new page. That means the default strategy is invoke-lifecycle.

Update: the above is wrong, the default strategy is no-change, means aurelia does nothing, this is definitely not what you expected. invoke-lifecycle however means aurelia will reuse the instance (new Product(...)) created by the old page to render the new page.

invoke-lifecycle means aurelia only issues canDeactivate, deactivate, canActivate, activate callbacks on the same instance (new Product(...)) to switch from one route to another. This is efficient, but might surprise developers, because the instance is not in a clean state, you really need to be careful to design the logic to tolerant dirty state during route transition.

I would much like replace to be the default strategy. It’s less performant, but more intuitive to developers.

For routes transition across different view-models, this strategy value is irrelevant, because aurelia has to create new instance of new view-model, there is nothing can be reused through invoke-lifecycle.

1 Like

To be clear

export const determineActivationStrategy = (
  currentNavInstruction: NavigationInstruction,
  prevViewPortInstruction: ViewPortInstruction,
  newViewPortConfig: RouteConfig | ViewPortInstruction,
  // indicates whether there is difference between old and new url params
  hasNewParams: boolean,
  forceLifecycleMinimum?: boolean
): ActivationStrategyType => {

  let newInstructionConfig = currentNavInstruction.config;
  let prevViewPortViewModel = prevViewPortInstruction.component.viewModel;
  let viewPortPlanStrategy: ActivationStrategyType;

  if (prevViewPortInstruction.moduleId !== newViewPortConfig.moduleId) {
// different view-model, use "replace".
    viewPortPlanStrategy = InternalActivationStrategy.Replace;
  } else if ('determineActivationStrategy' in prevViewPortViewModel) {
// when you defined determineActivationStrategy() in view-model
    viewPortPlanStrategy = prevViewPortViewModel.determineActivationStrategy(...currentNavInstruction.lifecycleArgs);
  } else if (newInstructionConfig.activationStrategy) {
// when you define activationStrategy in route config like
// { route: ..., moduleId: ..., activationStrategy: 'replace'}
    viewPortPlanStrategy = newInstructionConfig.activationStrategy;
  } else if (hasNewParams || forceLifecycleMinimum) {
// when you navigate from product/a to product/b for a route defined
// with params like product/:id
// use invoke-lifecycle
    viewPortPlanStrategy = InternalActivationStrategy.InvokeLifecycle;
  } else {
// default to no-change
    viewPortPlanStrategy = InternalActivationStrategy.NoChange;
  }
  return viewPortPlanStrategy;
};
1 Like