原文:http://davidcai.github.io/blog/posts/router-dynamic-templates/

ui-router : templateProvider vs template

----------------------------------------------

Router: Dynamic Templates

Sat Aug 15, 2015

This post discusses how to create dynamic templates by leveraging the templateProvider configuration provided by Angular’s built-in router or the third-party UI Router.

PROBLEM

For Single Page Applications (SPAs), we often need to switch views or states inside containers. This is usually done through routers. With either Angular’s built-in router or the popular UI Router, we are able to define the relationship between states and their templates. For instance, here we defined a state home and its template URL app/home/home.html:

  1. app.config(function($stateProvider) {
  2. $stateProvider.state('home', {
  3. url: '/',
  4. templateUrl: 'app/home/home.html'
  5. });
  6. });

In some cases, this state-to-template relationship can not be determined beforehand at the config time. The decision of what the template or template URL will be used for a state has to wait for the availability of run-time data. For example:

  • User’s account type, e.g. show Home version A for members, and version B for public users.
  • A/B testing, e.g. a A/B testing service randomly picks from two versions – A or B.

In either scenario, the template cannot be fixed to app/home/home.html, and has be to resolved using run-time data.

Router’s templateUrl configuration accepts a function which can be used to create dynamic template URL. However, we are not able to inject run-time dependencies (e.g. user services, or A/B test services) into the templateUrl function. The only available argument of the templateUrl function is $stateParams.

  1. $stateProvider.state('home', {
  2. templateUrl: function($stateParams) { // Can not inject dependencies
  3. return 'app/home.' + $stateParams.option + '.html';
  4. }
  5. });

SOLUTION

The answer is templateProvider.

Both Angular built-in router and the UI Router have a templateProvider configuration. templateProvider accepts a function that can be injected with run-time dependencies.

  1. $stateProvider.state('home', {
  2. templateProvider: function(abTestService) { // abTestService is injected here
  3. var result = abTestService.pick('a', 'b'); // Choose version A or B
  4. return '...'; // Return template content based on the result
  5. }
  6. });

templateProvider returns template content (not an URL to the template). We can certainly embed HTML markups directly in JavaScript, but for complicate HTML, it’s better to externalize the HTML content to separate template files. Here, we created home-a.html and home-b.html, and ngInclude them in the templateProvider function:

  1. <!-- Home version A at app/home/home-a.html -->
  2. <div ng-controller="HomeAController">Version A</div>
  3. <!-- Home version B at app/home/home-b.html -->
  4. <div ng-controller="HomeBController">Version B</div>
  1. $stateProvider.state('home', {
  2. templateProvider: function(abTestService) {
  3. var result = abTestService.pick('a', 'b');
  4. // ngInclude template content based on the A/B test result
  5. return '<div ng-include="\'app/home/home-' + result + '.html\'"></div>';
  6. }
  7. });

templateProvider can also return a Promise which is resolved to template content.

  1. $stateProvider.state('home', {
  2. templateProvider: function($http, USER_SERVICE_REST_URL) {
  3. // Here, we return a promise instead of the template content
  4. return $http.get(USER_SERVICE_REST_URL).then(function(data) {
  5. var result = (data.type === 'member' ? 'a' : 'b');
  6. // Return the template content
  7. return '<div ng-include="\'app/home/home-' + result + '.html\'"></div>';
  8. });
  9. }
  10. });

EVEN BETTER SOLUTION

Having ngInclude in templateProvider function feels still a bit hackish to me. The ideal solution is to specify a template URL, and then let Angular fetch the content. However, sending separate HTTP requests just to fetch templates seems to be unnecessary web traffic. It will be better if the template content can be cached in the $templateCache service; and then, all I need to do is $templateCache.get('templateUrl'):

  1. $stateProvider.state('home', {
  2. templateProvider: function(abTestService, $templateCache) {
  3. var result = abTestService.pick('a', 'b');
  4. // Retrieve the cached template content from $templateCache service
  5. return $templateCache.get('app/home/home-' + result + '.html');
  6. }
  7. });

To achieve this, we need a Gulp task to convert all HTML files under the app/ directory to JavaScript strings, and save the strings in $templateCache.

  1. // Load gulp and its plugins
  2. var gulp = require('gulp');
  3. var minifyHtml = require('gulp-minify-html');
  4. var angularTemplateCache = require('gulp-angular-templatecache');
  5. gulp.task('templates', function() {
  6. return cacheTemplates('src/app/**/*.html', 'app.template.js');
  7. function cacheTemplates(input, output) {
  8. return gulp.src(input) // Get all HTML files
  9. .pipe(minifyHtml({ // Minify HTML content first
  10. empty: true,
  11. spare: true,
  12. quotes: true
  13. }))
  14. .pipe(angularTemplateCache(output, { // Save minified strings to cache
  15. module: 'myApp' // Setup $templateCache for Angular module 'myApp'
  16. }))
  17. .pipe(gulp.dest('.tmp/templates/'));
  18. } // /function cacheTemplates
  19. });

