Custom directive incorporating form validation with uib type-ahead

by Ahmad Baktash Hayeri

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-messages/1.5.5/angular-messages.js&#39;"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.0/angular-resource.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
<div ng-controller="mainCtrl">
  <form name="wkProfileCompany">
    <div class="col-xs-12 works">
      <div class='form-group' ng-class="{'has-error': wkProfileCompany.location2.$touched && wkProfileCompany.location2.$invalid}">
        <label class="control-label" for="location2">Location <span class='text-muted'>(type-ahead version 1 directive)</span></label>
        <div>
          <wk-location-suggest-old class="form-control" type="text" name="location2" ng-model="location2" form="wkProfileCompany" is-required="true"></wk-location-suggest-old>
          <br/> location2: {{ location2 }}
          <div ng-messages="wkProfileCompany.location2.$touched && wkProfileCompany.location2.$error" class="text-danger">
            <div ng-message="required">
              Location 2 is required
            </div>
          </div>
        </div>
      </div>
    </div>
  </form>
</div>

JavaScript

var app = angular.module('plunker', ['ngMessages','ui.bootstrap', 'ngResource']);

app.controller('MainCtrl', function($scope) {
  $scope.location2 = 'Location 2';
})

.directive('wkLocationSuggestOld', [function () {
  return {
    restrict: 'E',
    require: '?ngModel',
    scope: {
      name: '@',
      form: '=',
      isRequired: '='
    },
    template: '<input name="{{name}}" type="text" class="{{innerClass}}" ng-model="innerModel" ng-change="onChange()" uib-typeahead="location as row.location for row in typeAhead($viewValue)" typeahead-wait-ms="750" typeahead-on-select="onSelectInternal($item, $model, $label)" typeahead-min-length="2" typeahead-focus-first="true" ng-required="isRequired">'
              + '<div ng-messages="form[name].$error" class="text-danger"><div ng-message="required">{{name}} is required</div></div>',
    controller: 'LocationSuggestController',
    link: function (scope, element, attrs, ngModel) {
      if (!ngModel) {
        return;
      }

      scope.attrs = attrs;
      // locationSuggest form-control
      scope.innerClass = attrs.class;
      attrs.$set('class', null);
      // invoked when model changes from the outside
      ngModel.$render = function () {
        console.log('ngModel.$render');
        scope.innerModel = ngModel.$modelValue;
      };

      // invoked when model changes from the inside
      scope.onChange = function () {
        console.log('scope.onChange: ' + scope.innerModel);
        ngModel.$setViewValue(scope.innerModel);
      };

      scope.onSelectInternal = function ($item) {
        console.log('scope.onSelectInternal');
        ngModel.$setViewValue($item.location);
        scope.innerModel = $item.location;
      };
    }
  };
}])

// Locations API
.factory('locationsApi', ['$resource', function ($resource) {
  return $resource('', {  },
  {
    'suggest': { method: 'GET', url: 'https://www.workible.com.au/api/v4/locations/suggest', isArray: true },
  }
);
}])


// Type Ahead...