Welcome to Part 3 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:
Separation of Concerns
MVVM stands for Model-View-ViewModel. It is mostly about trying to achieve good separation of concerns.
Brian uses the laundry of an analogy for separation of concerns. Are you someone who separates your clothes out into separate piles according to the type of clothing? Or do you just throw everything together?
You save a bit of time at first by not organizing your clothes. But when you need to get dressed it takes a lot more time to find what you are looking for.
Taking the time upfront to organize your code will save you a lot of time and frustration over the longer term.
Using the MVVM pattern, and a framework such as Aurelia, we get good separations of concerns.
MVVM Goals and Benefits
Brian explains the following goals:
MVVM Key Concepts
MVVM is an architecture for structuring your client side code, which is a derivative of the MVC pattern.
Perhaps the main difference between MVC and MVVM is there is an ongoing interaction between the view and the viewModel in MVVM.
Often in MVC the View and the Controller have different lifetimes, but that is never the case with MVVM.
Also in MVC, interaction between the different elements is typically done through method calls. MVVM is instead designed for a rich data-binding system.
MVVM Pattern Responsibilities
Brian discusses the responsibilities of each of the elements in the MVVM pattern, starting with the model:
The model contains the client side data-structures. It can have computed properties and validation logic.
The view is the structural definition of what the user see on the screen. It also contains the bindings to properties and methods on the ViewModel, and these are usually HTML attributes.
The main responsibility of the ViewModel is to provide data to the view for presentation and manipulation.
It also encapsulates the interaction logic.
Brian shows an example of the ViewModel allowing the model to work directly with the view. He calls this exposing a model object directly.
We also see a couple of examples of wrapped model data. This is not an exact mapping between model and view as we have additional data set in our ViewModel. The examples Brian gives are:
- A boolean showCustomerAlert which is set based on other model data
- An orders collection where each order is composed of many pieces of model data
Finally we see an example of client state: isLoggedIn
Client Services / Repositories layer
These have shared functionality or data access and are often consumed by one or more ViewModels.
This layer exists to decouple the ViewModels from external dependencies, such as Web API calls. The client repository is responsible for talking to these services, and can act as a data caching container.
MVVM Approaches in Aurelia
There are several approaches for hooking up our Views and ViewModels.
In this part of the course we focus on compose element
Compose element lets us point to a view and a viewmodel and pull them in as a block of UI
The second approach is to use the routing and navigation system of Aurelia.
The final approach is using custom elements, which will be covered later in the course.
Using the Compose Element to setup an MVVM Hierarchy
The approach that Brian likes to take is to sketch out the main elements on the page and where they will appear on the page. For example the CommunityApp Page Structure:
- Header / Nav menu at the top
- Events List in the main section, composes of many events
- Sponsor List on right hand side
In this demo Brian installs jspm and express again (see the Prerequisites module for more on those) and uses jspm to install aurelia-bootstrapper, aurelia-framework and bootstrap.
We also see that we should update the following sections of our config.js:
- BabelOptions – es7 decorators and classProperties
- Paths – src folder
The root of our Single Page App is index.html and this contains script elements which load system.js, config.js and aurelia-bootstrapper.
The body tag has the attribute aurelia-app=”main”.
This references a small amount of aurelia setup code that we create in main.js. This is a configure function that sets the root to a view or viewmodel. The default name is “app” but in Brian’s demo we use “shell”.
We also need shell.js and shell.html
All MVVM views in Aurelia have the <template> element at the root.
The Aurelia framework includes a require element, which allows us to pull in content from a module that has been loaded.
In shell.html, Brian pastes in bootstrap code for setting up a nav bar.
shell.js is just an empty exported class for now.
That’s the Header / Nav menu done already. Next we create sponsors.html and sponsors.js and add a container-fluid div containing the events list and sponsors list.
Each of these use a compose element with the view-model attribute specifying the name of each ViewModel.
Brian runs the web server and opens index.html. We see our navigation menu is rendered as expected.
Leveraging Compose Element Options
We don’t have to specify a ViewModel in our compose element. Instead we can specify a view.
If we do this, Aurelia will assume that we don’t need a ViewModel here, but there wil be bindings to the parent ViewModel.
Brian demonstrates this in shell.js by setting parentprop to “Hug your parents!” and binding to this from events.html
“Hug your parents!” is rendered in the events section of the page.
Next Brian shows us that we can specify both a view and a ViewModel in our compose element.
Implementing Composite View Hierarchies with the Compose Element
Here we see the repeat.for binding expression.
We also see the activate method on a ViewModel
By the end of this lesson, we’ve rendered tow events to the screen:
- Aurelia Fundamentals
- Data Centric SPAs with Breeze.js
Override View Resolution Conventions in Aurelia
The default is to assume that the view and the ViewModel have the same name and are located in the same directory.
One common convention in MVVM is to put all of the views into their own views directory, and all of the ViewModels in their own viewmodels directory.
To achieve this we must override the Aurelia convention in our main.js configure function as follows:
- import ViewLocator
- redefine convertOriginToViewUrl with our own implementation
For further details see Dwayne Charrington’s Using Views From Different Locations in Aurelia.
Another way is to explicitly specify the view name including the .html extension. However in the case of routing we don’t have a single view, we have many. This is where the convention based mechanism becomes very important.