Then, import the generated template.js in index.html:

  1. <script src=".tmp/templates/app.template.js"></script>

CONCLUSION

By leveraging the templateProvider function that can be injected with dependencies, we are able to resolve template content based on run-time data. This technique is useful for switching among more than one templates for a state, for instance, A/B testing, and swappable content in limited space.

angular—— Dynamic Templates的更多相关文章

  1. ES - Dynamic templates 动态模板

    1.ES Mapping 在lucene中,索引中每个字段都需要指定很多属性,例如:是否分词.采用哪个分词器.是否存储等. 在ES中,其实索引中每个字段也需要指定这些属性,我们有时候并没有对这些属性进 ...

  2. [Angular 2] Set Values on Generated Angular 2 Templates with Template Context

    Angular 2 templates have a special let syntax that allows you to define and pass a context when they ...

  3. [Angular] Dynamic component's instance and sorting

    After create a component dynamic, we are able to change the component's props and listen to its even ...

  4. [Angular] Dynamic component rendering by using *ngComponentOutlet

    Let's say you want to rending some component based on condition, for example a Tabs component. Insid ...

  5. [Angular] Dynamic components with ComponentFactoryResolver

    To create a component dynamicly. 1. Add a container with ref: @Component({ selector: 'app-root', tem ...

  6. ANGULAR 2 FOR REACT DEVELOPERS

    Now that Angular 2 is in beta, the time has come for us to drop everything and learn something new, ...

  7. [Angular 2] Create template with Params

    Angular 2 templates have a special let syntax that allows you to define and pass a context when they ...

  8. [Angular 2] Generate and Render Angular 2 Template Elements in a Component

    Angular 2 Components have templates, but you can also create templates inside of your templates usin ...

  9. [Angular 2] Rendering an Observable with the Async Pipe

    Angular 2 templates use a special Async pipe to be able to render out Observables. This lesson cover ...

随机推荐

  1. 利用cron监视后台进程状态

    利用cron监视后台进程状态 http://blog.csdn.net/dyx810601/article/details/72967758 1. 利用cron监视后台进程状态,如果进程死掉或服务器重 ...

  2. Java坦克大战 (四) 之子弹的产生

    本文来自:小易博客专栏.转载请注明出处:http://blog.csdn.net/oldinaction 在此小易将坦克大战这个项目分为几个版本,以此对J2SE的知识进行回顾和总结,希望这样也能给刚学 ...

  3. [ 总结 ] nginx 安装过程

    本次安装使用源码编译安装: 在安装nginx之前需要先安装三个源码包:pcre.zilb.openssl    pcre:用于rewrite重写    zlib: 用于gzip压缩    openss ...

  4. phython正则表达式 Python Re模块

    反斜杠问题 与大多数编程语言相同,正则表达式里使用”\”作为转义字符,这就可能造成反斜杠困扰.假如你需要匹配文本中的字符”\”, Python里的原生字符串很好地解决了这个问题,这个例子中的正则表达式 ...

  5. python操作数据库的几种方式

    参照python 操作mysql python-mysqldb : http://www.cnblogs.com/wupeiqi/articles/5095821.html (python3 不支持) ...

  6. css :not 选择器

    :not 选择器是css3里面的 :not([class]){color:red;}  // 没有class属性的元素都设置为红色 p:not([class]){color:red;} // 没有cl ...

  7. mysql 文本搜索

    全文本搜索 MySQL支持几种基本的数据库引擎,但并非所有的引擎都支持全文本搜索.两个最常使用的引擎为MyISAM和InnoDB,前者支持全文本搜索,后者就不支持. 理解全文本搜索 在前面的学习中,我 ...

  8. HDU 1280 前m大的数(排序,字符串)

      前m大的数 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Sub ...

  9. Hibernate 配置文件与实体类

    今天在配置Hibernate的时候碰到了这样的错误: MappingException: Unknown entity 仔细想了一下,应该是因为我在开始时使用了 Configuration confi ...

  10. Spring Cloud 常用依赖

    <!-- 将微服务provider侧注册进eureka --> <dependency> <groupId>org.springframework.cloud< ...