mehaase mehaase - 1 month ago 18
Dart Question

How to make AngularDart request templates from an absolute URL?

When AngularDart requests an HTML template, it requests a URL relative to the broswer's current URL. E.g. if the user is currently at

/
, then AngularDart might request a template URL like
/packages/myapp/components/mycomponent.html
. But if the browser location is
/foo
or
/foo/bar
, that same exact template would be requested from
/foo/myapp/components/mycomponent.html
or
/foo/bar/myapp/components/mycomponent.html
, respectively.

This is obviously undesirable behavior. The URLs
/foo
and
/foo/bar
only exist in the AngularDart router (I'm using
pushState
routing). The server doesn't know anything about them! Even if the server did have identically named URLs, it won't have the
packages
directory symlinked or aliased under all of them.

I tried absolute URLs when declaring the component…

/// The top navigation bar.
@Component(
selector: 'nav',
templateUrl: '/static/dart/web/packages/myapp/component/nav.html',
useShadowDom: false
)


…but
pub build
complains.

[Warning from _Serial on myapp|web/main.dart with input myapp|lib/component/nav.dart]:
line 6, column 1 of package:myapp/component/nav.dart: Cannot cache non-package absolute URIs. /static/dart/web/packages/myapp/component/nav.html
@Component(
^^^^^^^^^^^


The blessed way to declare a
templateUrl
is to make it a relative URL that starts with
packages/
.

/// The top navigation bar.
@Component(
selector: 'nav',
templateUrl: 'packages/myapp/component/nav.html',
useShadowDom: false
)


So it seems like I'm forced to use relative URLs for templates even though the browser's location is completely divorced from where things are stored on the server.

So how should I tell AngularDart to request these templates from an absolute URL, rather than constructing a URL relative to the browser's current location?

Answer

Angular 1

I don't have an ideal solution, but here's something that helps. I declared a <base href='…'> element at the document level so that all relative links will be resolved relative to that base URL, not the browser's location (which, as mentioned in the question, is divorced from the actual server resource being accessed).

<!DOCTYPE html>
<html ng-app>
  <head>
    <title>My App</title>
    <base href="/static/dart/">
    ...

This will cause AngularDart template URLs to resolve to absolute URLs like /static/dart/packages/myapp/components/mycomponent.html, regardless of the browser's location. This set up works for my development environment, but the base href needs to be changed for production to point to pub build artifacts. I've omitted that part from my answer since it's unique to my server-side software and has nothing to do with Dart or Angular.

The downside to this approach is that you won't be able to use relative URLs for anything else: they will all be resolved with respect to the <base> element.

Angular 2

Sigh... 18 months later and the brand-new-shiny Angular 2 has the same exact problem, except that the solution for Angular 1 doesn't work in Angular 2.

You must inject a custom UrlResolver, e.g.

import 'package:angular2/platform/browser.dart' show bootstrap;
import 'package:angular2/src/compiler/url_resolver.dart' show UrlResolver;
import 'package:angular2/src/core/di/provider.dart' show provide;
import 'package:myapp/component/app.dart' show AppComponent;

void main() {
    bootstrap(AppComponent, [
        provide(UrlResolver, useFactory:() => new UrlResolver('/static/dart/web/packages'))
    ]);
}