Welcome to Part 6 of this series reviewing the Pluralsight course Aurelia Fundamentals by Brian Noyes.
Brian is CTO and Architect at Solliance, an expert technology solutions development company.
Brian is a Microsoft Regional Director and MVP, and specializes in rich client technologies including XAML and HTML 5, as well as building the services that back them with WCF and ASP.NET Web API.
You can follow Brian through his blog at http://briannoyes.net
Also in this series:
Aurelia Routing Beyond the Basics
Screen Activation Lifecycle
Almost every Aurelia application we write will use the screen activation lifecycle.
There are four stages:
In the previous module, we saw examples of writing our own activate method in our ViewModel.
Brian explains that canDeactivate gives the current ViewModel the opportunity to veto the navigation, or at a minimum to do whatever it needs to prepare for that navigation, such as prompting the user to save any unsaved input.
If canDeactivate returns true then it calls the constructor on the destination ViewModel. Next, it calls canActivate on the destination ViewModel. The code here decides whether it is appropriate to be present the view to the user. Returning false here prevents the navigation.
The next stage is to call deactivate on the current ViewModel, and the final stage calls activate on the destination ViewModel.
Returning Promises from your Activate Method
Promises only resolve when the ViewModel is ready to support rendering the view.
This demo uses Font Awesome to display a spinning cog when navigation is in progress.
We use the method router.isNavigating, ad this has the value true when navigation is taking place.
(At the time of writing, there isn’t very good API documentation for the Router, but it is very similar to the Durandal router at you can find Using The Router documentation for that.)
We see the spinning cog disappears before the screen renders. This is because we haven’t yet used a promise. Adding a promise makes the spinning display right up until the screen renders fully.
Rejecting Navigation with CanActivate
canActivate proceeds the call to activate.
In this lesson we see a Jobs class with a canActivate method returning false, meaning that it’s not ready to be navigated to.
We write a promise that resolves to false after 3 seconds, and see the cog spins for 3 seconds until navigation fails.
This is currently a crappy user experience, but this will be addressed later with a custom navigation pipeline step.
Handling Unfinished Actions Before Navigation
Brian updates the discussion view, binding to a discussionInput and save methods, and we see a trick for cloning an object: JSON.parse(JSON.stringify(obj));
The Discussion class is created with activate, save and canDeactivate methods.
When we try to navigate away with unsaved data, we see the message:
Unsaved data, are you sure you want to navigate away?
Sibling Navigation Panes with ViewPorts
Aurelia lets us have multiple views within a container view.
We see two views leftPane and rightPane, and the config.map ViewPort configuration code.
Implementing Sibling Navigation Panes with ViewPorts
We create a sideBar directory with ads.html, ads.js, sponsors.html and sponsors.js.
Each router-view is named in shell.html and we update shell.js with the new routes.
Child Routers Overview
These are also sometimes called nested routers.
We always have a top level ViewModel and view using the <template> element.
Inside <template> is our <router-view> and we can put a <template> inside our <router-view>. This pattern can go on and on…
Implement Child Routers
A demo implementing this pattern. We see two tabs “Future Events” and “Past Events”, and clicking on “Past Events” creates the error:
ERROR [app-router] Error: Route not found: /past
This is because we have an empty route defined in shell.js. We fix this by adding a href property.
The activation strategy is used by the router to determine what to do when two or more routes have the same module.
We control this by implementing determineActivationStrategy on our ViewModel, returning an activationStrategy enumerated value, such as one of these:
- activationStrategy.no-change – reuse instance with no lifecycle events
- activationStrategy.invokeLifecycle – call lifecycle methods on the ViewModel instance each time the route switches
- activationStrategy.replace – construct new instance of ViewModel and invoke full lifecycle on it
Reusing ViewModels with Activation Strategies
We put the filtering logic into dataRepository.js, adding a new method filterAndFormat.
We also update eventsList.js, adding routeConfig as a new parameter in the activate method, and adding console logging to each lifecycle event.
Because the default activationStrategy is .no-change, we fail to navigate to the past events view.
We can fix this by adding the method determineActivationStrategy, returning activationStrategy.invokeLifecycle.
We must not forget to import the activationStrategy class from the ‘aurelia-router’ module.
Brian also demonstrates the effect of activationStrategy.replace
PushState is a new technology for avoiding the use of # in URLs.
Brian explains that some web crawlers ignore everything to the right of the # symbol, so PushState gives us more control over our SEO. It also provides a better user experience without the # symbols.
Using PushState is fairly simple in Aurelia, but we do need to setup some server-side handling to ignore client route portions of URL for SPA base route. How we do that depends on which server side framework we are using.
Enabling PushState in your Client Addressing
This demo covers the client side changes needed to implement PushState.
Brian says there is official documentation for how to implement the server side changes for Node JS and ASP.NET backends. I couldn’t find this but I did find Andrew Heuermann’s Push State with Node.js and Express.
Custom Navigation Pipeline Steps
Aurelia offers a middleware type model where we can insert custom processing steps during the routing process.
We define a class with a run method on it. This method takes two parameters:
- navigationInstruction – a datastructure describing the navigation step
- next – a function pointer (or delegate).
At the end of our run method we return next(). This returns a promise, so we can chain handling onto the end of next().
We must also add the step into our pipeline e.g.
This runs MyCusomPipelineStep immediately after the authorize step.
There are two pipelines steps to choose from:
- authorize (near the beginning of the pipeline)
- modelbind (near the end of the pipeline)
Implementing Custom Navigation Pipeline Steps
Brian finishes this module with a demonstration of these concepts. This uses the library toastr, a very handy popup notification utility which was also used by Cory House in the React Forms module of his latest React course.
Part 7 – Aurelia Databinding Fundamentals is coming soon