【问题标题】:How to get two way data binding in a directive *without* an isolate scope?如何在*没有*隔离范围的指令中获得双向数据绑定?
【发布时间】:2014-07-11 07:35:50
【问题描述】:

在指令中使用scope: { ... } 会引入一个隔离作用域,该作用域在原型上不会从其父作用域继承。但我一直使用它是出于不同的原因:一种通过两种方式数据绑定来声明 HTML 属性的便捷方式:

scope: {
    attr1: '=',
    attr2: '?='
}

要获得非隔离作用域,您必须使用scope: true,它不提供声明此类属性的机会。我现在发现自己需要一个具有非隔离范围的指令,但具有两种方式的绑定。实现这一目标的最佳方法是什么?


示例:outer-directive 看来,我的用例是这样的:

<div ng-repeat="e in element">
    <inner-directive two-way-attr="e.value"></inner-directive>
</div>

inner-directiveouter-directive 在同一个模块中。它不需要用隔离范围封装。事实上,我需要将$scope 继承用于其他目的,因此隔离范围不是一个选项。只是用一个HTML属性来建立这种双向通信是极其方便的。

【问题讨论】:

  • 当你使用scope: true时,你可以直接访问scope,那有什么问题吗? scope:true 将创建一个子作用域,该作用域在原型上从其父作用域继承,因此您将能够访问在指令中定义在父作用域上的值。
  • @Satpal:我知道这一点,但就我的目的而言,这是建立沟通的一种相当不雅的方式。实际上,您刚刚共享的链接说:“两个控制器通过 $scope 继承共享信息被认为是一种不好的形式”。 --- 我将在我的问题中添加一个用例示例。
  • @Satpal 有时需要类似组件的绑定设计(例如,指令通信的接口)用于与其他作用域一起使用的指令:true 指令,但由于无法使用隔离作用域新/隔离范围的非法组合。

标签: javascript angularjs angularjs-directive angularjs-scope


【解决方案1】:

pixelbits 的回答帮助我解决了这个问题,但作为对我最初问题的直接回答,它似乎过于复杂。研究了一下,解决方法真的很简单。

使用具有如下隔离范围的指令:

scope: { model: '=myModel' },
link: function(scope, element, attr) {
    //...
}

以下是等价的,只是作用域不是隔离的:

scope: true,
link: function(scope, element, attr) {
    scope.model = scope.$parent.$eval(attr.myModel);
    //...
}

在此处查看工作示例:http://jsfiddle.net/mhelvens/SZ55R/1/

【讨论】:

  • 要使用非对象模型,您应该使用解析:attr="{{model}}" 或 attr="{{'string'}}"
  • 为什么不在这里使用 $parse 呢? $parse(attr.myModel)(this.$scope)
【解决方案2】:

Working Demo Here

same 指令中可以同时具有非隔离作用域和隔离作用域。例如,如果您混合了非隔离模板(意味着它们不应通过范围继承查找绑定)和隔离模板(它们应仅在其自己的范围内查找绑定)和它们都在同一个指令中定义。

为了设置隔离范围和非隔离范围,您可以执行以下操作:

  1. 在您的指令定义中,指定scope=true
  2. 在您的链接函数中,根据范围参数编译和链接您的模板。执行此操作时,将针对非隔离范围评估绑定(这意味着它通过原型范围继承解析绑定)。

      link: function(scope, element, attr) {
    
        // this template should look for 'model' using scope inheritance
        var template2 = angular.element('<div> Prototypical Scope: {{ model }}</div>');
    
        // add the template to the DOM
        element.append(template2);
    
        // compile and link the template against the prototypical scope
        $compile(template2)(scope);
      }
    

    原型范围继承的优点是您不必显式地将绑定导入指令的当前范围。只要它是在当前作用域或继承链更高的任何作用域(一直到根作用域)中定义的,角度运行时就能够解决它。

  3. 在同一个链接函数中,使用scope.$new(true) 定义一个隔离范围。您可以通过将模型导入隔离范围来建立模型的双向绑定 - isolatedScope.model = scope.$eval(attr.model)

     link: function(scope, element, attr) {
    
        // this template should look for 'model' in the current isolated scope only
        var template = angular.element('<div>Isolate Scope: {{model}}</div>');
    
        // create an isolate scope
        var isolatedScope = scope.$new(true);
    
        // import the model from the parent scope into your isolated scope. This establishes the two-way binding.            
        isolatedScope.model = scope.$eval(attr.model);
    
        // add the template to the DOM
        element.append(template);
    
        // compile and link the template against the isolate scope
        $compile(template)(isolatedScope);
    
    }
    

    隔离作用域的优点是存在的任何绑定(即在作用域内)都是您显式导入的绑定。将此与非隔离范围进行对比 - 其中绑定不需要在当前范围上显式定义 - 它可以从链上更高的任何范围继承。

