自定义 ngTagsInput &自动完成指令(AngularJS)

我是 AngularJS 的新手,目前正在处理一个输入字段,它可以一次接受多个标签以及自动完成功能,它将可用标签显示为下拉选项.为此,我使用了我在网上找到的 ngTagsInput 指令(http://mbenford.github.io/ngTagsInput/),它为我提供了一个自定义 HTML 元素 .这很好用:

I am new to AngularJS and am currently working on an input field, which can accept multiple tags at a time along with the auto-complete feature, which display the available tags as dropdown options. For this I am using the ngTagsInput directive I found on the web(http://mbenford.github.io/ngTagsInput/), which gives me a custom HTML element <tags-input>. This works beautifully:

index.html:

<script>
var app = angular.module('plunker', ['ngTagsInput']);

app.controller('MainCtrl', function($scope, $http) {
  $scope.tags = [
    { text: 'Tag1' },
    { text: 'Tag2' },
    { text: 'Tag3' }
  ];

  $scope.loadTags = function(query) {
    return $http.get('tags.json');
  };
});
</script>
<div ng-app="plunker" ng-controller="MainCtrl">    
    <tags-input ng-model="tags" add-on-paste="true" display-property="text" placeholder="Add a Tag" add-from-autocomplete-only="true">
           <auto-complete max-results-to-show="4" min-length="2" source="loadTags($query)"></auto-complete>
    </tags-input>
</div>

tags.json:

[
  { "text": "Tag1" },
  { "text": "Tag2" },
  { "text": "Tag3" },
  { "text": "Tag4" },
  { "text": "Tag5" },
  { "text": "Tag6" },
  { "text": "Tag7" },
  { "text": "Tag8" },
  { "text": "Tag9" },
  { "text": "Tag10" }
]

但是我想使用标准的 HTML <input> 元素,而不是指令附带的自定义 <tags-input> 元素,所以使用很多帮助和使用 <script src="https://code.jquery.com/jquery-3.1.0.js" integrity="sha256-slogkvB1K3VOkzAI8QITxV3VzpOnkeNVsKvtkYLMjfk="crossorigin="anonymous"></script> 我可以在这里做到:

However I wanted to use the standard HTML <input> element instead of the custom <tags-input> element which comes along with the directive, so with a lot of help and using <script src="https://code.jquery.com/jquery-3.1.0.js" integrity="sha256-slogkvB1K3VOkzAI8QITxV3VzpOnkeNVsKvtkYLMjfk=" crossorigin="anonymous"></script> I was able to do it here:

这是新的index.html:

<script>
var app = angular.module('plunker', ['ngTagsInput']);

app.controller('MainCtrl', function($scope, $http) {
  $scope.tags = [
    { "id":1, "tagname": 'Tag1' },
    { "id":2, "tagname": 'Tag2' },
    { "id":3, "tagname": 'Tag3' },
    { "id":4, "tagname": 'Tag4' }
  ];

    $scope.loadTags = function(query) {
    return $http.get('tags.json');
  };

});

app.directive('tagsInputAttr', 
  function($compile){
    return {
      restrict: 'A',
      require: '?ngModel',
      scope:{
        ngModel: '='
      },
      link: function($scope, element, attrs, controller) {
        var attrsText = '';
        $.each($(element)[0].attributes, function(idx, attr) {
          if (attr.nodeName === "tags-input-attr" || attr.nodeName === "ng-model") 
            return;

          attrsText += " " + attr.nodeName + "='" + attr.nodeValue + "'";
        });

        var html ='<tags-input ng-model="ngModel" ' + attrsText + '></tags-input>';
        e =$compile(html)($scope);
        $(element).replaceWith(e);
      }
    };
  }
);
</script>
<div ng-app="plunker" ng-controller="MainCtrl">

    <input tags-input-attr ng-model="tags" add-on-paste="true" display-property="tagname" placeholder="Add tags here..." add-from-autocomplete-only="true">
      <auto-complete max-results-to-show="3" min-length="2" source="loadTags($query)"></auto-complete>
    </input>

  </div>

还有新的tags.json:

[
  { "id":1, "tagname": "Tag1" },
  { "id":2, "tagname": "Tag2" },
  { "id":3, "tagname": "Tag3" },
  { "id":4, "tagname": "Tag4" },
  { "id":5, "tagname": "Tag5" },
  { "id":6, "tagname": "Tag6" },
  { "id":7, "tagname": "Tag7" },
  { "id":8, "tagname": "Tag8" },
  { "id":9, "tagname": "Tag9" },
  { "id":10, "tagname": "Tag10" }
]

如您所见,新指令 tagsInputAttr 包装了 提供了相同的功能,并且可以在 中使用.input> 标记作为一个属性以及其他属性,例如 ng-modeldisplay-property 等.所以我不必使用 元素直接.问题是 <auto-complete> 放置在 <input> 标记内不起作用.

As you can notice,the new directive tagsInputAttr, which wraps the <tags-input> provides the same functionality and can be used inside <input> tag as an attribute along with the rest of attributes such as ng-model, display-property etc. So I don't have to use the <tags-input> element directly. The problem is that the <auto-complete> placed inside the <input> tag doesn't work.

为此,我需要修改我的指令,考虑以下几点:

For this I need to alter my directive, considering the following:

注意:我不想为此使用 jquery

Note: I do not want to use jquery for this

我的问题是如何将 <auto-complete> 包装在同一个 <input tags-input-attr> 元素中:

My question is how do I wrap the <auto-complete> inside the same <input tags-input-attr> element:

  1. 作为同一<input tags-input-attr>元素内的属性

或作为标准 HTML 元素内的属性,如 <div>,包裹在相同的 元素.

or as an attribute inside a standard HTML element like <div> or <span>, wrapped inside the same <input tags-input-attr> element.

如果不是以上两个,那么作为最后的手段,将<auto-complete>标签包裹在同一个 元素

If not the above two, then as last resort, as the <auto-complete> tag wrapped inside the same <input tags-input-attr> element

感谢所有帮助.提前致谢.

All help is appreciated. Thanks in advance.

推荐答案

我对previus指令做了一些修改,现在它接受了从attributeelement 指令.

I made some changes on the previus directive and now it accepts all kind of transformation from attribute to element directive.

你仍然有 elem-as-attr 属性,但现在你必须指定它的 value,它将是 element将替换.

You still have the elem-as-attr attribute, but now you have to specify the value of it, that it will be the element that will replace.

例子:

<div elem-as-attr="tags-input"></div>

您的应用程序 JavaScript:

var app = angular.module('plunker', ['ngTagsInput']);

app.controller('MainCtrl', function($scope, $http) {
  $scope.allTags = [
    { "id":1, "tagname": "Tag1" },
    { "id":2, "tagname": "Tag2" },
    { "id":3, "tagname": "Tag3" },
    { "id":4, "tagname": "Tag4" },
    { "id":5, "tagname": "Tag5" },
    { "id":6, "tagname": "Tag6" },
    { "id":7, "tagname": "Tag7" },
    { "id":8, "tagname": "Tag8" },
    { "id":9, "tagname": "Tag9" },
    { "id":10, "tagname": "Tag10" }
  ];

  $scope.myTags =[
    $scope.allTags[2],
    $scope.allTags[4],
    $scope.allTags[8]
  ];

  $scope.loadTags = function(query) {
    return $scope.allTags;
  };
});

指令代码:

app.directive('elemAsAttr', function($compile) {
  return {
    restrict: 'A',
    require: '?ngModel',
    replace: true,
    scope: true,
    compile: function(tElement, tAttrs) {
      return function($scope) {
        var attrs = tElement[0].attributes;

        var attrsText = '';
        for (var i=0; i < attrs.length; i++) {
          var attr = attrs.item(i);
          if (attr.nodeName === "elem-as-attr") {
            continue;
          }
          attrsText += " " + attr.nodeName + "='" + attr.nodeValue + "'";        
        }

        var hasModel = $(tElement)[0].hasAttribute("ng-model");
        var innerHtml = $(tElement)[0].innerHTML;
        var html = '<' + tAttrs.elemAsAttr  + attrsText + '>' + innerHtml + '</' + tAttrs.elemAsAttr + '>';

        var e = hasModel ? $compile(html)($scope) : html;
        $(tElement).replaceWith(e);
      };
    }
  }
});

HTML 代码:

元素方式:

  <tags-input ng-model="myTags" add-on-paste="true" display-property="tagname" placeholder="Add a Tag" add-from-autocomplete-only="true">
    <auto-complete max-results-to-show="10" display-property="tagname" min-length="2" source="loadTags($query)"></auto-complete>
  </tags-input>

属性方式:

  <div elem-as-attr="tags-input" ng-model="myTags" add-on-paste="true" display-property="tagname" placeholder="Add tags here..." add-from-autocomplete-only="true">
    <div elem-as-attr="auto-complete" max-results-to-show="10" display-property="tagname" min-length="2" source="loadTags($query)"></div>
  </div>

Plunker:

https://plnkr.co/edit/9TqsXy

请注意,您不能将 input 元素用于 tagsInput因为 input 元素在 HTML 中没有结束标记.所以您将无法将 auto-complete 元素放入其中.

Note that you cannot use the input element for the tagsInput because the input element does not have the closing tag in HTML. So you will not be able to put the auto-complete element inside it.

相关文章