Ember.js Cheatsheet

/*
* ———————–
* Ember.js Cheatsheet
* ———————–
*
* Docs: https://guides.emberjs.com/
* Quick start: https://guides.emberjs.com/current/getting-started/quick-start/
*
* Table of contents
* ——————-
* 01 | Installation
* 02 | Ember CLI
* 03 | Directory layout
* 04 | Routes
* 05 | Templates
* 06 | Components
* 07 | Models
* 08 | Services
* 09 | Testing
* 10 | Addons
* 11 | Configuration
* 12 | Community
*
*/

/*
* 01 | Installation
* ————————————————————–
* Getting started with Ember is easy. Ember projects are created
* and managed through our command line build tool Ember CLI.
*/

“`
npm install -g ember-cli
ember new <application-name>
ember serve
“`

/*
* 02 | Ember CLI
* ————————————————————–
* Ember CLI is the Ember.js command line utility that provides a
* fast Broccoli-powered asset pipeline, a strong conventional
* project structure, and a powerful addon system for extension.
*
* Among the principal features of Ember CLI are:
– Project/Addon creation: create new projects quickly without having to worry about project structure;
– Build pipeline: asset compilation, finger-printing and more out of the box;
– Generators: use the built-in generators and get files that follow the latest practices, and matching tests;
– Ember Addons: extend both your application and Ember CLI itself with community solutions. Also an excellent
way to easily integrate 3rd party libraries into your Ember app.
*/

Basic commands

Command | Aliases | Description
————————————————–
“`
ember new | | Create new project with the provided name (ex. ember new <app-name>)
ember server | ember s | Starts development server (default port is 4200)
ember test | ember t | Run tests in CI mode
ember generate | ember g | Run generator
ember destroy | ember d | Remove code created by generator
ember help | ember h | Get more info on available cli command
ember install | ember i | Install given addon into project and save to package.json
ember | | List available cli commands
“`

Useful commands
“`
ember s –proxy <proxy-url>
ember s –port <port>
“`

/*
* 03 | Directory Layout
* ————————————————————–
*
* Layout of the root folder */

app/
` This is where folders and files for models, components, routes, templates and styles are stored. The majority of your coding on an Ember project happens in this folder. `
config/
` The config directory contains the environment.js where you can configure settings for your app. `
node_modules/
` This directory is from npm. npm is the package manager for Node.js. Ember is built with Node and uses a variety of Node.js modules for operation. The package.json file maintains the list of current npm dependencies for the app. Any Ember CLI addons you install will also show up here. `
public/
` This directory contains assets such as images and fonts. `
vendor/
` This directory is where front-end dependencies (such as JavaScript or CSS) that are not managed by NPM go. `
tests/testem.js
` Automated tests for our app go in the tests folder, and Ember CLI’s test runner testem is configured in testem.js. `
ember-cli-build.js
` This file describes how Ember CLI should build our app. `
package.json
` Packages listed in package.json are installed in the node_modules directory. `

/*
* Layout of the app directory */
adapters/
` Adapters with the convention adapter-name.js. `
components/
` Components with the convention component-name.js. Components must have a dash in their name. So blog-post is an acceptable name, but post is not. `
controllers/
` Controllers behave like a specialized type of Component that is rendered by the router when entering a Route. `
helpers/
` Helpers with the convention helper-name.js. Helpers must have a dash in their name. Remember that you must register your helpers by exporting makeBoundHelper or calling registerBoundHelper explicitly. `
models/
` Models with the convention model-name.js. `
routes/
` Routes with the convention route-name.js. Child routes are defined in sub-directories, parent/child.js. To provide a custom implementation for generated routes (equivalent to App.Route when using globals), use app/routes/basic.js. `
styles/
` Contains your stylesheets, whether SASS, LESS, Stylus, Compass, or plain CSS (though only one type is allowed, see Asset Compilation). These are all compiled into <app-name>.css. `
templates/
` Your HTMLBars templates. These are compiled to /dist/assets/<app-name>.js. The templates are named the same as their filename, minus the extension (i.e. templates/foo/bar.hbs -> foo/bar). `
serializers/
` Serializers for your models or adapter, where model-name.js or adapter-name.js. `
utils/
` Utility modules with the convention utility-name.js. `
router.js
` Your route configuration. The routes defined here correspond to routes in app/routes/. `

