Client Side
The purpose of this guide is to summarize some of the key components of Ember used frequently within our apps, along with some of those concepts we extend in our addons. This guide is not designed to replace the Ember guides, but rather to point the reader towards the guides.
Router
Whenever you navigate to a URL within an Ember app, the first place that processes the action is the router. It is the router’s job to take the URL and convert it into a specific route within the app. Routes have the ability to be nested, in which case loading a route will also load all parent routes. There is even a top level route, application, which loads before all other routes.
In addition to app specific routes being added like described in the guide, FW apps add some additional routes from Group Control related to logins and profiles. This is accomplished using the following import in their router:
import authRouter from '@bennerinformatics/ember-fw-gc/router';
Then a call to authRouter(this)
at the end of the route map.
For more information on routers, see:
Routes
Routes represent a specific page within the app, as defined by the URL and router. The router’s job is to load any data required to view the page via the model, then select the correct controller and template to render the page.
Route model
Nearly every route overrides the model() method. This method is called when the route loads to return all relevant data for the route. It returns a promise, and the page will show a loading spinner until the promise resolves.
In many routes, this function will simply return a call to either this.store.findAll() or this.store.query():
model() {
return this.store.findAll('user');
}
It is worth noting here that if a parent route also has a findAll()
request for this type of model type, you can reduce network requests by calling this.store.peekAll() instead.
If multiple types of models need to be fetched, it can be accomplished using RSVP.hash() as follows:
model() {
return RSVP.hash({
services: this.store.query('service', {all: true}),
categories: this.store.findAll('category'),
users: this.store.findAll('user')
});
}
This will fetch an array of services, an array of categories, and an array of users as properties of model. In other words, to access the users array in the controller, you would call this.get('model.users')
.
AuthRoute and RestrictedRoute
Ember FW GC defines two custom route types for dealing with users and permissions: AuthRoute
and RestrictedRoute
.
AuthRoute
will redirect the user to the login
route if they are not logged in. Most app routes should extend AuthRoute
(with the notable exception of job application) unless the route is supposed to be usable by users who are not logged in. API documentation for AuthRoute
can be found here
RestrictedRoute
allows the user to define an array called roles containing required roles to view the route. If the user does not have the required roles, they are redirected to the index route in the same way as an invalid route. Any roles in the array will be processed using an AND condition, though you can add an array inside the array to make an OR condition. For example:
roles: [['admin', ‘supervisor’], ‘stats’]
This array will require the user to have the stats role, and to be either an admin or a supervisor. API documentation for RestrictedRoute
can be found here
Links
For more information on routes, check out the following Ember guides:
Models
Models are the primary data holder in Ember apps. In FW specifically, models contain any data that should persist beyond the current session. Models can be fetched from the server, most commonly in the route’s model function, then can be modified then saved back to the server.
Attributes
Models are defined by defining all their attributes as properties. Most attributes are defined using attr(), which supports not only Ember transforms such as strings, numbers, and booleans, but also moments using a custom transform in Ember FW.
Models also support relationships; a belongsTo relationship represents a key that is a single model, while hasMany link to an array of models. Typically, in both cases the key will be represented as just the model ID when saving and loading
All interactions used for finding existing models, saving models, and permanently deleting models is handled by the server side of FW, which will be covered in the server side segment of this guide.
Computed properties
In addition to attributes, models typically contain computed properties, which are dynamically calculated based on other model properties. These properties are cached, making them more efficient than a function for getting complex data. Ember ships a large number of macros that create commonly used computed properties, or they can be manually defined using the computed function.
Links
For more information on models, check out the following Ember guides:
Templates
Templates determine which data renders based on a given route. They are based on Handlebars.js, though this is modified a bit by Ember. Handlebars makes it easy to produce HTML content that dynamically updates based on JavaScript variables, causing it to look like a cross between HTML and custom syntax.
Templates do not directly support running JavaScript, but support various ways of interacting with JavaScript code. Templates are able to fetch variables, and many helpers are shipped with Ember allow simple actions such as conditionals, links, and inputs to be created.
One specific helper that should be mentioned is the action helper. This helper allows adding actions to HTML elements such as buttons, and allows passing in actions to components within the template.
Controllers
The controller’s role is to define the logic behind the page. Where templates determine what is shown, controllers aid the user in interacting with the page. One of the primary ways this is done is through computed properties, which are described in more detail in the model section above.
Another feature is actions. As mentioned in templates, the action helper can be utilized to add an action to an element such as a button. This action is defined within the controller in the actions object. Each action is essentially a JavaScript function that is called based on the user’s interaction with the page. Parameters can be passed into the action helper to use in JavaScript. In addition, actions keep their context, meaning within actions this
refers to the controller containing the action.
Components
In addition to variables and handlebars content within the page, templates also support adding components to reuse behavior. Components are called similar to an HTML tag with by passing parameters as key value pairs (except this tag is in Pascal Case). As an example, to call the custom-component
component, you would use the following:
<CustomComponent
@key1={{value1}}
@key2={{value2}}
/>
In addition, components can be invoked in block format, which essentially adds a parameter of Handlebars content
<CustomComponent>
{{some-handlebars-content}}
</CustomComponent>
Many components we use are provided through Ember addons, so you can typically find how they are called by reading the documentation.
It is also possible to define custom components in the app. Each component contains two files: a JavaScript file located in the components
folder, and a Handlebars file located in templates/components
. The Handlebars file is structured a lot like the template file from the route, while the JavaScript file is structured a lot like a controller.
For more information, see the following guides: