Question

Here's the situation. I have a directive, that depends on a templateUrl.

The directive looks something like this:

angular.module('foo')
.directive('bar', function(){
  return {
    restrict: 'E',
    replace: true,
    templateUrl: '/foo/bar.html',
    controller: 'fooController',
    require: '^ngModel',
    scope: {
      onSuccess: '&'
    }
  };
});

This directive is part of one of my applications, and it's important that it stays part of the application. However, I'd also like to use the same directive in other projects, and currently I'm using bower to pull down the repository into my other projects. However, this directive will break because the templateUrl will be incorrect. Bower clones down my entire project, and the actual path of the template, at best, will need to be this in my other project:

/lib/public/foo/bar.html

How do other people manage this?

Was it helpful?

Solution

I've never liked using template strings for templating due to issues with maintainability. I prototype html very quickly so I opt for a grunt task that reads my files and processes them into a single $templateCache js file.

Solution

Grunt Angular Templates

Grunt build task to concatenate & register your AngularJS templates in the $templateCache

// within my Gruntfile.js
grunt.initConfig({
  ngtemplates: {
    'angular-my-directives': {
      src:      'views/**/*.html', // where my view files are
      dest:     'src/templates.js' // single file of $templateCache
    }
  }
  // ...
});

generates something like: ./src/templates.js which preserves my folder structure:

angular.module('angular-my-directives').run(['$templateCache', function($templateCache) {

  $templateCache.put('views/directives/my-download.html',
    "<form name=\"myDownloadForm\" ng-submit=\"submit()\" novalidate>\n" +
    "</form>"
  );

}]);

Now in my directive I can simply use templateUrl: 'views/directives/my-download.html' and it will use the $templateCache.

Finally I used grunt-contrib-concat to combine my files for easy loading.

Checkout "Grunt concat + uglify with sourcemaps" (or leave a better link in comments) to learn about how to concat + uglify (aka min) js files into a single "dist" file.

If working on your own custom bower package..

Be sure to commit the concatenated (aka built) files to the package repo so your package consumers can simply include my-concat-package.js or my-concat-package.min.js

OTHER TIPS

Angular directives have templateUrl and template properties. template receives a HTML string. It's not a perfect solution, because you need to put HTML into your JS. But it is a common pattern for library creators to put the HTML string directly on template property, so they can wrap their module into a single file.

You may want make that templateUrl-to-template a build step of you lib.

Take a look on Angular Bootstrap Bower Repo.

One solution to this, and my personally preferred solution to this, is to put the template into $templateCache in a Module.run function. That way you never have to worry about the url referring to the wrong thing--you can give it any arbitrary identifying url you want--and it will never require an http request to fetch that template, to boot.

In your-directive project:

Create all template with xyz.tpl.html file name, and put into your-directive dir with all js code. All templateUrl looks like

templateUrl: '/template/my.tpl.html'

In app project:

Create a gulp/grunt task in your project for copy all *.tpl.html file into /template/ dir from bower_components (default).

e.g.:

// copy template files
gulp.task('copy-tpl', function() {
    return gulp.src([
        config.dirs.src.bower_components + '/**/*.tpl.html'
    ])
            .pipe(flatten())
            .pipe(gulp.dest(config.dirs.build.tpl));
});

Important! The /template dir is a convention (as js, css, etc).

You can do this with gulp using gulp-angular-templatecache.

You directive would look like so:

angular.module('foo').directive('bar', function($templateCache){
  return {
    restrict: 'E',
    replace: true,
    template: '$templateCache.get('bar.html')',
    controller: 'fooController',
    require: '^ngModel',
    scope: {
      onSuccess: '&'
    }
  };
});

And your gulpfile.js would look like this:

var gulp = require('gulp'),
    addTemplates = require('gulp-angular-templatecache');

gulp.task('default', function() {
    gulp.src('templatesDir/**/*.html')
        .pipe(addTemplates('templateFile.js', {module: 'foo' }))
        .pipe(gulp.dest('dist'));
});

This produces the file 'templateFile.js' in your dist folder which then can be packaged with your directive file in your bower.json.

I already gave an answer using grunt to build your html files into $templateCache file, but I've since switched to gulp, so anyone looking to follow same process but using gulp, checkout gulp-angular-templatecache

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top