Loading relative templateUrl

48,386

Solution 1

I think you'll eventually find that maintaining the paths relative to the js file will be harder, if even possible. When it comes time to ship, you are most likely going to want to concatenate all of your javascript files to one file, in which case you are going to want the templates to be relative to the baseUrl. Also, if you are fetching the templates via ajax, which Angular does by default unless you pre-package them in the $templateCache, you are definitely going to want them relative to the baseUrl, so the server knows where to find them once your js file has already been sent to the browser.

Perhaps the reason that you don't like having them relative to the baseUrl in development is because you aren't running a server locally? If that's the case, I would change that. It will make your life much easier, especially if you are going to work with routes. I would check out Grunt, it has a very simple server that you can run locally to mimic a production setup called Grunt Connect. You could also checkout a project like Yeoman, which provides a pre-packaged front end development environment using Grunt, so you don't have to spend a lot of time getting setup. The Angular Seed project is a good example of a Grunt setup for local development as well.

Solution 2

The best way to do this now is using a module loader like browserify, webpack, or typescript. There are plenty of others as well. Since requires can be made from the relative location of the file, and the added bonus of being able to import templates via transforms or loaders like partialify, you don't even have to use template urls anymore. Just simply inline the Template via a require.

My old answered is still available below:

I wrote a post on exactly this subject and spoke on it at our local Angular Meetup. Most of us are now using it in production.

It is quite simple as long as your file structure is represented effectively in your modules. Here is a quick preview of the solution. Full article link follows.

var module = angular.module('myApp.things', []);

var all = angular.module('myApp.things.all', [
    'myApp.things',
    'things.someThing',
    'things.someOtherThing',
    'things.someOtherOtherThing',
]);

module.paths = {
    root: '/path/to/this/thing/',
    partials: '/path/to/this/thing/partials/',
    sub: '/path/to/this/thing/sub/',
};

module.constant('THINGS_ROOT', module.paths.root);
module.constant('THINGS_PARTIALS', module.paths.partials);
module.constant('THINGS_SUB', module.paths.sub);

module.config(function(stateHelperProvider, THINGS_PARTIALS) {

    stateHelperProvider.setNestedState({
        name: 'things',
        url: '/things',
        templateUrl: THINGS_PARTIALS + 'things.html',
    });
});

And then any sub modules or "relative" modules look like this:

var module = angular.module('things.someThing', ['myApp.things']);
var parent = angular.module('myApp.things');

module.paths = {
    root: parent.paths.sub + '/someThing/',
    sub: parent.paths.sub + '/someThing/sub/',
    partials: parent.paths.sub + '/someThing/module/partials/',
};

module.constant('SOMETHING_ROOT', module.paths.root);
module.constant('SOMETHING_PARTIALS', module.paths.partials);
module.constant('SOMETHING_SUB', module.paths.sub);

module.config(function(stateHelperProvider, SOMETHING_PARTIALS) {

    stateHelperProvider.setNestedState({
        name: 'things.someThing',
        url: "/someThing",
        templateUrl: SOMETHING_PARTIALS + 'someThing.html',
    });
});

Hope this helps!

Full Article: Relative AngularJS Modules

Cheers!

Solution 3

Currenly it is possible to do what you want using systemJs modules loader.

import usersTemplate from './users.tpl';

var directive = {
   templateUrl: usersTemplate.name
}

You can check good example here https://github.com/swimlane-contrib/angular1-systemjs-seed

Solution 4

I've been chewing on this issue for a while now. I use gulp to package up my templates for production, but I was struggling to find a solution that I was happy with for development.

This is where I ended up. The snippet below allows any url to be rewired as you see fit.

angular.module('rm').config(function($httpProvider) {

  //this map can be defined however you like
  //one option is to loop through script tags and create it automatically
  var templateMap = {
    "relative.tpl.html":"/full/path/to/relative.tpl.html",
    etc...
  };

  //register an http interceptor to transform your template urls
  $httpProvider.interceptors.push(function () {
    return {
      'request': function (config) {
        var url = config.url;
        config.url = templateMap[url] || url;
        return config;
      }
    };
  });
});
Share:
48,386
Danny Nelson
Author by

Danny Nelson

Updated on July 09, 2022

Comments

  • Danny Nelson
    Danny Nelson almost 2 years

    I've been trying to find the best way to create a modular, scalable angular application. I really like the structure of projects like angular-boilerplate, angular-app, where all the related files are grouped together by feature for partials and directives.

    project
    |-- partial
    |   |-- partial.js
    |   |-- partial.html
    |   |-- partial.css
    |   |-- partial.spec.js
    

    However, in all these examples, the template URL is loaded relative to the base url, not relative to the current file:

    angular.module('account', [])
    .config(function($stateProvider) {
      $stateProvider.state('account', {
        url: '/account',
        templateUrl: 'main/account/account.tpl.html', // this is not very modular
        controller: 'AccountCtrl',
      });
    })
    

    This is not very modular, and could become difficult to maintain in large projects. I would need to remember to change the templateUrl path every time I moved any of these modules. It would be nice if there was some way to load the template relative to the current file like:

    templateUrl: './account.tpl.html'
    

    Is there any way to do something like this in angular?

  • Danny Nelson
    Danny Nelson about 10 years
    Ya, I guess Angular is not really set up for this. I just didn't like having a relative baseUrl because, in the project structure above, the template/directive template/partial pairs are always in the same directory. I guess I need to use something like RequireJS or Browserify to get this kind of functionality.
  • Charlie Martin
    Charlie Martin about 10 years
    I usually put the path to my template files in a constant. Something like... angular.constant('TPL_PATH', '/main/templates/') I give each module its own template path and constant by the same name. That way all of my directives are just... templateUrl: TPL_PATH + 'name.tpl.html' but TPL_PATH is still different for each module
  • Danny Nelson
    Danny Nelson about 10 years
    Interesting. That's definitely an improvement over the other way. Another option would be to register a more concise template name (maybe one that matches the controller/directive name) by wrapping the template with an <script type="text/ng-template" id="controllerName.tpl.html">.
  • Cowman
    Cowman about 10 years
    The problem I see with this, in my attempt to understand this, is that when I transfer my feature from app to app, I have to remember to transfer not only the scripts area part, but also wherever all the templates are stored. Instead of moving one directory, I'm moving two directories. Or maybe I'm not understanding this correctly.
  • Derek Perkins
    Derek Perkins over 9 years
    That's a really elegant solution. Can't wait to start using it!
  • Justin German
    Justin German about 9 years
    This looks interesting. The main concern I have with it is that this adds a dependency to the parent module, and potentially circular dependencies. Isn't it usually better practice to have parent modules depend on child modules? Can you still test module 'things.someThing' in isolation ?
  • tannerlinsley
    tannerlinsley about 9 years
    The use case for this is not regarding dependency injection but more file structure. It mostly applies to modules that may live inside of other modules for organizations sake or for building a platform for 3rd party integrations and modules to be correctly built and located in your file structure.
  • windmaomao
    windmaomao almost 9 years
    Because we are using injection here, maybe it's worth to have another centralized unit (or module) just for paths, so that we'll have lots of constant defined as Home.Second.Third etc.
  • Charlie Martin
    Charlie Martin almost 9 years
    Two directories become one when you put them in a shared parent. You should be thinking about your templates and your scripts as the same thing. They, combined, are the source code of your application. Your application will not work without one or the other
  • hugsbrugs
    hugsbrugs about 8 years
    I've found this gulp package very useful for changing templateUrl to plain template !