Basic Templating¶
In the next step we will look at the basics of creating Ember templates. Ember uses a templating language called Handlebars, which augments standard HTML with control structures, dynamic values, and user input.
We will take the login page and the modules page from the first tutorial and now implement that in Ember. While we could test some of the templating functionality directly in the application.hbs
, allowing only a single, very complex template. Instead we will create two “routes” with separate templates. A “route” is basically part of a URL that allows selecting different functionality within the application. We will look at them in more detail in the next tutorial. For the moment it is sufficient to know that each route is associated with a separate template.
We will start with the login page and create the login route first:
$ yarn ember generate route login
After running the generate command, you will see that inside the app
folder there are now two additional files app/routes/login.js
and app/templates/login.hbs
. We will look at the login.js
next time. Now edit the login.hbs
and add the following code:
<div class="row justify-content-lg-center">
<form class="col-lg-6">
<h1>Login</h1>
<div class="form-group">
<label>E-Mail
<Input type="email" @value={{this.email}} />
</label>
</div>
<div class="form-group">
<label>Password
<Input type="password" @value={{this.password}} />
</label>
</div>
<div class="btn-container">
<button class="button">Login</button>
</div>
</form>
</div>
The code demonstrates a few things. First, Bootstrap comes with a powerful grid system that we can use for layouting. The col-lg-6
CSS class says that the column should be 6/12 wide (most CSS grids are 12 columns wide). By adding the justify-content-lg-center
CSS class, we specify that the content should be centered on large displays.
The other important aspect is the use of <Input>
. This is a so-called component that will generate the actual <input>
element. It basically takes the same parameters as the standard <input>
element. The important difference is the use of the value=
attribute. Here we specify the value without the double quotes "
. This tells Ember that the value is to be treated as a variable name and the value should be taken from that variable. All that remains is to actually define that variable.
Nested Templates¶
One of the useful features of Ember is that it comes with a template nesting structure built in, which is, as we will see in the future, strongly linked to the route structure. Before we get into that level of detail, at the simplest level we can make use of the fact that all templates are embedded into the application template. Thus, if we want any kind of HTML structure that is shared across all templates, the app/templates/application.hbs
is the place to put it.
We will now use that to add a header to all of our pages, by updating the app/templates/application.hbs
to look like this:
{{page-title "Athena"}}
<div class="container">
<header class="row">
<nav class="navbar navbar-dark bg-dark" aria-label="Main">
<ul class="navbar-nav horizontal" role="menu" aria-label="Main">
<li class="navbar-brand">Athena - Study Portal</li>
<li><a href="" role="menuitem" tabindex="0" class="active nav-link">My Modules</a></li>
<li><a href="" role="menuitem" tabindex="-1" class="nav-link">My Exams</a></li>
</ul>
<ul class="navbar-nav horizontal" role="menu" aria-label="User">
<li><a role="menuitem" tabindex="0" class="nav-link">Logout</a></li>
</ul>
</nav>
</header>
</div>
{{outlet}}
Most of the code is taken directly from the header we used in the first week. The only difference is that it has been reformatted to make use of the Bootstrap grid system and navbar
class. If you test this, you will see that the basic structure is now also shown when we look at the login URL. However, the menu is not styled correctly.
Styling with SCSS¶
To add styles to our application we edit the app/styles/app.scss
file, which will look something like this:
@import "node_modules/bootstrap/scss/bootstrap";
The first line imports the _bootstrap.scss
file, which contains the configuration settings, mixins, variables and classes for the Bootstrap framework.
In addition to the standard classes we can use custom styling. However, our form button does not use the Bootstrap CSS classes, so we need to use the button mixins defined by Bootstrap to create the button, but with our CSS selectors driven by the ARIA tags. Add the following code to the app/styles/app.scss
:
/*
* Styles
*/
.container {
max-width: 100%;
}
body {
background-color: $gray-200;
}
/*
* Form Styles
*/
.row form {
margin: auto;
padding: 15px;
margin-top: 50px;
border: 1px solid #333;
background-color: $navbar-light-color;
border-radius: 7px;
color: white;
overflow: hidden;
label {
padding: 5px;
width: 100%;
max-width: 100%;
input {
max-width: 100%;
}
}
}
.btn-container {
float: right;
}
form button[role='button'] {
@include button-variant($dark, $dark, $white);
@include button-size(5px, 5px, 20px, $btn-border-radius-lg);
margin-top: 15px;
}
This code snippet demonstrates a range of the functionality provided by sass. At the top it starts normally, by defining that a button is selected based on the form button[role="button"]
selector. Next, we use the mixins provided by Bootstrap to generate the actual CSS properties. The button-variant
defines the color properties of a button, while with the button-size
mixin we define the button’s size.
Another thing the code snippet demonstrates is the use of nesting to define more complex CSS selectors. By nesting label
inside .row form
, the scss compiler knows to simply append the rules only to label
elements that are children of a form
. Inside this rule, we create simple . In the generated CSS this will look like this:
.row form label {
padding: 5px;
width: 100%;
max-width: 100%;
}
If you add empty rules, these rules are not generated, as scss is smart enough not to generate CSS rules that have no properties.
The last bit of functionality we see is the use of variables, such as $gray-200
. All variables in scss are global and shared across all scss files that are beiong compiled. This one is defined in the node_modules/bootstrap/scss/bootstrap.scss
file and if you change it there, it will be updated wherever it is used.
Before we make the login page interactive, we want to have a look on another important concept of ember: Components.