【问题标题】:Change in one-way bound directive attribute during test not updating directive scope测试期间单向绑定指令属性的更改不更新指令范围
【发布时间】:2014-08-07 19:34:17
【问题描述】:

我目前正在尝试使用 jasmine 测试 angular 指令。

下面是被测指令的简化示例:

angular.module('app', []).directive('testDirective', [
  function() {
    return {
      restrict:'A',
      scope: {
        myAttr: '@'
      },
      link: function(scope, element, attrs) {
        element.html(scope.myAttr);

        scope.$watch('myAttr', function(n, o) {
          if(n !== o) {
            element.html(n);
          }
        });
      }
    };
  }
]);

使用隔离范围,它将属性 data-my-attr 的值单向绑定到 myAttr 的范围值,然后观察 myAttr 的值是否有任何变化,此时它会更新显示的元素。

再一次,为了清晰起见,这段代码已经过大量修改,所以请原谅这个高度人为的例子。

测试是编译一个包含这个指令的元素,验证元素的初始html设置是否正确(是)然后改变属性的值并期望元素发生相应的变化(不是)。这是测试代码的简化版本。

it('fires myAttr watch', function() {
    var body = angular.element('body');
    var template = angular.element('<div id="testid" data-test-directive data-my-attr="initialValue"/>');

    body.append([template]);
    compile(template)(scope);
    scope.$digest();

    expect(angular.element('#testid').text()).toEqual('initialValue');

    template.attr('data-my-attr', 'newValue');

    scope.$digest();

    expect(angular.element('#testid').text()).toEqual('newValue');
  });

虽然该指令在生产中使用时按预期工作,但在测试中,更新属性的值似乎不会导致相应的范围值更新,从而导致手表永远不会触发。为什么作用域上的值没有更新,为什么这只发生在测试中?

这是一个演示问题的 plunkr http://plnkr.co/edit/MmS1X2zrPZyGSUIVUsDg?p=preview

有什么想法吗?

【问题讨论】:

  • 在调用范围摘要之前更新值后需要添加compile(template)(scope);
  • 我注意到这样做确实可以解决问题,但我想我不确定这是正确的解决方案还是碰巧有效的破解方法。我认为 $compile 是一次性交易,旨在识别指令并将它们适当地绑定到范围,这是否意味着每当我进入 html 并更改某些东西时,在幕后重新编译所有内容?此外,如果是这种情况,是什么触发了测试中没有发生的生产代码中的重新编译(在哪里可以正常工作)?
  • 在这种情况下你的测试是错误的。在您的测试中,您正在修改元素的属性,该属性不会被摘要评估。你需要重新编译。是的,即使在应用程序中,如果您通过手动 DOM 操作在 html 中手动更改某些内容(例如:绑定模型、更改指令、在元素上动态添加指令),您也需要重新编译它。那么你是如何在你的生产代码中做到这一点的......
  • 您是否在生产代码中这样做? plnkr.co/edit/BwDtXZRMtMWAcvMSL8vZ?p=preview您还需要发布您的实际代码..
  • 啊,很好,你是对的。我没有注意到指令的实际用法是将其绑定到范围变量(正如您的 plunkr 建议的那样),这解释了为什么简单的摘要循环足以更新值。感谢您的帮助。

标签: javascript angularjs testing jasmine directive


【解决方案1】:

您需要在使用新文本更新元素的属性后编译该元素。在您的情况下,您根本不需要scope.digest(因为您并没有真正将文本与{{modelvalue}} 绑定)。原因是您没有附加绑定变量,而是将计划文本分配给属性,因此您需要重新编译元素。

所以你只需要这样做:-

template.attr('data-my-attr', 'newValue');
compile(template)(scope);

Demo

你的测试是这样的吗(使用指令时使用角度绑定):-

var body = angular.element('body');
rootScope.initialValue ="initialValue";
var template = angular.element('<div id="testid" data-test-directive data-my-attr="{{initialValue}}"/>');

body.append([template]);
compile(template)(scope);
scope.$digest();
expect(angular.element('#testid').text()).toEqual('initialValue');

rootScope.initialValue = "newValue";
scope.$digest();

expect(angular.element('#testid').text()).toEqual('newValue');

它可以通过调用 $digest 来工作。

Demo

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-11-29
    • 2017-06-08
    • 2013-10-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多