ngModel——如何处理它在不同浏览器中的不同行为

ngModel - How to deal with its different behavior in different browsers?

本文关键字:浏览器 何处理 处理 ngModel      更新时间:2023-09-26

我正在尝试处理ngModel在不同浏览器中的不同行为。

我的指令包装jqueryUI自动完成,并在其select事件中调用ngModel.$setViewValue(selectedItem.id)。自动补全允许用户通过鼠标点击或按键盘上的enter键来选择项目。

如果建议项目为:

{
  "name": "Apple",
  "id": "1000"
}

我希望在选择它之后,ngModel的值将是被选中项目的id - 1000

  • 在Chrome中它工作正常-它设置$viewValue$modelValue正确($modelValue=1000)。

  • 在Firefox中它设置模型为Chrome ($modelValue=1000),但当我点击其他地方-使模糊(然后浏览器可能触发change事件),模型改变,它变得与可见输入值($modelValue='Apple')相同。

  • 在IE 11中,只有当我用鼠标点击选择项目时才会设置模型正确。如果我按下enter选择它,模型变成可见输入值($modelValue='Apple')

这是plunkr: http://plnkr.co/edit/o2Jkgprf8EakGqnpu22Y?p=preview

我想在每个浏览器中都达到相同的行为。如何处理这些问题?

这似乎与http://bugs.jqueryui.com/ticket/8878

有关

正如上面的链接所指出的,更改事件仅在Firefox中触发,而在Chrome中不会触发。所以在你的例子中,$setViewValue在点击外部时再次被触发,模型值被设置为"Apple"。

有自动完成jquery ui小部件的更改回调。要处理这两种情况/浏览器可能是你必须显式地设置视图值在这个回调(它工作)。

http://plnkr.co/edit/GFxhzwieBJTSL8zjSPSZ?p =

预览
    link: function(scope, elem, attrs, ngModel) {
      elem.on('change', function(){
      // This will not be printed in Chrome and only on firefox
      console.log('change');
    });

    select: function(event, ui) {
      ngModel.$setViewValue(ui.item.data.id);
      scope.$apply();
    },
    // To handle firefox browser were change event is triggered
    // when clicked outside/blur
    change: function(event, ui) {
      ngModel.$setViewValue(ui.item.data.id);
      scope.$apply();
    }

好了,我想我做到了。解决方案是基于Yoshi的评论,它使用本地模型来保存选定的数据。

当用户选择某项时,本地模型设置为所选Object, $viewValue设置为所选项的文本值。解析器将局部模型的id属性设置为$modelValue

select: function(event, ui) {
  if(ui.item && ui.item.data){
    model = ui.item.data
    ngModel.$setViewValue(model.name);
    scope.$apply();
  }
}
ngModel.$parsers.push(function(value) {
  if(_.isObject(model) && value!==model.name){
    model = value;
    return model;
  }
  return model.id;
});

解析器函数还做一件重要的事情。因为它是在用户输入内容或change事件时运行的(这是firefox中的问题!),它检查该值是否与当前本地模型的文本值相同,如果不相同,它将本地模型更改为此值。这意味着如果解析器函数由change运行,事件值将与文本值相同,因此$modelValue不会改变,但如果用户类型模型更新为类型值(它变成String)。

Validator函数检查本地模型是否为Object。如果不是,则表示该字段无效,因此默认情况下其$modelValue将消失。

这是plunkr:http://plnkr.co/edit/2ZkXFvgLIwDljfJoyeJ1?p=preview

(在formatter函数中,我返回来的是什么,因此$viewValue暂时是Object,但随后在$render方法中,我调用$setViewValue来正确设置$viewValue$modelValue,因此它成为String。我听说$setViewValue不应该在$render方法中运行,但我没有看到其他方法来设置正确的$modelValue当某些东西来自外部时)。

我和ngModelController$setViewValue有过类似的战斗。

最后我寻找其他解决方案。我发现一种非常有效的方法是创建一个新元素作为组件指令,其中包括输入标签作为一个跨包元素。

app.directive('fruitAutocomplete', function($http) {
  return {
    restrict: 'E',
    require: 'ngModel',
    transclude: true,
    template: '<ng-transclude></ng-transclude>',
    link: function(scope, elem, attrs, ngModelController) {
      var $input = elem.find('input');
      $input.autocomplete({
        ...
      });
    }
  }
})

在HTML中:

<fruit-autocomplete name="fruit" ng-model="model.fruit">
  <input ng-disabled="inputDisabled" placeholder="input fruit"/>
</fruit-autocomplete>

这是一个工作的活塞

有了这个建议的解决方案,你可以将ngModelController和jQueryUI模态相互作用隔离到它自己的自定义元素,它不会干扰"正常"的<input>标签,你也不会担心jQueryUI的错误。

通过使用<input>标签作为跨包元素,你仍然可以从大多数Angular的输入中获益,比如ng-disabledplaceholder等…