Tobson Tobson - 28 days ago 18x
AngularJS Question

Angular $templateCache 404 error for relative templateUrls after bundling

I am using Grails 3 with the AngularJS profile together with the Angular Toastr plugin.

When I run the application in development mode, everything works perfectly but when I bundle the app (without minification), the templates from the plugin can't be loaded anymore and I get the following error:

Error: [$compile:tpload] Failed to load template: directives/toast/toast.html (HTTP status: 404 Not Found)

I checked the bundled code and found the lines:

.constant('toastrConfig', {
allowHtml: false,
autoDismiss: false,
closeButton: false,
closeHtml: '<button>&times;</button>',
containerId: 'toast-container',
extendedTimeOut: 1000,
iconClasses: {
error: 'toast-error',
info: 'toast-info',
success: 'toast-success',
warning: 'toast-warning'
maxOpened: 0,
messageClass: 'toast-message',
newestOnTop: true,
onHidden: null,
onShown: null,
onTap: null,
positionClass: 'toast-top-right',
preventDuplicates: false,
preventOpenDuplicates: false,
progressBar: false,
tapToDismiss: true,
target: 'body',
templates: {
toast: 'directives/toast/toast.html',
progressbar: 'directives/progressbar/progressbar.html'
timeOut: 5000,
titleClass: 'toast-title',
toastClass: 'toast'


angular.module("toastr").run(["$templateCache", function($templateCache) {$templateCache.put("directives/progressbar/progressbar.html","<div class=\"toast-progress\"></div>\n");
$templateCache.put("directives/toast/toast.html","<div class=\"{{toastClass}} {{toastType}}\" ng-click=\"tapToast()\">\n <div ng-switch on=\"allowHtml\">\n <div ng-switch-default ng-if=\"title\" class=\"{{titleClass}}\" aria-label=\"{{title}}\">{{title}}</div>\n <div ng-switch-default class=\"{{messageClass}}\" aria-label=\"{{message}}\">{{message}}</div>\n <div ng-switch-when=\"true\" ng-if=\"title\" class=\"{{titleClass}}\" ng-bind-html=\"title\"></div>\n <div ng-switch-when=\"true\" class=\"{{messageClass}}\" ng-bind-html=\"message\"></div>\n </div>\n <progress-bar ng-if=\"progressBar\"></progress-bar>\n</div>\n");}]);

so it seems that the templates are initialized correctly in the template cache.

I tried injecting the $templateCache in a controller and called
and this returns me the correct template.

What could be the reason that the template is not loaded correctly when bundled, although I can access it with

Is there anything I am missing about the correct usage of the $templateCache?

PS: I noted the same problem for angular-bootstrap templates

Edit I found out, that everything works, when I use absolute templateUrls, so apparently, I don't fully understand, how the relative templateUrls work.

When the app is bundled, all the JS code is concatenated to a single file with a different path, which seems to break the loading via the $templateCache. Now, making all the templateUrls absolute would be a solution, but I can not do that for plugins, which use a relative templateUrl, without changing their code.

So, can anybody explain to me what is actually happening here and how I could fix this, without touching the plugin code?


I found out, that when generating the Grails app for Angular, it automatically includes the following lines in the index.gsp:

<script type="text/javascript">
    window.contextPath = "${request.contextPath}";

This sets the window.contextPath when the app is bundled for production which breaks the Angular $templateCache.

In other words: Set window.contextPath = "" or the template resolution from the $templateCache will fail.