/*
* 03 | Routes
* ————————————————————–
*
* When your application starts, the router matches the current URL to the routes that you’ve defined.
* The routes, in turn, are responsible for displaying templates, loading data, and setting up application state.
*
* > ember g route <route-name>
*/

import Route from ‘@ember/routing/route’;

export default Route.extend({
model() {
// Typically, the model hook should return an Ember Data record,
// but it can also return any promise object (Ember Data records are
// promises), or a plain JavaScript object or array. Ember will wait
// until the data finishes loading (until the promise is resolved)
// before rendering the template.
}
});

/*
* 04 | Templates
* ————————————————————–
*
* Ember uses the Handlebars templating library to power your app’s user
* interface. Handlebars templates contain static HTML and dynamic
* content inside Handlebars expressions, which are invoked with
* double curly braces: {{}}.
*
* Templates are backed with a context. A context is an object from
* which Handlebars expressions read their properties. In Ember this
* is often a component. For templates rendered by a route (like application.hbs),
* the context is a controller.
*
* > ember g template <template-name>
*/

// Here’s an basic exmpale of a template
“`hbs
Hello, <strong>{{firstName}} {{lastName}}</strong>!
“`

/*
* 05 | Components
* ————————————————————–
*
* Ember components are used to encapsulate markup and style into
* reusable content. Components consist of two parts: a JavaScript
* component file that defines behavior, and its accompanying Handlebars
* template that defines the markup for the component’s UI.
*
* > ember g component <component-name>
*/

// app/components/<component-name>.js
import Component from ‘@ember/component’;

export default Component.extend({
});

// app/templates/components/<component-name>.hbs
“`hbs
{{yield}}
“`

/*
* Actions
*
* Provide a means to communicate events and changes
*/

// app/components/rental-listing.js
import Component from ‘@ember/component’;

export default Component.extend({

isWide: false,

actions: {
toggleImageSize() {
this.toggleProperty(‘isWide’);
}
}

});

// Actions can be attached to DOM elements inside templates using the {{action}} helper
“` app/templates/components/rental-listing.hbs
<article class=”listing”>
<a {{action ‘toggleImageSize’}} class=”image {{if isWide “wide”}}”>
<img src=”http://www.fillmurray.com/200/300″ alt=””>
</a>
</article>
“`

/*
* Component lifecycle
*
* As components are rendered, re-rendered and finally removed, Ember provides
* lifecycle hooks that allow you to run code at specific times in a component’s life.
*/

On Initial Render
1 init
2 didReceiveAttrs
3 willRender
4 didInsertElement // Good place to integrate with 3rd party libraries
5 didRender

On Re-Render
1 didUpdateAttrs
2 didReceiveAttrs
3 willUpdate
4 willRender
5 didUpdate
6 didRender

On Component Destroy
1 willDestroyElement
2 willClearRender
2 didDestroyElement

/*
* Block params
*
* Components can have properties passed in, but they can also return
* output to be used in a block expression.
*/

// Here an entire blog post model is being passed to the component as a
// single component property. In turn the component is returning values using yield.

“` app/templates/index.hbs
{{blog-post post=model}}
“`

“` app/templates/components/blog-post.hbs
{{yield post.title post.body post.author}}
“`

// The block expression can then use block params to bind names to any yielded
// values for use in the block. This allows for template customization when using
// a component, where the markup is provided by the consuming template, but any
// event handling behavior implemented in the component is retained such as click() handlers.

“` app/templates/index.hbs
{{#blog-post post=model as |title body author|}}
<h2>{{title}}</h2>
<p class=”author”>by {{author}}</p>
<p class=”post-body”>{{body}}</p>
{{/blog-post}}
“`
// The names are bound in the order that they are passed to yield in the component template.

