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

app.controller('MainCtrl', function($scope) {
  $scope.location1 = 'Location 1';
  $scope.location2 = 'Location 2';
  $scope.location3 = 'Location 3';
  $scope.location4 = 'Location 4';
  $scope.location5 = 'Location 5';
})

.directive('wkLocationSuggestNew', ['$compile', function ($compile) {
  return {
    restrict: 'A',
    require: 'ngModel',
    replace: false,
    //terminal: false,
    //priority: 100,
    scope: {
      wkApiModel: '=' // (Ignore this for Plunkr)
    },
    controller: 'LocationSuggestController',
    link: function (scope, element, attrs, ngModelCtrl) {
      if (!ngModelCtrl) {
        return;
      }

      element.attr('uib-typeahead', 'location as row.location for row in typeAhead($viewValue)');
      element.attr('typeahead-wait-ms', '750');
      element.attr('typeahead-on-select', 'onSelectInternal($item, $model, $label)');
      element.attr('typeahead-min-length', '2');
      element.attr('typeahead-focus-first', 'true');

      element.removeAttr("wk-location-suggest-new");        //remove the location-suggest-new to avoid indefinite loop

      // invoked when model changes from the outside
      ngModelCtrl.$render = function () {
        console.log('changing model from outside');
      //  //scope.innerModel = ngModelCtrl.$modelValue;
      };

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

      scope.onSelectInternal = function ($item, $model, $label) {
        console.log()
        // This fires, but it effects the ng-model on the first input, 
        // but not the input that this directive is attached too
        ngModelCtrl.$setViewValue($item.location);
      };

      $compile(element)(scope);

    }
  };
}])

.directive('wkLocationSuggestOld', [function () {
  return {
    restrict: 'E',
    require: '?ngModel',
    scope: {
    },
    template: '<input 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">',
    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 Suggestion
.controller('LocationSuggestController', ['$scope', 'locationsApi', function ($scope, locationsApi) {

console.log('instantiate: LocationSuggestController');

  $scope.typeAhead = function (val) { 
      console.log('typeahead: ' + val)
      return locationsApi.suggest({ location: val }).$promise.then(function (data) {
        return data;
      });
  };

}])


;
<!DOCTYPE html>
<html ng-app="plunker">

<head>
  <meta charset="utf-8" />
  <title>AngularJS Plunker</title>
  <script>
    document.write('<base href="' + document.location + '" />');
  </script>
  <link rel="stylesheet" href="style.css" />
  <script data-require="angular.js@1.4.x" src="https://code.angularjs.org/1.4.9/angular.js" data-semver="1.4.9"></script>
  <script data-require="ui-bootstrap@*" data-semver="1.3.2" src="https://cdn.rawgit.com/angular-ui/bootstrap/gh-pages/ui-bootstrap-tpls-1.3.2.js"></script>
  <script src='https://cdnjs.cloudflare.com/ajax/libs/angular-messages/1.5.5/angular-messages.js'></script>
  <script src='https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.0/angular-resource.js'></script>
  <script src="app.js"></script>
  <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">

  <style>
    .works {
      padding: 10px 3px;
      border: 1px solid green;
    }
    
    .fails {
      padding: 10px 3px;
      border: 1px solid red;
    }
  </style>
</head>

<body ng-controller="MainCtrl">

  <form name="wkProfileCompany">
    <div class="form-group">

      <!-- Simple Input with ng-model & ng-required, this is my baseline -->
      <div class="col-xs-12 works">
        <div class='form-group' ng-class="{'has-error': wkProfileCompany.location1.$touched && wkProfileCompany.location1.$invalid}">
          <label class="control-label" for="location1">Location <span class='text-muted'>(required input)</span></label>
          <div>
            <input class="form-control" type="text" name="location1" ng-required="true" ng-model="location1">
            <br/>
            <div ng-messages="wkProfileCompany.location1.$touched && wkProfileCompany.location1.$error" class="text-danger">
              <div ng-message="required">
                Location 1 is required
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>


    <!-- This Example Uses the Old directive, could not get ng-require to work with this -->
    <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-required="true" ng-model="location2"></wk-location-suggest-old>
          <br/>
          <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 class='form-group'>
        <label class="control-label" for="location2">Location <span class='text-muted'>(change this and see field above change)</span></label>
        <div>
          <input class="form-control" type="text" ng-required="true" ng-model="location2">
        </div>
      </div>
      <br/>
      <pre>&lt;wk-location-suggest-old&gt; &lt;/wk-location-suggest-old&gt;</pre>
      <p>This directive works well, but I was never able to get it working with ng-require, so I have the 2nd version below that kind of works, but does not update the model correctly</p>

    </div>
    </div>


    <!-- This Example Uses the New directive, could not get ng-model to update correctly -->
    <div class="col-xs-12 fails">
      <div class='form-group' ng-class="{'has-error': wkProfileCompany.location3.$touched && wkProfileCompany.location3.$invalid}">
        <label class="control-label" for="location2">Location <span class='text-muted'>(type-ahead version 2 directive)</span></label>
        <div>
          <input wk-location-suggest-new
                               class="form-control"
                               type="text"
                               name="location3"
                               ng-model="location3"
                               ng-required="true">

          <br/>
          <div ng-messages="wkProfileCompany.location3.$touched && wkProfileCompany.location3.$error" class="text-danger">
            <div ng-message="required">
              Location 3 is required
            </div>
          </div>
        </div>
      </div>
      <div class='form-group'>
        <label class="control-label" for="location3">Location <span class='text-muted'>(change this and see field above change)</span></label>
        <div>
          <input class="form-control" type="text" ng-required="true" ng-model="location3">
        </div>
      </div>
      <br/>
      <pre>&lt;input wk-location-suggest-new
                     class="form-control"
                     type="text"
                     name="location3"
                     ng-model="location3"
                     ng-required="true"&gt;</pre>
      <p>This directive works well, but I was never able to get it working with ng-require, so I have the 2nd version below that kind of works, but does not update the model correctly</p>

    </div>
    </div>

    <pre ng-if='false'>{{wkProfileCompany|json}}</pre>
  </form>

</body>

</html>
/* Put your css in here */