Angular for the jQuery developer

One of the most commonly asked questions about learning AngularJS is how to think about it when coming from a jQuery background where manually manipulating the DOM tree is a requirement to adding interactivity to our web pages.

jQuery itself is simply a DOM manipulation utility belt. It is an imperative wrapper around DOM manipulation, not a tool for building web apps. We obviously can use jQuery to build dynamic websites, but that’s like using a hammer to cut through wood… Why not just buy a saw and do it right?

This article is intended on illustrating how to think about Angular, not abandoning jQuery. jQuery, the library is a great tool with years of production maturity and can be used alongside Angular and we use it in our production applications as well.

In this post, we’ll walk through how to think in Angular and provide a mental model for structuring how to think about building better, faster, more stable web apps with AngularJS.

Throughout this section, we’re working through a jQuery application and demonstrating how to build it in Angular.

How we build web apps with jQuery

1
2
3
4
5
$(document).ready(function() { $("#article").click(function(evt) { $("#article_placeholder").append("Angular for the jQuery developer"); }); });

Historically, we as developers have been required to manually update DOM components to build a dynamic webpage.

We first lay out the structure to our website and then design the components with CSS and add interactivity to the page. That is, we layer interactivity on top of our HTML as an afterthought in the build process and treat interactivity as a second-class citizen.

Why not jQuery for building webapps?

Throughout the source, we are binding actions to specific DOMs with specific DOM IDs. What if a team member determines that the name #message isn’t specific enough, we change the DOM entirely to accompany a new design, or we are adding new functionality that includes an ID of #name?

Tight coupling

Our JavaScript is written to specifically correspond to our HTML. Such high cohesion often necessitates a lot more work on our part to keep our JavaScript in sync with our HTML. Whenever we change the DOM, we must be delicate and aware so that we don’t change the functionality of our JavaScript.

Code disorganization

When using jQuery, there is no clearly encouraged organization for our web app functionality; it’s up to us to determine how we’ll organize it.

Low-level tools for high-level functionality

When we’re building a web app with any level of complexity, we’re forced to write our custom actions with low-level DOM manipulation tools rather than focusing on the functionality.

Angular takes a different approach to building web apps: It treats building interactivity as a native component to webapps and encourages working interactivity through development while we build our apps.

The following 8 core ideas to keep in mind when we are building Angular apps are:

1) Declare the interactivity alongside the HTML

When we write Angular apps, we don’t separate how we write our page and our interaction; instead, we define the functionality inside our HTML. If we want to run an action when we click a button, we attach the action to the button:

1
2
<!-- Call the runAction() method --> <button ng-click="runAction()">Run action</button>

Doing so allows us to be explicit and declarative with our DOM. Additionally, our DOM tells us explicitly what it will do in the browser.

Imperative programming: telling the “machine” how to do something, and as a result what you want to happen will happen. Declarative programming: telling the “machine” what you would like to happen, and letting the computer figure out how to do it.

2) Leave the DOM alone

Angular very clearly lays out a strategy of building applications using the data to drive the functionality. Rather than building a page to manipulate, we interact with a data object (called $scope).

When we want to change an element in our view, we change the data that is tied to it and let Angular take care of updating the DOM.

1
2
3
4
5
6
<!-- Show the name in the browser --> <h1>Welcome {{ name }}</h1> <!-- Bind the input to the name --> <input ng-model="name" name="name" placeholder="Enter your name" />

See it

Welcome {{ name }}

At no point in this example have we needed to manipulate the DOM. This fact allows us to write HTML and design it at the same time.

3) Architecting the app

Writing an Angular app allows us to think in terms of building an application. We can focus on the functionality, from understanding how our app interacts with a server-side API and what actions happen when we click on a button to which routes our app supports.

We need to think about how to divide the functionality of our app into small components that make it easy to extend and test our app.

4) Leave jQuery cold turkey

It’s tempting to use jQuery as a crutch when we first start learning Angular. In our classes and books, we constantly recommend to not even import jQuery when first starting to build with Angular.

