Embed EmberJS app in Drupal 8 block

Here’s a quick overview of the requirements I had.

  • Use EmberJS to create a Javascript Component Embedded in a single Drupal 8 block
  • Only load the ember javascript code on the pages where the block is displayed
  • Don’t use a separate stylesheet for the component’s styling
  • Don’t change the url on route changes
  • Use jQuery included in Drupal Core

Versions

  • Drupal 8.0.1
  • jQuery 2.1.4 (current version in Drupal core)
  • Ember 2.3.0
  • Ember Data 2.3.3
  • Ember CLI 2.2.0-beta.6 (used for developing, obviously not used in prod)

Configuring Ember

1. Change library versions

When creating a new ember app via Ember CLI, the jQuery and Ember versions were incorrect. Let’s start of by editing our bower.json this is how my bower.json looked after editing

{
 “name”: “drupal-component”,
 “dependencies”: {
  “ember”: “2.3.0”,
  “ember-cli-shims”: “0.1.0”,
  “ember-cli-test-loader”: “0.2.2”,
  “ember-load-initializers”: “0.1.7”,
  “ember-qunit-notifications”: “0.1.0”,
  “jquery”: “2.1.4”,
  “loader.js”: “^3.5.0”,
  “qunit”: “~1.20.0”
 },
 “resolutions”: {
  “ember”: “2.2.0”
 }
}

make sure to rerun bower install

2. Configure ember-cli-build.js

We’ll have to make sure that our jQuery is excluded when we are building our application for production, but is still included for easy development.

And, include the config in the builds, because Ember defaults by adding config to a meta tag in the head of your html, which we can’t do.

This is how my ember-cli-build.js looks like

var EmberApp = require(‘ember-cli/lib/broccoli/ember-app’);

module.exports = function(defaults) {
 var app = new EmberApp(defaults, {

  // don't store config in meta
  storeConfigInMeta: false,

  // exlude jquery from production builds
  vendorFiles: {
   ‘jquery.js’: { 
    development: ‘bower_components/jquery/dist/jquery.js’,
    production: false
   }
  }
 });

 return app.toTree();
};
3. Configure environment.js

Next we’ll configure our environment, there are a few things we need to do.

  • Stop router from changing URLs
  • Define a root element in production builds

Stopping the router from changing URLs is possible by setting the locationType to ‘none’

And a root element can be defined in ENV.APP.rootElement, there you define a jquery selector string.

This is what my environment.js looks like after the changes.

module.exports = function(environment) {
 var ENV = {
  modulePrefix: ‘drupal-component’,
  environment: environment,
  baseURL: ‘/’,
  locationType: ‘none’, 
  EmberENV: {
   FEATURES: {
   }
  }, 

  APP: {
   // Here you can pass flags/options to your application instance
   // when it is created
  }
 };

 if (environment === ‘development’) {
 }

 if (environment === ‘test’) {
  // Testem prefers this…
  ENV.baseURL = ‘/’;
  ENV.locationType = ‘none’;

  // keep test console output quieter
  ENV.APP.LOG_ACTIVE_GENERATION = false;
  ENV.APP.LOG_VIEW_LOOKUPS = false;

  ENV.APP.rootElement = ‘#ember-testing’;
 }

 if (environment === ‘production’) {
  ENV.APP.rootElement = ‘#block-embercomponent’
 }

 return ENV;
};
4. Building a production version of the App

Now we want to make a production version of our app.

$ ember build — environment=production
version: 2.2.0-beta.6

Built project successfully. Stored in “dist/”.

Inside the dist directory you’ll find these contents

  • assets/
  • index.html
  • crossdomain.xml
  • robots.txt

the contents of assets is where the important files are, inside that folder
you’ll find

  • drupal-component-{random hash}.js
  • vendor-{random hash}.js

All other content can be important to you, but for the sake of this article, it isn’t for us.

we’ll copy both the drupal-coponent.js and vendor.js to our theme directory inside our drupal installation,

For the sake of easily identifiying everything in my drupal site, we’ll do some renames:

  • vendor.js to ember.js
  • drupal-component.js to ember-component.js

Drupal Configuration

1. Configure Libraries

Because we want to only load the ember application on the pages where the block is displayed, we’ll have to define our files in a library.

In our theme directory, let’s create or edit the mydrupaltheme.libraries.yml file, and add this content

ember:
  version: 2.3.x
  js:
    js/ember.js: { minified: true }
  dependencies:
    — core/jquery

ember-component:
  version: 1.x
  js:
    js/ember-component.js: { minified: true }
  dependencies:
    — mydrupaltheme/ember

Make sure to include the dependencies, remember we excluded jquery from the ember app because we are using the one from drupal core.

2. Create a block.

First we’ll create a Drupal block, and call it Ember Component, this is important, because drupal will generate an id for the block, and it will be #block-embercomponent (based on the name of the block), which we defined in our Ember application,

And we’ll need to create a template file based on this name as well.

3. Create a template file.

inside our template directory we’ll create a new template file for the block we just created.

The name is important, ours is: block--embercomponent.html.twig

The content of the template file is a copy of the regular block.html.twig,
with the libraries attached.

This way our ember app is only loaded on the pages where the block is displayed.

{{ attach_library(‘mydrupaltheme/ember-component’) }}

<div{{ attributes }}>
 {{ title_prefix }}
 {% if label %}
  <h2{{ title_attributes }}>{{ label }}</h2>
 {% endif %}
 {{ title_suffix }}
 {% block content %}
  {{ content }}
 {% endblock %}
</div>