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

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

问题描述

JS小提琴 http://jsfiddle.net/suras/JzaV9/4/

这是我的指令

 使用严格的;barterApp.directive('自动提示',函数($超时,$ HTTP){
  返回{
    限制:E,
    范围: {
      modelupdate:=,
      建议:=,
      urlsend:@
    },
    模板:'< UL><李NG重复=建议的建议NG点击=的UpdateModel(建议)> {{建议}}< /李>< / UL>,
    链接:功能(范围,元素){
      范围。$腕表('modelupdate',函数(){
        $超时(函数(){
          $ http.post(scope.urlsend,{Q:scope.modelupdate}),然后(功能(数据){
            scope.suggestions =数据。数据;
            的console.log(数据。数据);
          });
        },3000);
      });
      scope.updateModel =功能(值){
        scope.modelupdate =价值;
        范围$ parent.getBookInfo()。
      }
    }
  };});

控制器

  barterApp.controller('openLibraryCtrl',['$范围,$ HTTP',函数($范围,$ HTTP){
    $ scope.title =;
    $ scope.getBookInfo =功能(值){
      如果($ scope.title ==|| $ scope.title ==)//这里的标题是'R'(previous值)
      {
        返回;
      }
      $ http.get('/ book_info.json?Q ='+ $ scope.title)。然后(功能(RES){
        如果(Object.keys(RES)。长度!== 0)
        {
           数据= res.data
           的console.log(数据);        }
      });
    }
    //这里的标题是'轨'(从指令更新后的值)。
     //(用于手表功能在这里模型更新
    //并检查它但里面getBookInfo它仍然是'R')}]);

在更新模型的功能,我设定的模型值并调用父范围getBookInfo功能。但这里的事情是,当(这是一个自动完成)我在包含例如R,然后触发手表,我从帖子的网址建议纳克模型说输入栏中输入值(可以说轨道, 岩石),并显示它通过模板中的指令。当我点击的建议之一(说'轨')它触发的UpdateModel功能指令,并设置模型值。其优良的高达这个,但是当我调用函数getBookInfo父然后范围$ scope.title是函数(我控制台日志检查函数模型值正确更新为'轨道'外)内R。当我再次点击'摇滚'里面getBookInfo模型值是'轨'。
我不知道怎么回事。 (我还与控制器的手表功能测试的模型被正确地更新,但是函数调用getBookInfo持有回previous值)

 视图 <形式NG控制器=openLibraryController>
 <输入类型=文本NG模型=称号ID =标题NAME =书[标题]/>
   <自动提示modelupdate =头衔的建议=book_suggestionsurlsend =/ book_suggestions.json> < /自动提示>
< /表及GT;


解决方案

我没仔细端详它,但我怀疑(有高度的信心)父范围尚未在调用时更新 getBookInfo()(因为我们仍然在一个$中间消化周期)。

不那么好解决方案1:结果
您可以立即更新父范围,以及(如范围。$ parent.title = ... ),但是这显然是一个坏主意(以同样的理由为NR 2,但更是这样)。

不那么好解决方案2:结果
您可以通过新的标题作为参数传递给 getBookInfo()

这两种解决方案导致混合控制器code与指令code和创建组件,结果变得不太可重复使用和可测试少之间的紧密耦合。

不那么坏的解决方案:结果
你可以观看在标题和号召 getBookInfo()只要稍有改变:

  $范围。$表(标题,功能(为newValue,属性oldValue){
    getBookInfo();
});

这将是罚款,除了事实,这是完全没有必要的。


更好的解决方案:结果
角应该是照顾所有保在同步的东西对我们来说,它实际上做。你不给的是什么调用的目的很多情况下 getBookInfo(),但我猜你打算更新与所选书的一些信息视图。结果
在这种情况下,你可以只将其绑定到一个元素(使用 NG-绑定)和角将确保它得到正确执行和及时的。结果
例如:

 < D​​IV>图书信息:LT;跨度NG绑定=getBookInfo()>< / SPAN>< / DIV>

更进一步,在自动提示指令不必知道这件事。它应该只关心展示建议,操纵DOM(如有必要)和更新指定的模型属性每当建议点击(例如标题)。你更新后的值做什么应该是没有的事。

<子>(顺便说一句,最好的建议应该由服务提供。)


下面是解决问题的一个变形例(根据您的code)。如上所述有解决问题的几种方法,我只是觉得这一个砥更清洁,更一致的角路:

 书名:LT;输入类型=文本NG模型=book.title/&GT;
&LT;自动提示modelupdate =book.title
             建议=book.suggest()&GT;&下; /自动提示&GT;
图书信息:LT;跨度NG绑定=book.getInfo()&GT;&LT; / SPAN&GT;

只是通过查看HTML(不知道什么是JS),人们可以很容易知道是怎么回事:


  1. 有必然 book.title 文本字段。

  2. 有一个自定义的自动提示啄,提供由提供建议book.suggest()和更新 book.title

  3. 有一个跨度显示有关图书的信息。

相应的指令如下:

  app.directive('自动提示',函数(){
    返回{
        限制:'E',
        范围: {
            modelupdate:'=',
            建议:'和;'
        },
         模板:
          '&LT; UL&GT;&LT;李NG重复=建议的建议()'+
                  'NG点击=modelupdated =建议&GT;' +
               {{}建议}&LT; /李&GT;&LT; / UL&GT;'
    };
});

正如你所看到的,所有的指令都知道的是如何获取的建议和更新的东西。

请注意,同样的指令可以与任何类型的suggestables的使用(甚至是那些不具有 getBookInfo());只是通过在正确的属性( modelupdated 建议)。
还要注意,我们可以删除自动提示元素和应用程序将继续按预期(不五言的建议),而不在HTML或JS(任何进一步的修改,而在你的版图书信息将停止更新)。


您可以找到完整版本的 简短的演示这里 的。

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

This is my directive

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

});

controller is

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

}]);

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>

解决方案

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).

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).

Not-so-good Solution 2:
You could pass the new title as a parameter to 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.

Not-so-bad Solution:
You could watch over the title and call getBookInfo() whenever it changes:

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

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


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>

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.

(BTW, ideally the suggestions should be provided by a service.)


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>

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

  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.

The corresponding directive looks like this:

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.

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从指令设置模型值并调用父作用域函数持有该函数内部的previous价值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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