【讨论】:

  • 谢谢!这看起来很有希望。我今天晚些时候试试。
  • 谢谢,您的回答帮助很大。但我的问题的解决方案实际上似乎要简单得多。关键是你使用了scope.$eval。老实说,我不确定您的其余代码要完成什么,但我想知道。 --- 在这里查看我修改后的解决方案:jsfiddle.net/mhelvens/SZ55R/1
  • $eval 这里建立的不是真正的双重绑定,而只是复制对象引用,因此它不适用于非对象值(标量,如字符串、数字、对象引用)
【解决方案3】:

这是我写的。你可以这样使用它:

twowaybinder.attach($scope, $attrs.isDeactivated, 'isDeactivated');

.factory('twowaybinder', function ($parse) {
  function twoWayBind($scope, remote, local){
    var remoteSetter = $parse(remote).assign;
    var localSetter = $parse(local).assign;

    $scope.$parent.$watch(remote, function (value) {
      localSetter($scope, value);
    });

    $scope.$watch(local, function (value) {
      remoteSetter($scope, value);
    });
  }

  return {
    attach : twoWayBind
  };
});

它将为您提供真正的来自范围值的双向绑定。注意我不认为 $scope.$parent 是必要的,因为在继承或没有范围的情况下,任何表达式都应该在当前范围上解析。您只需要在隔离范围内调用 $parent 在这种情况下您不会使用它,您将使用隔离范围配置。

【讨论】:

  • 你测试过这个吗?我认为这是在正确的轨道上,但正如所写的那样,它会导致一个无限的摘要循环。如果您查看angular source for $compile - 搜索“case: '='”,您会看到他们引入了一些额外的逻辑来防止这种情况。不过,恕我直言,该组的最佳答案。
  • 是的,我正在使用它,但到目前为止只有一个实例。我还没有看到任何无限循环类型的行为。我明白你的意思,如果远程值发生变化,那么观察者将设置本地值,这将触发本地观察者并设置远程值等等。然而,这似乎并没有发生。也许 $parse 中的某些东西正在阻止它。我会仔细看看,谢谢。
  • 这个解决方案比 $eval 解决方案更好,因为它也适用于原始值和对象引用。
  • 请注意,这不再需要。在 1.5 中,我认为 bindToController 现在以与作用域大致相同的方式接受哈希,并将值绑定到控制器,完成两种方式的绑定。您可以将范围设置为 false 到 true。
【解决方案4】:

你可以使用两个指令 如果 gg 是一个对象,那么 "=" 指向一个内存位置!

angular.module('mymodule', []).directive('a', function($parse, $modal) {
return {
    restrict : 'A',
    scope : {
        gg : "="
    },
    require : "b",
    link : function(scope, element, attrs, bCtrl) {
        scope.$watch('gg',function(gg){
            bCtrl.setset(scope.gg);
        }
    }
}
});

angular.module('mymodule').directive('b', function($parse, $modal) {

return {
    restrict : 'A',
    /*
     * scope : { showWarn : "=" },
     */
    controller : function($scope) {
        $scope.bb = {};

        this.setset = function(nn) {

            $scope.bb=nn;
        };

    }
});

【讨论】:

    猜你喜欢
    • 2014-04-28
    • 2015-01-04
    • 2017-09-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-15
    • 1970-01-01
    相关资源
    最近更新 更多