Angular $ compile导致Datepicker弹出框略有错误

时间:2017-04-27 20:30:35

标签: angularjs angular-ui-datepicker

更新1:添加更多细节 更新2:添加了plunker代码以重现问题。请参阅以下链接 更新3:我在github上得到了角度团队的回复。检查here
更新4:根据AngularJS团队的要求在github上发布更新。此外,我之前添加的建议解决方案证明它正在创建一个新的视觉问题。请参阅以下详细信息 更新5:从Angular团队获得另一个反馈。我认为我们80%接近找到解决方案。

我在我的项目中实现了Angular UI datepicker,过了一段时间,我注意到当我打开弹出框并点击月份更改为另一个月时,另一个弹出窗口显示在现有弹出窗口的顶部。请参阅下面的快照以获取更多详细信息:

enter image description here

最初,当我点击月份时,当前弹出窗口应该消失,另一个弹出窗口应显示以选择新月份。

然而,当我在月份点击两次(下面以黄色突出显示)时,弹出窗口将消失,并且它将正常工作。但是,在我选择一个日期之后,问题就会回来。

我使用的是与角度ui datepicker官方演示网站相同的示例代码。在下面找到相关网站和plunker代码:

http://angular-ui.github.io/bootstrap/versioned-docs/1.3.3/#/datepickerPopup http://plnkr.co/edit/lEgJ9eC9SzBWsgVhhQkq?p=preview

我的代码与上面的plunker示例中的代码完全相同。唯一的区别是我使用$compile服务动态添加所需的字段验证。

经过大量的故障排除后,发现$compile()服务导致了这种行为。我做了一些研究,发现$compile服务还会在下拉列表或select元素中导致重复的项目。我使用了提议的解决方法并且它有效。请参阅以下代码:

$compile(child)(elmScope, function (clone) {
    angular.element(child).after(clone);     
    angular.element(child).remove();
});

我使用$compile的原因是使用此方法here从DB向元素添加动态验证规则。

我收到reply from angular team on github后,发现他们suggested this fix

.directive('checkIfRequired', ['$compile', function ($compile) {
    return {
      priority: 1000,
      terminal: true,
        /*require: '?ngModel',*/
        //JIRA: NE-2535 - inject the current 'ngForm' controller with this directive
        //      This is required to add automatice error message when the Form Field is invalid.
        require: '?^form',
      link: function (scope, el, attrs, ngForm) {
        el.removeAttr('check-if-required');
        el.attr('ng-required', 'true');
        $compile(el, 1000)(scope);
      }
    };
}]);

当我尝试应用建议的修复程序时,我收到大量错误。似乎在使用terminal=true时,'ng-init'下的内部元素中的所有代码都没有被执行。我注意到许多范围变量变为“未定义”。

这是尝试找到解决方案的final update on github。请参阅以下代码:

app.directive('checkIfRequired', ['$compile', '$timeout', function ($compile, $timeout) {
  return {
    priority: 2000,
    terminal: true,
    /*link: function (scope, el, attrs) {
      el.removeAttr('check-if-required');
      var children = $(':input', el);
      children.each(function(key, child) {
        if (child && child.id === 'test_me') {
            angular.element(child).attr('ng-required', 'true');
                }
      });
      $compile(children)(scope);
    },*/
    compile: function (el, attrs) {
        el.removeAttr('check-if-required');
        var children = $(':input', el);
        children.each(function(key, child) {
            if (child && child.id === 'test_me') {
                angular.element(child).attr('ng-required', 'true');
                    }
            });
        var compiled = $compile(children, null, 2000);
        return function( scope ) {
            compiled( scope );
        };
    }
  };
}]);

感谢您在我的项目中以正确的方式应用修复程序的帮助。 See the related parts of the code here

之前我发布了一个解决方案,但之后我删除了它。我后来注意到它引起了一种有趣的堆叠效应,这比我之前报道的更糟糕。

以下是github上的更新,其中包含完整的详细信息:

https://github.com/angular/angular.js/issues/15956#issuecomment-300324812

当我收到AngularJS团队的回复时,会不断更新。

在我找到永久性解决方案之前,请耐心等待。

旧提出的解决方案:

为避免此问题,请勿使用$compile服务,或者必须使用此代码示例来解决问题:

$compile(child)(elmScope, function (clone) {
    angular.element(child).after(clone);     
    angular.element(child).remove();
});

希望这对面临同样问题的其他人有价值。

2 个答案:

答案 0 :(得分:0)

这是为了进行修正。之前添加的解决方案没有按预期工作。

有关完整的详细信息,请查看github上的更新:

https://github.com/angular/angular.js/issues/15956#issuecomment-300324812

现在我可以提出一种不同的方法来使用动态验证规则,这些规则存储在DB上并加载AngularJS表单。

请参考此处的代码部分作为此建议解决方案的基础:

Angular dynamic required validation of group of fields with field highlight if invalid

而不是在指令$compile中使用check-if-required,而是为字段scope创建一个新的child.id变量:

if (scope.isFieldRequired(child.id)) {
    //angular.element(child).attr('ng-required', "true");
    //$compile(child)(elmScope);
    scope.RootController.ngRequired[child.id] = true;
} else {
    scope.RootController.ngRequired[child.id] = false;
}

定义元素时,请确保它与以下内容类似:

<input id="customer_id" name="customer_id" ng-model="customer_id" ng-required="RootController.ngRequired.customer_id"/>

基本上,所有验证规则必须链接到范围变量,该变量必须在根角度控制器RootController'. For simplicity, the required validation will be under RootController.ngRequired`中定义。

有必要在最顶层的外部控制器上使用RootController表示法定义controller as。否则,上述解决方案将无法正常工作。以下是根控制器的示例元素定义:

<body ng-app="myApp" ng-controller="formMainController as RootController">
...
</body>

如果我没有得到Angular团队关于因使用$compile而发现的问题的满意答复,我将转而采用上述方法。我几乎可以肯定上述方法可行。

如果您有任何反馈意见,请执行此操作。

塔雷克

答案 1 :(得分:0)

这是我从github的AngularJS团队获得的另一个解决方案。 Click here to see the answer at github

请参考此处的代码部分作为此建议解决方案的基础:

Angular dynamic required validation of group of fields with field highlight if invalid

基本上,您可以使用以下代码示例来避免在指令链接函数中使用$compile时出现双重编译问题:

app.directive('checkIfRequired', ['$compile', '$timeout', function ($compile, $timeout) {
    return {
        priority: 2000,
        terminal: true,
        link: function (scope, el, attrs) {
            el.removeAttr('check-if-required');
            $(':input', el).each(function(key, child) {
                if (child && child.id === 'test_me') {
                    angular.element(child).attr('ng-required', 'true');
                }
            });
            $compile(el, null, 2000)(scope);
        }
    };
}]);

这是必需的,你有一个循环遍历与指令check-if-required相关的父元素。