Ng-model does not update controller value

270,298

Solution 1

Controller as version (recommended)

Here the template

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

The JS

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

An example: http://codepen.io/Damax/pen/rjawoO

The best will be to use component with Angular 2.x or Angular 1.5 or upper

########

Old way (NOT recommended)

This is NOT recommended because a string is a primitive, highly recommended to use an object instead

Try this in your markup

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

and this in your controller

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

Solution 2

"If you use ng-model, you have to have a dot in there."
Make your model point to an object.property and you'll be good to go.

Controller

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

Template

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

This happens when child scopes are in play - like child routes or ng-repeats. The child-scope creates it's own value and a name conflict is born as illustrated here:

See this video clip for more: https://www.youtube.com/watch?v=SBwoFkRjZvE&t=3m15s

Solution 3

In Mastering Web Application Development with AngularJS book p.19, it is written that

Avoid direct bindings to scope's properties. Two-way data binding to object's properties (exposed on a scope) is a preferred approach. As a rule of thumb, you should have a dot in an expression provided to the ng-model directive (for example, ng-model="thing.name").

Scopes are just JavaScript objects, and they mimic dom hierarchy. According to JavaScript Prototype Inheritance, scopes properties are separated through scopes. To avoid this, dot notation should use to bind ng-models.

Solution 4

Using this instead of $scope works.

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>

