-
Notifications
You must be signed in to change notification settings - Fork 11
Recipe: app shell
The main purpose of this guide is to provide an easy way to implement this pattern in your Ember application by using this addon.
We'll create an in-repo addon
that will do all the necessary magic to enable this pattern. Why an addon? Because the code is separated from the rest of the application and can be asily toggled on/off.
Let's start creating the an addon inside the root of our Ember application. We will use the Ember cli tool by using the next command:
$ ember generate in-repo-addon app-shell
The previous command will generate something like this structure:
lib/app-shell
├── addon/
├── app/
├── index.js
└── package.json
We must have fully control about how the Service Worker going to be created, so we have to disable the automatic creation using the addon configuration, also we have to cache the index.html
and the rest of the assets:
// config/environment.js
{
workbox: {
globPatterns: includedAppCache.concat([
'index.html',
'assets/vendor.js',
'assets/vendor.css',
'assets/app.js',
'assets/app.css'
])
},
'ember-cli-workbox': {
autoRegister: false
}
}
Once the addon is configured, the first thing we are going to do is to remove all the assets that are loaded in the index.html
file.
We will want to keep our app-shell as light as possible so it's time to remove the vendor.js
, app.js
and the css
files (all the files that we have previously cached).
Now we are ready to start writing some Ember code:
Create a file in lib/app-shell/inline-content/boot.js
. This file will create the Service Worker also load the application once it's ready.
Here it's an example of how to create it:
// lib/app-shell/inline-content/boot.js
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('sw.js').then((registration) => {
// Registration was successful
}).catch((err) => {
// registration failed :(
});
}
Your app should always load even if the service worker fails (maybe depends on how it works) so now it's time to inject the necessary <script>
and <link>
tags in your DOM.
// lib/app-shell/inline-content/boot.js
function loadScript(src) {
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = src;
script.addEventListener('load', resolve);
script.addEventListener('error', reject);
document.body.appendChild(script);
});
}
// Registration was successful/failed
loadScript('assets/vendor.js');
loadScript('assets/app.js');
loadLink('assets/vendor.css');
loadLink('assets/app.css');
The app-shell must contain the minimum content to show something similar like your app, so it's time to add some HTML and CSS to our DOM.
// lib/app-shell/inline-content/app-shell.html
<div style="color: red">
<p>My awesome app!</p>
</div>
We've created a couple of files in lib/app-shell/inline-content
, but this files are outside the ember-cli
building process. We should inject this content into the index.html
using some ember-cli
hooks:
// lib/app-shell/index.js
{
contentFor(type, config) {
if (type === 'body-footer' && config && config.environment !== 'test') {
const bootJs = fs.readFileSync(path.resolve(__dirname, 'inline-content', 'boot.js'));
const appShellHtml = fs.readFileSync(path.resolve(__dirname, 'inline-content', 'app-shell.html'));
// Remember!!!!!
// You should use babel after injecting your code
return [
appShellHtml,
`<script type="text/javascript">${bootJs}</script>`
].concat('');
}
}
}
// package.json
{
"ember-addon": {
"paths": [
"lib/app-shell"
}
}
It's time to check if everything works :)