alchemication
alchemication

Reputation: 5314

Ng-model does not update controller value

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

Upvotes: 284

Views: 282323

Answers (14)

Will Stern
Will Stern

Reputation: 17579

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

Upvotes: 651

Prateek Magarde
Prateek Magarde

Reputation: 1

Provide a name property to your form control

Upvotes: -1

SnktJavaMaster
SnktJavaMaster

Reputation: 21

I was facing same problem... The resolution that worked for me is to use this keyword..........

alert(this.ModelName);

Upvotes: 2

Hike Nalbandyan
Hike Nalbandyan

Reputation: 1056

I had the same problem.
The proper way would be setting the 'searchText' to be a property inside an object.

But what if I want to leave it as it is, a string? well, I tried every single method mentioned here, nothing worked.
But then I noticed that the problem is only in the initiation, so I've just set the value attribute and it worked.

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

This way the value is just set to '$scope.searchText' value and it's being updated when the input value changes.

I know it's a workaround, but it worked for me..

Upvotes: 2

Millsionaire
Millsionaire

Reputation: 389

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!

Upvotes: 14

Damax
Damax

Reputation: 1487

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

Upvotes: 75

Eric Herlitz
Eric Herlitz

Reputation: 26267

Since no one mentioned this the problem can be resolved by adding $parent to the bound property

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

And the controller

app.controller("LoginController", ['$scope', function ($scope) {
    $scope.ssn = '';

    $scope.BankLogin = function () {
        console.log($scope.ssn); // works!
    };
}]);

Upvotes: 7

Feyyaz
Feyyaz

Reputation: 3206

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.

Upvotes: 46

Plici St&#233;phane
Plici St&#233;phane

Reputation: 291

For me the problem was solved by stocking my datas into an object (here "datas").

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

});

I Hop it will help

Upvotes: 5

Naoufal Gaffa
Naoufal Gaffa

Reputation: 21

You can do that to enable search in ng-keypress enter for input text and in ng-click for the icon:

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

Upvotes: 2

moritz
moritz

Reputation: 25757

I just had this very issue using a root_controller bound to the body-element. Then I was using ng-view with the angular router. The problem is that angular ALWAYS creates a new scope when it inserts the html into ng-view element. As a consequence, my "check" function was defined on the parent scope of the scope that was modified by my ng-model element.

To solve the problem, just use a dedicated controller within route-loaded html content.

Upvotes: 2

efirat
efirat

Reputation: 3867

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.

Upvotes: 61

Roger Jin
Roger Jin

Reputation: 563

I came across the same issue when dealing with a non-trivial view (there are nested scopes). And finally discovered this is a known tricky thing when developing AngularJS application due to the nature of prototype-based inheritance of java-script. AngularJS nested scopes are created through this mechanism. And value created from ng-model is placed in children scope, not saying parent scope (maybe the one injected into controller) won't see the value, the value will also shadow any property with same name defined in parent scope if not use dot to enforce a prototype reference access. For more details, checkout the online video specific to illustrate this issue, http://egghead.io/video/angularjs-the-dot/ and comments following up it.

Upvotes: 11

Ganaraj
Ganaraj

Reputation: 26841

Have a look at this fiddle http://jsfiddle.net/ganarajpr/MSjqL/

I have ( I assume! ) done exactly what you were doing and it seems to be working. Can you check what is not working here for you?

Upvotes: 6

Related Questions