Edit: At the time writing this answer, I had much more complicated situation than this. After the comments, I tried to reproduce it to understand why it works, but no luck. I think somehow (don't really know why) a new child scope is generated and this refers to that scope. But if $scope is used, it actually refers to the parent $scope because of javascript's lexical scope feature.

Would be great if someone having this problem tests this way and inform us.

Solution 5

I had the same problem and it was due to me not declaring the blank object first at the top of my controller:

$scope.model = {}

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

Hope this will works for you!

Share:
270,298

Related videos on Youtube

alchemication
Author by

alchemication

Updated on March 24, 2021

Comments

  • alchemication
    alchemication about 3 years

    Probably silly question, but I have my html form with simple input and button:

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

    Then in the controller (template and controller are called from routeProvider):

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

    Why do I see the view updated correctly but undefined in the console when clicking the button?

    Thanks!

    Update: Seems like I have actually solved that issue (before had to come up with some workarounds) with: Only had to change my property name from searchText to search.text, then define empty $scope.search = {}; object in the controller and voila... Have no idea why it's working though ;]

    • wroniasty
      wroniasty over 11 years
      are you sure you are using this controller in this part of the document? can you post a minimal failing example?
    • alchemication
      alchemication over 11 years
      Yes, 100% sure the controller is ok, that issue seems to be familiar to me... Surprisingly it works when I change the property name from searchText to search.text, any idea why??
    • Arthur Frankel
      Arthur Frankel almost 11 years
      This is a great question. I ran across the same issue and following the "Update" block solved it for me as well. I'm very curious as well why this solution works - or more importantly why can't I just a $scope.var and have it updated by angular.
    • alchemication
      alchemication almost 11 years
      @Arthur: It's kinda not obvious but ng-model only creates a sort of speak local variable in your view, there fore if you want to keep it this way you would need to pass it into the check() function, like: check(searchText) and your controller will recognise it then. Hope it helps
    • Arthur Frankel
      Arthur Frankel almost 11 years
      Thanks, but why does your hash solution work (i.e., hash var vs. regular var)?
    • Dan Bechard
      Dan Bechard over 9 years
      For the record, it's spelled voila, not vuala, wolla, etc.
    • Willa
      Willa over 8 years
      I think the answer you are looking for is at stackoverflow.com/a/14049482/1217913
    • Kalamarico
      Kalamarico almost 7 years
      Great explanation in that link Willa, thanks!
  • alchemication
    alchemication over 11 years
    Hmmmm.. thanks for looking into this, I would presume that it should work... why isn't it working for me? And what's more important: Why does it work with objects and doesn't with plain string variables?? Maybe cause I refer my controllers in an in-appropriate way in the routeProvider?? Was trying to avoid globals and have put my controllers as modulename.ctrlName into controllers.js file. Could that cause a headache?
  • ganaraj
    ganaraj over 11 years
    I am not really sure why it doesnt work for you. If you could isolate this issue in a fiddle, I guess someone would be able to give you a better answer :)
  • alchemication
    alchemication over 11 years
    Okay, will do, just finishing work now but will do that for sure tomorrow or later in the evening, cheers! There might be a problem with the way I've namespaced my ctrl's, we'll see...
  • alchemication
    alchemication over 11 years
    Thanks!! That is perfect (and also solving a lot of issues I had in the past)! +1 ;)
  • cdmckay
    cdmckay almost 11 years
    This only works one-way... what if you want to change the value of searchText?
  • cdmckay
    cdmckay almost 11 years
    This also doesn't answer the "why?" question.
  • danikoren
    danikoren over 10 years
    what if i don't want to use any button? i need to submit on enter for instance
  • Damax
    Damax over 10 years
    Saniko => To submit on enter, you must use form tag and ng-submit on it (docs.angularjs.org/api/ng.directive:ngSubmit)
  • keslert
    keslert almost 10 years
    This is the real answer.
  • Catfish
    Catfish almost 10 years
    This worked for me, but can you elaborate on "this quirk" in javascript?
  • Will Stern
    Will Stern almost 10 years
    @Catfish With prototypal inheritance, whenever you write to the child property, the reference/connection to the parent property ceases to exist. The way Angular Scopes models, if you don't have a dot, then a new property within your child scope is created. This video explains it in more detail: youtube.com/watch?v=ZhfUv0spHCY&feature=youtu.be&t=30m
  • zashu
    zashu over 9 years
    @cdmckay - It has to do with the way scopes are inherited via ng-model and the fact that the data is a String (a primitive) and thus not updated by reference. See Will Stern's answer.
  • Don Barry
    Don Barry over 9 years
    Gracias Boss. This is true! However, it doesnt seem to be a consistent quirk as I encounter the problem only when using with Ionic framework
  • Reza
    Reza over 9 years
    Could you explain why this happens?
  • Will Stern
    Will Stern over 9 years
    @RezaRahmati When you try to access a property on a child scope, it first checks the child...if not found, it checks the parent for that property and so on. If the child adds the property, it now no longer looks to the parent property, it will always modify the child property of the same name leaving the parent property untouched.
  • user3677331
    user3677331 almost 9 years
    Here it works without a dot. It mostly works without a dot and sometimes only it doesn't. Haven't noticed a pattern, have you?
  • Will Stern
    Will Stern almost 9 years
    @user3677331 It works fine without a dot until you have a child scope that tries to talk to it (like an item within an ng-repeat for example). Say your model name was "phone" Your child scope creates "phone", then you get a scope conflict because child-scope has a "phone" variable and thus can't access "phone" on the parent scope. Whereas if the child scope creates user.phone, it will be added to the parent's user object so both scopes are pointing to the same object
  • fjcero
    fjcero almost 9 years
    Flawless, you're a genius
  • Jakub Kotrs
    Jakub Kotrs almost 9 years
    Thank you, but for me it works without .$modelValue. Maybe newer version.
  • CodeManiak
    CodeManiak almost 9 years
    Very helpful. I wasn't sure I would locate an answer to a relatively vague question but this was dead on.
  • Kings
    Kings over 8 years
    thanks for this reference. I consider this an interesting point related with ng-model.
  • Xun Yang
    Xun Yang over 8 years
    Looks like the functions attached to $scope creates a new scope(i.e. in function check(), this refers to a scope that's a child scope of $scope). Why is it so? Can you give some explanations?
  • Rashedul.Rubel
    Rashedul.Rubel about 8 years
    why should i use 'this' instead of '$scope' in this case? because it worked using $scope in my previous project but this time same thing does not work using $scope. But 'this' has worked. Why is that?
  • devios1
    devios1 about 8 years
    Angular really should at least warn you if you attempt to bind directly to a scope property for this very reason. It seems this is confusing a lot of people (myself included).
  • Shri
    Shri about 8 years
    this works, but if you say this.searchText = "something else" or even if $scope.searchText = "something else", it doesn't update the view. can you explain this problem?
  • Feyyaz
    Feyyaz about 8 years
    it should. I tried it using the code above, it updated the view.
  • thethakuri
    thethakuri about 8 years
    Little off topic here, but 'data' is a plural term for 'datum'
  • Hasan Tuna Oruç
    Hasan Tuna Oruç almost 8 years
    perfect solution. thanx.
  • skmasq
    skmasq almost 8 years
    Thank you, saved so much time. Works with angular 1.0.8
  • Wand Maker
    Wand Maker almost 8 years
    Thanks, I wasted couple of hours with this issue - luckily I hit this answer.
  • EpicPandaForce
    EpicPandaForce almost 8 years
    @thethakuri and "datum" in hungarian is "date", so I sure wouldn't use that either :P
  • Hardik Bharadava
    Hardik Bharadava over 7 years
    @kba I'm having the Same Problem. Angular's ng-model behaviour is if it is changed it will be changed into Controller also. but why this happens, can anyone explain, what is the issue?
  • Damax
    Damax over 7 years
    @HP's411 it's because a string is a primitive. When Angular assign the value it change the pointer to the value, so the controller looks at the old value because it have the old pointer to the value.
  • Nico Westerdale
    Nico Westerdale about 7 years
    I prefer waffle house to IHOP
  • Dheeraj Avvari
    Dheeraj Avvari about 7 years
    You are a life saver! Thanks for the Answer @Damax
  • V31
    V31 almost 7 years
    thanks for the answer, however y is this working? it should ideally be working on the same scope element right and not the parent?
  • Nico Westerdale
    Nico Westerdale over 6 years
    It's depressing that this rule is not shown in many places on the web. e.g. w3schools.com/angular/angular_model.asp which is incorrect.
  • Jiro Matchonson
    Jiro Matchonson over 5 years
    That was a nasty one, tnx much!, I had input ng-model="email" and ng-model="userPassword". First one worked the second one no. After adding dots everything work but I would specify this as an Angular bug.