2016-10-04 123 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