Understanding Ember.js Public Asset Path

Write Me

Correction

Thanks to @neojp for his response https://twitter.com/neojp/status/1269708074387939329

I wrote this blog post without fully understanding the reasoning behind ember rewriting urls, fingerprinting and the TIL <base /> tag, the simple and pretty straightforward solution to handle urls in both environments is to just add a <base href={{rootURL}} /> in /app/index.html. I started using ember around 2.8, and I just found out what was the actual purpose of baseURL in config/environment.js and well, it got deprecated back in ember-cli 2.7, this practically changes how assets urls work in ember. Surprisingly the recommended solution is to use

fully-qualified paths or a root-relative URL.

For example

{{! Old: (with baseURL and/or <base /> tag) }}
<img src="assets/images/logo.png" />

{{! New: }}
<img src="/assets/images/logo.png" />

If you want to add the base tag, which I do recommend:

<!-- index.html -->
...
<head>
  <base href="{{rootURL}}" /> 
</head>

This means that all non fully-qualified urls will get the rootURL of your config/environment.js prefixed by the browser.

I think this is not really that well documented in Ember.js guides or Ember.js tutorial. New ember apps starts without the <base /> tag, thus won't seamlessly work in the browser and in corber, because while running in cordova you can't simply point relative paths i.e /asset/img/photo.jpg, all assets are placed elsewhere. The silver bullet is the <base /> tag.

The helper proposed in this blog post is useful if you want another way around adding the <base /> tag in your index.html because of some unexpected behaviors around it or just to follow(?) the current ember-cli blueprint… the following helper gives you the correct relative path in templates and JS regardless if you are running in cordova or the browser, you just gotta make sure this helper runs after cordova.deviceReady

// helpers/public-url
import Helper from '@ember/component/helper';
import config from 'ember-get-config';

let IS_READY = false;

export function publicUrl(url) {
  if (typeof FastBoot === 'undefined' && window.cordova && IS_READY) {
    return `${window.cordova.file.applicationDirectory}www/${config.ENV.rootURL}${url}`;
  }
  return `${config.ENV.rootURL}${url}`;
}

export default Helper.extend({
  cordovaEvents: service('ember-cordova/events'),

  deviceReadyObserver: subscribe('cordovaEvents.deviceready', 
    function(){
      IS_READY = true;
      recompute();
    })

  compute([url]){
    return publicUrl(url);
  }
});

And use it in templates like this

<img src={{public-url "assets/images/teams/{{team.short_name}}.png"}} alt="{{team.name}}'s flag" style="width: 50px;">

And in js like this

import { publicUrl } from '../helpers/public-url';
publicUrl(`assets/images/teams/${team.short_name}.png`);