7

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/

Community
  • 1
  • 1
Alon
  • 7,618
  • 18
  • 61
  • 99
  • 1
    There's not enough code here to tell you precisely what's wrong. Can you show us an example of an interaction that's causing the model to update, but not the view? If you have a working example on jsFiddle or similar, that's even better. – Michelle Tilley Jan 17 '13 at 08:00
  • If you could maybe post an excerpt from your view code, showing how it interacts with the Controller and where it expects myModel, that would be fantastic. Also helpful would be if you could flesh out the MyModel function somewhat. As Brandon said above, what you have there isn't really sufficient to telly you what's going on. – Abraham P Jan 17 '13 at 08:14
  • @BrandonTilley, I added a JSfiddle example: http://jsfiddle.net/DAk8r/1/ – Alon Jan 17 '13 at 08:21
  • @AbrahamP, I added a JSfiddle.. – Alon Jan 17 '13 at 08:23
  • I fixed an issue there - this is the updated version: http://jsfiddle.net/DAk8r/2/ – Alon Jan 17 '13 at 08:24

4 Answers4

10

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

Kaushik Shrestha
  • 932
  • 1
  • 11
  • 26
  • 1
    Inside my controller $scope is not defeinded as you can see in jsfiddle.net/DAk8r/2 – Alon Jan 17 '13 at 08:26
  • This works great. But could you please explain why its needed. It should reflect by default as angular promises [two way data binding](https://docs.angularjs.org/tutorial/step_04) – Sami Aug 17 '15 at 12:35
9

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/

Community
  • 1
  • 1
Michelle Tilley
  • 157,729
  • 40
  • 374
  • 311
1

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

Abraham P
  • 15,029
  • 13
  • 58
  • 126
  • 2
    Controllers are not singletons. You can instantiate MyCtrl multiple times on the same page. Maybe you are confusing services with controllers -- services are singletons. – Mark Rajcok Jan 17 '13 at 15:51
0

In Angular apps, models are normally sometimes implemented on $scope, not as a separate JavaScript object. Here is a rewrite of your app showing how to do that

function myModelCtrl($scope) {
  $scope.number = 0;
  $scope.updateNumber = function () {
    $scope.number += 1;
    alert('new number should display ' + $scope.number);
  }
}

HTML:

<div ng-controller="myModelCtrl">
  <div>Number is {{number}}</div> 
  <a ng-click="updateNumber()">Update Number</a>
</div>

Here's a fiddle, with no jQuery, and it shows the "normal way" of setting up a fiddle for Angular.

Mark Rajcok
  • 362,217
  • 114
  • 495
  • 492
  • 2
    AngularJS best practice is often cited that ["Scope is not your model. Scope is what you attach your model to."](http://youtu.be/ZhfUv0spHCY?t=30m) For very simple controllers, this kind of thing may be okay, but normally you'll want to have your own model layer. – Michelle Tilley Jan 17 '13 at 17:31
  • @Brandon, thanks for the video link (I hadn't seen that one yet). I feel I now understand this sentence from the [Scope](http://docs.angularjs.org/guide/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 Jan 17 '13 at 17:58
  • 1
    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](http://stackoverflow.com/a/14667066/215945) by Brandon. – Mark Rajcok Feb 02 '13 at 22:40
  • 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. – Michelle Tilley Feb 02 '13 at 23:05