Everything that we do in jQuery, we can do with Angular, often with much less code.

We’ll write more efficient, more extensible, more testable applications if we stick to using Angular and don’t rely on tools we’re used to as crutches. If you want to build a piece of functionality that is not included in Angular, someone in the community has likely built it.

5) The view is the state official record

Angular apps use the view as the container of interactivity and data. We use directives to attach functionality to the view, which, in turn, uses data bindings to create an explicit data-action chain inside the app.

1
2
3
4
<div fs-modal on-ok="proceedAsPlanned()" on-cancel="abort()" title="Are you sure?"></div>

The fs-modal directive used above very clearly demonstrates what the <div> element’s responsibility is. The only way that we can change the view’s responsibility is by changing the HTML, unlike the sticky situation we get into when we imperatively assign functionality to elements outside in a foreign document.

6) Models update the view, which updates the models

One of the most basic, coolest features of Angular is that we don’t need to worry about the DOM beyond writing the HTML.

Unlike jQuery, where we need to imperatively build elements and attach them, while maintaining the state or query the document at runtime, we can focus on building the functionality based on expected data.

For instance, to show a loading indicator, we can set a boolean flag that shows or hides the element based upon a model value:

1
2
3
<div ng-show="isLoading"> <img src="/images/loading.gif" /> </div>

Now, whenever we are loading new data, we can simply flip this “switch” on (by setting isLoading to true). When we are done loading data, we can flip it back to false, and the indicator will hide.

1
2
3
4
5
6
7
8
9
10
11
$scope.loadNewData = function() { $scope.isLoading = true; $http.get('/api/data.json') .success(function(data) { $scope.data = data; $scope.isLoading = false; }) .error(function(reason) { $scope.isLoading = false; }); }

See it

This will change isLoading for two seconds, not actually load any real data

If we update the model in our controllers, then the view will update. If we act upon and change the model in the view, then the model in our controllers will update and everything is kept in sync.

The magic of Angular is that throughout this entire process, we don’t need to even think about how – we can simply concern ourselves with the functionality we are building.

7) Dependencies, dependencies, dependencies - oh my!

Another task Angular handles seamlessly for us in the background is dependency injection: We simply tell Angular what we need to operate our app, and, as long as Angular can find it, Angular will handle loading it for us.

8) Test-driven development

One of the greatest components of Angular is that it was built, from the ground up, to be testable. When we write tests, we not only guarantee that our web app is working, but also that components we are using work as expected.

Angular was built with test-driven development in mind, and thus, it is incredibly easy to write testable applications.

We can write tests for all parts of our application – for the model layer, the service layer, the view layer, etc.

We can test both from the perspective of the developer (and unit test each component) and from the perspective of our users (by end-to-end testing, wherein we load our app in a browser and tell the test framework to click on buttons and test that the view is showing what we expect).

No more manually clicking buttons every time we make a minor change to test if a feature is working.

In summary

When we think in Angular terms, we think about:

The jQuery chat app

For instance, let’s say we have a very basic chat application built in jQuery. The application is incredibly simple and has the following features:

The app that we’ll be building has the following HTML structure (with styles removed for simplicity):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<div id="chatApp" class="row"> <div id="message_placeholder"></div> <form action="#"> <input type="text" id="name" placeholder="Your name" /> <input type="text" id="message" placeholder="Your message..."/> <input type="submit" class="button prefix" value="Send" /> </form> </div> </div>

We have a form with several input fields and a div that will contain our list of messages.

Live example in jQuery

Building it in jQuery

To build this chat app in jQuery, we’re going to need some important pieces of information. We need to know the IDs of the following input fields:

For these field, we’ll attach an event listener to the <form> submit action where we’ll handle attaching the messages to the chat window.

Inside the chat window, we’ll only keep a limited number of messages in the history, so we’ll need to make sure we remove old messages.

