Angular $compile 导致 Datepicker 弹出框出现轻微异常
Angular $compile causes Datepicker popup box to misbehave slightly
更新 1:添加更多详细信息。
更新 2:添加了 plunker 代码以重现问题。请参阅下面的 link。
更新 3:我在 github 上收到了 angular 团队的回复。检查一下 here.
更新 4:根据 AngularJS 团队的要求,在 github 上发布更新。此外,我之前添加的建议解决方案结果证明它正在创建一个新的视觉问题。请参阅下面的详细信息。
更新 5:从 Angular 团队获得另一个反馈。我认为我们已经接近找到解决方案的 80%。
我在我的项目中实现了Angular UI datepicker,过了一会儿,我注意到当我打开弹出框,点击月份更改为另一个月份时,另一个弹出框window 显示在现有弹出窗口的顶部。请参阅下面的快照以获取更多详细信息:
最初,当我点击月份时,当前弹出窗口应该消失,另一个弹出窗口应该显示 select 新的月份。
但是,当我在月份(下面以黄色突出显示)上单击两次时,弹出窗口将消失,并且可以正常工作。但是,在我 select 约会之后,问题又会回来。
我正在使用来自 angular ui datepicker 官方演示网站的相同示例代码。在下面找到相关网站和plunker代码:
http://angular-ui.github.io/bootstrap/versioned-docs/1.3.3/#/datepickerPopup
http://plnkr.co/edit/lEgJ9eC9SzBWsgVhhQkq?p=preview
我的代码与上面的 plunker 示例中的代码完全相同。唯一的区别是我使用 $compile
服务来动态添加 required 字段验证。
经过广泛的故障排除后,发现 $compile()
服务导致了此行为。我做了一些研究,发现 $compile
服务也会导致下拉列表或 select
元素中出现重复项。我使用了建议的解决方法并且有效。请参阅下面的代码:
$compile(child)(elmScope, function (clone) {
angular.element(child).after(clone);
angular.element(child).remove();
});
我使用 $compile
的原因是使用这种方法从数据库向元素添加动态验证规则 here。
我得到后reply from angular team on github, I found that they 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' 下的内部元素中的所有代码都没有执行。我注意到许多范围变量变成 "undefined".
这是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();
});
希望这对面临同样问题的其他人有用。
这是为了进行修正。之前添加的解决方案没有达到预期效果。
有关完整的详细信息,请查看 github 上的更新:
https://github.com/angular/angular.js/issues/15956#issuecomment-300324812
现在我可以提出一种不同的方法来使用动态验证规则,这些规则存储在数据库中并以 AngularJS 形式加载。
参考此处的代码部分作为此建议解决方案的基础:
Angular dynamic required validation of group of fields with field highlight if invalid
不要在指令 check-if-required
中使用 $compile
,而是为字段 child.id
创建一个新的 scope
变量,如下所示:
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"/>
基本上,所有验证规则都必须链接到必须在根 Angular 控制器中定义的范围变量 RootController'. For simplicity, the required validation will be under
RootController.ngRequired`。
有必要在最外层控制器使用controller as
符号定义RootController
。否则,上述解决方案将不起作用。以下是根控制器的示例元素定义:
<body ng-app="myApp" ng-controller="formMainController as RootController">
...
</body>
如果Angular团队对使用$compile
发现的问题没有得到满意的答复,我将改用上述方法。我几乎可以肯定上述方法会奏效。
如果您有任何反馈,请提出。
塔雷克
这是我从 github 的 AngularJS 团队得到的另一个解决方案。 Click here to see the answer at github.
参考此处的代码部分作为此建议解决方案的基础:
Angular dynamic required validation of group of fields with field highlight if invalid
基本上,您可以使用以下代码示例来避免在指令 link 函数中使用 $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
。
更新 1:添加更多详细信息。
更新 2:添加了 plunker 代码以重现问题。请参阅下面的 link。
更新 3:我在 github 上收到了 angular 团队的回复。检查一下 here.
更新 4:根据 AngularJS 团队的要求,在 github 上发布更新。此外,我之前添加的建议解决方案结果证明它正在创建一个新的视觉问题。请参阅下面的详细信息。
更新 5:从 Angular 团队获得另一个反馈。我认为我们已经接近找到解决方案的 80%。
我在我的项目中实现了Angular UI datepicker,过了一会儿,我注意到当我打开弹出框,点击月份更改为另一个月份时,另一个弹出框window 显示在现有弹出窗口的顶部。请参阅下面的快照以获取更多详细信息:
最初,当我点击月份时,当前弹出窗口应该消失,另一个弹出窗口应该显示 select 新的月份。
但是,当我在月份(下面以黄色突出显示)上单击两次时,弹出窗口将消失,并且可以正常工作。但是,在我 select 约会之后,问题又会回来。
我正在使用来自 angular ui datepicker 官方演示网站的相同示例代码。在下面找到相关网站和plunker代码:
http://angular-ui.github.io/bootstrap/versioned-docs/1.3.3/#/datepickerPopup http://plnkr.co/edit/lEgJ9eC9SzBWsgVhhQkq?p=preview
我的代码与上面的 plunker 示例中的代码完全相同。唯一的区别是我使用 $compile
服务来动态添加 required 字段验证。
经过广泛的故障排除后,发现 $compile()
服务导致了此行为。我做了一些研究,发现 $compile
服务也会导致下拉列表或 select
元素中出现重复项。我使用了建议的解决方法并且有效。请参阅下面的代码:
$compile(child)(elmScope, function (clone) {
angular.element(child).after(clone);
angular.element(child).remove();
});
我使用 $compile
的原因是使用这种方法从数据库向元素添加动态验证规则 here。
我得到后reply from angular team on github, I found that they 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' 下的内部元素中的所有代码都没有执行。我注意到许多范围变量变成 "undefined".
这是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();
});
希望这对面临同样问题的其他人有用。
这是为了进行修正。之前添加的解决方案没有达到预期效果。
有关完整的详细信息,请查看 github 上的更新:
https://github.com/angular/angular.js/issues/15956#issuecomment-300324812
现在我可以提出一种不同的方法来使用动态验证规则,这些规则存储在数据库中并以 AngularJS 形式加载。
参考此处的代码部分作为此建议解决方案的基础:
Angular dynamic required validation of group of fields with field highlight if invalid
不要在指令 check-if-required
中使用 $compile
,而是为字段 child.id
创建一个新的 scope
变量,如下所示:
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"/>
基本上,所有验证规则都必须链接到必须在根 Angular 控制器中定义的范围变量 RootController'. For simplicity, the required validation will be under
RootController.ngRequired`。
有必要在最外层控制器使用controller as
符号定义RootController
。否则,上述解决方案将不起作用。以下是根控制器的示例元素定义:
<body ng-app="myApp" ng-controller="formMainController as RootController">
...
</body>
如果Angular团队对使用$compile
发现的问题没有得到满意的答复,我将改用上述方法。我几乎可以肯定上述方法会奏效。
如果您有任何反馈,请提出。
塔雷克
这是我从 github 的 AngularJS 团队得到的另一个解决方案。 Click here to see the answer at github.
参考此处的代码部分作为此建议解决方案的基础:
Angular dynamic required validation of group of fields with field highlight if invalid
基本上,您可以使用以下代码示例来避免在指令 link 函数中使用 $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
。