The view is not updated when the model updates in AngularJS

23,488

Solution 1

The crux of your problem is that you're trying to mix AngularJS with a very traditional, "jQuery-soup style" JavaScript pattern. In Angular, you should always use directives to do DOM manipulation and interaction (including clicks). In this case, your code should look more like the following:

<div ng-controller="myModelCtrl">
  <div>Number is {{myModel.number}}</div>
  <a ng-click="myModel.updateNumber()">Update Number</a>
</div>
function myModelCtrl($scope) {
   var myModel = new MyModel();
   $scope.myModel = myModel;
}

function MyModel() {
  var _this = this;
  
  _this.number = 0;
  
  _this.updateNumber = function() {
     _this.number += 1;
    alert('new number should display ' + _this.number);
  }
  
  return _this;
}

Notice how, instead of setting up a manual click handler with jQuery, we use Angular's built-in ng-click directive. This allows us to tie in to the Angular scope lifecycle, and correctly update the view when the user clicks the link.

Here's a quote from the AngularJS FAQ; I've bolded a part that might help you break the habit.

Common Pitfalls

The Angular support channel (#angularjs on Freenode) sees a number of recurring pitfalls that new users of Angular fall into. This document aims to point them out before you discover them the hard way.

DOM Manipulation

Stop trying to use jQuery to modify the DOM in controllers. Really. That includes adding elements, removing elements, retrieving their contents, showing and hiding them. Use built-in directives, or write your own where necessary, to do your DOM manipulation. See below about duplicating functionality.

If you're struggling to break the habit, consider removing jQuery from your app. Really. Angular has the $http service and powerful directives that make it almost always unnecessary. Angular's bundled jQLite has a handful of the features most commonly used in writing Angular directives, especially binding to events.

Finally, here your example, working using this technique: http://jsfiddle.net/BinaryMuse/xFULR/1/

Solution 2

try $scope.$apply() inside your controller after you update your model

Solution 3

http://jsfiddle.net/zCC2c/

Your problem is twofold.

1) There was a syntax error in your jsfiddle, as DOMElements (such as the a returned by $(this) inside your clickhandler have no prevent default. You actually needed to pass an event. (Minor syntax issue).

2) By putting your variable definition within the controllers scope like so:

 function MyCtrl($scope){
     $scope.foo = foo;
 }

You ensure it gets run once per class instantiation.

What you want is:

 function MyCtrl($scope){
      $scope.someAngularClickHandler=function(){
         $scope.foo = foo;
      }
 }

And then on your link

   <a href="foo" id="bar" ng-click="someAngularClickHandler()">

For a more exhaustive codesample see the linked jsfiddle.

A good overview of the theory can be found in the first talk here:

http://www.youtube.com/watch?feature=player_embedded&v=VxuN6WO3tIA

Share:
23,488
Alon
Author by

Alon

Updated on July 09, 2022

Comments

  • Alon
    Alon almost 2 years

    I have read threads on this issue such as: The view is not updated in AngularJS but I still can't understand how to apply it on my simple example.

    I have this function:

    function MyPageView($scope) {
      var myModel = new MyModel();
      $scope.myModel = myModel;
    }
    

    when myModel is updated elsewhere in the code (when user clicks, interacts, send XHR requests) it doesn't update my view. I understand I need to do something with $apply but I didn't understand where and how.

    Can someone explain to me how do I solve this issue for this simple use-case?

    My model looks something like this (if that is necessary for the question) - it has no AngularJS code inside of it:

    var MyModel = function() {
      var _this = this;
      ...
      _this.load = function(){...};
      _this.updateModel = function(){...};
      ...
      return _this;
    }
    

    adding a JSfiddle example: http://jsfiddle.net/DAk8r/2/

  • Alon
    Alon over 11 years
    Inside my controller $scope is not defeinded as you can see in jsfiddle.net/DAk8r/2
  • Mark Rajcok
    Mark Rajcok over 11 years
    Controllers are not singletons. You can instantiate MyCtrl multiple times on the same page. Maybe you are confusing services with controllers -- services are singletons.
  • Michelle Tilley
    Michelle Tilley over 11 years
    AngularJS best practice is often cited that "Scope is not your model. Scope is what you attach your model to." For very simple controllers, this kind of thing may be okay, but normally you'll want to have your own model layer.
  • Mark Rajcok
    Mark Rajcok over 11 years
    @Brandon, thanks for the video link (I hadn't seen that one yet). I feel I now understand this sentence from the Scope page: "scope is an object that refers to the application model." Do you see services as a way to define the model layer? If not, where do you recommend models be defined? Are you aware of any good examples of Angular apps that have a separate model layer?
  • Mark Rajcok
    Mark Rajcok about 11 years
    FYI for other readers, in other comment exchanges with Brandon, I think he would say that services are the place to define the models. For example, see this answer by Brandon.
  • Michelle Tilley
    Michelle Tilley about 11 years
    Woah, small world! (Sorry I never got back to this comment.) Services are especially good for model objects because they can be injected, and thus easily mocked in tests.
  • Sami
    Sami over 8 years
    This works great. But could you please explain why its needed. It should reflect by default as angular promises two way data binding
  • Alex78191
    Alex78191 almost 7 years
    Why do you need var _this = this;?