/*
* 06 | Models
* ————————————————————–
*
* Models are objects that represent the underlying data that your application
* presents to the user. Different apps will have very different models,
* depending on what problems they’re trying to solve.
*
* Ember Data, included by default when you create a new application,
* is a library that integrates tightly with Ember to make it easy to
* retrieve models from your server as JSON, save updates back to the server,
* and create new models in the browser.
*
* > ember g model <model-name>
*/

import DS from ’ember-data’;
import { computed } from ‘@ember/object’;

const { attr, Model } = DS;

export default Model.extend({

firstName: attr(‘string’),
lastName: attr(‘string’),
birthday: attr(‘date’),

// Computed properties
// These are effectively fuctions declared as properties. The function’s result
// will recompute every time one of the provided ‘dependent keys’ changes.

fullName: computed(‘firstName’, ‘lastName’, function() {
let firstName = this.get(‘firstName’);
let lastName = this.get(‘lastName’);

return `${firstName} ${lastName}`;
})

});

/*
* 07 | Services
* ————————————————————–
*
* A Service is an Ember object that lives for the duration of the application, and can
* be made available in different parts of your application. Services are useful for
* features that require shared state or persistent connections.
*
* Example uses of services might include:
*
* – User/session authentication.
* – Geolocation.
* – WebSockets.
* – Server-sent events or notifications.
* – Server-backed API calls that may not fit Ember Data.
* – Third-party APIs.
* – Logging.
*
* > ember g service <service-name>
*/

/*
* Defining services
*
* Like any Ember object, a service is initialized and can have properties and
* methods of its own. Below, the shopping cart service manages an items array
* that represents the items currently in the shopping cart.
*/

// app/services/shopping-cart.js
import Service from ‘@ember/service’;

export default Service.extend({
items: null,

init() {
this._super(…arguments);
this.set(‘items’, []);
},

remove(item) {
this.get(‘items’).removeObject(item);
}
});

/*
* Accessing services
*
* To access a service, you can inject it in any object such as a component or another
* service using the `inject` function from the `@ember/service` module.
*/

// app/components/cart-contents.js
import Component from ‘@ember/component’;
import { inject } from ‘@ember/service’;

export default Component.extend({

shoppingCart: inject() // will load the service in file /app/services/shopping-cart.js

actions: {
remove(item) {
this.get(‘shoppingCart’).remove(item);
}
}
});

// Once injected into a component, a service can also be used in the template.
// Note cart being used below to get data from the cart.
“`hbs app/templates/components/cart-contents.hbs
<ul>
{{#each cart.items as |item|}}
<li>
{{item.name}}
<button {{action “remove” item}}>Remove</button>
</li>
{{/each}}
</ul>
“`

/*
* 08 | Testing
* ————————————————————–
*
* Testing is a core part of the Ember framework and its development cycle.
*
* > ember g acceptance-test <test-name>
* > ember g integration-test <test-name>
* > ember g unit-test <test-name>
* > ember t –server
* > ember s && visit localhost:4200/tests
*/

/*
* Acceptance & application tests
*
* In these kinds of tests, we interact with the application in the same ways that a
* user would, such as filling out form fields and clicking buttons. Application
* tests ensure that the interactions within a project are basically functional, the
* core features of a project have not regressed, and the project’s goals are being met.
*
* Some useful helpers from ‘@ember/test-helpers’:
* – `click(selector)`
* Clicks an element and triggers any actions triggered by the element’s click event and
* returns a promise that fulfills when all resulting async behavior is complete.
*
* – `fillIn(selector, value)`
* Fills in the selected input with the given value and returns a promise that
* fulfills when all resulting async behavior is complete. Works with <select> elements
* as well as <input> elements. Keep in mind that with <select> elements, value must be set
* to the value of the <option> tag, rather than its content (for example, true rather than “Yes”).
*
* – `triggerKeyEvent(selector, type, keyCode)`
* Simulates a key event type, e.g. keypress, keydown, keyup with the desired keyCode on element found by the selector.
*
* – `triggerEvent(selector, type, options)`
* Triggers the given event, e.g. blur, dblclick on the element identified by the provided selector.
*
* – `visit(url)`
* Visits the given route and returns a promise that fulfills when all resulting async behavior is complete.
*
* – `currentURL()`
* Returns the current URL.
*
* – `find(selector, context)`
* Finds an element within the app’s root element and within the context (optional). Scoping to the
* root element is especially useful to avoid conflicts with the test framework’s reporter, and this
* is done by default if the context is not specified.
*
* – `findAll(selector)`
* Find all elements matched by the given selector. Equivalent to calling querySelectorAll() on the
* test root element. Returns an array of matched elements.
*
*/

