The AngularJS $scope is not the MVC Model
Monday 15 April 2013
- Getting started with AngularJS
- Creating an AngularJS Controller
- The AngularJS $scope is not the MVC Model
- Using repeating elements in AngularJS
- Filtering the data in an AngularJS ngRepeat element
- Using the AngularJS FormController to control form submission
- Creating an AngularJS Directive
In the previous post I showed how to create and use an MVC Controller in AngularJS to manage the logic of an application. In this post I showed how to use the $scope variable that is passed into the Controller to share data between the Controller and the View. This may lead you to believe that the $scope is the MVC Model used but you would be completely wrong.
The $scope is not the MVC Model
There is a common believe that the $scope object in AngularJS is the MVC Model, or ViewModel, but that is wrong. In fact the $scope is just the glue object that should be used to share things between the controller and the view. Now that sounds awfully like a model but it isn’t. In fact you will see a lot of people do just that and as a result report bugs like this one. In fact this isn’t a bug and it works as designed. The correct way is to add the model as a property to the @scope instead so the controller and the view can share it.
So what is the problem with treating the $scope as the Model?
The correct way of working with $scope objects
It turns out the correct way of working with scope is pretty easy.
Treat $scope as read-only in views
Treat $scope as write-only in controllers
Or an even simpler rule:
An ng-model directive should always contain a “.” so it updates a property on the model which is on the scope and not a property on the scope directly. If you don’t have a “.” in your ng-model you are doing it wrong!
But don’t just take my word for it. Take a good look at this video from Miško Hevery starting at about 0:29. BTW the rest of this video is highly recommended as well.
Fixing our calculator controller and view
With this knowledge is it easy to see that the controller and view from the previous post are actually wrong as the view is writing directly to the $scope x and y properties. In fact the only reason this appeared to work is that the example was so simple that no nested scopes where created. Still lets do the proper thing and fix the example.
The corrected view is now:
And the corrected controller now is as follows.
Maybe not a big change but important to get right.