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

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

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

我的指令包装了 jqueryUI 自动完成并在其 select 事件上调用 ngModel.$setViewValue(selectedItem.id)。自动完成允许用户通过单击鼠标或按键盘上的 enter select 项。

如果建议的项目是:

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

我希望在 selecting 之后,ngModel 值将是 selected 项目的 id - 1000.

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

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

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

有关

正如上面link中指出的,change事件只在Firefox中触发,在Chrome中不会触发。因此,在您的情况下,在外部单击时再次触发 $setViewValue 并且模型值设置为 "Apple".

自动完成 jquery ui 小部件有更改回调。要处理这两个 case/browsers 可能您必须在此回调中再次显式设置视图值(并且有效)。

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

    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();
    }

我和 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>

这是一个有效的 Plunker

使用这个建议的解决方案,您可以将 ngModelController 和 jQueryUI 模态相互作用隔离到它自己的自定义元素中,并且它不会干扰 "normal" <input> 标记,您也不会关注 jQueryUI 错误。

通过使用 <input> 标签作为嵌入元素,您仍然可以从大多数 Angular 输入内容中受益,例如 ng-disabledplaceholder 等...

好的,我想我做到了。该解决方案基于 并使用本地模型来保留选定的数据。

当用户选择某些东西时,本地模型被设置为选中的 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 中的问题!),它检查该值是否与当前本地模型的文本值相同,如果不相同,它会更改本地模型到这个值。这意味着如果解析器函数是 运行 by change 事件值将与文本值相同,因此 $modelValue 不会更改,但是如果用户键入某些内容,模型将更新为键入的值(变成 String)。

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

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

(在格式化程序函数中,我 return 来了,所以 $viewValue 暂时是一个 Object 但随后在 $render 方法中我调用 $setViewValue 到正确设置 $viewValue$modelValue,所以它变成了 String。我听说 $setViewValue$render 方法中不应该是 运行,但我不知道当有东西来自外部时,我看不到其他设置正确 $modelValue 的方法。