AngularJS从指令设置模型值并调用父作用域函数保持该函数内的先前值

时间:2014-01-15 18:15:03

标签: angularjs angularjs-directive angularjs-scope

js fiddle http://jsfiddle.net/suras/JzaV9/4/

这是我的指示

'use strict';

barterApp.directive('autosuggest', function($timeout, $http) {
  return {
    restrict: "E",
    scope: {
      modelupdate:"=",
      suggestions:"=",
      urlsend:"@"
    },
    template: '<ul><li ng-repeat="suggest in suggestions" ng-click="updateModel(suggest)">{{suggest}}</li></ul>',
    link: function (scope, element) {
      scope.$watch('modelupdate', function() {
        $timeout(function(){
          $http.post(scope.urlsend, {q:scope.modelupdate}).then(function(data){
            scope.suggestions = data.data;
            console.log(data.data);
          });
        }, 3000);
      });
      scope.updateModel = function(value){
        scope.modelupdate = value;
        scope.$parent.getBookInfo();
      }
    }
  };

});

控制器

barterApp.controller('openLibraryCtrl', ['$scope','$http',function ($scope,$http) {
    $scope.title = "";
    $scope.getBookInfo = function(value){
      if($scope.title == "" || $scope.title == " ") //here title is 'r'(previous value)
      {
        return;
      }
      $http.get('/book_info.json?q='+$scope.title).then(function(res){
        if(Object.keys(res).length !== 0)
        {
           data = res.data
           console.log(data);

        }
      });
    }
    //here title is 'rails' (updated value from directive).
     //( used a watch function here  on model update 
    // and checked it but inside getBookInfo it is still 'r' )   

}]);

在更新模型函数中,我设置模型值并在父作用域上调用getBookInfo函数。但这里的事情是当(这是一个自动完成)我输入包含ng-model的输入字段中的值,例如'r'然后触发手表,我从帖子网址获取建议(让我们说“rails”, “摇滚”)并在指令中通过模板显示它。当我点击其中一个建议(比如'rails')时,它会触发指令中的updatemodel函数并设置模型值。这很好但是当我在父作用域中调用getBookInfo函数时,$ scope.title在函数内部是'r'(我在函数外部使用控制台日志检查模型值被正确更新为'rails')。再次当我点击'摇滚'时,getBookInfo中的模型值是'rails'。 我不知道最近发生了什么。 (我也在控制器中使用watch函数进行测试,模型正确更新,但函数调用getBookInfo保持回到之前的值)

view

 <form ng-controller="openLibraryController">
 <input type="text" ng-model="title" id="title" name="book[title]"  />
   <autosuggest modelupdate = "title" suggestions = "book_suggestions" urlsend="/book_suggestions.json"> </autosuggest>
</form>

1 个答案:

答案 0 :(得分:1)

我没有深入研究它,但我怀疑(高度自信)在调用getBookInfo()时父级范围尚未更新(因为我们仍处于中间位置) $ digest周期)。

不太好解决方案1:
您也可以立即更新父作用域(例如scope.$parent.title = ...),但这显然是一个坏主意(出于与nr 2相同的原因,但更是如此)。

不太好解决方案2:
您可以将新标题作为参数传递给getBookInfo()

这两种解决方案都会导致控制器代码与指令代码混合,并在组件之间创建紧密耦合,从而减少可重用性并降低可测试性。

不太糟糕的解决方案:
您可以查看标题,并在其发生变化时致电getBookInfo()

$scope.$watch('title', function (newValue, oldValue) {
    getBookInfo();
}); 

这样可以,但事实上完全没必要。


更好的解决方案:
Angular应该为我们处理所有保持同步的东西,它实际上是这样。你没有给出调用getBookInfo()的目的的上下文,但我猜你打算用所选书上的一些信息来更新视图。
在这种情况下,你可以将它绑定到一个元素(使用ng-bind),Angular将确保它正确和及时地执行。
E.g:

<div>Book info: <span ng-bind="getBookInfo()"></span></div>

此外,autosuggest指令不必了解它。它应该只关心显示建议,操纵DOM(如果需要)并在每次点击建议时更新指定的模型属性(例如title)。您使用更新后的值应该不做任何事情。

(顺便说一句,理想情况下,建议应由服务提供。)


以下是解决问题的修改示例(基于您的代码)。如上所述,有几种方法可以解决这个问题,我觉得这个方法更清洁,更符合“Angular方式”:

Book title: <input type="text" ng-model="book.title" />
<autosuggest modelupdate="book.title"
             suggestions="book.suggest()"></autosuggest>
Book info: <span ng-bind="book.getInfo()"></span>

只需查看HTML(不知道JS中的内容),就可以很容易地知道发生了什么:

  1. 文本字段绑定到book.title
  2. 有一个自定义autosuggest内容提供book.suggest()提供的建议并更新book.title
  3. span显示有关该图书的信息。
  4. 相应的指令如下所示:

    app.directive('autosuggest', function() {
        return {
            restrict: 'E',
            scope: {
                modelupdate: '=',
                suggestions: '&'
            },
             template:
              '<ul><li ng-repeat="suggest in suggestions()" ' +
                      'ng-click="modelupdated = suggest">' +
                   '{{suggest}}</li></ul>'
        };
    });
    

    如您所见,所有指令都知道如何检索建议和更新内容。

    请注意,同一指令可用于任何类型的“建议”(即使是没有getBookInfo()的指令);只需传入正确的属性(modelupdatedsuggestions)。 另请注意,我们可以删除autosuggest元素,并且应用程序将继续按预期工作(没有cource的建议),无需在HTML或JS中进行任何进一步修改(在您的版本中,书籍信息将停止更新)


    您可以找到此 short demo here 的完整版。