This is a simple and practical introduction to AngularJS written from the point of view of a developer who already has a firm knowledge of jQuery. jQuery and Angular work very well together, so the point is not to establish which solution is better for you but to start using Angular effectively.
Data binding
Binding data to a specific document fragment or structure is something that is implemented in two different ways by Angular and jQuery. jQuery uses the following approach:
$(function() {
$( "[data-message]" ).each(function() {
$( this ).text( $( this ).data( "message" ) );
});
});
In jQuery there's always a direct manipulation of the DOM structure. Angular, instead, doesn't work this way:
<body ng-app="MyApp">
<div ng-controller="MessageCtrl">
<p>{{message}}</p>
</div>
</body>
The JavaScript code is as follows:
var app = angular.module('MyApp', []);
app.controller('MessageCtrl', ['$scope', function($scope) {
$scope.message = 'Hello world';
}]);
Angular first initializes the main module of our application by using MyApp
through the ng-app
directive. This simply bootstraps our code. Then you define
a controller, in this case MessageCtrl
, linked by the ng-controller
directive to our HTML code (called View in the Model View Whatever design pattern).
This controller has a dependency injected into its constructor function, namely $scope
, which is the current scope of our controller. What we do now? We define a property called message
and finally magic happens: Angular finds the {{message}}
block (note the variable's name within the block) and replaces this block with the value of our property.
No DOM manipulation required. Clean, simple and concise. It's a little bit difficult to grasp at first because Angular forces you to structure your code in a specific way. A well-organized way.
Two-way data binding
We have a text input and we want to show on the page what the user is typing in real time. In jQuery we can write the following code:
$(function() {
var message = "Hello world",
$output = $( "p" ).eq( 0 ),
$input = $( ".form-control" );
$input.val( message );
$output.text( message );
$input.keyup(function() {
var value = $( this ).val();
$output.text( value );
});
});
Two-way data binding with jQuery
It works well, so why bother? But the Angular team has found a way to make it even simpler than this:
<body ng-app="MyApp">
<div ng-controller="MessageCtrl">
<p>{{message}}</p>
<p><input type="text" class="form-control" ng-model="message" ng-value="message" /></p>
</div>
</body>
The JavaScript code:
var app = angular.module('MyApp', []);
app.controller('MessageCtrl', ['$scope', function($scope) {
$scope.message = 'Hello world';
}]);
The magic here is performed by the ng-model
directive that refers to the message
property. When a user types something in the text field, the value of our property is automatically updated. Also note that Angular provides specific directives that enhance the traditional HTML attributes, such as ng-value
.
Working with data
Suppose that we have a list of contacts that we want to show on a page. In jQuery this involves looping through an array, build an HTML string and finally call the .html()
method to insert our data into a specific element.
In Angular things get separated. First, the controller:
app.controller('DataCtrl', ['$scope', function($scope) {
$scope.data = [
{
name: 'Louisa Newman',
phone: '+1 (935) 582-3513'
},
{
name: 'Irene Lindsey',
phone: '+1 (871) 573-3578'
},
{
name: 'Case Bright',
phone: '+1 (917) 457-3358'
},
{
name: 'Davidson Clarke',
phone: '+1 (850) 526-2418'
},
{
name: 'Nolan Stevenson',
phone: '+1 (894) 584-3227'
},
{
name: 'Sonia Ferguson',
phone: '+1 (815) 447-2996'
}
];
}]);
Then our view:
<body ng-app="MyApp">
<div ng-controller="DataCtrl">
<ul class="contacts">
<li ng-repeat="item in data">
<h4><i class="glyphicon glyphicon-user"></i> {{item.name}}</h4>
<div>{{item.phone}}</div>
</li>
</ul>
</div>
</body>
The ng-repeat
takes care of the loop and it provides access to every single object in the array. As you can see, our array has been declared as a property of the controller's scope.
Now I bet you're tired to see only static properties in the scope and you want to see what happens when we create methods. Let's add the ability to add or remove a contact to our controller:
app.controller('DataCtrl', ['$scope', function($scope) {
$scope.data = [/*...*/];
$scope.addContact = function(name, phone) {
if($scope.dataForm.$valid) {
$scope.data.push({
name: name,
phone: phone
});
}
};
$scope.removeContact = function(name) {
for(var i = 0; i < $scope.data.length; ++i) {
if($scope.data[i].name == name) {
$scope.data.splice(i, 1);
}
}
};
}]);
And in our view:
<body ng-app="MyApp">
<div ng-controller="DataCtrl">
<ul class="contacts" ng-if="data.length > 0">
<li ng-repeat="item in data">
<h4><i class="glyphicon glyphicon-user"></i> {{item.name}}
<a href="" ng-click="removeContact(item.name)">
<i class="glyphicon glyphicon-remove"></i>
</a>
</h4>
<div>{{item.phone}}</div>
</li>
</ul>
<form novalidate name="dataForm" ng-submit="addContact(name, phone)">
<p><input type="text" name="name" class="form-control" ng-model="name" placeholder="Name" required /></p>
<p><input type="text" name="phone" class="form-control" ng-model="phone" placeholder="Phone" required /></p>
<p><input type="submit" class="btn btn-primary" value="Add contact" /></p>
</form>
</div>
</body>
The main changes are in our view. We use the ng-if
directive to test if there are contacts to show. If there are none, the list won't be created. Then we attach a click handler using the ng-click
directive. Inside this directive we call our removeContact()
method which accepts the name of a contact as its sole argument. Note that within our loop we're invoking this method on the current contact object.
We don't need the default HTML5 validation on our form, so we use the novalidate
attribute to stop the browser's behavior. We assign a name
attribute to the form, so we can reference it in our controller's scope. When the form is submitted, we call our method that creates a new contact. This method checks whether a name and a phone number have been actually inserted.
Why name
and phone
are not undefined
? The two-way data binding automatically creates them and assign it to the controller's scope if they don't exist. Really handy!
AJAX
So far it's been easy to work with data because everything was already in place. Let's rewrite our previous example by using AJAX. In jQuery we can write the following code:
var getData = function( url, callback ) {
$.when( $.getJSON( url ) ).done(function( response ) {
callback( response );
});
};
var parseData = function( data, element ) {
var html = "";
for( var i = 0; i < data.length; ++i ) {
html += "<li>";
html += "<h4><i class='glyphicon glyphicon-user'></i> ";
html += data[i].name;
html += " <a href='' class='remove'><i class='glyphicon glyphicon-remove'></i></a>";
html += "</h4>";
html += "<div>" + data[i].phone + "</div>";
html += "</li>";
}
element.html( html );
};
$(function() {
$( document ).on( "click", ".remove", function( e ) {
e.preventDefault();
$( this ).parents( "li" ).remove();
});
$( ".dataForm" ).on( "submit", function( e ) {
e.preventDefault();
var name = $( this ).find( "[name='name']" ).val();
var phone = $( this ).find( "[name='phone']" ).val();
if( phone !== "" && name !== "" ) {
var html = "<li>";
html += "<h4><i class='glyphicon glyphicon-user'></i> ";
html += name;
html += " <a href='' class='remove'><i class='glyphicon glyphicon-remove'></i></a>";
html += "</h4>";
html += "<div>" + phone + "</div>";
html += "</li>";
$( ".contacts" ).append( html );
}
});
getData( "api/data.json", function( response) {
parseData( response, $( ".contacts" ) );
});
});
It should be clear now that jQuery 90% of the times manipulates the DOM directly either by injecting HTML code directly or appending new elements to an existing structure. Angular works in a different way.
Every property in the scope is live. This means that Angular updates a property whenever a change occurs, even after an AJAX request. In Angular the JavaScript code is as follows:
app.controller('DataCtrl', ['$scope', '$http', function($scope, $http) {
$scope.data = [];
$http.get('api/data.json').then(function(response) {
$scope.data = response.data;
}, function(response) {
});
$scope.addContact = function(name, phone) {
if($scope.dataForm.$valid) {
$scope.data.push({
name: name,
phone: phone
});
}
};
$scope.removeContact = function(name) {
for(var i = 0; i < $scope.data.length; ++i) {
if($scope.data[i].name == name) {
$scope.data.splice(i, 1);
}
}
};
}]);
$http
is defined as follows:
The $http service is a core Angular service that facilitates communication with the remote HTTP servers via the browser's XMLHttpRequest object or via JSONP.
Note that this service must be explicitly injected into our controller: this happens because every dependency must be always injected in Angular. Despite its asynchronous nature, implemented with promises, when the data is received and associated with the data
property, it immediately becomes available in the scope.
Complete examples
Introduction to AngularJS for jQuery developers