简体   繁体   English

Angular 1.x指令与模板

[英]Angular 1.x Directive With A Template

I'm trying to create an angular directive for forming sentences. 我正在尝试创建一个用于形成句子的角度指令。 The goal is to take a list and iterate through them as necessary. 目标是获取一个列表并根据需要迭代它们。 The result of the directive would be something like: 该指令的结果如下:

shoes, pants and socks 鞋子,裤子和袜子

or 要么

shoes, pants and +5 more 鞋子,裤子和+5更多

I have the basic directive setup to work with a array of strings - but I'd like to customize it to allow custom templates for each sentence element (ie hyperlinks, styling, etc). 我有基本的指令设置来处理字符串数组 - 但我想自定义它以允许每个句子元素的自定义模板(即超链接,样式等)。 That is: 那是:

<sentence values="article in articles">
<strong>{{article.title}}</strong> by <span>{{article.author}}</span>
</sentence>

The HTML the user sees in the browser needs to be something like: 用户在浏览器中看到的HTML需要类似于:

$scope.articles = [
  { title: '...', author: '...'},
  { title: '...', author: '...'},
  ...
]

<span><strong>ABC</strong> by <span>123</span></span>
<span>, </span>
<span><strong>DEF</strong> by <span>456</span></span>
<span>and</span>
<span>+5 more</span>

I'm guessing it has something to do with transclude but cannot figure out the API. 我猜它与transclude但无法弄清楚API。 I've also experimented with using ng-repeat instead of the directive template but wasn't able to find a solution. 我也尝试过使用ng-repeat而不是指令模板但是无法找到解决方案。

Something like this should work where maxArticles is a number defined on your scope 这样的东西应该适用于maxArticles是在你的范围上定义的数字

<sentence values="article in articles | limitTo: maxArticles">
    <strong>{{article.title}}</strong> by <span>{{article.author}}</span>
    <span ng-if="$index < maxArticles - 2">, </span>
    <span ng-if="$index === articles.length - 1 && articles.length <= maxArticles">and</span>
</sentence>
<span ng-if="articles.length > maxArticles">
    and +{{articles.length - maxArticles}} more.
</span>

Iterating AND providing dynamic content is a common use for a custom directive with the compile function + the $compile service. 迭代提供动态内容是使用compile函数+ $compile服务的自定义指令的常见用法。 Watch out: essentially you are repeating the functionality of ng-repeat , you may want to consider alternatives. 注意:基本上你是在重复ng-repeat的功能,你可能想要考虑替代方案。

Eg instead of the articles list, use another one (perhaps named articlesLimited ). 例如,而不是articles列表,使用另一个(可能名为articlesLimited )。 The new list is constructed dynamically and contains the first elements from articles . 新列表是动态构建的,包含articles中的第一个元素。 A flag (eg hasMore ) indicates whether the original articles has more elements, simply as: $scope.hasMore = articles.length > 5 . 标志(例如hasMore )指示原始articles是否具有更多元素,简单如下: $scope.hasMore = articles.length > 5 You use the hasMore flag to show/hide the "+N more" message. 您使用hasMore标志来显示/隐藏“+ N more”消息。

For what it's worth however, below is an implementation of the sentence directive. 然而,对于它的价值,下面是sentence指令的实现。 See the comment for weak points! 看弱点的评论!

app.directive('sentence', ['$compile', function($compile) {
  var RE = /^([a-z_0-9\$]+)\s+in\s([a-z_0-9\$]+)$/i, ONLY_WHITESPACE = /^\s*$/;

  function extractTrimmedContent(tElem) {
    var result = tElem.contents();
    while( result[0].nodeType === 3 && ONLY_WHITESPACE.test(result[0].textContent) ) {
      result.splice(0, 1);
    }
    while( result[result.length-1].nodeType === 3 && ONLY_WHITESPACE.test(result[result.length-1].textContent) ) {
      result.length = result.length - 1;
    }
    return result;
  }

  function extractIterationMeta(tAttrs) {
    var result = RE.exec(tAttrs.values);
    if( !result ) {
      throw new Error('malformed values expression, use "itervar in list": ', tAttrs.values);
    }
    var cutoff = parseInt(tAttrs.cutoff || '5');
    if( isNaN(cutoff) ) {
      throw new Error('malformed cutoff: ' + tAttrs.cutoff);
    }
    return {
      varName: result[1],
      list: result[2],
      cutoff: cutoff
    };
  }

  return {
    scope: true, // investigate isolated scope too...
    compile: function(tElem, tAttrs) {
      var iterationMeta = extractIterationMeta(tAttrs);

      var content = $compile(extractTrimmedContent(tElem));
      tElem.empty();

      return function link(scope, elem, attrs) {
        var scopes = [];
        scope.$watchCollection(
          function() {
            // this is (IMO) the only legit usage of scope.$parent:
            // evaluating an expression we know is meant to run in our parent
            return scope.$parent.$eval(iterationMeta.list);
          },
          function(newval, oldval) {
            var i, item, childScope;

            // this needs OPTIMIZING, the way ng-repeat does it (identities, track by); omitting for brevity
            // if however the lists are not going to change, it is OK as it is
            scopes.forEach(function(s) {
              s.$destroy();
            });
            scopes.length = 0;
            elem.empty();

            for( i=0; i < newval.length && i < iterationMeta.cutoff; i++ ) {
              childScope = scope.$new(false, scope);
              childScope[iterationMeta.varName] = newval[i];
              scopes.push(childScope);
              content(childScope, function(clonedElement) {
                if( i > 0 ) {
                  elem.append('<span class="sentence-sep">, </span>');
                }
                elem.append(clonedElement);
              });
            }

            if( newval.length > iterationMeta.cutoff ) {
              // this too can be parametric, leaving for another time ;)
              elem.append('<span class="sentence-more"> +' + (newval.length - iterationMeta.cutoff) + ' more</span>');
            }
          }
        );
      };
    }
  };
}]);

And the fiddle: https://jsfiddle.net/aza6u64p/ 小提琴: https//jsfiddle.net/aza6u64p/

This is a tricky problem. 这是一个棘手的问题。 Transclude is used to wrap elements but when using transclude you don't have access to the directive scope, only to the scope of where the directive is being used: Transclude用于包装元素,但是当使用transclude时,您无权访问指令范围,只能访问指令的使用范围:

AnglularJS: Creating Custom Directives AnglularJS:创建自定义指令

What does this transclude option do, exactly? 这个转换选项究竟做了什么? transclude makes the contents of a directive with this option have access to the scope outside of the directive rather than inside. transclude使得带有此选项的指令的内容可以访问指令之外的范围而不是内部。

So a solution is to create another component to inject the template's scope inside the directive, like this: 因此,解决方案是创建另一个组件以在模板中注入模板的范围,如下所示:

.directive('myList', function() {
  return {
    restrict: 'E',
    transclude: true,
    scope: { items: '=' },
    template: '<div ng-repeat="item in items" inject></div>'
  };
})

.directive('inject', function() {
  return {
    link: function($scope, $element, $attrs, controller, $transclude) {
      $transclude($scope, function(clone) {
        $element.empty();
        $element.append(clone);
      });
    }
  };
})

<my-list items="articles">
    <strong>{{item.title}}</strong> by <span>{{item.author}}</span>
</my-list>

This was taken from this discussion: #7874 这是从这个讨论中得出的: #7874

And I made a Plnkr . 我做了一个Plnkr

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM