Angularjs 带模板的Angular 1.x指令

Angularjs 带模板的Angular 1.x指令,angularjs,Angularjs,我试图创建一个角度指令来造句。目标是获取一个列表,并根据需要对其进行迭代。该指令的结果如下: $scope.articles = [ { title: '...', author: '...'}, { title: '...', author: '...'}, ... ] <span><strong>ABC</strong> by <span>123</span></span> <span>, &l

我试图创建一个角度指令来造句。目标是获取一个列表,并根据需要对其进行迭代。该指令的结果如下:

$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>
鞋子、裤子和袜子

鞋子、裤子和+5以上

我有使用字符串数组的基本指令设置,但我想对其进行自定义,以便为每个句子元素(即超链接、样式等)提供自定义模板。即:

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

{{article.title}}作者{{article.author}}
用户在浏览器中看到的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>
$scope.articles=[
{标题:'..',作者:'..'},
{标题:'..',作者:'..'},
...
]
ABC123
, 
DEF以456
和
+还有5个

我猜它与
transclude
有关,但无法理解API。我还尝试过使用
ng repeat
代替指令模板,但未能找到解决方案。

类似的方法应该可以在
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>

{{article.title}}作者{{article.author}}
, 
和
和+{articles.length-maxArticles}更多。
迭代和提供动态内容是自定义指令与
compile
函数+
$compile
服务的共同用途。注意:基本上你正在重复<代码> NG重复> /代码>的功能,你可能想考虑其他选项。

例如,使用另一个列表(可能命名为
articlesLimited
)代替
文章列表。新列表是动态构建的,包含
文章
中的第一个元素。标志(例如,
hasMore
)指示原始的
文章
是否有更多的元素,简单地说:
$scope.hasMore=articles.length>5
。您可以使用
hasMore
标志来显示/隐藏“+N more”消息

然而,值得一提的是,下面是
语句
指令的实现。请参阅评论中的弱点

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>');
            }
          }
        );
      };
    }
  };
}]);
app.directive('语句',['$compile',函数($compile){
var RE=/^([a-z_0-9\$]+)\s+in\s([a-z_0-9\$]+)$/i,只有空格=/^\s*$/;
功能内容(远程通信){
var result=tElem.contents();
while(结果[0]。节点类型===3&&ONLY_WHITESPACE.test(结果[0]。文本内容)){
结果:拼接(0,1);
}
while(结果[result.length-1].nodeType==3&&ONLY_WHITESPACE.test(结果[result.length-1].textContent)){
result.length=result.length-1;
}
返回结果;
}
函数提取迭代元(tAttrs){
var结果=RE.exec(tAttrs.values);
如果(!结果){
抛出新错误('格式错误的值表达式,使用“列表中的itervar”:',tAttrs.values);
}
var-cutoff=parseInt(tAttrs.cutoff | | |“5”);
如果(isNaN(截止)){
抛出新错误('格式错误的截止:'+tAttrs.cutoff);
}
返回{
varName:result[1],
列表:结果[2],
切断:切断
};
}
返回{
范围:true,//也调查隔离范围。。。
编译:功能(TELM、tAttrs){
var iterationMeta=extractioniterationmeta(tAttrs);
var content=$compile(extractTrimmedContent(telm));
empty();
返回函数链接(范围、元素、属性){
var作用域=[];
范围:$watchCollection(
函数(){
//这是(IMO)范围的唯一合法用法。$parent:
//计算我们知道要在父表达式中运行的表达式
返回范围$parent.$eval(iterationMeta.list);
},
函数(newval、oldval){
变量i,项目,子范围;
//这需要优化,就像ng repeat所做的那样(身份、跟踪);为了简洁起见省略
//但是,如果列表不会改变,就可以保持原样
作用域。forEach(函数){
s、 $destroy();
});
scopes.length=0;
elem.empty();
对于(i=0;i0){
元素追加(',');
}
元素追加(克隆元素);
});
}
if(newval.length>iterationMeta.cutoff){
//这也可以是参数化的,留待下次使用;)
元素追加(+'+'+(newval.length-iterationMeta.cutoff)+'more');
}
}
);
};
}
};
}]);

小提琴:

这是一个棘手的问题。Transclude用于包装元素,但使用Transclude时,您无权访问指令范围,只能访问正在使用指令的范围:

这个转移选项到底是做什么的?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>
.directive('myList',function(){
返回{
限制:'E',
是的,
作用域:{items:'='},
模板:“”
};
})
.directive('inject',function(){
返回{
链接:函数($scope、$element、$attrs、controller、$transclude){
$transclude($范围、功能(克隆){
$element.empty();
$element.append(克隆);
});
}
};
})
{{item.title}by{{item.author}
这是从这次讨论中得出的:

我做了一个。

你可以使用ng repeat=“article in articles”,它将