AngularJS 从指令设置模型值并调用父作用域函数保留该函数内的先前值 [英] AngularJS setting model value from directive and calling a parent scope function holds on to the previous value inside that function

查看:19
本文介绍了AngularJS 从指令设置模型值并调用父作用域函数保留该函数内的先前值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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"然后触发手表,我从帖子 url 获得建议(可以说rails","rock") 并按照指令中的模板显示它.当我单击其中一项建议(比如rails")时,它会触发指令中的 updatemodel 函数并设置模型值.这很好,但是当我在父作用域中调用 getBookInfo 函数时, $scope.title 在函数内部是 'r' (我在函数外部检查了控制台日志,模型值被正确更新为 'rails' ).再次单击rock"时,getBookInfo 中的模型值为rails".我不知道发生了什么.(我还在控制器中使用 watch 函数进行了测试,模型正确更新,但对 getBookInfo 的函数调用保留了先前的值)

in the update model function i set the model value and call the getBookInfo function on parent scope. but the thing here is when (this is a autocomplete) i enter the value in a input field that contains ng-model say for example 'r' then triggers the watch and i get suggestions from a post url (lets say "rails", "rock") and show it through the template as in the directive. when i click one of the suggestions (say 'rails') it triggers the updatemodel function in directive and sets the model value. its fine upto this but when i call the getBookInfo function in parent scope then $scope.title is 'r' inside the function (i checked with console log outside the function the model value was updated correctly as 'rails' ). again when i click 'rock' the model value inside getBookInfo is 'rails'. i have no clue whats going on. (i also tested with watch function in controller the model gets updated correctly but the function call to getBookInfo holds back to the previous value)

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>

推荐答案

我没有深入研究,但我怀疑(高度自信)在调用时父作用域尚未更新getBookInfo()(因为我们仍处于 $digest 循环的中间).

I didn't look deep into it, but I suspect (with a high degree of confidence) that the parent scope has not been updated at the time of calling getBookInfo() (since we are still in the middle of a $digest cycle).

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

Not-so-good Solution 1:
You could immediately update the parent scope as well (e.g. scope.$parent.title = ...), but this is clearly a bad idea (for the same reasons as nr 2, but even more so).

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

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

Both solutions result in mixing controller code with directive code and creating a tight coupling between your components, which as a result become less reusable and less testable.

还不错的解决方案:
您可以查看标题并在其更改时调用 getBookInfo():

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

这很好,除了完全没有必要.

This would be fine, except for the fact that it is totally unnecessary.

更好的解决方案:
Angular 应该为我们处理所有保持同步的事情,它确实做到了.您没有提供太多关于调用 getBookInfo() 的目的的上下文,但我猜您打算用所选书籍的一些信息更新视图.
在这种情况下,您可以将它绑定到一个元素(使用 ng-bind),Angular 将确保它正确及时地执行.
例如:

Better Solution:
Angular is supposed to take care of all that keep-in-sync stuff for us and it actually does. You don't have given much context on what is the purpose of calling getBookInfo(), but I am guessing you intend to update the view with some info on the selected book.
In that case you could just bind it to an element (using ng-bind) and Angular will make sure it is executed properly and timely.
E.g.:

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

此外,autosuggest 指令不需要知道任何关于它的信息.它应该只关心显示建议、操作 DOM(如果需要)和更新指定的模型属性(例如 title),只要点击建议.你用更新后的值做什么应该与它无关.

Further more, the autosuggest directive doesn't have to know anything about it. It should only care about displaying suggestions, manipulating the DOM (if necessary) and updating the specified model property (e.g. title) whenever a suggestion is clicked. What you do with the updated value should be none of its business.

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

以下是解决问题的修改示例(基于您的代码).如上所述,有几种解决问题的方法,我只是觉得这种方法更简洁,更符合角度方式":

Below is a modified example (based on your code) that solves the problem. As stated above there are several methods of solving the problem, I just feel this one tobe cleaner and more aligned to the "Angular way":

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 中的内容),就可以轻松知道发生了什么:

Just by looking at the HTML (without knowing what is in JS), one can easily tell what is going on:

  1. 有一个绑定到 book.title 的文本字段.
  2. 有一个自定义的 autosuggest 东西,它提供 book.suggest() 提供的建议并更新 book.title.
  3. 有一个 span 显示关于这本书的信息.
  1. There is a text-field bound to book.title.
  2. There is a custom autosuggest thingy that offers suggestions provided by book.suggest() and updates book.title.
  3. There is a span that displays info about the book.

相应的指令如下所示:

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>'
    };
});

如您所见,指令所知道的只是如何检索建议和更新什么.

As you can see, all the directive knows about is how to retrieve suggestions and what to update.

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

Note that the same directive can be used with any type of "suggestables" (even ones that don't have getBookInfo()); just pass in the right attributes (modelupdated, suggestions). Note also, that we could remove the autosuggest element and the app would continue to work as expected (no suggestions of cource) without any further modification in HTML or JS (while in your version the book info would have stopped updating).

您可以在此处找到完整版的简短演示.

You can find the full version of this short demo here.

这篇关于AngularJS 从指令设置模型值并调用父作用域函数保留该函数内的先前值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