Now it is time to talk about our application we will build together. Apart from our web application written, of course, in Aurelia, we also need a backend service to persist our data. For the backend service, we will be using Node.js with the Express framework to build a robust API and MongoDB as our non-relational data storage. The following diagram explains our World Cup project architecture:
This a very simple architecture; the Aurelia app talks to the Node API directly, and the Node API talks to the database, which is a MongoDB database, using a very popular open source library called Mongoose. This is getting better; keep reading!
The app we will develop is the Football World Cup app. We will use an awesome UI framework called Materialize, which, by default, will help us create a responsive web application, so our users can open this app in their mobile and desktop browser with an adaptable user interface.
Although this is a simple application, we will cover the most important concepts of Aurelia that you will find in a real production application. We will improve this app along with the book. Listed are the features we will develop for this app:
- Matches explorer
- Teams explorer
- News
- Admin portal
- Social authentication
So, let's start exploring the features that this application delivers to our users.
This feature is related to the matches in the entire competition. The users will be able to perform the following actions:
- List the
Matches
activity - Create a new
Match
, which requires admin
This feature is related to the matches in the entire competition. The users will be able to perform the following actions:
- List the
Teams
- Create a new
Team
, which requires admin
Let's start creating our application. If you remember our last section about the Aurelia CLI, we need to use it again to create a new application, so open your favorite Terminal tool and execute the following command:
au new worldcup-app
Enter the following input in the Terminal:
- Select
3
to define our custom options for this project First option:
Which module loader / bundler would you like to use? RequireJS (Default)
Second option:
What transpiler would you like to use? : Babel (Default)
Third option:
How would you like to setup your template? : Default
(No markup processing
.)Fourth option:
What CSS processor would you like to use?
In this case, we will chooseSass
(3
)Fifth option:
Would you like to configure unit testing?
Of course, we will markYes (Default)
- Sixth option:
What is your default code editor?
We use WebStorm, but you can choose the most familiar to you.
Now, you will see the main structure of your application on the console:
Project Configuration Name: worldcup-app Platform: Web Bundler: Aurelia-CLI Loader: RequireJS Transpiler: Babel Markup Processor: None CSS Processor: Sass Unit Test Runner: Karma Editor: WebStorm
- Finally, select
1
to create the project and then install the project dependencies
This is a custom setup. Our project will be composed with the following features:
- RequireJS: Well known file and module loader, with a good browser support. Another option could be SystemJS and Webpack.
- Babel: Babel is one of the most used transpilation tools nowadays. A
transpiler
is a tool that transforms code written in JavaScript ES6 syntax or later into ES5 code. Why? Because most of the browsers does not have well supported yet the last JavaScript version. - Markup processing: It loads our modules and create the final files which will be interpreted by the browser. We won't use a custom markup processing in this stage.
- SASS: A nice preprocessor CSS library, we will review it at more detail in the next chapter.
- Karma: A JavaScript test library. We will talk about it in more detail in the Chapter 3, Testing and Debugging.
- WebStorm: A very nice IDE for JavaScript developers. It is not free but if you have an educational email account from your university or institute, you can get a student license for one year.
Once everything is done, open the worldcup-app
folder with your favorite editor.
The Aurelia CLI will generate the source code with its base structure, which has everything configured and ready to start writing our application's source code.
The following screenshot shows the root application folder:
Let's start talking about the aurelia_project
folder, which contains the main aurelia.json
configuration file with all the settings about the dependencies, blunder, build target, loaders, testing run-time tool such as Karma, testing framework, and more. You will modify this file frequently to specify new dependencies our application needs to use.
The next element in the aurelia_folder
is the environments
folder, which contains three files: dev.json
, stage.json
, and prod.json
. These files contain values depending on the environment you are running on. Do you remember the --env
flag in the run option? The CLI will use one of these files to configure our app's environmental values.
The remaining two folders are generators
and tasks
. They are used to generate Aurelia custom reusable components and to declare gulp tasks, respectively.
The scripts
folder contains the bundles generated after we execute the au build
command.
As you might guess, the src
folder contains our application source code, followed by the test
folder, which contains our source code to test our project.
Like many JavaScript frameworks such as Angular and React, Aurelia needs a place in the index.html
page to mount the application. This place is known as the entry point. Open the index.html
file, and you should see something similar to the following code:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Aurelia</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body aurelia-app="main">
<script src="scripts/vendor-bundle.js" data-main="aurelia-bootstrapper"></script>
</body>
</html>
Aurelia requires an HTML element to load our application. By default, the application is loaded in the body
element; we know this because this element uses the aurelia-app
attribute, which is used to specify the main JavaScript script file that contains all the configuration for our application, and as you note, by default, Aurelia is configured to use the main
file. The following is the content of themain.js
file:
import environment from './environment'; export function configure(aurelia) { aurelia.use .standardConfiguration() .feature('resources'); if (environment.debug) { aurelia.use.developmentLogging(); } if (environment.testing) { aurelia.use.plugin('aurelia-testing'); } aurelia.start().then(() => aurelia.setRoot()); }
Let's analyze this file; the first line imports the environment variables from the environment.js
file located in the root folder. When you specify the --flag
{env}
option, the CLI looks for the {env}.json
file in the aurelia_project
folder and copies its content into the environment.js
file.
This file also exports a single configure
function, which receives as a parameter the aurelia
object that you use to override default configurations and add any code you wish before the app is launched. For example, you can tell Aurelia that you want to declare components as global (features), configure internationalization to manage different languages, and so on.
Once the aurelia
object is configured, the last line of code will render our application into the root HTML element, which has the aurelia-app
attribute in the index.html
page. By default, it renders the app.js
component into the root element. Of course, we can override the default values by passing the element you wish to render as the first parameter and the HTML element where you wish to render the app as a second parameter:
aurelia.start().then(() => aurelia.setRoot('my-component', document.getElementById('my-div')));
We will be modifying this file along the way; the most important thing to remember is that this file is processed before the app is rendered and apart from the Aurelia.json
file, this is the second most important file. The following diagram explains the bootstrapping process:
Now you know how the bootstrapping process works. Let's understand how you can create reusable components.
In the last section, we saw that Aurelia requires a component to mount as the root of our entire application and by default, it was the app component. Now let's explore this component.
A component is composed of two files, the first written in JavaScript that contains the component's view model and the second one is the markup template written in HTML. They must have the same filename to help the view model resolve its view template. The component's view model is a JavaScript file, which exports a class that contains the component's attributes and functions. For example, this is the content of the app.js
component:
export class App { constructor() { this.message = 'Hello World!'; } }
The App
class declares a constructor that initializes the message
property. Properties can be declared into the constructor
or can be defined outside of it. Consider this example:
export class App { message = 'Hello World!'; }
Note
Use the outside property declaration style when you are declaring simple classes such as Plain Old CLR Objects (POCO), which now implements more logic than simply get and set its property values.
To use the properties defined in our app.js
view model, we need an HTML template with the same filename. Open the app.html
file to see its content:
<template> <h1>${message}</h1> </template>
The first thing to note is that the message
property declared in the view model is present, but in order to bind the value, we have to use the ${}
string interpolation operator. Finally, when Aurelia renders the component in the web page, the ${message}
declaration will be replaced by 'Hello World!'
.
We can extend our components by adding functions that can be called from the template. For example, let's declare the changeMessage()
function:
export class App { constructor() { this.message = 'Hello World!'; } changeMessage() { this.message = 'World-Cup App'; } }
From the preceding code, you can see how declaring a function is a simple; we use the same syntax of the contructor
declaration. If you want to use the properties declared in the App
class, you have to use the this
reserved word to access any property or function.
Now it is time to invoke our changeMessage
function. First, we will create a button in our template in the app.html
file and declare a trigger to the click
event of the button. Open the app.html
file and apply the following changes:
<template>
<h1>${message}</h1>
<button click.trigger="changeMessage()">Change</button>
</template>
The first thing to note here is that we don't use the default HTML onclick
event; instead, we use the click
event with no on
at the start of the event name. This convention is used only for the Aurelia templating engine. So, we say that we want to invoke the changeMessage()
functions by binding this function to the click
event using the trigger
binding mechanism.
Launch your app by executing the au run
command in your Terminal, and test this out. When you click on the Change
button, you should see how the message is changed from 'Hello World!'
to 'World-Cup' App
. The h1
HTML element is changed because we have previously declared and bound the ${message}
property into its content.
Binding is a big concept we will cover in next chapters in a more detailed way. So, keep reading my friend, it just starting.