Настройка инструкций ngTagsInput и autoComplete (AngularJS)

Я новичок в AngularJS и в настоящее время работаю над полем ввода, которое может одновременно принимать несколько тегов вместе с функцией автозаполнения, которые отображают доступные теги в качестве раскрывающихся опций. Для этого я использую директиву ngTagsInput, которую я нашел в Интернете (http://mbenford.github.io/ngTagsInput/), которая дает мне пользовательский элемент HTML <tags-input>, Это прекрасно работает:

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> я смог сделать это здесь:

Вот новый 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, которая обертывает <tags-input>, обеспечивает ту же функциональность и может использоваться внутри тега <input> как атрибут вместе с остальными атрибутами, такими как ng-model, display-property и т.д. Поэтому мне не нужно напрямую использовать элемент <tags-input>. проблема заключается в том, что <auto-complete>, помещенный внутри тега <input>, не работает.

Для этого мне нужно изменить мою директиву, учитывая следующее:

Примечание. Я не хочу использовать jquery для этого

Мой вопрос заключается в том, как обернуть <auto-complete> внутри того же элемента <input tags-input-attr>:

  • Либо как атрибут внутри одного и того же элемента <input tags-input-attr>

  • или как атрибут внутри стандартного элемента HTML, такого как <div> или <span>, завернутый внутри того же элемента <input tags-input-attr>.

  • Если не указанные выше два, то в качестве последнего средства, как тег <auto-complete>, заключенный внутри одного и того же элемента <input tags-input-attr>

Вся помощь приветствуется. Спасибо заранее.

Ответ 1

Я внес некоторые изменения в директиву previus, и теперь он принимает все виды преобразований от директивы attribute to element.

У вас все еще есть атрибут elem-as-attr, но теперь вы должны указать его value, что он заменит element.

Пример:

<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.