5
votes

Is there a simple way to use Durandal's transitions to fade out an old view immediately, and fade in the new one once its activate function has resolved?

Here's some context:

I have a Durandal 1.2 SPA where most of the views' activate functions make service calls and return a promise as appropriate. This all works great, but transitions only run when loading is complete sometimes services can take few seconds to respond - leading to a poor user experience where you click on a link and it takes a few seconds before anything noticeable happens.

The solution is to animate away the old view immediately (before activate resolves) and then animate in the new view when activate is done. At the moment, every animated view corresponds to a route and I don't expect this to change. I've come up with and tested a few solutions that all work but seem less than ideal (and don't leverage Durandal's transition framework):

  • Manually animate away every view in its deactivate() and animate it back in via its viewAttached()
  • Bind the .page-host div's visibility to router.isNavigating (using a custom binding to handle the transition such as the fadeVisible example from the knockout site)
  • Manually subscribe to router.isNavigating and run custom logic when it changes

I've tried them all and so far I like the custom binding on the .page-host div the best since it involves the least amount of code, but it only works in my case because of my specific circumstances and isn't a generic solution.

This seems like exactly the sort of thing Durandal's transitions were created for. Is there a more elegant way to do this in Durandal using transitions (in 1.2 or the upcoming 2.0 release)?

I saw this question which seems to be asking something similar, but seems a little less specific and doesn't have a relevant answer.

2

2 Answers

0
votes

Move your async logic out of activate and put it into viewAttached. This will allow the view to animate in immediately. Then, from viewAttached, you are safe to execute async code without breaking KO because the bindings have already been applied. (Note: viewAttached is renamed in 2.0 to attachedToParent.)

0
votes

You can add handler for 'router:route:activating' event by adding next code into your shell.js:

router.on('router:route:activating').then(function () {
  //fade out animation goes here;
  //Usually that is changing of observable which will add/remove css class for your view (or shows loading overlay)
  //Animation of view transition could be done by add class css3 animation.
});

And in case if you are using "loading" observable - in custom transition you can set this observable back to hide loading overlay:

if (context.bindingContext.$data && context.bindingContext.$data.isLoading && ko.isObservable(context.bindingContext.$data.isLoading )) {
  composition.current.complete(function() {
  context.bindingContext.$data.isLoading (true);
  });
}