【问题标题】:Ng-model does not update controller valueng-model 不更新控制器值
【发布时间】:2012-09-19 01:25:52
【问题描述】:

可能是个愚蠢的问题,但我的 html 表单带有简单的输入和按钮:

<input type="text" ng-model="searchText" />
<button ng-click="check()">Check!</button>
{{ searchText }}

然后在控制器中(从routeProvider调用模板和控制器):

$scope.check = function () {
    console.log($scope.searchText);
}

为什么在单击按钮时,我在控制台中看到视图已正确更新但未定义?

谢谢!

更新: 似乎我实际上已经解决了这个问题(之前不得不想出一些解决方法): 只需将我的属性名称从searchText 更改为search.text,然后在控制器中定义空的$scope.search = {}; 对象,瞧……不知道为什么它会起作用;]

【问题讨论】:

  • 您确定在这部分文档中使用了这个控制器吗?你能发布一个最小的失败示例吗?
  • 是的,100% 确定控制器没问题,这个问题对我来说似乎很熟悉......令人惊讶的是,当我将属性名称从 searchText 更改为 search.text 时,它会起作用,知道为什么??
  • @Arthur:这有点不明显,但 ng-model 只会在您的视图中创建一种 speak 局部变量,因此如果您想保持这种方式,您需要将它传递给检查() 函数,例如:check(searchText),然后您的控制器将识别它。希望对你有帮助
  • 为了记录,拼写为voila,而不是vualawolla等。
  • 我认为您正在寻找的答案是*.com/a/14049482/1217913

标签: javascript angularjs data-binding angular-ngmodel


【解决方案1】:

“如果你使用 ng-model,你必须有一个点。”
让你的模型指向一个 object.property,然后你就可以开始了。

控制器

$scope.formData = {};
$scope.check = function () {
  console.log($scope.formData.searchText.$modelValue); //works
}

模板

<input ng-model="formData.searchText"/>
<button ng-click="check()">Check!</button>

当子作用域在起作用时会发生这种情况 - 例如子路由或 ng-repeats。 子作用域创建了它自己的值并产生了名称冲突as illustrated here

请参阅此视频剪辑了解更多信息:https://www.youtube.com/watch?v=SBwoFkRjZvE&t=3m15s

【讨论】:

  • 这才是真正的答案。
  • @Catfish 使用原型继承,每当您写入子属性时,对父属性的引用/连接将不复存在。 Angular Scopes 建模的方式,如果你没有点,那么在你的子范围内创建一个新属性。该视频更详细地解释了它:youtube.com/watch?v=ZhfUv0spHCY&feature=youtu.be&t=30m
  • @user3677331 在没有点的情况下它可以正常工作,直到您有一个尝试与之对话的子范围(例如 ng-repeat 中的项目)。假设您的模型名称是“电话”您的子范围创建“电话”,然后您会遇到范围冲突,因为子范围有一个“电话”变​​量,因此无法访问父范围上的“电话”。而如果子作用域创建了 user.phone,它将被添加到父作用域的用户对象中,因此两个作用域都指向同一个对象
  • 非常有帮助。我不确定我是否会找到一个相对模糊问题的答案,但这已经结束了。
  • 谢谢,我在这个问题上浪费了几个小时 - 幸运的是我找到了这个答案。
【解决方案2】:

Controller as 版本(推荐)

这里是模板

<div ng-app="example" ng-controller="myController as $ctrl">
    <input type="text" ng-model="$ctrl.searchText" />
    <button ng-click="$ctrl.check()">Check!</button>
    {{ $ctrl.searchText }}
</div>

JS

angular.module('example', [])
  .controller('myController', function() {
    var vm = this;
    vm.check = function () {
      console.log(vm.searchText);
    };
  });

一个例子:http://codepen.io/Damax/pen/rjawoO

最好使用 Angular 2.x 或 Angular 1.5 或更高版本的组件

########

旧方式(不推荐)

不建议这样做,因为字符串是原始的,强烈建议使用对象来代替

在你的标记中试试这个

<input type="text" ng-model="searchText" />
<button ng-click="check(searchText)">Check!</button>
{{ searchText }}