Lastly, we’ll only be able to run this entire process after the document is ready to go, so we’ll need to wrap it in a $(document).ready(function() {}).

Since this is not a jQuery tutorial, this source is:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$(document).ready(function() { var maxMessages = 5, messageBox = $("#message_placeholder"); $("#chatApp form").on('submit', function(evt) { var from = $("#name").val() || "Anonymous", msg = $("#message").val() var msg = $("<div />").text(from+": "+msg); messageBox.append(msg); $("#message").val(""); if (messageBox.children().length > maxMessages) messageBox.children("div").first().remove(); return false; }); });

Building it with Angular

When we build this same chat program in Angular, we don’t need to bother with knowing the selectors for the elements that we’re attaching interactivity to, because we’ll write our DOM to include the interactivity.

Like all Angular apps, we’ll start by defining our app in the page. Angular apps do not have to take over the entire page; rather, they can be placed in specific elements by using the ng-app directive. We’ll also want to contain our functionality inside of a controller so we don’t dirty the global scope:

1
2
<div ng-app="ngChatApp" ng-controller="ChatCtrl"> </div>

A directive is simply a fancy term for a function that is run on a specific DOM element. It allows us to attach functionality to HTML elements.

Next, we’ll create the JavaScript that we’ll use to write our functionality:

1
2
3
4
angular.module('ngChatApp', []) .controller('ChatCtrl', function($scope) { // Write our controller here });

It’s about the data, not the DOM

We only need to think about what data we want to place in our HTML and where, not worry about how it gets there. With that in mind, we’ll be keeping track of messages to display, not DOM elements to create.

In our view, we can simply iterate over our messages and allow the DOM to display as it naturally will when the messages model is filled with data:

1
2
3
4
5
<div id="message_placeholder"> <div ng-repeat="message in messages"> {{ message.from }}: {{ message.msg }} </div> </div>

Similarly, we can bind model values to our input fields and allow the model to be built in the background so that we can use it within our controller action. We can also bind actions, such as form submission, to the HTML.

1
2
3
4
5
6
7
8
9
10
<form ng-submit="addMessage()"> <input type="text" ng-model="username" placeholder="Your name" /> <input type="text" ng-model="message" placeholder="Your message" /> <input type="submit" value="Send" /> </form>

When the form is submitted, the addMessage() function will be called, and we’ll have the values of username and message available to us… but where can we get them?

$scope

The $scope is the glue between the view and the controller. Anything that we bind in the view will become available on the $scope of the controller and vice versa. Thus, we can simply manipulate the messages array on our scope, and the view will updates.

The addMessage() function looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
angular.module('ngChatApp', []) .controller('ChatCtrl', function($scope) { var maxMessages = 5; $scope.messages = []; $scope.addMessage = function() { $scope.messages.push({ from: $scope.username, msg: $scope.message }); if ($scope.messages.length > maxMessages) $scope.messages.splice(0,1); $scope.message = ""; } });

Yep, that’s it! Angular will take care of loading the data model into the DOM and rendering the view.

Live example in Angular

{{ message.from }}: {{ message.msg }}

We hope this article helps to clear up some confusion about how to start working with Angular if you come from a jQuery background.

For more information about Angular, check out the rest of our posts or check out our book on Angular, ng-book: The Complete Book on AngularJS.

Cheers!

Get the weekly email all focused on AngularJS. Sign up below to receive the weekly email and exclusive content.
We will never send you spam and it's a cinch to unsubscribe.

Download a free sample of the ng-book: The Complete Book on AngularJS

ng-book: The Complete Book on AngularJS is the canonical AngularJS book available today.

It's free, so just enter your email address and the PDF will be sent directly to your inbox. Mailchimp can take up to an hour to deliver the free sample chapter, but if you don't receive it within the hour, send us an email and we'll manually send them to you!

We'll send you updates about the book, when it updates and other free content.

We will never send you spam and it's a cinch to unsubscribe.

Comments

comments powered by Disqus