select(name).option(value)选择错误的选项?

时间:2013-12-02 14:25:09

标签: angularjs karma-runner angularjs-e2e

我有一个奇怪的问题。当我尝试选择日期时,select()函数选择了错误的选项。

伍 - 型号:

days = ['01', '02', '03', '04', ..., '31'];

标记:

<select ng-model="day" id="day" name="day" ng-options="day for day in days">
    <option value="" disabled="disabled"></option>
</select>
e2e测试:

it('should select correct date', function () {
   select('day').option('30');
   expect(element('#day option:selected').text()).toEqual('30');
});

所以我的问题是:为什么select('day').option('30')选择select('day').option('02')按预期工作的第31天?


所以我想知道它的目标价值是什么,因为它在其他地方运作良好。 文档很稀疏,所以它是一个功能或一个bug:)

我认为正在进行的是select()尝试以某种顺序选择值。 I.E.它首先尝试按值选择选项,然后尝试按模型值选择它,或沿着那些线选择它。

2 个答案:

答案 0 :(得分:2)

所以我查看了select函数的实现。

来自github

/**
 * Usage:
 *    select(name).option('value') select one option
 *    select(name).options('value1', 'value2', ...) select options from a multi select
 */
angular.scenario.dsl('select', function() {
  var chain = {};

  chain.option = function(value) {
    return this.addFutureAction("select '" + this.name + "' option '" + value + "'",
      function($window, $document, done) {
        var select = $document.elements('select[ng\\:model="$1"]', this.name);
        var option = select.find('option[value="' + value + '"]');
        if (option.length) {
          select.val(value);
        } else {
          option = select.find('option').filter(function(){
            return _jQuery(this).text() === value;
          });
          if (!option.length) {
            option = select.find('option:contains("' + value + '")');
          }
          if (option.length) {
            select.val(option.val());
          } else {
              return done("option '" + value + "' not found");
          }
        }
        select.trigger('change');
        done();
    });
  };

  return function(name) {
    this.name = name;
    return chain;
  };
});

所以“问题”是select尝试从DOM元素中的值中选择,即<option value="THIS VALUE">然后它会尝试通过显示的内容找到值<option>THIS VALUE</option>然后它尝试对值进行contains。它实际上并不在任何时候使用模型值。

所以select('day').option('02')有效,因为它与显示的文本匹配,其中select('day').option('30')与具有偏移量的选项值匹配。

请记住生成的HTML是:

<select ng-model="day" id="day" name="day" ng-options="day for day in days">
  <option value="" disabled="disabled"></option>
  <option value="0">01</option> <-- note it starts at 0 not 1
  <option value="1">02</option> <-- select('day').option('02') matches display text '02' as no value 02 exists.
  <option value="2">03</option>
  <option value="29">30</option>
  <option value="30">31</option> <-- select('day').option('30') matches value 30 before display text 30 with value 29.
</select>

要“解决”此问题,需要创建一个新功能(或更改现有功能)。

angular.scenario.dsl('selectModel', function() {
    var chain = {};

    chain.option = function(value) {
        return this.addFutureAction("select '" + this.name + "' option '" + value + "'",
            function($window, $document, done) {
                var $ = $window.$; // jQuery inside the iframe
                var select = $document.elements('select[ng\\:model="$1"]', this.name);
                var option = select.find('option').filter(function(){
                    return $(this).text() === value;
                });
                if (!option.length) {
                    option = select.find('option:contains("' + value + '")');
                }
                if (option.length) {
                    select.val(option.val());
                } else {
                    return done("option '" + value + "' not found");
                }

                select.trigger('change');
                done();
            });
    };

    return function(name) {
        this.name = name;
        return chain;
    };
});

这就是诀窍。

答案 1 :(得分:1)

当源模型是列表时,Angular创建的option标记将 indices 作为值:

$scope.listItems = [
    "day 1",
    "day 2",
    "day 3",
    "day 4",
    "day 5"
];

<select ng-model="listItem" ng-options="item for item in listItems"></select>

创建以下HTML:

<select ng-options="item for item in listItems" ng-model="listItem" 
class="ng-pristine ng-valid">
    <option value="?" selected="selected"></option>
    <option value="0">day 1</option>
    <option value="1">day 2</option>
    <option value="2">day 3</option>
    <option value="3">day 4</option>
    <option value="4">day 5</option>
</select>

虽然option标记的值为,但源模型是地图时:

$scope.objItems = {
    "day 1":"1",
    "day 2":"2",
    "day 3":"3",
    "day 4":"4",
    "day 5":"5"
};

<select ng-model="objItem2" ng-options="value as key for (key, value) 
in objItems"></select>

创建:

<select ng-options="value as key for (key, value) in objItems" 
ng-model="objItem2" class="ng-valid ng-dirty">
    <option value="day 1" selected="selected">day 1</option>
    <option value="day 2">day 2</option>
    <option value="day 3">day 3</option>
    <option value="day 4">day 4</option>
    <option value="day 5">day 5</option>
</select>

然而,这从来不会产生问题,因为当用户选择一个选项时,Angular会查看源内的索引位置并将该值分配给模型。

我对e2e测试不是很熟悉,但我认为每次选择一个选项时你获得不同值的原因是你试图访问存储在option标签中的值,而不是Angular有什么值存储在模型中。

Fiddle