这在你的控制器中

$scope.check = function (searchText) {
    console.log(searchText);
}

【讨论】:

  • 这只适用于单向...如果你想改变searchText的值怎么办?
  • 这也没有回答“为什么?”问题。
  • 如果我不想使用任何按钮怎么办?例如,我需要在输入时提交
  • Saniko => 要在输入时提交,您必须在其上使用表单标签和 ng-submit (docs.angularjs.org/api/ng.directive:ngSubmit)
  • @HP's411 这是因为字符串是原语。当 Angular 分配值时,它会更改指向该值的指针,因此控制器会查看旧值,因为它具有指向该值的旧指针。
【解决方案3】:

在 Mastering Web Application Development with AngularJS book p.19 中写到

避免直接绑定到作用域的属性。双向数据绑定到 对象的属性(暴露在范围上)是一种首选方法。作为一个 根据经验,您应该在提供给 ng-model 指令(例如,ng-model="thing.name")。

作用域只是 JavaScript 对象,它们模仿 dom 层次结构。根据JavaScript Prototype Inheritance,范围属性通过范围分隔。为避免这种情况,dot notation 应该用于绑定 ng-models。

【讨论】:

  • 感谢您的参考。我认为这是与 ng-model 相关的一个有趣点。
【解决方案4】:

使用this 代替$scope 有效。

