AngularJS 强制模型从 Angular Ui TypeAhead 更新

AngularJS Force Model To Update From Angular Ui TypeAhead

所以我有一个正在工作的 Angular UI Typeahead,但是我有一个用例,我无法让它与之合作。

我的问题是让它根据状态而不是基于用户输入显示特定值。

我正在使用 Angular UI Router,所以我的代码如下所示:

if($stateParams.assetID != undefined)
  {
    $http.get('/getData/idRepo', {
      params: {
        id: $stateParams.assetID
      }
    }).then(function(response){
        $scope.getMaterial(response.data[0].Title);
        //this is where I try and trigger the getMaterial 
        //function to show the Typeahead value based on a url parameter
        //
    });
  }

$scope.getMaterial = function(val) {
    return $http.get($scope.material[0].Query, {
      params: {
        title: val
      }
    }).then(function(response){
        return response.data;
    });
};

我的 Typeahead HTML 是这样的:

<input type="text" ng-model="selected.asyncSelected"
       placeholder="Material Name..." 
       uib-typeahead="med as med.name for med in getMaterial($viewValue)" 
       typeahead-editable="false"
       typeahead-template-url="meditationssearch.html" 
       typeahead-select-on-exact="true" 
       typeahead-select-on-exact="true" 
       typeahead-loading="loadingLocations" 
       typeahead-no-results="noResults"
       class="form-control">

如您所见,预输入从 getMaterial() 中读取,我尝试在 if($stateParams.assetID != undefined) 条件中触发。

预期工作流程:

  1. 使用 url 中 assetID$stateparam 导航到此视图。
  2. 有条件地看到 assetID 参数不是未定义的,触发 GET 请求然后让它 return 来自 运行 getMaterial() 的结果(结果第一个 GET 请求的一部分通过 getMaterial's() 自己的 GET 请求传递 - 就好像第一个 GET 请求的结果是输入的一样)。
  3. 更新 model/typeahead 以在视图中显示 getMaterial() 值。

我试过 $scope.$apply() 但摘要已经在进行中。我希望有另一个我不知道的快速修复来触发提前输入。

我也试过这样的:

jQuery("input[uib-typeahead]").val(response.data[0].Title).trigger('input');

...但这不是一个好的解决方案,因为我正在输入的一些资产具有完全相同的名称,这就是我尝试使用 id 作为我的原因url 参数并让 typeahead 源函数选择名称,就好像它是输入的一样,然后 return 到模型的完整对象 (name/id)。

请帮我实现我想要的工作流程。

编辑: Typeahead Source Code

我相信我想在 select 函数中激活一些东西:

 scope.select = function(activeIdx, evt) {
      //called from within the $digest() cycle
      var locals = {};
      var model, item;

      selected = true;
      locals[parserResult.itemName] = item = scope.matches[activeIdx].model;
      model = parserResult.modelMapper(originalScope, locals);
      $setModelValue(originalScope, model);
      modelCtrl.$setValidity('editable', true);
      modelCtrl.$setValidity('parse', true);

      onSelectCallback(originalScope, {
        $item: item,
        $model: model,
        $label: parserResult.viewMapper(originalScope, locals),
        $event: evt
      });

      resetMatches();

      //return focus to the input element if a match was selected via a mouse click event
      // use timeout to avoid $rootScope:inprog error
      if (scope.$eval(attrs.typeaheadFocusOnSelect) !== false) {
        $timeout(function() { element[0].focus(); }, 0, false);
      }
    };

也许resetMatches()?我如何从我自己的控制器中调用它?

而不是在 html 中使用 getMaterial($viewValue)(这将在每个摘要周期触发...)您可以连接一个 ng-change 并且这种方式只在数据实际发生时触发变了。在这种情况下,您现在有一个模型 $scope.materials,您可以随时更新以获取更改。

javascript:

$scope.modelChanged = function(){
    $http.get($scope.material[0].Query, {
        params: {
            title: $scope.selected.asyncSelected
        }
    }).then(function (response) {
        $scope.materials = response.data;
    });
}

if ($stateParams.assetID != undefined) {
    $http.get('/getData/idRepo', {
        params: {
            id: $stateParams.assetID
        }
    }).then(function (response) {
        $scope.selected.asyncSelected = response.data[0].Title;

        $scope.modelChanged(); //not sure if you have to run this it might fire automatically
    });
}

HTML:

<input type="text" ng-model="selected.asyncSelected"
       placeholder="Material Name..." 
       uib-typeahead="material as material.name for material in materials" 
       typeahead-editable="false"
       typeahead-template-url="meditationssearch.html" 
       typeahead-select-on-exact="true" 
       typeahead-select-on-exact="true" 
       typeahead-loading="loadingLocations" 
       typeahead-no-results="noResults"
       ng-change="modelChanged()"
       class="form-control">

使用自定义指令:

app.directive("forceInput", function() {
  return {
    require: "ngModel",
    link: postLink
  }
  function postLink(scope, elem, attrs, ngModel) {
    attrs.$observe('forceInput', function(value) {
      if (!value) return;
      elem[0].value = value;
      ngModel.$setViewValue(value);
    })
  }
})

用法:

<input type="text" ng-model="selected.asyncSelected"
       force-input="{{forceValue}}"
       placeholder="Material Name..." 
       uib-typeahead="med as med.name for med in getMaterial($viewValue)" 
       typeahead-editable="false"
       typeahead-template-url="meditationssearch.html" 
       typeahead-select-on-exact="true" 
       typeahead-select-on-exact="true" 
       typeahead-loading="loadingLocations" 
       typeahead-no-results="noResults"
       class="form-control">

JS

if($stateParams.assetID != undefined)
  {
    $http.get('/getData/idRepo', {
      params: {
        id: $stateParams.assetID
      }
    }).then(function(response){
        ̶$̶s̶c̶o̶p̶e̶.̶g̶e̶t̶M̶a̶t̶e̶r̶i̶a̶l̶(̶r̶e̶s̶p̶o̶n̶s̶e̶.̶d̶a̶t̶a̶[̶0̶]̶.̶T̶i̶t̶l̶e̶)̶;̶
        $scope.forceValue = response.data[0].Title;
        //this is where I try and trigger the getMaterial 
        //function to show the Typeahead value based on a url parameter
        //
    });
  }

The DEMO

angular.module('app', ['ngAnimate', 'ngSanitize', 'ui.bootstrap']);
angular.module('app').controller('ctrl', function($scope, $http) {

  var _selected;

  $scope.selected = undefined;
  $scope.states = ['Alabama', 'Alaska', 'Arizona', 'Arkansas', 'California',
                   'Colorado', 'Connecticut', 'Delaware', 'Florida', 'Georgia',
                   'Hawaii', 'Idaho', 'Illinois', 'Indiana', 'Iowa', 'Kansas',
                   'Kentucky', 'Louisiana', 'Maine', 'Maryland', 'Massachusetts', 
                   'Michigan', 'Minnesota', 'Mississippi', 'Missouri', 'Montana', 
                   'Nebraska', 'Nevada', 'New Hampshire', 'New Jersey', 'New Mexico',
                   'New York', 'North Dakota', 'North Carolina', 'Ohio', 'Oklahoma',
                   'Oregon', 'Pennsylvania', 'Rhode Island', 'South Carolina', 
                   'South Dakota', 'Tennessee', 'Texas', 'Utah', 'Vermont', 'Virginia',
                   'Washington', 'West Virginia', 'Wisconsin', 'Wyoming'];
});

angular.module('app').directive("forceInput", function() {
  return {
    require: "ngModel",
    link: postLink
  }
  function postLink(scope, elem, attrs, ngModel) {
    attrs.$observe('forceInput', function(value) {
      if (!value) return;
      elem[0].value = value;
      ngModel.$setViewValue(value);
    })
  }
})
    <script src="//unpkg.com/angular/angular.js"></script>
    <script src="//unpkg.com/angular-animate/angular-animate.js"></script>
    <script src="//unpkg.com/angular-sanitize/angular-sanitize.js"></script>
    <script src="//unpkg.com/angular-ui-bootstrap/dist/ui-bootstrap-tpls.js"></script>
    <link href="//unpkg.com/bootstrap@3.3/dist/css/bootstrap.css" rel="stylesheet">

<body ng-app="app">

  <div class='container-fluid typeahead-demo' ng-controller="ctrl">

    <pre>Model: {{selected | json}}</pre>
    <input type="text" ng-model="selected"
           force-input="a"
           uib-typeahead="state for state in states | filter:$viewValue | limitTo:8"
           class="form-control">

 </div>
</body>

另一种方法是挂接到 Typeahead 本身,这样如果它具有相同的名称,它将为模型选择正确的对象。

看这里:

https://plnkr.co/edit/ABM72yyV4dtp5PsH4bIm?p=preview

angular.module('ui.bootstrap.demo').controller('TypeaheadCtrl', function($scope, $http,uibTypeaheadParser) {

$scope.fire = function(val){
      var locals = {};
      var model;
      var parserResult = uibTypeaheadParser.parse(angular.element("input[uib-typeahead]")[0].getAttribute('uib-typeahead'));
      locals[parserResult.itemName] = val;
    model = parserResult.modelMapper($scope, locals);
    console.log(model);
    $scope.customSelected = model; //$scope.customSelected is the ng-model in this example
  }

  });

其中 val 是对象,而不是字符串。

<button ng-click="fire({
  'name': 'Minnesota',
  'flag': 'b/b9/Flag_of_Minnesota.svg/46px-Flag_of_Minnesota.svg.png'
})" class="button btn-primary">CLICK ME TO SET TYPEAHEAD</button>