Angular Material: md-autocomplete - 如何在 Enter 事件中隐藏 md-autocomplete-suggestions?

Angular Material: md-autocomplete - how to hide md-autocomplete-suggestions on Enter event?

我有 md-autocomplete:

<md-autocomplete 
                         md-min-length="1"
                         ng-enter="presEnter();"
                         md-no-cache="true"                        
                         md-selected-item="selectedItem" 
                         md-search-text="searchText" 
                         md-items="item in querySearch(searchText)"
                         md-item-text="item.name" 
                         placeholder="Search for a vegetable">
          <span md-highlight-text="searchText">{{item.name}} :: {{item.type}}</span>
        </md-autocomplete>

使用指令:ng-enter.

我的目标:当用户按下 Enter 我想隐藏 md-autocomplete-suggestions 下拉列表

我从 HTML 知道我需要以某种方式调用:$mdAutocompleteCtrl.hidden = true; 但不知道如何在 Controller 中使用 $mdAutocompleteCtrl

我用谷歌搜索发现:

$timeout( function() { $scope.$$childHead.$mdAutocompleteCtrl.hidden = true; },100);

但没有 $mdAutocompleteCtrl(至少在我的 JS 中,仅在 HTML 中,我不知道它的范围)

我玩这个 example:输入 'a' 并在下拉菜单后按 Enter。

有什么想法吗?

$mdAutocompleteCtrl 在自动完成范围内作为 属性 放置。

首先,您需要访问自动完成元素。一种方法是在自动完成中添加一个 ID:

<md-autocomplete id='Auto'
                 md-min-length="1"
                 ng-enter="presEnter();"
                 md-no-cache="true"
                 md-selected-item="selectedItem"
                 md-search-text="searchText"
                 md-items="item in querySearch(searchText)"
                 md-item-text="item.name"
                 placeholder="Search for a vegetable">

然后您可以使用该元素来获取自动完成的内部范围。因为自动完成元素本身在您提供的范围内,所以您需要获取自动完成的子元素之一的范围。

$scope.presEnter = function(e){
    var autoChild = document.getElementById('Auto').firstElementChild;
    var el = angular.element(autoChild);
    el.scope().$mdAutocompleteCtrl.hidden = true;
};

这是一个工作示例:http://codepen.io/anon/pen/rVPZKN?editors=101

TLDR:触发隐藏的示例代码 http://codepen.io/anon/pen/mJvGzp?editors=101

问题:

首先,"Angular Way" 建议应避免在 Controller 中操纵指令。控制器本质上应该只是检索(通过服务等)并提供构建视图所需的数据;它通常应该避免关心这些视图是如何实现的(即它应该不知道将使用什么指令)。这样做有多种充分的理由,其中之一可能是当您想要修改视图时,它会让生活变得更加轻松,例如换出指令。

如果确实需要手动修改指令,最好从另一个指令进行。这允许更大的灵活性,并简化以后的重构(相同的例子:如果换出不同的自动完成指令)。

此外,虽然这似乎是解决这种情况下问题的唯一方法,但 $scope.$$childHead.$mdAutocompleteCtrl.hidden 代码似乎相当老套 - 除非别无选择,否则应避免访问以 [= 开头的属性13=],并且还避免在不通过共享范围属性的情况下修改同级指令。

不幸的是,在深入研究源代码(在 master branch 上)之后,我找不到比(如您所建议的)抓住它的范围并修改 hidden 属性.

通过 Controller 访问它的另一个问题是它有点困难,因为它嵌套在其他几个范围内。您可以传递事件,获取 DOM 节点,然后拉起它的作用域,但控制器中有很多不相关的东西。

解决方法:

因此,我们可以添加同级指令,类似于您在 Codepen 示例中包含的 ngEnter 示例指令。也许更明确一点,这样它在做什么就更明显了:

.directive('mdHideAutocompleteOnEnter', function () {
    return function (scope, element, attrs) {
        element.bind("keydown keypress", function (event) {
            if(event.which === 13) {
                scope.$apply(function (){
                    scope.$$childHead.$mdAutocompleteCtrl.hidden = true; // $scope  modified to scope
                });

                event.preventDefault();
            }
        });
    };
});

HTML 将在相关时简单地包含此指令:

<md-autocomplete 
               md-hide-autocomplete-on-enter
               md-items="item in querySearch(searchText)"
               md-item-text="item.name">
     <span md-highlight-text="searchText">{{item.name}} :: {{item.type}}</span>
</md-autocomplete>

这是示例,在实际操作中进行了修改:http://codepen.io/anon/pen/mJvGzp?editors=101

访问控制器方法的更好方法是定位元素,然后使用 jqLit​​e 对象来访问控制器:

var $acElement = angular.element(document.getElementById('Auto'));
var acCtrl = $acElement.controller('mdAutocomplete');
acCtrl.hidden = true;

每当您在 angular 元素上使用 scope() 方法访问任何内容时,如果您想禁用 angular 调试信息,您的实现将会中断。

如果您不介意在输入时失去对 md-autocomplete 输入元素的关注,您可以关闭 md-autocomplete 建议,而无需使用涉及扰乱内部 $mdAutocompleteCtrl 控制器的 hacky 方法。这取决于 md-autocomplete 在输入元素不再聚焦时自动隐藏建议。

  1. 将您的 md-autocomplete 元素包装在一个表单中(或使用像 ng-enter 这样的指令)并使用 md-input-id
  2. 向输入元素添加一个 ID

  <form ng-submit="vm.handleFormSubmit()">
    <md-autocomplete <strong>md-input-id="autocomplete"</strong> ...>
    </md-autocomplete>
  </form>

  1. #autocomplete 输入元素上调用 blur()

handleFormSubmit() {
  angular.element(document.querySelector('#autocomplete')).blur();
}

我认为这个解决方案更好,因为:

  • 它使用指令而不是控制器。

  • 它比其他给定的指令解决方案更简单。

Javascript

app.directive('closeOnEnter', function($compile) {
      return {
         restrict: 'A',
         require: 'mdAutocomplete',
         link: function(scope, element) {
            element.on('keydown keypress', function($event) {
               // 13: Enter
               if ($event.keyCode == 13) {
                  var eAcInput = this.getElementsByTagName('input')[0];
                  eAcInput.blur();
               }
            });
         },
      };
});

HTML

<md-autocomplete close-on-enter ... ...>