function AppCtrl($scope){
  $scope.searchText = "";
  $scope.check = function () {
    console.log("You typed '" + this.searchText + "'"); // used 'this' instead of $scope
  }
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

<div ng-app>
  <div ng-controller="AppCtrl">
    <input ng-model="searchText"/>
    <button ng-click="check()">Write console log</button>
  </div>
</div>

编辑:在写这个答案的时候,我的情况比这复杂得多。在 cmets 之后,我试图重现它以了解它为什么起作用,但没有运气。我认为以某种方式(不知道为什么)生成了一个新的子范围,this 指的是该范围。但是如果使用$scope,由于javascript的lexical scope特性,它实际上是指父$scope。

如果有人以这种方式测试并通知我们,那就太好了。

【讨论】:

  • 完美无瑕,你是个天才
  • 看起来附加到$scope 的函数创建了一个新范围(即在函数check() 中,this 指的是一个属于$scope 子范围的范围)。为什么会这样?你能解释一下吗?
  • 为什么在这种情况下我应该使用 'this' 而不是 '$scope'?因为它在我之前的项目中使用 $scope 有效,但这次使用 $scope 时同样的事情不起作用。但是“这个”已经奏效了。这是为什么呢?
  • 这行得通,但如果你说this.searchText = "something else" 或者即使$scope.searchText = "something else",它也不会更新视图。你能解释一下这个问题吗?
  • 应该。我使用上面的代码进行了尝试,它更新了视图。
【解决方案5】:

我遇到了同样的问题,这是因为我没有先在控制器顶部声明空白对象:

$scope.model = {}

<input ng-model="model.firstProperty">

希望这对你有用!

【讨论】:

    【解决方案6】:

    在处理非平凡视图(存在嵌套范围)时,我遇到了同样的问题。最后发现,由于 java-script 的基于原型的继承性质,在开发 AngularJS 应用程序时这是一个已知的棘手事情。 AngularJS 嵌套范围是通过这种机制创建的。并且从 ng-model 创建的值被放置在子作用域中,并不是说父作用域(可能是注入控制器的那个)不会看到该值,如果不使用点,该值还将隐藏在父作用域中定义的任何同名属性强制执行原型引用访问。有关详细信息,请查看专门用于说明此问题的在线视频,http://egghead.io/video/angularjs-the-dot/ 和 cmets 跟进。

    【讨论】:

      【解决方案7】:

      看看这个小提琴http://jsfiddle.net/ganarajpr/MSjqL/

      我已经(我假设!)完全按照您正在做的事情做,而且它似乎正在工作。你能检查一下这里不适合你吗?

      【讨论】:

      • 嗯..感谢您对此进行调查,我认为它应该可以工作...为什么它不适合我?更重要的是:为什么它适用于对象而不适用于纯字符串变量?也许是因为我在 routeProvider 中以不适当的方式引用了我的控制器?试图避免全局变量并将我的控制器作为 modulename.ctrlName 放入 controllers.js 文件中。会不会让人头疼?
      • 我不太确定为什么它不适合你。如果您可以在小提琴中隔离此问题,我想有人可以给您更好的答案:)
      • 好的,会的,现在刚刚完成工作,但肯定会在明天或晚上晚些时候完成,干杯!我命名我的 ctrl 的方式可能有问题,我们会看到...
      【解决方案8】:

      由于没有人提到这一点,可以通过将$parent 添加到绑定属性来解决问题

      <div ng-controller="LoginController">
          <input type="text" name="login" class="form-control" ng-model="$parent.ssn" ng-pattern="/\d{6,8}-\d{4}|\d{10,12}/" ng-required="true" />
          <button class="button-big" type="submit" ng-click="BankLogin()" ng-disabled="!bankidForm.login.$valid">Logga in</button>
      </div>
      

      还有控制器

      app.controller("LoginController", ['$scope', function ($scope) {
          $scope.ssn = '';
      
          $scope.BankLogin = function () {
              console.log($scope.ssn); // works!
          };
      }]);
      

      【讨论】:

      • 感谢您的回答,但是这有效吗?理想情况下,它应该在同一个范围元素上工作,而不是父级?
      【解决方案9】:

      对我来说,问题是通过将我的数据存储到一个对象(这里是“数据”)中来解决的。

      NgApp.controller('MyController', function($scope) {
      
         $scope.my_title = ""; // This don't work in ng-click function called
      
         $scope.datas = {
            'my_title' : "",
         };
      
         $scope.doAction = function() {
               console.log($scope.my_title); // bad value
               console.log($scope.datas.my_title); // Good Value binded by'ng-model'
         }
         
      
      });

      我希望它会有所帮助

      【讨论】:

      • 这里有点离题,但“数据”是“基准”的复数形式
      • @thethakuri 和匈牙利语中的“datum”是“日期”,所以我肯定也不会使用它:P
      • 我更喜欢华夫饼屋而不是 IHOP
      【解决方案10】:

      我刚刚使用绑定到 body-element 的 root_controller 遇到了这个问题。然后我将 ng-view 与角度路由器一起使用。问题是 Angular 总是在将 html 插入 ng-view 元素时创建一个新范围。因此,我的“检查”函数是在由我的 ng-model 元素修改的范围的父范围上定义的。

      要解决这个问题,只需在路由加载的 html 内容中使用专用控制器。

      【讨论】:

        【解决方案11】:

        您可以这样做以在 ng-keypress 中启用搜索输入文本并在 ng-click 中搜索图标:

        <input type="text" ng-model="searchText" ng-keypress="keyEnter(this,$event)" />
        <button ng-click="check(searchText)">Check!</button>
        
        in the controller
        $scope.search = function (searchText) {
                console.log(searchText);
            }
            $scope.keyEnter = function (serachText,$event) {
                var keyCode = $event.which || $event.keyCode;
                if (keyCode === 13) {//KeyCode for Enter key
                   console.log(searchText);
                }
            }
        

        【讨论】:

          【解决方案12】:

          我遇到了同样的问题。
          正确的方法是将“searchText”设置为对象内的属性。

          但是如果我想保持原样,一个字符串呢?好吧,我尝试了这里提到的每一种方法,但没有任何效果。
          但是后来我注意到问题只是在启动中,所以我只是设置了 value 属性并且它起作用了。

          <input type="text" ng-model="searchText" value={{searchText}} />
          

          这样,该值只是设置为 '$scope.searchText' 值,并在输入值更改时更新。

          我知道这是一种解决方法,但它对我有用..

          【讨论】:

            【解决方案13】:

            我遇到了同样的问题... 对我有用的解决方案是使用这个关键字............

            alert(this.ModelName);

            【讨论】:

              【解决方案14】:

              为表单控件提供名称属性

              【讨论】: