Search code examples
javascripthtmlangularjstypeahead.jsangular-directive

Angular floating input label to use typeahead


I have used one of the style from here: http://tympanus.net/Development/TextInputEffects/index.html

To create an input directive, please see plunker: https://plnkr.co/edit/wELJGgUUoiykcp402u1G?p=preview

This working great for standard input fields, however, i am struggling to work wirth Twitter typeahead: https://github.com/twitter/typeahead.js/

Question - How can i use my floating input label with a typeahead?

app.directive('floatInput', function($compile) {
  return {
    restrict: 'E',
    replace: true,
    transclude: true,
    scope: {
      elemTitle: '=elemTitle',
      elemtId: '=elemeId'
    },
    templateUrl: 'input-template.html',
    link: function(scope, elem, attrs) {
      var ngModelName = elem.attr('input-model');
      var inputElem = angular.element(elem[0].querySelector('input'));
      inputElem.attr('ng-model', ngModelName);

      $compile(inputElem)(scope);
      $compile(inputElem)(scope.$parent);

      var inputLabel = angular.element(elem[0].querySelector('label span'));
      inputLabel.attr('ng-class', '{\'annimate-input\' : '+ngModelName+'.length > 0}');
      $compile(inputLabel)(scope);
    },
    controller: function($scope) {
      $scope.title = $scope.elemTitle;
      $scope.inputId = $scope.elemId
    }
  }
})

HTML:

<div>
<span class="input input--juro">
    <input class="input__field input__field--juro" type="text" id="{{inputId}}" ng-model="tmp" />
    <label class="input__label input__label--juro" for="{{inputId}}">
    <span class="input__label-content input__label-content--juro">{{title}}</span>
  </label>
  </span>
</div>

Solution

  • The easiest way I know of to achieve this is to initialize the typeahead input in the link function of the directive. For initializing the typeahead with available options I would create an optional parameter to the directive and selectively initialize the input as a typeahead input if the list is provided.

    Here is an example of how the directive could look instead:

    app.directive('floatInput', function($compile) {
      return {
        restrict: 'E',
        replace: true,
        transclude: true,
        scope: {
          elemTitle: '=elemTitle',
          elemtId: '=elemeId',
          typeaheadSrc: '=?typeaheadSrc'
        },
        templateUrl: 'input-template.html',
        link: function(scope, elem, attrs) {
          var inputElem = angular.element(elem[0].querySelector('input'));
    
          if(scope.typeaheadSrc && scope.typeaheadSrc.length > 0){
            var typeahead = jQuery(inputElem).typeahead({
              hint: true,
              highlight: true,
              minLength: 1
            }, {
              name: 'typeahead',
              source: substringMatcher(scope.typeaheadSrc)
            });
          }
        },
        controller: function($scope) {
          $scope.title = $scope.elemTitle;
          $scope.inputId = $scope.elemId
        }
      }
    });
    // from http://twitter.github.io/typeahead.js/examples/
    var substringMatcher = function(strs) {
      return function findMatches(q, cb) {
        var matches= [],
            substrRegex = new RegExp(q, 'i');
    
        $.each(strs, function(i, str) {
          if (substrRegex.test(str)) {
            matches.push({value: str});
          }
        });
    
        cb(matches);
      };
    };
    

    I have updated your plunker to achieve the desired result: Plunker