2014-07-10 181 views
2

我正在构建一个指令,可以在不同的控制器中使用,我希望能够将指令绑定到我的$scope的特定属性。AngularJS:指示指令的范围

我愿做这样的事情:

<div ng-app="myapp"> 
    <div ng-controller="myController"> 
     <my-directive-wrapper ng-model="mymodel"> 
      <my-directive-inner ng-repeat="item in items" /> 
     </my-directive-wrapper> 
    </div> 
</div> 

在这种模式下:

$scope.mymodel = { 
    name : "Transclude test", 
    items : [ 
     { title : "test1" }, 
     { title : "test2" }, 
     { title : "test3" } 
    ] 
}; 

所以该指令myDirectiveWrapper得到$scope.mymodel的范围,而不是其他。然后我可以将指令放两遍,每个指向一个不同的属性。

我这儿有问题的演示项目:http://jsfiddle.net/vtortola/P8JMm/3/

而同样的演示正常工作(不限制范围)位置:http://jsfiddle.net/vtortola/P8JMm

的问题是,如何在使用说明我的指令,我想使用我的$scope的特定属性作为我的指令的范围。应该可以将该指令绑定到相同的$scope中的任意属性。

干杯。

+0

你看过传递给链接函数参数的'$ transclude'函数吗?它允许你传递一个对象作为被转义部分的作用域和一个DOM替换函数。结帐这个SO线程http://stackoverflow.com/questions/13183005/what-exactly-do-you-do-with-the-transclude-function-and-the-clone-linking-functi – javaCity

+1

这是如何http:///jsfiddle.net/P8JMm/4/ –

+0

@NidhishKrishnan变量名没有被绑定,它应该在屏幕上显示“Transclude test”。该指令使用$ scope,而不仅仅是它的一部分。好主意,但。 – vtortola

回答

2

因此,此问题的基本答案是 - 您可以执行您想要执行的操作,但它比你想象的要复杂一点。要了解这里发生的情况,您需要了解有关角度范围的知识。范围本质上是一个包含视图可访问的数据的对象。有(至少)三种示波器在角度上运行:

  • 隔离 - 在这种情况下,角度基本上为指令创建了一个全新的范围。没有任何属性被复制。
  • 扩展 - 在这种情况下,您将从根作用域开始,但对其进行浅层复制。被改变的对象将在根范围内被改变,但基元不会被改变。
  • 共享 - 在这种情况下,您可以与根作用域共享部分甚至全部数据。

根据你上面的问题,你在这里做什么是扩展父范围 - 复制一个对象到新创建的子范围中具有特定名称的属性。获得此行为的方法是在transclude之前手动创建新的子范围。代码的两个关键的线要做到这一点是:

// create a "new" scope 
var childScope = scope.$new(); 

// extend using the model binding provided 
angular.extend(childScope, scope[iAttr.myModel]); 

在你的指令的情况下,这看起来像:

.directive('myDirectiveWrapper', ['$compile', function ($compile) { 
    return { 
     transclude: true, 
     restrict: 'E', 
     compile: function (element, attrs, transclude) { 
      var contents = element.contents().remove(); 
      var compiledContents; 
      return function(scope, iElement, iAttr) { 
       var childScope = scope.$new(); 
       angular.extend(childScope, scope[iAttr.myModel]); 
       if (!compiledContents) { 
        compiledContents = $compile(contents, transclude); 
       } 
       compiledContents(childScope, function(clone, childScope) { 
         iElement.append(clone); 
       }); 
      }; 
     }, 
     template: "<div><h3>{{ name }}</h6><a class='back'>Back</a><div ng-transclude class='list'></div><a class='next'>Next</a>" 
    } 
}]) 

现在,您可以指定要作为“样板”的任何变量子范围,然后你可以直接访问你的transcluded代码的内容!

查阅小提琴:http://jsfiddle.net/P8JMm/7/

编辑:只是为了好玩,我创建了这个指令较为复杂的情况下:http://jsfiddle.net/P8JMm/9/

- 角部位也有一些非常好的更好地理解范围的资源。 See here

+1

当你不能'angular.extend'时,它会复制mymodel上的所有原语,这样就会中断双向绑定。 – rob

+0

@rob你是对的。为此,OP需要分配一个特定的变量,或者需要使用对象进行双向绑定而不是基元。不幸的是,无论采用何种方法都存在限制... –

1

如果您希望双向绑定工作,只需在您的指令范围上创建一个变量,而不是直接将mymodel应用于指令范围,就会容易得多。

HTML

<div ng-app="myapp"> 
    <div ng-controller="myController"> 
     <my-directive-wrapper model="mymodel"> 
      <my-directive-inner ng-repeat="item in mymodel.items" /> 
     </my-directive-wrapper> 
    </div> 
</div> 

指令

.directive("myDirectiveWrapper", function(){ 
    return { 
     scope: { 
      model: '='   
     }, 
     restrict: 'E', 
     transclude: true, 
     link: function(scope, element, attrs, controller) { 

     }, 
     template: "<div><h3>{{ model.name }}</h6><a class='back'>Back</a><div ng-transclude class='list'></div><a class='next'>Next</a>" 
    } 
}) 

http://jsfiddle.net/kQ4TV/

如果你不关心双向绑定我想你可以做这样的事情,但我不会不推荐:

.directive("myDirectiveWrapper", function(){ 
    return { 
     scope: { 
      model: '='   
     }, 
     restrict: 'E', 
     transclude: true, 
     link: function(scope, element, attrs, controller) { 
      angular.extend(scope, scope.model); 
     }, 
     template: "<div><h3>{{ name }}</h6><a class='back'>Back</a><div ng-transclude class='list'></div><a class='next'>Next</a>" 
    } 
}) 

http://jsfiddle.net/vWftR/

下面是当第二种方法可能会导致问题的例子。请注意,当您在输入字段中输入内容时,它将更改指令的名称,但不会更改外部范围中的名称:http://jsfiddle.net/r5JeJ/

+0

您的解决方案也不错,但@ drew_w的解决方案更接近我想要的,尽管存在绑定问题。 – vtortola