2016-10-04 90 views
3

我正在尝试创建一个用于形成句子的角度指令。目标是获取一个列表并根据需要对它们进行迭代。该指令的结果会是这样的:Angular 1.x指令与模板

鞋子,裤子和袜子

鞋子,裤子和+5更

我有基本的指令设置来处理字符串数组 - 但我想定制它以允许定制te每个句子元素的模板(即,超链接,造型等)。那就是:

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

用户看到在浏览器的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> 

我猜它是与transclude但想不通的API。我也尝试过使用ng-repeat代替指令模板,但无法找到解决方案。

+0

你可以使用ng-repeat =“article in articles”,它会创建必要的“句子”标签。 –

+0

我知道我可以使用'ng-repeat'遍历所有标签。我的目标是通过显示“+5更多”来生成一个句子(即,与“,”和“和”),如果句子长于三个句子,则生成一个句子。我知道如何构建一个一次性版本 - 但希望能够重用指令,以便我可以在应用程序的多个区域使用它。或者我错过了什么? – Stussa

+0

你在找什么对我来说听起来更像一个过滤器 – MilitelloVinx

回答

1

像这样的东西应该在哪里工作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> 
0

迭代提供动态内容定义的数量是与compile功能+的$compile服务自定义指令中普遍使用。当心:基本上你正在重复ng-repeat的功能,你可能想要考虑替代方案。

E.g.而不是articles列表,请使用另一个(可能名为articlesLimited)。新列表是动态构建的,包含来自articles的第一个元素。标志(例如hasMore)指示原始articles是否具有更多元素,简单地如下:$scope.hasMore = articles.length > 5。您使用hasMore标志来显示/隐藏“+ N多”消息。

但是值得一提的是,下面是sentence指令的实现。查看弱点的评论!

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>'); 
      } 
      } 
     ); 
     }; 
    } 
    }; 
}]); 

和提琴:https://jsfiddle.net/aza6u64p/

0

这是一个棘手的问题。 Transclude用于包装的元素,但使用transclude当你没有获得该指令的范围,只有正在使用的指令,其中的范围:这是什么transclude选项做

AnglularJS: Creating Custom Directives

,究竟? transclude使得使用此选项的指令内容可以访问指令外部的范围,而不是内部。

因此,一个解决方案是创建另一个组件注入模板的范围指令里面,像这样:

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

这是本次讨论采取:#7874

而且我做了一个Plnkr

+0

我没有在解决方案中包含处理列表大小的代码并且包括“+5更多”,但它是容易的部分。 – Vinicius