AngularJS–Part 3, Inheritance
Introduction
This is a series of posts to describe our approach to slowly migrate a Silverlight based client of a huge and complex LOB to an HTML5/CSS3/JavaScript based client. These posts report on the lessons learnt and are meant to be used as a reference for our development teams. The first few post can be found here
I have been surprised by the positive feedback to my first few posts. This encourages me to continue my series full steam. Thanks to all readers sharing their thoughts!
In this post I want to discuss the aspect of controller inheritance.
JavaScript is not an OO language
Once again it is important to remind ourselves that JavaScript is not one of the classical OO computer languages. Yes, we can write object oriented code in JavaScript but this is certainly not the best language to do so. Much more should we embrace the functional aspect of JavaScript. Many of the popular libraries do this. jQuery seems to be the most prominent example and also AngularJS follows this line clearly. In this regard when talking about inheritance we should not expect the same behavior as we know from C# or Java, which are two of the most popular OO languages.
Controller inheritance
What can I expect then from controller inheritance? Let’s look at a sample right away. We create two controllers, a parent and a child controller. We can add the necessary code to the app.js file of our sample application. Let’s just start with the definition of the empty controllers
Whenever we define a new controller we give it a name and add it to the controller collection of our Angular app. The controller itself is a function which wraps the data (or the model) and the logic which constitute the controller. So far our two new controllers are just empty hulls.
Simple variables
Let’s start by defining a simple variable title in the parent controller and content in the child controller. It is important that these are simple (string) variables and not objects.
Now we need a view where we use these two new controllers. Let’s create a new HTML page called part3.html. As we have learned in the previous post a controller manages part of a view. Often this is a div tag containing the Angular directive ng-controller whose value is set to the name of the controller. To make the child controller ChildCtrl a true child of the parent controller ParentCtrl the area managed by the parent needs to wrap the area managed by the child controller, or said differently, the child controller must be wrapped by the parent controller. This is a typical layout
As you can see, the outer parent div wraps the inner child div . Make sure you don’t forget to also add the ng-app directive referencing our application called myApp. Usually we add the directive to the body tag. Also make sure you have referenced the Angular JavaScript angular.js file and our application JavaScript file app.js. Furthermore we also need our app.css file containing the layout defined so far. I have added the scope class to the two div tags to be able to visually see the parent and the child context or scope on the view.
Now let’s add some input tags that bind to the title and the content values defined in the controllers. We add
- an input bound to title to the parent div
- an input also bound to title (defined in the parent controller) to the child div
- an input bound to content to the child div
Save and open the view part3.html in your favorite browser. You should see something similar to this
We can nicely see how the child area is nested inside the parent area. We can also see how the binding works as we see the texts displayed as we have defined them in the controllers. It is important to note how the child inherits the title value from its parent.
Now type something into the first input box (owned by the parent controller) and observe how the text in the input box owned by the child controller changes too.
That’s what we expect, right? But now to the interesting part, change the text in the input box in the child area and observe the outcome.
Apparently my changes entered in the child area did not affect the value displayed in the parent area! And if you now change the value in the parent area again, the child value won’t change anymore.
What happened here? The title variable – a simple variable of type string – was defined on the parent $scope. At first the child inherited this value. But then, once the user changes the title value in a control bound to title located in the child area Angular creates a new variable title on the child $scope and from this moment on child and parent title are decoupled. Each controller has its own instance.
Complex types
If we want to avoid this behavior of having a local instance in the child scope we need to use complex types, that is objects. Let’s change our definition in the parent controller as follows
Our title is now a property of a variable model which in turn is an object. We also have to adjust our binding in the view as shown below
If we refresh the browser and play with the two input boxes we can verify that this time model.title does not get redefined on the client scope and remains a property of the parent scope inherited by the child scope. This is a very important difference in behavior that we need to be aware of.
Now the clever person might ask: “And what happens if I reset the model object all together from the client $scope?”. Ah, good question I say to myself. Let’s try it. Add a button to the child div and bind the click event to a function called setModel.
define the setModel on the child $scope as follows
Refresh the browser and verify that after clicking the button once again the two input boxes are disconnected. We have now our own instance of model defined on the child $scope.
Inheriting functions
Now we want to explore how functions are inherited. For this purpose we define a function greet on the parent $scope. This function when called just shows an alert box greeting the user.
We then add a button to the parent and one to the child area and bind both click events to this newly defined function
Please refresh the browser and confirm that no matter which of the two buttons you click the application will always show the same alert box. With this we have proven that a child controller can indeed inherit functions from its parent controller.
But what happens if we redefine the function greet on the child $scope? Let’s try and define such a function which shows an alert box with a different text.
Ok, when doing that we realize that now the button defined in the child area displays the new alert message. This means that we have just broken the inheritance chain and the child has now its very own definition of a function with name greet.
Summary
We have discussed how inheritance works in the context of Angular controllers. In Angular we talk of a child controller if the view area that is managed by the child controller is nested inside the view area that is managed by the parent controller. Simple values (e.g. strings, numbers, booleans, etc.) are only inherited by a child controller as long as they are not redefined on the child $scope. Properties of object on the other hand are always inherited by the child controller as long as we do not redefine the whole object on the client $scope. A child controller also inherits functions defined on the parent controller as long as they are not redefined on the child $scope.
In the next post I will have a look on how we get data from a server by using the $http service provided by AngularJS.