In any web application we have the "model" - our data that keeps changing, and the "view" - the DOM or HTML that the user sees in the browser. It is hard to update the "view" ourselves, and the AngularJS framework takes the update pain away. Whenever one designs an Angular application, one should think about the data first, and then the view will be an easy part.
Let us start with an empty angular page
1 |
|
1 | angular.module('AngularModelExampleApp', []) |
We have an empty page. The body of the page will be controlled by the Angular framework.
In particular the body will be updated by the controller called AngularModelController
.
The controller function is nothing special, except that it injects as its argument an special
object $scope
. This object is created by the Angular framework behind the scenes and should store
all our model data - the data we want or might to want to somehow show in our HTML (view).
Let us add a header to the empty page.
1 | angular.module('AngularModelExampleApp', []) |
The model object $scope
is now { header: 'Angular Model vs View' }
. How does the page (the DOM = HTML) reflect
this? We can reflect the model by using a template and writing almost any expression that refers to the
header
property. For example we can simply use the text
1 | <body ng-app="AngularModelExampleApp" ng-controller="AngularModelController"> |
If we reload the page we will see the following HTML if we inspect the page's "Elements" tab
1 | <body ng-app="AngularModelExampleApp" ng-controller="AngularModelController" class="ng-scope"> |
Notice that the Angular framework automatically updated or synchronized the DOM with our template
by using the data from the model object called $scope
. What if the model changes? Let us
change the header
5 seconds after the application starts.
1 | angular.module('AngularModelExampleApp', []) |
Hmm, nothing is happening. Well, something is happening, trust me. The $scope.header
property has been changed
5 seconds after the application starts. You can even inspect the scope if you do not believe me.
Open the browser console and grab the $scope
object to inspet - it is just a plain object after all!
1 | // in the browser console |
Why is the changed model data not reflected in the view? Because Angular has no idea that the model data
has changed. The scope object is a plain object that does not tell the view or anyone when the data has been changed.
In our case, the Angular framework does not see / override / listen to setTimeout(function () { }, 5000)
call.
Usually, things like setTimeout
, setInterval
, event listeners, jQuery callbacks, websocket messages need
to manually tell Angular framework - the model data has been changed, please propagate the changes and update
the DOM. It is simple to do.
1 | angular.module('AngularModelExampleApp', []) |
All we had to do was to call $scope.$apply()
after changing the data.
Bonus 1 - avoiding $apply
In most cases we do not have to call $scope.$apply()
- any own Angular code does it for us automatically
at the end. If you handle click events using ng-click,
then the $scope.$apply()
will be called for you. AngularJS even comes with its own
$timeout service you can use instead of the plain
setTimeout
.
1 | angular.module('AngularModelExampleApp', []) |
The HTML is updated automatically 5 seconds after the application starts. We used $timeout
which is
part of the Angular framework. The $timeout
suspects that something in our data model has changed, thus
it will always call $scope.$apply()
for us. If nothing has changed, the framework will see this, and
nothing will be updated. If something has changed, like in our example, every place in the HTML page
that refers to any $scope
property will be updated.
Bonus 2 - multiple view templates
We have separated the model object from the view so we do not have to worry how to show or update
the view's HTML when the data changes. For example we can have several visually different
places that put the $scope.header
data on the page.
1 | <body ng-app="AngularModelExampleApp" ng-controller="AngularModelController"> |
// renders
Angular sync example
This is a small example, showing ANGULAR SYNC feature.
The h2
element will have the header
value as is, while the paragraph below will have the
header
value, but the string will be rendered in the HTML in upper case letters. Whenever
the header
property in the model is changed, both places are updated correctly.
This is a very common approach in AngularJS. Rather than having a separate precomputed value for the upper cased header in the model, and try to keep it in sync with the lower cased header, we have one model, and only its view representation is generated differently in the DOM by the framework.