14

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?

bioball
  • 1,249
  • 11
  • 20

6 Answers6

10

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

Community
  • 1
  • 1
electblake
  • 1,707
  • 16
  • 25
  • This would definitely work, although it introduces some overhead in that any project that includes this directive will also need to include this grunt script. It might make more sense to use a grunt task to generate the template string whenever the template has changed (say, by using a replace function) – bioball Jan 06 '15 at 19:43
  • @bioball that is actually what indeed happens. in the final step of grunt-concat, I commit the build files on release of my package. Grunt only required for contribution/development of package - not usage – electblake Jan 07 '15 at 15:49
  • 1
    Ah, gotcha. Yeah, makes sense. I had thought you were putting the `$templateCache` logic in the application that includes the directive, but I get it now. I like it. Seems like the best solution to me. – bioball Jan 07 '15 at 18:47
  • in this case there is no way to add also css files...any best practice on it? i was thinking to add a – Giovanni Bitliner Oct 06 '15 at 17:06
  • @GiovanniBitliner, I compile each type of asset into a single file. All js into single js file, all sass compiled into single css file. I'd never insert the styles or other assets (like fonts) inline/directly myself. My goal here is to provide really simple api for including unto a page directly (userland installs 1 file per file-type is nice), or bundling within a greater app (for professional use). – electblake Oct 06 '15 at 22:55
  • @electblake do you have any example repository that I may have a look at in order to check your approach? – Giovanni Bitliner Oct 07 '15 at 07:39
5

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.

Ken Rosaka
  • 391
  • 3
  • 5
  • I've thought about using `template` and stringifying my template, but I haven't found a build process that can turn a jade template into HTML, into a javascript string, and substitute out one line in my javascript file to place it in there. Does that exist? – bioball Jun 04 '14 at 20:26
  • I checked out the angular bootstrap bower repo, and they hard coded the html strings, which I'm trying to avoid doing – bioball Jun 04 '14 at 20:33
  • 1
    They're probably not really "hard-coded", they're likely using something like this Grunt plugin to generate that stuff: https://www.npmjs.org/package/grunt-angular-templates I'm thinking I may have to do the same... – John Munsch Jun 11 '14 at 17:23
3

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.

Jeff Hubbard
  • 9,444
  • 3
  • 27
  • 27
0

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).

István Pató
  • 343
  • 3
  • 7
0

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.

SyntaxRules
  • 1,503
  • 19
  • 32
0

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

Community
  • 1
  • 1
electblake
  • 1,707
  • 16
  • 25