import { module, test } from ‘qunit’;
import { setupApplicationTest } from ’ember-qunit’;
import { visit, fillIn, click } from ‘@ember/test-helpers’;

module(‘Acceptance | posts’, function(hooks) {

// `setupApplicationTest` deals with application setup and teardown.
setupApplicationTest(hooks);

test(‘should add new post’, async function(assert) {
await visit(‘/posts/new’);
await fillIn(‘input.title’, ‘My new post’);
await click(‘button.submit’);

const title = this.element.querySelector(‘ul.posts li:first’).textContent;
assert.equal(title, ‘My new post’);
});
});

/*
* Integration & rendering tests
*
* Rendering Tests are, as the name suggests, rendering components and helpers
* by verifying the correct behaviour when the component or helper interacts
* with the system in the same way that it will within the context of the application,
* including being rendered from a template and receiving Ember’s lifecycle hooks.
*
* If we need to test the interactions between various parts of the application,
* such as behaviour between UI controls we can utilize Rendering Tests.
*/

// app/components/pretty-color.js
import Component from ‘@ember/component’;
import { computed } from ‘@ember/object’;

export default Component.extend({
attributeBindings: [‘style’],

style: computed(‘name’, function() {
const name = this.get(‘name’);
return `color: ${name}`;
})
});

// tests/integration/components/pretty-color-test.js
import { module, test } from ‘qunit’;
import { setupRenderingTest } from ’ember-qunit’;
import { render } from ‘@ember/test-helpers’;
import hbs from ‘htmlbars-inline-precompile’;

module(‘Integration | Component | pretty color’, function(hooks) {

// Make sure to call the setupRenderingTest function together with the hooks
// parameter first in your new module. This will do the necessary setup for
// testing your component for you, including setting up a way to access the
// rendered DOM of your component later on in the test, and cleaning up
// once your tests in this module are finished.
setupRenderingTest(hooks);

test(‘it renders’, async function(assert) {
assert.expect(2);

// set the outer context to red
this.set(‘colorValue’, ‘red’);

await render(hbs`{{pretty-color name=colorValue}}`);

assert.equal(this.element.querySelector(‘div’).getAttribute(‘style’), ‘color: red’, ‘starts as red’);

this.set(‘colorValue’, ‘blue’);

assert.equal(this.element.querySelector(‘div’).getAttribute(‘style’), ‘color: blue’, ‘updates to blue’); });
});

// Stubbing services
//
// In cases where components have dependencies on Ember services, it is
// possible to stub these dependencies for rendering tests. You stub Ember
// services by using the built-in register() function to register your
// stub service in place of the default.

import { module, test } from ‘qunit’;
import { setupRenderingTest } from ’ember-qunit’;
import { render } from ‘@ember/test-helpers’;
import hbs from ‘htmlbars-inline-precompile’;
import Service from ‘@ember/service’;

//Stub location service
const locationStub = Service.extend({
city: ‘New York’,
country: ‘USA’,
currentLocation: {
x: 1234,
y: 5678
},

getCurrentCity() {
return this.get(‘city’);
},
getCurrentCountry() {
return this.get(‘country’);
}
});

module(‘Integration | Component | location indicator’, function(hooks) {
setupRenderingTest(hooks);

hooks.beforeEach(function(assert) {
this.owner.register(‘service:location-service’, locationStub);
});

test(‘should reveal current location’, async function(assert) {
await render(hbs`{{location-indicator}}`);
assert.equal(this.element.textContent.trim(),
‘You currently are located in New York, USA’);
});
});

