people get a deeper understanding of core Angular concepts. It answers three questions: 1. Why Learn to Think with Angular? 2. Does Angular Have a Philosophy? 3. How Do I Think with Angular?
Lets get started! David Graunke, david@thomasstreet.net Thomas STREET Thinking with Angular Why Learn to Think with Angular? Programming with Angular can be very different from programming with plain Javascript, jQuery, or even frameworks like Backbone. Angular offers so much to developers, but requires a slightly new way of thinking in return. If you learn to think like Angular, youll not only be faster and more effective when using built-in Angular tools, but your own code will be simpler, more expressive, and more direct. Does Angular have a Philosophy? I think it does. I would say its create declarative bindings between state and view. Lets unpack that a little. First, some denitions. By state I mean all the data youre storing in memory to support the app in the case of a todo-list app, it includes domain data like user objects and todo-list items. It also includes application-specic state like the current active page, and whether a given modal or drawer is activated. Thinking with Angular - 2 Now, you can use the view to store state for example, if the only way to know how many todo tasks a user has is by the return value of $(.todos).children().length, then youre storing state in the view. Hopefully youre not doing that, and state is instead stored in Javascript objects in memory. By view I mean the state of the document visible to the user the DOM. Now how about declarative bindings what does that mean? By declarative, I mostly mean not imperative. Imperative code is a sequence of instructions that change the execution state of the application. Heres an example of imperative code for creating a view (i.e., modifying the DOM) for our example todo-list app: for (var i = 0; i < todos.length; i++) { var el = $(<li>); el.innerHTML = todos[i].value; $(.todos).append(el); } This code is imperative because we have a sequence of instructions that each create and mutate objects, and manually modify the DOM. We Thinking with Angular - 3 create a new <li> html element and assign it to the variable el, we mutate (update) a eld on el, and we insert it into the DOM. Each step changes the execution context. Lets look at the Angular equivalent of that code: <li ng-repeat=todo in todos>{{todo.value}}</li> Whats going on here? Weve taken a <li> HTML element and added a custom attribute ng-repeat. By adding that attribute, were telling the Angular template compiler that we always want one <li> element for each todo in our todo list. Weve also put a reference to the todo for that <li> in curly-brackets between the tags. That tells the Angular compiler that we want to bind that chunk of the DOM to the value of the todo. That brings us to our last denition: bindings. In this context, were contrasting binding with assignment. Assignment is saying el.innerHTML = todos[i].value its a one-time operation that copies the contents of todos[i].value and puts it in el.innerHTML. Its an imperative action that alters our state. What were calling binding is a declaration of a relationship between two values. Thinking with Angular - 4 Angular has one-way bindings that supercially resemble the imperative assignment above. However, after we assign el.innerHTML, updates to todos[i].value dont have any effect on it. With a one-way binding, that piece of the DOM will be automatically updated to reect its bound value. Bindings can also be two-way, which well talk about in a moment. In our Angular example above, we have two bindings. Our rst is ng-repeat, and the second is {{todo.value}}. I mentioned above that Angular will create one list item element for each item in todos, just like our imperative code does. But theres a difference. In the Angular example, were not just telling Angular to create a <li> right now were telling it to bind this chunk of the DOM to the todos data, making sure that theres always a <li> element for each item in todos. That means when we alter todos by doing something like todos.push({date: new Date(), value: Write Angular handbook!}) the DOM will automatically be updated! Now, without Angular, we could certainly track each time todos changes and then re-trigger our imperative view-building code. Wed have a Thinking with Angular - 5 simple (declarative!) model in our heads make sure I always have one <li> element for each todo, displaying its content but wed be forced to translate that simple model into complex data tracking and DOM manipulation. With Angular, were able translate our simple mental model into the application with a minimum of fuss. The Angular code above gives us an example of a declarative one-way binding every time todos changes, the DOM is updated. But Angular also gives us two-way bindings, where data not only propagates forward to the DOM, but user actions propagate data back to the application state! Lets look at an example: <script> ourApp = function($scope) { $scope.rating = 50; }; </script> <body ng-app ng-controller=ourApp> Rate: <input type=range ng-model=rating min=0 max=100 / > Your rating: {{rating}} </body> (You can play with a live version of this here: http://plnkr.co/edit/MJ1yyh4HvbGIJ9WK1RVh?p=preview) Thinking with Angular - 6 Here we see an Angular controller the ourApp is a factory object that creates our data on our controller, called $scope, which is just an object we use to hold our state. You also see the attribute ng-app on the body element, which is a signal to Angular that everything inside the body should be compiled as an Angular app. Play with the linked version of the code what happens when you scrub the slider back and forth? The readout of the rating updates. Now, the readout (following Your rating:) is a simple one- way binding like we saw before whats interesting is that were updating the value of $scope.rating without any event handlers! Instead, we use the ng-model attribute to bind the value of the input slider to the value of rating. When the value of the slider changes, the value of rating changes, which is reected in the DOM.
If something else in our code (like a message from the server) updated the value of $scope.rating, the position of the slider would change Angular has bound them so theyre always the same. Rather than wiring up a bunch of event handlers to keep our data and view in sync, weve instead used Angular to declare relationships between values. Thinking with Angular - 7 Now, Angular is not a purely declarative framework. Most apps will have a lot of imperative code modifying the application state code like our todos.push example above. But using Angular will save you from hundreds to thousands of lines of code dedicated to keeping your state and view and sync and youll be happy to leave that chore to the library developers. How Do I Think with Angular?
So, why did I think it was worth exploring the philosophy behind Angular? When I started with Angular, I had no trouble creating simple bindings like our examples above the introductory documentation is great, and for beginners Angular can be simple and intuitive. But when trying to extend Angular by creating new directives, or trying to integrate third-party libraries I struggled to create code that went with the grain of Angular. It took me a while to understand just why Angular worked the way it did. But when I did, I was able to create code that captured some of the philosophy of Angular code that let developers (including myself!) declare relationships, rather than writing imperative code. Thinking with Angular - 8 There are lots of things we didnt touch on here that will help you write declarative code: custom directives, Angulars watch system, and the $ngModel module to name a few. Learning to write declarative code involved using all of those things. Heres a list of techniques I learned, which I hope can be a jumping off point for you as you dive into building and thinking with Angular: 4. Avoid imperative code that modies the DOM. 5. Avoid events let Angulars change-watch system propagate updates. 6. Use directives to hide imperative code for interacting with third-party libraries. Put the imperative code they need in compile and link functions. 7. Use the $ngModel module to create two- bindings in your custom directives. I hope this has been helpful! Angular can be very different from other ways of programming, but investing in learning to think with Angular, rather than against Angular, can be a great help. - David Thinking with Angular - 9