This article provides a basic introduction to the concepts behind the Model View Controller (MVC) software architecture pattern, and then explains how this is implemented by Ember.js - a popular framework for app development.
The theory behind Model View Controller
Model View Controller (MVC) is a software architecture pattern, commonly used to implement user interfaces: it is therefore a popular choice for architecting web apps. In general, it separates out the application logic into three separate parts, promoting modularity and ease of collaboration and reuse. It also makes applications more flexible and welcoming to iterations.
To make this a little more clear, let's imagine a simple shopping list app. All we want is a list of the name, quantity and price of each item we need to buy this week. Below we'll describe how we could implement some of this functionality using MVC.
The Model
The model defines what data the app should contain. If the state of this data changes, then the model will usually notify the view (so the display can change as needed) and sometimes the controller (if different logic is needed to control the updated view).
Going back to our shopping list app, the model would specify what data the list items should contain — item, price, etc. — and what list items are already present.
The View
The view defines how the app's data should be displayed.
In our shopping list app, the view would define how the list is presented to the user, and receive the data to display from the model.
The Controller
The controller contains logic that updates the model and/or view in response to input from the users of the app.
So for example, our shopping list could have input forms and buttons that allow us to add or delete items. These actions require the model to be updated, so the input is sent to the controller, which then manipulates the model as appropriate, which then sends updated data to the view.
You might however also want to just update the view to display the data in a different format, e.g., change the item order to alphabetical, or lowest to highest price. In this case the controller could handle this directly without needing to update the model.
Evolution of MVC on the web
As a web developer, this pattern will probably be quite familiar even if you've never consciously used it before. Your data model is probably contained in some kind of database (be it a traditional server-side database like MySQL, or a client-side solution such as IndexedDB [en-US].) Your app's controlling code is probably written in HTML/JavaScript, and your user interface is probably written using HTML/CSS/whatever else you like. This sounds very much like MVC, but MVC makes these components follow a more rigid pattern.
In the early days of the Web, MVC architecture was mostly implemented on the server-side, with the client requesting updates via forms or links, and receiving updated views back to display in the browser. However, these days, more of the logic is pushed to the client with the advent of client-side data stores, and XMLHttpRequest allowing partial page updates as required.
Popular web frameworks such as AngularJS, Ember.js and Backbone all implement an MVC architecture, albeit in slightly different ways.
How Ember.js puts MVC into practice
Ember — the framework we'll be using in this series — implements MVC according to a strict set of concepts and rules; this document provides a brief summary of what you need to know for now. You can read a lot more about it in Ember's documentation — a good place to start is Core Concepts.
You should also familiarize yourself with Ember's naming conventions, which are enforced strictly to allow associated objects to talk to each other easily without a lot of extra cruft to link them together.
Finally, since we are using Ember CLI to automate a lot of this process, you should keep the Ember CLI documentation at hand.
The Ember View layer
In Ember, views are slightly different to how they are in general MVC. In MVC terms, we usually think of the view as being "the entire UI layer", whereas in Ember a view is another specific component of the UI, along with routes and templates.
A typical simple Ember.js UI uses Route objects and template files to handle Views. Routes specify the URL that a certain template will be accessible at by your users, and can also specify what model that template should display data from. Each route is represented by a JavaScript file. Templates specify what the UI should look like, and are represented by Handlebars template files (.hbs
.) These files are HTML with special dynamic expressions inserted in them, contained in double curly braces ({{ ... }}
). The double curly braces, in their simplest form, can grab data/properties contained in the Model (or Controller) and output them in the UI, for example:
<div class="entry"> <h1>{{title}}</h1> <div class="body"> {{body}} </div> </div>
These properties will automatically update in the template when the Model/Controller changes.
Ember CLI can generate a route and associated template using the following command:
ember generate route my-route-name
This will generate:
- A JavaScript file in
your-app-root/app/routes
to control the route. - A Handlebars template in
your-app-root/app/templates
to define the content that will appear at the named URL. - A unit test file in
your-app-root/tests/unit/routes
where you can define a test for the functionality at your route.
The route file will contain a basic skeleton for you to start writing any necessary route logic:
import Ember from 'ember'; export default Ember.Route.extend({ // your own features, as required });
Like pretty much any other MVC framework, Ember provides a foundation of core application structures that you can customize to suit your application's needs by extending them. This code imports the main Ember
object, then allows you to build functionality on top of Ember's built-in Route
object by calling its extend
method. This is a very common pattern that you'll begin to recognize as you build your application.
The route and template files will both be called the same thing, for example shopping-list.js
and shopping-list.hbs
, so they associate together by default; the view is then available to the user at your-server.com/shopping-list/
(or whatever you called it.)
Ember Controllers
Ember.js represents Controllers using controller objects, which are contained in JavaScript files.
Ember CLI can generate a controller using the following syntax:
ember generate controller name-of-my-controller
This will create two new files:
- A JavaScript file in
your-app-root/app/controllers
to control that particular model/view. - A unit test file in
your-app-root/tests/unit/controllers
where you can define a test for the functionality in your controller.
So for our shopping list example, if we already have a route and template called shopping-list
, it would make sense to generate a controller called shopping-list
. Naming it like this will automatically associate this controller with the correct route and/or model.
The generated controller file contains this:
import Ember from 'ember'; export default Ember.Controller.extend({ // your own features, as required });
In a similar fashion to our route, our controller is importing the main Ember
object and extending the default Controller
object to allow us to create our own custom controller that does whatever we want.
Ember Models
Ember.js represents Models using model objects, which as before are contained in JavaScript files.
Ember CLI can generate a model using the following syntax:
ember generate model name-of-my-model
This will create two new files:
- A JavaScript file in
your-app-root/app/models
to define the data to be associated with this particular view. - A unit test file in
your-app-root/tests/unit/models
where you can define a test for the functionality in your model.
So for our shopping list example, it would make sense to generate a model called shopping-list
. Naming it like this will automatically associate this model with the correct route and/or controller.
The generated model file contains this:
import DS from 'ember-data'; export default DS.Model.extend({ // your own features, as required });
In a similar fashion to our route and controller, our model is importing the main Ember
object and extending the default Model
object to allow us to create our own custom model that does whatever we want.
Next
Now that we have gone through some brief theory behind Ember and MVC, we'll start putting this into practice and build a real Ember-based MVC application.