/*
* Unit & container tests
*
* Unit tests (as well as container tests) are generally used to test a
* small piece of code and ensure that it is doing what was intended.
*/

// app/services/some-thing.js
import Service from ‘@ember/service’;

export default Service.extend({
foo: ‘bar’,

testMethod() {
this.set(‘foo’, ‘baz’);
}
});

// tests/unit/services/some-thing-test.js
import { module, test } from ‘qunit’;
import { setupTest } from ’ember-qunit’;

module(‘Unit | Service | some thing’, function(hooks) {

// The `setupTest` helper provides us with some conveniences, such as the `this.owner` object,
// that helps us to create or lookup objects which are needed to setup our test.
// In this example, we use the `this.owner` object to lookup the service instance that
// becomes our test subject: `someThing`. Note that in a unit test you can customize any
// object under test by setting its properties accordingly. We can use the `set` method
// of the test object to achieve this.
setupTest(hooks);

test(‘should update foo on testMethod’, function(assert) {
const someThing = this.owner.lookup(‘service:some-thing’);

someThing.testMethod();

assert.equal(someThing.get(‘foo’), ‘baz’);
});
});

/*
* Running tests
*
* Run your tests with `ember test` on the command-line. You can re-run your tests on
* every file-change with `ember test –server`.
*
* Tests can also be executed when you are running a local development server
* (started by running `ember server`), at the `/tests` URI which renders the `tests/index.html` template.
*/

“`
ember test
ember test –server
ember test –filter=”dashboard”
run ember server then visit http://localhost:4200/tests
“`

/*
* 09 | Addons
* ————————————————————–
*
* Ember has a rich ecosystem of addons that can be easily added to projects.
* Addons provide a wide range of functionality to projects, often saving time and
* letting you focus on your project.
*
* To browse addons, visit the [EmberObserver](https://emberobserver.com/) website. It catalogs and categorizes Ember
* addons that have been published to NPM and assigns them a score based on a variety of criteria.
*
* > ember install <addont-name>
*/

/*
* 10 | Configuration
* ————————————————————–
*
* Ember CLI ships with support for managing your application’s environment. Ember CLI
* will setup a default environment config file at config/environment. Here, you can define
* an ENV object for each environment, which are currently limited to three: development,
* test, and production.
*/

// The ENV object has three important keys:
// – `EmberENV` can be used to define Ember feature flags (see the Feature Flags guide).
// – `APP` can be used to pass flags/options to your application instance.
// – `environment` contains the name of the current environment (development,production or test).

// You can access these environment variables in your application code by importing from `your-application-name/config/environment`.
import ENV from ‘your-application-name/config/environment’;

if (ENV.environment === ‘development’) {
// …
}

/*
* 11 | Community
* ————————————————————–
*
* Ember’s secret sauce
*
* more at [Ember Community Page](https://emberjs.com/community/)
*
*/

// Ember Discussion Forum
//
// url: http://discuss.emberjs.com/
//
// A great venue for discussing things like features, architecture, and best practices
// and a great place to ask questions (and get great answers from Ember Core Team members
// and other members of the community)

// Ember Community Slack
//
// url: https://embercommunity.slack.com/
//
// Use the Slackin app to receive an invitation.

// Ember Times
//
// url: https://the-emberjs-times.ongoodbits.com/
//
// Follow the progress of new features in the Ember ecosystem, requests for community
// input (RFCs), and calls for contributors

// Ember Weekly
//
// url: http://www.emberweekly.com/
//
// A curated list of Ember learning resources (podcasts, videos, blog posts, books, and more)

// Official Ember Blog
//
// url: https://emberjs.com/blog/
//
// Big announcements like new Ember.js version release notes or State of the Union information

// Ember Github
//
// url: https://github.com/emberjs/

// Ember Meetups
//
// url: https://emberjs.com/community/meetups/