This Ember addon implements the functionality described in the Ember Engines RFC. Engines allow multiple logical applications to be composed together into a single application from the user's perspective.
This addon must be installed in any ember-cli projects that function as either consumers or providers of engines. The following functionality is supported:
- Routable engines which can be mounted at specific routes in a routing map, and which can contain routes of their own.
- Route-less engines, which can be rendered in a template using the
{{mount}}
keyword. - Sharing of dependencies from parents (applications or other engines) to contained engines. Shared dependencies are currently limited to services.
The following functionality will soon be supported:
- Lazy loading of engines.
- Route serializer modules that isolate serialization logic from the rest of the route definition.
Support for the following concepts is under consideration:
- Namespaced access to engine resources from applications.
- Sharing of dependencies other than services.
- Passing configuration attributes from an engine's parent.
From your Ember CLI project's root directory, run the following:
ember install ember-engines
You will also need to install Ember Canary:
rm -rf bower_components
bower install --save ember#canary
bower install
Bower may prompt you to select various "resolutions". Make sure to choose
ember#canary
if prompted, and prefix the choice with ! to persist it to
bower.json
.
Engines can be created as separate addon projects or in-repo addons.
Separate addon projects can be created with the addon
command:
ember addon <engine-name>
Note: As described in the RFC, ember-cli will hopefully support an engine
command to get started more easily with engine projects.
In order to create an engine within an existing application's project, run the
in-repo-addon
generator:
ember g in-repo-addon <engine-name>
Note: As described in the RFC, ember-cli will hopefully support an
in-repo-engine
generator to get started more easily with in-repo engines.
Don't forget to install ember-engines
and Ember Canary in your project, as
described above.
Within your engine's root directory, modify index.js
so that your addon
is configured as an engine using the EngineAddon
extension:
/*jshint node:true*/
var EngineAddon = require('ember-engines/lib/engine-addon');
module.exports = EngineAddon.extend({
name: 'ember-blog'
});
Within your engine's addon
directory, add a new engine.js
file:
import Engine from 'ember-engines/engine';
import Resolver from 'ember-engines/resolver';
export default Engine.extend({
modulePrefix: 'ember-blog',
Resolver
});
It's important to define a modulePrefix
that will be used to resolve your
engine and its constituent modules.
Routable engines should declare their route map in a routes.js
file.
For example:
import buildRoutes from 'ember-engines/routes';
export default buildRoutes(function() {
this.route('new');
this.route('post', { path: 'post/:id' }, function() {
this.route('comments', function() {
this.route('comment', { path: ':id' });
});
});
});
Routable engines interact with the parent application's router as if they are an extension of the parent application. A routable engine's application route will be mounted wherever specified by the parent's route map (its "mountpoint").
Route-less engines should define an engine.js
as described above. Neither
router.js
nor routes.js
should be defined. Route-less engines will be rendered as their application template
(templates/application.hbs
).
Your engine should declare any dependencies that it expects from its parent. Dependencies must be declared in the engine definition.
For example, the following engine requires a store
service from its parent:
import Engine from 'ember-engines/engine';
import Resolver from 'ember-engines/resolver';
export default Engine.extend({
modulePrefix: 'ember-blog',
Resolver,
dependencies: {
services: [
'store'
]
}
});
Currently, only services can be shared across the parent/engine boundary.
Engines that are published as separate addons should be installed like any other addon:
ember install <engine-name>
As mentioned above, engines can also exist as in-repo addons, in which case
you just need to ensure that this addon (ember-engines
) has been installed
in your main project.
An Application or Engine that contains other engines must use the Resolver
provided in the ember-engines/resolver
module. For example:
import Ember from 'ember';
import Resolver from 'ember-engines/resolver';
import loadInitializers from 'ember/load-initializers';
import config from './config/environment';
let App;
Ember.MODEL_FACTORY_INJECTIONS = true;
App = Ember.Application.extend({
modulePrefix: config.modulePrefix,
podModulePrefix: config.podModulePrefix,
Resolver
});
loadInitializers(App, config.modulePrefix);
export default App;
Route-less engines can be rendered in a template using the {{mount}}
keyword.
Route-less engines can be mounted in templates using the {{mount}}
keyword.
For example, the following template renders the ember-chat
engine:
Currently, the engine name is the only argument that can be passed to
{{mount}}
.
Routable engines should be mounted in your router's route map using the
mount()
method. For example:
import Ember from 'ember';
import config from './config/environment';
const Router = Ember.Router.extend({
location: config.locationType
});
Router.map(function() {
this.route('blogs', function() {
// Mount the main blog at /blogs/ember-blog
this.mount('ember-blog');
// Mount the hr blog at /blogs/hr-blog
this.mount('ember-blog', { as: 'hr-blog' });
// Mount the admin blog at /blogs/special-admin-blog-here
this.mount('ember-blog', { as: 'admin-blog', path: '/special-admin-blog-here' });
});
});
export default Router;
The above example mounts three different instances of the ember-blog
engine
within the blogs
route.
The engine mounted with this.mount('ember-blog')
will have a root path of
/blogs/ember-blog
and its root route can be referenced as ember-blog
.
The engine mounted with this.mount('ember-blog', { as: 'hr-blog' })
will have
a root path of /blogs/hr-blog
and its root route can be referenced as
hr-blog
.
The engine mounted with this.mount('ember-blog', { as: 'admin-blog', path: '/special-admin-blog-here' })
will have a root path of
/blogs/special-admin-blog-here
and its root route can be referenced as
admin-blog
.
Note: The above example is not very practical currently without a method to
configure individual instances of ember-blog
.
Applications or engines that contain an engine must provide mappings that fulfill the dependencies required by that engine.
For example, the following engine expects its parent to provide store
and
session
services:
import Engine from 'ember-engines/engine';
import Resolver from 'ember-engines/resolver';
export default Engine.extend({
modulePrefix: 'ember-blog',
Resolver,
dependencies: {
services: [
'store',
'session'
]
}
});
An application that contains this engine must explicitly fulfill these dependencies. For example:
import Ember from 'ember';
import Resolver from 'ember-engines/resolver';
import loadInitializers from 'ember/load-initializers';
import config from './config/environment';
let App;
Ember.MODEL_FACTORY_INJECTIONS = true;
App = Ember.Application.extend({
modulePrefix: config.modulePrefix,
podModulePrefix: config.podModulePrefix,
Resolver,
engines: {
emberBlog: {
dependencies: {
services: [
'store',
{'session': 'user-session'}
]
}
}
}
});
loadInitializers(App, config.modulePrefix);
export default App;
Note that the app's store
service is directly mapped to the engine's store
service, while the app's user-session
service is mapped to the engine's
session
service.
Also note that multiple engines can be configured per parent application/engine,
and that each engine name should be camelCased (emberBlog
instead of
ember-blog
).
git clone
this repositorynpm install
bower install
ember server
- Visit your app at http://localhost:4200.
npm test
(Runsember try:testall
to test your addon against multiple Ember versions)ember test
ember test --server
ember build
For more information on using ember-cli, visit http://www.ember-cli.com/.
Copyright 2015 Dan Gebhardt and Robert Jackson. MIT License (see LICENSE.md for details).